From 273e4abb1440ca9d5b9e7fdd3c85b86ab7dd2aa8 Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Fri, 17 Jan 2025 22:17:36 +0100
Subject: [PATCH] [dxvk] Add latency tracker to queue submissions

---
 src/d3d11/d3d11_swapchain.cpp |  2 +-
 src/d3d9/d3d9_swapchain.cpp   |  2 +-
 src/dxvk/dxvk_context.cpp     |  6 +++---
 src/dxvk/dxvk_device.cpp      | 17 +++++++++++++++--
 src/dxvk/dxvk_device.h        |  8 +++++++-
 src/dxvk/dxvk_queue.cpp       | 29 +++++++++++++++++++++++++++--
 src/dxvk/dxvk_queue.h         | 18 ++++++++++++++++++
 7 files changed, 72 insertions(+), 10 deletions(-)

diff --git a/src/d3d11/d3d11_swapchain.cpp b/src/d3d11/d3d11_swapchain.cpp
index 4566b1e8f..8178639f0 100644
--- a/src/d3d11/d3d11_swapchain.cpp
+++ b/src/d3d11/d3d11_swapchain.cpp
@@ -425,7 +425,7 @@ namespace dxvk {
       ctx->synchronizeWsi(cSync);
       ctx->flushCommandList(nullptr);
 
-      cDevice->presentImage(cPresenter, cFrameId, nullptr);
+      cDevice->presentImage(cPresenter, nullptr, cFrameId, nullptr);
     });
 
     if (m_backBuffers.size() > 1u)
diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp
index 8771e4894..de6604904 100644
--- a/src/d3d9/d3d9_swapchain.cpp
+++ b/src/d3d9/d3d9_swapchain.cpp
@@ -876,7 +876,7 @@ namespace dxvk {
 
         uint64_t frameId = cRepeat ? 0 : cFrameId;
 
-        cDevice->presentImage(cPresenter, frameId, nullptr);
+        cDevice->presentImage(cPresenter, nullptr, frameId, nullptr);
       });
 
       m_parent->FlushCsChunk();
diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp
index 1451226c0..dfb7760b7 100644
--- a/src/dxvk/dxvk_context.cpp
+++ b/src/dxvk/dxvk_context.cpp
@@ -111,9 +111,9 @@ namespace dxvk {
 
 
   void DxvkContext::flushCommandList(DxvkSubmitStatus* status) {
-    m_device->submitCommandList(
-      this->endRecording(), status);
-    
+    m_device->submitCommandList(this->endRecording(),
+      nullptr, 0, status);
+
     this->beginRecording(
       m_device->createCommandList());
   }
diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp
index c029949aa..e0df92ee7 100644
--- a/src/dxvk/dxvk_device.cpp
+++ b/src/dxvk/dxvk_device.cpp
@@ -318,12 +318,18 @@ namespace dxvk {
 
   void DxvkDevice::presentImage(
     const Rc<Presenter>&            presenter,
+    const Rc<DxvkLatencyTracker>&   tracker,
           uint64_t                  frameId,
           DxvkSubmitStatus*         status) {
     DxvkPresentInfo presentInfo = { };
     presentInfo.presenter = presenter;
     presentInfo.frameId = frameId;
-    m_submissionQueue.present(presentInfo, status);
+
+    DxvkLatencyInfo latencyInfo;
+    latencyInfo.tracker = tracker;
+    latencyInfo.frameId = frameId;
+
+    m_submissionQueue.present(presentInfo, latencyInfo, status);
     
     std::lock_guard<sync::Spinlock> statLock(m_statLock);
     m_statCounters.addCtr(DxvkStatCounter::QueuePresentCount, 1);
@@ -332,10 +338,17 @@ namespace dxvk {
 
   void DxvkDevice::submitCommandList(
     const Rc<DxvkCommandList>&      commandList,
+    const Rc<DxvkLatencyTracker>&   tracker,
+          uint64_t                  frameId,
           DxvkSubmitStatus*         status) {
     DxvkSubmitInfo submitInfo = { };
     submitInfo.cmdList = commandList;
-    m_submissionQueue.submit(submitInfo, status);
+
+    DxvkLatencyInfo latencyInfo;
+    latencyInfo.tracker = tracker;
+    latencyInfo.frameId = frameId;
+
+    m_submissionQueue.submit(submitInfo, latencyInfo, status);
 
     std::lock_guard<sync::Spinlock> statLock(m_statLock);
     m_statCounters.merge(commandList->statCounters());
diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h
index 5d8b4e37b..e06d92e36 100644
--- a/src/dxvk/dxvk_device.h
+++ b/src/dxvk/dxvk_device.h
@@ -496,11 +496,13 @@ namespace dxvk {
      * the submission thread. The status of this operation
      * can be retrieved with \ref waitForSubmission.
      * \param [in] presenter The presenter
-     * \param [in] frameId Optional frame ID
+     * \param [in] tracker Latency tracker
+     * \param [in] frameId Frame ID
      * \param [out] status Present status
      */
     void presentImage(
       const Rc<Presenter>&            presenter,
+      const Rc<DxvkLatencyTracker>&   tracker,
             uint64_t                  frameId,
             DxvkSubmitStatus*         status);
     
@@ -510,10 +512,14 @@ namespace dxvk {
      * Submits the given command list to the device using
      * the given set of optional synchronization primitives.
      * \param [in] commandList The command list to submit
+     * \param [in] tracker Latency tracker
+     * \param [in] frameId Frame ID
      * \param [out] status Submission feedback
      */
     void submitCommandList(
       const Rc<DxvkCommandList>&      commandList,
+      const Rc<DxvkLatencyTracker>&   tracker,
+            uint64_t                  frameId,
             DxvkSubmitStatus*         status);
 
     /**
diff --git a/src/dxvk/dxvk_queue.cpp b/src/dxvk/dxvk_queue.cpp
index d9d8b6c30..dcf5f900a 100644
--- a/src/dxvk/dxvk_queue.cpp
+++ b/src/dxvk/dxvk_queue.cpp
@@ -42,7 +42,10 @@ namespace dxvk {
   }
   
   
-  void DxvkSubmissionQueue::submit(DxvkSubmitInfo submitInfo, DxvkSubmitStatus* status) {
+  void DxvkSubmissionQueue::submit(
+          DxvkSubmitInfo            submitInfo,
+          DxvkLatencyInfo           latencyInfo,
+          DxvkSubmitStatus*         status) {
     std::unique_lock<dxvk::mutex> lock(m_mutex);
 
     m_finishCond.wait(lock, [this] {
@@ -52,18 +55,23 @@ namespace dxvk {
     DxvkSubmitEntry entry = { };
     entry.status = status;
     entry.submit = std::move(submitInfo);
+    entry.latency = std::move(latencyInfo);
 
     m_submitQueue.push(std::move(entry));
     m_appendCond.notify_all();
   }
 
 
-  void DxvkSubmissionQueue::present(DxvkPresentInfo presentInfo, DxvkSubmitStatus* status) {
+  void DxvkSubmissionQueue::present(
+          DxvkPresentInfo           presentInfo,
+          DxvkLatencyInfo           latencyInfo,
+          DxvkSubmitStatus*         status) {
     std::unique_lock<dxvk::mutex> lock(m_mutex);
 
     DxvkSubmitEntry entry = { };
     entry.status  = status;
     entry.present = std::move(presentInfo);
+    entry.latency = std::move(latencyInfo);
 
     m_submitQueue.push(std::move(entry));
     m_appendCond.notify_all();
@@ -142,10 +150,21 @@ namespace dxvk {
           m_callback(true);
 
         if (entry.submit.cmdList != nullptr) {
+          if (entry.latency.tracker)
+            entry.latency.tracker->notifyQueueSubmit(entry.latency.frameId);
+
           entry.result = entry.submit.cmdList->submit(m_semaphores, m_timelines);
           entry.timelines = m_timelines;
         } else if (entry.present.presenter != nullptr) {
+          if (entry.latency.tracker)
+            entry.latency.tracker->notifyQueuePresentBegin(entry.latency.frameId);
+
           entry.result = entry.present.presenter->presentImage(entry.present.frameId);
+
+          if (entry.latency.tracker) {
+            entry.latency.tracker->notifyQueuePresentEnd(
+              entry.latency.frameId, entry.result);
+          }
         }
 
         if (m_callback)
@@ -217,12 +236,18 @@ namespace dxvk {
           std::array<VkSemaphore, 2> semaphores = { m_semaphores.graphics, m_semaphores.transfer };
           std::array<uint64_t, 2> timelines = { entry.timelines.graphics, entry.timelines.transfer };
 
+          if (entry.latency.tracker)
+            entry.latency.tracker->notifyGpuExecutionBegin(entry.latency.frameId);
+
           VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO };
           waitInfo.semaphoreCount = semaphores.size();
           waitInfo.pSemaphores = semaphores.data();
           waitInfo.pValues = timelines.data();
 
           status = vk->vkWaitSemaphores(vk->device(), &waitInfo, ~0ull);
+
+          if (entry.latency.tracker && status == VK_SUCCESS)
+            entry.latency.tracker->notifyGpuExecutionEnd(entry.latency.frameId);
         }
 
         if (status != VK_SUCCESS) {
diff --git a/src/dxvk/dxvk_queue.h b/src/dxvk/dxvk_queue.h
index 8ef6a7148..4cc63ef60 100644
--- a/src/dxvk/dxvk_queue.h
+++ b/src/dxvk/dxvk_queue.h
@@ -7,6 +7,7 @@
 #include "../util/thread.h"
 
 #include "dxvk_cmdlist.h"
+#include "dxvk_latency.h"
 #include "dxvk_presenter.h"
 
 namespace dxvk {
@@ -47,6 +48,18 @@ namespace dxvk {
   };
 
 
+  /**
+   * \brief Latency info
+   *
+   * Optionally stores a latency tracker
+   * and the associated frame ID.
+   */
+  struct DxvkLatencyInfo {
+    Rc<DxvkLatencyTracker>  tracker;
+    uint64_t                frameId = 0;
+  };
+
+
   /**
    * \brief Submission queue entry
    */
@@ -55,6 +68,7 @@ namespace dxvk {
     DxvkSubmitStatus*   status;
     DxvkSubmitInfo      submit;
     DxvkPresentInfo     present;
+    DxvkLatencyInfo     latency;
     DxvkTimelineSemaphoreValues timelines;
   };
 
@@ -102,10 +116,12 @@ namespace dxvk {
      * dedicated submission thread. Use this to take
      * the submission overhead off the calling thread.
      * \param [in] submitInfo Submission parameters
+     * \param [in] latencyInfo Latency tracker info
      * \param [out] status Submission feedback
      */
     void submit(
             DxvkSubmitInfo      submitInfo,
+            DxvkLatencyInfo     latencyInfo,
             DxvkSubmitStatus*   status);
     
     /**
@@ -115,10 +131,12 @@ namespace dxvk {
      * and then presents the current swap chain image
      * of the presenter. May stall the calling thread.
      * \param [in] present Present parameters
+     * \param [in] latencyInfo Latency tracker info
      * \param [out] status Submission feedback
      */
     void present(
             DxvkPresentInfo     presentInfo,
+            DxvkLatencyInfo     latencyInfo,
             DxvkSubmitStatus*   status);
     
     /**