From a537ecf4214d8e3e745c62054a7c8a63444a9e9f Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 17 Jan 2025 22:22:10 +0100 Subject: [PATCH] [d3d9] Implement latency tracking --- src/d3d9/d3d9_device.cpp | 26 ++++++++++++--- src/d3d9/d3d9_device.h | 3 +- src/d3d9/d3d9_swapchain.cpp | 66 +++++++++++++++++++++++++++++++++---- src/d3d9/d3d9_swapchain.h | 10 +++++- 4 files changed, 92 insertions(+), 13 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 80bdcf88f..46f9061bf 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -4270,7 +4270,7 @@ namespace dxvk { m_implicitSwapchain->Invalidate(pPresentationParameters->hDeviceWindow); try { - auto* swapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode); + auto* swapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode, false); *ppSwapChain = ref(swapchain); m_losableResourceCounter++; } @@ -6098,11 +6098,29 @@ namespace dxvk { } - void D3D9DeviceEx::EndFrame() { + void D3D9DeviceEx::BeginFrame(Rc LatencyTracker, uint64_t FrameId) { D3D9DeviceLock lock = LockDevice(); - EmitCs([] (DxvkContext* ctx) { + EmitCs([ + cTracker = std::move(LatencyTracker), + cFrameId = FrameId + ] (DxvkContext* ctx) { + if (cTracker && cTracker->needsAutoMarkers()) + ctx->beginLatencyTracking(cTracker, cFrameId); + }); + } + + + void D3D9DeviceEx::EndFrame(Rc LatencyTracker) { + D3D9DeviceLock lock = LockDevice(); + + EmitCs([ + cTracker = std::move(LatencyTracker) + ] (DxvkContext* ctx) { ctx->endFrame(); + + if (cTracker && cTracker->needsAutoMarkers()) + ctx->endLatencyTracking(cTracker); }); } @@ -8445,7 +8463,7 @@ namespace dxvk { return hr; } else { - m_implicitSwapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode); + m_implicitSwapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode, true); m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr(); } diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index 7fd4ab70a..ed86bb85e 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -800,7 +800,8 @@ namespace dxvk { void Flush(); void FlushAndSync9On12(); - void EndFrame(); + void BeginFrame(Rc LatencyTracker, uint64_t FrameId); + void EndFrame(Rc LatencyTracker); void UpdateActiveRTs(uint32_t index); diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index de6604904..c268fa5dc 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -23,10 +23,12 @@ namespace dxvk { D3D9SwapChainEx::D3D9SwapChainEx( D3D9DeviceEx* pDevice, D3DPRESENT_PARAMETERS* pPresentParams, - const D3DDISPLAYMODEEX* pFullscreenDisplayMode) + const D3DDISPLAYMODEEX* pFullscreenDisplayMode, + bool EnableLatencyTracking) : D3D9SwapChainExBase(pDevice) , m_device (pDevice->GetDXVKDevice()) , m_frameLatencyCap (pDevice->GetOptions()->maxFrameLatency) + , m_latencyTracking (EnableLatencyTracking) , m_swapchainExt (this) { this->NormalizePresentParameters(pPresentParams); m_presentParams = *pPresentParams; @@ -186,7 +188,7 @@ namespace dxvk { #define DCX_USESTYLE 0x00010000 HRESULT D3D9SwapChainEx::PresentImageGDI(HWND Window) { - m_parent->EndFrame(); + m_parent->EndFrame(nullptr); m_parent->Flush(); if (!std::exchange(m_warnedAboutGDIFallback, true)) @@ -717,6 +719,9 @@ namespace dxvk { if (entry->second.presenter) { entry->second.presenter->destroyResources(); entry->second.presenter = nullptr; + + if (m_presentParams.hDeviceWindow == hWindow) + DestroyLatencyTracker(); } if (m_wctx == &entry->second) @@ -802,10 +807,15 @@ namespace dxvk { void D3D9SwapChainEx::PresentImage(UINT SyncInterval) { - m_parent->EndFrame(); + m_parent->EndFrame(m_latencyTracker); m_parent->Flush(); + if (m_latencyTracker) + m_latencyTracker->notifyCpuPresentBegin(m_wctx->frameId + 1u); + // Retrieve the image and image view to present + VkResult status = VK_SUCCESS; + Rc swapImage = m_backBuffers[0]->GetCommonTexture()->GetImage(); Rc swapImageView = m_backBuffers[0]->GetImageView(false); @@ -814,10 +824,12 @@ namespace dxvk { PresenterSync sync = { }; Rc backBuffer; - VkResult status = m_wctx->presenter->acquireNextImage(sync, backBuffer); + status = m_wctx->presenter->acquireNextImage(sync, backBuffer); - if (status < 0 || status == VK_NOT_READY) + if (status < 0 || status == VK_NOT_READY) { + status = i ? VK_SUCCESS : status; break; + } VkRect2D srcRect = { { int32_t(m_srcRect.left), int32_t(m_srcRect.top) }, @@ -854,7 +866,8 @@ namespace dxvk { cDstRect = dstRect, cRepeat = i, cSync = sync, - cFrameId = m_wctx->frameId + cFrameId = m_wctx->frameId, + cLatency = m_latencyTracker ] (DxvkContext* ctx) { // Update back buffer color space as necessary if (cSrcView->image()->info().colorSpace != cColorSpace) { @@ -876,14 +889,33 @@ namespace dxvk { uint64_t frameId = cRepeat ? 0 : cFrameId; - cDevice->presentImage(cPresenter, nullptr, frameId, nullptr); + cDevice->presentImage(cPresenter, cLatency, frameId, nullptr); }); m_parent->FlushCsChunk(); } + if (m_latencyTracker) { + if (status == VK_SUCCESS) + m_latencyTracker->notifyCpuPresentEnd(m_wctx->frameId); + else + m_latencyTracker->discardTimings(); + } + SyncFrameLatency(); + DxvkLatencyStats latencyStats = { }; + + if (m_latencyTracker && status == VK_SUCCESS) { + latencyStats = m_latencyTracker->getStatistics(m_wctx->frameId); + m_latencyTracker->sleepAndBeginFrame(m_wctx->frameId + 1, std::abs(m_targetFrameRate)); + + m_parent->BeginFrame(m_latencyTracker, m_wctx->frameId + 1u); + } + + if (m_latencyHud) + m_latencyHud->accumulateStats(latencyStats); + // Rotate swap chain buffers so that the back // buffer at index 0 becomes the front buffer. for (uint32_t i = 1; i < m_backBuffers.size(); i++) @@ -941,6 +973,9 @@ namespace dxvk { entry->second.frameLatencySignal = new sync::Fence(entry->second.frameId); entry->second.presenter = CreatePresenter(m_window, entry->second.frameLatencySignal); + + if (m_presentParams.hDeviceWindow == m_window && m_latencyTracking) + m_latencyTracker = m_device->createLatencyTracker(entry->second.presenter); } m_wctx = &entry->second; @@ -1017,6 +1052,10 @@ namespace dxvk { if (hud) { m_apiHud = hud->addItem("api", 1, GetApiName()); + + if (m_latencyTracking) + m_latencyHud = hud->addItem("latency", 4); + hud->addItem("samplers", -1, m_parent); hud->addItem("ffshaders", -1, m_parent); hud->addItem("swvp", -1, m_parent); @@ -1041,6 +1080,18 @@ namespace dxvk { } + void D3D9SwapChainEx::DestroyLatencyTracker() { + if (!m_latencyTracker) + return; + + m_parent->InjectCs([ + cTracker = std::move(m_latencyTracker) + ] (DxvkContext* ctx) { + ctx->endLatencyTracking(cTracker); + }); + } + + void D3D9SwapChainEx::UpdateTargetFrameRate(uint32_t SyncInterval) { double frameRateOption = double(m_parent->GetOptions()->maxFrameRate); double frameRate = std::max(frameRateOption, 0.0); @@ -1049,6 +1100,7 @@ namespace dxvk { frameRate = -m_displayRefreshRate / double(SyncInterval); m_wctx->presenter->setFrameRateLimit(frameRate, GetActualFrameLatency()); + m_targetFrameRate = frameRate; } diff --git a/src/d3d9/d3d9_swapchain.h b/src/d3d9/d3d9_swapchain.h index 9ab394975..9f69e44c5 100644 --- a/src/d3d9/d3d9_swapchain.h +++ b/src/d3d9/d3d9_swapchain.h @@ -69,7 +69,8 @@ namespace dxvk { D3D9SwapChainEx( D3D9DeviceEx* pDevice, D3DPRESENT_PARAMETERS* pPresentParams, - const D3DDISPLAYMODEEX* pFullscreenDisplayMode); + const D3DDISPLAYMODEEX* pFullscreenDisplayMode, + bool EnableLatencyTracking); ~D3D9SwapChainEx(); @@ -173,12 +174,17 @@ namespace dxvk { wsi::DxvkWindowState m_windowState; double m_displayRefreshRate = 0.0; + double m_targetFrameRate = 0.0; bool m_warnedAboutGDIFallback = false; VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + bool m_latencyTracking = false; + Rc m_latencyTracker = nullptr; + Rc m_apiHud; + Rc m_latencyHud; std::optional m_hdrMetadata; bool m_unlockAdditionalFormats = false; @@ -197,6 +203,8 @@ namespace dxvk { void CreateBlitter(); + void DestroyLatencyTracker(); + void InitRamp(); void UpdateTargetFrameRate(uint32_t SyncInterval);