From 3fa3075867513b81faec44e06f0b47616b79060c Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Sat, 18 Jan 2025 00:23:24 +0100
Subject: [PATCH] [dxvk] Add latency tracker to presenter

---
 src/dxvk/dxvk_presenter.cpp | 22 ++++++++++++++++++----
 src/dxvk/dxvk_presenter.h   | 16 +++++++++++-----
 src/dxvk/dxvk_queue.cpp     |  3 ++-
 3 files changed, 31 insertions(+), 10 deletions(-)

diff --git a/src/dxvk/dxvk_presenter.cpp b/src/dxvk/dxvk_presenter.cpp
index c51bcc4e6..63e9b91c7 100644
--- a/src/dxvk/dxvk_presenter.cpp
+++ b/src/dxvk/dxvk_presenter.cpp
@@ -224,7 +224,10 @@ namespace dxvk {
   }
 
 
-  void Presenter::signalFrame(VkResult result, uint64_t frameId) {
+  void Presenter::signalFrame(
+          VkResult                result,
+          uint64_t                frameId,
+    const Rc<DxvkLatencyTracker>& tracker) {
     if (m_signal == nullptr || !frameId)
       return;
 
@@ -232,15 +235,19 @@ namespace dxvk {
       std::lock_guard<dxvk::mutex> lock(m_frameMutex);
 
       PresenterFrame frame = { };
-      frame.result = result;
-      frame.mode = m_presentMode;
-      frame.frameId = frameId;
+      frame.frameId   = frameId;
+      frame.tracker   = tracker;
+      frame.mode      = m_presentMode;
+      frame.result    = result;
 
       m_frameQueue.push(frame);
       m_frameCond.notify_one();
     } else {
       m_fpsLimiter.delay();
       m_signal->signal(frameId);
+
+      if (tracker)
+        tracker->notifyGpuPresentEnd(frameId);
     }
 
     m_lastFrameId.store(frameId, std::memory_order_release);
@@ -1054,6 +1061,13 @@ namespace dxvk {
           Logger::err(str::format("Presenter: vkWaitForPresentKHR failed: ", vr));
       }
 
+      // Signal latency tracker right away to get more accurate
+      // measurements if the frame rate limiter is enabled.
+      if (frame.tracker) {
+        frame.tracker->notifyGpuPresentEnd(frame.frameId);
+        frame.tracker = nullptr;
+      }
+
       // Apply FPS limtier here to align it as closely with scanout as we can,
       // and delay signaling the frame latency event to emulate behaviour of a
       // low refresh rate display as closely as we can.
diff --git a/src/dxvk/dxvk_presenter.h b/src/dxvk/dxvk_presenter.h
index 8fe7d9aae..633ca7753 100644
--- a/src/dxvk/dxvk_presenter.h
+++ b/src/dxvk/dxvk_presenter.h
@@ -18,6 +18,7 @@
 
 #include "dxvk_format.h"
 #include "dxvk_image.h"
+#include "dxvk_latency.h"
 
 namespace dxvk {
 
@@ -55,9 +56,10 @@ namespace dxvk {
    * \brief Queued frame
    */
   struct PresenterFrame {
-    uint64_t          frameId = 0u;
-    VkPresentModeKHR  mode    = VK_PRESENT_MODE_FIFO_KHR;
-    VkResult          result  = VK_NOT_READY;
+    uint64_t                frameId   = 0u;
+    Rc<DxvkLatencyTracker>  tracker   = nullptr;
+    VkPresentModeKHR        mode      = VK_PRESENT_MODE_FIFO_KHR;
+    VkResult                result    = VK_NOT_READY;
   };
 
   /**
@@ -135,9 +137,13 @@ namespace dxvk {
      * called before GPU work prior to the present submission has
      * completed in order to maintain consistency.
      * \param [in] result Presentation result
-     * \param [in] frameId Frame number
+     * \param [in] frameId Frame ID
+     * \param [in] tracker Latency tracker
      */
-    void signalFrame(VkResult result, uint64_t frameId);
+    void signalFrame(
+            VkResult                result,
+            uint64_t                frameId,
+      const Rc<DxvkLatencyTracker>& tracker);
 
     /**
      * \brief Changes sync interval
diff --git a/src/dxvk/dxvk_queue.cpp b/src/dxvk/dxvk_queue.cpp
index dcf5f900a..4e3855ba9 100644
--- a/src/dxvk/dxvk_queue.cpp
+++ b/src/dxvk/dxvk_queue.cpp
@@ -260,7 +260,8 @@ namespace dxvk {
         // Signal the frame and then immediately destroy the reference.
         // This is necessary since the front-end may want to explicitly
         // destroy the presenter object. 
-        entry.present.presenter->signalFrame(entry.result, entry.present.frameId);
+        entry.present.presenter->signalFrame(entry.result,
+          entry.present.frameId, entry.latency.tracker);
         entry.present.presenter = nullptr;
       }