1754 lines
69 KiB
C++
1754 lines
69 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 "FFXFrameInterpolation.h"
|
||
|
#include "FFXFrameInterpolationViewExtension.h"
|
||
|
#include "FFXSharedBackend.h"
|
||
|
#include "FFXFrameInterpolationSlate.h"
|
||
|
#include "FFXFrameInterpolationCustomPresent.h"
|
||
|
#include "FFXFSR3Settings.h"
|
||
|
|
||
|
#include "PostProcess/PostProcessing.h"
|
||
|
#include "PostProcess/TemporalAA.h"
|
||
|
|
||
|
#include "FFXFrameInterpolationApi.h"
|
||
|
#include "FFXOpticalFlowApi.h"
|
||
|
|
||
|
#include "ScenePrivate.h"
|
||
|
#include "RenderTargetPool.h"
|
||
|
#if UE_VERSION_AT_LEAST(5, 2, 0)
|
||
|
#include "DataDrivenShaderPlatformInfo.h"
|
||
|
#endif
|
||
|
#include "Engine/GameViewportClient.h"
|
||
|
#include "UnrealClient.h"
|
||
|
|
||
|
#include "IAntiLag2.h"
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
#define GET_RHI_TARGET_ARG
|
||
|
#define GET_RHI_VIEW_ARG
|
||
|
#define GET_TEXTURE
|
||
|
#else
|
||
|
#define GET_RHI_TARGET_ARG ERenderTargetTexture::Targetable
|
||
|
#define GET_RHI_VIEW_ARG ERenderTargetTexture::ShaderResource
|
||
|
#define GET_TEXTURE .GetTexture()
|
||
|
#endif
|
||
|
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
// In order to access the distortion data prior to our code executing it is necessary to gain access to FFXFIRDGBuilder internals.
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
#if UE_VERSION_AT_LEAST(5, 3, 0)
|
||
|
struct FFXFIParallelPassSet : public FRHICommandListImmediate::FQueuedCommandList
|
||
|
{
|
||
|
FFXFIParallelPassSet() = default;
|
||
|
|
||
|
TArray<FRDGPass*, FRDGArrayAllocator> Passes;
|
||
|
#if UE_VERSION_OLDER_THAN(5, 5, 0)
|
||
|
IF_RHI_WANT_BREADCRUMB_EVENTS(FRDGBreadcrumbState* BreadcrumbStateBegin{});
|
||
|
IF_RHI_WANT_BREADCRUMB_EVENTS(FRDGBreadcrumbState* BreadcrumbStateEnd{});
|
||
|
int8 bInitialized = 0;
|
||
|
#endif
|
||
|
bool bDispatchAfterExecute = false;
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
bool bTaskModeAsync = false;
|
||
|
#else
|
||
|
bool bParallelTranslate = false;
|
||
|
#endif
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 1, 0)
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
class FFXFIRDGBuilder : FRDGScopeState
|
||
|
#elif UE_VERSION_AT_LEAST(5, 4, 0)
|
||
|
class FFXFIRDGBuilder
|
||
|
#else
|
||
|
class FFXFIRDGBuilder : FRDGAllocatorScope
|
||
|
#endif
|
||
|
{
|
||
|
#if UE_VERSION_AT_LEAST(5, 4, 0)
|
||
|
struct FAsyncDeleter
|
||
|
{
|
||
|
TUniqueFunction<void()> Function;
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
UE::Tasks::FTask Prerequisites;
|
||
|
#endif
|
||
|
//static UE::Tasks::FTask LastTask;
|
||
|
|
||
|
RENDERCORE_API ~FAsyncDeleter()
|
||
|
{
|
||
|
}
|
||
|
} AsyncDeleter;
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
FRDGAllocator Root;
|
||
|
FRDGAllocator Task;
|
||
|
FRDGAllocator Transition;
|
||
|
|
||
|
int32 GetByteCount() const
|
||
|
{
|
||
|
return Root.GetByteCount() + Task.GetByteCount() + Transition.GetByteCount();
|
||
|
}
|
||
|
|
||
|
} Allocators;
|
||
|
|
||
|
FRDGAllocatorScope RootAllocatorScope;
|
||
|
#endif
|
||
|
|
||
|
public:
|
||
|
FFXFIRDGBuilder(FRHICommandListImmediate& InRHICmdList, FRDGEventName InName = {}, ERDGBuilderFlags InFlags = ERDGBuilderFlags::None)
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
: FRDGScopeState(InRHICmdList, true, true)
|
||
|
, RootAllocatorScope(Allocators.Transition)
|
||
|
#elif UE_VERSION_AT_LEAST(5, 4, 0)
|
||
|
: RootAllocatorScope(Allocators.Transition)
|
||
|
, RHICmdList(InRHICmdList)
|
||
|
#else
|
||
|
: RHICmdList(InRHICmdList)
|
||
|
#endif
|
||
|
, BuilderName(InName)
|
||
|
#if UE_VERSION_OLDER_THAN(5, 4, 0)
|
||
|
, CompilePipe(TEXT("FFXFIRDGCompilePipe"))
|
||
|
#if RDG_CPU_SCOPES
|
||
|
|
||
|
, CPUScopeStacks(Allocator)
|
||
|
#endif
|
||
|
, GPUScopeStacks(Allocator)
|
||
|
#endif
|
||
|
#if UE_VERSION_AT_LEAST(5, 4, 0)
|
||
|
, ExtendResourceLifetimeScope(InRHICmdList)
|
||
|
#endif
|
||
|
#if RDG_ENABLE_DEBUG
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
, UserValidation(Allocators.Transition)
|
||
|
#elif UE_VERSION_AT_LEAST(5, 4, 0)
|
||
|
, UserValidation(Allocators.Transition, false)
|
||
|
#else
|
||
|
, UserValidation(Allocator, bParallelExecuteEnabled)
|
||
|
#endif
|
||
|
, BarrierValidation(&Passes, BuilderName)
|
||
|
#endif
|
||
|
#if UE_VERSION_AT_LEAST(5, 2, 0) && UE_VERSION_OLDER_THAN(5, 4, 0)
|
||
|
, ExtendResourceLifetimeScope(InRHICmdList)
|
||
|
#endif
|
||
|
{
|
||
|
}
|
||
|
|
||
|
FFXFIRDGBuilder(const FFXFIRDGBuilder&) = delete;
|
||
|
~FFXFIRDGBuilder()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
#if UE_VERSION_OLDER_THAN(5, 5, 0)
|
||
|
FRHICommandListImmediate& RHICmdList;
|
||
|
#endif
|
||
|
struct FFXFSR3BlackBoard
|
||
|
{
|
||
|
FRDGAllocator* Allocator;
|
||
|
TArray<struct FStruct*, FRDGArrayAllocator> Blackboard;
|
||
|
};
|
||
|
FFXFSR3BlackBoard Blackboard;
|
||
|
|
||
|
FRDGTextureRef FindTexture(TCHAR const* Name)
|
||
|
{
|
||
|
for (FRDGTextureHandle It = Textures.Begin(); It != Textures.End(); ++It)
|
||
|
{
|
||
|
FRDGTextureRef Texture = Textures.Get(It);
|
||
|
if (FCString::Strcmp(Texture->Name, Name) == 0)
|
||
|
{
|
||
|
return Texture;
|
||
|
}
|
||
|
}
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const FRDGEventName BuilderName;
|
||
|
#if UE_VERSION_AT_LEAST(5, 4, 0)
|
||
|
FRDGPass* ProloguePass = nullptr;
|
||
|
FRDGPass* EpiloguePass = nullptr;
|
||
|
uint32 AsyncComputePassCount = 0;
|
||
|
uint32 RasterPassCount = 0;
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
TArray<FRDGDispatchPass*, FRDGArrayAllocator> DispatchPasses;
|
||
|
#else
|
||
|
EAsyncComputeBudget AsyncComputeBudgetScope = EAsyncComputeBudget::EAll_4;
|
||
|
EAsyncComputeBudget AsyncComputeBudgetState = EAsyncComputeBudget(~0u);
|
||
|
IF_RDG_CMDLIST_STATS(TStatId CommandListStatScope);
|
||
|
IF_RDG_CMDLIST_STATS(TStatId CommandListStatState);
|
||
|
IF_RDG_CPU_SCOPES(FRDGCPUScopeStacks CPUScopeStacks);
|
||
|
FRDGGPUScopeStacksByPipeline GPUScopeStacks;
|
||
|
IF_RHI_WANT_BREADCRUMB_EVENTS(FRDGBreadcrumbState* BreadcrumbState{});
|
||
|
#endif
|
||
|
FRDGPassRegistry Passes;
|
||
|
FRDGTextureRegistry Textures;
|
||
|
FRDGBufferRegistry Buffers;
|
||
|
FRDGViewRegistry Views;
|
||
|
FRDGUniformBufferRegistry UniformBuffers;
|
||
|
|
||
|
struct FExtractedTexture
|
||
|
{
|
||
|
FRDGTexture* Texture{};
|
||
|
TRefCountPtr<IPooledRenderTarget>* PooledTexture{};
|
||
|
};
|
||
|
|
||
|
TArray<FExtractedTexture, FRDGArrayAllocator> ExtractedTextures;
|
||
|
|
||
|
struct FExtractedBuffer
|
||
|
{
|
||
|
FRDGBuffer* Buffer{};
|
||
|
TRefCountPtr<FRDGPooledBuffer>* PooledBuffer{};
|
||
|
};
|
||
|
|
||
|
TArray<FExtractedBuffer, FRDGArrayAllocator> ExtractedBuffers;
|
||
|
|
||
|
Experimental::TRobinHoodHashMap<FRHITexture*, FRDGTexture*, DefaultKeyFuncs<FRHITexture*>, FRDGArrayAllocator> ExternalTextures;
|
||
|
Experimental::TRobinHoodHashMap<FRHIBuffer*, FRDGBuffer*, DefaultKeyFuncs<FRHIBuffer*>, FRDGArrayAllocator> ExternalBuffers;
|
||
|
|
||
|
TArray<FRDGBuffer*, FRDGArrayAllocator> NumElementsCallbackBuffers;
|
||
|
|
||
|
IRHITransientResourceAllocator* TransientResourceAllocator = nullptr;
|
||
|
bool bSupportsTransientTextures = false;
|
||
|
bool bSupportsTransientBuffers = false;
|
||
|
|
||
|
Experimental::TRobinHoodHashMap<FRDGPooledTexture*, FRDGTexture*, DefaultKeyFuncs<FRDGPooledTexture*>, FConcurrentLinearArrayAllocator> PooledTextureOwnershipMap;
|
||
|
Experimental::TRobinHoodHashMap<FRDGPooledBuffer*, FRDGBuffer*, DefaultKeyFuncs<FRDGPooledBuffer*>, FConcurrentLinearArrayAllocator> PooledBufferOwnershipMap;
|
||
|
|
||
|
TMap<FRDGBarrierBatchBeginId, FRDGBarrierBatchBegin*, FRDGSetAllocator> BarrierBatchMap;
|
||
|
TArray<FRHITrackedAccessInfo, FRDGArrayAllocator> EpilogueResourceAccesses;
|
||
|
TArray<TRefCountPtr<IPooledRenderTarget>, FRDGArrayAllocator> ActivePooledTextures;
|
||
|
TArray<TRefCountPtr<FRDGPooledBuffer>, FRDGArrayAllocator> ActivePooledBuffers;
|
||
|
FRDGTransitionCreateQueue TransitionCreateQueue;
|
||
|
FRDGTextureSubresourceState ScratchTextureState;
|
||
|
FRDGSubresourceState PrologueSubresourceState;
|
||
|
|
||
|
struct FAsyncSetupOp
|
||
|
{
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
enum class EType : uint8
|
||
|
{
|
||
|
SetupPassResources,
|
||
|
CullRootBuffer,
|
||
|
CullRootTexture,
|
||
|
ReservedBufferCommit
|
||
|
};
|
||
|
#else
|
||
|
enum class EType
|
||
|
{
|
||
|
SetupPassResources,
|
||
|
CullRootBuffer,
|
||
|
CullRootTexture
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
uint64 Type : 8;
|
||
|
uint64 Payload : 48;
|
||
|
#else
|
||
|
EType Type;
|
||
|
#endif
|
||
|
|
||
|
union
|
||
|
{
|
||
|
FRDGPass* Pass;
|
||
|
FRDGBuffer* Buffer;
|
||
|
FRDGTexture* Texture;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
struct FAsyncSetupQueue
|
||
|
{
|
||
|
UE::FMutex Mutex;
|
||
|
TArray<FAsyncSetupOp, FRDGArrayAllocator> Ops;
|
||
|
#if UE_VERSION_OLDER_THAN(5, 5, 0)
|
||
|
UE::Tasks::FTask LastTask;
|
||
|
#endif
|
||
|
UE::Tasks::FPipe Pipe{ TEXT("FRDGBuilder::AsyncSetupQueue") };
|
||
|
|
||
|
} AsyncSetupQueue;
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
TArray<uint64, FRDGArrayAllocator> ReservedBufferCommitSizes;
|
||
|
#endif
|
||
|
|
||
|
TArray<FRDGPass*, FRDGArrayAllocator> CullPassStack;
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
TStaticArray<TArray<UE::Tasks::FTask, FRDGArrayAllocator>, (int32)ERDGSetupTaskWaitPoint::MAX> Tasks;
|
||
|
#else
|
||
|
TArray<UE::Tasks::FTask, FRDGArrayAllocator> Tasks;
|
||
|
TArray<FRHICommandListImmediate::FQueuedCommandList, FConcurrentLinearArrayAllocator> CommandLists;
|
||
|
#endif
|
||
|
bool bEnabled = false;
|
||
|
|
||
|
} ParallelSetup;
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
bool bParallelCompileEnabled = false;
|
||
|
#endif
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
TArray<FFXFIParallelPassSet, FRDGArrayAllocator> ParallelPassSets;
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
TOptional<UE::Tasks::FTaskEvent> TasksAwait;
|
||
|
TOptional<UE::Tasks::FTaskEvent> TasksAsync;
|
||
|
TOptional<UE::Tasks::FTaskEvent> DispatchTaskEventAwait;
|
||
|
TOptional<UE::Tasks::FTaskEvent> DispatchTaskEventAsync;
|
||
|
ERDGPassTaskMode TaskMode = ERDGPassTaskMode::Inline;
|
||
|
#else
|
||
|
TArray<UE::Tasks::FTask, FRDGArrayAllocator> Tasks;
|
||
|
TOptional<UE::Tasks::FTaskEvent> DispatchTaskEvent;
|
||
|
bool bEnabled = false;
|
||
|
#endif
|
||
|
} ParallelExecute;
|
||
|
|
||
|
struct FUploadedBuffer
|
||
|
{
|
||
|
bool bUseDataCallbacks;
|
||
|
bool bUseFreeCallbacks;
|
||
|
FRDGBuffer* Buffer{};
|
||
|
const void* Data{};
|
||
|
uint64 DataSize{};
|
||
|
FRDGBufferInitialDataCallback DataCallback;
|
||
|
FRDGBufferInitialDataSizeCallback DataSizeCallback;
|
||
|
FRDGBufferInitialDataFreeCallback DataFreeCallback;
|
||
|
FRDGBufferInitialDataFillCallback DataFillCallback;
|
||
|
};
|
||
|
|
||
|
TArray<FUploadedBuffer, FRDGArrayAllocator> UploadedBuffers;
|
||
|
|
||
|
TArray<FRDGViewableResource*, FRDGArrayAllocator> AccessModeQueue;
|
||
|
TSet<FRDGViewableResource*, DefaultKeyFuncs<FRDGViewableResource*>, FRDGSetAllocator> ExternalAccessResources;
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
TArray<TUniqueFunction<void()>, FRDGArrayAllocator> PostExecuteCallbacks;
|
||
|
|
||
|
FGraphEventArray WaitOutstandingTasks;
|
||
|
#endif
|
||
|
bool bFlushResourcesRHI = false;
|
||
|
FRHICommandListScopedExtendResourceLifetime ExtendResourceLifetimeScope;
|
||
|
|
||
|
struct FAuxiliaryPass
|
||
|
{
|
||
|
uint8 Clobber = 0;
|
||
|
uint8 Visualize = 0;
|
||
|
uint8 Dump = 0;
|
||
|
uint8 FlushAccessModeQueue = 0;
|
||
|
} AuxiliaryPasses;
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0) && WITH_MGPU
|
||
|
bool bForceCopyCrossGPU = false;
|
||
|
#endif
|
||
|
|
||
|
IF_RDG_ENABLE_TRACE(FRDGTrace Trace);
|
||
|
|
||
|
#if RDG_ENABLE_DEBUG
|
||
|
FRDGUserValidation UserValidation;
|
||
|
FRDGBarrierValidation BarrierValidation;
|
||
|
#endif
|
||
|
|
||
|
#else // 5.0.3 or older
|
||
|
FRDGPassRegistry Passes;
|
||
|
FRDGTextureRegistry Textures;
|
||
|
FRDGBufferRegistry Buffers;
|
||
|
FRDGViewRegistry Views;
|
||
|
FRDGUniformBufferRegistry UniformBuffers;
|
||
|
TArray<FRDGUniformBufferHandle, FRDGArrayAllocator> UniformBuffersToCreate;
|
||
|
TSortedMap<FRHITexture*, FRDGTexture*, FRDGArrayAllocator> ExternalTextures;
|
||
|
TSortedMap<FRHIBuffer*, FRDGBuffer*, FRDGArrayAllocator> ExternalBuffers;
|
||
|
TMap<FRDGPooledTexture*, FRDGTexture*, FRDGSetAllocator> PooledTextureOwnershipMap;
|
||
|
TMap<FRDGPooledBuffer*, FRDGBuffer*, FRDGSetAllocator> PooledBufferOwnershipMap;
|
||
|
TArray<TRefCountPtr<IPooledRenderTarget>, FRDGArrayAllocator> ActivePooledTextures;
|
||
|
TArray<TRefCountPtr<FRDGPooledBuffer>, FRDGArrayAllocator> ActivePooledBuffers;
|
||
|
TMap<FRDGBarrierBatchBeginId, FRDGBarrierBatchBegin*, FRDGSetAllocator> BarrierBatchMap;
|
||
|
FRDGTransitionCreateQueue TransitionCreateQueue;
|
||
|
|
||
|
template <typename LambdaType>
|
||
|
UE::Tasks::FTask LaunchCompileTask(const TCHAR* Name, bool bCondition, LambdaType&& Lambda);
|
||
|
|
||
|
UE::Tasks::FPipe CompilePipe;
|
||
|
|
||
|
class FPassQueue
|
||
|
{
|
||
|
TLockFreePointerListFIFO<FRDGPass, PLATFORM_CACHE_LINE_SIZE> Queue;
|
||
|
UE::Tasks::FTask LastTask;
|
||
|
};
|
||
|
|
||
|
FPassQueue SetupPassQueue;
|
||
|
|
||
|
TArray<FRDGPassHandle, FRDGArrayAllocator> CullPassStack;
|
||
|
|
||
|
FRDGPass* ProloguePass;
|
||
|
FRDGPass* EpiloguePass;
|
||
|
|
||
|
struct FExtractedTexture
|
||
|
{
|
||
|
FRDGTexture* Texture{};
|
||
|
TRefCountPtr<IPooledRenderTarget>* PooledTexture{};
|
||
|
};
|
||
|
|
||
|
TArray<FExtractedTexture, FRDGArrayAllocator> ExtractedTextures;
|
||
|
|
||
|
struct FExtractedBuffer
|
||
|
{
|
||
|
FRDGBuffer* Buffer{};
|
||
|
TRefCountPtr<FRDGPooledBuffer>* PooledBuffer{};
|
||
|
};
|
||
|
|
||
|
TArray<FExtractedBuffer, FRDGArrayAllocator> ExtractedBuffers;
|
||
|
|
||
|
struct FUploadedBuffer
|
||
|
{
|
||
|
bool bUseDataCallbacks;
|
||
|
bool bUseFreeCallbacks;
|
||
|
FRDGBuffer* Buffer{};
|
||
|
const void* Data{};
|
||
|
uint64 DataSize{};
|
||
|
FRDGBufferInitialDataCallback DataCallback;
|
||
|
FRDGBufferInitialDataSizeCallback DataSizeCallback;
|
||
|
FRDGBufferInitialDataFreeCallback DataFreeCallback;
|
||
|
};
|
||
|
|
||
|
TArray<FUploadedBuffer, FRDGArrayAllocator> UploadedBuffers;
|
||
|
|
||
|
#if UE_VERSION_OLDER_THAN(5, 3, 0)
|
||
|
struct FParallelPassSet : public FRHICommandListImmediate::FQueuedCommandList
|
||
|
{
|
||
|
TArray<FRDGPass*, FRDGArrayAllocator> Passes;
|
||
|
IF_RHI_WANT_BREADCRUMB_EVENTS(FRDGBreadcrumbState* BreadcrumbStateBegin{});
|
||
|
IF_RHI_WANT_BREADCRUMB_EVENTS(FRDGBreadcrumbState* BreadcrumbStateEnd{});
|
||
|
int8 bInitialized;
|
||
|
bool bDispatchAfterExecute;
|
||
|
#if UE_VERSION_AT_LEAST(5, 2, 0)
|
||
|
bool bParallelTranslate;
|
||
|
#endif
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 3, 0)
|
||
|
TArray<FFXFIParallelPassSet, FRDGArrayAllocator> ParallelPassSets;
|
||
|
#else
|
||
|
TArray<FParallelPassSet, FRDGArrayAllocator> ParallelPassSets;
|
||
|
#endif
|
||
|
|
||
|
TArray<UE::Tasks::FTask, FRDGArrayAllocator> ParallelExecuteEvents;
|
||
|
|
||
|
TArray<UE::Tasks::FTask, FRDGArrayAllocator> ParallelSetupEvents;
|
||
|
|
||
|
TArray<FRHITrackedAccessInfo, FRDGArrayAllocator> EpilogueResourceAccesses;
|
||
|
|
||
|
TArray<FRDGViewableResource*, FRDGArrayAllocator> AccessModeQueue;
|
||
|
TSet<FRDGViewableResource*, DefaultKeyFuncs<FRDGViewableResource*>, FRDGSetAllocator> ExternalAccessResources;
|
||
|
|
||
|
FRDGTextureSubresourceStateIndirect ScratchTextureState;
|
||
|
|
||
|
EAsyncComputeBudget AsyncComputeBudgetScope;
|
||
|
EAsyncComputeBudget AsyncComputeBudgetState;
|
||
|
|
||
|
FRHICommandList* RHICmdListBufferUploads;
|
||
|
|
||
|
IF_RDG_CPU_SCOPES(FRDGCPUScopeStacks CPUScopeStacks);
|
||
|
FRDGGPUScopeStacksByPipeline GPUScopeStacks;
|
||
|
IF_RHI_WANT_BREADCRUMB_EVENTS(FRDGBreadcrumbState* BreadcrumbState{});
|
||
|
|
||
|
IF_RDG_ENABLE_TRACE(FRDGTrace Trace);
|
||
|
|
||
|
bool bFlushResourcesRHI;
|
||
|
bool bParallelExecuteEnabled;
|
||
|
bool bParallelSetupEnabled;
|
||
|
#if UE_VERSION_AT_LEAST(5, 2, 0)
|
||
|
bool bFinalEventScopeActive;
|
||
|
#endif
|
||
|
|
||
|
#if RDG_ENABLE_DEBUG
|
||
|
FRDGUserValidation UserValidation;
|
||
|
FRDGBarrierValidation BarrierValidation;
|
||
|
#endif
|
||
|
|
||
|
struct FAuxiliaryPass
|
||
|
{
|
||
|
uint8 Clobber;
|
||
|
uint8 Visualize;
|
||
|
uint8 Dump;
|
||
|
uint8 FlushAccessModeQueue;
|
||
|
} AuxiliaryPasses;
|
||
|
|
||
|
#if WITH_MGPU
|
||
|
#if UE_VERSION_OLDER_THAN(5, 2, 0)
|
||
|
FName NameForTemporalEffect;
|
||
|
bool bWaitedForTemporalEffect;
|
||
|
#endif
|
||
|
bool bForceCopyCrossGPU;
|
||
|
#endif // WITH_MGPU
|
||
|
|
||
|
uint32 AsyncComputePassCount;
|
||
|
uint32 RasterPassCount;
|
||
|
|
||
|
IF_RDG_CMDLIST_STATS(TStatId CommandListStatScope);
|
||
|
IF_RDG_CMDLIST_STATS(TStatId CommandListStatState);
|
||
|
|
||
|
IRHITransientResourceAllocator* TransientResourceAllocator;
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 2, 0)
|
||
|
FRHICommandListScopedExtendResourceLifetime ExtendResourceLifetimeScope;
|
||
|
#endif
|
||
|
|
||
|
#endif
|
||
|
};
|
||
|
static_assert(sizeof(FRDGBuilder) == sizeof(FFXFIRDGBuilder), "FFXFIRDGBuilder must match the layout of FRDGBuilder so we can access the Lumen reflection texture!");
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 6, 0)
|
||
|
#error "Unsupported Unreal Engine 5 version - update the definition for FFXFIRDGBuilder."
|
||
|
#endif
|
||
|
|
||
|
#endif
|
||
|
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
// Helper variable declarations.
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
static uint32_t s_opticalFlowBlockSize = 8;
|
||
|
static uint32_t s_opticalFlowSearchRadius = 8;
|
||
|
|
||
|
extern ENGINE_API float GAverageFPS;
|
||
|
extern ENGINE_API float GAverageMS;
|
||
|
|
||
|
#if UE_VERSION_OLDER_THAN(4, 27, 0)
|
||
|
#define GFrameCounterRenderThread GFrameNumberRenderThread
|
||
|
#endif
|
||
|
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
// Input declaration for the frame interpolation pass.
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
struct FFXFrameInterpolationPass
|
||
|
{
|
||
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
||
|
RDG_TEXTURE_ACCESS(ColorTexture, ERHIAccess::SRVCompute)
|
||
|
RDG_TEXTURE_ACCESS(BackBufferTexture, ERHIAccess::SRVCompute)
|
||
|
RDG_TEXTURE_ACCESS(SceneDepth, ERHIAccess::SRVCompute)
|
||
|
RDG_TEXTURE_ACCESS(MotionVectors, ERHIAccess::SRVCompute)
|
||
|
RDG_TEXTURE_ACCESS(DistortionTexture, ERHIAccess::SRVCompute)
|
||
|
RDG_TEXTURE_ACCESS(HudTexture, ERHIAccess::CopyDest)
|
||
|
RDG_TEXTURE_ACCESS(InterpolatedRT, ERHIAccess::CopyDest)
|
||
|
RDG_TEXTURE_ACCESS(Interpolated, ERHIAccess::CopyDest)
|
||
|
END_SHADER_PARAMETER_STRUCT()
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
// Unreal shader to convert from the Velocity texture format to the Motion Vectors used by FFX.
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
class FFXFIConvertVelocityCS : public FGlobalShader
|
||
|
{
|
||
|
public:
|
||
|
static const int ThreadgroupSizeX = 8;
|
||
|
static const int ThreadgroupSizeY = 8;
|
||
|
static const int ThreadgroupSizeZ = 1;
|
||
|
|
||
|
DECLARE_GLOBAL_SHADER(FFXFIConvertVelocityCS);
|
||
|
SHADER_USE_PARAMETER_STRUCT(FFXFIConvertVelocityCS, FGlobalShader);
|
||
|
|
||
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
||
|
RDG_TEXTURE_ACCESS(DepthTexture, ERHIAccess::SRVCompute)
|
||
|
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputDepth)
|
||
|
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputVelocity)
|
||
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
||
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutputTexture)
|
||
|
END_SHADER_PARAMETER_STRUCT()
|
||
|
|
||
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
||
|
{
|
||
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
||
|
}
|
||
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
||
|
{
|
||
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), ThreadgroupSizeX);
|
||
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), ThreadgroupSizeY);
|
||
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEZ"), ThreadgroupSizeZ);
|
||
|
OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1);
|
||
|
OutEnvironment.SetDefine(TEXT("UNREAL_ENGINE_MAJOR_VERSION"), ENGINE_MAJOR_VERSION);
|
||
|
OutEnvironment.SetDefine(TEXT("UNREAL_ENGINE_MINOR_VERSION"), ENGINE_MINOR_VERSION);
|
||
|
}
|
||
|
};
|
||
|
IMPLEMENT_GLOBAL_SHADER(FFXFIConvertVelocityCS, "/Plugin/FSR3/Private/PostProcessFFX_FSR3ConvertVelocity.usf", "MainCS", SF_Compute);
|
||
|
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
// Unreal shader to convert from the Distortion texture format for use by FFX.
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
class FFXFIConvertDistortionCS : public FGlobalShader
|
||
|
{
|
||
|
public:
|
||
|
static const int ThreadgroupSizeX = 8;
|
||
|
static const int ThreadgroupSizeY = 8;
|
||
|
static const int ThreadgroupSizeZ = 1;
|
||
|
|
||
|
DECLARE_GLOBAL_SHADER(FFXFIConvertDistortionCS);
|
||
|
SHADER_USE_PARAMETER_STRUCT(FFXFIConvertDistortionCS, FGlobalShader);
|
||
|
|
||
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
||
|
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, UEDistortion)
|
||
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
||
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutputTexture)
|
||
|
END_SHADER_PARAMETER_STRUCT()
|
||
|
|
||
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
||
|
{
|
||
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
||
|
}
|
||
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
||
|
{
|
||
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), ThreadgroupSizeX);
|
||
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), ThreadgroupSizeY);
|
||
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEZ"), ThreadgroupSizeZ);
|
||
|
OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1);
|
||
|
}
|
||
|
};
|
||
|
IMPLEMENT_GLOBAL_SHADER(FFXFIConvertDistortionCS, "/Plugin/FSR3/Private/PostProcessFFX_FIConvertDistortion.usf", "MainCS", SF_Compute);
|
||
|
|
||
|
#if UE_VERSION_OLDER_THAN(5, 1, 0)
|
||
|
inline void TransitionAndCopyTexture(FRHICommandList& RHICmdList, FRHITexture* SrcTexture, FRHITexture* DstTexture, const FRHICopyTextureInfo& Info)
|
||
|
{
|
||
|
check(SrcTexture && DstTexture);
|
||
|
check(SrcTexture->GetNumSamples() == DstTexture->GetNumSamples());
|
||
|
|
||
|
if (SrcTexture == DstTexture)
|
||
|
{
|
||
|
RHICmdList.Transition({
|
||
|
FRHITransitionInfo(SrcTexture, ERHIAccess::Unknown, ERHIAccess::SRVMask)
|
||
|
});
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RHICmdList.Transition({
|
||
|
FRHITransitionInfo(SrcTexture, ERHIAccess::Unknown, ERHIAccess::CopySrc),
|
||
|
FRHITransitionInfo(DstTexture, ERHIAccess::Unknown, ERHIAccess::CopyDest)
|
||
|
});
|
||
|
|
||
|
RHICmdList.CopyTexture(SrcTexture, DstTexture, Info);
|
||
|
|
||
|
RHICmdList.Transition({
|
||
|
FRHITransitionInfo(SrcTexture, ERHIAccess::CopySrc, ERHIAccess::SRVMask),
|
||
|
FRHITransitionInfo(DstTexture, ERHIAccess::CopyDest, ERHIAccess::SRVMask)
|
||
|
});
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
// Implementation for the Frame Interpolation.
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
FFXFrameInterpolation::FFXFrameInterpolation()
|
||
|
: GameDeltaTime(0.0)
|
||
|
, LastTime(FPlatformTime::Seconds())
|
||
|
, AverageTime(0.f)
|
||
|
, AverageFPS(0.f)
|
||
|
, InterpolationCount(0llu)
|
||
|
, PresentCount(0llu)
|
||
|
, Index(0u)
|
||
|
, ResetState(0u)
|
||
|
, bInterpolatedFrame(false)
|
||
|
{
|
||
|
UGameViewportClient::OnViewportCreated().AddRaw(this, &FFXFrameInterpolation::OnViewportCreatedHandler_SetCustomPresent);
|
||
|
FCoreDelegates::OnPostEngineInit.AddRaw(this, &FFXFrameInterpolation::OnPostEngineInit);
|
||
|
}
|
||
|
|
||
|
FFXFrameInterpolation::~FFXFrameInterpolation()
|
||
|
{
|
||
|
ViewExtension = nullptr;
|
||
|
}
|
||
|
|
||
|
IFFXFrameInterpolationCustomPresent* FFXFrameInterpolation::CreateCustomPresent(IFFXSharedBackend* Backend, uint32_t Flags, FIntPoint RenderSize, FIntPoint DisplaySize, FfxSwapchain RawSwapChain, FfxCommandQueue Queue, FfxApiSurfaceFormat Format, EFFXBackendAPI Api)
|
||
|
{
|
||
|
FFXFrameInterpolationCustomPresent* Result = new FFXFrameInterpolationCustomPresent;
|
||
|
if (Result)
|
||
|
{
|
||
|
if (Result->InitSwapChain(Backend, Flags, RenderSize, DisplaySize, RawSwapChain, Queue, Format, Api))
|
||
|
{
|
||
|
SwapChains.Add(RawSwapChain, Result);
|
||
|
}
|
||
|
}
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
bool FFXFrameInterpolation::GetAverageFrameTimes(float& AvgTimeMs, float& AvgFPS)
|
||
|
{
|
||
|
bool bOK = false;
|
||
|
AvgTimeMs = GAverageMS;
|
||
|
AvgFPS = GAverageFPS;
|
||
|
auto* Engine = GEngine;
|
||
|
auto GameViewport = Engine ? Engine->GameViewport : nullptr;
|
||
|
auto Viewport = GameViewport ? GameViewport->Viewport : nullptr;
|
||
|
auto ViewportRHI = Viewport ? Viewport->GetViewportRHI() : nullptr;
|
||
|
FFXFrameInterpolationCustomPresent* Presenter = ViewportRHI.IsValid() ? (FFXFrameInterpolationCustomPresent*)ViewportRHI->GetCustomPresent() : nullptr;
|
||
|
if (Presenter)
|
||
|
{
|
||
|
if ((Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative) || Presenter->GetUseFFXSwapchain())
|
||
|
{
|
||
|
bOK = Presenter->GetBackend()->GetAverageFrameTimes(AvgTimeMs, AvgFPS);
|
||
|
}
|
||
|
else if (Presenter->GetMode() == EFFXFrameInterpolationPresentModeRHI)
|
||
|
{
|
||
|
AvgTimeMs = AverageTime;
|
||
|
AvgFPS = AverageFPS;
|
||
|
bOK = true;
|
||
|
}
|
||
|
}
|
||
|
return bOK;
|
||
|
}
|
||
|
|
||
|
void FFXFrameInterpolation::OnViewportCreatedHandler_SetCustomPresent()
|
||
|
{
|
||
|
if (GEngine && GEngine->GameViewport)
|
||
|
{
|
||
|
if (!GEngine->GameViewport->Viewport->GetViewportRHI().IsValid())
|
||
|
{
|
||
|
GEngine->GameViewport->OnBeginDraw().AddRaw(this, &FFXFrameInterpolation::OnBeginDrawHandler);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FFXFrameInterpolation::OnBeginDrawHandler()
|
||
|
{
|
||
|
if (GEngine->GameViewport->Viewport->GetViewportRHI().IsValid() && (GEngine->GameViewport->Viewport->GetViewportRHI()->GetCustomPresent() == nullptr))
|
||
|
{
|
||
|
auto ViewportRHI = GEngine->GameViewport->Viewport->GetViewportRHI();
|
||
|
void* NativeSwapChain = ViewportRHI->GetNativeSwapChain();
|
||
|
FFXFrameInterpolationCustomPresent** PresentHandler = SwapChains.Find(NativeSwapChain);
|
||
|
if (PresentHandler)
|
||
|
{
|
||
|
(*PresentHandler)->InitViewport(GEngine->GameViewport->Viewport, GEngine->GameViewport->Viewport->GetViewportRHI());
|
||
|
}
|
||
|
else if ((CVarFSR3UseRHI.GetValueOnAnyThread() != 0) || FParse::Param(FCommandLine::Get(), TEXT("fsr3rhi")))
|
||
|
{
|
||
|
IFFXSharedBackendModule* RHIBackendModule = FModuleManager::GetModulePtr<IFFXSharedBackendModule>(TEXT("FFXRHIBackend"));
|
||
|
check(RHIBackendModule);
|
||
|
|
||
|
auto* RHIBackend = RHIBackendModule->GetBackend();
|
||
|
RHIBackend->Init();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FFXFrameInterpolation::CalculateFPSTimings()
|
||
|
{
|
||
|
auto* Engine = GEngine;
|
||
|
auto GameViewport = Engine ? Engine->GameViewport : nullptr;
|
||
|
auto Viewport = GameViewport ? GameViewport->Viewport : nullptr;
|
||
|
auto ViewportRHI = Viewport ? Viewport->GetViewportRHI() : nullptr;
|
||
|
FFXFrameInterpolationCustomPresent* Presenter = ViewportRHI.IsValid() ? (FFXFrameInterpolationCustomPresent*)ViewportRHI->GetCustomPresent() : nullptr;
|
||
|
if (CVarEnableFFXFI.GetValueOnAnyThread() != 0 && Presenter && Presenter->GetMode() == EFFXFrameInterpolationPresentModeRHI)
|
||
|
{
|
||
|
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)
|
||
|
{
|
||
|
GAverageMS = AverageTime;
|
||
|
GAverageFPS = AverageFPS;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FFXFrameInterpolation::OnPostEngineInit()
|
||
|
{
|
||
|
if (FSlateApplication::IsInitialized())
|
||
|
{
|
||
|
FSlateApplication& App = FSlateApplication::Get();
|
||
|
|
||
|
// Has to be used by all backends as otherwise we end up waiting on DrawBuffers.
|
||
|
{
|
||
|
FSlateApplicationBase& BaseApp = static_cast<FSlateApplicationBase&>(App);
|
||
|
FFXFISlateApplicationAccessor& Accessor = (FFXFISlateApplicationAccessor&)BaseApp;
|
||
|
TSharedPtr<FSlateRenderer>* Ptr = &Accessor.Renderer;
|
||
|
auto SharedRef = Ptr->ToSharedRef();
|
||
|
TSharedRef<FFXFrameInterpolationSlateRenderer> RendererWrapper = MakeShared<FFXFrameInterpolationSlateRenderer>(SharedRef);
|
||
|
App.InitializeRenderer(RendererWrapper, true);
|
||
|
}
|
||
|
|
||
|
FSlateRenderer* SlateRenderer = App.GetRenderer();
|
||
|
SlateRenderer->OnSlateWindowRendered().AddRaw(const_cast<FFXFrameInterpolation*>(this), &FFXFrameInterpolation::OnSlateWindowRendered);
|
||
|
SlateRenderer->OnBackBufferReadyToPresent().AddRaw(const_cast<FFXFrameInterpolation*>(this), &FFXFrameInterpolation::OnBackBufferReadyToPresentCallback);
|
||
|
GEngine->GetPostRenderDelegateEx().AddRaw(const_cast<FFXFrameInterpolation*>(this), &FFXFrameInterpolation::InterpolateFrame);
|
||
|
|
||
|
FFXFrameInterpolation* Self = this;
|
||
|
FCoreDelegates::OnBeginFrame.AddLambda([Self]()
|
||
|
{
|
||
|
ENQUEUE_RENDER_COMMAND(BeginFrameRT)([Self](FRHICommandListImmediate& RHICmdList)
|
||
|
{
|
||
|
Self->CalculateFPSTimings();
|
||
|
});
|
||
|
});
|
||
|
|
||
|
ViewExtension = FSceneViewExtensions::NewExtension<FFXFrameInterpolationViewExtension>(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FFXFrameInterpolation::SetupView(const FSceneView& InView, const FPostProcessingInputs& Inputs)
|
||
|
{
|
||
|
if (InView.bIsViewInfo)
|
||
|
{
|
||
|
FFXFrameInterpolationView View;
|
||
|
View.ViewFamilyTexture = Inputs.ViewFamilyTexture;
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
View.SceneDepth = Inputs.SceneTextures->GetContents()->SceneDepthTexture;
|
||
|
View.SceneVelocity = Inputs.SceneTextures->GetContents()->GBufferVelocityTexture;
|
||
|
View.CameraNear = InView.ViewMatrices.ComputeNearPlane();
|
||
|
#else
|
||
|
View.SceneDepth = (*Inputs.SceneTextures)->SceneDepthTexture;
|
||
|
View.SceneVelocity = (*Inputs.SceneTextures)->GBufferVelocityTexture;
|
||
|
View.CameraNear = GNearClippingPlane;
|
||
|
#endif
|
||
|
View.ViewRect = ((FViewInfo const&)InView).ViewRect;
|
||
|
View.InputExtentsQuantized = View.ViewRect.Size();
|
||
|
QuantizeSceneBufferSize(((FViewInfo const&)InView).GetSecondaryViewRectSize(), View.OutputExtents);
|
||
|
View.OutputExtents = FIntPoint(FMath::Max(View.InputExtentsQuantized.X, View.OutputExtents.X), FMath::Max(View.InputExtentsQuantized.Y, View.OutputExtents.Y));
|
||
|
View.bReset = InView.bCameraCut;
|
||
|
View.CameraFOV = InView.ViewMatrices.ComputeHalfFieldOfViewPerAxis().Y * 2.0f;
|
||
|
#if UE_VERSION_AT_LEAST(5, 1, 0)
|
||
|
View.bEnabled = InView.bIsGameView && !InView.bIsSceneCapture && !InView.bIsSceneCaptureCube && !InView.bIsReflectionCapture && !InView.bIsPlanarReflection;
|
||
|
#else
|
||
|
View.bEnabled = InView.bIsGameView && !InView.bIsSceneCapture && !InView.bIsReflectionCapture && !InView.bIsPlanarReflection;
|
||
|
#endif
|
||
|
View.TemporalJitterPixels = ((FViewInfo const&)InView).TemporalJitterPixels;
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
if (View.bEnabled && (InView.GetFeatureLevel() >= ERHIFeatureLevel::SM6))
|
||
|
{
|
||
|
View.GameTimeMs = InView.Family->Time.GetDeltaWorldTimeSeconds();
|
||
|
GameDeltaTime = InView.Family->Time.GetDeltaWorldTimeSeconds();
|
||
|
#else
|
||
|
if (View.bEnabled)
|
||
|
{
|
||
|
GameDeltaTime = InView.Family->DeltaWorldTime;
|
||
|
#endif
|
||
|
Views.Add(&InView, View);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static FfxCommandList GCommandList = nullptr;
|
||
|
|
||
|
#if UE_VERSION_OLDER_THAN(5, 1, 0)
|
||
|
enum class EDisplayOutputFormat : uint8
|
||
|
{
|
||
|
SDR_sRGB,
|
||
|
SDR_Rec709,
|
||
|
SDR_ExplicitGammaMapping,
|
||
|
HDR_ACES_1000nit_ST2084,
|
||
|
HDR_ACES_2000nit_ST2084,
|
||
|
HDR_ACES_1000nit_ScRGB,
|
||
|
HDR_ACES_2000nit_ScRGB,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static uint32_t GetFfxTransferFunction(EDisplayOutputFormat UEFormat)
|
||
|
{
|
||
|
uint32_t Output = FFX_API_BACKBUFFER_TRANSFER_FUNCTION_SRGB;
|
||
|
switch (UEFormat)
|
||
|
{
|
||
|
// Gamma ST.2084
|
||
|
case EDisplayOutputFormat::HDR_ACES_1000nit_ST2084:
|
||
|
case EDisplayOutputFormat::HDR_ACES_2000nit_ST2084:
|
||
|
Output = FFX_API_BACKBUFFER_TRANSFER_FUNCTION_PQ;
|
||
|
break;
|
||
|
|
||
|
// Gamma 1.0 (Linear)
|
||
|
case EDisplayOutputFormat::HDR_ACES_1000nit_ScRGB:
|
||
|
case EDisplayOutputFormat::HDR_ACES_2000nit_ScRGB:
|
||
|
// Linear. Still supports expanded color space with values >1.0f and <0.0f.
|
||
|
// The actual range is determined by the pixel format (e.g. a UNORM format can only ever have 0-1).
|
||
|
Output = FFX_API_BACKBUFFER_TRANSFER_FUNCTION_SCRGB;
|
||
|
break;
|
||
|
|
||
|
// Gamma 2.2
|
||
|
case EDisplayOutputFormat::SDR_sRGB:
|
||
|
case EDisplayOutputFormat::SDR_Rec709:
|
||
|
Output = FFX_API_BACKBUFFER_TRANSFER_FUNCTION_SRGB;
|
||
|
break;
|
||
|
|
||
|
// Unsupported types that require modifications to the FidelityFX code in order to support
|
||
|
case EDisplayOutputFormat::SDR_ExplicitGammaMapping:
|
||
|
#if UE_VERSION_AT_LEAST(5, 1, 0)
|
||
|
case EDisplayOutputFormat::HDR_LinearEXR:
|
||
|
case EDisplayOutputFormat::HDR_LinearNoToneCurve:
|
||
|
case EDisplayOutputFormat::HDR_LinearWithToneCurve:
|
||
|
#endif
|
||
|
default:
|
||
|
check(false);
|
||
|
Output = FFX_API_BACKBUFFER_TRANSFER_FUNCTION_SRGB;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return Output;
|
||
|
}
|
||
|
|
||
|
bool FFXFrameInterpolation::InterpolateView(FRDGBuilder& GraphBuilder, FFXFrameInterpolationCustomPresent* Presenter, const FSceneView* View, FFXFrameInterpolationView const& ViewDesc, FRDGTextureRef FinalBuffer, FRDGTextureRef InterpolatedRDG, FRDGTextureRef BackBufferRDG, uint32 InterpolateIndex)
|
||
|
{
|
||
|
bool bInterpolated = false;
|
||
|
auto* Engine = GEngine;
|
||
|
auto GameViewport = Engine ? Engine->GameViewport : nullptr;
|
||
|
auto Viewport = GameViewport ? GameViewport->Viewport : nullptr;
|
||
|
auto ViewportRHI = Viewport ? Viewport->GetViewportRHI() : nullptr;
|
||
|
FIntPoint ViewportSizeXY = Viewport ? Viewport->GetSizeXY() : FIntPoint::ZeroValue;
|
||
|
|
||
|
FRDGTextureRef ViewFamilyTexture = ViewDesc.ViewFamilyTexture;
|
||
|
FIntRect ViewRect = ViewDesc.ViewRect;
|
||
|
FIntPoint InputExtents = ViewDesc.ViewRect.Size();
|
||
|
FIntPoint InputExtentsQuantized = ViewDesc.InputExtentsQuantized;
|
||
|
FIntPoint InputTextureExtents = CVarFSR3QuantizeInternalTextures.GetValueOnRenderThread() ? InputExtentsQuantized : InputExtents;
|
||
|
FIntPoint OutputExtents = ((FViewInfo*)View)->UnscaledViewRect.Size();
|
||
|
FIntPoint OutputPoint = ((FViewInfo*)View)->UnscaledViewRect.Min;
|
||
|
float CameraNear = ViewDesc.CameraNear;
|
||
|
float CameraFOV = ViewDesc.CameraFOV;
|
||
|
bool bEnabled = ViewDesc.bEnabled;
|
||
|
bool bReset = ViewDesc.bReset || (ResetState == 0);
|
||
|
bool const bResized = Presenter->Resized();
|
||
|
float DeltaTimeMs = GameDeltaTime * 1000.f;
|
||
|
FRHICopyTextureInfo Info;
|
||
|
|
||
|
ffxDispatchDescFrameGenerationPrepare UpscalerDesc;
|
||
|
FMemory::Memzero(UpscalerDesc);
|
||
|
UpscalerDesc.header.type = FFX_API_DISPATCH_DESC_TYPE_FRAMEGENERATION_PREPARE;
|
||
|
#if UE_VERSION_AT_LEAST(5, 3, 0)
|
||
|
UpscalerDesc.frameID = View->Family->FrameCounter;
|
||
|
#else
|
||
|
UpscalerDesc.frameID = GFrameCounterRenderThread;
|
||
|
#endif
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
UpscalerDesc.frameTimeDelta = View->Family->Time.GetDeltaWorldTimeSeconds() * 1000.f;
|
||
|
#else
|
||
|
UpscalerDesc.frameTimeDelta = DeltaTimeMs;
|
||
|
#endif
|
||
|
if (bool(ERHIZBuffer::IsInverted))
|
||
|
{
|
||
|
UpscalerDesc.cameraNear = FLT_MAX;
|
||
|
UpscalerDesc.cameraFar = CameraNear;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UpscalerDesc.cameraNear = CameraNear;
|
||
|
UpscalerDesc.cameraFar = FLT_MAX;
|
||
|
}
|
||
|
UpscalerDesc.cameraFovAngleVertical = CameraFOV;
|
||
|
UpscalerDesc.viewSpaceToMetersFactor = 1.f / View->WorldToMetersScale;
|
||
|
|
||
|
UpscalerDesc.jitterOffset.x = ((FViewInfo*)View)->TemporalJitterPixels.X;
|
||
|
UpscalerDesc.jitterOffset.y = ((FViewInfo*)View)->TemporalJitterPixels.Y;
|
||
|
|
||
|
UpscalerDesc.renderSize.width = InputExtents.X;
|
||
|
UpscalerDesc.renderSize.height = InputExtents.Y;
|
||
|
UpscalerDesc.motionVectorScale.x = InputExtents.X;
|
||
|
UpscalerDesc.motionVectorScale.y = InputExtents.Y;
|
||
|
|
||
|
FIntPoint MaxRenderSize = ((FViewInfo*)View)->GetSecondaryViewRectSize();
|
||
|
|
||
|
ffxCreateContextDescFrameGeneration FgDesc;
|
||
|
FMemory::Memzero(FgDesc);
|
||
|
FgDesc.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_FRAMEGENERATION;
|
||
|
FgDesc.backBufferFormat = GetFFXApiFormat(BackBufferRDG->Desc.Format, false);
|
||
|
FgDesc.displaySize.width = FMath::Max((uint32)MaxRenderSize.X, (uint32)OutputExtents.X);
|
||
|
FgDesc.displaySize.height = FMath::Max((uint32)MaxRenderSize.Y, (uint32)OutputExtents.Y);
|
||
|
FgDesc.maxRenderSize.width = MaxRenderSize.X;
|
||
|
FgDesc.maxRenderSize.height = MaxRenderSize.Y;
|
||
|
FgDesc.flags |= (bool(ERHIZBuffer::IsInverted)) ? FFX_FRAMEGENERATION_ENABLE_DEPTH_INVERTED : 0;
|
||
|
FgDesc.flags |= FFX_FRAMEGENERATION_ENABLE_HIGH_DYNAMIC_RANGE | FFX_FRAMEGENERATION_ENABLE_DEPTH_INFINITE;
|
||
|
FgDesc.flags |= CVarFSR3AllowAsyncWorkloads.GetValueOnAnyThread() ? FFX_FRAMEGENERATION_ENABLE_ASYNC_WORKLOAD_SUPPORT : 0;
|
||
|
|
||
|
FRDGTextureRef ColorBuffer = FinalBuffer;
|
||
|
FRDGTextureRef InterBuffer = InterpolatedRDG;
|
||
|
FRDGTextureRef HudBuffer = nullptr;
|
||
|
FFXFIResourceRef Context = Presenter->UpdateContexts(GraphBuilder, ((FSceneViewState*)View->State)->UniqueID, UpscalerDesc, FgDesc);
|
||
|
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
// Consolidate Motion Vectors
|
||
|
// UE motion vectors are in sparse format by default. Convert them to a format consumable by FFX.
|
||
|
//------------------------------------------------------------------------------------------------------
|
||
|
if (!IsValidRef(Context->MotionVectorRT) || Context->MotionVectorRT->GetDesc().Extent.X != InputTextureExtents.X || Context->MotionVectorRT->GetDesc().Extent.Y != InputTextureExtents.Y)
|
||
|
{
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
ETextureCreateFlags DescFlags = TexCreate_ShaderResource | TexCreate_UAV;
|
||
|
#else
|
||
|
ETextureCreateFlags DescFlags = TexCreate_ShaderResource;
|
||
|
#endif
|
||
|
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(InputTextureExtents,
|
||
|
PF_G16R16F,
|
||
|
FClearValueBinding::Transparent,
|
||
|
DescFlags,
|
||
|
TexCreate_ShaderResource | TexCreate_UAV | TexCreate_RenderTargetable,
|
||
|
false));
|
||
|
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, Desc, Context->MotionVectorRT, TEXT("FFXFIMotionVectorTexture"));
|
||
|
}
|
||
|
|
||
|
FRDGTextureRef MotionVectorTexture = GraphBuilder.RegisterExternalTexture(Context->MotionVectorRT);
|
||
|
{
|
||
|
FFXFIConvertVelocityCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FFXFIConvertVelocityCS::FParameters>();
|
||
|
FRDGTextureUAVDesc OutputDesc(MotionVectorTexture);
|
||
|
|
||
|
FRDGTextureSRVDesc DepthDesc = FRDGTextureSRVDesc::Create(ViewDesc.SceneDepth);
|
||
|
FRDGTextureSRVDesc VelocityDesc = FRDGTextureSRVDesc::Create(ViewDesc.SceneVelocity);
|
||
|
|
||
|
PassParameters->DepthTexture = ViewDesc.SceneDepth;
|
||
|
PassParameters->InputDepth = GraphBuilder.CreateSRV(DepthDesc);
|
||
|
PassParameters->InputVelocity = GraphBuilder.CreateSRV(VelocityDesc);
|
||
|
|
||
|
PassParameters->View = ((FViewInfo*)View)->ViewUniformBuffer;
|
||
|
|
||
|
PassParameters->OutputTexture = GraphBuilder.CreateUAV(OutputDesc);
|
||
|
|
||
|
TShaderMapRef<FFXFIConvertVelocityCS> ComputeShaderFSR(((FViewInfo*)View)->ShaderMap);
|
||
|
FComputeShaderUtils::AddPass(
|
||
|
GraphBuilder,
|
||
|
RDG_EVENT_NAME("FidelityFX-FI/ConvertVelocity (CS)"),
|
||
|
ComputeShaderFSR,
|
||
|
PassParameters,
|
||
|
FComputeShaderUtils::GetGroupCount(FIntVector(ViewDesc.SceneDepth->Desc.Extent.X, ViewDesc.SceneDepth->Desc.Extent.Y, 1),
|
||
|
FIntVector(FFXFIConvertVelocityCS::ThreadgroupSizeX, FFXFIConvertVelocityCS::ThreadgroupSizeY, FFXFIConvertVelocityCS::ThreadgroupSizeZ))
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
FRDGTextureRef DistortionTexture = nullptr;
|
||
|
#if UE_VERSION_AT_LEAST(5, 1, 0)
|
||
|
if (CVarFFXFIUseDistortionTexture.GetValueOnRenderThread()) {
|
||
|
FFXFIRDGBuilder& GraphBulderAccessor = (FFXFIRDGBuilder&)GraphBuilder;
|
||
|
FRDGTextureRef UEDistortionTexture = GraphBulderAccessor.FindTexture(TEXT("Distortion"));
|
||
|
|
||
|
if (HasBeenProduced(UEDistortionTexture)) {
|
||
|
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(InputTextureExtents, PF_G16R16F, FClearValueBinding::Transparent, TexCreate_ShaderResource | TexCreate_UAV);
|
||
|
|
||
|
DistortionTexture = GraphBuilder.CreateTexture(Desc, TEXT("FFXFIDistortionTexture"));
|
||
|
|
||
|
FFXFIConvertDistortionCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FFXFIConvertDistortionCS::FParameters>();
|
||
|
|
||
|
PassParameters->View = ((FViewInfo*)View)->ViewUniformBuffer;
|
||
|
|
||
|
PassParameters->UEDistortion = GraphBuilder.CreateSRV(UEDistortionTexture);
|
||
|
PassParameters->OutputTexture = GraphBuilder.CreateUAV(DistortionTexture);
|
||
|
|
||
|
TShaderMapRef<FFXFIConvertDistortionCS> ComputeShaderFSR(((FViewInfo*)View)->ShaderMap);
|
||
|
FComputeShaderUtils::AddPass(
|
||
|
GraphBuilder,
|
||
|
RDG_EVENT_NAME("FidelityFX-FI/ConvertDistortion (CS)"),
|
||
|
ComputeShaderFSR,
|
||
|
PassParameters,
|
||
|
FComputeShaderUtils::GetGroupCount(FIntVector(ViewDesc.SceneDepth->Desc.Extent.X, ViewDesc.SceneDepth->Desc.Extent.Y, 1),
|
||
|
FIntVector(FFXFIConvertDistortionCS::ThreadgroupSizeX, FFXFIConvertDistortionCS::ThreadgroupSizeY, FFXFIConvertDistortionCS::ThreadgroupSizeZ))
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (Context->Desc.displaySize.width != ViewportSizeXY.X || Context->Desc.displaySize.height != ViewportSizeXY.Y)
|
||
|
{
|
||
|
if (!IsValidRef(Context->Color) || Context->Color->GetDesc().Extent.X != Context->Desc.displaySize.width || Context->Color->GetDesc().Extent.Y != Context->Desc.displaySize.height || Context->Color->GetDesc().Format != BackBufferRDG->Desc.Format)
|
||
|
{
|
||
|
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(Context->Desc.displaySize.width, Context->Desc.displaySize.height),
|
||
|
BackBufferRDG->Desc.Format,
|
||
|
FClearValueBinding::Transparent,
|
||
|
TexCreate_UAV | TexCreate_ShaderResource,
|
||
|
TexCreate_UAV | TexCreate_ShaderResource,
|
||
|
false,
|
||
|
1,
|
||
|
true,
|
||
|
true));
|
||
|
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, Desc, Context->Color, TEXT("FIColor"));
|
||
|
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, Desc, Context->Inter, TEXT("FIInter"));
|
||
|
|
||
|
if ((Presenter->GetBackend()->GetAPI() != EFFXBackendAPI::Unreal) && (Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative))
|
||
|
{
|
||
|
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, Desc, Context->Hud, TEXT("FIHud"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FRHICopyTextureInfo CopyInfo;
|
||
|
ColorBuffer = GraphBuilder.RegisterExternalTexture(Context->Color);
|
||
|
CopyInfo.SourcePosition.X = OutputPoint.X;
|
||
|
CopyInfo.SourcePosition.Y = OutputPoint.Y;
|
||
|
CopyInfo.Size.X = FMath::Min((uint32)Context->Desc.displaySize.width, (uint32)FinalBuffer->Desc.Extent.X);
|
||
|
CopyInfo.Size.Y = FMath::Min((uint32)Context->Desc.displaySize.height, (uint32)FinalBuffer->Desc.Extent.Y);
|
||
|
AddCopyTexturePass(GraphBuilder, FinalBuffer, ColorBuffer, CopyInfo);
|
||
|
|
||
|
if ((Presenter->GetBackend()->GetAPI() != EFFXBackendAPI::Unreal) && (Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative))
|
||
|
{
|
||
|
HudBuffer = GraphBuilder.RegisterExternalTexture(Context->Hud);
|
||
|
AddCopyTexturePass(GraphBuilder, BackBufferRDG, HudBuffer, CopyInfo);
|
||
|
}
|
||
|
|
||
|
InterBuffer = GraphBuilder.RegisterExternalTexture(Context->Inter);
|
||
|
|
||
|
FRDGTextureUAVDesc Interpolatedesc(InterBuffer);
|
||
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Interpolatedesc), FVector::ZeroVector);
|
||
|
}
|
||
|
|
||
|
FFXFrameInterpolationPass::FParameters* PassParameters = GraphBuilder.AllocParameters<FFXFrameInterpolationPass::FParameters>();
|
||
|
PassParameters->ColorTexture = ColorBuffer;
|
||
|
PassParameters->BackBufferTexture = BackBufferRDG;
|
||
|
PassParameters->HudTexture = HudBuffer;
|
||
|
PassParameters->InterpolatedRT = InterBuffer;
|
||
|
PassParameters->Interpolated = InterpolatedRDG;
|
||
|
PassParameters->SceneDepth = ViewDesc.SceneDepth;
|
||
|
PassParameters->MotionVectors = MotionVectorTexture;
|
||
|
|
||
|
static const auto CVarHDRMinLuminanceLog10 = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.HDR.Display.MinLuminanceLog10"));
|
||
|
static const auto CVarHDRMaxLuminance = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HDR.Display.MaxLuminance"));
|
||
|
|
||
|
float GHDRMinLuminnanceLog10 = CVarHDRMinLuminanceLog10 ? CVarHDRMinLuminanceLog10->GetValueOnAnyThread() : 0.f;
|
||
|
int32 GHDRMaxLuminnance = CVarHDRMaxLuminance ? CVarHDRMaxLuminance->GetValueOnAnyThread() : 1;
|
||
|
#if UE_VERSION_AT_LEAST(5, 1, 0)
|
||
|
EDisplayOutputFormat ViewportOutputFormat = Viewport->GetDisplayOutputFormat();
|
||
|
#else
|
||
|
static const auto CVarHDROutputDevice = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HDR.Display.OutputDevice"));
|
||
|
static const auto CVarHDROutputEnabled = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HDR.EnableHDROutput"));
|
||
|
const EDisplayOutputFormat ViewportOutputFormat = (CVarHDROutputDevice && GRHISupportsHDROutput && (CVarHDROutputEnabled && CVarHDROutputEnabled->GetValueOnAnyThread() != 0)) ? EDisplayOutputFormat(CVarHDROutputDevice->GetValueOnAnyThread()) : EDisplayOutputFormat::SDR_sRGB;
|
||
|
if ((GHDRMaxLuminnance == 0) && (ViewportOutputFormat == EDisplayOutputFormat::HDR_ACES_1000nit_ST2084 || ViewportOutputFormat == EDisplayOutputFormat::HDR_ACES_2000nit_ST2084 || ViewportOutputFormat == EDisplayOutputFormat::HDR_ACES_1000nit_ScRGB || ViewportOutputFormat == EDisplayOutputFormat::HDR_ACES_2000nit_ScRGB))
|
||
|
{
|
||
|
if (ViewportOutputFormat == EDisplayOutputFormat::HDR_ACES_1000nit_ST2084 || ViewportOutputFormat == EDisplayOutputFormat::HDR_ACES_1000nit_ScRGB)
|
||
|
{
|
||
|
GHDRMaxLuminnance = 1000;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
GHDRMaxLuminnance = 2000;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// compute how many VSync intervals interpolated and real frame should be displayed
|
||
|
ffxDispatchDescFrameGeneration* interpolateParams = new ffxDispatchDescFrameGeneration;
|
||
|
{
|
||
|
interpolateParams->header.type = FFX_API_DISPATCH_DESC_TYPE_FRAMEGENERATION;
|
||
|
interpolateParams->header.pNext = nullptr;
|
||
|
interpolateParams->numGeneratedFrames = 1;
|
||
|
#if UE_VERSION_AT_LEAST(5, 3, 0)
|
||
|
interpolateParams->frameID = View->Family->FrameCounter;
|
||
|
#else
|
||
|
interpolateParams->frameID = GFrameCounterRenderThread;
|
||
|
#endif
|
||
|
interpolateParams->backbufferTransferFunction = GetFfxTransferFunction(ViewportOutputFormat);
|
||
|
interpolateParams->generationRect = {0 ,0, (int32_t)Context->Desc.displaySize.width, (int32_t)Context->Desc.displaySize.height};
|
||
|
interpolateParams->reset = bReset;
|
||
|
|
||
|
interpolateParams->minMaxLuminance[0] = interpolateParams->backbufferTransferFunction != FFX_API_BACKBUFFER_TRANSFER_FUNCTION_SRGB ? FMath::Pow(10, GHDRMinLuminnanceLog10) : 0.f;
|
||
|
interpolateParams->minMaxLuminance[1] = interpolateParams->backbufferTransferFunction != FFX_API_BACKBUFFER_TRANSFER_FUNCTION_SRGB ? GHDRMaxLuminnance : 1.f;
|
||
|
}
|
||
|
|
||
|
auto DisplaySize = ColorBuffer->Desc.Extent;
|
||
|
bool const bOverrideSwapChain = ((CVarFSR3OverrideSwapChainDX12.GetValueOnAnyThread() != 0) || FParse::Param(FCommandLine::Get(), TEXT("fsr3swapchain")));
|
||
|
|
||
|
ffxConfigureDescFrameGeneration ConfigDesc;
|
||
|
FMemory::Memzero(ConfigDesc);
|
||
|
ConfigDesc.header.type = FFX_API_CONFIGURE_DESC_TYPE_FRAMEGENERATION;
|
||
|
ConfigDesc.swapChain = Presenter->GetBackend()->GetSwapchain(ViewportRHI->GetNativeSwapChain());
|
||
|
ConfigDesc.frameGenerationEnabled = true;
|
||
|
ConfigDesc.allowAsyncWorkloads = (CVarFSR3AllowAsyncWorkloads.GetValueOnAnyThread() != 0);
|
||
|
ConfigDesc.generationRect = interpolateParams->generationRect;
|
||
|
ConfigDesc.frameID = interpolateParams->frameID;
|
||
|
ConfigDesc.flags |= bOverrideSwapChain ? 0 : FFX_FRAMEGENERATION_FLAG_NO_SWAPCHAIN_CONTEXT_NOTIFY;
|
||
|
#if (UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT || UE_BUILD_TEST)
|
||
|
ConfigDesc.flags |= CVarFFXFIShowDebugTearLines.GetValueOnAnyThread() ? FFX_FRAMEGENERATION_FLAG_DRAW_DEBUG_TEAR_LINES : 0;
|
||
|
ConfigDesc.flags |= CVarFFXFIShowDebugView.GetValueOnAnyThread() ? FFX_FRAMEGENERATION_FLAG_DRAW_DEBUG_VIEW : 0;
|
||
|
#endif
|
||
|
|
||
|
if (Presenter->GetBackend()->GetAPI() == EFFXBackendAPI::Unreal)
|
||
|
{
|
||
|
bInterpolated = true;
|
||
|
interpolateParams->presentColor = Presenter->GetBackend()->GetNativeResource(PassParameters->ColorTexture GET_TEXTURE, FFX_API_RESOURCE_STATE_COPY_DEST);
|
||
|
|
||
|
Presenter->GetBackend()->SetFeatureLevel(&Context->Context, View->GetFeatureLevel());
|
||
|
|
||
|
|
||
|
if (!HasBeenProduced(DistortionTexture)) {
|
||
|
DistortionTexture = GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy);
|
||
|
}
|
||
|
PassParameters->DistortionTexture = DistortionTexture;
|
||
|
|
||
|
GraphBuilder.AddPass(RDG_EVENT_NAME("FidelityFX-FrameInterpolation"), PassParameters, ERDGPassFlags::Compute | ERDGPassFlags::NeverCull | ERDGPassFlags::Copy, [Presenter, PassParameters, Context, ConfigDesc](FRHICommandListImmediate& RHICmdList)
|
||
|
{
|
||
|
PassParameters->ColorTexture->MarkResourceAsUsed();
|
||
|
PassParameters->InterpolatedRT->MarkResourceAsUsed();
|
||
|
PassParameters->SceneDepth->MarkResourceAsUsed();
|
||
|
PassParameters->MotionVectors->MarkResourceAsUsed();
|
||
|
PassParameters->DistortionTexture->MarkResourceAsUsed();
|
||
|
Context->Distortion = PassParameters->DistortionTexture->GetRHI();
|
||
|
|
||
|
Presenter->SetCustomPresentStatus(FFXFrameInterpolationCustomPresentStatus::InterpolateRT);
|
||
|
RHICmdList.EnqueueLambda([Presenter, Context, ConfigDesc](FRHICommandListImmediate& cmd) mutable
|
||
|
{
|
||
|
if (Context->Distortion.IsValid())
|
||
|
{
|
||
|
ffxConfigureDescFrameGenerationRegisterDistortionFieldResource DistortionDesc;
|
||
|
DistortionDesc.header.pNext = nullptr;
|
||
|
DistortionDesc.header.type = FFX_API_CONFIGURE_DESC_TYPE_FRAMEGENERATION_REGISTERDISTORTIONRESOURCE;
|
||
|
DistortionDesc.distortionField = Presenter->GetBackend()->GetNativeResource(Context->Distortion, FFX_API_RESOURCE_STATE_PIXEL_READ);
|
||
|
Presenter->GetBackend()->UpdateSwapChain(&Context->Context, ConfigDesc, DistortionDesc);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Presenter->GetBackend()->UpdateSwapChain(&Context->Context, ConfigDesc);
|
||
|
}
|
||
|
|
||
|
Presenter->SetCustomPresentStatus(FFXFrameInterpolationCustomPresentStatus::InterpolateRHI);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
|
||
|
// Interpolate the frame
|
||
|
{
|
||
|
interpolateParams->commandList = (void*)&GraphBuilder;
|
||
|
interpolateParams->outputs[0] = Presenter->GetBackend()->GetNativeResource(PassParameters->InterpolatedRT GET_TEXTURE, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS);
|
||
|
|
||
|
UpscalerDesc.commandList = interpolateParams->commandList;
|
||
|
UpscalerDesc.depth = Presenter->GetBackend()->GetNativeResource(PassParameters->SceneDepth GET_TEXTURE, FFX_API_RESOURCE_STATE_COMPUTE_READ);
|
||
|
UpscalerDesc.motionVectors = Presenter->GetBackend()->GetNativeResource(PassParameters->MotionVectors GET_TEXTURE, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS);
|
||
|
|
||
|
auto Code = Presenter->GetBackend()->ffxDispatch(&Context->Context, &UpscalerDesc.header);
|
||
|
check(Code == FFX_API_RETURN_OK);
|
||
|
|
||
|
Code = Presenter->GetBackend()->ffxDispatch(&Context->Context, &interpolateParams->header);
|
||
|
check(Code == FFX_API_RETURN_OK);
|
||
|
|
||
|
Info.Size.X = DisplaySize.X;
|
||
|
Info.Size.Y = DisplaySize.Y;
|
||
|
if (PassParameters->Interpolated != PassParameters->InterpolatedRT)
|
||
|
{
|
||
|
Info.DestPosition.X = OutputPoint.X;
|
||
|
Info.DestPosition.Y = OutputPoint.Y;
|
||
|
Info.Size.X = FMath::Min((uint32)DisplaySize.X, (uint32)PassParameters->Interpolated->Desc.Extent.X);
|
||
|
Info.Size.Y = FMath::Min((uint32)DisplaySize.Y, (uint32)PassParameters->Interpolated->Desc.Extent.Y);
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
AddCopyTexturePass(GraphBuilder, PassParameters->InterpolatedRT, PassParameters->Interpolated, Info);
|
||
|
AddCopyTexturePass(GraphBuilder, PassParameters->InterpolatedRT, BackBufferRDG, Info);
|
||
|
#else
|
||
|
AddCopyTexturePass(GraphBuilder, PassParameters->InterpolatedRT.GetTexture(), PassParameters->Interpolated.GetTexture(), Info);
|
||
|
AddCopyTexturePass(GraphBuilder, PassParameters->InterpolatedRT.GetTexture(), BackBufferRDG, Info);
|
||
|
#endif
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
check(Info.Size.X == BackBufferRDG->Desc.Extent.X && Info.Size.Y == BackBufferRDG->Desc.Extent.Y);
|
||
|
check(Info.Size.X == PassParameters->InterpolatedRT->Desc.Extent.X && Info.Size.Y == PassParameters->InterpolatedRT->Desc.Extent.Y);
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
AddCopyTexturePass(GraphBuilder, PassParameters->InterpolatedRT, BackBufferRDG, Info);
|
||
|
#else
|
||
|
AddCopyTexturePass(GraphBuilder, PassParameters->InterpolatedRT.GetTexture(), BackBufferRDG, Info);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
delete interpolateParams;
|
||
|
}
|
||
|
}
|
||
|
else if (!bResized)
|
||
|
{
|
||
|
bInterpolated = true;
|
||
|
|
||
|
if (HasBeenProduced(DistortionTexture)) {
|
||
|
PassParameters->DistortionTexture = DistortionTexture;
|
||
|
}
|
||
|
|
||
|
GraphBuilder.AddPass(RDG_EVENT_NAME("FidelityFX-FrameInterpolation"), PassParameters, ERDGPassFlags::Compute | ERDGPassFlags::NeverCull | ERDGPassFlags::Copy, [InterpolateIndex, UpscalerDesc, ConfigDesc, OutputExtents, OutputPoint, ViewportRHI, Presenter, Context, PassParameters, interpolateParams, DeltaTimeMs, Engine, ViewportOutputFormat, GHDRMinLuminnanceLog10, GHDRMaxLuminnance](FRHICommandListImmediate& RHICmdList)
|
||
|
{
|
||
|
PassParameters->ColorTexture->MarkResourceAsUsed();
|
||
|
PassParameters->InterpolatedRT->MarkResourceAsUsed();
|
||
|
if (PassParameters->HudTexture)
|
||
|
{
|
||
|
PassParameters->HudTexture->MarkResourceAsUsed();
|
||
|
}
|
||
|
PassParameters->SceneDepth->MarkResourceAsUsed();
|
||
|
PassParameters->MotionVectors->MarkResourceAsUsed();
|
||
|
if (PassParameters->DistortionTexture)
|
||
|
{
|
||
|
PassParameters->DistortionTexture->MarkResourceAsUsed();
|
||
|
Context->Distortion = PassParameters->DistortionTexture->GetRHI();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Context->Distortion.SafeRelease();
|
||
|
}
|
||
|
|
||
|
bool const bWholeScreen = (PassParameters->Interpolated.GetTexture() == PassParameters->InterpolatedRT.GetTexture());
|
||
|
ffxConfigureDescFrameGeneration ConfigureDesc = ConfigDesc;
|
||
|
|
||
|
if (Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative)
|
||
|
{
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
ConfigureDesc.HUDLessColor = Presenter->GetBackend()->GetNativeResource(PassParameters->ColorTexture, FFX_API_RESOURCE_STATE_COPY_DEST);
|
||
|
#else
|
||
|
ConfigureDesc.HUDLessColor = Presenter->GetBackend()->GetNativeResource(PassParameters->ColorTexture.GetTexture(), FFX_API_RESOURCE_STATE_COPY_DEST);
|
||
|
#endif
|
||
|
interpolateParams->presentColor = Presenter->GetBackend()->GetNativeResource(bWholeScreen ? PassParameters->BackBufferTexture.GetTexture() : PassParameters->HudTexture.GetTexture(), bWholeScreen ? FFX_API_RESOURCE_STATE_PRESENT : FFX_API_RESOURCE_STATE_COPY_DEST);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
interpolateParams->presentColor = Presenter->GetBackend()->GetNativeResource(PassParameters->ColorTexture, FFX_API_RESOURCE_STATE_COPY_DEST);
|
||
|
#else
|
||
|
interpolateParams->presentColor = Presenter->GetBackend()->GetNativeResource(PassParameters->ColorTexture.GetTexture(), FFX_API_RESOURCE_STATE_COPY_DEST);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (InterpolateIndex != 0)
|
||
|
{
|
||
|
ConfigureDesc.swapChain = nullptr;
|
||
|
ConfigureDesc.presentCallback = nullptr;
|
||
|
ConfigureDesc.presentCallbackUserContext = nullptr;
|
||
|
ConfigureDesc.frameGenerationCallback = nullptr;
|
||
|
ConfigureDesc.frameGenerationCallbackUserContext = nullptr;
|
||
|
ConfigureDesc.flags |= FFX_FRAMEGENERATION_FLAG_NO_SWAPCHAIN_CONTEXT_NOTIFY;
|
||
|
}
|
||
|
|
||
|
ffxDispatchDescFrameGenerationPrepare PrepareDesc = UpscalerDesc;
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
PrepareDesc.depth = Presenter->GetBackend()->GetNativeResource(PassParameters->SceneDepth, FFX_API_RESOURCE_STATE_COMPUTE_READ);
|
||
|
PrepareDesc.motionVectors = Presenter->GetBackend()->GetNativeResource(PassParameters->MotionVectors, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS);
|
||
|
|
||
|
auto InterpolatedRes = Presenter->GetBackend()->GetNativeResource(PassParameters->InterpolatedRT, Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative || !bWholeScreen || InterpolateIndex == 0 ? FFX_API_RESOURCE_STATE_UNORDERED_ACCESS : FFX_API_RESOURCE_STATE_COPY_SRC);
|
||
|
#else
|
||
|
PrepareDesc.depth = Presenter->GetBackend()->GetNativeResource(PassParameters->SceneDepth.GetTexture(), FFX_API_RESOURCE_STATE_COMPUTE_READ);
|
||
|
PrepareDesc.motionVectors = Presenter->GetBackend()->GetNativeResource(PassParameters->MotionVectors.GetTexture(), FFX_API_RESOURCE_STATE_UNORDERED_ACCESS);
|
||
|
|
||
|
auto InterpolatedRes = Presenter->GetBackend()->GetNativeResource(PassParameters->InterpolatedRT.GetTexture(), Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative || !bWholeScreen || InterpolateIndex == 0 ? FFX_API_RESOURCE_STATE_UNORDERED_ACCESS : FFX_API_RESOURCE_STATE_COPY_SRC);
|
||
|
#endif
|
||
|
Presenter->SetCustomPresentStatus(FFXFrameInterpolationCustomPresentStatus::InterpolateRT);
|
||
|
RHICmdList.EnqueueLambda([PrepareDesc, ConfigureDesc, ViewportRHI, Presenter, Context, InterpolatedRes, interpolateParams, OutputExtents, OutputPoint, bWholeScreen, ViewportOutputFormat, GHDRMinLuminnanceLog10, GHDRMaxLuminnance](FRHICommandListImmediate& cmd) mutable
|
||
|
{
|
||
|
if (Context->Distortion.IsValid())
|
||
|
{
|
||
|
ffxConfigureDescFrameGenerationRegisterDistortionFieldResource DistortionDesc;
|
||
|
DistortionDesc.header.pNext = nullptr;
|
||
|
DistortionDesc.header.type = FFX_API_CONFIGURE_DESC_TYPE_FRAMEGENERATION_REGISTERDISTORTIONRESOURCE;
|
||
|
DistortionDesc.distortionField = Presenter->GetBackend()->GetNativeResource(Context->Distortion, FFX_API_RESOURCE_STATE_PIXEL_READ);
|
||
|
Presenter->GetBackend()->UpdateSwapChain(&Context->Context, ConfigureDesc, DistortionDesc);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Presenter->GetBackend()->UpdateSwapChain(&Context->Context, ConfigureDesc);
|
||
|
}
|
||
|
|
||
|
Presenter->SetCustomPresentStatus(FFXFrameInterpolationCustomPresentStatus::InterpolateRHI);
|
||
|
if ((Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative) || Presenter->GetUseFFXSwapchain())
|
||
|
{
|
||
|
Presenter->GetBackend()->RegisterFrameResources(Context.GetReference(), ConfigureDesc.frameID);
|
||
|
}
|
||
|
|
||
|
FfxCommandList CmdBuffer = nullptr;
|
||
|
if (Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative)
|
||
|
{
|
||
|
if (!GCommandList)
|
||
|
{
|
||
|
GCommandList = Presenter->GetBackend()->GetInterpolationCommandList(Presenter->GetBackend()->GetSwapchain(ViewportRHI->GetNativeSwapChain()));
|
||
|
}
|
||
|
CmdBuffer = GCommandList;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CmdBuffer = Presenter->GetBackend()->GetNativeCommandBuffer(cmd, Context->MotionVectorRT->GetRHI(GET_RHI_VIEW_ARG));
|
||
|
}
|
||
|
if (CmdBuffer)
|
||
|
{
|
||
|
// Prepare the interpolation context on the current RHI command list
|
||
|
{
|
||
|
ffxDispatchDescFrameGenerationPrepare UpscalerDesc = PrepareDesc;
|
||
|
UpscalerDesc.commandList = Presenter->GetBackend()->GetNativeCommandBuffer(cmd, Context->MotionVectorRT->GetRHI(GET_RHI_VIEW_ARG));
|
||
|
|
||
|
auto Code = Presenter->GetBackend()->ffxDispatch(&Context->Context, &UpscalerDesc.header);
|
||
|
check(Code == FFX_API_RETURN_OK);
|
||
|
}
|
||
|
|
||
|
// Interpolate the frame
|
||
|
{
|
||
|
FfxApiResource OutputRes = Presenter->GetBackend()->GetInterpolationOutput(Presenter->GetBackend()->GetSwapchain(ViewportRHI->GetNativeSwapChain()));
|
||
|
interpolateParams->outputs[0] = (Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative && bWholeScreen) ? OutputRes : InterpolatedRes;
|
||
|
interpolateParams->commandList = CmdBuffer;
|
||
|
|
||
|
auto Code = Presenter->GetBackend()->ffxDispatch(&Context->Context, &interpolateParams->header);
|
||
|
check(Code == FFX_API_RETURN_OK);
|
||
|
|
||
|
if (!bWholeScreen && (Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative))
|
||
|
{
|
||
|
Presenter->GetBackend()->CopySubRect(CmdBuffer, InterpolatedRes, OutputRes, OutputExtents, OutputPoint);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
delete interpolateParams;
|
||
|
});
|
||
|
|
||
|
Presenter->GetBackend()->Flush(Context->MotionVectorRT->GetRHI(GET_RHI_VIEW_ARG), RHICmdList);
|
||
|
|
||
|
if (Presenter->GetMode() != EFFXFrameInterpolationPresentModeNative)
|
||
|
{
|
||
|
#if UE_VERSION_AT_LEAST(5, 2, 0)
|
||
|
FTextureRHIRef BackBuffer = RHIGetViewportBackBuffer(ViewportRHI);
|
||
|
#else
|
||
|
FTextureRHIRef BackBuffer = RHICmdList.GetViewportBackBuffer(ViewportRHI);
|
||
|
#endif
|
||
|
|
||
|
if (PassParameters->Interpolated != PassParameters->InterpolatedRT)
|
||
|
{
|
||
|
FRHICopyTextureInfo CopyInfo;
|
||
|
CopyInfo.DestPosition.X = OutputPoint.X;
|
||
|
CopyInfo.DestPosition.Y = OutputPoint.Y;
|
||
|
CopyInfo.Size.X = OutputExtents.X;
|
||
|
CopyInfo.Size.Y = OutputExtents.Y;
|
||
|
FTextureRHIRef InterpolatedFrame = PassParameters->InterpolatedRT->GetRHI();
|
||
|
TransitionAndCopyTexture(RHICmdList, InterpolatedFrame, PassParameters->Interpolated->GetRHI(), CopyInfo);
|
||
|
if (Presenter->GetMode() == EFFXFrameInterpolationPresentModeRHI)
|
||
|
{
|
||
|
check(PassParameters->Interpolated->Desc.Extent == FIntPoint(BackBuffer->GetSizeXYZ().X, BackBuffer->GetSizeXYZ().Y));
|
||
|
TransitionAndCopyTexture(RHICmdList, InterpolatedFrame, BackBuffer, CopyInfo);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FTextureRHIRef InterpolatedFrame = PassParameters->InterpolatedRT->GetRHI();
|
||
|
check(FIntPoint(InterpolatedFrame->GetSizeXYZ().X, InterpolatedFrame->GetSizeXYZ().Y) == FIntPoint(BackBuffer->GetSizeXYZ().X, BackBuffer->GetSizeXYZ().Y));
|
||
|
TransitionAndCopyTexture(RHICmdList, InterpolatedFrame, BackBuffer, {});
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (bInterpolated)
|
||
|
{
|
||
|
if (Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative)
|
||
|
{
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_BEGIN(TEXT("FFXFrameInterpolation-Native"), GraphBuilder, 0.05f);
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_PARAM(TEXT("ColorTexture"), PassParameters->ColorTexture.GetTexture(), GraphBuilder, 0.05f);
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_PARAM(TEXT("BackBufferTexture"), PassParameters->BackBufferTexture.GetTexture(), GraphBuilder, 0.05f);
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_PARAM(TEXT("HudTexture"), PassParameters->HudTexture.GetTexture(), GraphBuilder, 0.05f);
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_PARAM(TEXT("SceneDepth"), PassParameters->SceneDepth.GetTexture(), GraphBuilder, 0.05f);
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_PARAM(TEXT("MotionVectors"), PassParameters->MotionVectors.GetTexture(), GraphBuilder, 0.05f);
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_END(GraphBuilder)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_BEGIN(TEXT("FFXFrameInterpolation-RHI"), GraphBuilder, 0.05f);
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_PARAMS(FFXFrameInterpolationPass::FParameters, PassParameters, GraphBuilder, 0.05f);
|
||
|
FFX_RENDER_TEST_CAPTURE_PASS_END(GraphBuilder)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bInterpolated;
|
||
|
}
|
||
|
|
||
|
#if UE_VERSION_OLDER_THAN(5, 0, 0)
|
||
|
inline static FRDGTextureRef RegisterExternalTexture(FRDGBuilder& GraphBuilder, FRHITexture* Texture, const TCHAR* NameIfUnregistered = nullptr)
|
||
|
{
|
||
|
if (FRDGTextureRef FoundTexture = GraphBuilder.FindExternalTexture(Texture))
|
||
|
{
|
||
|
return FoundTexture;
|
||
|
}
|
||
|
|
||
|
return GraphBuilder.RegisterExternalTexture(CreateRenderTarget(Texture, NameIfUnregistered));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void FFXFrameInterpolation::InterpolateFrame(FRDGBuilder& InGraphBuilder)
|
||
|
{
|
||
|
auto* Engine = GEngine;
|
||
|
auto GameViewport = Engine ? Engine->GameViewport : nullptr;
|
||
|
auto Viewport = GameViewport ? GameViewport->Viewport : nullptr;
|
||
|
auto ViewportRHI = Viewport ? Viewport->GetViewportRHI() : nullptr;
|
||
|
FIntPoint ViewportSizeXY = Viewport ? Viewport->GetSizeXY() : FIntPoint::ZeroValue;
|
||
|
FFXFrameInterpolationCustomPresent* Presenter = ViewportRHI.IsValid() ? (FFXFrameInterpolationCustomPresent*)ViewportRHI->GetCustomPresent() : nullptr;
|
||
|
bool bAllowed = CVarEnableFFXFI.GetValueOnAnyThread() != 0 && Presenter && (Views.Num() > 0);
|
||
|
#if WITH_EDITORONLY_DATA
|
||
|
bAllowed &= !GIsEditor;
|
||
|
#endif
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
FRDGBuilder& GraphBuilder = InGraphBuilder;
|
||
|
#else
|
||
|
if (IFFXSharedBackend::GetGraphBuilder() == nullptr)
|
||
|
{
|
||
|
bAllowed = false;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (bAllowed)
|
||
|
{
|
||
|
FTextureRHIRef BackBuffer = RHIGetViewportBackBuffer(ViewportRHI);
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
FRDGTextureRef BackBufferRDG = RegisterExternalTexture(GraphBuilder, BackBuffer, nullptr);
|
||
|
#else
|
||
|
FRDGBuilder& GraphBuilder = *IFFXSharedBackend::GetGraphBuilder();
|
||
|
FRDGTextureRef BackBufferRDG = RegisterExternalTexture(GraphBuilder, BackBuffer);
|
||
|
#endif
|
||
|
|
||
|
#if UE_VERSION_AT_LEAST(5, 0, 0)
|
||
|
ETextureCreateFlags DescFlags = TexCreate_UAV;
|
||
|
#else
|
||
|
ETextureCreateFlags DescFlags = TexCreate_None;
|
||
|
#endif
|
||
|
|
||
|
if (!IsValidRef(BackBufferRT) || BackBufferRT->GetDesc().Extent.X != BackBufferRDG->Desc.Extent.X || BackBufferRT->GetDesc().Extent.Y != BackBufferRDG->Desc.Extent.Y || BackBufferRT->GetDesc().Format != BackBufferRDG->Desc.Format)
|
||
|
{
|
||
|
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(BackBufferRDG->Desc.Extent.X, BackBufferRDG->Desc.Extent.Y),
|
||
|
BackBuffer->GetFormat(),
|
||
|
FClearValueBinding::Transparent,
|
||
|
DescFlags,
|
||
|
TexCreate_UAV | TexCreate_ShaderResource,
|
||
|
false,
|
||
|
1,
|
||
|
true,
|
||
|
true));
|
||
|
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, Desc, BackBufferRT, TEXT("BackBufferRT"));
|
||
|
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, Desc, InterpolatedRT, TEXT("InterpolatedRT"));
|
||
|
}
|
||
|
|
||
|
if ((Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative) && (!IsValidRef(AsyncBufferRT[0]) || AsyncBufferRT[0]->GetDesc().Extent.X != BackBufferRDG->Desc.Extent.X || AsyncBufferRT[0]->GetDesc().Extent.Y != BackBufferRDG->Desc.Extent.Y || AsyncBufferRT[0]->GetDesc().Format != BackBufferRDG->Desc.Format))
|
||
|
{
|
||
|
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(BackBufferRDG->Desc.Extent.X, BackBufferRDG->Desc.Extent.Y),
|
||
|
BackBuffer->GetFormat(),
|
||
|
FClearValueBinding::Transparent,
|
||
|
DescFlags,
|
||
|
TexCreate_UAV | TexCreate_ShaderResource,
|
||
|
false,
|
||
|
1,
|
||
|
true,
|
||
|
true));
|
||
|
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, Desc, AsyncBufferRT[0], TEXT("AsyncBufferRT0"));
|
||
|
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, Desc, AsyncBufferRT[1], TEXT("AsyncBufferRT1"));
|
||
|
}
|
||
|
|
||
|
Presenter->BeginFrame();
|
||
|
Presenter->SetPreUITextures(BackBufferRT, InterpolatedRT);
|
||
|
Presenter->SetEnabled(true);
|
||
|
|
||
|
FRHICopyTextureInfo Info;
|
||
|
FRDGTextureRef FinalBuffer = GraphBuilder.RegisterExternalTexture(BackBufferRT);
|
||
|
FRDGTextureRef AsyncBuffer = nullptr;
|
||
|
FRDGTextureRef InterpolatedRDG = GraphBuilder.RegisterExternalTexture(InterpolatedRT);
|
||
|
check(BackBufferRDG->Desc.Extent == FinalBuffer->Desc.Extent);
|
||
|
AddCopyTexturePass(GraphBuilder, BackBufferRDG, FinalBuffer, Info);
|
||
|
|
||
|
if (Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative)
|
||
|
{
|
||
|
AsyncBuffer = GraphBuilder.RegisterExternalTexture(AsyncBufferRT[Index]);
|
||
|
AddCopyTexturePass(GraphBuilder, BackBufferRDG, AsyncBuffer, Info);
|
||
|
FinalBuffer = AsyncBuffer;
|
||
|
Index = (Index + 1) % 2;
|
||
|
|
||
|
// Reset the state if the present counter falls behind the interpolation, this ensures that textures will get cleared before first use
|
||
|
ResetState = (PresentCount >= InterpolationCount) ? ResetState : 0u;
|
||
|
}
|
||
|
|
||
|
FRDGTextureUAVDesc Interpolatedesc(InterpolatedRDG);
|
||
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Interpolatedesc), FVector::ZeroVector);
|
||
|
|
||
|
bAllowed = false;
|
||
|
uint32 InterpolateIndex = 0;
|
||
|
for (auto Pair : Views)
|
||
|
{
|
||
|
if (Pair.Key->State)
|
||
|
{
|
||
|
if (Pair.Value.bEnabled && Pair.Value.ViewFamilyTexture && (ViewportSizeXY.X == Pair.Value.ViewFamilyTexture->Desc.Extent.X) && (ViewportSizeXY.Y == Pair.Value.ViewFamilyTexture->Desc.Extent.Y))
|
||
|
{
|
||
|
bool const bInterpolated = InterpolateView(GraphBuilder, Presenter, Pair.Key, Pair.Value, FinalBuffer, InterpolatedRDG, BackBufferRDG, InterpolateIndex);
|
||
|
InterpolateIndex = bInterpolated ? InterpolateIndex +1 : InterpolateIndex;
|
||
|
bAllowed |= bInterpolated;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Presenter->EndFrame();
|
||
|
}
|
||
|
|
||
|
#if UE_VERSION_OLDER_THAN(5, 0, 0)
|
||
|
if (IFFXSharedBackend::GetGraphBuilder() == nullptr)
|
||
|
#endif
|
||
|
{
|
||
|
#if UE_VERSION_OLDER_THAN(5, 0, 0)
|
||
|
FRDGBuilder& GraphBuilder = *IFFXSharedBackend::GetGraphBuilder();
|
||
|
#endif
|
||
|
GraphBuilder.AddPass(
|
||
|
RDG_EVENT_NAME("FidelityFX-FrameInterpolation Unset CommandList"),
|
||
|
ERDGPassFlags::None | ERDGPassFlags::NeverCull,
|
||
|
[](FRHICommandListImmediate& RHICmdList)
|
||
|
{
|
||
|
RHICmdList.EnqueueLambda([](FRHICommandListImmediate& cmd)
|
||
|
{
|
||
|
GCommandList = nullptr;
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
Views.Empty();
|
||
|
|
||
|
bInterpolatedFrame |= bAllowed;
|
||
|
if (Presenter && Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative)
|
||
|
{
|
||
|
// If the present count fell behind reset it - otherwise it will persist indefinitely
|
||
|
PresentCount = (PresentCount >= InterpolationCount) ? PresentCount : (InterpolationCount + (bInterpolatedFrame ? 1llu : 0llu));
|
||
|
InterpolationCount += bInterpolatedFrame ? 1llu : 0llu;
|
||
|
ResetState = bInterpolatedFrame ? 2u : 0u;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ResetState = bInterpolatedFrame ? 3u : 0u;
|
||
|
}
|
||
|
|
||
|
IAntiLag2Module* AntiLag2Interface = (IAntiLag2Module*)FModuleManager::Get().GetModule(TEXT("AntiLag2"));
|
||
|
if (AntiLag2Interface)
|
||
|
{
|
||
|
InGraphBuilder.AddPass(
|
||
|
RDG_EVENT_NAME("FidelityFX-FrameInterpolation Set AntLag2 FrameGen Mode"),
|
||
|
ERDGPassFlags::None | ERDGPassFlags::NeverCull,
|
||
|
[AntiLag2Interface](FRHICommandListImmediate& RHICmdList)
|
||
|
{
|
||
|
RHICmdList.EnqueueLambda([AntiLag2Interface](FRHICommandListImmediate& cmd)
|
||
|
{
|
||
|
AntiLag2Interface->MarkEndOfFrameRendering();
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FFXFrameInterpolation::OnSlateWindowRendered(SWindow& SlateWindow, void* ViewportRHIPtr)
|
||
|
{
|
||
|
static bool bProcessing = false;
|
||
|
|
||
|
FViewportRHIRef Viewport = *((FViewportRHIRef*)ViewportRHIPtr);
|
||
|
FFXFrameInterpolationCustomPresent* PresentHandler = (FFXFrameInterpolationCustomPresent*)Viewport->GetCustomPresent();
|
||
|
|
||
|
if (IsInGameThread() && PresentHandler && PresentHandler->Enabled() && CVarEnableFFXFI.GetValueOnAnyThread())
|
||
|
{
|
||
|
if (!bProcessing)
|
||
|
{
|
||
|
bProcessing = true;
|
||
|
FSlateApplication& App = FSlateApplication::Get();
|
||
|
TSharedPtr<SWindow> WindowPtr;
|
||
|
TSharedPtr<SWidget> TestWidget = SlateWindow.AsShared();
|
||
|
while (TestWidget && !WindowPtr.IsValid())
|
||
|
{
|
||
|
if (TestWidget->Advanced_IsWindow())
|
||
|
{
|
||
|
WindowPtr = StaticCastSharedPtr<SWindow>(TestWidget);
|
||
|
}
|
||
|
|
||
|
TestWidget = TestWidget->GetParentWidget();
|
||
|
}
|
||
|
|
||
|
Windows.Add(&SlateWindow, Viewport.GetReference());
|
||
|
|
||
|
bool bDrawDebugView = false;
|
||
|
#if (UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT || UE_BUILD_TEST)
|
||
|
bDrawDebugView = CVarFFXFIShowDebugView.GetValueOnAnyThread() != 0;
|
||
|
#endif
|
||
|
|
||
|
if (PresentHandler->GetMode() == EFFXFrameInterpolationPresentModeRHI && !bDrawDebugView)
|
||
|
{
|
||
|
ENQUEUE_RENDER_COMMAND(UpdateWindowBackBufferCommand)(
|
||
|
[this, Viewport](FRHICommandListImmediate& RHICmdList)
|
||
|
{
|
||
|
#if UE_VERSION_AT_LEAST(5, 2, 0)
|
||
|
FTextureRHIRef BackBuffer = RHIGetViewportBackBuffer(Viewport);
|
||
|
#else
|
||
|
FTextureRHIRef BackBuffer = RHICmdList.GetViewportBackBuffer(Viewport);
|
||
|
#endif
|
||
|
FFXFrameInterpolationCustomPresent* Presenter = (FFXFrameInterpolationCustomPresent*)Viewport->GetCustomPresent();
|
||
|
if (Presenter && BackBufferRT.IsValid())
|
||
|
{
|
||
|
this->CalculateFPSTimings();
|
||
|
FTextureRHIRef InterpolatedFrame = BackBufferRT->GetRHI(GET_RHI_TARGET_ARG);
|
||
|
{
|
||
|
#if UE_VERSION_AT_LEAST(5, 5, 0)
|
||
|
SCOPED_DRAW_EVENT(RHICmdList, FFXFrameInterpolation::OnSlateWindowRendered)
|
||
|
#else
|
||
|
RHICmdList.PushEvent(TEXT("FFXFrameInterpolation::OnSlateWindowRendered"), FColor::White);
|
||
|
#endif
|
||
|
check(FIntPoint(InterpolatedFrame->GetSizeXYZ().X, InterpolatedFrame->GetSizeXYZ().Y) == FIntPoint(BackBuffer->GetSizeXYZ().X, BackBuffer->GetSizeXYZ().Y));
|
||
|
TransitionAndCopyTexture(RHICmdList, InterpolatedFrame, BackBuffer, {});
|
||
|
#if UE_VERSION_OLDER_THAN(5, 5, 0)
|
||
|
RHICmdList.PopEvent();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
Presenter->SetCustomPresentStatus(FFXFrameInterpolationCustomPresentStatus::PresentRT);
|
||
|
RHICmdList.EnqueueLambda([this, Presenter](FRHICommandListImmediate& cmd) mutable
|
||
|
{
|
||
|
Presenter->SetCustomPresentStatus(FFXFrameInterpolationCustomPresentStatus::PresentRHI);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
double OldLastTickTime = 0.0;
|
||
|
bool const bModifySlateDeltaTime = (CVarFFXFIModifySlateDeltaTime.GetValueOnAnyThread() != 0);
|
||
|
if (bModifySlateDeltaTime)
|
||
|
{
|
||
|
OldLastTickTime = ((FFXFISlateApplication&)App).LastTickTime;
|
||
|
((FFXFISlateApplication&)App).LastTickTime = (((FFXFISlateApplication&)App).CurrentTime);
|
||
|
}
|
||
|
|
||
|
// If we hold on to this and the viewport resizes during redrawing then bad things will happen.
|
||
|
Viewport.SafeRelease();
|
||
|
|
||
|
App.ForceRedrawWindow(WindowPtr.ToSharedRef());
|
||
|
|
||
|
if (bModifySlateDeltaTime)
|
||
|
{
|
||
|
((FFXFISlateApplication&)App).LastTickTime = OldLastTickTime;
|
||
|
}
|
||
|
}
|
||
|
bProcessing = false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ENQUEUE_RENDER_COMMAND(UpdateWindowBackBufferCommand)(
|
||
|
[Viewport](FRHICommandListImmediate& RHICmdList)
|
||
|
{
|
||
|
FFXFrameInterpolationCustomPresent* Presenter = (FFXFrameInterpolationCustomPresent*)Viewport->GetCustomPresent();
|
||
|
if (Presenter)
|
||
|
{
|
||
|
Presenter->SetCustomPresentStatus(FFXFrameInterpolationCustomPresentStatus::PresentRT);
|
||
|
RHICmdList.EnqueueLambda([Presenter](FRHICommandListImmediate& cmd) mutable
|
||
|
{
|
||
|
Presenter->SetCustomPresentStatus(FFXFrameInterpolationCustomPresentStatus::PresentRHI);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FFXFrameInterpolation::OnBackBufferReadyToPresentCallback(class SWindow& SlateWindow, const FTextureRHIRef& BackBuffer)
|
||
|
{
|
||
|
/** Callback for when a backbuffer is ready for reading (called on render thread) */
|
||
|
FRHIViewport** ViewportPtr = Windows.Find(&SlateWindow);
|
||
|
FViewportRHIRef Viewport = ViewportPtr ? *ViewportPtr : nullptr;
|
||
|
FFXFrameInterpolationCustomPresent* Presenter = Viewport ? (FFXFrameInterpolationCustomPresent*)Viewport->GetCustomPresent() : nullptr;
|
||
|
|
||
|
ResetState = (ResetState > 0) ? ResetState - 1 : ResetState;
|
||
|
|
||
|
if (Presenter && CVarEnableFFXFI.GetValueOnAnyThread())
|
||
|
{
|
||
|
PresentCount += (Presenter->GetMode() == EFFXFrameInterpolationPresentModeNative) ? 1llu : 0llu;
|
||
|
if (ResetState)
|
||
|
{
|
||
|
Presenter->CopyBackBufferRT(BackBuffer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bInterpolatedFrame = false;
|
||
|
}
|