mirror of
https://github.com/doitsujin/dxvk.git
synced 2024-12-05 01:24:14 +01:00
[dxgi] Implement frame statistics based on IDXGIVkSwapChain1
This commit is contained in:
parent
28f48f9fdc
commit
a287566c65
@ -16,12 +16,16 @@ namespace dxvk {
|
|||||||
m_window (hWnd),
|
m_window (hWnd),
|
||||||
m_desc (*pDesc),
|
m_desc (*pDesc),
|
||||||
m_descFs (*pFullscreenDesc),
|
m_descFs (*pFullscreenDesc),
|
||||||
m_presentCount(0u),
|
m_presentId (0u),
|
||||||
m_presenter (pPresenter),
|
m_presenter (pPresenter),
|
||||||
m_monitor (wsi::getWindowMonitor(m_window)) {
|
m_monitor (wsi::getWindowMonitor(m_window)) {
|
||||||
if (FAILED(m_presenter->GetAdapter(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&m_adapter))))
|
if (FAILED(m_presenter->GetAdapter(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&m_adapter))))
|
||||||
throw DxvkError("DXGI: Failed to get adapter for present device");
|
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
|
// Query monitor info form DXVK's DXGI factory, if available
|
||||||
m_factory->QueryInterface(__uuidof(IDXGIVkMonitorInfo), reinterpret_cast<void**>(&m_monitorInfo));
|
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
|
// Populate frame statistics with local present count and current time
|
||||||
auto t1Counter = dxvk::high_resolution_clock::get_counter();
|
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->PresentRefreshCount = 0;
|
||||||
pStats->SyncRefreshCount = 0;
|
pStats->SyncRefreshCount = 0;
|
||||||
pStats->SyncQPCTime.QuadPart = t1Counter;
|
pStats->SyncQPCTime.QuadPart = t1Counter;
|
||||||
pStats->SyncGPUTime.QuadPart = 0;
|
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))) {
|
if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorData))) {
|
||||||
auto refreshPeriod = computeRefreshPeriod(
|
auto refreshPeriod = computeRefreshPeriod(
|
||||||
monitorData->LastMode.RefreshRate.Numerator,
|
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 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 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->PresentRefreshCount = m_presenter1 != nullptr
|
||||||
|
? monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t2, refreshPeriod)
|
||||||
|
: monitorData->FrameStats.PresentRefreshCount;
|
||||||
pStats->SyncRefreshCount = monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t1, refreshPeriod);
|
pStats->SyncRefreshCount = monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t1, refreshPeriod);
|
||||||
|
|
||||||
ReleaseMonitorData();
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -259,7 +282,12 @@ namespace dxvk {
|
|||||||
if (pLastPresentCount == nullptr)
|
if (pLastPresentCount == nullptr)
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
|
||||||
*pLastPresentCount = m_presentCount;
|
UINT64 presentId = m_presentId;
|
||||||
|
|
||||||
|
if (m_presenter1 != nullptr)
|
||||||
|
m_presenter1->GetLastPresentCount(&presentId);
|
||||||
|
|
||||||
|
*pLastPresentCount = UINT(presentId);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,26 +309,27 @@ namespace dxvk {
|
|||||||
UINT PresentFlags,
|
UINT PresentFlags,
|
||||||
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
|
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
|
||||||
|
|
||||||
if (!wsi::isWindow(m_window))
|
|
||||||
return S_OK;
|
|
||||||
|
|
||||||
if (SyncInterval > 4)
|
if (SyncInterval > 4)
|
||||||
return DXGI_ERROR_INVALID_CALL;
|
return DXGI_ERROR_INVALID_CALL;
|
||||||
|
|
||||||
std::lock_guard<dxvk::recursive_mutex> lockWin(m_lockWindow);
|
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);
|
std::lock_guard<dxvk::mutex> lockBuf(m_lockBuffer);
|
||||||
HRESULT hr = m_presenter->Present(SyncInterval, PresentFlags, nullptr);
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update frame statistics
|
if (PresentFlags & DXGI_PRESENT_TEST)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
if (hr == S_OK) {
|
||||||
|
|
||||||
|
m_presentId += 1;
|
||||||
|
|
||||||
|
// 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;
|
DXGI_VK_MONITOR_DATA* monitorData = nullptr;
|
||||||
|
|
||||||
if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorData))) {
|
if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorData))) {
|
||||||
@ -315,9 +344,9 @@ namespace dxvk {
|
|||||||
monitorData->FrameStats.PresentRefreshCount = monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t1, refreshPeriod);
|
monitorData->FrameStats.PresentRefreshCount = monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t1, refreshPeriod);
|
||||||
ReleaseMonitorData();
|
ReleaseMonitorData();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_presentCount += 1;
|
return hr;
|
||||||
return S_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -804,9 +833,27 @@ namespace dxvk {
|
|||||||
HRESULT DxgiSwapChain::AcquireMonitorData(
|
HRESULT DxgiSwapChain::AcquireMonitorData(
|
||||||
HMONITOR hMonitor,
|
HMONITOR hMonitor,
|
||||||
DXGI_VK_MONITOR_DATA** ppData) {
|
DXGI_VK_MONITOR_DATA** ppData) {
|
||||||
return m_monitorInfo != nullptr
|
if (m_monitorInfo == nullptr || !hMonitor)
|
||||||
? m_monitorInfo->AcquireMonitorData(hMonitor, ppData)
|
return E_NOINTERFACE;
|
||||||
: 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -183,11 +183,14 @@ namespace dxvk {
|
|||||||
HWND m_window;
|
HWND m_window;
|
||||||
DXGI_SWAP_CHAIN_DESC1 m_desc;
|
DXGI_SWAP_CHAIN_DESC1 m_desc;
|
||||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_descFs;
|
DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_descFs;
|
||||||
UINT m_presentCount;
|
UINT m_presentId;
|
||||||
|
|
||||||
Com<IDXGIVkSwapChain> m_presenter;
|
Com<IDXGIVkSwapChain> m_presenter;
|
||||||
|
Com<IDXGIVkSwapChain1> m_presenter1;
|
||||||
|
|
||||||
HMONITOR m_monitor;
|
HMONITOR m_monitor;
|
||||||
|
bool m_monitorHasOutput = true;
|
||||||
|
bool m_frameStatisticsDisjoint = true;
|
||||||
wsi::DxvkWindowState m_windowState;
|
wsi::DxvkWindowState m_windowState;
|
||||||
|
|
||||||
HRESULT EnterFullscreenMode(
|
HRESULT EnterFullscreenMode(
|
||||||
|
Loading…
Reference in New Issue
Block a user