From aa2ec3f99810fea8f79bcf2c6016fa1b1afb1bea Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Tue, 11 Dec 2018 15:32:06 +0100
Subject: [PATCH] [dxgi] Port DxgiOutput and DxgiSwapChain to new monitor data
 API

This allows us to remove the dependency between DxgiSwapChain
and DxgiVkAdapter without losing gamma control emulation.
---
 src/dxgi/dxgi_output.cpp    | 79 ++++++++++++++++++-------------------
 src/dxgi/dxgi_output.h      |  1 +
 src/dxgi/dxgi_swapchain.cpp | 56 ++++++++++++++++++++------
 src/dxgi/dxgi_swapchain.h   |  6 ++-
 4 files changed, 88 insertions(+), 54 deletions(-)

diff --git a/src/dxgi/dxgi_output.cpp b/src/dxgi/dxgi_output.cpp
index f8908217..07ac6f13 100644
--- a/src/dxgi/dxgi_output.cpp
+++ b/src/dxgi/dxgi_output.cpp
@@ -19,21 +19,19 @@ namespace dxvk {
               HMONITOR      monitor)
   : m_adapter(adapter),
     m_monitor(monitor) {
-    // Init output data if necessary
-    if (FAILED(m_adapter->GetOutputData(m_monitor, nullptr))) {
-      DXGI_VK_OUTPUT_DATA outputData;
-      outputData.FrameStats = DXGI_FRAME_STATISTICS();
-      outputData.GammaCurve.Scale  = { 1.0f, 1.0f, 1.0f };
-      outputData.GammaCurve.Offset = { 0.0f, 0.0f, 0.0f };
-      
-      for (uint32_t i = 0; i < DXGI_VK_GAMMA_CP_COUNT; i++) {
-        const float value = GammaControlPointLocation(i);
-        outputData.GammaCurve.GammaCurve[i] = { value, value, value };
-      }
-      
-      outputData.GammaDirty = FALSE;
-      m_adapter->SetOutputData(m_monitor, &outputData);
+    // Init monitor info if necessary
+    DXGI_VK_MONITOR_DATA monitorData;
+    monitorData.pSwapChain = nullptr;
+    monitorData.FrameStats = DXGI_FRAME_STATISTICS();
+    monitorData.GammaCurve.Scale  = { 1.0f, 1.0f, 1.0f };
+    monitorData.GammaCurve.Offset = { 0.0f, 0.0f, 0.0f };
+    
+    for (uint32_t i = 0; i < DXGI_VK_GAMMA_CP_COUNT; i++) {
+      const float value = GammaControlPointLocation(i);
+      monitorData.GammaCurve.GammaCurve[i] = { value, value, value };
     }
+    
+    InitMonitorData(monitor, &monitorData);    
   }
   
   
@@ -330,27 +328,27 @@ namespace dxvk {
   
   
   HRESULT STDMETHODCALLTYPE DxgiOutput::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) {
-    DXGI_VK_OUTPUT_DATA outputData;
+    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
+    HRESULT hr = AcquireMonitorData(m_monitor, &monitorInfo);
+
+    if (FAILED(hr))
+      return hr;
     
-    if (FAILED(m_adapter->GetOutputData(m_monitor, &outputData))) {
-      Logger::err("DXGI: Failed to query output data");
-      return E_FAIL;
-    }
-    
-    *pStats = outputData.FrameStats;
+    *pStats = monitorInfo->FrameStats;
+    ReleaseMonitorData();
     return S_OK;
   }
   
   
   HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControl(DXGI_GAMMA_CONTROL* pArray) {
-    DXGI_VK_OUTPUT_DATA outputData;
+    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
+    HRESULT hr = AcquireMonitorData(m_monitor, &monitorInfo);
+
+    if (FAILED(hr))
+      return hr;
     
-    if (FAILED(m_adapter->GetOutputData(m_monitor, &outputData))) {
-      Logger::err("DXGI: Failed to query output data");
-      return E_FAIL;
-    }
-    
-    *pArray = outputData.GammaCurve;
+    *pArray = monitorInfo->GammaCurve;
+    ReleaseMonitorData();
     return S_OK;
   }
   
@@ -385,20 +383,21 @@ namespace dxvk {
   
   
   HRESULT STDMETHODCALLTYPE DxgiOutput::SetGammaControl(const DXGI_GAMMA_CONTROL* pArray) {
-    DXGI_VK_OUTPUT_DATA outputData;
+    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
+    HRESULT hr = AcquireMonitorData(m_monitor, &monitorInfo);
+
+    if (FAILED(hr))
+      return hr;
     
-    if (FAILED(m_adapter->GetOutputData(m_monitor, &outputData))) {
-      Logger::err("DXGI: Failed to query output data");
-      return E_FAIL;
+    monitorInfo->GammaCurve = *pArray;
+
+    if (monitorInfo->pSwapChain) {
+      hr = monitorInfo->pSwapChain->SetGammaControl(
+        DXGI_VK_GAMMA_CP_COUNT, pArray->GammaCurve);
     }
-    
-    outputData.GammaCurve = *pArray;
-    outputData.GammaDirty = TRUE;
-    
-    if (FAILED(m_adapter->SetOutputData(m_monitor, &outputData))) {
-      Logger::err("DXGI: Failed to update output data");
-      return E_FAIL;
-    } return S_OK;
+
+    ReleaseMonitorData();
+    return hr;
   }
   
   
diff --git a/src/dxgi/dxgi_output.h b/src/dxgi/dxgi_output.h
index bb9d5d97..e94fa3ed 100644
--- a/src/dxgi/dxgi_output.h
+++ b/src/dxgi/dxgi_output.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "dxgi_monitor.h"
 #include "dxgi_object.h"
 
 namespace dxvk {
diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp
index 6495711b..aebecf2e 100644
--- a/src/dxgi/dxgi_swapchain.cpp
+++ b/src/dxgi/dxgi_swapchain.cpp
@@ -47,6 +47,16 @@ namespace dxvk {
     
     if (SUCCEEDED(GetOutputFromMonitor(m_monitor, &output)))
       RestoreDisplayMode(output.ptr());
+
+    // Decouple swap chain from monitor if necessary
+    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
+    
+    if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorInfo))) {
+      if (monitorInfo->pSwapChain == this)
+        monitorInfo->pSwapChain = nullptr;
+      
+      ReleaseMonitorData();
+    }
   }
   
   
@@ -263,17 +273,6 @@ namespace dxvk {
     SyncInterval = std::min<UINT>(SyncInterval, 4);
 
     try {
-      // If in fullscreen mode, apply any updated gamma curve
-      // if it has been changed since the last present call.
-      DXGI_VK_OUTPUT_DATA outputData;
-      
-      if (SUCCEEDED(m_adapter->GetOutputData(m_monitor, &outputData)) && outputData.GammaDirty) {
-        m_presenter->SetGammaControl(DXGI_VK_GAMMA_CP_COUNT, outputData.GammaCurve.GammaCurve);
-        
-        outputData.GammaDirty = FALSE;
-        m_adapter->SetOutputData(m_monitor, &outputData);
-      }
-      
       return m_presenter->Present(SyncInterval, PresentFlags, nullptr);
     } catch (const DxvkError& err) {
       Logger::err(err.message());
@@ -481,10 +480,18 @@ namespace dxvk {
   }
 
 
-  HRESULT DxgiSwapChain::SetColorSpace1(DXGI_COLOR_SPACE_TYPE ColorSpace) {
+  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetColorSpace1(DXGI_COLOR_SPACE_TYPE ColorSpace) {
     Logger::err("DxgiSwapChain::SetColorSpace1: Not implemented");
     return E_NOTIMPL;
   }
+  
+  
+  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetGammaControl(
+          UINT                      NumPoints,
+    const DXGI_RGB*                 pGammaCurve) {
+    std::lock_guard<std::mutex> lockBuf(m_lockBuffer);
+    return m_presenter->SetGammaControl(NumPoints, pGammaCurve);
+  }
 
 
   VkExtent2D DxgiSwapChain::GetWindowSize() const {
@@ -560,6 +567,18 @@ namespace dxvk {
       SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);
     
     m_monitor = desc.Monitor;
+
+    // Apply current gamma curve of the output
+    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
+
+    if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorInfo))) {
+      if (!monitorInfo->pSwapChain)
+        monitorInfo->pSwapChain = this;
+      
+      SetGammaControl(DXGI_VK_GAMMA_CP_COUNT, monitorInfo->GammaCurve.GammaCurve);
+      ReleaseMonitorData();
+    }
+
     return S_OK;
   }
   
@@ -574,6 +593,17 @@ namespace dxvk {
      || FAILED(RestoreDisplayMode(output.ptr())))
       Logger::warn("DXGI: LeaveFullscreenMode: Failed to restore display mode");
     
+    // Reset gamma control and decouple swap chain from monitor
+    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
+
+    if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorInfo))) {
+      if (monitorInfo->pSwapChain == this)
+        monitorInfo->pSwapChain = nullptr;
+      
+      SetGammaControl(0, nullptr);
+      ReleaseMonitorData();
+    }
+    
     // Restore internal state
     m_descFs.Windowed = TRUE;
     m_monitor = nullptr;
@@ -596,7 +626,7 @@ namespace dxvk {
       rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
       SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE);
     
-    return m_presenter->SetGammaControl(0, nullptr);
+    return S_OK;
   }
   
   
diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h
index e6e5e77f..4b56f3ac 100644
--- a/src/dxgi/dxgi_swapchain.h
+++ b/src/dxgi/dxgi_swapchain.h
@@ -155,6 +155,10 @@ namespace dxvk {
     HRESULT STDMETHODCALLTYPE SetColorSpace1(
             DXGI_COLOR_SPACE_TYPE     ColorSpace) final;
     
+    HRESULT STDMETHODCALLTYPE SetGammaControl(
+            UINT                      NumPoints,
+      const DXGI_RGB*                 pGammaCurve);
+    
   private:
     
     struct WindowState {
@@ -167,7 +171,7 @@ namespace dxvk {
     std::mutex                      m_lockBuffer;
 
     Com<IDXGIFactory>               m_factory;
-    Com<DxgiAdapter>                m_adapter;
+    Com<IDXGIAdapter>               m_adapter;
     
     HWND                            m_window;
     DXGI_SWAP_CHAIN_DESC1           m_desc;