// 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 "FFXFrameInterpolationSlate.h" #include "RenderingThread.h" //------------------------------------------------------------------------------------------------------ // Helper definitions. //------------------------------------------------------------------------------------------------------ struct FSlateReleaseDrawBufferCommandString { static const TCHAR* TStr() { return TEXT("FSlateReleaseDrawBufferCommand"); } }; struct FFISlateReleaseDrawBufferCommand final : public FRHICommand < FFISlateReleaseDrawBufferCommand, FSlateReleaseDrawBufferCommandString > { FSlateDrawBuffer* DrawBuffer; FFISlateReleaseDrawBufferCommand(FSlateDrawBuffer* InDrawBuffer) : DrawBuffer(InDrawBuffer) {} void Execute(FRHICommandListBase& CmdList) { DrawBuffer->Unlock(); } static void ReleaseDrawBuffer(FRHICommandListImmediate& RHICmdList, FSlateDrawBuffer* InDrawBuffer) { if (!RHICmdList.Bypass()) { ALLOC_COMMAND_CL(RHICmdList, FFISlateReleaseDrawBufferCommand)(InDrawBuffer); } else { FFISlateReleaseDrawBufferCommand Cmd(InDrawBuffer); Cmd.Execute(RHICmdList); } } }; //------------------------------------------------------------------------------------------------------ // Implementation for the SlateRenderer override that allows for more draw buffers. //------------------------------------------------------------------------------------------------------ FFXFrameInterpolationSlateRenderer::FFXFrameInterpolationSlateRenderer(TSharedRef InUnderlyingRenderer) : FSlateRenderer(InUnderlyingRenderer->GetFontServices()) , UnderlyingRenderer(InUnderlyingRenderer) , FreeBufferIndex(0) , ResourceVersion(0) { InUnderlyingRenderer->OnSlateWindowRendered().AddRaw(this, &FFXFrameInterpolationSlateRenderer::OnSlateWindowRenderedThunk); InUnderlyingRenderer->OnSlateWindowDestroyed().AddRaw(this, &FFXFrameInterpolationSlateRenderer::OnSlateWindowDestroyedThunk); InUnderlyingRenderer->OnPreResizeWindowBackBuffer().AddRaw(this, &FFXFrameInterpolationSlateRenderer::OnPreResizeWindowBackBufferThunk); InUnderlyingRenderer->OnPostResizeWindowBackBuffer().AddRaw(this, &FFXFrameInterpolationSlateRenderer::OnPostResizeWindowBackBufferThunk); InUnderlyingRenderer->OnBackBufferReadyToPresent().AddRaw(this, &FFXFrameInterpolationSlateRenderer::OnBackBufferReadyToPresentThunk); } FFXFrameInterpolationSlateRenderer::~FFXFrameInterpolationSlateRenderer() { } #if UE_VERSION_AT_LEAST(5, 1, 0) /** Returns a draw buffer that can be used by Slate windows to draw window elements */ FSlateDrawBuffer& FFXFrameInterpolationSlateRenderer::AcquireDrawBuffer() { FreeBufferIndex = (FreeBufferIndex + 1) % NumDrawBuffers; FSlateDrawBuffer* Buffer = &DrawBuffers[FreeBufferIndex]; while (!Buffer->Lock()) { // If the buffer cannot be locked then the buffer is still in use. If we are here all buffers are in use // so wait until one is free. if (IsInSlateThread()) { // We can't flush commands on the slate thread, so simply spinlock until we're done // this happens if the render thread becomes completely blocked by expensive tasks when the Slate thread is running // in this case we cannot tick Slate. FPlatformProcess::Sleep(0.001f); } else { FlushCommands(); UE_LOG(LogSlate, Warning, TEXT("Slate: Had to block on waiting for a draw buffer")); FreeBufferIndex = (FreeBufferIndex + 1) % NumDrawBuffers; } Buffer = &DrawBuffers[FreeBufferIndex]; } // Safely remove brushes by emptying the array and releasing references DynamicBrushesToRemove[FreeBufferIndex].Empty(); Buffer->ClearBuffer(); Buffer->UpdateResourceVersion(ResourceVersion); return *Buffer; } void FFXFrameInterpolationSlateRenderer::ReleaseDrawBuffer(FSlateDrawBuffer& InWindowDrawBuffer) { #if DO_CHECK bool bFound = false; for (int32 Index = 0; Index < NumDrawBuffers; ++Index) { if (&DrawBuffers[Index] == &InWindowDrawBuffer) { bFound = true; break; } } ensureMsgf(bFound, TEXT("It release a DrawBuffer that is not a member of the SlateRHIRenderer")); #endif FSlateDrawBuffer* DrawBuffer = &InWindowDrawBuffer; ENQUEUE_RENDER_COMMAND(SlateReleaseDrawBufferCommand)( [DrawBuffer](FRHICommandListImmediate& RHICmdList) { FFISlateReleaseDrawBufferCommand::ReleaseDrawBuffer(RHICmdList, DrawBuffer); } ); } #else /** Returns a draw buffer that can be used by Slate windows to draw window elements */ FSlateDrawBuffer& FFXFrameInterpolationSlateRenderer::GetDrawBuffer() { FreeBufferIndex = (FreeBufferIndex + 1) % NumDrawBuffers; FSlateDrawBuffer* Buffer = &DrawBuffers[FreeBufferIndex]; while (!Buffer->Lock()) { // If the buffer cannot be locked then the buffer is still in use. If we are here all buffers are in use // so wait until one is free. if (IsInSlateThread()) { // We can't flush commands on the slate thread, so simply spinlock until we're done // this happens if the render thread becomes completely blocked by expensive tasks when the Slate thread is running // in this case we cannot tick Slate. FPlatformProcess::Sleep(0.001f); } else { FlushCommands(); UE_LOG(LogSlate, Warning, TEXT("Slate: Had to block on waiting for a draw buffer")); FreeBufferIndex = (FreeBufferIndex + 1) % NumDrawBuffers; } Buffer = &DrawBuffers[FreeBufferIndex]; } // Safely remove brushes by emptying the array and releasing references DynamicBrushesToRemove[FreeBufferIndex].Empty(); Buffer->ClearBuffer(); Buffer->UpdateResourceVersion(ResourceVersion); return *Buffer; } #endif bool FFXFrameInterpolationSlateRenderer::Initialize() { // Already initialized return true; } void FFXFrameInterpolationSlateRenderer::Destroy() { UnderlyingRenderer->Destroy(); } void FFXFrameInterpolationSlateRenderer::CreateViewport(const TSharedRef InWindow) { UnderlyingRenderer->CreateViewport(InWindow); } void FFXFrameInterpolationSlateRenderer::RequestResize(const TSharedPtr& InWindow, uint32 NewSizeX, uint32 NewSizeY) { UnderlyingRenderer->RequestResize(InWindow, NewSizeX, NewSizeY); } void FFXFrameInterpolationSlateRenderer::UpdateFullscreenState(const TSharedRef InWindow, uint32 OverrideResX, uint32 OverrideResY) { UnderlyingRenderer->UpdateFullscreenState(InWindow, OverrideResX, OverrideResY); } void FFXFrameInterpolationSlateRenderer::SetSystemResolution(uint32 Width, uint32 Height) { UnderlyingRenderer->SetSystemResolution(Width, Height); } void FFXFrameInterpolationSlateRenderer::RestoreSystemResolution(const TSharedRef InWindow) { UnderlyingRenderer->RestoreSystemResolution(InWindow); } void FFXFrameInterpolationSlateRenderer::DrawWindows(FSlateDrawBuffer& InWindowDrawBuffer) { UnderlyingRenderer->DrawWindows(InWindowDrawBuffer); } void FFXFrameInterpolationSlateRenderer::SetColorVisionDeficiencyType(EColorVisionDeficiency Type, int32 Severity, bool bCorrectDeficiency, bool bShowCorrectionWithDeficiency) { UnderlyingRenderer->SetColorVisionDeficiencyType(Type, Severity, bCorrectDeficiency, bShowCorrectionWithDeficiency); } FIntPoint FFXFrameInterpolationSlateRenderer::GenerateDynamicImageResource(const FName InTextureName) { return UnderlyingRenderer->GenerateDynamicImageResource(InTextureName); } bool FFXFrameInterpolationSlateRenderer::GenerateDynamicImageResource(FName ResourceName, uint32 Width, uint32 Height, const TArray< uint8 >& Bytes) { return UnderlyingRenderer->GenerateDynamicImageResource(ResourceName, Width, Height, Bytes); } bool FFXFrameInterpolationSlateRenderer::GenerateDynamicImageResource(FName ResourceName, FSlateTextureDataRef TextureData) { return UnderlyingRenderer->GenerateDynamicImageResource(ResourceName, TextureData); } #if UE_VERSION_AT_LEAST(5, 0, 0) #if UE_VERSION_AT_LEAST(5, 1, 0) FSlateResourceHandle FFXFrameInterpolationSlateRenderer::GetResourceHandle(const FSlateBrush& Brush, FVector2f LocalSize, float DrawScale) #else FSlateResourceHandle FFXFrameInterpolationSlateRenderer::GetResourceHandle(const FSlateBrush& Brush, FVector2D LocalSize, float DrawScale) #endif { return UnderlyingRenderer->GetResourceHandle(Brush, LocalSize, DrawScale); } #endif FSlateResourceHandle FFXFrameInterpolationSlateRenderer::GetResourceHandle(const FSlateBrush& Brush) { return UnderlyingRenderer->GetResourceHandle(Brush); } bool FFXFrameInterpolationSlateRenderer::CanRenderResource(UObject& InResourceObject) const { return UnderlyingRenderer->CanRenderResource(InResourceObject); } void FFXFrameInterpolationSlateRenderer::RemoveDynamicBrushResource(TSharedPtr BrushToRemove) { if (BrushToRemove.IsValid()) { DynamicBrushesToRemove[FreeBufferIndex].Add(BrushToRemove); } } void FFXFrameInterpolationSlateRenderer::ReleaseDynamicResource(const FSlateBrush& Brush) { return UnderlyingRenderer->ReleaseDynamicResource(Brush); } void FFXFrameInterpolationSlateRenderer::OnWindowDestroyed(const TSharedRef& InWindow) { return UnderlyingRenderer->OnWindowDestroyed(InWindow); } void FFXFrameInterpolationSlateRenderer::OnWindowFinishReshaped(const TSharedPtr& InWindow) { return UnderlyingRenderer->OnWindowFinishReshaped(InWindow); } void* FFXFrameInterpolationSlateRenderer::GetViewportResource(const SWindow& Window) { return UnderlyingRenderer->GetViewportResource(Window); } void FFXFrameInterpolationSlateRenderer::FlushCommands() const { return UnderlyingRenderer->FlushCommands(); } void FFXFrameInterpolationSlateRenderer::Sync() const { return UnderlyingRenderer->Sync(); } void FFXFrameInterpolationSlateRenderer::BeginFrame() const { return UnderlyingRenderer->BeginFrame(); } void FFXFrameInterpolationSlateRenderer::EndFrame() const { return UnderlyingRenderer->EndFrame(); } void FFXFrameInterpolationSlateRenderer::ReloadTextureResources() { return UnderlyingRenderer->ReloadTextureResources(); } void FFXFrameInterpolationSlateRenderer::LoadStyleResources(const ISlateStyle& Style) { return UnderlyingRenderer->LoadStyleResources(Style); } bool FFXFrameInterpolationSlateRenderer::AreShadersInitialized() const { return UnderlyingRenderer->AreShadersInitialized(); } void FFXFrameInterpolationSlateRenderer::InvalidateAllViewports() { return UnderlyingRenderer->InvalidateAllViewports(); } void FFXFrameInterpolationSlateRenderer::ReleaseAccessedResources(bool bImmediatelyFlush) { UnderlyingRenderer->ReleaseAccessedResources(bImmediatelyFlush); if (bImmediatelyFlush) { // Increment resource version to allow buffers to shrink or cached structures to clean up. ResourceVersion++; } } void FFXFrameInterpolationSlateRenderer::PrepareToTakeScreenshot(const FIntRect& Rect, TArray* OutColorData, SWindow* InScreenshotWindow) { return UnderlyingRenderer->PrepareToTakeScreenshot(Rect, OutColorData, InScreenshotWindow); } #if UE_VERSION_OLDER_THAN(5, 5, 0) void FFXFrameInterpolationSlateRenderer::SetWindowRenderTarget(const SWindow& Window, class IViewportRenderTargetProvider* Provider) { return UnderlyingRenderer->SetWindowRenderTarget(Window, Provider); } #endif FSlateUpdatableTexture* FFXFrameInterpolationSlateRenderer::CreateUpdatableTexture(uint32 Width, uint32 Height) { return UnderlyingRenderer->CreateUpdatableTexture(Width, Height); } #if UE_VERSION_AT_LEAST(5, 0, 0) FSlateUpdatableTexture* FFXFrameInterpolationSlateRenderer::CreateSharedHandleTexture(void* SharedHandle) { return UnderlyingRenderer->CreateSharedHandleTexture(SharedHandle); } #endif void FFXFrameInterpolationSlateRenderer::ReleaseUpdatableTexture(FSlateUpdatableTexture* Texture) { return UnderlyingRenderer->ReleaseUpdatableTexture(Texture); } ISlateAtlasProvider* FFXFrameInterpolationSlateRenderer::GetTextureAtlasProvider() { return UnderlyingRenderer->GetTextureAtlasProvider(); } ISlateAtlasProvider* FFXFrameInterpolationSlateRenderer::GetFontAtlasProvider() { return UnderlyingRenderer->GetFontAtlasProvider(); } void FFXFrameInterpolationSlateRenderer::CopyWindowsToVirtualScreenBuffer(const TArray& KeypressBuffer) { return UnderlyingRenderer->CopyWindowsToVirtualScreenBuffer(KeypressBuffer); } void FFXFrameInterpolationSlateRenderer::MapVirtualScreenBuffer(FMappedTextureBuffer* OutImageData) { return UnderlyingRenderer->MapVirtualScreenBuffer(OutImageData); } void FFXFrameInterpolationSlateRenderer::UnmapVirtualScreenBuffer() { return UnderlyingRenderer->UnmapVirtualScreenBuffer(); } FCriticalSection* FFXFrameInterpolationSlateRenderer::GetResourceCriticalSection() { return UnderlyingRenderer->GetResourceCriticalSection(); } int32 FFXFrameInterpolationSlateRenderer::RegisterCurrentScene(FSceneInterface* Scene) { return UnderlyingRenderer->RegisterCurrentScene(Scene); } int32 FFXFrameInterpolationSlateRenderer::GetCurrentSceneIndex() const { return UnderlyingRenderer->GetCurrentSceneIndex(); } void FFXFrameInterpolationSlateRenderer::ClearScenes() { return UnderlyingRenderer->ClearScenes(); } void FFXFrameInterpolationSlateRenderer::DestroyCachedFastPathRenderingData(struct FSlateCachedFastPathRenderingData* VertexData) { return UnderlyingRenderer->DestroyCachedFastPathRenderingData(VertexData); } void FFXFrameInterpolationSlateRenderer::DestroyCachedFastPathElementData(struct FSlateCachedElementData* ElementData) { return UnderlyingRenderer->DestroyCachedFastPathElementData(ElementData); } bool FFXFrameInterpolationSlateRenderer::HasLostDevice() const { return UnderlyingRenderer->HasLostDevice(); } void FFXFrameInterpolationSlateRenderer::AddWidgetRendererUpdate(const struct FRenderThreadUpdateContext& Context, bool bDeferredRenderTargetUpdate) { return UnderlyingRenderer->AddWidgetRendererUpdate(Context, bDeferredRenderTargetUpdate); } EPixelFormat FFXFrameInterpolationSlateRenderer::GetSlateRecommendedColorFormat() { return UnderlyingRenderer->GetSlateRecommendedColorFormat(); } #if UE_VERSION_AT_LEAST(5, 5, 0) void FFXFrameInterpolationSlateRenderer::SetCurrentSceneIndex(int32 index) { return UnderlyingRenderer->SetCurrentSceneIndex(index); } #endif