Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/doitsujin/dxvk.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Rebohle <philip.rebohle@tu-dortmund.de>2023-06-16 17:07:48 +0300
committerPhilip Rebohle <philip.rebohle@tu-dortmund.de>2023-06-16 20:45:59 +0300
commit2814ceac8a5ccc940c40fab2a5926f2bc6ccee55 (patch)
tree8384b13a8167edcb4a97a19653c5129d09aac6c3
parenta31bf30cc498199da3c6d3d7f9b6bb47b3f7c0fc (diff)
[dxgi] Implement frame statistics based on IDXGIVkSwapChain1present-id
-rw-r--r--src/dxgi/dxgi_swapchain.cpp123
-rw-r--r--src/dxgi/dxgi_swapchain.h5
2 files changed, 89 insertions, 39 deletions
diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp
index 4a78d38f..adfa667e 100644
--- a/src/dxgi/dxgi_swapchain.cpp
+++ b/src/dxgi/dxgi_swapchain.cpp
@@ -16,12 +16,16 @@ namespace dxvk {
m_window (hWnd),
m_desc (*pDesc),
m_descFs (*pFullscreenDesc),
- m_presentCount(0u),
+ m_presentId (0u),
m_presenter (pPresenter),
m_monitor (wsi::getWindowMonitor(m_window)) {
if (FAILED(m_presenter->GetAdapter(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&m_adapter))))
throw DxvkError("DXGI: Failed to get adapter for present device");
-
+
+ // 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));
+
// Query monitor info form DXVK's DXGI factory, if available
m_factory->QueryInterface(__uuidof(IDXGIVkMonitorInfo), reinterpret_cast<void**>(&m_monitorInfo));
@@ -183,15 +187,24 @@ namespace dxvk {
// Populate frame statistics with local present count and current time
auto t1Counter = dxvk::high_resolution_clock::get_counter();
- pStats->PresentCount = m_presentCount;
+ DXGI_VK_FRAME_STATISTICS frameStatistics = { };
+ frameStatistics.PresentCount = m_presentId;
+ frameStatistics.PresentQPCTime = t1Counter;
+
+ if (m_presenter1 != nullptr)
+ m_presenter1->GetFrameStatistics(&frameStatistics);
+
+ // Fill in actual DXGI statistics, using monitor data to help compute
+ // vblank counts if possible. This is not fully accurate, especially on
+ // displays with variable refresh rates, but it's the best we can do.
+ DXGI_VK_MONITOR_DATA* monitorData = nullptr;
+
+ pStats->PresentCount = frameStatistics.PresentCount;
pStats->PresentRefreshCount = 0;
pStats->SyncRefreshCount = 0;
pStats->SyncQPCTime.QuadPart = t1Counter;
pStats->SyncGPUTime.QuadPart = 0;
- // If possible, use the monitor's frame statistics for vblank stats
- DXGI_VK_MONITOR_DATA* monitorData = nullptr;
-
if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorData))) {
auto refreshPeriod = computeRefreshPeriod(
monitorData->LastMode.RefreshRate.Numerator,
@@ -199,14 +212,24 @@ namespace dxvk {
auto t0 = dxvk::high_resolution_clock::get_time_from_counter(monitorData->FrameStats.SyncQPCTime.QuadPart);
auto t1 = dxvk::high_resolution_clock::get_time_from_counter(t1Counter);
+ auto t2 = dxvk::high_resolution_clock::get_time_from_counter(frameStatistics.PresentQPCTime);
- pStats->PresentRefreshCount = monitorData->FrameStats.PresentRefreshCount;
- pStats->SyncRefreshCount = monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t1, refreshPeriod);
+ pStats->PresentRefreshCount = m_presenter1 != nullptr
+ ? monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t2, refreshPeriod)
+ : monitorData->FrameStats.PresentRefreshCount;
+ pStats->SyncRefreshCount = monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t1, refreshPeriod);
ReleaseMonitorData();
}
- return S_OK;
+ // Docs say that DISJOINT is returned on the first call and around
+ // mode changes. Just make this swap chain state for now.
+ HRESULT hr = S_OK;
+
+ if (std::exchange(m_frameStatisticsDisjoint, false))
+ hr = DXGI_ERROR_FRAME_STATISTICS_DISJOINT;
+
+ return hr;
}
@@ -258,8 +281,13 @@ namespace dxvk {
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetLastPresentCount(UINT* pLastPresentCount) {
if (pLastPresentCount == nullptr)
return E_INVALIDARG;
-
- *pLastPresentCount = m_presentCount;
+
+ UINT64 presentId = m_presentId;
+
+ if (m_presenter1 != nullptr)
+ m_presenter1->GetLastPresentCount(&presentId);
+
+ *pLastPresentCount = UINT(presentId);
return S_OK;
}
@@ -281,43 +309,44 @@ namespace dxvk {
UINT PresentFlags,
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
- if (!wsi::isWindow(m_window))
- return S_OK;
-
if (SyncInterval > 4)
return DXGI_ERROR_INVALID_CALL;
std::lock_guard<dxvk::recursive_mutex> lockWin(m_lockWindow);
+ HRESULT hr = S_OK;
- try {
+ if (wsi::isWindow(m_window)) {
std::lock_guard<dxvk::mutex> lockBuf(m_lockBuffer);
- HRESULT hr = m_presenter->Present(SyncInterval, PresentFlags, nullptr);
-
- if (hr != S_OK || (PresentFlags & DXGI_PRESENT_TEST))
- return hr;
- } catch (const DxvkError& err) {
- Logger::err(err.message());
- return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
+ hr = m_presenter->Present(SyncInterval, PresentFlags, nullptr);
}
- // Update frame statistics
- DXGI_VK_MONITOR_DATA* monitorData = nullptr;
+ if (PresentFlags & DXGI_PRESENT_TEST)
+ return hr;
- if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorData))) {
- auto refreshPeriod = computeRefreshPeriod(
- monitorData->LastMode.RefreshRate.Numerator,
- monitorData->LastMode.RefreshRate.Denominator);
+ if (hr == S_OK) {
- auto t0 = dxvk::high_resolution_clock::get_time_from_counter(monitorData->FrameStats.SyncQPCTime.QuadPart);
- auto t1 = dxvk::high_resolution_clock::now();
+ m_presentId += 1;
- monitorData->FrameStats.PresentCount += 1;
- monitorData->FrameStats.PresentRefreshCount = monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t1, refreshPeriod);
- ReleaseMonitorData();
+ // Update monitor frame statistics. This is not consistent with swap chain
+ // frame statistics at all, but we want to ensure that all presents become
+ // visible to the IDXGIOutput in case applications rely on that behaviour.
+ DXGI_VK_MONITOR_DATA* monitorData = nullptr;
+
+ if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorData))) {
+ auto refreshPeriod = computeRefreshPeriod(
+ monitorData->LastMode.RefreshRate.Numerator,
+ monitorData->LastMode.RefreshRate.Denominator);
+
+ auto t0 = dxvk::high_resolution_clock::get_time_from_counter(monitorData->FrameStats.SyncQPCTime.QuadPart);
+ auto t1 = dxvk::high_resolution_clock::now();
+
+ monitorData->FrameStats.PresentCount += 1;
+ monitorData->FrameStats.PresentRefreshCount = monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t1, refreshPeriod);
+ ReleaseMonitorData();
+ }
}
- m_presentCount += 1;
- return S_OK;
+ return hr;
}
@@ -804,9 +833,27 @@ namespace dxvk {
HRESULT DxgiSwapChain::AcquireMonitorData(
HMONITOR hMonitor,
DXGI_VK_MONITOR_DATA** ppData) {
- return m_monitorInfo != nullptr
- ? m_monitorInfo->AcquireMonitorData(hMonitor, ppData)
- : E_NOINTERFACE;
+ if (m_monitorInfo == nullptr || !hMonitor)
+ return E_NOINTERFACE;
+
+ HRESULT hr = m_monitorInfo->AcquireMonitorData(hMonitor, ppData);
+
+ if (FAILED(hr)) {
+ // We may need to initialize a DXGI output to populate monitor data.
+ // If acquiring monitor data has failed previously, do not try again.
+ if (hMonitor == m_monitor && !m_monitorHasOutput)
+ return E_NOINTERFACE;
+
+ Com<IDXGIOutput1> output;
+
+ if (SUCCEEDED(GetOutputFromMonitor(hMonitor, &output)))
+ hr = m_monitorInfo->AcquireMonitorData(hMonitor, ppData);
+ }
+
+ if (hMonitor == m_monitor)
+ m_monitorHasOutput = SUCCEEDED(hr);
+
+ return hr;
}
diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h
index c32c2c79..b87b71e4 100644
--- a/src/dxgi/dxgi_swapchain.h
+++ b/src/dxgi/dxgi_swapchain.h
@@ -183,11 +183,14 @@ namespace dxvk {
HWND m_window;
DXGI_SWAP_CHAIN_DESC1 m_desc;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_descFs;
- UINT m_presentCount;
+ UINT m_presentId;
Com<IDXGIVkSwapChain> m_presenter;
+ Com<IDXGIVkSwapChain1> m_presenter1;
HMONITOR m_monitor;
+ bool m_monitorHasOutput = true;
+ bool m_frameStatisticsDisjoint = true;
wsi::DxvkWindowState m_windowState;
HRESULT EnterFullscreenMode(