diff --git a/src/d3d11/d3d11_swapchain.cpp b/src/d3d11/d3d11_swapchain.cpp index cb5679854..e2100eb8d 100644 --- a/src/d3d11/d3d11_swapchain.cpp +++ b/src/d3d11/d3d11_swapchain.cpp @@ -295,6 +295,9 @@ namespace dxvk { if (m_latencyHud) m_latencyHud->accumulateStats(latencyStats); + if (m_renderLatencyHud) + m_renderLatencyHud->updateLatencyTracker(m_latency); + return hr; } @@ -604,8 +607,14 @@ namespace dxvk { if (hud) { hud->addItem("api", 1, GetApiName()); - if (m_latency) + if (m_latency) { m_latencyHud = hud->addItem("latency", 4); + FramePacer* framePacer = dynamic_cast(m_latency.ptr()); + if (framePacer) { + int32_t fpsItemPos = hud->getItemPos(); + m_renderLatencyHud = hud->addItem("renderlatency", fpsItemPos+1); + } + } } m_blitter = new DxvkSwapchainBlitter(m_device, std::move(hud)); diff --git a/src/d3d11/d3d11_swapchain.h b/src/d3d11/d3d11_swapchain.h index 99f09450c..6a77c7351 100644 --- a/src/d3d11/d3d11_swapchain.h +++ b/src/d3d11/d3d11_swapchain.h @@ -125,7 +125,8 @@ namespace dxvk { dxvk::mutex m_frameStatisticsLock; DXGI_VK_FRAME_STATISTICS m_frameStatistics = { }; - Rc m_latencyHud; + Rc m_latencyHud; + Rc m_renderLatencyHud; Rc GetBackBufferView(); diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index 05466eca5..539b0815c 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -925,6 +925,9 @@ namespace dxvk { if (m_latencyHud) m_latencyHud->accumulateStats(latencyStats); + if (m_renderLatencyHud) + m_renderLatencyHud->updateLatencyTracker(m_latencyTracker); + // 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++) @@ -1062,8 +1065,14 @@ namespace dxvk { if (hud) { m_apiHud = hud->addItem("api", 1, GetApiName()); - if (m_latencyTracking) + if (m_latencyTracking) { m_latencyHud = hud->addItem("latency", 4); + FramePacer* framePacer = dynamic_cast(m_latencyTracker.ptr()); + if (framePacer) { + int32_t fpsItemPos = hud->getItemPos(); + m_renderLatencyHud = hud->addItem("renderlatency", fpsItemPos+1); + } + } hud->addItem("samplers", -1, m_parent); hud->addItem("ffshaders", -1, m_parent); diff --git a/src/d3d9/d3d9_swapchain.h b/src/d3d9/d3d9_swapchain.h index 6ea0d96cb..d06c388a9 100644 --- a/src/d3d9/d3d9_swapchain.h +++ b/src/d3d9/d3d9_swapchain.h @@ -183,8 +183,9 @@ namespace dxvk { bool m_latencyTracking = false; Rc m_latencyTracker = nullptr; - Rc m_apiHud; - Rc m_latencyHud; + Rc m_apiHud; + Rc m_latencyHud; + Rc m_renderLatencyHud; std::optional m_hdrMetadata; bool m_unlockAdditionalFormats = false; diff --git a/src/dxvk/hud/dxvk_hud.h b/src/dxvk/hud/dxvk_hud.h index 58c383f07..388cbf22a 100644 --- a/src/dxvk/hud/dxvk_hud.h +++ b/src/dxvk/hud/dxvk_hud.h @@ -59,6 +59,11 @@ namespace dxvk::hud { Rc addItem(const char* name, int32_t at, Args... args) { return m_hudItems.add(name, at, std::forward(args)...); } + + template + int32_t getItemPos() { + return m_hudItems.getItemPos(); + } /** * \brief Creates the HUD diff --git a/src/dxvk/hud/dxvk_hud_item.cpp b/src/dxvk/hud/dxvk_hud_item.cpp index f80ba77d4..138f1333f 100644 --- a/src/dxvk/hud/dxvk_hud_item.cpp +++ b/src/dxvk/hud/dxvk_hud_item.cpp @@ -1,4 +1,5 @@ #include "dxvk_hud_item.h" +#include "../framepacer/dxvk_framepacer.h" #include #include @@ -213,6 +214,63 @@ namespace dxvk::hud { } + HudRenderLatencyItem::HudRenderLatencyItem() { } + HudRenderLatencyItem::~HudRenderLatencyItem() { } + + void HudRenderLatencyItem::update(dxvk::high_resolution_clock::time_point time) { + // we cannot measure latency when fps-limiting is performed in Presenter::runFrameThread() + // because it's interfering with getting the right timestamp from vkWaitForPresent() + // if we truely wanted to measure it, we would need one additional thread + if (FpsLimiter::m_isActive) { + m_latency = "N/A"; + return; + } + + const Rc tracker = m_tracker; + const FramePacer* framePacer = dynamic_cast( tracker.ptr() ); + if (!framePacer) + return; + + auto elapsed = std::chrono::duration_cast(time - m_lastUpdate); + + if (elapsed.count() >= UpdateInterval) { + m_lastUpdate = time; + + LatencyMarkersReader reader = framePacer->m_latencyMarkersStorage.getReader(100); + const LatencyMarkers* markers; + uint32_t count = 0; + uint64_t totalLatency = 0; + while (reader.getNext(markers)) { + totalLatency += markers->presentFinished; + ++count; + } + + if (!count) + return; + + uint64_t latency = totalLatency / count; + m_latency = str::format(latency / 1000, ".", (latency/100) % 10, " ms"); + } + } + + + HudPos HudRenderLatencyItem::render( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + + position.y += 12; + renderer.drawText(16, position, 0xff4040ffu, "Render latency:"); + renderer.drawText(16, { position.x + 195, position.y }, + 0xffffffffu, m_latency); + + position.y += 8; + return position; + } + + HudFrameTimeItem::HudFrameTimeItem(const Rc& device, HudRenderer* renderer) : m_device (device), m_gfxSetLayout (createDescriptorSetLayout()), diff --git a/src/dxvk/hud/dxvk_hud_item.h b/src/dxvk/hud/dxvk_hud_item.h index 866849b4e..fab3fc97f 100644 --- a/src/dxvk/hud/dxvk_hud_item.h +++ b/src/dxvk/hud/dxvk_hud_item.h @@ -131,6 +131,15 @@ namespace dxvk::hud { return value; } + template + int32_t getItemPos() { + for (int i=0; i<(int)m_items.size(); ++i) { + if (dynamic_cast(m_items[i].ptr())) + return i; + } + return -1; + } + private: bool m_enableFull = false; @@ -244,6 +253,42 @@ namespace dxvk::hud { }; + /** + * \brief HUD item to display render latency + */ + class HudRenderLatencyItem : public HudItem { + constexpr static int64_t UpdateInterval = 500'000; + public: + + HudRenderLatencyItem(); + + ~HudRenderLatencyItem(); + + void updateLatencyTracker( const Rc& tracker ) { + m_tracker = tracker; + } + + void update(dxvk::high_resolution_clock::time_point time); + + HudPos render( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); + + private: + + Rc m_tracker; + + dxvk::high_resolution_clock::time_point m_lastUpdate + = dxvk::high_resolution_clock::now(); + + std::string m_latency; + + }; + + /** * \brief HUD item to display the frame rate */