mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-01-19 14:52:10 +01:00
[dxgi] Limit frame rate to display refresh as necessary
This commit is contained in:
parent
379346751a
commit
1c198dcd48
@ -43,7 +43,12 @@
|
||||
# bugs in games that have physics or other simulation tied to their frame
|
||||
# rate, but do not provide their own limiter.
|
||||
#
|
||||
# Supported values : Any non-negative integer
|
||||
# Supported values
|
||||
# -1: Always disables the limiter
|
||||
# 0: Default behaviour. Limits the frame rate to the selected display
|
||||
# refresh rate when vertical synchronization is enabled if the
|
||||
# actual display mode does not match the game's one.
|
||||
# n: Limit to n frames per second.
|
||||
|
||||
# dxgi.maxFrameRate = 0
|
||||
# d3d9.maxFrameRate = 0
|
||||
|
@ -30,7 +30,6 @@ namespace dxvk {
|
||||
this->deferSurfaceCreation = config.getOption<bool>("dxgi.deferSurfaceCreation", false);
|
||||
this->numBackBuffers = config.getOption<int32_t>("dxgi.numBackBuffers", 0);
|
||||
this->maxFrameLatency = config.getOption<int32_t>("dxgi.maxFrameLatency", 0);
|
||||
this->maxFrameRate = config.getOption<int32_t>("dxgi.maxFrameRate", 0);
|
||||
this->exposeDriverCommandLists = config.getOption<bool>("d3d11.exposeDriverCommandLists", true);
|
||||
this->longMad = config.getOption<bool>("d3d11.longMad", false);
|
||||
this->reproducibleCommandStream = config.getOption<bool>("d3d11.reproducibleCommandStream", false);
|
||||
|
@ -80,9 +80,6 @@ namespace dxvk {
|
||||
/// a higher value. May help with frame timing issues.
|
||||
int32_t maxFrameLatency;
|
||||
|
||||
/// Limit frame rate
|
||||
int32_t maxFrameRate;
|
||||
|
||||
/// Limit discardable resource size
|
||||
VkDeviceSize maxImplicitDiscardSize;
|
||||
|
||||
|
@ -99,7 +99,8 @@ namespace dxvk {
|
||||
|
||||
if (riid == __uuidof(IUnknown)
|
||||
|| riid == __uuidof(IDXGIVkSwapChain)
|
||||
|| riid == __uuidof(IDXGIVkSwapChain1)) {
|
||||
|| riid == __uuidof(IDXGIVkSwapChain1)
|
||||
|| riid == __uuidof(IDXGIVkSwapChain2)) {
|
||||
*ppvObject = ref(this);
|
||||
return S_OK;
|
||||
}
|
||||
@ -347,6 +348,15 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
void STDMETHODCALLTYPE D3D11SwapChain::SetTargetFrameRate(
|
||||
double FrameRate) {
|
||||
m_targetFrameRate = FrameRate;
|
||||
|
||||
if (m_presenter != nullptr)
|
||||
m_presenter->setFrameRateLimit(m_targetFrameRate);
|
||||
}
|
||||
|
||||
|
||||
HRESULT D3D11SwapChain::PresentImage(UINT SyncInterval) {
|
||||
// Flush pending rendering commands before
|
||||
auto immediateContext = m_parent->GetContext();
|
||||
@ -496,7 +506,7 @@ namespace dxvk {
|
||||
presenterDesc.fullScreenExclusive = PickFullscreenMode();
|
||||
|
||||
m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc);
|
||||
m_presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate);
|
||||
m_presenter->setFrameRateLimit(m_targetFrameRate);
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,7 +13,7 @@ namespace dxvk {
|
||||
class D3D11Device;
|
||||
class D3D11DXGIDevice;
|
||||
|
||||
class D3D11SwapChain : public ComObject<IDXGIVkSwapChain1> {
|
||||
class D3D11SwapChain : public ComObject<IDXGIVkSwapChain2> {
|
||||
constexpr static uint32_t DefaultFrameLatency = 1;
|
||||
public:
|
||||
|
||||
@ -86,6 +86,9 @@ namespace dxvk {
|
||||
void STDMETHODCALLTYPE GetFrameStatistics(
|
||||
DXGI_VK_FRAME_STATISTICS* pFrameStatistics);
|
||||
|
||||
void STDMETHODCALLTYPE SetTargetFrameRate(
|
||||
double FrameRate);
|
||||
|
||||
private:
|
||||
|
||||
enum BindingIds : uint32_t {
|
||||
@ -116,18 +119,20 @@ namespace dxvk {
|
||||
|
||||
std::vector<Rc<DxvkImageView>> m_imageViews;
|
||||
|
||||
uint64_t m_frameId = DXGI_MAX_SWAP_CHAIN_BUFFERS;
|
||||
uint32_t m_frameLatency = DefaultFrameLatency;
|
||||
uint32_t m_frameLatencyCap = 0;
|
||||
HANDLE m_frameLatencyEvent = nullptr;
|
||||
Rc<sync::CallbackFence> m_frameLatencySignal;
|
||||
uint64_t m_frameId = DXGI_MAX_SWAP_CHAIN_BUFFERS;
|
||||
uint32_t m_frameLatency = DefaultFrameLatency;
|
||||
uint32_t m_frameLatencyCap = 0;
|
||||
HANDLE m_frameLatencyEvent = nullptr;
|
||||
Rc<sync::CallbackFence> m_frameLatencySignal;
|
||||
|
||||
bool m_dirty = true;
|
||||
bool m_dirty = true;
|
||||
|
||||
VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
|
||||
std::optional<VkHdrMetadataEXT> m_hdrMetadata;
|
||||
bool m_dirtyHdrMetadata = true;
|
||||
bool m_dirtyHdrMetadata = true;
|
||||
|
||||
double m_targetFrameRate = 0.0;
|
||||
|
||||
dxvk::mutex m_frameStatisticsLock;
|
||||
DXGI_VK_FRAME_STATISTICS m_frameStatistics = { };
|
||||
|
@ -137,6 +137,13 @@ IDXGIVkSwapChain1 : public IDXGIVkSwapChain {
|
||||
};
|
||||
|
||||
|
||||
MIDL_INTERFACE("aed91093-e02e-458c-bdef-a97da1a7e6d2")
|
||||
IDXGIVkSwapChain2 : public IDXGIVkSwapChain1 {
|
||||
virtual void STDMETHODCALLTYPE SetTargetFrameRate(
|
||||
double FrameRate) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Private DXGI presenter factory
|
||||
*/
|
||||
@ -471,5 +478,6 @@ __CRT_UUID_DECL(IDXGIVkInteropSurface, 0x5546cf8c,0x77e7,0x4341,0xb0,0x5d,0x
|
||||
__CRT_UUID_DECL(IDXGIVkSurfaceFactory, 0x1e7895a1,0x1bc3,0x4f9c,0xa6,0x70,0x29,0x0a,0x4b,0xc9,0x58,0x1a);
|
||||
__CRT_UUID_DECL(IDXGIVkSwapChain, 0xe4a9059e,0xb569,0x46ab,0x8d,0xe7,0x50,0x1b,0xd2,0xbc,0x7f,0x7a);
|
||||
__CRT_UUID_DECL(IDXGIVkSwapChain1, 0x785326d4,0xb77b,0x4826,0xae,0x70,0x8d,0x08,0x30,0x8e,0xe6,0xd1);
|
||||
__CRT_UUID_DECL(IDXGIVkSwapChain2, 0xaed91093,0xe02e,0x458c,0xbd,0xef,0xa9,0x7d,0xa1,0xa7,0xe6,0xd2);
|
||||
__CRT_UUID_DECL(IDXGIVkSwapChainFactory, 0xe7d6c3ca,0x23a0,0x4e08,0x9f,0x2f,0xea,0x52,0x31,0xdf,0x66,0x33);
|
||||
#endif
|
||||
|
@ -93,6 +93,7 @@ namespace dxvk {
|
||||
this->maxDeviceMemory = VkDeviceSize(config.getOption<int32_t>("dxgi.maxDeviceMemory", 0)) << 20;
|
||||
this->maxSharedMemory = VkDeviceSize(config.getOption<int32_t>("dxgi.maxSharedMemory", 0)) << 20;
|
||||
|
||||
this->maxFrameRate = config.getOption<int32_t>("dxgi.maxFrameRate", 0);
|
||||
this->syncInterval = config.getOption<int32_t>("dxgi.syncInterval", -1);
|
||||
|
||||
// Expose Nvidia GPUs properly if NvAPI is enabled in environment
|
||||
|
@ -49,6 +49,9 @@ namespace dxvk {
|
||||
/// Enable HDR
|
||||
bool enableHDR;
|
||||
|
||||
/// Limit frame rate
|
||||
int32_t maxFrameRate;
|
||||
|
||||
/// Sync interval. Overrides the value
|
||||
/// passed to IDXGISwapChain::Present.
|
||||
int32_t syncInterval;
|
||||
|
@ -25,6 +25,9 @@ namespace dxvk {
|
||||
// Query updated interface versions from presenter, this
|
||||
// may fail e.g. with older vkd3d-proton builds.
|
||||
m_presenter->QueryInterface(__uuidof(IDXGIVkSwapChain1), reinterpret_cast<void**>(&m_presenter1));
|
||||
m_presenter->QueryInterface(__uuidof(IDXGIVkSwapChain2), reinterpret_cast<void**>(&m_presenter2));
|
||||
|
||||
m_frameRateOption = m_factory->GetOptions()->maxFrameRate;
|
||||
|
||||
// Query monitor info form DXVK's DXGI factory, if available
|
||||
m_factory->QueryInterface(__uuidof(IDXGIVkMonitorInfo), reinterpret_cast<void**>(&m_monitorInfo));
|
||||
@ -328,6 +331,7 @@ namespace dxvk {
|
||||
SyncInterval = options->syncInterval;
|
||||
|
||||
UpdateGlobalHDRState();
|
||||
UpdateTargetFrameRate(SyncInterval);
|
||||
|
||||
std::lock_guard<dxvk::recursive_mutex> lockWin(m_lockWindow);
|
||||
HRESULT hr = S_OK;
|
||||
@ -777,6 +781,9 @@ namespace dxvk {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (!selectedMode.RefreshRate.Denominator)
|
||||
selectedMode.RefreshRate.Denominator = 1;
|
||||
|
||||
if (!wsi::setWindowMode(outputDesc.Monitor, m_window, ConvertDisplayMode(selectedMode)))
|
||||
return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;
|
||||
|
||||
@ -798,6 +805,8 @@ namespace dxvk {
|
||||
ReleaseMonitorData();
|
||||
}
|
||||
|
||||
m_frameRateRefresh = double(selectedMode.RefreshRate.Numerator)
|
||||
/ double(selectedMode.RefreshRate.Denominator);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@ -809,6 +818,7 @@ namespace dxvk {
|
||||
if (!wsi::restoreDisplayMode())
|
||||
return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;
|
||||
|
||||
m_frameRateRefresh = 0.0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@ -952,4 +962,23 @@ namespace dxvk {
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
void DxgiSwapChain::UpdateTargetFrameRate(
|
||||
UINT SyncInterval) {
|
||||
if (m_presenter2 == nullptr)
|
||||
return;
|
||||
|
||||
// Use a negative number to indicate that the limiter should only
|
||||
// be engaged if the target frame rate is actually exceeded
|
||||
double frameRate = std::max(m_frameRateOption, 0.0);
|
||||
|
||||
if (SyncInterval && m_frameRateOption == 0.0)
|
||||
frameRate = -m_frameRateRefresh / double(SyncInterval);
|
||||
|
||||
if (m_frameRateLimit != frameRate) {
|
||||
m_frameRateLimit = frameRate;
|
||||
m_presenter2->SetTargetFrameRate(frameRate);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -187,12 +187,17 @@ namespace dxvk {
|
||||
|
||||
Com<IDXGIVkSwapChain> m_presenter;
|
||||
Com<IDXGIVkSwapChain1> m_presenter1;
|
||||
Com<IDXGIVkSwapChain2> m_presenter2;
|
||||
|
||||
HMONITOR m_monitor;
|
||||
bool m_monitorHasOutput = true;
|
||||
bool m_frameStatisticsDisjoint = true;
|
||||
wsi::DxvkWindowState m_windowState;
|
||||
|
||||
double m_frameRateOption = 0.0;
|
||||
double m_frameRateRefresh = 0.0;
|
||||
double m_frameRateLimit = 0.0;
|
||||
|
||||
DXGI_COLOR_SPACE_TYPE m_colorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
|
||||
|
||||
uint32_t m_globalHDRStateSerial = 0;
|
||||
@ -233,6 +238,9 @@ namespace dxvk {
|
||||
DXGI_FORMAT Format,
|
||||
DXGI_COLOR_SPACE_TYPE ColorSpace);
|
||||
|
||||
void UpdateTargetFrameRate(
|
||||
UINT SyncInterval);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE PresentBase(
|
||||
UINT SyncInterval,
|
||||
UINT PresentFlags,
|
||||
|
Loading…
x
Reference in New Issue
Block a user