From a690210eae7d7eba64567bd164821d055273358b Mon Sep 17 00:00:00 2001
From: Joshua Ashton <joshua@froggi.es>
Date: Tue, 30 Mar 2021 11:47:15 +0100
Subject: [PATCH] [dxgi] Implement swapchain dispatcher

Some overlays and hooks check if the vtable funcs reside in the dxgi.dll module.

Fixes: #1996
---
 src/dxgi/dxgi_factory.cpp            |  14 +-
 src/dxgi/dxgi_swapchain_dispatcher.h | 280 +++++++++++++++++++++++++++
 2 files changed, 292 insertions(+), 2 deletions(-)
 create mode 100644 src/dxgi/dxgi_swapchain_dispatcher.h

diff --git a/src/dxgi/dxgi_factory.cpp b/src/dxgi/dxgi_factory.cpp
index 00510462..f159999d 100644
--- a/src/dxgi/dxgi_factory.cpp
+++ b/src/dxgi/dxgi_factory.cpp
@@ -1,5 +1,6 @@
 #include "dxgi_factory.h"
 #include "dxgi_swapchain.h"
+#include "dxgi_swapchain_dispatcher.h"
 
 namespace dxvk {
 
@@ -130,9 +131,18 @@ namespace dxvk {
     if (SUCCEEDED(pDevice->QueryInterface(
           __uuidof(IWineDXGISwapChainFactory),
           reinterpret_cast<void**>(&wineDevice)))) {
-      return wineDevice->CreateSwapChainForHwnd(
+      IDXGISwapChain4* frontendSwapChain;
+
+      HRESULT hr = wineDevice->CreateSwapChainForHwnd(
         this, hWnd, pDesc, pFullscreenDesc,
-        pRestrictToOutput, ppSwapChain);
+        pRestrictToOutput, reinterpret_cast<IDXGISwapChain1**>(&frontendSwapChain));
+
+      // No ref as that's handled by the object we're wrapping
+      // which was ref'ed on creation.
+      if (SUCCEEDED(hr))
+        *ppSwapChain = new DxgiSwapChainDispatcher(frontendSwapChain);
+
+      return hr;
     }
     
     Logger::err("DXGI: CreateSwapChainForHwnd: Unsupported device type");
diff --git a/src/dxgi/dxgi_swapchain_dispatcher.h b/src/dxgi/dxgi_swapchain_dispatcher.h
new file mode 100644
index 00000000..fd48d837
--- /dev/null
+++ b/src/dxgi/dxgi_swapchain_dispatcher.h
@@ -0,0 +1,280 @@
+#pragma once
+
+#include "dxgi_swapchain.h"
+
+namespace dxvk {
+
+  class DxgiSwapChainDispatcher : public IDXGISwapChain4 {
+
+  public:
+
+    DxgiSwapChainDispatcher(IDXGISwapChain4* dispatch)
+      : m_dispatch(dispatch) {
+    }
+
+    virtual ~DxgiSwapChainDispatcher() {
+    }
+
+    ULONG STDMETHODCALLTYPE AddRef() {
+      return m_dispatch->AddRef();
+    }
+
+    ULONG STDMETHODCALLTYPE Release() {
+      ULONG refCount = m_dispatch->Release();
+
+      if (unlikely(!refCount))
+        delete this;
+
+      return refCount;
+    }
+
+    HRESULT STDMETHODCALLTYPE QueryInterface(
+            REFIID                    riid,
+            void**                    ppvObject) final {
+      if (ppvObject == nullptr)
+        return E_POINTER;
+
+      *ppvObject = nullptr;
+
+      if (riid == __uuidof(IUnknown)
+       || riid == __uuidof(IDXGIObject)
+       || riid == __uuidof(IDXGIDeviceSubObject)
+       || riid == __uuidof(IDXGISwapChain)
+       || riid == __uuidof(IDXGISwapChain1)
+       || riid == __uuidof(IDXGISwapChain2)
+       || riid == __uuidof(IDXGISwapChain3)
+       || riid == __uuidof(IDXGISwapChain4)) {
+        *ppvObject = ref(this);
+        return S_OK;
+      }
+
+      Logger::warn("DxgiSwapChainDispatcher::QueryInterface: Unknown interface query");
+      Logger::warn(str::format(riid));
+      return m_dispatch->QueryInterface(riid, ppvObject);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetPrivateData(
+            REFGUID       Name,
+            UINT*         pDataSize,
+            void*         pData) final {
+      return m_dispatch->GetPrivateData(Name, pDataSize, pData);
+    }
+
+    HRESULT STDMETHODCALLTYPE SetPrivateData(
+            REFGUID       Name,
+            UINT          DataSize,
+      const void*         pData) final {
+      return m_dispatch->SetPrivateData(Name, DataSize, pData);
+    }
+
+    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
+            REFGUID       Name,
+      const IUnknown*     pUnknown) final {
+      return m_dispatch->SetPrivateDataInterface(Name, pUnknown);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetParent(
+            REFIID                    riid,
+            void**                    ppParent) final {
+      return m_dispatch->GetParent(riid, ppParent);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetDevice(
+            REFIID                    riid,
+            void**                    ppDevice) final {
+      return m_dispatch->GetDevice(riid, ppDevice);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetBuffer(
+            UINT                      Buffer,
+            REFIID                    riid,
+            void**                    ppSurface) final {
+      return m_dispatch->GetBuffer(Buffer, riid, ppSurface);
+    }
+
+    UINT STDMETHODCALLTYPE GetCurrentBackBufferIndex() final {
+      return m_dispatch->GetCurrentBackBufferIndex();
+    }
+
+    HRESULT STDMETHODCALLTYPE GetContainingOutput(
+            IDXGIOutput**             ppOutput) final {
+      return m_dispatch->GetContainingOutput(ppOutput);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetDesc(
+            DXGI_SWAP_CHAIN_DESC*     pDesc) final {
+      return m_dispatch->GetDesc(pDesc);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetDesc1(
+            DXGI_SWAP_CHAIN_DESC1*    pDesc) final {
+      return m_dispatch->GetDesc1(pDesc);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetFullscreenState(
+            BOOL*                     pFullscreen,
+            IDXGIOutput**             ppTarget) final {
+      return m_dispatch->GetFullscreenState(pFullscreen, ppTarget);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetFullscreenDesc(
+            DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) final {
+      return m_dispatch->GetFullscreenDesc(pDesc);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetHwnd(
+            HWND*                     pHwnd) final {
+      return m_dispatch->GetHwnd(pHwnd);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetCoreWindow(
+            REFIID                    refiid,
+            void**                    ppUnk) final {
+      return m_dispatch->GetCoreWindow(refiid, ppUnk);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetBackgroundColor(
+            DXGI_RGBA*                pColor) final {
+      return m_dispatch->GetBackgroundColor(pColor);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetRotation(
+            DXGI_MODE_ROTATION*       pRotation) final {
+      return m_dispatch->GetRotation(pRotation);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetRestrictToOutput(
+            IDXGIOutput**             ppRestrictToOutput) final {
+      return m_dispatch->GetRestrictToOutput(ppRestrictToOutput);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetFrameStatistics(
+            DXGI_FRAME_STATISTICS*    pStats) final {
+      return m_dispatch->GetFrameStatistics(pStats);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetLastPresentCount(
+            UINT*                     pLastPresentCount) final {
+      return m_dispatch->GetLastPresentCount(pLastPresentCount);
+    }
+
+    BOOL STDMETHODCALLTYPE IsTemporaryMonoSupported() final {
+      return m_dispatch->IsTemporaryMonoSupported();
+    }
+
+    HRESULT STDMETHODCALLTYPE Present(
+            UINT                      SyncInterval,
+            UINT                      Flags) final {
+      return m_dispatch->Present(SyncInterval, Flags);
+    }
+
+    HRESULT STDMETHODCALLTYPE Present1(
+            UINT                      SyncInterval,
+            UINT                      PresentFlags,
+      const DXGI_PRESENT_PARAMETERS*  pPresentParameters) final {
+      return m_dispatch->Present1(SyncInterval, PresentFlags, pPresentParameters);
+    }
+
+    HRESULT STDMETHODCALLTYPE ResizeBuffers(
+            UINT                      BufferCount,
+            UINT                      Width,
+            UINT                      Height,
+            DXGI_FORMAT               NewFormat,
+            UINT                      SwapChainFlags) final {
+      return m_dispatch->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags);
+    }
+
+    HRESULT STDMETHODCALLTYPE ResizeBuffers1(
+            UINT                      BufferCount,
+            UINT                      Width,
+            UINT                      Height,
+            DXGI_FORMAT               Format,
+            UINT                      SwapChainFlags,
+      const UINT*                     pCreationNodeMask,
+            IUnknown* const*          ppPresentQueue) final {
+      return m_dispatch->ResizeBuffers1(BufferCount, Width, Height, Format, SwapChainFlags, pCreationNodeMask, ppPresentQueue);
+    }
+
+    HRESULT STDMETHODCALLTYPE ResizeTarget(
+      const DXGI_MODE_DESC*           pNewTargetParameters) final {
+      return m_dispatch->ResizeTarget(pNewTargetParameters);
+    }
+
+    HRESULT STDMETHODCALLTYPE SetFullscreenState(
+            BOOL                      Fullscreen,
+            IDXGIOutput*              pTarget) final {
+      return m_dispatch->SetFullscreenState(Fullscreen, pTarget);
+    }
+
+
+    HRESULT STDMETHODCALLTYPE SetBackgroundColor(
+      const DXGI_RGBA*                pColor) final {
+      return m_dispatch->SetBackgroundColor(pColor);
+    }
+
+    HRESULT STDMETHODCALLTYPE SetRotation(
+            DXGI_MODE_ROTATION        Rotation) final {
+      return m_dispatch->SetRotation(Rotation);
+    }
+
+    HANDLE STDMETHODCALLTYPE GetFrameLatencyWaitableObject() final {
+      return m_dispatch->GetFrameLatencyWaitableObject();
+    }
+
+    HRESULT STDMETHODCALLTYPE GetMatrixTransform(
+            DXGI_MATRIX_3X2_F*        pMatrix) final {
+      return m_dispatch->GetMatrixTransform(pMatrix);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency(
+            UINT*                     pMaxLatency) final {
+      return m_dispatch->GetMaximumFrameLatency(pMaxLatency);
+    }
+
+    HRESULT STDMETHODCALLTYPE GetSourceSize(
+            UINT*                     pWidth,
+            UINT*                     pHeight) final {
+      return m_dispatch->GetSourceSize(pWidth, pHeight);
+    }
+
+    HRESULT STDMETHODCALLTYPE SetMatrixTransform(
+      const DXGI_MATRIX_3X2_F*        pMatrix) final {
+      return m_dispatch->SetMatrixTransform(pMatrix);
+    }
+
+    HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency(
+            UINT                      MaxLatency) final {
+      return m_dispatch->SetMaximumFrameLatency(MaxLatency);
+    }
+
+    HRESULT STDMETHODCALLTYPE SetSourceSize(
+            UINT                      Width,
+            UINT                      Height) final {
+      return m_dispatch->SetSourceSize(Width, Height);
+    }
+
+    HRESULT STDMETHODCALLTYPE CheckColorSpaceSupport(
+            DXGI_COLOR_SPACE_TYPE     ColorSpace,
+            UINT*                     pColorSpaceSupport) final {
+      return m_dispatch->CheckColorSpaceSupport(ColorSpace, pColorSpaceSupport);
+    }
+
+    HRESULT STDMETHODCALLTYPE SetColorSpace1(
+            DXGI_COLOR_SPACE_TYPE     ColorSpace) final {
+      return m_dispatch->SetColorSpace1(ColorSpace);
+    }
+
+    HRESULT STDMETHODCALLTYPE SetHDRMetaData(
+            DXGI_HDR_METADATA_TYPE    Type,
+            UINT                      Size,
+            void*                     pMetaData) final {
+      return m_dispatch->SetHDRMetaData(Type, Size, pMetaData);
+    }
+
+  private:
+
+    IDXGISwapChain4* m_dispatch;
+
+  };
+
+}