diff --git a/FSR3UnityPlugin.vcxproj b/FSR3UnityPlugin.vcxproj
index 8c9bf42..d2adce9 100644
--- a/FSR3UnityPlugin.vcxproj
+++ b/FSR3UnityPlugin.vcxproj
@@ -124,7 +124,7 @@
Windows
true
false
- $(CoreLibraryDependencies);%(AdditionalDependencies);lib\ffx_fsr2_api\ffx_fsr2_api_x64d.lib;lib\ffx_fsr2_api\ffx_fsr2_api_dx11_x64d.lib;dxguid.lib;vulkan-1.lib
+ $(CoreLibraryDependencies);%(AdditionalDependencies);lib\ffx_fsr2_api\ffx_fsr2_api_x64d.lib;lib\ffx_fsr2_api\ffx_fsr2_api_dx11_x64d.lib;dxguid.lib;vulkan-1.lib;dxgi.lib
%VULKAN_SDK%\Lib;%(AdditionalLibraryDirectories)
@@ -155,8 +155,10 @@
+
+
diff --git a/FSR3UnityPlugin.vcxproj.filters b/FSR3UnityPlugin.vcxproj.filters
index 990e195..1a0ac66 100644
--- a/FSR3UnityPlugin.vcxproj.filters
+++ b/FSR3UnityPlugin.vcxproj.filters
@@ -27,6 +27,9 @@
Source Files
+
+ Source Files
+
@@ -50,5 +53,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/include/SwapChainTrampoline.h b/include/SwapChainTrampoline.h
new file mode 100644
index 0000000..2de220c
--- /dev/null
+++ b/include/SwapChainTrampoline.h
@@ -0,0 +1,92 @@
+#pragma once
+
+#include
+#include
+
+class SwapChainTrampoline : public IDXGISwapChain4
+{
+public:
+ SwapChainTrampoline(IDXGISwapChain4* swapChain);
+
+ HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject) override;
+
+ ULONG __stdcall AddRef(void) override;
+
+ ULONG __stdcall Release(void) override;
+
+ HRESULT __stdcall SetPrivateData(REFGUID Name, UINT DataSize, const void* pData) override;
+
+ HRESULT __stdcall SetPrivateDataInterface(REFGUID Name, const IUnknown* pUnknown) override;
+
+ HRESULT __stdcall GetPrivateData(REFGUID Name, UINT* pDataSize, void* pData) override;
+
+ HRESULT __stdcall GetParent(REFIID riid, void** ppParent) override;
+
+ HRESULT __stdcall GetDevice(REFIID riid, void** ppDevice) override;
+
+ HRESULT __stdcall Present(UINT SyncInterval, UINT Flags) override;
+
+ HRESULT __stdcall GetBuffer(UINT Buffer, REFIID riid, void** ppSurface) override;
+
+ HRESULT __stdcall SetFullscreenState(BOOL Fullscreen, IDXGIOutput* pTarget) override;
+
+ HRESULT __stdcall GetFullscreenState(BOOL* pFullscreen, IDXGIOutput** ppTarget) override;
+
+ HRESULT __stdcall GetDesc(DXGI_SWAP_CHAIN_DESC* pDesc) override;
+
+ HRESULT __stdcall ResizeBuffers(UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) override;
+
+ HRESULT __stdcall ResizeTarget(const DXGI_MODE_DESC* pNewTargetParameters) override;
+
+ HRESULT __stdcall GetContainingOutput(IDXGIOutput** ppOutput) override;
+
+ HRESULT __stdcall GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) override;
+
+ HRESULT __stdcall GetLastPresentCount(UINT* pLastPresentCount) override;
+
+ HRESULT __stdcall GetDesc1(DXGI_SWAP_CHAIN_DESC1* pDesc) override;
+
+ HRESULT __stdcall GetFullscreenDesc(DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) override;
+
+ HRESULT __stdcall GetHwnd(HWND* pHwnd) override;
+
+ HRESULT __stdcall GetCoreWindow(REFIID refiid, void** ppUnk) override;
+
+ HRESULT __stdcall Present1(UINT SyncInterval, UINT PresentFlags, const DXGI_PRESENT_PARAMETERS* pPresentParameters) override;
+
+ BOOL __stdcall IsTemporaryMonoSupported(void) override;
+
+ HRESULT __stdcall GetRestrictToOutput(IDXGIOutput** ppRestrictToOutput) override;
+
+ HRESULT __stdcall SetBackgroundColor(const DXGI_RGBA* pColor) override;
+
+ HRESULT __stdcall GetBackgroundColor(DXGI_RGBA* pColor) override;
+
+ HRESULT __stdcall SetRotation(DXGI_MODE_ROTATION Rotation) override;
+
+ HRESULT __stdcall GetRotation(DXGI_MODE_ROTATION* pRotation) override;
+
+ HRESULT __stdcall SetSourceSize(UINT Width, UINT Height) override;
+
+ HRESULT __stdcall GetSourceSize(UINT* pWidth, UINT* pHeight) override;
+
+ HRESULT __stdcall SetMaximumFrameLatency(UINT MaxLatency) override;
+
+ HRESULT __stdcall GetMaximumFrameLatency(UINT* pMaxLatency) override;
+
+ HANDLE __stdcall GetFrameLatencyWaitableObject(void) override;
+
+ HRESULT __stdcall SetMatrixTransform(const DXGI_MATRIX_3X2_F* pMatrix) override;
+
+ HRESULT __stdcall GetMatrixTransform(DXGI_MATRIX_3X2_F* pMatrix) override;
+
+ UINT __stdcall GetCurrentBackBufferIndex(void) override;
+
+ HRESULT __stdcall CheckColorSpaceSupport(DXGI_COLOR_SPACE_TYPE ColorSpace, UINT* pColorSpaceSupport) override;
+
+ HRESULT __stdcall SetColorSpace1(DXGI_COLOR_SPACE_TYPE ColorSpace) override;
+
+ HRESULT __stdcall ResizeBuffers1(UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT Format, UINT SwapChainFlags, const UINT* pCreationNodeMask, IUnknown* const* ppPresentQueue) override;
+
+ HRESULT __stdcall SetHDRMetaData(DXGI_HDR_METADATA_TYPE Type, UINT Size, void* pMetaData) override;
+};
diff --git a/src/FSR3UnityPlugin.cpp b/src/FSR3UnityPlugin.cpp
index fe8ae1b..3ef108d 100644
--- a/src/FSR3UnityPlugin.cpp
+++ b/src/FSR3UnityPlugin.cpp
@@ -9,6 +9,8 @@
#include "FSR3Upscaler_DX12.h"
#include "FSR3Upscaler_Vulkan.h"
+#include "SwapChainTrampoline.h"
+
static const int32_t BaseEventId = 313;
static IUnityInterfaces* s_UnityInterfaces = nullptr;
@@ -22,6 +24,8 @@ static void UNITY_INTERFACE_API OnSetTextureEvent(int eventID, void* data);
static IUpscaler* s_Upscaler = nullptr;
+static IUnityGraphicsD3D12v7* s_GraphicsD3D12 = nullptr;
+
// Unity plugin load event
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
@@ -78,6 +82,8 @@ static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType ev
return;
}
+ s_GraphicsD3D12 = graphicsD3D12;
+
s_Upscaler = new FSR3Upscaler_DX12(s_Log, graphicsD3D12);
UnityD3D12PluginEventConfig eventConfig{};
@@ -85,6 +91,12 @@ static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType ev
eventConfig.flags = kUnityD3D12EventConfigFlag_EnsurePreviousFrameSubmission | kUnityD3D12EventConfigFlag_FlushCommandBuffers | kUnityD3D12EventConfigFlag_SyncWorkerThreads | kUnityD3D12EventConfigFlag_ModifiesCommandBuffersState;
eventConfig.ensureActiveRenderTextureIsBound = false;
graphicsD3D12->ConfigureEvent(BaseEventId + FSR3PluginEvent::eExecute, &eventConfig);
+
+ UnityD3D12PluginEventConfig eventConfigSC{};
+ eventConfigSC.graphicsQueueAccess = kUnityD3D12GraphicsQueueAccess_Allow;
+ eventConfigSC.flags = kUnityD3D12EventConfigFlag_EnsurePreviousFrameSubmission | kUnityD3D12EventConfigFlag_FlushCommandBuffers | kUnityD3D12EventConfigFlag_SyncWorkerThreads;
+ eventConfigSC.ensureActiveRenderTextureIsBound = false;
+ graphicsD3D12->ConfigureEvent(BaseEventId + FSR3PluginEvent::eSwapChain, &eventConfigSC);
break;
}
case kUnityGfxRendererVulkan:
@@ -131,6 +143,82 @@ static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType ev
};
}
+static void TestSwapChainStuff()
+{
+ if (!s_GraphicsD3D12)
+ return;
+
+ UNITY_LOG_ERROR(s_Log, "Testing SwapChain...");
+ auto* swapChain = s_GraphicsD3D12->GetSwapChain();
+ if (!swapChain)
+ {
+ UNITY_LOG_ERROR(s_Log, "Did not get SwapChain :(");
+ return;
+ }
+
+ HRESULT hr;
+ IDXGISwapChain1* swapChain1;
+ hr = swapChain->QueryInterface(&swapChain1);
+ if (!SUCCEEDED(hr))
+ {
+ UNITY_LOG_ERROR(s_Log, "Did not get SwapChain1 :(");
+ return;
+ }
+
+ DXGI_SWAP_CHAIN_DESC1 swapChainDesc;
+ hr = swapChain1->GetDesc1(&swapChainDesc);
+ if (!SUCCEEDED(hr))
+ {
+ UNITY_LOG_ERROR(s_Log, "Did not get SwapChain descriptor :(");
+ return;
+ }
+
+ bool fullscreen = false;
+ DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullScreenDesc;
+ hr = swapChain1->GetFullscreenDesc(&fullScreenDesc);
+ if (SUCCEEDED(hr))
+ {
+ fullscreen = true;
+ }
+
+ HWND hwnd = nullptr;
+ swapChain1->GetHwnd(&hwnd);
+
+ std::stringstream ss;
+ ss << "SwapChain: HWND = " << hwnd << ", width = " << swapChainDesc.Width << ", height = " << swapChainDesc.Height << ", format = " << swapChainDesc.Format << ", flags = " << swapChainDesc.Flags;
+ UNITY_LOG_ERROR(s_Log, ss.str().c_str());
+
+ IDXGISwapChain4* swapChain4;
+ hr = swapChain->QueryInterface(&swapChain4);
+ if (!SUCCEEDED(hr))
+ {
+ UNITY_LOG_ERROR(s_Log, "Did not get SwapChain4 :(");
+ return;
+ }
+
+ SwapChainTrampoline trampoline(swapChain4);
+ memcpy(swapChain4, &trampoline, sizeof(IDXGISwapChain4));
+
+ //IDXGIFactory2* dxgiFactory;
+ //hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&dxgiFactory));
+ //if (!SUCCEEDED(hr))
+ //{
+ // UNITY_LOG_ERROR(s_Log, "Could not create DXGI Factory :(");
+ // return;
+ //}
+
+ //// TODO: may need to do this from an event with kUnityD3D12GraphicsQueueAccess_Allow, to allow access to GetCommandQueue()
+ //IDXGISwapChain1* replacementSwapChain;
+ //hr = dxgiFactory->CreateSwapChainForHwnd(s_GraphicsD3D12->GetCommandQueue(), hwnd, &swapChainDesc, fullscreen ? &fullScreenDesc : nullptr, nullptr, &replacementSwapChain);
+ //if (!SUCCEEDED(hr))
+ //{
+ // std::stringstream ss;
+ // ss << "Could not create replacement swapchain, error code = " << hr;
+ // UNITY_LOG_ERROR(s_Log, ss.str().c_str());
+ // return;
+ //}
+}
+
extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_InitApi()
{
if (s_Upscaler != nullptr)
@@ -240,6 +328,9 @@ static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data)
s_Upscaler->InitFeature(initData);
break;
}
+ case BaseEventId + FSR3PluginEvent::eSwapChain:
+ TestSwapChainStuff();
+ break;
}
}
diff --git a/src/FSR3UnityTypes.h b/src/FSR3UnityTypes.h
index 4f0a463..e8187dc 100644
--- a/src/FSR3UnityTypes.h
+++ b/src/FSR3UnityTypes.h
@@ -5,7 +5,8 @@ enum FSR3PluginEvent : int32_t
eDestroyFeature,
eExecute,
ePostExecute,
- eInit
+ eInit,
+ eSwapChain
};
enum FSR3Quality: int32_t
diff --git a/src/SwapChainTrampoline.cpp b/src/SwapChainTrampoline.cpp
new file mode 100644
index 0000000..0472f77
--- /dev/null
+++ b/src/SwapChainTrampoline.cpp
@@ -0,0 +1,227 @@
+#include "SwapChainTrampoline.h"
+
+// There can be ever only one swap chain per window, so it's safe to make this a singleton
+static IDXGISwapChain4* s_SwapChain = nullptr;
+
+typedef HRESULT(__stdcall IDXGISwapChain4::*PFN_QueryInterface)(REFIID riid, void** ppvObject);
+PFN_QueryInterface pfQueryInterface = nullptr;
+
+SwapChainTrampoline::SwapChainTrampoline(IDXGISwapChain4* swapChain)
+{
+ s_SwapChain = swapChain;
+ // TODO: copy the original swap chain's vtable (sizeof IDXGISwapChain4), then somehow call the virtual methods in that vtable using the this-pointer from ourselves...
+
+ pfQueryInterface = (PFN_QueryInterface)((intptr_t)swapChain + reinterpret_cast(&SwapChainTrampoline::QueryInterface));
+
+ // IDEA: we could replace the original swap chain's vtable entries one-by-one with regular function pointers, including a this-pointer argument
+ // Unity will then give us the this-pointer we require, no need to globally store it here
+ // (We might not even require the this-pointer in the long run anyway, since we'll be forwarding to the FG swapchain)
+ // Then forward the call to the original vtable function, reusing that this-pointer where necessary
+ // Only thing we really need to know for sure: the offset in the vtable of each virtual function
+ // This doesn't feel like it would work but try it anyway: https://stackoverflow.com/questions/5590015/detect-the-the-vtable-offset-of-a-specific-virtual-function-using-visual-c/5590181
+}
+
+HRESULT __stdcall SwapChainTrampoline::QueryInterface(REFIID riid, void** ppvObject)
+{
+ return s_SwapChain->QueryInterface(riid, ppvObject);
+}
+
+ULONG __stdcall SwapChainTrampoline::AddRef(void)
+{
+ return s_SwapChain->AddRef();
+}
+
+ULONG __stdcall SwapChainTrampoline::Release(void)
+{
+ return s_SwapChain->Release();
+}
+
+HRESULT __stdcall SwapChainTrampoline::SetPrivateData(REFGUID Name, UINT DataSize, const void* pData)
+{
+ return s_SwapChain->SetPrivateData(Name, DataSize, pData);
+}
+
+HRESULT __stdcall SwapChainTrampoline::SetPrivateDataInterface(REFGUID Name, const IUnknown* pUnknown)
+{
+ return s_SwapChain->SetPrivateDataInterface(Name, pUnknown);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetPrivateData(REFGUID Name, UINT* pDataSize, void* pData)
+{
+ return s_SwapChain->GetPrivateData(Name, pDataSize, pData);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetParent(REFIID riid, void** ppParent)
+{
+ return s_SwapChain->GetParent(riid, ppParent);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetDevice(REFIID riid, void** ppDevice)
+{
+ return s_SwapChain->GetDevice(riid, ppDevice);
+}
+
+HRESULT __stdcall SwapChainTrampoline::Present(UINT SyncInterval, UINT Flags)
+{
+ return s_SwapChain->Present(SyncInterval, Flags);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetBuffer(UINT Buffer, REFIID riid, void** ppSurface)
+{
+ return s_SwapChain->GetBuffer(Buffer, riid, ppSurface);
+}
+
+HRESULT __stdcall SwapChainTrampoline::SetFullscreenState(BOOL Fullscreen, IDXGIOutput* pTarget)
+{
+ return s_SwapChain->SetFullscreenState(Fullscreen, pTarget);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetFullscreenState(BOOL* pFullscreen, IDXGIOutput** ppTarget)
+{
+ return s_SwapChain->GetFullscreenState(pFullscreen, ppTarget);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetDesc(DXGI_SWAP_CHAIN_DESC* pDesc)
+{
+ return s_SwapChain->GetDesc(pDesc);
+}
+
+HRESULT __stdcall SwapChainTrampoline::ResizeBuffers(UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags)
+{
+ return s_SwapChain->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags);
+}
+
+HRESULT __stdcall SwapChainTrampoline::ResizeTarget(const DXGI_MODE_DESC* pNewTargetParameters)
+{
+ return s_SwapChain->ResizeTarget(pNewTargetParameters);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetContainingOutput(IDXGIOutput** ppOutput)
+{
+ return s_SwapChain->GetContainingOutput(ppOutput);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats)
+{
+ return s_SwapChain->GetFrameStatistics(pStats);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetLastPresentCount(UINT* pLastPresentCount)
+{
+ return s_SwapChain->GetLastPresentCount(pLastPresentCount);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetDesc1(DXGI_SWAP_CHAIN_DESC1* pDesc)
+{
+ return s_SwapChain->GetDesc1(pDesc);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetFullscreenDesc(DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc)
+{
+ return s_SwapChain->GetFullscreenDesc(pDesc);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetHwnd(HWND* pHwnd)
+{
+ return s_SwapChain->GetHwnd(pHwnd);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetCoreWindow(REFIID refiid, void** ppUnk)
+{
+ return s_SwapChain->GetCoreWindow(refiid, ppUnk);
+}
+
+HRESULT __stdcall SwapChainTrampoline::Present1(UINT SyncInterval, UINT PresentFlags, const DXGI_PRESENT_PARAMETERS* pPresentParameters)
+{
+ return s_SwapChain->Present1(SyncInterval, PresentFlags, pPresentParameters);
+}
+
+BOOL __stdcall SwapChainTrampoline::IsTemporaryMonoSupported(void)
+{
+ return s_SwapChain->IsTemporaryMonoSupported();
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetRestrictToOutput(IDXGIOutput** ppRestrictToOutput)
+{
+ return s_SwapChain->GetRestrictToOutput(ppRestrictToOutput);
+}
+
+HRESULT __stdcall SwapChainTrampoline::SetBackgroundColor(const DXGI_RGBA* pColor)
+{
+ return s_SwapChain->SetBackgroundColor(pColor);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetBackgroundColor(DXGI_RGBA* pColor)
+{
+ return s_SwapChain->GetBackgroundColor(pColor);
+}
+
+HRESULT __stdcall SwapChainTrampoline::SetRotation(DXGI_MODE_ROTATION Rotation)
+{
+ return s_SwapChain->SetRotation(Rotation);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetRotation(DXGI_MODE_ROTATION* pRotation)
+{
+ return s_SwapChain->GetRotation(pRotation);
+}
+
+HRESULT __stdcall SwapChainTrampoline::SetSourceSize(UINT Width, UINT Height)
+{
+ return s_SwapChain->SetSourceSize(Width, Height);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetSourceSize(UINT* pWidth, UINT* pHeight)
+{
+ return s_SwapChain->GetSourceSize(pWidth, pHeight);
+}
+
+HRESULT __stdcall SwapChainTrampoline::SetMaximumFrameLatency(UINT MaxLatency)
+{
+ return s_SwapChain->SetMaximumFrameLatency(MaxLatency);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetMaximumFrameLatency(UINT* pMaxLatency)
+{
+ return s_SwapChain->GetMaximumFrameLatency(pMaxLatency);
+}
+
+HANDLE __stdcall SwapChainTrampoline::GetFrameLatencyWaitableObject(void)
+{
+ return s_SwapChain->GetFrameLatencyWaitableObject();
+}
+
+HRESULT __stdcall SwapChainTrampoline::SetMatrixTransform(const DXGI_MATRIX_3X2_F* pMatrix)
+{
+ return s_SwapChain->SetMatrixTransform(pMatrix);
+}
+
+HRESULT __stdcall SwapChainTrampoline::GetMatrixTransform(DXGI_MATRIX_3X2_F* pMatrix)
+{
+ return s_SwapChain->GetMatrixTransform(pMatrix);
+}
+
+UINT __stdcall SwapChainTrampoline::GetCurrentBackBufferIndex(void)
+{
+ return s_SwapChain->GetCurrentBackBufferIndex();
+}
+
+HRESULT __stdcall SwapChainTrampoline::CheckColorSpaceSupport(DXGI_COLOR_SPACE_TYPE ColorSpace, UINT* pColorSpaceSupport)
+{
+ return s_SwapChain->CheckColorSpaceSupport(ColorSpace, pColorSpaceSupport);
+}
+
+HRESULT __stdcall SwapChainTrampoline::SetColorSpace1(DXGI_COLOR_SPACE_TYPE ColorSpace)
+{
+ return s_SwapChain->SetColorSpace1(ColorSpace);
+}
+
+HRESULT __stdcall SwapChainTrampoline::ResizeBuffers1(UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT Format, UINT SwapChainFlags, const UINT* pCreationNodeMask, IUnknown* const* ppPresentQueue)
+{
+ return s_SwapChain->ResizeBuffers1(BufferCount, Width, Height, Format, SwapChainFlags, pCreationNodeMask, ppPresentQueue);
+}
+
+HRESULT __stdcall SwapChainTrampoline::SetHDRMetaData(DXGI_HDR_METADATA_TYPE Type, UINT Size, void* pMetaData)
+{
+ return s_SwapChain->SetHDRMetaData(Type, Size, pMetaData);
+}