diff --git a/src/FSR3UnityPlugin.cpp b/src/FSR3UnityPlugin.cpp index 9a1275a..8818426 100644 --- a/src/FSR3UnityPlugin.cpp +++ b/src/FSR3UnityPlugin.cpp @@ -198,26 +198,28 @@ static void TestSwapChainStuff() } 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; - //} + //memcpy(swapChain4, &trampoline, sizeof(IDXGISwapChain4)); + + // This successfully destroys Unity's original swapchain, allowing a new one to be created below. Now to swap them out... + while (swapChain4->Release()); + + IDXGIFactory2* dxgiFactory; + hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&dxgiFactory)); + if (!SUCCEEDED(hr)) + { + UNITY_LOG_ERROR(s_Log, "Could not create DXGI Factory :("); + return; + } + + 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() @@ -329,7 +331,7 @@ static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data) s_Upscaler->InitFeature(initData); break; } - case BaseEventId + FSR3PluginEvent::eSwapChain: + case FSR3PluginEvent::eSwapChain: TestSwapChainStuff(); break; } diff --git a/src/SwapChainTrampoline.cpp b/src/SwapChainTrampoline.cpp index 0472f77..97ff8e1 100644 --- a/src/SwapChainTrampoline.cpp +++ b/src/SwapChainTrampoline.cpp @@ -1,17 +1,56 @@ #include "SwapChainTrampoline.h" +#include + // 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; +template +intptr_t SeeBits(T func) +{ + union + { + T ptr; + intptr_t i; + }; + ptr = func; + + return i; +} + +template +size_t FindFunctionOffset(T* obj, F func) +{ + intptr_t* vtable = *(intptr_t**)obj; + int cnt = 42;// sizeof(T) / sizeof(intptr_t); + + intptr_t funcBits = SeeBits(func); + + for (size_t offs = 0; offs < cnt; ++offs) + { + if (vtable[offs] == funcBits) + return offs; + } + + return 0u; +} + 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)); + //pfQueryInterface = (PFN_QueryInterface)((intptr_t)swapChain + reinterpret_cast(&SwapChainTrampoline::QueryInterface)); + + /*intptr_t pfQueryInterfaceOffset = SeeBits(&SwapChainTrampoline::QueryInterface); + intptr_t pfPresentOffset = SeeBits(&SwapChainTrampoline::Present);*/ + intptr_t pfQueryInterfaceOffset = FindFunctionOffset(this, &SwapChainTrampoline::QueryInterface); + intptr_t pfPresentOffset = FindFunctionOffset(this, &SwapChainTrampoline::Present); + + std::cout << "QueryInterface: " << pfQueryInterfaceOffset << ", Present: " << pfPresentOffset << std::endl; // 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 @@ -19,6 +58,10 @@ SwapChainTrampoline::SwapChainTrampoline(IDXGISwapChain4* 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 + + // TODO: might also be able to do something with IDXGIObject::Get/SetPrivateData + + // TODO: not sure if any of this is really necessary? If we can destroy the original swap chain, construct a new one through FFX, then plug that in here, we're already done right? } HRESULT __stdcall SwapChainTrampoline::QueryInterface(REFIID riid, void** ppvObject)