1445 lines
46 KiB
C++
1445 lines
46 KiB
C++
|
// This file is part of the FidelityFX Super Resolution 3.1 Unreal Engine Plugin.
|
||
|
//
|
||
|
// Copyright (c) 2023-2025 Advanced Micro Devices, Inc. All rights reserved.
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
// of this software and associated documentation files (the "Software"), to deal
|
||
|
// in the Software without restriction, including without limitation the rights
|
||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
// copies of the Software, and to permit persons to whom the Software is
|
||
|
// furnished to do so, subject to the following conditions:
|
||
|
// The above copyright notice and this permission notice shall be included in
|
||
|
// all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
// THE SOFTWARE.
|
||
|
|
||
|
#include "FFXD3D12Backend.h"
|
||
|
#include "FFXSharedBackend.h"
|
||
|
#include "../../FFXFrameInterpolation/Public/FFXFrameInterpolationModule.h"
|
||
|
#include "../../FFXFrameInterpolation/Public/IFFXFrameInterpolation.h"
|
||
|
#include "CoreMinimal.h"
|
||
|
#include "Interfaces/IPluginManager.h"
|
||
|
#include "RenderGraphResources.h"
|
||
|
#include "Features/IModularFeatures.h"
|
||
|
#include "FFXFSR3Settings.h"
|
||
|
|
||
|
#include "FFXFrameInterpolationApi.h"
|
||
|
#include "FFXD3D12Includes.h"
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 2, 0)
|
||
|
#define FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V1 1
|
||
|
#else
|
||
|
#define FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V1 0
|
||
|
#endif
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 3, 0)
|
||
|
#define FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V2 1
|
||
|
#else
|
||
|
#define FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V2 0
|
||
|
#endif
|
||
|
|
||
|
#if FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V1
|
||
|
#include "Windows/IDXGISwapchainProvider.h"
|
||
|
#endif
|
||
|
|
||
|
#include "IAntiLag2.h"
|
||
|
|
||
|
#include <mutex>
|
||
|
|
||
|
#define LOCTEXT_NAMESPACE "FFXD3D12Backend"
|
||
|
|
||
|
DECLARE_LOG_CATEGORY_EXTERN(LogFFXD3D12, Verbose, All);
|
||
|
DEFINE_LOG_CATEGORY(LogFFXD3D12);
|
||
|
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
// Definitions and includes to interact with the internals of the D3D12RHI.
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
#if PLATFORM_WINDOWS
|
||
|
#define GetD3D11CubeFace GetD3D12CubeFace
|
||
|
#define VerifyD3D11Result VerifyD3D12Result
|
||
|
#define GetD3D11TextureFromRHITexture GetD3D12TextureFromRHITexture
|
||
|
#define FRingAllocation FRingAllocation_D3D12
|
||
|
#define GetRenderTargetFormat GetRenderTargetFormat_D3D12
|
||
|
#define ED3D11ShaderOffsetBuffer ED3D12ShaderOffsetBuffer
|
||
|
#define FindShaderResourceDXGIFormat FindShaderResourceDXGIFormat_D3D12
|
||
|
#define FindUnorderedAccessDXGIFormat FindUnorderedAccessDXGIFormat_D3D12
|
||
|
#define FindDepthStencilDXGIFormat FindDepthStencilDXGIFormat_D3D12
|
||
|
#define HasStencilBits HasStencilBits_D3D12
|
||
|
#define FVector4VertexDeclaration FVector4VertexDeclaration_D3D12
|
||
|
#define GLOBAL_CONSTANT_BUFFER_INDEX GLOBAL_CONSTANT_BUFFER_INDEX_D3D12
|
||
|
#define MAX_CONSTANT_BUFFER_SLOTS MAX_CONSTANT_BUFFER_SLOTS_D3D12
|
||
|
#define FD3DGPUProfiler FD3D12GPUProfiler
|
||
|
#define FRangeAllocator FRangeAllocator_D3D12
|
||
|
|
||
|
#ifndef WITH_NVAPI
|
||
|
#define WITH_NVAPI 0
|
||
|
#endif
|
||
|
#ifndef NV_AFTERMATH
|
||
|
#define NV_AFTERMATH 0
|
||
|
#endif // !NV_AFTERMATH
|
||
|
#ifndef INTEL_EXTENSIONS
|
||
|
#define INTEL_EXTENSIONS 0
|
||
|
#endif // !INTEL_EXTENSIONS
|
||
|
|
||
|
#include "D3D12RHIPrivate.h"
|
||
|
#include "D3D12Util.h"
|
||
|
|
||
|
#undef GetD3D11CubeFace
|
||
|
#undef VerifyD3D11Result
|
||
|
#undef GetD3D11TextureFromRHITexture
|
||
|
#undef FRingAllocation
|
||
|
#undef GetRenderTargetFormat
|
||
|
#undef ED3D11ShaderOffsetBuffer
|
||
|
#undef FindShaderResourceDXGIFormat
|
||
|
#undef FindUnorderedAccessDXGIFormat
|
||
|
#undef FindDepthStencilDXGIFormat
|
||
|
#undef HasStencilBits
|
||
|
#undef FVector4VertexDeclaration
|
||
|
#undef GLOBAL_CONSTANT_BUFFER_INDEX
|
||
|
#undef MAX_CONSTANT_BUFFER_SLOTS
|
||
|
#undef FD3DGPUProfiler
|
||
|
#undef FRangeAllocator
|
||
|
#endif // PLATFORM_WINDOWS
|
||
|
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
// Helper variable declarations.
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
IMPLEMENT_MODULE(FFXD3D12BackendModule, FFXD3D12Backend)
|
||
|
extern ENGINE_API float GAverageFPS;
|
||
|
extern ENGINE_API float GAverageMS;
|
||
|
#if FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V1
|
||
|
TCHAR SwapChainProviderName[] = TEXT("FSR3SwapchainProvider");
|
||
|
#endif
|
||
|
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
// Static helper functions.
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
static EPixelFormat ffxGetSurfaceFormatDX12ToUE(DXGI_FORMAT format)
|
||
|
{
|
||
|
EPixelFormat UEFormat = PF_Unknown;
|
||
|
for (uint32 i = 0; i < PF_MAX; i++)
|
||
|
{
|
||
|
DXGI_FORMAT PlatformFormat = (DXGI_FORMAT)GPixelFormats[i].PlatformFormat;
|
||
|
if (PlatformFormat == format)
|
||
|
{
|
||
|
UEFormat = (EPixelFormat)i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return UEFormat;
|
||
|
}
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
#define FFX_TEXTURE_CREATE_FLAGS(Name) ETextureCreateFlags::Name
|
||
|
#else
|
||
|
#define FFX_TEXTURE_CREATE_FLAGS(Name) TexCreate_##Name
|
||
|
#endif
|
||
|
|
||
|
static ETextureCreateFlags ffxGetSurfaceFlagsDX12ToUE(D3D12_RESOURCE_FLAGS flags)
|
||
|
{
|
||
|
ETextureCreateFlags NewFlags = FFX_TEXTURE_CREATE_FLAGS(None);
|
||
|
switch(flags)
|
||
|
{
|
||
|
case D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET:
|
||
|
NewFlags |= FFX_TEXTURE_CREATE_FLAGS(RenderTargetable);
|
||
|
NewFlags |= FFX_TEXTURE_CREATE_FLAGS(ShaderResource);
|
||
|
break;
|
||
|
case D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL:
|
||
|
NewFlags |= FFX_TEXTURE_CREATE_FLAGS(DepthStencilTargetable);
|
||
|
NewFlags |= FFX_TEXTURE_CREATE_FLAGS(ShaderResource);
|
||
|
break;
|
||
|
case D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS:
|
||
|
NewFlags |= FFX_TEXTURE_CREATE_FLAGS(UAV);
|
||
|
NewFlags |= FFX_TEXTURE_CREATE_FLAGS(ShaderResource);
|
||
|
break;
|
||
|
case D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE:
|
||
|
NewFlags |= FFX_TEXTURE_CREATE_FLAGS(DisableSRVCreation);
|
||
|
NewFlags &= ~FFX_TEXTURE_CREATE_FLAGS(ShaderResource);
|
||
|
break;
|
||
|
case D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER:
|
||
|
case D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS:
|
||
|
NewFlags |= FFX_TEXTURE_CREATE_FLAGS(Shared);
|
||
|
break;
|
||
|
case D3D12_RESOURCE_FLAG_VIDEO_DECODE_REFERENCE_ONLY:
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
case D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY:
|
||
|
#endif
|
||
|
case D3D12_RESOURCE_FLAG_NONE:
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return NewFlags;
|
||
|
}
|
||
|
|
||
|
// {BEED74B2-282E-4AA3-BBF7-534560507A45}
|
||
|
static const GUID IID_IFfxFrameInterpolationSwapChain = { 0xbeed74b2, 0x282e, 0x4aa3, {0xbb, 0xf7, 30, 0x45, 0x60, 0x50, 0x7a, 0x45} };
|
||
|
static const GUID IID_IFfxD3D12SwapChain = { 0x51a371da, 0x9961, 0x438c, {0x81, 0x21, 0xe2, 0x67, 0xf4, 0x30, 0x78, 0x30} };
|
||
|
|
||
|
class DECLSPEC_UUID("51A371DA-9961-438C-8121-E267F4307830") FFXD3D12SwapChain : public IDXGISwapChain4, private FThreadSafeRefCountedObject
|
||
|
{
|
||
|
public:
|
||
|
FFXD3D12SwapChain(IFFXSharedBackend* InBackend, ffxContext InContext, IDXGISwapChain4* InSwapChain)
|
||
|
: Context(InContext)
|
||
|
, SwapChain(InSwapChain)
|
||
|
, Backend(InBackend)
|
||
|
{
|
||
|
}
|
||
|
virtual ~FFXD3D12SwapChain()
|
||
|
{
|
||
|
Backend->ffxDestroyContext(&Context);
|
||
|
}
|
||
|
|
||
|
ffxContext* GetContext()
|
||
|
{
|
||
|
return &Context;
|
||
|
}
|
||
|
|
||
|
IDXGISwapChain4* GetSwapChain()
|
||
|
{
|
||
|
return SwapChain;
|
||
|
}
|
||
|
|
||
|
// IUnknown
|
||
|
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)
|
||
|
{
|
||
|
const GUID guidReplacements[] = {
|
||
|
__uuidof(this),
|
||
|
IID_IUnknown,
|
||
|
IID_IDXGIObject,
|
||
|
IID_IDXGIDeviceSubObject,
|
||
|
IID_IDXGISwapChain,
|
||
|
IID_IDXGISwapChain1,
|
||
|
IID_IDXGISwapChain2,
|
||
|
IID_IDXGISwapChain3,
|
||
|
IID_IDXGISwapChain4,
|
||
|
IID_IFfxD3D12SwapChain
|
||
|
};
|
||
|
|
||
|
for (auto guid : guidReplacements)
|
||
|
{
|
||
|
if (IsEqualGUID(riid, guid) == 1)
|
||
|
{
|
||
|
AddRef();
|
||
|
*ppvObject = this;
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (IsEqualGUID(riid, IID_IFfxFrameInterpolationSwapChain) == 1)
|
||
|
{
|
||
|
SwapChain->AddRef();
|
||
|
*ppvObject = SwapChain;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
ULONG STDMETHODCALLTYPE AddRef(void) final
|
||
|
{
|
||
|
return FThreadSafeRefCountedObject::AddRef();
|
||
|
}
|
||
|
|
||
|
ULONG STDMETHODCALLTYPE Release(void) final
|
||
|
{
|
||
|
return FThreadSafeRefCountedObject::Release();
|
||
|
}
|
||
|
|
||
|
// IDXGIObject
|
||
|
virtual HRESULT STDMETHODCALLTYPE SetPrivateData(REFGUID Name, UINT DataSize, const void* pData)
|
||
|
{
|
||
|
return SwapChain->SetPrivateData(Name, DataSize, pData);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(REFGUID Name, const IUnknown* pUnknown)
|
||
|
{
|
||
|
return SwapChain->SetPrivateDataInterface(Name, pUnknown);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetPrivateData(REFGUID Name, UINT* pDataSize, void* pData)
|
||
|
{
|
||
|
return SwapChain->GetPrivateData(Name, pDataSize, pData);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetParent(REFIID riid, void** ppParent)
|
||
|
{
|
||
|
return SwapChain->GetParent(riid, ppParent);
|
||
|
}
|
||
|
|
||
|
// IDXGIDeviceSubObject
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetDevice(REFIID riid, void** ppDevice)
|
||
|
{
|
||
|
return SwapChain->GetDevice(riid, ppDevice);
|
||
|
}
|
||
|
|
||
|
// IDXGISwapChain1
|
||
|
virtual HRESULT STDMETHODCALLTYPE Present(UINT SyncInterval, UINT Flags)
|
||
|
{
|
||
|
return SwapChain->Present(SyncInterval, Flags);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetBuffer(UINT Buffer, REFIID riid, void** ppSurface)
|
||
|
{
|
||
|
return SwapChain->GetBuffer(Buffer, riid, ppSurface);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE SetFullscreenState(BOOL Fullscreen, IDXGIOutput* pTarget)
|
||
|
{
|
||
|
return SwapChain->SetFullscreenState(Fullscreen, pTarget);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetFullscreenState(BOOL* pFullscreen, IDXGIOutput** ppTarget)
|
||
|
{
|
||
|
return SwapChain->GetFullscreenState(pFullscreen, ppTarget);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetDesc(DXGI_SWAP_CHAIN_DESC* pDesc)
|
||
|
{
|
||
|
return SwapChain->GetDesc(pDesc);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE ResizeBuffers(UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags)
|
||
|
{
|
||
|
return SwapChain->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE ResizeTarget(const DXGI_MODE_DESC* pNewTargetParameters)
|
||
|
{
|
||
|
return SwapChain->ResizeTarget(pNewTargetParameters);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetContainingOutput(IDXGIOutput** ppOutput)
|
||
|
{
|
||
|
return SwapChain->GetContainingOutput(ppOutput);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats)
|
||
|
{
|
||
|
return SwapChain->GetFrameStatistics(pStats);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetLastPresentCount(UINT* pLastPresentCount)
|
||
|
{
|
||
|
return SwapChain->GetLastPresentCount(pLastPresentCount);
|
||
|
}
|
||
|
|
||
|
// IDXGISwapChain1
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetDesc1(DXGI_SWAP_CHAIN_DESC1* pDesc)
|
||
|
{
|
||
|
return SwapChain->GetDesc1(pDesc);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetFullscreenDesc(DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc)
|
||
|
{
|
||
|
return SwapChain->GetFullscreenDesc(pDesc);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetHwnd(HWND* pHwnd)
|
||
|
{
|
||
|
return SwapChain->GetHwnd(pHwnd);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetCoreWindow(REFIID refiid, void** ppUnk)
|
||
|
{
|
||
|
return SwapChain->GetCoreWindow(refiid, ppUnk);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE Present1(UINT SyncInterval, UINT PresentFlags, const DXGI_PRESENT_PARAMETERS* pPresentParameters)
|
||
|
{
|
||
|
return SwapChain->Present1(SyncInterval, PresentFlags, pPresentParameters);
|
||
|
}
|
||
|
virtual BOOL STDMETHODCALLTYPE IsTemporaryMonoSupported(void)
|
||
|
{
|
||
|
return SwapChain->IsTemporaryMonoSupported();
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetRestrictToOutput(IDXGIOutput** ppRestrictToOutput)
|
||
|
{
|
||
|
return SwapChain->GetRestrictToOutput(ppRestrictToOutput);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE SetBackgroundColor(const DXGI_RGBA* pColor)
|
||
|
{
|
||
|
return SwapChain->SetBackgroundColor(pColor);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetBackgroundColor(DXGI_RGBA* pColor)
|
||
|
{
|
||
|
return SwapChain->GetBackgroundColor(pColor);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE SetRotation(DXGI_MODE_ROTATION Rotation)
|
||
|
{
|
||
|
return SwapChain->SetRotation(Rotation);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetRotation(DXGI_MODE_ROTATION* pRotation)
|
||
|
{
|
||
|
return SwapChain->GetRotation(pRotation);
|
||
|
}
|
||
|
|
||
|
// IDXGISwapChain2
|
||
|
virtual HRESULT STDMETHODCALLTYPE SetSourceSize(UINT Width, UINT Height)
|
||
|
{
|
||
|
return SwapChain->SetSourceSize(Width, Height);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetSourceSize(UINT* pWidth, UINT* pHeight)
|
||
|
{
|
||
|
return SwapChain->GetSourceSize(pWidth, pHeight);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency(UINT MaxLatency)
|
||
|
{
|
||
|
return SwapChain->SetMaximumFrameLatency(MaxLatency);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency(UINT* pMaxLatency)
|
||
|
{
|
||
|
return SwapChain->GetMaximumFrameLatency(pMaxLatency);
|
||
|
}
|
||
|
virtual HANDLE STDMETHODCALLTYPE GetFrameLatencyWaitableObject(void)
|
||
|
{
|
||
|
return SwapChain->GetFrameLatencyWaitableObject();
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE SetMatrixTransform(const DXGI_MATRIX_3X2_F* pMatrix)
|
||
|
{
|
||
|
return SwapChain->SetMatrixTransform(pMatrix);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE GetMatrixTransform(DXGI_MATRIX_3X2_F* pMatrix)
|
||
|
{
|
||
|
return SwapChain->GetMatrixTransform(pMatrix);
|
||
|
}
|
||
|
|
||
|
// IDXGISwapChain3
|
||
|
virtual UINT STDMETHODCALLTYPE GetCurrentBackBufferIndex(void)
|
||
|
{
|
||
|
return SwapChain->GetCurrentBackBufferIndex();
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE CheckColorSpaceSupport(DXGI_COLOR_SPACE_TYPE ColorSpace, UINT* pColorSpaceSupport)
|
||
|
{
|
||
|
return SwapChain->CheckColorSpaceSupport(ColorSpace, pColorSpaceSupport);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE SetColorSpace1(DXGI_COLOR_SPACE_TYPE ColorSpace)
|
||
|
{
|
||
|
return SwapChain->SetColorSpace1(ColorSpace);
|
||
|
}
|
||
|
virtual HRESULT STDMETHODCALLTYPE ResizeBuffers1(UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT Format, UINT SwapChainFlags, const UINT* pCreationNodeMask, IUnknown* const* ppPresentQueue)
|
||
|
{
|
||
|
return SwapChain->ResizeBuffers1(BufferCount, Width, Height, Format, SwapChainFlags, pCreationNodeMask, ppPresentQueue);
|
||
|
}
|
||
|
|
||
|
// IDXGISwapChain4
|
||
|
virtual HRESULT STDMETHODCALLTYPE SetHDRMetaData(DXGI_HDR_METADATA_TYPE Type, UINT Size, void* pMetaData)
|
||
|
{
|
||
|
return SwapChain->SetHDRMetaData(Type, Size, pMetaData);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
ffxContext Context;
|
||
|
IDXGISwapChain4* SwapChain;
|
||
|
IFFXSharedBackend* Backend;
|
||
|
};
|
||
|
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
// The D3D12 implementation of the FFX shared backend that interacts with the D3D12RHI.
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
class FFXD3D12Backend : public IFFXSharedBackend
|
||
|
{
|
||
|
struct FFXFrameResources
|
||
|
{
|
||
|
TRefCountPtr<FRHIResource> FIResources;
|
||
|
TRefCountPtr<IRefCountedObject> FSR3Resources;
|
||
|
};
|
||
|
|
||
|
FFXSharedAllocCallbacks AllocCbs;
|
||
|
ffxFunctions FfxFunctions;
|
||
|
void* FfxModule;
|
||
|
TQueue<TPair<uint64, FFXFrameResources>> FrameResources;
|
||
|
uint32 NumPresentsIssued;
|
||
|
static double LastTime;
|
||
|
static float AverageTime;
|
||
|
static float AverageFPS;
|
||
|
public:
|
||
|
static FFXD3D12Backend sFFXD3D12Backend;
|
||
|
|
||
|
FFXD3D12Backend()
|
||
|
{
|
||
|
NumPresentsIssued = 0;
|
||
|
FMemory::Memzero(FfxFunctions);
|
||
|
FfxModule = nullptr;
|
||
|
}
|
||
|
|
||
|
virtual ~FFXD3D12Backend()
|
||
|
{
|
||
|
if (FfxModule)
|
||
|
{
|
||
|
FPlatformProcess::FreeDllHandle(FfxModule);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool LoadDLL()
|
||
|
{
|
||
|
bool bOk = false;
|
||
|
|
||
|
FString Name = TEXT("amd_fidelityfx_dx12.dll");
|
||
|
|
||
|
#if WITH_EDITOR
|
||
|
FModuleStatus ModuleStatus;
|
||
|
if (FModuleManager::Get().QueryModule(TEXT("FFXD3D12Backend"), ModuleStatus))
|
||
|
{
|
||
|
FString Dir = FPaths::Combine(FPaths::GetPath(ModuleStatus.FilePath), TEXT("../../Source/fidelityfx-sdk/PrebuiltSignedDLL"));
|
||
|
FPaths::CollapseRelativeDirectories(Dir);
|
||
|
FPlatformProcess::AddDllDirectory(*Dir);
|
||
|
Name = FPaths::Combine(Dir, Name);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
FfxModule = FPlatformProcess::GetDllHandle(*Name);
|
||
|
if (FfxModule)
|
||
|
{
|
||
|
ffxLoadFunctions(&FfxFunctions, (HMODULE)FfxModule);
|
||
|
bOk = true;
|
||
|
}
|
||
|
|
||
|
return bOk;
|
||
|
}
|
||
|
|
||
|
ffxReturnCode_t ffxCreateContext(ffxContext* context, ffxCreateContextDescHeader* desc) final
|
||
|
{
|
||
|
ffxCreateBackendDX12Desc Dx12Header;
|
||
|
Dx12Header.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_DX12;
|
||
|
Dx12Header.header.pNext = nullptr;
|
||
|
Dx12Header.device = (ID3D12Device*)GDynamicRHI->RHIGetNativeDevice();
|
||
|
desc->pNext = (ffxApiHeader*)&Dx12Header;
|
||
|
|
||
|
return FfxFunctions.CreateContext(context, desc, &AllocCbs.Cbs);
|
||
|
}
|
||
|
|
||
|
ffxReturnCode_t ffxDestroyContext(ffxContext* context) final
|
||
|
{
|
||
|
return FfxFunctions.DestroyContext(context, &AllocCbs.Cbs);
|
||
|
}
|
||
|
|
||
|
ffxReturnCode_t ffxConfigure(ffxContext* context, const ffxConfigureDescHeader* desc) final
|
||
|
{
|
||
|
return FfxFunctions.Configure(context, desc);
|
||
|
}
|
||
|
|
||
|
ffxReturnCode_t ffxQuery(ffxContext* context, ffxQueryDescHeader* desc) final
|
||
|
{
|
||
|
return FfxFunctions.Query(context, desc);
|
||
|
}
|
||
|
|
||
|
ffxReturnCode_t ffxDispatch(ffxContext* context, const ffxDispatchDescHeader* desc) final
|
||
|
{
|
||
|
return FfxFunctions.Dispatch(context, desc);
|
||
|
}
|
||
|
|
||
|
void Init() final
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
EFFXBackendAPI GetAPI() const
|
||
|
{
|
||
|
return EFFXBackendAPI::D3D12;
|
||
|
}
|
||
|
void SetFeatureLevel(ffxContext* context, ERHIFeatureLevel::Type FeatureLevel) final
|
||
|
{
|
||
|
|
||
|
}
|
||
|
FfxSwapchain GetSwapchain(void* swapChain) final
|
||
|
{
|
||
|
FFXD3D12SwapChain* SwapChain4 = nullptr;
|
||
|
if (swapChain)
|
||
|
{
|
||
|
((IDXGISwapChain1*)swapChain)->QueryInterface<FFXD3D12SwapChain>(&SwapChain4);
|
||
|
if (SwapChain4)
|
||
|
((IDXGISwapChain1*)swapChain)->Release();
|
||
|
}
|
||
|
|
||
|
return reinterpret_cast<FfxSwapchain>(SwapChain4);
|
||
|
}
|
||
|
|
||
|
FfxApiResource GetNativeResource(FRHITexture* Texture, FfxApiResourceState State) final
|
||
|
{
|
||
|
return ffxApiGetResourceDX12((ID3D12Resource*)Texture->GetNativeResource(), State);
|
||
|
}
|
||
|
|
||
|
FfxApiResource GetNativeResource(FRDGTexture* Texture, FfxApiResourceState State) final
|
||
|
{
|
||
|
return GetNativeResource(Texture->GetRHI(), State);
|
||
|
}
|
||
|
|
||
|
FfxCommandList GetNativeCommandBuffer(FRHICommandListImmediate& RHICmdList, FRHITexture* Texture) final
|
||
|
{
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
ID3D12DynamicRHI* DynamicRHI = GetID3D12DynamicRHI();
|
||
|
uint32 const DeviceIndex = DynamicRHI->RHIGetResourceDeviceIndex(Texture);
|
||
|
FfxCommandList CmdList = reinterpret_cast<FfxCommandList>((ID3D12CommandList*)DynamicRHI->RHIGetGraphicsCommandList(RHICmdList, DeviceIndex));
|
||
|
#elif UE_VERSION_AT_LEAST(5, 1, 0)
|
||
|
ID3D12DynamicRHI* DynamicRHI = GetID3D12DynamicRHI();
|
||
|
uint32 const DeviceIndex = DynamicRHI->RHIGetResourceDeviceIndex(Texture);
|
||
|
FfxCommandList CmdList = reinterpret_cast<FfxCommandList>((ID3D12CommandList*)DynamicRHI->RHIGetGraphicsCommandList(DeviceIndex));
|
||
|
#else
|
||
|
void* CmdList = ((FD3D12CommandContext&)RHICmdList.GetContext()).CommandListHandle.GraphicsCommandList();
|
||
|
#endif
|
||
|
return CmdList;
|
||
|
}
|
||
|
|
||
|
FfxShaderModel GetSupportedShaderModel()
|
||
|
{
|
||
|
FfxShaderModel ShaderModel = FFX_SHADER_MODEL_5_1;
|
||
|
ID3D12Device* dx12Device = (ID3D12Device*)GDynamicRHI->RHIGetNativeDevice();
|
||
|
D3D12_FEATURE_DATA_SHADER_MODEL shaderModel = { D3D_SHADER_MODEL_6_6 };
|
||
|
if (dx12Device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shaderModel, sizeof(D3D12_FEATURE_DATA_SHADER_MODEL)) >= 0)
|
||
|
{
|
||
|
switch (shaderModel.HighestShaderModel)
|
||
|
{
|
||
|
case D3D_SHADER_MODEL_5_1:
|
||
|
ShaderModel = FFX_SHADER_MODEL_5_1;
|
||
|
break;
|
||
|
case D3D_SHADER_MODEL_6_0:
|
||
|
ShaderModel = FFX_SHADER_MODEL_6_0;
|
||
|
break;
|
||
|
case D3D_SHADER_MODEL_6_1:
|
||
|
ShaderModel = FFX_SHADER_MODEL_6_1;
|
||
|
break;
|
||
|
case D3D_SHADER_MODEL_6_2:
|
||
|
ShaderModel = FFX_SHADER_MODEL_6_2;
|
||
|
break;
|
||
|
case D3D_SHADER_MODEL_6_3:
|
||
|
ShaderModel = FFX_SHADER_MODEL_6_3;
|
||
|
break;
|
||
|
case D3D_SHADER_MODEL_6_4:
|
||
|
ShaderModel = FFX_SHADER_MODEL_6_4;
|
||
|
break;
|
||
|
case D3D_SHADER_MODEL_6_5:
|
||
|
ShaderModel = FFX_SHADER_MODEL_6_5;
|
||
|
break;
|
||
|
case D3D_SHADER_MODEL_6_6:
|
||
|
default:
|
||
|
ShaderModel = FFX_SHADER_MODEL_6_6;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ShaderModel;
|
||
|
}
|
||
|
bool IsFloat16Supported()
|
||
|
{
|
||
|
bool bIsSupported = false;
|
||
|
ID3D12Device* dx12Device = (ID3D12Device*)GDynamicRHI->RHIGetNativeDevice();
|
||
|
// check if we have 16bit floating point.
|
||
|
D3D12_FEATURE_DATA_D3D12_OPTIONS d3d12Options = {};
|
||
|
if (SUCCEEDED(dx12Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &d3d12Options, sizeof(d3d12Options)))) {
|
||
|
|
||
|
bIsSupported = !!(d3d12Options.MinPrecisionSupport & D3D12_SHADER_MIN_PRECISION_SUPPORT_16_BIT);
|
||
|
}
|
||
|
return bIsSupported;
|
||
|
}
|
||
|
|
||
|
static D3D12_RESOURCE_STATES GetDX12StateFromResourceState(FfxResourceStates state)
|
||
|
{
|
||
|
switch (state) {
|
||
|
|
||
|
case(FFX_RESOURCE_STATE_GENERIC_READ):
|
||
|
return D3D12_RESOURCE_STATE_GENERIC_READ;
|
||
|
case(FFX_RESOURCE_STATE_UNORDERED_ACCESS):
|
||
|
return D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||
|
case (FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ):
|
||
|
return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||
|
case(FFX_RESOURCE_STATE_COMPUTE_READ):
|
||
|
return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||
|
case (FFX_RESOURCE_STATE_PIXEL_READ):
|
||
|
return D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||
|
case FFX_RESOURCE_STATE_COPY_SRC:
|
||
|
return D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||
|
case FFX_RESOURCE_STATE_COPY_DEST:
|
||
|
return D3D12_RESOURCE_STATE_COPY_DEST;
|
||
|
case FFX_RESOURCE_STATE_INDIRECT_ARGUMENT:
|
||
|
return D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
|
||
|
default:
|
||
|
return D3D12_RESOURCE_STATE_COMMON;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ForceUAVTransition(FRHICommandListImmediate& RHICmdList, FRHITexture* OutputTexture, ERHIAccess Access)
|
||
|
{
|
||
|
FRHITransitionInfo Info(OutputTexture, ERHIAccess::Unknown, Access);
|
||
|
RHICmdList.Transition(Info);
|
||
|
}
|
||
|
|
||
|
static FfxResource FFXConvertResource(FfxApiResource ApiResource)
|
||
|
{
|
||
|
FfxResource Resource;
|
||
|
FMemory::Memzero(Resource);
|
||
|
|
||
|
Resource.resource = ApiResource.resource;
|
||
|
Resource.state = (FfxResourceStates)ApiResource.state;
|
||
|
memcpy(&Resource.description, &ApiResource.description, sizeof(FfxResourceDescription));
|
||
|
|
||
|
return Resource;
|
||
|
}
|
||
|
|
||
|
static ffxReturnCode_t FFXFrameInterpolationUiCompositionCallback(ffxCallbackDescFrameGenerationPresent* params, void* unusedUserCtx)
|
||
|
{
|
||
|
if (!params->isGeneratedFrame)
|
||
|
{
|
||
|
sFFXD3D12Backend.ReleaseFrameResources(params->frameID);
|
||
|
}
|
||
|
|
||
|
FfxPresentCallbackDescription PresentParams;
|
||
|
PresentParams.device = (FfxDevice)params->device;
|
||
|
PresentParams.commandList = (FfxCommandList)params->commandList;
|
||
|
PresentParams.isInterpolatedFrame = params->isGeneratedFrame;
|
||
|
PresentParams.frameID = params->frameID;
|
||
|
PresentParams.usePremulAlpha = false;
|
||
|
PresentParams.currentBackBuffer = FFXConvertResource(params->currentBackBuffer);
|
||
|
PresentParams.currentUI = FFXConvertResource(params->currentUI);
|
||
|
PresentParams.outputSwapChainBuffer = FFXConvertResource(params->outputSwapChainBuffer);
|
||
|
|
||
|
ffxFrameInterpolationUiComposition(&PresentParams, unusedUserCtx);
|
||
|
|
||
|
if (params->isGeneratedFrame)
|
||
|
{
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_BEGIN_DX12(TEXT("FFXFrameInterpolationUiCompositionCallback"));
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_ADD_DX12(params->device, params->commandList, PresentParams.outputSwapChainBuffer.resource, (uint32_t)GetDX12StateFromResourceState(PresentParams.outputSwapChainBuffer.state), 3, "SwapChainBuffer");
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_END_DX12;
|
||
|
}
|
||
|
|
||
|
{
|
||
|
double CurrentTime = FPlatformTime::Seconds();
|
||
|
float FrameTimeMS = (float)((CurrentTime - LastTime) * 1000.0);
|
||
|
AverageTime = AverageTime * 0.75f + FrameTimeMS * 0.25f;
|
||
|
LastTime = CurrentTime;
|
||
|
AverageFPS = 1000.f / AverageTime;
|
||
|
|
||
|
if (CVarFFXFIUpdateGlobalFrameTime.GetValueOnAnyThread() != 0 && (CVarEnableFFXFI.GetValueOnAnyThread() != 0))
|
||
|
{
|
||
|
GAverageMS = AverageTime;
|
||
|
GAverageFPS = AverageFPS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IAntiLag2Module* AntiLag2Interface = (IAntiLag2Module*)FModuleManager::Get().GetModule(TEXT("AntiLag2"));
|
||
|
if (AntiLag2Interface)
|
||
|
{
|
||
|
AntiLag2Interface->SetFrameType(PresentParams.isInterpolatedFrame);
|
||
|
}
|
||
|
|
||
|
return FFX_API_RETURN_OK;
|
||
|
}
|
||
|
|
||
|
void UpdateSwapChain(ffxContext* Context, ffxConfigureDescFrameGeneration& Desc)
|
||
|
{
|
||
|
if (Context)
|
||
|
{
|
||
|
FFXD3D12SwapChain* SwapChain = (FFXD3D12SwapChain*)Desc.swapChain;
|
||
|
Desc.swapChain = Desc.swapChain ? SwapChain->GetSwapChain() : nullptr;
|
||
|
Desc.presentCallback = Desc.swapChain ? &FFXFrameInterpolationUiCompositionCallback : nullptr;
|
||
|
|
||
|
auto Code = ffxConfigure(Context, &Desc.header);
|
||
|
check(Code == FFX_API_RETURN_OK);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void UpdateSwapChain(ffxContext* Context, ffxConfigureDescFrameGeneration& Desc, ffxConfigureDescFrameGenerationRegisterDistortionFieldResource& DescDistortion)
|
||
|
{
|
||
|
Desc.header.pNext = &(DescDistortion.header);
|
||
|
UpdateSwapChain(Context, Desc);
|
||
|
}
|
||
|
|
||
|
FfxApiResource GetInterpolationOutput(FfxSwapchain SwapChain)
|
||
|
{
|
||
|
FfxApiResource Output;
|
||
|
FMemory::Memzero(Output);
|
||
|
|
||
|
FFXD3D12SwapChain* D3D12Swapchain = (FFXD3D12SwapChain*)SwapChain;
|
||
|
if (D3D12Swapchain)
|
||
|
{
|
||
|
ffxQueryDescFrameGenerationSwapChainInterpolationTextureDX12 Desc;
|
||
|
FMemory::Memzero(Desc);
|
||
|
Desc.header.type = FFX_API_QUERY_DESC_TYPE_FRAMEGENERATIONSWAPCHAIN_INTERPOLATIONTEXTURE_DX12;
|
||
|
Desc.pOutTexture = &Output;
|
||
|
auto Code = ffxQuery(D3D12Swapchain->GetContext(), &Desc.header);
|
||
|
check(Code == FFX_API_RETURN_OK);
|
||
|
}
|
||
|
|
||
|
return Output;
|
||
|
}
|
||
|
|
||
|
void* GetInterpolationCommandList(FfxSwapchain SwapChain)
|
||
|
{
|
||
|
FfxCommandList CmdList = nullptr;
|
||
|
|
||
|
FFXD3D12SwapChain* D3D12Swapchain = (FFXD3D12SwapChain*)SwapChain;
|
||
|
if (D3D12Swapchain)
|
||
|
{
|
||
|
ffxQueryDescFrameGenerationSwapChainInterpolationCommandListDX12 Desc;
|
||
|
FMemory::Memzero(Desc);
|
||
|
Desc.header.type = FFX_API_QUERY_DESC_TYPE_FRAMEGENERATIONSWAPCHAIN_INTERPOLATIONCOMMANDLIST_DX12;
|
||
|
Desc.pOutCommandList = &CmdList;
|
||
|
auto Code = ffxQuery(D3D12Swapchain->GetContext(), &Desc.header);
|
||
|
check(Code == FFX_API_RETURN_OK);
|
||
|
}
|
||
|
|
||
|
return CmdList;
|
||
|
}
|
||
|
|
||
|
void RegisterFrameResources(FRHIResource* FIResources, uint64 FrameID) final
|
||
|
{
|
||
|
FFXFrameResources Resources;
|
||
|
Resources.FIResources = FIResources;
|
||
|
FrameResources.Enqueue(TPair<uint64, FFXFrameResources>(FrameID, Resources));
|
||
|
}
|
||
|
|
||
|
void ReleaseFrameResources(uint64 FrameID)
|
||
|
{
|
||
|
TPair<uint64, FFXFrameResources> Resources;
|
||
|
while (FrameResources.Peek(Resources) && ((FrameID - (Resources.Key <= FrameID ? Resources.Key : 0llu)) > (3llu)))
|
||
|
{
|
||
|
FrameResources.Pop();
|
||
|
}
|
||
|
NumPresentsIssued = FrameID;
|
||
|
}
|
||
|
|
||
|
bool GetAverageFrameTimes(float& AvgTimeMs, float& AvgFPS) final
|
||
|
{
|
||
|
AvgTimeMs = AverageTime;
|
||
|
AvgFPS = AverageFPS;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CopySubRect(FfxCommandList CmdList, FfxApiResource Src, FfxApiResource Dst, FIntPoint OutputExtents, FIntPoint OutputPoint) final
|
||
|
{
|
||
|
ID3D12GraphicsCommandList* pCmdList = (ID3D12GraphicsCommandList*)CmdList;
|
||
|
ID3D12Resource* SrcRes = (ID3D12Resource*)Src.resource;
|
||
|
ID3D12Resource* DstRes = (ID3D12Resource*)Dst.resource;
|
||
|
|
||
|
D3D12_RESOURCE_BARRIER barriers[2] = {};
|
||
|
barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||
|
barriers[0].Transition.pResource = SrcRes;
|
||
|
barriers[0].Transition.StateBefore = GetDX12StateFromResourceState((FfxResourceStates)Src.state);
|
||
|
barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||
|
|
||
|
barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||
|
barriers[1].Transition.pResource = DstRes;
|
||
|
barriers[1].Transition.StateBefore = GetDX12StateFromResourceState((FfxResourceStates)Dst.state);
|
||
|
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
|
||
|
pCmdList->ResourceBarrier(_countof(barriers), barriers);
|
||
|
|
||
|
CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(SrcRes, 0);
|
||
|
CD3DX12_TEXTURE_COPY_LOCATION DstLoc(DstRes, 0);
|
||
|
CD3DX12_BOX SrcBox(0, 0, OutputExtents.X, OutputExtents.Y);
|
||
|
|
||
|
pCmdList->CopyTextureRegion(&DstLoc, OutputPoint.X, OutputPoint.Y, 0, &SrcLoc, nullptr);
|
||
|
|
||
|
std::swap(barriers[0].Transition.StateBefore, barriers[0].Transition.StateAfter);
|
||
|
std::swap(barriers[1].Transition.StateBefore, barriers[1].Transition.StateAfter);
|
||
|
pCmdList->ResourceBarrier(_countof(barriers), barriers);
|
||
|
}
|
||
|
|
||
|
void Flush(FRHITexture* Tex, FRHICommandListImmediate& RHICmdList) final
|
||
|
{
|
||
|
#if UE_VERSION_OLDER_THAN(5, 1, 0)
|
||
|
RHICmdList.SubmitCommandsHint();
|
||
|
#else
|
||
|
RHICmdList.EnqueueLambda([this, Tex](FRHICommandListImmediate& cmd)
|
||
|
{
|
||
|
ID3D12DynamicRHI* DynamicRHI = GetID3D12DynamicRHI();
|
||
|
uint32 const DeviceIndex = DynamicRHI->RHIGetResourceDeviceIndex(Tex);
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
DynamicRHI->RHIFinishExternalComputeWork(cmd, DeviceIndex, (ID3D12GraphicsCommandList*)GetNativeCommandBuffer(cmd, Tex));
|
||
|
#else
|
||
|
DynamicRHI->RHIFinishExternalComputeWork(DeviceIndex, (ID3D12GraphicsCommandList*)GetNativeCommandBuffer(cmd, Tex));
|
||
|
#endif
|
||
|
});
|
||
|
#endif
|
||
|
}
|
||
|
};
|
||
|
double FFXD3D12Backend::LastTime = FPlatformTime::Seconds();
|
||
|
float FFXD3D12Backend::AverageTime = 0.f;
|
||
|
float FFXD3D12Backend::AverageFPS = 0.f;
|
||
|
FFXD3D12Backend FFXD3D12Backend::sFFXD3D12Backend;
|
||
|
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
// Factory/provider implementation used to create & insert the proxy swapchain.
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
class FFXD3D12BackendDXGIFactory2Wrapper : public IDXGIFactory2,
|
||
|
#if FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V1
|
||
|
public IDXGISwapchainProvider,
|
||
|
#endif
|
||
|
private FThreadSafeRefCountedObject
|
||
|
{
|
||
|
IDXGIFactory* Inner;
|
||
|
IDXGIFactory2* Inner2;
|
||
|
IFFXFrameInterpolation* FFXFrameInterpolation;
|
||
|
FFXD3D12Backend& Backend;
|
||
|
public:
|
||
|
FFXD3D12BackendDXGIFactory2Wrapper(IFFXFrameInterpolation* InFFXFrameInterpolation)
|
||
|
: Inner(nullptr)
|
||
|
, Inner2(nullptr)
|
||
|
, FFXFrameInterpolation(InFFXFrameInterpolation)
|
||
|
, Backend(FFXD3D12Backend::sFFXD3D12Backend)
|
||
|
{
|
||
|
#if FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V1
|
||
|
IModularFeatures::Get().RegisterModularFeature("DXGISwapchainProvider", this);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void Init(IDXGIFactory2* Original)
|
||
|
{
|
||
|
Inner = Original;
|
||
|
Inner2 = Original;
|
||
|
Inner->AddRef();
|
||
|
check(Inner && Inner2);
|
||
|
}
|
||
|
|
||
|
virtual ~FFXD3D12BackendDXGIFactory2Wrapper()
|
||
|
{
|
||
|
#if FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V1
|
||
|
IModularFeatures::Get().UnregisterModularFeature("DXGISwapchainProvider", this);
|
||
|
#endif
|
||
|
if (Inner)
|
||
|
{
|
||
|
Inner->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#if FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V2
|
||
|
const TCHAR* GetProviderName(void) const
|
||
|
{
|
||
|
return SwapChainProviderName;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V1
|
||
|
bool SupportsRHI(ERHIInterfaceType RHIType) const
|
||
|
{
|
||
|
return RHIType == ERHIInterfaceType::D3D12;
|
||
|
}
|
||
|
|
||
|
TCHAR* GetName() const
|
||
|
{
|
||
|
return SwapChainProviderName;
|
||
|
}
|
||
|
|
||
|
HRESULT CreateSwapChainForHwnd(
|
||
|
IDXGIFactory2* pFactory,
|
||
|
IUnknown* pDevice,
|
||
|
HWND hWnd,
|
||
|
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||
|
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullScreenDesc,
|
||
|
IDXGIOutput* pRestrictToOutput,
|
||
|
IDXGISwapChain1** ppSwapChain) override
|
||
|
{
|
||
|
Inner = pFactory;
|
||
|
Inner2 = pFactory;
|
||
|
check(Inner);
|
||
|
HRESULT Res = CreateSwapChainForHwnd(pDevice, hWnd, pDesc, pFullScreenDesc, pRestrictToOutput, ppSwapChain);
|
||
|
Inner = nullptr;
|
||
|
Inner2 = nullptr;
|
||
|
return Res;
|
||
|
}
|
||
|
|
||
|
HRESULT CreateSwapChain(
|
||
|
IDXGIFactory* pFactory,
|
||
|
IUnknown* pDevice,
|
||
|
DXGI_SWAP_CHAIN_DESC* pDesc,
|
||
|
IDXGISwapChain** ppSwapChain) override
|
||
|
{
|
||
|
Inner = pFactory;
|
||
|
check(Inner);
|
||
|
HRESULT Res = CreateSwapChain(pDevice, pDesc, ppSwapChain);
|
||
|
Inner = nullptr;
|
||
|
return Res;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
BOOL STDMETHODCALLTYPE IsWindowedStereoEnabled(void) final
|
||
|
{
|
||
|
return Inner2->IsWindowedStereoEnabled();
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE CreateSwapChainForHwnd(
|
||
|
/* [annotation][in] */
|
||
|
_In_ IUnknown* pDevice,
|
||
|
/* [annotation][in] */
|
||
|
_In_ HWND hWnd,
|
||
|
/* [annotation][in] */
|
||
|
_In_ const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||
|
/* [annotation][in] */
|
||
|
_In_opt_ const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc,
|
||
|
/* [annotation][in] */
|
||
|
_In_opt_ IDXGIOutput* pRestrictToOutput,
|
||
|
/* [annotation][out] */
|
||
|
_COM_Outptr_ IDXGISwapChain1** ppSwapChain) final
|
||
|
{
|
||
|
HRESULT Result = E_INVALIDARG;
|
||
|
FFXD3D12SwapChain* D3D12SwapChain = nullptr;
|
||
|
IDXGISwapChain* RawSwapChain = nullptr;
|
||
|
FfxInterface* Interface = nullptr;
|
||
|
bool bOverrideSwapChain = ((CVarFSR3OverrideSwapChainDX12.GetValueOnAnyThread() != 0) || FParse::Param(FCommandLine::Get(), TEXT("fsr3swapchain")));
|
||
|
|
||
|
// Don't override the swapchain in the Editor - it causes too many issues
|
||
|
#if WITH_EDITORONLY_DATA
|
||
|
bOverrideSwapChain &= !GIsEditor;
|
||
|
#endif
|
||
|
|
||
|
HWND ParentWindow = hWnd ? ::GetParent(hWnd) : nullptr;
|
||
|
if (bOverrideSwapChain && !ParentWindow)
|
||
|
{
|
||
|
ID3D12CommandQueue* CmdQueue = (ID3D12CommandQueue*)pDevice;
|
||
|
check(CmdQueue);
|
||
|
|
||
|
DXGI_SWAP_CHAIN_DESC1 DescCopy = *pDesc;
|
||
|
DXGI_SWAP_CHAIN_FULLSCREEN_DESC FullscreenDescCopy = *pFullscreenDesc;
|
||
|
|
||
|
ffxCreateContextDescFrameGenerationSwapChainForHwndDX12 SwapChainDesc = {};
|
||
|
SwapChainDesc.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_FRAMEGENERATIONSWAPCHAIN_FOR_HWND_DX12;
|
||
|
SwapChainDesc.header.pNext = nullptr;
|
||
|
SwapChainDesc.swapchain = (IDXGISwapChain4**)&RawSwapChain;
|
||
|
SwapChainDesc.hwnd = hWnd;
|
||
|
SwapChainDesc.desc = &DescCopy;
|
||
|
SwapChainDesc.fullscreenDesc = &FullscreenDescCopy;
|
||
|
SwapChainDesc.dxgiFactory = Inner;
|
||
|
SwapChainDesc.gameQueue = CmdQueue;
|
||
|
|
||
|
ffxContext SwapChain = nullptr;
|
||
|
auto ReturnCode = Backend.ffxCreateContext(&SwapChain, (ffxCreateContextDescHeader*)&SwapChainDesc);
|
||
|
if (ReturnCode == FFX_API_RETURN_OK)
|
||
|
{
|
||
|
D3D12SwapChain = new FFXD3D12SwapChain(&Backend, SwapChain, (IDXGISwapChain4*)RawSwapChain);
|
||
|
D3D12SwapChain->AddRef();
|
||
|
Result = S_OK;
|
||
|
|
||
|
if (FParse::Param(FCommandLine::Get(), TEXT("fsr3hudless")))
|
||
|
{
|
||
|
CVarFFXFIUIMode->Set(1);
|
||
|
}
|
||
|
if (FParse::Param(FCommandLine::Get(), TEXT("fsr3async")))
|
||
|
{
|
||
|
CVarFSR3AllowAsyncWorkloads->Set(1);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Result = (HRESULT)ReturnCode;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Result = Inner2->CreateSwapChainForHwnd(pDevice, hWnd, pDesc, pFullscreenDesc, pRestrictToOutput, (IDXGISwapChain1**)&RawSwapChain);
|
||
|
}
|
||
|
if (Result == S_OK)
|
||
|
{
|
||
|
FIntPoint SwapChainSize = FIntPoint(pDesc->Width, pDesc->Height);
|
||
|
uint32 Flags = 0;
|
||
|
Flags |= bool(ERHIZBuffer::IsInverted) ? FFX_FRAMEINTERPOLATION_ENABLE_DEPTH_INVERTED : 0;
|
||
|
Flags |= FFX_FRAMEINTERPOLATION_ENABLE_DEPTH_INFINITE;
|
||
|
FfxApiSurfaceFormat SurfaceFormat = (FfxApiSurfaceFormat)ffxApiGetSurfaceFormatDX12(pDesc->Format);
|
||
|
IFFXFrameInterpolationCustomPresent* CustomPresent = FFXFrameInterpolation->CreateCustomPresent(&Backend, Flags, SwapChainSize, SwapChainSize, (FfxSwapchain)(D3D12SwapChain ? D3D12SwapChain : RawSwapChain), (FfxCommandQueue)pDevice, SurfaceFormat, EFFXBackendAPI::D3D12);
|
||
|
if (CustomPresent)
|
||
|
{
|
||
|
*ppSwapChain = (IDXGISwapChain1*)(D3D12SwapChain ? D3D12SwapChain : RawSwapChain);
|
||
|
if (bOverrideSwapChain)
|
||
|
{
|
||
|
bool bAllowAsyncWorkloads = (CVarFSR3AllowAsyncWorkloads.GetValueOnAnyThread() != 0);
|
||
|
bool bUIMode = (CVarFFXFIUIMode.GetValueOnAnyThread() != 0);
|
||
|
if (bAllowAsyncWorkloads || bUIMode)
|
||
|
{
|
||
|
CustomPresent->SetMode(EFFXFrameInterpolationPresentModeNative);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CustomPresent->SetUseFFXSwapchain(true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Result = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE CreateSwapChainForCoreWindow(
|
||
|
/* [annotation][in] */
|
||
|
_In_ IUnknown* pDevice,
|
||
|
/* [annotation][in] */
|
||
|
_In_ IUnknown* pWindow,
|
||
|
/* [annotation][in] */
|
||
|
_In_ const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||
|
/* [annotation][in] */
|
||
|
_In_opt_ IDXGIOutput* pRestrictToOutput,
|
||
|
/* [annotation][out] */
|
||
|
_COM_Outptr_ IDXGISwapChain1** ppSwapChain) final
|
||
|
{
|
||
|
return Inner2->CreateSwapChainForCoreWindow(pDevice, pWindow, pDesc, pRestrictToOutput, ppSwapChain);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE GetSharedResourceAdapterLuid(
|
||
|
/* [annotation] */
|
||
|
_In_ HANDLE hResource,
|
||
|
/* [annotation] */
|
||
|
_Out_ LUID* pLuid) final
|
||
|
{
|
||
|
return Inner2->GetSharedResourceAdapterLuid(hResource, pLuid);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE RegisterStereoStatusWindow(
|
||
|
/* [annotation][in] */
|
||
|
_In_ HWND WindowHandle,
|
||
|
/* [annotation][in] */
|
||
|
_In_ UINT wMsg,
|
||
|
/* [annotation][out] */
|
||
|
_Out_ DWORD* pdwCookie) final
|
||
|
{
|
||
|
return Inner2->RegisterStereoStatusWindow(WindowHandle, wMsg, pdwCookie);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE RegisterStereoStatusEvent(
|
||
|
/* [annotation][in] */
|
||
|
_In_ HANDLE hEvent,
|
||
|
/* [annotation][out] */
|
||
|
_Out_ DWORD* pdwCookie) final
|
||
|
{
|
||
|
return Inner2->RegisterStereoStatusEvent(hEvent, pdwCookie);
|
||
|
}
|
||
|
|
||
|
void STDMETHODCALLTYPE UnregisterStereoStatus(
|
||
|
/* [annotation][in] */
|
||
|
_In_ DWORD dwCookie) final
|
||
|
{
|
||
|
Inner2->UnregisterStereoStatus(dwCookie);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE RegisterOcclusionStatusWindow(
|
||
|
/* [annotation][in] */
|
||
|
_In_ HWND WindowHandle,
|
||
|
/* [annotation][in] */
|
||
|
_In_ UINT wMsg,
|
||
|
/* [annotation][out] */
|
||
|
_Out_ DWORD* pdwCookie) final
|
||
|
{
|
||
|
return Inner2->RegisterOcclusionStatusWindow(WindowHandle, wMsg, pdwCookie);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE RegisterOcclusionStatusEvent(
|
||
|
/* [annotation][in] */
|
||
|
_In_ HANDLE hEvent,
|
||
|
/* [annotation][out] */
|
||
|
_Out_ DWORD* pdwCookie) final
|
||
|
{
|
||
|
return Inner2->RegisterOcclusionStatusEvent(hEvent, pdwCookie);
|
||
|
}
|
||
|
|
||
|
void STDMETHODCALLTYPE UnregisterOcclusionStatus(
|
||
|
/* [annotation][in] */
|
||
|
_In_ DWORD dwCookie) final
|
||
|
{
|
||
|
Inner2->UnregisterOcclusionStatus(dwCookie);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE CreateSwapChainForComposition(
|
||
|
/* [annotation][in] */
|
||
|
_In_ IUnknown* pDevice,
|
||
|
/* [annotation][in] */
|
||
|
_In_ const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||
|
/* [annotation][in] */
|
||
|
_In_opt_ IDXGIOutput* pRestrictToOutput,
|
||
|
/* [annotation][out] */
|
||
|
_COM_Outptr_ IDXGISwapChain1** ppSwapChain) final
|
||
|
{
|
||
|
return Inner2->CreateSwapChainForComposition(pDevice, pDesc, pRestrictToOutput, ppSwapChain);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE EnumAdapters1(
|
||
|
/* [in] */ UINT Adapter,
|
||
|
/* [annotation][out] */
|
||
|
_COM_Outptr_ IDXGIAdapter1** ppAdapter) final
|
||
|
{
|
||
|
return Inner2->EnumAdapters1(Adapter, ppAdapter);
|
||
|
}
|
||
|
|
||
|
BOOL STDMETHODCALLTYPE IsCurrent(void) final
|
||
|
{
|
||
|
return Inner2->IsCurrent();
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE EnumAdapters(
|
||
|
/* [in] */ UINT Adapter,
|
||
|
/* [annotation][out] */
|
||
|
_COM_Outptr_ IDXGIAdapter** ppAdapter) final
|
||
|
{
|
||
|
return Inner->EnumAdapters(Adapter, ppAdapter);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE MakeWindowAssociation(
|
||
|
HWND WindowHandle,
|
||
|
UINT Flags) final
|
||
|
{
|
||
|
return Inner->MakeWindowAssociation(WindowHandle, Flags);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE GetWindowAssociation(
|
||
|
/* [annotation][out] */
|
||
|
_Out_ HWND* pWindowHandle) final
|
||
|
{
|
||
|
return Inner->GetWindowAssociation(pWindowHandle);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE CreateSwapChain(
|
||
|
/* [annotation][in] */
|
||
|
_In_ IUnknown* pDevice,
|
||
|
/* [annotation][in] */
|
||
|
_In_ DXGI_SWAP_CHAIN_DESC* pDesc,
|
||
|
/* [annotation][out] */
|
||
|
_COM_Outptr_ IDXGISwapChain** ppSwapChain) final
|
||
|
{
|
||
|
HRESULT Result = E_INVALIDARG;
|
||
|
FFXD3D12SwapChain* D3D12SwapChain = nullptr;
|
||
|
IDXGISwapChain* RawSwapChain = nullptr;
|
||
|
FfxInterface* Interface = nullptr;
|
||
|
bool bOverrideSwapChain = ((CVarFSR3OverrideSwapChainDX12.GetValueOnAnyThread() != 0) || FParse::Param(FCommandLine::Get(), TEXT("fsr3swapchain")));
|
||
|
|
||
|
// Don't override the swapchain in the Editor - it causes too many issues
|
||
|
#if WITH_EDITORONLY_DATA
|
||
|
bOverrideSwapChain &= !GIsEditor;
|
||
|
#endif
|
||
|
|
||
|
HWND ParentWindow = pDesc->OutputWindow ? ::GetParent(pDesc->OutputWindow) : nullptr;
|
||
|
if (bOverrideSwapChain && !ParentWindow)
|
||
|
{
|
||
|
ID3D12CommandQueue* CmdQueue = (ID3D12CommandQueue*)pDevice;
|
||
|
check(CmdQueue);
|
||
|
|
||
|
ffxCreateContextDescFrameGenerationSwapChainNewDX12 SwapChainDesc = {};
|
||
|
SwapChainDesc.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_FRAMEGENERATIONSWAPCHAIN_NEW_DX12;
|
||
|
SwapChainDesc.header.pNext = nullptr;
|
||
|
SwapChainDesc.swapchain = (IDXGISwapChain4**)&RawSwapChain;
|
||
|
SwapChainDesc.desc = pDesc;
|
||
|
SwapChainDesc.dxgiFactory = Inner;
|
||
|
SwapChainDesc.gameQueue = CmdQueue;
|
||
|
|
||
|
ffxContext SwapChain = nullptr;
|
||
|
auto ReturnCode = Backend.ffxCreateContext(&SwapChain, (ffxCreateContextDescHeader*)&SwapChainDesc);
|
||
|
if (ReturnCode == FFX_API_RETURN_OK)
|
||
|
{
|
||
|
D3D12SwapChain = new FFXD3D12SwapChain(&Backend, SwapChain, (IDXGISwapChain4*)RawSwapChain);
|
||
|
D3D12SwapChain->AddRef();
|
||
|
Result = S_OK;
|
||
|
|
||
|
if (FParse::Param(FCommandLine::Get(), TEXT("fsr3hudless")))
|
||
|
{
|
||
|
CVarFFXFIUIMode->Set(1);
|
||
|
}
|
||
|
if (FParse::Param(FCommandLine::Get(), TEXT("fsr3async")))
|
||
|
{
|
||
|
CVarFSR3AllowAsyncWorkloads->Set(1);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Result = (HRESULT)ReturnCode;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Result = Inner->CreateSwapChain(pDevice, pDesc, &RawSwapChain);
|
||
|
}
|
||
|
if (Result == S_OK)
|
||
|
{
|
||
|
FIntPoint SwapChainSize = FIntPoint(pDesc->BufferDesc.Width, pDesc->BufferDesc.Height);
|
||
|
uint32 Flags = 0;
|
||
|
Flags |= bool(ERHIZBuffer::IsInverted) ? FFX_FRAMEINTERPOLATION_ENABLE_DEPTH_INVERTED : 0;
|
||
|
Flags |= FFX_FRAMEINTERPOLATION_ENABLE_DEPTH_INFINITE;
|
||
|
FfxApiSurfaceFormat SurfaceFormat = (FfxApiSurfaceFormat)ffxApiGetSurfaceFormatDX12(pDesc->BufferDesc.Format);
|
||
|
IFFXFrameInterpolationCustomPresent* CustomPresent = FFXFrameInterpolation->CreateCustomPresent(&Backend, Flags, SwapChainSize, SwapChainSize, (FfxSwapchain)(D3D12SwapChain ? D3D12SwapChain : RawSwapChain), (FfxCommandQueue)pDevice, SurfaceFormat, EFFXBackendAPI::D3D12);
|
||
|
if (CustomPresent)
|
||
|
{
|
||
|
*ppSwapChain = D3D12SwapChain ? D3D12SwapChain : RawSwapChain;
|
||
|
if (bOverrideSwapChain)
|
||
|
{
|
||
|
bool bAllowAsyncWorkloads = (CVarFSR3AllowAsyncWorkloads.GetValueOnAnyThread() != 0);
|
||
|
bool bUIMode = (CVarFFXFIUIMode.GetValueOnAnyThread() != 0);
|
||
|
if (bAllowAsyncWorkloads || bUIMode)
|
||
|
{
|
||
|
CustomPresent->SetMode(EFFXFrameInterpolationPresentModeNative);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CustomPresent->SetUseFFXSwapchain(true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Result = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter(
|
||
|
/* [in] */ HMODULE Module,
|
||
|
/* [annotation][out] */
|
||
|
_COM_Outptr_ IDXGIAdapter * *ppAdapter) final
|
||
|
{
|
||
|
return Inner->CreateSoftwareAdapter(Module, ppAdapter);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE SetPrivateData(
|
||
|
/* [annotation][in] */
|
||
|
_In_ REFGUID Name,
|
||
|
/* [in] */ UINT DataSize,
|
||
|
/* [annotation][in] */
|
||
|
_In_reads_bytes_(DataSize) const void* pData) final
|
||
|
{
|
||
|
return Inner->SetPrivateData(Name, DataSize, pData);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
|
||
|
/* [annotation][in] */
|
||
|
_In_ REFGUID Name,
|
||
|
/* [annotation][in] */
|
||
|
_In_opt_ const IUnknown* pUnknown) final
|
||
|
{
|
||
|
return Inner->SetPrivateDataInterface(Name, pUnknown);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE GetPrivateData(
|
||
|
/* [annotation][in] */
|
||
|
_In_ REFGUID Name,
|
||
|
/* [annotation][out][in] */
|
||
|
_Inout_ UINT* pDataSize,
|
||
|
/* [annotation][out] */
|
||
|
_Out_writes_bytes_(*pDataSize) void* pData) final
|
||
|
{
|
||
|
return Inner->GetPrivateData(Name, pDataSize, pData);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE GetParent(
|
||
|
/* [annotation][in] */
|
||
|
_In_ REFIID riid,
|
||
|
/* [annotation][retval][out] */
|
||
|
_COM_Outptr_ void** ppParent) final
|
||
|
{
|
||
|
return Inner->GetParent(riid, ppParent);
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||
|
/* [in] */ REFIID riid,
|
||
|
/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) final
|
||
|
{
|
||
|
return Inner->QueryInterface(riid, ppvObject);
|
||
|
}
|
||
|
|
||
|
ULONG STDMETHODCALLTYPE AddRef(void) final
|
||
|
{
|
||
|
return FThreadSafeRefCountedObject::AddRef();
|
||
|
}
|
||
|
|
||
|
ULONG STDMETHODCALLTYPE Release(void) final
|
||
|
{
|
||
|
return FThreadSafeRefCountedObject::Release();
|
||
|
}
|
||
|
};
|
||
|
static TRefCountPtr<FFXD3D12BackendDXGIFactory2Wrapper> GFFXFSR3DXGISwapChainFactory;
|
||
|
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
// Accessor for the FD3D12Adapter on <= 5.1 so we can replace the DXGI factory to insert the proxy swapchain.
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
#if !FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V1
|
||
|
class FFXD3D12BackendAdapter : public FD3D12Adapter
|
||
|
{
|
||
|
public:
|
||
|
inline void WrapDXGIFactory()
|
||
|
{
|
||
|
GFFXFSR3DXGISwapChainFactory->Init(DxgiFactory2.GetReference());
|
||
|
DxgiFactory2.SafeRelease();
|
||
|
DxgiFactory2 = GFFXFSR3DXGISwapChainFactory;
|
||
|
}
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
// Implementation for FFXD3D12BackendModule.
|
||
|
//-------------------------------------------------------------------------------------
|
||
|
void FFXD3D12BackendModule::StartupModule()
|
||
|
{
|
||
|
if (!FParse::Param(FCommandLine::Get(), TEXT("fsr3rhi")) && (CVarFSR3UseNativeDX12.GetValueOnAnyThread() != 0 || FParse::Param(FCommandLine::Get(), TEXT("fsr3native"))))
|
||
|
{
|
||
|
if (FFXD3D12Backend::sFFXD3D12Backend.LoadDLL())
|
||
|
{
|
||
|
|
||
|
IFFXFrameInterpolationModule* FFXFrameInterpolationModule = FModuleManager::GetModulePtr<IFFXFrameInterpolationModule>(TEXT("FFXFrameInterpolation"));
|
||
|
bool bOverrideSwapChain = FFXFrameInterpolationModule != nullptr;
|
||
|
|
||
|
// Don't override the swapchain in the Editor - it causes too many issues
|
||
|
#if WITH_EDITORONLY_DATA
|
||
|
bOverrideSwapChain &= !GIsEditor;
|
||
|
#endif
|
||
|
if (bOverrideSwapChain)
|
||
|
{
|
||
|
IFFXFrameInterpolation* FFXFrameInterpolation = FFXFrameInterpolationModule->GetImpl();
|
||
|
check(FFXFrameInterpolation);
|
||
|
|
||
|
GFFXFSR3DXGISwapChainFactory = new FFXD3D12BackendDXGIFactory2Wrapper(FFXFrameInterpolation);
|
||
|
|
||
|
#if !FFX_UE_SUPPORTS_SWAPCHAIN_PROVIDER_V1
|
||
|
#if UE_VERSION_AT_LEAST(5, 1, 0)
|
||
|
auto& Adapter = ((FD3D12DynamicRHI*)GetID3D12DynamicRHI())->GetAdapter();
|
||
|
#else
|
||
|
static const auto CVarHDRMinLuminanceLog10 = IConsoleManager::Get().FindConsoleVariable(TEXT("r.HDR.Display.MinLuminanceLog10"));
|
||
|
static const auto CVarHDRMaxLuminance = IConsoleManager::Get().FindConsoleVariable(TEXT("r.HDR.Display.MaxLuminance"));
|
||
|
|
||
|
auto& Adapter = ((FD3D12DynamicRHI*)GDynamicRHI)->GetAdapter();
|
||
|
|
||
|
IDXGIAdapter* DXGIAdapter = Adapter.GetAdapter();
|
||
|
|
||
|
for (uint32 DisplayIndex = 0; true; ++DisplayIndex)
|
||
|
{
|
||
|
TRefCountPtr<IDXGIOutput> DXGIOutput;
|
||
|
if (S_OK != DXGIAdapter->EnumOutputs(DisplayIndex, DXGIOutput.GetInitReference()))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
TRefCountPtr<IDXGIOutput6> Output6;
|
||
|
if (SUCCEEDED(DXGIOutput->QueryInterface(IID_PPV_ARGS(Output6.GetInitReference()))))
|
||
|
{
|
||
|
DXGI_OUTPUT_DESC1 OutputDesc;
|
||
|
if (Output6->GetDesc1(&OutputDesc) == S_OK)
|
||
|
{
|
||
|
// Check for HDR support on the display.
|
||
|
const bool bDisplaySupportsHDROutput = (OutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
|
||
|
if (bDisplaySupportsHDROutput)
|
||
|
{
|
||
|
CVarHDRMaxLuminance->Set(OutputDesc.MaxLuminance, ECVF_SetByConstructor);
|
||
|
CVarHDRMinLuminanceLog10->Set(static_cast<float>(log10(OutputDesc.MinLuminance)), ECVF_SetByConstructor);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
FFXD3D12BackendAdapter* Wrapper = (FFXD3D12BackendAdapter*)&Adapter;
|
||
|
Wrapper->WrapDXGIFactory();
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
else if (FParse::Param(FCommandLine::Get(), TEXT("fsr3native")))
|
||
|
{
|
||
|
UE_LOG(LogFFXD3D12, Fatal, TEXT("FSR3 D3D12 Module Could Not Load amd_fidelityfx_dx12.dll when forcing its use with -fsr3native"));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CVarFSR3UseNativeDX12->Set(0, ECVF_SetByCode);
|
||
|
CVarFSR3UseRHI->Set(1, ECVF_SetByCode);
|
||
|
UE_LOG(LogFFXD3D12, Error, TEXT("FSR3 D3D12 Module Could Not Load amd_fidelityfx_dx12.dll - falling back to RHI implementation"));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FFXD3D12BackendModule::ShutdownModule()
|
||
|
{
|
||
|
GFFXFSR3DXGISwapChainFactory.SafeRelease();
|
||
|
}
|
||
|
|
||
|
IFFXSharedBackend* FFXD3D12BackendModule::GetBackend()
|
||
|
{
|
||
|
return &FFXD3D12Backend::sFFXD3D12Backend;
|
||
|
}
|
||
|
|
||
|
#undef LOCTEXT_NAMESPACE
|