diff --git a/src/DX12Interop.cpp b/src/DX12Interop.cpp new file mode 100644 index 0000000000..d2891d1eda --- /dev/null +++ b/src/DX12Interop.cpp @@ -0,0 +1,115 @@ +#include "DX12Interop.h" + +#include + +#include "Features/Upscaling.h" + +DX12Interop::~DX12Interop() +{ + if (fenceEvent) { + CloseHandle(fenceEvent); + fenceEvent = nullptr; + } +} + +void DX12Interop::Init(ID3D11Device* a_d3d11Device, ID3D11DeviceContext* a_immediateContext, IDXGIAdapter* a_adapter) +{ + if (!D3D12Mode()) + return; + + active = true; + + SetD3D11Device(a_d3d11Device); + SetD3D11DeviceContext(a_immediateContext); + + CreateD3D12Device(a_adapter); + + CreateInterop(); +} + + +bool DX12Interop::Active() const +{ + return active; +} + +bool DX12Interop::D3D12Mode() +{ + auto& upscaling = globals::features::upscaling; + if (upscaling.loaded && upscaling.HasFrameGenModule()) + return true; + + return false; +} + +void DX12Interop::CreateD3D12Device(IDXGIAdapter* a_adapter) +{ + DX::ThrowIfFailed(D3D12CreateDevice(a_adapter, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&d3d12Device))); + + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; + queueDesc.NodeMask = 0; + + DX::ThrowIfFailed(d3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue))); +} + +void DX12Interop::CreateInterop() +{ + HANDLE sharedFenceHandle; + DX::ThrowIfFailed(d3d12Device->CreateFence(0, D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d12Fence))); + DX::ThrowIfFailed(d3d12Device->CreateSharedHandle(d3d12Fence.get(), nullptr, GENERIC_ALL, nullptr, &sharedFenceHandle)); + DX::ThrowIfFailed(d3d11Device->OpenSharedFence(sharedFenceHandle, IID_PPV_ARGS(&d3d11Fence))); + CloseHandle(sharedFenceHandle); + + fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (!fenceEvent) + DX::ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError())); +} + +void DX12Interop::SetD3D11Device(ID3D11Device* a_d3d11Device) +{ + DX::ThrowIfFailed(a_d3d11Device->QueryInterface(IID_PPV_ARGS(&d3d11Device))); +} + +void DX12Interop::SetD3D11DeviceContext(ID3D11DeviceContext* a_d3d11Context) +{ + DX::ThrowIfFailed(a_d3d11Context->QueryInterface(IID_PPV_ARGS(&d3d11Context))); +} + +void DX12Interop::SetupResources() +{ + if (!active) + return; + + auto renderer = globals::game::renderer; + + auto& main = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; + D3D11_TEXTURE2D_DESC mainDesc{}; + main.texture->GetDesc(&mainDesc); + + // Main render target + mainDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS; + sharedResources.main = new WrappedResource(mainDesc, d3d11Device.get(), d3d12Device.get()); + + // Depth + mainDesc.Format = DXGI_FORMAT_R32_FLOAT; + mainDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS; + sharedResources.depth = new WrappedResource(mainDesc, d3d11Device.get(), d3d12Device.get()); + + // Motion vector + mainDesc.Format = DXGI_FORMAT_R16G16_FLOAT; + mainDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS; + sharedResources.motionVector = new WrappedResource(mainDesc, d3d11Device.get(), d3d12Device.get()); + + // Upscaler reactive mask + mainDesc.Format = DXGI_FORMAT_R8_UNORM; + mainDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS; + sharedResources.reactiveMask = new WrappedResource(mainDesc, d3d11Device.get(), d3d12Device.get()); +} + +UINT DX12Interop::GetFrameContextIndex() const +{ + return globals::state->frameCount % kMaxFramesInFlight; +} diff --git a/src/DX12Interop.h b/src/DX12Interop.h new file mode 100644 index 0000000000..0024fada1a --- /dev/null +++ b/src/DX12Interop.h @@ -0,0 +1,67 @@ +#pragma once + +#include "Feature.h" +#include "State.h" +#include "DX12Interop/WrappedResource.h" + +#include + +#include +#include +#include +#include + +struct DX12Interop +{ + static constexpr UINT kMaxFramesInFlight = 2; + + winrt::com_ptr d3d12Device; + + winrt::com_ptr commandQueue; + + UINT64 currentFenceValue = 0; + HANDLE fenceEvent = nullptr; + + struct SharedResources + { + WrappedResource* main = nullptr; + WrappedResource* depth = nullptr; + WrappedResource* motionVector = nullptr; + WrappedResource* reactiveMask = nullptr; + } sharedResources; + + winrt::com_ptr d3d11Device; + winrt::com_ptr d3d11Context; + + winrt::com_ptr d3d11Fence; + winrt::com_ptr d3d12Fence; + + static DX12Interop* GetSingleton() + { + static DX12Interop singleton; + return &singleton; + } + + ~DX12Interop(); + + void Init(ID3D11Device* d3d11Device, ID3D11DeviceContext* a_immediateContext, IDXGIAdapter* a_adapter); + + // Resources + void SetupResources(); + + bool Active() const; + + // Whether DirectX 12 is required or not + // True when Upscaling is loaded in frame generation mode + static bool D3D12Mode(); + + UINT GetFrameContextIndex() const; + +private: + bool active = false; + + void CreateInterop(); + void SetD3D11Device(ID3D11Device* a_d3d11Device); + void SetD3D11DeviceContext(ID3D11DeviceContext* a_d3d11Context); + void CreateD3D12Device(IDXGIAdapter* a_adapter); +}; \ No newline at end of file diff --git a/src/DX12Interop/InteropContext.h b/src/DX12Interop/InteropContext.h new file mode 100644 index 0000000000..b1a03db404 --- /dev/null +++ b/src/DX12Interop/InteropContext.h @@ -0,0 +1,79 @@ +#pragma once + +#include "DX12Interop.h" + +#include + +// Each execute call in the same frame requires its own context to avoid command allocator/list reuse hazards while frames are in flight. +class InteropContext +{ + InteropContext() + { + auto interop = globals::dx12Interop; + for (size_t i = 0; i < DX12Interop::kMaxFramesInFlight; i++) { + DX::ThrowIfFailed(interop->d3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&frames[i].commandAllocator))); + DX::ThrowIfFailed(interop->d3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, frames[i].commandAllocator.get(), nullptr, IID_PPV_ARGS(&frames[i].commandList))); + frames[i].commandList->Close(); + } + }; + + // Provides buffering for D3D12 command list recording and submission. + struct Frame + { + winrt::com_ptr commandAllocator; + winrt::com_ptr commandList; + UINT64 fenceValueAtSubmission = 0; // what value was signaled when this frame was submitted + }; + + Frame frames[DX12Interop::kMaxFramesInFlight]; + +public: + static InteropContext* Make() + { + return new InteropContext(); + } + + // Fences the GPU, waits for D3D11 to be idle, executes D3D12 commands in the provided function, then waits for D3D12 to be idle before returning. + template + void Execute(Func func, Func2 func2 = nullptr) + { + auto interop = globals::dx12Interop; + + // Get to next frame context + auto& frame = frames[interop->GetFrameContextIndex()]; + + // CPU-side wait: stall if this frame's previous submission isn't done yet + // (i.e. we've lapped the GPU) + if (frame.fenceValueAtSubmission != 0) { + if (interop->d3d12Fence->GetCompletedValue() < frame.fenceValueAtSubmission) { + // GPU hasn't finished with this allocator yet - stall CPU + DX::ThrowIfFailed(interop->d3d12Fence->SetEventOnCompletion(frame.fenceValueAtSubmission, interop->fenceEvent)); + WaitForSingleObject(interop->fenceEvent, INFINITE); + } + } + + // Safe to reset now - GPU is done with this allocator + DX::ThrowIfFailed(frame.commandAllocator->Reset()); + DX::ThrowIfFailed(frame.commandList->Reset(frame.commandAllocator.get(), nullptr)); + + // DX11 -> DX12 handoff + DX::ThrowIfFailed(interop->d3d11Context->Signal(interop->d3d11Fence.get(), ++interop->currentFenceValue)); + DX::ThrowIfFailed(interop->commandQueue->Wait(interop->d3d12Fence.get(), interop->currentFenceValue)); + + func(frame.commandList.get()); + DX::ThrowIfFailed(frame.commandList->Close()); + + ID3D12CommandList* lists[] = { frame.commandList.get() }; + interop->commandQueue->ExecuteCommandLists(1, lists); + + if constexpr (!std::is_same_v, std::nullptr_t>) + func2(); + + // DX12 -> DX11 handoff + DX::ThrowIfFailed(interop->commandQueue->Signal(interop->d3d12Fence.get(), ++interop->currentFenceValue)); + DX::ThrowIfFailed(interop->d3d11Context->Wait(interop->d3d11Fence.get(), interop->currentFenceValue)); + + // Record what fence value this frame context was submitted at + frame.fenceValueAtSubmission = interop->currentFenceValue; + } +}; \ No newline at end of file diff --git a/src/DX12Interop/WrappedResource.cpp b/src/DX12Interop/WrappedResource.cpp new file mode 100644 index 0000000000..f781b465c8 --- /dev/null +++ b/src/DX12Interop/WrappedResource.cpp @@ -0,0 +1,106 @@ +#include "WrappedResource.h" +#include "DX12Interop.h" + +WrappedResource::WrappedResource(D3D11_TEXTURE2D_DESC a_texDesc, ID3D11Device5* a_d3d11Device, ID3D12Device* a_d3d12Device) +{ + // Create D3D11 shared texture directly instead of wrapping D3D12 resource + a_texDesc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED; + DX::ThrowIfFailed(a_d3d11Device->CreateTexture2D(&a_texDesc, nullptr, &resource11)); + + // Get shared handle from D3D11 texture to enable D3D12 access + winrt::com_ptr dxgiResource; + DX::ThrowIfFailed(resource11->QueryInterface(IID_PPV_ARGS(dxgiResource.put()))); + + HANDLE sharedHandle = nullptr; + DX::ThrowIfFailed(dxgiResource->GetSharedHandle(&sharedHandle)); + + // Open the shared D3D11 texture as D3D12 resource + DX::ThrowIfFailed(a_d3d12Device->OpenSharedHandle(sharedHandle, IID_PPV_ARGS(resource.put()))); + + if (a_texDesc.BindFlags & D3D11_BIND_SHADER_RESOURCE) { + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.Format = a_texDesc.Format; + + if (a_texDesc.ArraySize > 1) + { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + srvDesc.Texture2DArray.FirstArraySlice = 0; + srvDesc.Texture2DArray.ArraySize = a_texDesc.ArraySize; + srvDesc.Texture2DArray.MostDetailedMip = 0; + srvDesc.Texture2DArray.MipLevels = 1; + } + else + { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MostDetailedMip = 0; + srvDesc.Texture2D.MipLevels = 1; + + } + + DX::ThrowIfFailed(a_d3d11Device->CreateShaderResourceView(resource11, &srvDesc, &srv)); + } + + if (a_texDesc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) { + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.Format = a_texDesc.Format; + + if (a_texDesc.ArraySize > 1) { + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; + uavDesc.Texture2DArray.FirstArraySlice = 0; + uavDesc.Texture2DArray.ArraySize = a_texDesc.ArraySize; + } else { + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; + uavDesc.Texture2D.MipSlice = 0; + } + + DX::ThrowIfFailed(a_d3d11Device->CreateUnorderedAccessView(resource11, &uavDesc, &uav)); + } + + if (a_texDesc.BindFlags & D3D11_BIND_RENDER_TARGET) { + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {}; + rtvDesc.Format = a_texDesc.Format; + + if (a_texDesc.ArraySize > 1) + { + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.FirstArraySlice = 0; + rtvDesc.Texture2DArray.ArraySize = a_texDesc.ArraySize; + rtvDesc.Texture2DArray.MipSlice = 0; + } + else + { + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtvDesc.Texture2D.MipSlice = 0; + } + + DX::ThrowIfFailed(a_d3d11Device->CreateRenderTargetView(resource11, &rtvDesc, &rtv)); + } +} + +WrappedResource::WrappedResource(D3D11_TEXTURE2D_DESC a_texDesc) : + WrappedResource(a_texDesc, + globals::dx12Interop->d3d11Device.get(), + globals::dx12Interop->d3d12Device.get()) +{ +} + +WrappedResource::~WrappedResource() +{ + if (resource11) { + resource11->Release(); + resource11 = nullptr; + } + if (srv) { + srv->Release(); + srv = nullptr; + } + if (uav) { + uav->Release(); + uav = nullptr; + } + if (rtv) { + rtv->Release(); + rtv = nullptr; + } + // resource (winrt::com_ptr) will be automatically released +} \ No newline at end of file diff --git a/src/DX12Interop/WrappedResource.h b/src/DX12Interop/WrappedResource.h new file mode 100644 index 0000000000..cc93d2bf0e --- /dev/null +++ b/src/DX12Interop/WrappedResource.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include +#include + +class WrappedResource +{ +public: + WrappedResource(D3D11_TEXTURE2D_DESC a_texDesc, ID3D11Device5* a_d3d11Device, ID3D12Device* a_d3d12Device); + WrappedResource(D3D11_TEXTURE2D_DESC a_texDesc); + ~WrappedResource(); + + ID3D11Texture2D* resource11 = nullptr; + ID3D11ShaderResourceView* srv = nullptr; + ID3D11UnorderedAccessView* uav = nullptr; + ID3D11RenderTargetView* rtv = nullptr; + winrt::com_ptr resource; +}; \ No newline at end of file diff --git a/src/Features/Upscaling.cpp b/src/Features/Upscaling.cpp index 649b1af626..dfbb6fb91f 100644 --- a/src/Features/Upscaling.cpp +++ b/src/Features/Upscaling.cpp @@ -5,6 +5,7 @@ #include "HDRDisplay.h" #include "Hooks.h" #include "State.h" +#include "DX12Interop.h" #include "Upscaling/DX12SwapChain.h" #include "Upscaling/FidelityFX.h" #include "Upscaling/Streamline.h" @@ -64,6 +65,8 @@ HRESULT WINAPI hk_D3D11CreateDeviceAndSwapChainUpscaling( pAdapter->GetDesc(&adapterDesc); globals::state->SetAdapterDescription(adapterDesc.Description); + auto dx12Interop = globals::dx12Interop; + auto& upscaling = globals::features::upscaling; upscaling.LoadUpscalingSDKs(); @@ -125,8 +128,8 @@ HRESULT WINAPI hk_D3D11CreateDeviceAndSwapChainUpscaling( pFeatureLevel, ppImmediateContext)); - upscaling.SetProxyD3D11Device(*ppDevice); - upscaling.SetProxyD3D11DeviceContext(*ppImmediateContext); + dx12Interop->Init(*ppDevice, *ppImmediateContext, pAdapter); + upscaling.CreateProxySwapChain(pAdapter, *pSwapChainDesc); upscaling.CreateProxyInterop(); @@ -1376,9 +1379,6 @@ void Upscaling::SetupResources() rcas.Initialize(); - if (d3d12SwapChainActive) - dx12SwapChain.CreateSharedResources(); - copyDepthToSharedBufferPS.attach((ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\Upscaling\\CopyDepthToSharedBufferPS.hlsl", { { "PSHADER", "" } }, "ps_5_0")); // Setup HDR resources only when the HDR Display feature is loaded @@ -1408,8 +1408,10 @@ void Upscaling::CopySharedD3D12Resources() auto renderer = globals::game::renderer; auto context = globals::d3d::context; + auto& sharedResources = globals::dx12Interop->sharedResources; + auto& motionVector = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR]; - context->CopyResource(dx12SwapChain.motionVectorBufferShared12->resource11, motionVector.texture); + context->CopyResource(sharedResources.motionVector->resource11, motionVector.texture); auto& depth = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kMAIN]; @@ -1444,7 +1446,7 @@ void Upscaling::CopySharedD3D12Resources() context->PSSetShaderResources(0, ARRAYSIZE(views), views); // Set render target view for pixel shader output - ID3D11RenderTargetView* rtvs[1] = { dx12SwapChain.depthBufferShared12->rtv }; + ID3D11RenderTargetView* rtvs[1] = { sharedResources.depth->rtv }; context->OMSetRenderTargets(ARRAYSIZE(rtvs), rtvs, nullptr); context->PSSetShader(copyDepthToSharedBufferPS.get(), nullptr, 0); @@ -1697,17 +1699,6 @@ bool Upscaling::HasFrameGenModule() const return fidelityFX.featureFSR3FG; } -// Proxy interface methods -void Upscaling::SetProxyD3D11Device(ID3D11Device* device) -{ - dx12SwapChain.SetD3D11Device(device); -} - -void Upscaling::SetProxyD3D11DeviceContext(ID3D11DeviceContext* context) -{ - dx12SwapChain.SetD3D11DeviceContext(context); -} - void Upscaling::CreateProxySwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC swapChainDesc) { dx12SwapChain.CreateSwapChain(adapter, swapChainDesc); diff --git a/src/Features/Upscaling.h b/src/Features/Upscaling.h index 52f98ca8e1..62127448bd 100644 --- a/src/Features/Upscaling.h +++ b/src/Features/Upscaling.h @@ -252,8 +252,6 @@ struct Upscaling : Feature bool HasFrameGenModule() const; // Proxy interface methods - void SetProxyD3D11Device(ID3D11Device* device); - void SetProxyD3D11DeviceContext(ID3D11DeviceContext* context); void CreateProxySwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC swapChainDesc); void CreateProxyInterop(); IDXGISwapChain* GetProxySwapChain(); diff --git a/src/Features/Upscaling/DX12SwapChain.cpp b/src/Features/Upscaling/DX12SwapChain.cpp index 430cf3a194..0c0339702c 100644 --- a/src/Features/Upscaling/DX12SwapChain.cpp +++ b/src/Features/Upscaling/DX12SwapChain.cpp @@ -8,28 +8,11 @@ #include "FidelityFX.h" #include "Streamline.h" -void DX12SwapChain::CreateD3D12Device(IDXGIAdapter* a_adapter) -{ - DX::ThrowIfFailed(D3D12CreateDevice(a_adapter, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&d3d12Device))); - - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; - queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; - queueDesc.NodeMask = 0; - - DX::ThrowIfFailed(d3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue))); - - for (int i = 0; i < 2; i++) { - DX::ThrowIfFailed(d3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocators[i]))); - DX::ThrowIfFailed(d3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocators[i].get(), nullptr, IID_PPV_ARGS(&commandLists[i]))); - commandLists[i]->Close(); - } -} +#include "DX12Interop.h" void DX12SwapChain::CreateSwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC a_swapChainDesc) { - CreateD3D12Device(adapter); + auto interop = globals::dx12Interop; IDXGIFactory4* dxgiFactory; DX::ThrowIfFailed(adapter->GetParent(IID_PPV_ARGS(&dxgiFactory))); @@ -42,7 +25,7 @@ void DX12SwapChain::CreateSwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC // Test R10G10B10A2 support (applies to both VR and non-VR for HDR capability) D3D12_FEATURE_DATA_FORMAT_SUPPORT formatSupport = { DXGI_FORMAT_R10G10B10A2_UNORM, D3D12_FORMAT_SUPPORT1_RENDER_TARGET, D3D12_FORMAT_SUPPORT2_NONE }; - if (SUCCEEDED(d3d12Device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatSupport, sizeof(formatSupport)))) { + if (SUCCEEDED(interop->d3d12Device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatSupport, sizeof(formatSupport)))) { if ((formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET) == 0) { logger::warn("[DX12SwapChain] R10G10B10A2_UNORM not supported as render target, falling back to R8G8B8A8_UNORM"); negotiatedFormat = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -77,7 +60,7 @@ void DX12SwapChain::CreateSwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC ffxSwapChainDesc.desc = &swapChainDesc; ffxSwapChainDesc.dxgiFactory = dxgiFactory; ffxSwapChainDesc.fullscreenDesc = nullptr; - ffxSwapChainDesc.gameQueue = commandQueue.get(); + ffxSwapChainDesc.gameQueue = interop->commandQueue.get(); ffxSwapChainDesc.hwnd = a_swapChainDesc.OutputWindow; ffxSwapChainDesc.swapchain = &swapChain; @@ -99,16 +82,12 @@ void DX12SwapChain::CreateSwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC SetColorSpace(enableHDR && !fallbackUsed); fidelityFX.SetupFrameGeneration(); + + interopContext = eastl::unique_ptr(InteropContext::Make()); } void DX12SwapChain::CreateInterop() { - HANDLE sharedFenceHandle; - DX::ThrowIfFailed(d3d12Device->CreateFence(0, D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d12Fence))); - DX::ThrowIfFailed(d3d12Device->CreateSharedHandle(d3d12Fence.get(), nullptr, GENERIC_ALL, nullptr, &sharedFenceHandle)); - DX::ThrowIfFailed(d3d11Device->OpenSharedFence(sharedFenceHandle, IID_PPV_ARGS(&d3d11Fence))); - CloseHandle(sharedFenceHandle); - swapChainProxy = new DXGISwapChainProxy(swapChain); D3D11_TEXTURE2D_DESC texDesc11{}; @@ -121,11 +100,11 @@ void DX12SwapChain::CreateInterop() texDesc11.SampleDesc.Quality = 0; texDesc11.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS; - swapChainBufferWrapped = new WrappedResource(texDesc11, d3d11Device.get(), d3d12Device.get()); + swapChainBufferWrapped = new WrappedResource(texDesc11); // UI buffer uses R8G8B8A8_UNORM - vanilla UI is SDR and 8-bit precision texDesc11.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - uiBufferWrapped = new WrappedResource(texDesc11, d3d11Device.get(), d3d12Device.get()); + uiBufferWrapped = new WrappedResource(texDesc11); } DXGISwapChainProxy* DX12SwapChain::GetSwapChainProxy() @@ -133,16 +112,6 @@ DXGISwapChainProxy* DX12SwapChain::GetSwapChainProxy() return swapChainProxy; } -void DX12SwapChain::SetD3D11Device(ID3D11Device* a_d3d11Device) -{ - DX::ThrowIfFailed(a_d3d11Device->QueryInterface(IID_PPV_ARGS(&d3d11Device))); -} - -void DX12SwapChain::SetD3D11DeviceContext(ID3D11DeviceContext* a_d3d11Context) -{ - DX::ThrowIfFailed(a_d3d11Context->QueryInterface(IID_PPV_ARGS(&d3d11Context))); -} - HRESULT DX12SwapChain::GetBuffer(void** ppSurface) { *ppSurface = swapChainBufferWrapped->resource11; @@ -152,6 +121,7 @@ HRESULT DX12SwapChain::GetBuffer(void** ppSurface) HRESULT DX12SwapChain::Present(UINT SyncInterval, UINT Flags) { auto& upscaling = globals::features::upscaling; + auto dx12Interop = globals::dx12Interop; // Scale UI brightness BEFORE fence sync so the D3D11 UIBrightnessCS dispatch // is covered by the D3D11→D3D12 fence. Without this, FidelityFX may read @@ -163,56 +133,43 @@ HRESULT DX12SwapChain::Present(UINT SyncInterval, UINT Flags) bool isHDR = hdr && hdr->settings.enableHDR; - // Wait for D3D11 to finish (includes ApplyHDR scene encoding AND UIBrightnessCS) - DX::ThrowIfFailed(d3d11Context->Signal(d3d11Fence.get(), fenceValue)); - DX::ThrowIfFailed(commandQueue->Wait(d3d12Fence.get(), fenceValue)); - fenceValue++; - - // New frame, reset - DX::ThrowIfFailed(commandAllocators[frameIndex]->Reset()); - DX::ThrowIfFailed(commandLists[frameIndex]->Reset(commandAllocators[frameIndex].get(), nullptr)); - - // Copy shared texture to swap chain buffer - { - auto fakeSwapChain = swapChainBufferWrapped->resource.get(); - auto realSwapChain = swapChainBuffers[frameIndex].get(); - { - std::vector barriers; - barriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(fakeSwapChain, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE)); - barriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(realSwapChain, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_COPY_DEST)); - commandLists[frameIndex]->ResourceBarrier(static_cast(barriers.size()), barriers.data()); - } - - commandLists[frameIndex]->CopyResource(realSwapChain, fakeSwapChain); - - { - std::vector barriers; - barriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(fakeSwapChain, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COMMON)); - barriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(realSwapChain, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PRESENT)); - commandLists[frameIndex]->ResourceBarrier(static_cast(barriers.size()), barriers.data()); - } + if (interopContext) { + interopContext->Execute([&](ID3D12GraphicsCommandList4* commandList) { + // Copy shared texture to swap chain buffer + { + auto fakeSwapChain = swapChainBufferWrapped->resource.get(); + auto realSwapChain = swapChainBuffers[frameIndex].get(); + { + D3D12_RESOURCE_BARRIER barriers[2] = { + CD3DX12_RESOURCE_BARRIER::Transition(fakeSwapChain, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE), + CD3DX12_RESOURCE_BARRIER::Transition(realSwapChain, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_COPY_DEST) + }; + commandList->ResourceBarrier(ARRAYSIZE(barriers), barriers); + } + + commandList->CopyResource(realSwapChain, fakeSwapChain); + + { + D3D12_RESOURCE_BARRIER barriers[2] = { + CD3DX12_RESOURCE_BARRIER::Transition(fakeSwapChain, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COMMON), + CD3DX12_RESOURCE_BARRIER::Transition(realSwapChain, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PRESENT) + }; + commandList->ResourceBarrier(ARRAYSIZE(barriers), barriers); + } + } + + upscaling.fidelityFX.Present(commandList, upscaling.ShouldUseFrameGenerationThisFrame(), isHDR); + }, [&]() { + // Present the frame + DX::ThrowIfFailed(swapChain->Present(SyncInterval, Flags)); + }); } - upscaling.fidelityFX.Present(upscaling.ShouldUseFrameGenerationThisFrame(), isHDR); - - DX::ThrowIfFailed(commandLists[frameIndex]->Close()); - - ID3D12CommandList* commandListsToExecute[] = { commandLists[frameIndex].get() }; - commandQueue->ExecuteCommandLists(1, commandListsToExecute); - - // Present the frame - DX::ThrowIfFailed(swapChain->Present(SyncInterval, Flags)); - - // Wait for D3D12 to finish - DX::ThrowIfFailed(commandQueue->Signal(d3d12Fence.get(), fenceValue)); - DX::ThrowIfFailed(d3d11Context->Wait(d3d11Fence.get(), fenceValue)); - fenceValue++; - // Update the frame index frameIndex = swapChain->GetCurrentBackBufferIndex(); float clearColor[4]{ 0, 0, 0, 0 }; - d3d11Context->ClearRenderTargetView(uiBufferWrapped->rtv, clearColor); + dx12Interop->d3d11Context->ClearRenderTargetView(uiBufferWrapped->rtv, clearColor); // If VSync is disabled, use frame limiter to prevent tearing and optimise pacing if (SyncInterval == 0) @@ -224,7 +181,7 @@ HRESULT DX12SwapChain::Present(UINT SyncInterval, UINT Flags) HRESULT DX12SwapChain::GetDevice(REFIID uuid, void** ppDevice) { if (uuid == __uuidof(ID3D11Device) || uuid == __uuidof(ID3D11Device1) || uuid == __uuidof(ID3D11Device2) || uuid == __uuidof(ID3D11Device3) || uuid == __uuidof(ID3D11Device4) || uuid == __uuidof(ID3D11Device5)) { - *ppDevice = d3d11Device.get(); + *ppDevice = globals::dx12Interop->d3d11Device.get(); return S_OK; } @@ -259,81 +216,6 @@ float DX12SwapChain::GetFrameTime() const return frameTime; } -WrappedResource::WrappedResource(D3D11_TEXTURE2D_DESC a_texDesc, ID3D11Device5* a_d3d11Device, ID3D12Device* a_d3d12Device) -{ - // Create D3D11 shared texture directly instead of wrapping D3D12 resource - a_texDesc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_NTHANDLE; - DX::ThrowIfFailed(a_d3d11Device->CreateTexture2D(&a_texDesc, nullptr, &resource11)); - - // Get shared handle from D3D11 texture to enable D3D12 access - winrt::com_ptr dxgiResource; - DX::ThrowIfFailed(resource11->QueryInterface(IID_PPV_ARGS(dxgiResource.put()))); - HANDLE sharedHandle = nullptr; - DX::ThrowIfFailed(dxgiResource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, &sharedHandle)); - - // Open the shared D3D11 texture as D3D12 resource - DX::ThrowIfFailed(a_d3d12Device->OpenSharedHandle(sharedHandle, IID_PPV_ARGS(resource.put()))); - CloseHandle(sharedHandle); - - if (a_texDesc.BindFlags & D3D11_BIND_SHADER_RESOURCE) { - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; - srvDesc.Format = a_texDesc.Format; - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - srvDesc.Texture2D.MostDetailedMip = 0; - srvDesc.Texture2D.MipLevels = 1; - - DX::ThrowIfFailed(a_d3d11Device->CreateShaderResourceView(resource11, &srvDesc, &srv)); - } - - if (a_texDesc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) { - if (a_texDesc.ArraySize > 1) { - D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; - uavDesc.Format = a_texDesc.Format; - uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; - uavDesc.Texture2DArray.FirstArraySlice = 0; - uavDesc.Texture2DArray.ArraySize = a_texDesc.ArraySize; - - DX::ThrowIfFailed(a_d3d11Device->CreateUnorderedAccessView(resource11, &uavDesc, &uav)); - } else { - D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; - uavDesc.Format = a_texDesc.Format; - uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; - uavDesc.Texture2D.MipSlice = 0; - - DX::ThrowIfFailed(a_d3d11Device->CreateUnorderedAccessView(resource11, &uavDesc, &uav)); - } - } - - if (a_texDesc.BindFlags & D3D11_BIND_RENDER_TARGET) { - D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {}; - rtvDesc.Format = a_texDesc.Format; - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - rtvDesc.Texture2D.MipSlice = 0; - DX::ThrowIfFailed(a_d3d11Device->CreateRenderTargetView(resource11, &rtvDesc, &rtv)); - } -} - -WrappedResource::~WrappedResource() -{ - if (resource11) { - resource11->Release(); - resource11 = nullptr; - } - if (srv) { - srv->Release(); - srv = nullptr; - } - if (uav) { - uav->Release(); - uav = nullptr; - } - if (rtv) { - rtv->Release(); - rtv = nullptr; - } - // resource (winrt::com_ptr) will be automatically released -} - DXGISwapChainProxy::DXGISwapChainProxy(IDXGISwapChain4* a_swapChain) { swapChain = a_swapChain; @@ -464,20 +346,3 @@ DX12SwapChain::BlurResources DX12SwapChain::GetBlurResources() const } return res; } - -void DX12SwapChain::CreateSharedResources() -{ - auto renderer = globals::game::renderer; - - // Create depth buffer - auto& main = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; - D3D11_TEXTURE2D_DESC texDesc{}; - main.texture->GetDesc(&texDesc); - texDesc.Format = DXGI_FORMAT_R32_FLOAT; - depthBufferShared12 = new WrappedResource(texDesc, d3d11Device.get(), d3d12Device.get()); - - // Create motion vector buffer - auto& motionVector = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR]; - motionVector.texture->GetDesc(&texDesc); - motionVectorBufferShared12 = new WrappedResource(texDesc, d3d11Device.get(), d3d12Device.get()); -} diff --git a/src/Features/Upscaling/DX12SwapChain.h b/src/Features/Upscaling/DX12SwapChain.h index ab7ff23ccc..a7b992e6ed 100644 --- a/src/Features/Upscaling/DX12SwapChain.h +++ b/src/Features/Upscaling/DX12SwapChain.h @@ -11,18 +11,9 @@ #include -class WrappedResource -{ -public: - WrappedResource(D3D11_TEXTURE2D_DESC a_texDesc, ID3D11Device5* a_d3d11Device, ID3D12Device* a_d3d12Device); - ~WrappedResource(); - - ID3D11Texture2D* resource11 = nullptr; - ID3D11ShaderResourceView* srv = nullptr; - ID3D11UnorderedAccessView* uav = nullptr; - ID3D11RenderTargetView* rtv = nullptr; - winrt::com_ptr resource; -}; +#include "DX12Interop.h" +#include "DX12Interop/InteropContext.h" +#include "DX12Interop/WrappedResource.h" struct DXGISwapChainProxy : IDXGISwapChain { @@ -61,11 +52,6 @@ struct DXGISwapChainProxy : IDXGISwapChain class DX12SwapChain { public: - winrt::com_ptr d3d12Device; - winrt::com_ptr commandQueue; - winrt::com_ptr commandAllocators[2]; - winrt::com_ptr commandLists[2]; - IDXGISwapChain4* swapChain; DXGI_SWAP_CHAIN_DESC1 swapChainDesc; @@ -73,20 +59,9 @@ class DX12SwapChain WrappedResource* swapChainBufferWrapped; WrappedResource* uiBufferWrapped; - // D3D12 interop resources for frame generation - WrappedResource* depthBufferShared12 = nullptr; - WrappedResource* motionVectorBufferShared12 = nullptr; - - winrt::com_ptr d3d11Device; - winrt::com_ptr d3d11Context; - - winrt::com_ptr d3d11Fence; - winrt::com_ptr d3d12Fence; - winrt::com_ptr swapChainBuffers[2]; UINT frameIndex = 0; - UINT64 fenceValue = 0; LARGE_INTEGER qpf; @@ -94,17 +69,16 @@ class DX12SwapChain DXGISwapChainProxy* swapChainProxy = nullptr; + eastl::unique_ptr interopContext; + // Returns the current frame time (in seconds) for accurate FPS calculation when frame generation is active float GetFrameTime() const; - void CreateD3D12Device(IDXGIAdapter* a_adapter); void CreateSwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC swapChainDesc); void CreateInterop(); DXGISwapChainProxy* GetSwapChainProxy(); - void SetD3D11Device(ID3D11Device* a_d3d11Device); - void SetD3D11DeviceContext(ID3D11DeviceContext* a_d3d11Context); HRESULT GetBuffer(void** ppSurface); HRESULT Present(UINT SyncInterval, UINT Flags); @@ -125,7 +99,4 @@ class DX12SwapChain // Get all resources needed for background blur in one call BlurResources GetBlurResources() const; - - // D3D12 interop resource management - void CreateSharedResources(); }; diff --git a/src/Features/Upscaling/FidelityFX.cpp b/src/Features/Upscaling/FidelityFX.cpp index b9976a99c7..806fa0c542 100644 --- a/src/Features/Upscaling/FidelityFX.cpp +++ b/src/Features/Upscaling/FidelityFX.cpp @@ -7,6 +7,7 @@ #include "../HDRDisplay.h" #include "../Upscaling.h" #include "DX12SwapChain.h" +#include "DX12Interop.h" ffxFunctions ffxModule; @@ -50,6 +51,7 @@ void FidelityFX::LoadFFX() void FidelityFX::SetupFrameGeneration() { auto& swapChain = globals::features::upscaling.dx12SwapChain; + auto dx12Interop = globals::dx12Interop; ffx::CreateContextDescFrameGeneration createFg{}; createFg.displaySize = { swapChain.swapChainDesc.Width, swapChain.swapChainDesc.Height }; @@ -58,7 +60,7 @@ void FidelityFX::SetupFrameGeneration() createFg.backBufferFormat = ffxApiGetSurfaceFormatDX12(swapChain.swapChainDesc.Format); ffx::CreateBackendDX12Desc backendDesc{}; - backendDesc.device = swapChain.d3d12Device.get(); + backendDesc.device = dx12Interop->d3d12Device.get(); if (ffx::CreateContext(frameGenContext, nullptr, createFg, backendDesc) != ffx::ReturnCode::Ok) logger::critical("[FidelityFX] Failed to create frame generation context!"); @@ -71,7 +73,7 @@ void FidelityFX::SetupFrameGeneration() * * @param a_useFrameGeneration If true, enables frame generation and dispatches the necessary workloads; otherwise, presents without frame generation. */ -void FidelityFX::Present(bool a_useFrameGeneration, bool a_isHDR) +void FidelityFX::Present(ID3D12GraphicsCommandList4* commandList, bool a_useFrameGeneration, bool a_isHDR) { auto& upscaling = globals::features::upscaling; auto& swapChain = globals::features::upscaling.dx12SwapChain; @@ -186,7 +188,7 @@ void FidelityFX::Present(bool a_useFrameGeneration, bool a_isHDR) if (a_useFrameGeneration) { ffx::DispatchDescFrameGenerationPrepare dispatchParameters{}; - dispatchParameters.commandList = swapChain.commandLists[swapChain.frameIndex].get(); + dispatchParameters.commandList = commandList; dispatchParameters.motionVectorScale.x = renderSize.x; dispatchParameters.motionVectorScale.y = renderSize.y; @@ -206,8 +208,10 @@ void FidelityFX::Present(bool a_useFrameGeneration, bool a_isHDR) dispatchParameters.frameID = frameID; - dispatchParameters.depth = ffxApiGetResourceDX12(swapChain.depthBufferShared12->resource.get()); - dispatchParameters.motionVectors = ffxApiGetResourceDX12(swapChain.motionVectorBufferShared12->resource.get()); + auto& sharedResources = globals::dx12Interop->sharedResources; + + dispatchParameters.depth = ffxApiGetResourceDX12(sharedResources.depth->resource.get()); + dispatchParameters.motionVectors = ffxApiGetResourceDX12(sharedResources.motionVector->resource.get()); ffx::DispatchDescFrameGenerationPrepareCameraInfo cameraConfig{}; diff --git a/src/Features/Upscaling/FidelityFX.h b/src/Features/Upscaling/FidelityFX.h index 5db922ebec..5806fe5272 100644 --- a/src/Features/Upscaling/FidelityFX.h +++ b/src/Features/Upscaling/FidelityFX.h @@ -49,7 +49,7 @@ class FidelityFX void LoadFFX(); void SetupFrameGeneration(); - void Present(bool a_useFrameGeneration, bool a_isHDR = false); + void Present(ID3D12GraphicsCommandList4* commandList, bool a_useFrameGeneration, bool a_isHDR = false); void CreateFSRResources(); diff --git a/src/Globals.cpp b/src/Globals.cpp index 15cba11777..d44c41d9d8 100644 --- a/src/Globals.cpp +++ b/src/Globals.cpp @@ -1,6 +1,7 @@ #include "Globals.h" #include "Deferred.h" +#include "DX12Interop.h" #include "Features/CloudShadows.h" #include "Features/DynamicCubemaps.h" #include "Features/ExponentialHeightFog.h" @@ -156,6 +157,7 @@ namespace globals Deferred* deferred = nullptr; Menu* menu = nullptr; SIE::ShaderCache* shaderCache = nullptr; + DX12Interop* dx12Interop = nullptr; static Profiler profilerInstance; Profiler* profiler = &profilerInstance; @@ -166,6 +168,7 @@ namespace globals state = State::GetSingleton(); menu = Menu::GetSingleton(); deferred = Deferred::GetSingleton(); + dx12Interop = DX12Interop::GetSingleton(); } void ReInit() diff --git a/src/Globals.h b/src/Globals.h index 9fd5bb081c..d944ee8128 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -37,6 +37,7 @@ struct CSEditor; struct ExponentialHeightFog; struct HDRDisplay; struct ScreenshotFeature; +struct DX12Interop; struct Skin; class State; @@ -264,6 +265,7 @@ namespace globals extern Deferred* deferred; extern Menu* menu; extern SIE::ShaderCache* shaderCache; + extern DX12Interop* dx12Interop; extern Profiler* profiler; void OnInit(); diff --git a/src/State.cpp b/src/State.cpp index 9bc07e59fd..47f6f6b455 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -5,6 +5,7 @@ #include #include "Deferred.h" +#include "DX12Interop.h" #include "FeatureIssues.h" #include "Features/CSEditor.h" #include "Features/CloudShadows.h" @@ -223,6 +224,7 @@ void State::Setup() Feature::ForEachLoadedFeature("SetupResources", [](Feature* feature) { feature->SetupResources(); }); globals::deferred->SetupResources(); + globals::dx12Interop->SetupResources(); // Load per-weather settings after features are setup WeatherManager::GetSingleton()->LoadPerWeatherSettingsFromDisk();