From 06b44c62370ba1a0b33ccef98f0cee4525956c10 Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Sun, 12 Jan 2025 19:25:07 +0100
Subject: [PATCH] [dxvk] Rework HDR metadata updates

---
 src/d3d11/d3d11_swapchain.cpp | 12 ++----------
 src/d3d11/d3d11_swapchain.h   |  3 ---
 src/dxvk/dxvk_presenter.cpp   | 29 ++++++++++++++++++++++++++---
 src/dxvk/dxvk_presenter.h     | 11 ++++++++---
 4 files changed, 36 insertions(+), 19 deletions(-)

diff --git a/src/d3d11/d3d11_swapchain.cpp b/src/d3d11/d3d11_swapchain.cpp
index 7e489f147..2fc84ab49 100644
--- a/src/d3d11/d3d11_swapchain.cpp
+++ b/src/d3d11/d3d11_swapchain.cpp
@@ -322,10 +322,8 @@ namespace dxvk {
   HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetHDRMetaData(
     const DXGI_VK_HDR_METADATA*     pMetaData) {
     // For some reason this call always seems to succeed on Windows
-    if (pMetaData->Type == DXGI_HDR_METADATA_TYPE_HDR10) {
-      m_hdrMetadata = ConvertHDRMetadata(pMetaData->HDR10);
-      m_dirtyHdrMetadata = true;
-    }
+    if (pMetaData->Type == DXGI_HDR_METADATA_TYPE_HDR10)
+      m_presenter->setHdrMetadata(ConvertHDRMetadata(pMetaData->HDR10));
 
     return S_OK;
   }
@@ -402,11 +400,6 @@ namespace dxvk {
         break;
     }
 
-    if (m_hdrMetadata && m_dirtyHdrMetadata) {
-      m_presenter->setHdrMetadata(*m_hdrMetadata);
-      m_dirtyHdrMetadata = false;
-    }
-
     m_frameId += 1;
 
     // Present from CS thread so that we don't
@@ -501,7 +494,6 @@ namespace dxvk {
     m_device->waitForIdle();
 
     m_presentStatus.result = VK_SUCCESS;
-    m_dirtyHdrMetadata = true;
 
     PresenterDesc presenterDesc;
     presenterDesc.imageExtent     = { m_desc.Width, m_desc.Height };
diff --git a/src/d3d11/d3d11_swapchain.h b/src/d3d11/d3d11_swapchain.h
index 6766701f8..80e8dfb81 100644
--- a/src/d3d11/d3d11_swapchain.h
+++ b/src/d3d11/d3d11_swapchain.h
@@ -123,9 +123,6 @@ namespace dxvk {
 
     VkColorSpaceKHR           m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
 
-    std::optional<VkHdrMetadataEXT> m_hdrMetadata;
-    bool                      m_dirtyHdrMetadata = true;
-
     double                    m_targetFrameRate = 0.0;
 
     dxvk::mutex               m_frameStatisticsLock;
diff --git a/src/dxvk/dxvk_presenter.cpp b/src/dxvk/dxvk_presenter.cpp
index 224b8d97f..a97c60200 100644
--- a/src/dxvk/dxvk_presenter.cpp
+++ b/src/dxvk/dxvk_presenter.cpp
@@ -72,7 +72,18 @@ namespace dxvk {
 
     if (m_acquireStatus != VK_SUCCESS && m_acquireStatus != VK_SUBOPTIMAL_KHR)
       return m_acquireStatus;
-    
+
+    // Update HDR metadata after a successful acquire. We know
+    // that there won't be a present in flight at this point.
+    if (m_hdrMetadataDirty && m_hdrMetadata) {
+      m_hdrMetadataDirty = false;
+
+      if (m_device->features().extHdrMetadata) {
+        m_vkd->vkSetHdrMetadataEXT(m_vkd->device(),
+          1, &m_swapchain, &(*m_hdrMetadata));
+      }
+    }
+
     image = m_images.at(m_imageIndex);
     return m_acquireStatus;
   }
@@ -415,6 +426,8 @@ namespace dxvk {
     }
     
     // Invalidate indices
+    m_hdrMetadataDirty = true;
+
     m_imageIndex = 0;
     m_frameIndex = 0;
     m_acquireStatus = VK_NOT_READY;
@@ -464,8 +477,18 @@ namespace dxvk {
 
 
   void Presenter::setHdrMetadata(const VkHdrMetadataEXT& hdrMetadata) {
-    if (m_device->features().extHdrMetadata)
-      m_vkd->vkSetHdrMetadataEXT(m_vkd->device(), 1, &m_swapchain, &hdrMetadata);
+    if (m_hdrMetadata->sType != VK_STRUCTURE_TYPE_HDR_METADATA_EXT) {
+      m_hdrMetadata = std::nullopt;
+      return;
+    }
+
+    if (hdrMetadata.pNext)
+      Logger::warn("HDR metadata extensions not currently supported.");
+
+    m_hdrMetadata = hdrMetadata;
+    m_hdrMetadata->pNext = nullptr;
+
+    m_hdrMetadataDirty = true;
   }
 
 
diff --git a/src/dxvk/dxvk_presenter.h b/src/dxvk/dxvk_presenter.h
index e10ca91af..2da3a78ca 100644
--- a/src/dxvk/dxvk_presenter.h
+++ b/src/dxvk/dxvk_presenter.h
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <functional>
+#include <optional>
 #include <queue>
 #include <vector>
 
@@ -190,16 +191,17 @@ namespace dxvk {
     }
 
     /**
-     * \brief Checks if a presenter supports the colorspace
+     * \brief Checks support for a Vulkan color space
      *
-     * \param [in] colorspace The colorspace to test
-     * * \returns \c true if the presenter supports the colorspace
+     * \param [in] colorspace The color space to test
+     * \returns \c true if the Vulkan surface supports the colorspace
      */
     bool supportsColorSpace(VkColorSpaceKHR colorspace);
 
     /**
      * \brief Sets HDR metadata
      *
+     * Updated HDR metadata will be applied on the next \c acquire.
      * \param [in] hdrMetadata HDR Metadata
      */
     void setHdrMetadata(const VkHdrMetadataEXT& hdrMetadata);
@@ -230,6 +232,9 @@ namespace dxvk {
 
     VkResult                    m_acquireStatus = VK_NOT_READY;
 
+    std::optional<VkHdrMetadataEXT> m_hdrMetadata;
+    bool                        m_hdrMetadataDirty = false;
+
     alignas(CACHE_LINE_SIZE)
     dxvk::mutex                 m_frameMutex;
     dxvk::condition_variable    m_frameCond;