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); +}