You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
This does not truly change HDR since these platforms do not support toggling this at runtime. Instead we change the swapchain to be in SDR, and we end up in SDR mode with HDR output mode. SDR in HDR mode is slightly darker on the TV, more than when comparing the screenshots of the swapchain. This is most likely due to the TV response, which needs to be investigated later on to see if we can somehow match the 2 properly. In addition to the 2 CLs from main, this CL allows BP to change the CVar r.HDR.EnableHDROutput . On these platforms, the CVar is set with ECVF_SetByCode as soon as HDR was supported and calling EnableHDRDisplayOutput with ECVF_SetByGameSetting would get ignored #jira UE-148923 #preflight 628fbfbd4f63120d8ee7af3d Original CLs: 19334984 fix for r.setres on d3d12: the backbuffers were still referenced by the present queue, preventing their proper destruction 19609874 support for HDR change on process resume #ROBOMERGE-OWNER: benjamin.rouveyrol #ROBOMERGE-AUTHOR: benjamin.rouveyrol #ROBOMERGE-SOURCE: CL 20439865 in //UE5/Release-5.0/... via CL 20440669 #ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v949-20362246) [CL 20449414 by benjamin rouveyrol in ue5-main branch]
2008 lines
69 KiB
C++
2008 lines
69 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SlateRHIRenderer.h"
|
|
#include "Fonts/FontCache.h"
|
|
#include "SlateRHIRenderingPolicy.h"
|
|
#include "Misc/ScopeLock.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "Styling/CoreStyle.h"
|
|
#include "Widgets/SWindow.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "EngineGlobals.h"
|
|
#include "RendererInterface.h"
|
|
#include "StaticBoundShaderState.h"
|
|
#include "SceneUtils.h"
|
|
#include "RHIStaticStates.h"
|
|
#include "UnrealEngine.h"
|
|
#include "GlobalShader.h"
|
|
#include "ScreenRendering.h"
|
|
#include "SlateShaders.h"
|
|
#include "Rendering/ElementBatcher.h"
|
|
#include "StereoRendering.h"
|
|
#include "SlateNativeTextureResource.h"
|
|
#include "SceneUtils.h"
|
|
#include "Runtime/Renderer/Public/VolumeRendering.h"
|
|
#include "ShaderCompiler.h"
|
|
#include "PipelineStateCache.h"
|
|
#include "EngineModule.h"
|
|
#include "Interfaces/ISlate3DRenderer.h"
|
|
#include "Slate/SlateTextureAtlasInterface.h"
|
|
#include "Types/ReflectionMetadata.h"
|
|
#include "CommonRenderResources.h"
|
|
#include "RenderTargetPool.h"
|
|
#include "RendererUtils.h"
|
|
#include "HAL/LowLevelMemTracker.h"
|
|
#include "Rendering/RenderingCommon.h"
|
|
#include "IHeadMountedDisplayModule.h"
|
|
#include "HDRHelper.h"
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("Slate RT: Rendering"), STAT_SlateRenderingRTTime, STATGROUP_Slate);
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("Slate RT: Draw Batches"), STAT_SlateRTDrawBatches, STATGROUP_Slate);
|
|
|
|
DECLARE_GPU_DRAWCALL_STAT_NAMED(SlateUI, TEXT("Slate UI"));
|
|
|
|
// Defines the maximum size that a slate viewport will create
|
|
#define MIN_VIEWPORT_SIZE 8
|
|
#define MAX_VIEWPORT_SIZE 16384
|
|
|
|
static TAutoConsoleVariable<float> CVarUILevel(
|
|
TEXT("r.HDR.UI.Level"),
|
|
1.0f,
|
|
TEXT("Luminance level for UI elements when compositing into HDR framebuffer (default: 1.0)."),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<int32> CVarUICompositeMode(
|
|
TEXT("r.HDR.UI.CompositeMode"),
|
|
1,
|
|
TEXT("Mode used when compositing the UI layer:\n")
|
|
TEXT("0: Standard compositing\n")
|
|
TEXT("1: Shader pass to improve HDR blending\n"),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<float> CVarDrawToVRRenderTarget(
|
|
TEXT("Slate.DrawToVRRenderTarget"),
|
|
1,
|
|
TEXT("If enabled while in VR. Slate UI will be drawn into the render target texture where the VR imagery for either eye was rendered, allow the viewer of the HMD to see the UI (for better or worse.) This render target will then be cropped/scaled into the back buffer, if mirroring is enabled. When disabled, Slate UI will be drawn on top of the backbuffer (not to the HMD) after the mirror texture has been cropped/scaled into the backbuffer."),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<int32> CVarMemorylessDepthStencil(
|
|
TEXT("Slate.MemorylessDepthStencil"),
|
|
0,
|
|
TEXT("Whether to use memoryless DepthStencil target for Slate. Reduces memory usage and implies that DepthStencil state can't be preserved between Slate renderpasses"),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
|
|
TAutoConsoleVariable<int32> CVarShowSlateOverdraw(
|
|
TEXT("Slate.ShowOverdraw"),
|
|
0,
|
|
TEXT("0: Don't show overdraw, 1: Show Overdraw"),
|
|
ECVF_Default
|
|
);
|
|
|
|
TAutoConsoleVariable<int32> CVarShowSlateBatching(
|
|
TEXT("Slate.ShowBatching"),
|
|
0,
|
|
TEXT("0: Don't show batching, 1: Show Batching"),
|
|
ECVF_Default
|
|
);
|
|
#endif
|
|
|
|
struct FSlateDrawWindowCommandParams
|
|
{
|
|
FSlateRHIRenderer* Renderer;
|
|
FSlateWindowElementList* WindowElementList;
|
|
SWindow* Window;
|
|
#if WANTS_DRAW_MESH_EVENTS
|
|
FString WindowTitle;
|
|
#endif
|
|
FGameTime Time;
|
|
bool bLockToVsync;
|
|
bool bClear;
|
|
};
|
|
|
|
void FViewportInfo::InitRHI()
|
|
{
|
|
// Viewport RHI is created on the game thread
|
|
// Create the depth-stencil surface if needed.
|
|
RecreateDepthBuffer_RenderThread();
|
|
}
|
|
|
|
void FViewportInfo::ReleaseRHI()
|
|
{
|
|
DepthStencil.SafeRelease();
|
|
ViewportRHI.SafeRelease();
|
|
}
|
|
|
|
void FViewportInfo::ReleaseResource()
|
|
{
|
|
FRenderResource::ReleaseResource();
|
|
UITargetRT.SafeRelease();
|
|
UITargetRTMask.SafeRelease();
|
|
HDRSourceRT.SafeRelease();
|
|
}
|
|
|
|
void FViewportInfo::ConditionallyUpdateDepthBuffer(bool bInRequiresStencilTest, uint32 InWidth, uint32 InHeight)
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
bool bWantsMemorylessDepthStencil = (CVarMemorylessDepthStencil.GetValueOnAnyThread() != 0);
|
|
|
|
bool bDepthStencilStale =
|
|
bInRequiresStencilTest &&
|
|
(!bRequiresStencilTest ||
|
|
(DepthStencil.IsValid() && (DepthStencil->GetSizeX() != InWidth || DepthStencil->GetSizeY() != InHeight || IsMemorylessTexture(DepthStencil) != bWantsMemorylessDepthStencil)));
|
|
|
|
bRequiresStencilTest = bInRequiresStencilTest;
|
|
|
|
// Allocate a stencil buffer if needed and not already allocated
|
|
if (bDepthStencilStale)
|
|
{
|
|
RecreateDepthBuffer_RenderThread();
|
|
}
|
|
}
|
|
|
|
void FViewportInfo::RecreateDepthBuffer_RenderThread()
|
|
{
|
|
check(IsInRenderingThread());
|
|
DepthStencil.SafeRelease();
|
|
if (bRequiresStencilTest)
|
|
{
|
|
ETextureCreateFlags TargetableTextureFlags = TexCreate_DepthStencilTargetable;
|
|
if (CVarMemorylessDepthStencil.GetValueOnAnyThread() != 0)
|
|
{
|
|
// Use Memoryless target, expecting that DepthStencil content is intermediate and can't be preserved between renderpasses
|
|
TargetableTextureFlags|= TexCreate_Memoryless;
|
|
}
|
|
|
|
const FRHITextureCreateDesc Desc =
|
|
FRHITextureCreateDesc::Create2D(TEXT("SlateViewportDepthStencil"))
|
|
.SetExtent(Width, Height)
|
|
.SetFormat(PF_DepthStencil)
|
|
.SetFlags(TargetableTextureFlags | ETextureCreateFlags::ShaderResource)
|
|
.SetInitialState(ERHIAccess::SRVMask)
|
|
.SetClearValue(FClearValueBinding::DepthZero);
|
|
|
|
DepthStencil = RHICreateTexture(Desc);
|
|
check(IsValidRef(DepthStencil));
|
|
}
|
|
}
|
|
|
|
bool IsMemorylessTexture(const FTexture2DRHIRef& Tex)
|
|
{
|
|
if (Tex)
|
|
{
|
|
return EnumHasAnyFlags(Tex->GetFlags(), TexCreate_Memoryless);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
FSlateRHIRenderer::FSlateRHIRenderer(TSharedRef<FSlateFontServices> InSlateFontServices, TSharedRef<FSlateRHIResourceManager> InResourceManager)
|
|
: FSlateRenderer(InSlateFontServices)
|
|
, EnqueuedWindowDrawBuffer(NULL)
|
|
, FreeBufferIndex(0)
|
|
, FastPathRenderingDataCleanupList(nullptr)
|
|
, CurrentSceneIndex(-1)
|
|
, ResourceVersion(0)
|
|
{
|
|
ResourceManager = InResourceManager;
|
|
|
|
ViewMatrix = FMatrix(FPlane(1, 0, 0, 0),
|
|
FPlane(0, 1, 0, 0),
|
|
FPlane(0, 0, 1, 0),
|
|
FPlane(0, 0, 0, 1));
|
|
|
|
bTakingAScreenShot = false;
|
|
OutScreenshotData = NULL;
|
|
ScreenshotViewportInfo = nullptr;
|
|
bIsStandaloneStereoOnlyDevice = IHeadMountedDisplayModule::IsAvailable() && IHeadMountedDisplayModule::Get().IsStandaloneStereoOnlyDevice();
|
|
}
|
|
|
|
FSlateRHIRenderer::~FSlateRHIRenderer()
|
|
{
|
|
}
|
|
|
|
FMatrix FSlateRHIRenderer::CreateProjectionMatrix(uint32 Width, uint32 Height)
|
|
{
|
|
// Create ortho projection matrix
|
|
const float Left = 0;
|
|
const float Right = Left + Width;
|
|
const float Top = 0;
|
|
const float Bottom = Top + Height;
|
|
const float ZNear = -100.0f;
|
|
const float ZFar = 100.0f;
|
|
return AdjustProjectionMatrixForRHI(
|
|
FMatrix(
|
|
FPlane(2.0f / (Right - Left), 0, 0, 0),
|
|
FPlane(0, 2.0f / (Top - Bottom), 0, 0),
|
|
FPlane(0, 0, 1 / (ZNear - ZFar), 0),
|
|
FPlane((Left + Right) / (Left - Right), (Top + Bottom) / (Bottom - Top), ZNear / (ZNear - ZFar), 1)
|
|
)
|
|
);
|
|
}
|
|
|
|
bool FSlateRHIRenderer::Initialize()
|
|
{
|
|
LoadUsedTextures();
|
|
|
|
RenderingPolicy = MakeShareable(new FSlateRHIRenderingPolicy(SlateFontServices.ToSharedRef(), ResourceManager.ToSharedRef()));
|
|
|
|
ElementBatcher = MakeUnique<FSlateElementBatcher>(RenderingPolicy.ToSharedRef());
|
|
|
|
CurrentSceneIndex = -1;
|
|
ActiveScenes.Empty();
|
|
return true;
|
|
}
|
|
|
|
void FSlateRHIRenderer::Destroy()
|
|
{
|
|
RenderingPolicy->ReleaseResources();
|
|
ResourceManager->ReleaseResources();
|
|
SlateFontServices->ReleaseResources();
|
|
|
|
for (TMap< const SWindow*, FViewportInfo*>::TIterator It(WindowToViewportInfo); It; ++It)
|
|
{
|
|
BeginReleaseResource(It.Value());
|
|
}
|
|
|
|
if (FastPathRenderingDataCleanupList)
|
|
{
|
|
FastPathRenderingDataCleanupList->Cleanup();
|
|
FastPathRenderingDataCleanupList = nullptr;
|
|
}
|
|
|
|
FlushRenderingCommands();
|
|
|
|
ElementBatcher.Reset();
|
|
RenderingPolicy.Reset();
|
|
ResourceManager.Reset();
|
|
SlateFontServices.Reset();
|
|
|
|
DeferredUpdateContexts.Empty();
|
|
|
|
for (TMap< const SWindow*, FViewportInfo*>::TIterator It(WindowToViewportInfo); It; ++It)
|
|
{
|
|
FViewportInfo* ViewportInfo = It.Value();
|
|
delete ViewportInfo;
|
|
}
|
|
|
|
WindowToViewportInfo.Empty();
|
|
CurrentSceneIndex = -1;
|
|
ActiveScenes.Empty();
|
|
}
|
|
|
|
/** Returns a draw buffer that can be used by Slate windows to draw window elements */
|
|
FSlateDrawBuffer& FSlateRHIRenderer::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 FSlateRHIRenderer::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)
|
|
{
|
|
FSlateReleaseDrawBufferCommand::ReleaseDrawBuffer(RHICmdList, DrawBuffer);
|
|
}
|
|
);
|
|
}
|
|
|
|
void FSlateRHIRenderer::CreateViewport(const TSharedRef<SWindow> Window)
|
|
{
|
|
FlushRenderingCommands();
|
|
|
|
if (!WindowToViewportInfo.Contains(&Window.Get()))
|
|
{
|
|
const FVector2f WindowSize = UE::Slate::CastToVector2f(Window->GetViewportSize());
|
|
|
|
// Clamp the window size to a reasonable default anything below 8 is a d3d warning and 8 is used anyway.
|
|
// @todo Slate: This is a hack to work around menus being summoned with 0,0 for window size until they are ticked.
|
|
int32 Width = FMath::Max(MIN_VIEWPORT_SIZE, FMath::CeilToInt(WindowSize.X));
|
|
int32 Height = FMath::Max(MIN_VIEWPORT_SIZE, FMath::CeilToInt(WindowSize.Y));
|
|
|
|
// Sanity check dimensions
|
|
if (!ensureMsgf(Width <= MAX_VIEWPORT_SIZE && Height <= MAX_VIEWPORT_SIZE, TEXT("Invalid window with Width=%u and Height=%u"), Width, Height))
|
|
{
|
|
Width = FMath::Clamp(Width, MIN_VIEWPORT_SIZE, MAX_VIEWPORT_SIZE);
|
|
Height = FMath::Clamp(Height, MIN_VIEWPORT_SIZE, MAX_VIEWPORT_SIZE);
|
|
}
|
|
|
|
|
|
FViewportInfo* NewInfo = new FViewportInfo();
|
|
// Create Viewport RHI if it doesn't exist (this must be done on the game thread)
|
|
TSharedRef<FGenericWindow> NativeWindow = Window->GetNativeWindow().ToSharedRef();
|
|
NewInfo->OSWindow = NativeWindow->GetOSWindowHandle();
|
|
NewInfo->Width = Width;
|
|
NewInfo->Height = Height;
|
|
NewInfo->DesiredWidth = Width;
|
|
NewInfo->DesiredHeight = Height;
|
|
NewInfo->ProjectionMatrix = CreateProjectionMatrix( Width, Height );
|
|
// In MobileLDR case backbuffer format should match or be compatible with a SceneColor format in FSceneRenderTargets::GetDesiredMobileSceneColorFormat()
|
|
if (bIsStandaloneStereoOnlyDevice || (GMaxRHIFeatureLevel == ERHIFeatureLevel::ES3_1 && !IsMobileHDR()))
|
|
{
|
|
NewInfo->PixelFormat = GetSlateRecommendedColorFormat();
|
|
}
|
|
#if ALPHA_BLENDED_WINDOWS
|
|
if (Window->GetTransparencySupport() == EWindowTransparency::PerPixel)
|
|
{
|
|
NewInfo->PixelFormat = GetSlateRecommendedColorFormat();
|
|
}
|
|
#endif
|
|
|
|
// SDR format holds the requested format in non HDR mode
|
|
NewInfo->SDRPixelFormat = NewInfo->PixelFormat;
|
|
HDRGetMetaData(NewInfo->HDRDisplayOutputFormat, NewInfo->HDRDisplayColorGamut, NewInfo->bSceneHDREnabled, Window->GetPositionInScreen(), Window->GetPositionInScreen() + Window->GetSizeInScreen(), NewInfo->OSWindow);
|
|
|
|
if (NewInfo->bSceneHDREnabled)
|
|
{
|
|
NewInfo->PixelFormat = GRHIHDRDisplayOutputFormat;
|
|
}
|
|
|
|
// Sanity check dimensions
|
|
checkf(Width <= MAX_VIEWPORT_SIZE && Height <= MAX_VIEWPORT_SIZE, TEXT("Invalid window with Width=%u and Height=%u"), Width, Height);
|
|
|
|
bool bFullscreen = IsViewportFullscreen( *Window );
|
|
NewInfo->ViewportRHI = RHICreateViewport( NewInfo->OSWindow, Width, Height, bFullscreen, NewInfo->PixelFormat );
|
|
NewInfo->bFullscreen = bFullscreen;
|
|
|
|
// Was the window created on a HDR compatible display?
|
|
NewInfo->bHDREnabled = RHIGetColorSpace(NewInfo->ViewportRHI) != EColorSpaceAndEOTF::ERec709_sRGB ;
|
|
Window->SetIsHDR(NewInfo->bHDREnabled);
|
|
|
|
WindowToViewportInfo.Add(&Window.Get(), NewInfo);
|
|
|
|
BeginInitResource(NewInfo);
|
|
}
|
|
}
|
|
|
|
void FSlateRHIRenderer::ConditionalResizeViewport(FViewportInfo* ViewInfo, uint32 Width, uint32 Height, bool bFullscreen, SWindow* Window)
|
|
{
|
|
checkSlow(IsThreadSafeForSlateRendering());
|
|
|
|
// Force update if HDR output state changes
|
|
|
|
bool bHDREnabled = IsHDREnabled();
|
|
EDisplayColorGamut HDRColorGamut = HDRGetDefaultDisplayColorGamut();
|
|
EDisplayOutputFormat HDROutputDevice = HDRGetDefaultDisplayOutputFormat();
|
|
|
|
bool bHDRStale = false;
|
|
if (ViewInfo)
|
|
{
|
|
HDRGetMetaData(HDROutputDevice, HDRColorGamut, bHDREnabled, Window->GetPositionInScreen(), Window->GetPositionInScreen() + Window->GetSizeInScreen(), ViewInfo->OSWindow);
|
|
bHDRStale |= HDROutputDevice != ViewInfo->HDRDisplayOutputFormat;
|
|
bHDRStale |= HDRColorGamut != ViewInfo->HDRDisplayColorGamut;
|
|
bHDRStale |= bHDREnabled != ViewInfo->bSceneHDREnabled;
|
|
}
|
|
|
|
if (IsInGameThread() && !IsInSlateThread() && ViewInfo && (bHDRStale || ViewInfo->Height != Height || ViewInfo->Width != Width || ViewInfo->bFullscreen != bFullscreen || !IsValidRef(ViewInfo->ViewportRHI)))
|
|
{
|
|
// The viewport size we have doesn't match the requested size of the viewport.
|
|
// Resize it now.
|
|
|
|
// Prevent the texture update logic to use the RHI while the viewport is resized.
|
|
// This could happen if a streaming IO request completes and throws a callback.
|
|
SuspendTextureStreamingRenderTasks();
|
|
|
|
// cannot resize the viewport while potentially using it.
|
|
FlushRenderingCommands();
|
|
|
|
// Windows are allowed to be zero sized ( sometimes they are animating to/from zero for example)
|
|
// but viewports cannot be zero sized. Use 8x8 as a reasonably sized viewport in this case.
|
|
uint32 NewWidth = FMath::Max<uint32>(8, Width);
|
|
uint32 NewHeight = FMath::Max<uint32>(8, Height);
|
|
|
|
// Sanity check dimensions
|
|
if (NewWidth > MAX_VIEWPORT_SIZE)
|
|
{
|
|
UE_LOG(LogSlate, Warning, TEXT("Tried to set viewport width size to %d. Clamping size to max allowed size of %d instead."), NewWidth, MAX_VIEWPORT_SIZE);
|
|
NewWidth = MAX_VIEWPORT_SIZE;
|
|
}
|
|
|
|
if (NewHeight > MAX_VIEWPORT_SIZE)
|
|
{
|
|
UE_LOG(LogSlate, Warning, TEXT("Tried to set viewport height size to %d. Clamping size to max allowed size of %d instead."), NewHeight, MAX_VIEWPORT_SIZE);
|
|
NewHeight = MAX_VIEWPORT_SIZE;
|
|
}
|
|
|
|
ViewInfo->Width = NewWidth;
|
|
ViewInfo->Height = NewHeight;
|
|
ViewInfo->DesiredWidth = NewWidth;
|
|
ViewInfo->DesiredHeight = NewHeight;
|
|
ViewInfo->ProjectionMatrix = CreateProjectionMatrix(NewWidth, NewHeight);
|
|
ViewInfo->bFullscreen = bFullscreen;
|
|
|
|
ViewInfo->PixelFormat = bHDREnabled ? GRHIHDRDisplayOutputFormat : ViewInfo->SDRPixelFormat;
|
|
ViewInfo->HDRDisplayColorGamut = HDRColorGamut;
|
|
ViewInfo->HDRDisplayOutputFormat = HDROutputDevice;
|
|
ViewInfo->bSceneHDREnabled = bHDREnabled;
|
|
|
|
PreResizeBackBufferDelegate.Broadcast(&ViewInfo->ViewportRHI);
|
|
if (IsValidRef(ViewInfo->ViewportRHI))
|
|
{
|
|
ensureMsgf(ViewInfo->ViewportRHI->GetRefCount() == 1, TEXT("Viewport backbuffer was not properly released"));
|
|
RHIResizeViewport(ViewInfo->ViewportRHI, NewWidth, NewHeight, bFullscreen, ViewInfo->PixelFormat);
|
|
}
|
|
else
|
|
{
|
|
ViewInfo->ViewportRHI = RHICreateViewport(ViewInfo->OSWindow, NewWidth, NewHeight, bFullscreen, ViewInfo->PixelFormat);
|
|
}
|
|
|
|
PostResizeBackBufferDelegate.Broadcast(&ViewInfo->ViewportRHI);
|
|
|
|
// Reset texture streaming texture updates.
|
|
ResumeTextureStreamingRenderTasks();
|
|
}
|
|
}
|
|
|
|
void FSlateRHIRenderer::UpdateFullscreenState(const TSharedRef<SWindow> Window, uint32 OverrideResX, uint32 OverrideResY)
|
|
{
|
|
FViewportInfo* ViewInfo = WindowToViewportInfo.FindRef(&Window.Get());
|
|
|
|
if (!ViewInfo)
|
|
{
|
|
CreateViewport(Window);
|
|
}
|
|
|
|
ViewInfo = WindowToViewportInfo.FindRef(&Window.Get());
|
|
|
|
if (ViewInfo)
|
|
{
|
|
const bool bFullscreen = IsViewportFullscreen(*Window);
|
|
|
|
uint32 ResX = OverrideResX ? OverrideResX : GSystemResolution.ResX;
|
|
uint32 ResY = OverrideResY ? OverrideResY : GSystemResolution.ResY;
|
|
|
|
bool bIsRenderingStereo = GEngine && GEngine->XRSystem.IsValid() && GEngine->StereoRenderingDevice.IsValid() && GEngine->StereoRenderingDevice->IsStereoEnabled();
|
|
if ((GIsEditor && Window->IsViewportSizeDrivenByWindow()) || (Window->GetWindowMode() == EWindowMode::WindowedFullscreen) || bIsRenderingStereo)
|
|
{
|
|
ResX = ViewInfo->DesiredWidth;
|
|
ResY = ViewInfo->DesiredHeight;
|
|
}
|
|
|
|
ConditionalResizeViewport(ViewInfo, ResX, ResY, bFullscreen, &Window.Get());
|
|
}
|
|
}
|
|
|
|
void FSlateRHIRenderer::SetSystemResolution(uint32 Width, uint32 Height)
|
|
{
|
|
FSystemResolution::RequestResolutionChange(Width, Height, FPlatformProperties::HasFixedResolution() ? EWindowMode::Fullscreen : GSystemResolution.WindowMode);
|
|
IConsoleManager::Get().CallAllConsoleVariableSinks();
|
|
}
|
|
|
|
void FSlateRHIRenderer::RestoreSystemResolution(const TSharedRef<SWindow> InWindow)
|
|
{
|
|
if (!GIsEditor && InWindow->GetWindowMode() == EWindowMode::Fullscreen)
|
|
{
|
|
// Force the window system to resize the active viewport, even though nothing might have appeared to change.
|
|
// On windows, DXGI might change the window resolution behind our backs when we alt-tab out. This will make
|
|
// sure that we are actually in the resolution we think we are.
|
|
GSystemResolution.ForceRefresh();
|
|
}
|
|
}
|
|
|
|
/** Called when a window is destroyed to give the renderer a chance to free resources */
|
|
void FSlateRHIRenderer::OnWindowDestroyed(const TSharedRef<SWindow>& InWindow)
|
|
{
|
|
checkSlow(IsThreadSafeForSlateRendering());
|
|
|
|
FViewportInfo** ViewportInfoPtr = WindowToViewportInfo.Find(&InWindow.Get());
|
|
if (ViewportInfoPtr)
|
|
{
|
|
OnSlateWindowDestroyedDelegate.Broadcast(&(*ViewportInfoPtr)->ViewportRHI);
|
|
|
|
// Need to flush rendering commands as the viewport may be in use by the render thread
|
|
// and the rendering resources must be released on the render thread before the viewport can be deleted
|
|
FlushRenderingCommands();
|
|
|
|
BeginReleaseResource(*ViewportInfoPtr);
|
|
|
|
// Need to flush rendering commands as the viewport may be in use by the render thread
|
|
// and the rendering resources must be released on the render thread before the viewport can be deleted
|
|
FlushRenderingCommands();
|
|
|
|
delete *ViewportInfoPtr;
|
|
}
|
|
|
|
WindowToViewportInfo.Remove(&InWindow.Get());
|
|
}
|
|
|
|
/** Called when a window is Finished being Reshaped - Currently need to check if its HDR status has changed */
|
|
void FSlateRHIRenderer::OnWindowFinishReshaped(const TSharedPtr<SWindow>& InWindow)
|
|
{
|
|
FViewportInfo* ViewInfo = WindowToViewportInfo.FindRef(InWindow.Get());
|
|
RHICheckViewportHDRStatus(ViewInfo->ViewportRHI);
|
|
}
|
|
|
|
// Limited platform support for HDR UI composition
|
|
bool SupportsUICompositionRendering(const EShaderPlatform Platform)
|
|
{
|
|
return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5) && (RHISupportsGeometryShaders(Platform) || RHISupportsVertexShaderLayer(Platform));
|
|
}
|
|
|
|
// Pixel shader to generate LUT for HDR UI composition
|
|
class FCompositeLUTGenerationPS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FCompositeLUTGenerationPS, Global);
|
|
public:
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return SupportsUICompositionRendering(Parameters.Platform);
|
|
}
|
|
|
|
FCompositeLUTGenerationPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) :
|
|
FGlobalShader(Initializer)
|
|
{
|
|
OutputDevice.Bind(Initializer.ParameterMap, TEXT("OutputDevice"));
|
|
OutputGamut.Bind(Initializer.ParameterMap, TEXT("OutputGamut"));
|
|
}
|
|
FCompositeLUTGenerationPS() {}
|
|
|
|
void SetParameters(FRHICommandList& RHICmdList, EDisplayOutputFormat DisplayOutputFormat, EDisplayColorGamut DisplayColorGamut)
|
|
{
|
|
static const auto CVarOutputGamma = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.TonemapperGamma"));
|
|
|
|
int32 OutputDeviceValue = (int32)DisplayOutputFormat;
|
|
int32 OutputGamutValue = (int32)DisplayColorGamut;
|
|
float Gamma = CVarOutputGamma->GetValueOnRenderThread();
|
|
|
|
if (PLATFORM_APPLE && Gamma == 0.0f)
|
|
{
|
|
Gamma = 2.2f;
|
|
}
|
|
|
|
if (Gamma > 0.0f)
|
|
{
|
|
// Enforce user-controlled ramp over sRGB or Rec709
|
|
OutputDeviceValue = FMath::Max(OutputDeviceValue, (int32)EDisplayOutputFormat::SDR_ExplicitGammaMapping);
|
|
}
|
|
|
|
SetShaderValue(RHICmdList, RHICmdList.GetBoundPixelShader(), OutputDevice, OutputDeviceValue);
|
|
SetShaderValue(RHICmdList, RHICmdList.GetBoundPixelShader(), OutputGamut, OutputGamutValue);
|
|
}
|
|
|
|
static const TCHAR* GetSourceFilename()
|
|
{
|
|
return TEXT("/Engine/Private/CompositeUIPixelShader.usf");
|
|
}
|
|
|
|
static const TCHAR* GetFunctionName()
|
|
{
|
|
return TEXT("Main");
|
|
}
|
|
|
|
private:
|
|
LAYOUT_FIELD(FShaderParameter, OutputDevice);
|
|
LAYOUT_FIELD(FShaderParameter, OutputGamut);
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(, FCompositeLUTGenerationPS, TEXT("/Engine/Private/CompositeUIPixelShader.usf"), TEXT("GenerateLUTPS"), SF_Pixel);
|
|
|
|
// Pixel shader to composite UI over HDR buffer
|
|
template<uint32 EncodingType>
|
|
class FCompositePS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FCompositePS, Global);
|
|
public:
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return SupportsUICompositionRendering(Parameters.Platform);
|
|
}
|
|
|
|
FCompositePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) :
|
|
FGlobalShader(Initializer)
|
|
{
|
|
UITexture.Bind(Initializer.ParameterMap, TEXT("UITexture"));
|
|
UIWriteMaskTexture.Bind(Initializer.ParameterMap, TEXT("UIWriteMaskTexture"));
|
|
UISampler.Bind(Initializer.ParameterMap, TEXT("UISampler"));
|
|
SceneTexture.Bind(Initializer.ParameterMap, TEXT("SceneTexture"));
|
|
SceneSampler.Bind(Initializer.ParameterMap, TEXT("SceneSampler"));
|
|
ColorSpaceLUT.Bind(Initializer.ParameterMap, TEXT("ColorSpaceLUT"));
|
|
ColorSpaceLUTSampler.Bind(Initializer.ParameterMap, TEXT("ColorSpaceLUTSampler"));
|
|
UILevel.Bind(Initializer.ParameterMap, TEXT("UILevel"));
|
|
OutputDevice.Bind(Initializer.ParameterMap, TEXT("OutputDevice"));
|
|
}
|
|
FCompositePS() {}
|
|
|
|
void SetParameters(FRHICommandList& RHICmdList, FRHITexture* UITextureRHI, FRHITexture* UITextureWriteMaskRHI, FRHITexture* SceneTextureRHI, FRHITexture* ColorSpaceLUTRHI)
|
|
{
|
|
static const auto CVarOutputDevice = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HDR.Display.OutputDevice"));
|
|
|
|
SetTextureParameter(RHICmdList, RHICmdList.GetBoundPixelShader(), UITexture, UISampler, TStaticSamplerState<SF_Point>::GetRHI(), UITextureRHI);
|
|
SetTextureParameter(RHICmdList, RHICmdList.GetBoundPixelShader(), SceneTexture, SceneSampler, TStaticSamplerState<SF_Point>::GetRHI(), SceneTextureRHI);
|
|
SetTextureParameter(RHICmdList, RHICmdList.GetBoundPixelShader(), ColorSpaceLUT, ColorSpaceLUTSampler, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(), ColorSpaceLUTRHI);
|
|
SetShaderValue(RHICmdList, RHICmdList.GetBoundPixelShader(), UILevel, CVarUILevel.GetValueOnRenderThread());
|
|
SetShaderValue(RHICmdList, RHICmdList.GetBoundPixelShader(), OutputDevice, CVarOutputDevice->GetValueOnRenderThread());
|
|
|
|
if (RHISupportsRenderTargetWriteMask(GMaxRHIShaderPlatform))
|
|
{
|
|
SetTextureParameter(RHICmdList, RHICmdList.GetBoundPixelShader(), UIWriteMaskTexture, UITextureWriteMaskRHI);
|
|
}
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("SCRGB_ENCODING"), EncodingType);
|
|
}
|
|
|
|
static const TCHAR* GetSourceFilename()
|
|
{
|
|
return TEXT("/Engine/Private/CompositeUIPixelShader.usf");
|
|
}
|
|
|
|
static const TCHAR* GetFunctionName()
|
|
{
|
|
return TEXT("Main");
|
|
}
|
|
|
|
private:
|
|
LAYOUT_FIELD(FShaderResourceParameter, UITexture);
|
|
LAYOUT_FIELD(FShaderResourceParameter, UIWriteMaskTexture);
|
|
LAYOUT_FIELD(FShaderResourceParameter, UISampler);
|
|
LAYOUT_FIELD(FShaderResourceParameter, SceneTexture);
|
|
LAYOUT_FIELD(FShaderResourceParameter, SceneSampler);
|
|
LAYOUT_FIELD(FShaderResourceParameter, ColorSpaceLUT);
|
|
LAYOUT_FIELD(FShaderResourceParameter, ColorSpaceLUTSampler);
|
|
LAYOUT_FIELD(FShaderParameter, UILevel);
|
|
LAYOUT_FIELD(FShaderParameter, OutputDevice);
|
|
};
|
|
|
|
#define SHADER_VARIATION(A) typedef FCompositePS<A> FCompositePS##A; \
|
|
IMPLEMENT_SHADER_TYPE2(FCompositePS##A, SF_Pixel);
|
|
SHADER_VARIATION(0) SHADER_VARIATION(1)
|
|
#undef SHADER_VARIATION
|
|
|
|
int32 SlateWireFrame = 0;
|
|
static FAutoConsoleVariableRef CVarSlateWireframe(TEXT("Slate.ShowWireFrame"), SlateWireFrame, TEXT(""), ECVF_Default);
|
|
|
|
void RenderSlateBatch(FTexture2DRHIRef SlateRenderTarget, bool bClear, bool bIsHDR, FViewportInfo& ViewportInfo, const FMatrix& ViewMatrix, FSlateBatchData& BatchData, FRHICommandListImmediate& RHICmdList,
|
|
const uint32 ViewportWidth, const uint32 ViewportHeight, const struct FSlateDrawWindowCommandParams& DrawCommandParams, TSharedPtr<FSlateRHIRenderingPolicy> RenderingPolicy,
|
|
FTexture2DRHIRef PostProcessBuffer)
|
|
{
|
|
FRHIRenderPassInfo RPInfo(SlateRenderTarget, ERenderTargetActions::Load_Store);
|
|
|
|
if (bClear)
|
|
{
|
|
RPInfo.ColorRenderTargets[0].Action = ERenderTargetActions::Clear_Store;
|
|
}
|
|
|
|
if (ViewportInfo.bRequiresStencilTest)
|
|
{
|
|
check(IsValidRef(ViewportInfo.DepthStencil));
|
|
|
|
ERenderTargetActions StencilAction = IsMemorylessTexture(ViewportInfo.DepthStencil) ? ERenderTargetActions::DontLoad_DontStore : ERenderTargetActions::DontLoad_Store;
|
|
RPInfo.DepthStencilRenderTarget.Action = MakeDepthStencilTargetActions(ERenderTargetActions::DontLoad_DontStore, StencilAction);
|
|
RPInfo.DepthStencilRenderTarget.DepthStencilTarget = ViewportInfo.DepthStencil;
|
|
RPInfo.DepthStencilRenderTarget.ExclusiveDepthStencil = FExclusiveDepthStencil::DepthNop_StencilWrite;
|
|
}
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
if (CVarShowSlateBatching.GetValueOnRenderThread() != 0 || CVarShowSlateOverdraw.GetValueOnRenderThread() != 0)
|
|
{
|
|
RPInfo.ColorRenderTargets[0].Action = ERenderTargetActions::Clear_Store;
|
|
if (ViewportInfo.bRequiresStencilTest)
|
|
{
|
|
// Reset the backbuffer as our color render target and also set a depth stencil buffer
|
|
ERenderTargetActions StencilAction = IsMemorylessTexture(ViewportInfo.DepthStencil) ? ERenderTargetActions::Clear_DontStore : ERenderTargetActions::Clear_Store;
|
|
RPInfo.DepthStencilRenderTarget.Action = MakeDepthStencilTargetActions(ERenderTargetActions::Load_Store, StencilAction);
|
|
RPInfo.DepthStencilRenderTarget.DepthStencilTarget = ViewportInfo.DepthStencil;
|
|
RPInfo.DepthStencilRenderTarget.ExclusiveDepthStencil = FExclusiveDepthStencil::DepthWrite_StencilWrite;
|
|
}
|
|
}
|
|
#endif
|
|
{
|
|
bool bHasBatches = BatchData.GetRenderBatches().Num() > 0;
|
|
if (bHasBatches || bClear)
|
|
{
|
|
TransitionRenderPassTargets(RHICmdList, RPInfo);
|
|
RHICmdList.BeginRenderPass(RPInfo, TEXT("SlateBatches"));
|
|
SCOPE_CYCLE_COUNTER(STAT_SlateRTDrawBatches);
|
|
|
|
if (bHasBatches)
|
|
{
|
|
FSlateBackBuffer SlateBackBuffer(SlateRenderTarget, FIntPoint(ViewportWidth, ViewportHeight));
|
|
|
|
FSlateRenderingParams RenderParams(ViewMatrix * ViewportInfo.ProjectionMatrix, DrawCommandParams.Time);
|
|
RenderParams.bWireFrame = !!SlateWireFrame;
|
|
RenderParams.bIsHDR = bIsHDR;
|
|
RenderingPolicy->SetUseGammaCorrection(!bIsHDR);
|
|
|
|
FTexture2DRHIRef EmptyTarget;
|
|
|
|
RenderingPolicy->DrawElements
|
|
(
|
|
RHICmdList,
|
|
SlateBackBuffer,
|
|
SlateRenderTarget,
|
|
PostProcessBuffer,
|
|
ViewportInfo.bRequiresStencilTest ? ViewportInfo.DepthStencil : EmptyTarget,
|
|
BatchData.GetFirstRenderBatchIndex(),
|
|
BatchData.GetRenderBatches(),
|
|
RenderParams
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// @todo Could really use a refactor.
|
|
// Kind of gross but we don't want to restart renderpasses for no reason.
|
|
// If the color deficiency shaders are active within DrawElements there will not be a renderpass here.
|
|
// In the general case there will be a RenderPass active at this point.
|
|
if (RHICmdList.IsInsideRenderPass())
|
|
{
|
|
RHICmdList.EndRenderPass();
|
|
}
|
|
}
|
|
|
|
inline bool CompositeUIWithHdrRenderTarget(const FViewportInfo* ViewInfo)
|
|
{
|
|
// Optional off-screen UI composition during HDR rendering
|
|
static const auto CVarCompositeMode = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HDR.UI.CompositeMode"));
|
|
|
|
const bool bSupportsUIComposition = GRHISupportsHDROutput && GSupportsVolumeTextureRendering && SupportsUICompositionRendering(GetFeatureLevelShaderPlatform(GMaxRHIFeatureLevel));
|
|
const bool bCompositeUI = bSupportsUIComposition
|
|
&& CVarCompositeMode && CVarCompositeMode->GetValueOnAnyThread() != 0
|
|
&& ViewInfo->bSceneHDREnabled;
|
|
|
|
return bCompositeUI;
|
|
}
|
|
|
|
/** Draws windows from a FSlateDrawBuffer on the render thread */
|
|
void FSlateRHIRenderer::DrawWindow_RenderThread(FRHICommandListImmediate& RHICmdList, FViewportInfo& ViewportInfo, FSlateWindowElementList& WindowElementList, const struct FSlateDrawWindowCommandParams& DrawCommandParams)
|
|
{
|
|
LLM_SCOPE(ELLMTag::SceneRender);
|
|
|
|
bool bRenderOffscreen = false; // Render to an offscreen texture which can then be finally color converted at the end.
|
|
|
|
#if WITH_EDITOR
|
|
if (RHIGetColorSpace(ViewportInfo.ViewportRHI) != EColorSpaceAndEOTF::ERec709_sRGB)
|
|
{
|
|
bRenderOffscreen = true;
|
|
}
|
|
#endif
|
|
|
|
FMemMark MemMark(FMemStack::Get());
|
|
|
|
static uint32 LastTimestamp = FPlatformTime::Cycles();
|
|
{
|
|
const FRHIGPUMask PresentingGPUMask = FRHIGPUMask::FromIndex(RHICmdList.GetViewportNextPresentGPUIndex(ViewportInfo.ViewportRHI));
|
|
SCOPED_GPU_MASK(RHICmdList, PresentingGPUMask);
|
|
SCOPED_DRAW_EVENTF(RHICmdList, SlateUI, TEXT("SlateUI Title = %s"), DrawCommandParams.WindowTitle.IsEmpty() ? TEXT("<none>") : *DrawCommandParams.WindowTitle);
|
|
SCOPED_GPU_STAT(RHICmdList, SlateUI);
|
|
SCOPED_NAMED_EVENT_TEXT("Slate::DrawWindow_RenderThread", FColor::Magenta);
|
|
|
|
// Should only be called by the rendering thread
|
|
check(IsInRenderingThread());
|
|
|
|
FMaterialRenderProxy::UpdateDeferredCachedUniformExpressions();
|
|
GetRendererModule().InitializeSystemTextures(RHICmdList);
|
|
|
|
const bool bCompositeUI = CompositeUIWithHdrRenderTarget(&ViewportInfo);
|
|
|
|
const int32 CompositionLUTSize = 32;
|
|
|
|
// Only need to update LUT on settings change
|
|
const int32 HDROutputDevice = (int32)ViewportInfo.HDRDisplayOutputFormat;
|
|
const int32 HDROutputGamut = (int32)ViewportInfo.HDRDisplayColorGamut;
|
|
|
|
bool bLUTStale = ViewportInfo.ColorSpaceLUTOutputDevice != HDROutputDevice || ViewportInfo.ColorSpaceLUTOutputGamut != HDROutputGamut;
|
|
|
|
ViewportInfo.ColorSpaceLUTOutputDevice = HDROutputDevice;
|
|
ViewportInfo.ColorSpaceLUTOutputGamut = HDROutputGamut;
|
|
|
|
bool bRenderedStereo = false;
|
|
if (CVarDrawToVRRenderTarget->GetInt() == 0 && GEngine && IsValidRef(ViewportInfo.GetRenderTargetTexture()) && GEngine->StereoRenderingDevice.IsValid())
|
|
{
|
|
const FVector2D WindowSize = WindowElementList.GetWindowSize();
|
|
GEngine->StereoRenderingDevice->RenderTexture_RenderThread(RHICmdList, RHICmdList.GetViewportBackBuffer(ViewportInfo.ViewportRHI), ViewportInfo.GetRenderTargetTexture(), WindowSize);
|
|
bRenderedStereo = true;
|
|
}
|
|
|
|
{
|
|
SCOPED_GPU_STAT(RHICmdList, SlateUI);
|
|
SCOPE_CYCLE_COUNTER(STAT_SlateRenderingRTTime);
|
|
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(Slate);
|
|
|
|
FSlateBatchData& BatchData = WindowElementList.GetBatchData();
|
|
|
|
// Update the vertex and index buffer
|
|
RenderingPolicy->BuildRenderingBuffers(RHICmdList, BatchData);
|
|
|
|
// This must happen after rendering buffers are created
|
|
ViewportInfo.ConditionallyUpdateDepthBuffer(BatchData.IsStencilClippingRequired(), ViewportInfo.DesiredWidth, ViewportInfo.DesiredHeight);
|
|
|
|
// should have been created by the game thread
|
|
check(IsValidRef(ViewportInfo.ViewportRHI));
|
|
|
|
FTexture2DRHIRef ViewportRT = bRenderedStereo ? nullptr : ViewportInfo.GetRenderTargetTexture();
|
|
FTexture2DRHIRef BackBuffer = (ViewportRT) ? ViewportRT : RHICmdList.GetViewportBackBuffer(ViewportInfo.ViewportRHI);
|
|
FTexture2DRHIRef PostProcessBuffer = BackBuffer; // If compositing UI then this will be different to the back buffer
|
|
|
|
const uint32 ViewportWidth = (ViewportRT) ? ViewportRT->GetSizeX() : ViewportInfo.Width;
|
|
const uint32 ViewportHeight = (ViewportRT) ? ViewportRT->GetSizeY() : ViewportInfo.Height;
|
|
|
|
// Check to see that targets are up-to-date
|
|
if (bCompositeUI && (!ViewportInfo.UITargetRT || ViewportInfo.UITargetRT->GetRHI()->GetSizeX() != ViewportWidth || ViewportInfo.UITargetRT->GetRHI()->GetSizeY() != ViewportHeight
|
|
|| !ViewportInfo.HDRSourceRT || ViewportInfo.HDRSourceRT->GetRHI()->GetFormat() != BackBuffer->GetFormat()))
|
|
{
|
|
// Composition buffers
|
|
{
|
|
ETextureCreateFlags BaseFlags = RHISupportsRenderTargetWriteMask(GMaxRHIShaderPlatform) ? TexCreate_NoFastClearFinalize | TexCreate_DisableDCC : TexCreate_None;
|
|
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(ViewportWidth, ViewportHeight),
|
|
GetSlateRecommendedColorFormat(),
|
|
FClearValueBinding::Transparent,
|
|
BaseFlags,
|
|
TexCreate_ShaderResource | TexCreate_RenderTargetable,
|
|
false,
|
|
1,
|
|
true,
|
|
true));
|
|
|
|
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, ViewportInfo.UITargetRT, TEXT("UITargetRT"));
|
|
|
|
Desc.Format = BackBuffer->GetFormat();
|
|
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, ViewportInfo.HDRSourceRT, TEXT("HDRSourceRT"));
|
|
}
|
|
|
|
// LUT
|
|
{
|
|
ViewportInfo.ColorSpaceLUT.SafeRelease();
|
|
|
|
const FRHITextureCreateDesc Desc =
|
|
FRHITextureCreateDesc::Create3D(TEXT("ColorSpaceLUT"))
|
|
.SetExtent(CompositionLUTSize)
|
|
.SetDepth(CompositionLUTSize)
|
|
.SetFormat(PF_A2B10G10R10)
|
|
.SetFlags(ETextureCreateFlags::RenderTargetable | ETextureCreateFlags::ShaderResource)
|
|
.SetInitialState(ERHIAccess::SRVMask);
|
|
|
|
ViewportInfo.ColorSpaceLUT = RHICreateTexture(Desc);
|
|
}
|
|
|
|
bLUTStale = true;
|
|
}
|
|
|
|
FTexture2DRHIRef FinalBuffer = BackBuffer;
|
|
|
|
bool bClear = DrawCommandParams.bClear;
|
|
if (bCompositeUI)
|
|
{
|
|
bClear = true; // Force a clear of the UI buffer to black
|
|
|
|
#if WITH_EDITOR
|
|
// in editor mode, we actually don't render to the true backbuffer, but to BufferedRT. The Scene image is rendered in the backbuffer with Slate
|
|
// We add it with FSlateElementBatcher::AddViewportElement and render it with RenderSlateBatch
|
|
if (WindowElementList.GetBatchDataHDR().GetRenderBatches().Num() > 0)
|
|
{
|
|
FRHIRenderPassInfo RPInfo(FinalBuffer, ERenderTargetActions::Clear_Store);
|
|
TransitionRenderPassTargets(RHICmdList, RPInfo);
|
|
RHICmdList.BeginRenderPass(RPInfo, TEXT("Clear back buffer"));
|
|
RHICmdList.EndRenderPass();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// Grab HDR backbuffer
|
|
TransitionAndCopyTexture(RHICmdList, FinalBuffer, ViewportInfo.HDRSourceRT->GetRHI(), {});
|
|
}
|
|
|
|
// UI backbuffer is temp target
|
|
BackBuffer = ViewportInfo.UITargetRT->GetRHI();
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
TRefCountPtr<IPooledRenderTarget> HDRRenderRT;
|
|
|
|
if (bRenderOffscreen )
|
|
{
|
|
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(ViewportWidth, ViewportHeight),
|
|
PF_FloatRGBA,
|
|
FClearValueBinding::Transparent,
|
|
TexCreate_None,
|
|
TexCreate_ShaderResource | TexCreate_RenderTargetable,
|
|
false,
|
|
1,
|
|
true,
|
|
true));
|
|
|
|
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, HDRRenderRT, TEXT("HDRTargetRT"));
|
|
|
|
BackBuffer = HDRRenderRT->GetRHI();
|
|
}
|
|
#endif
|
|
|
|
if (SlateWireFrame)
|
|
{
|
|
bClear = true;
|
|
}
|
|
|
|
RHICmdList.BeginDrawingViewport(ViewportInfo.ViewportRHI, FTextureRHIRef());
|
|
RHICmdList.SetViewport(0, 0, 0, ViewportWidth, ViewportHeight, 0.0f);
|
|
|
|
bool bHdrTarget = ViewportInfo.bSceneHDREnabled && !bCompositeUI;
|
|
RenderSlateBatch(BackBuffer, bClear, bHdrTarget, ViewportInfo, ViewMatrix, BatchData, RHICmdList, ViewportWidth, ViewportHeight, DrawCommandParams, RenderingPolicy, PostProcessBuffer);
|
|
|
|
if (bCompositeUI)
|
|
{
|
|
FSlateBatchData& BatchDataHDR = WindowElementList.GetBatchDataHDR();
|
|
bool bHasBatches = BatchDataHDR.GetRenderBatches().Num() > 0;
|
|
if (bHasBatches)
|
|
{
|
|
RenderingPolicy->BuildRenderingBuffers(RHICmdList, BatchDataHDR);
|
|
FTexture2DRHIRef UITargetHDRRTRHI(ViewportInfo.HDRSourceRT->GetRHI());
|
|
RenderSlateBatch(UITargetHDRRTRHI, /*bClear*/ false, /*bIsHDR*/ true, ViewportInfo, ViewMatrix, BatchDataHDR, RHICmdList, ViewportWidth, ViewportHeight, DrawCommandParams, RenderingPolicy, PostProcessBuffer);
|
|
}
|
|
|
|
SCOPED_DRAW_EVENT(RHICmdList, SlateUI_Composition);
|
|
|
|
static const FName RendererModuleName("Renderer");
|
|
IRendererModule& RendererModule = FModuleManager::GetModuleChecked<IRendererModule>(RendererModuleName);
|
|
|
|
const auto FeatureLevel = GMaxRHIFeatureLevel;
|
|
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
|
|
|
|
// Generate composition LUT
|
|
if (bLUTStale)
|
|
{
|
|
// #todo-renderpasses will this touch every pixel? use NoAction?
|
|
FRHIRenderPassInfo RPInfo(ViewportInfo.ColorSpaceLUT, ERenderTargetActions::Load_Store);
|
|
RHICmdList.Transition(FRHITransitionInfo(ViewportInfo.ColorSpaceLUT, ERHIAccess::Unknown, ERHIAccess::RTV));
|
|
RHICmdList.BeginRenderPass(RPInfo, TEXT("GenerateLUT"));
|
|
{
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
|
|
TShaderMapRef<FWriteToSliceVS> VertexShader(ShaderMap);
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader(ShaderMap);
|
|
TShaderMapRef<FCompositeLUTGenerationPS> PixelShader(ShaderMap);
|
|
const FVolumeBounds VolumeBounds(CompositionLUTSize);
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GScreenVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.SetGeometryShader(GeometryShader.GetGeometryShader());
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleStrip;
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
|
|
|
|
VertexShader->SetParameters(RHICmdList, VolumeBounds, FIntVector(VolumeBounds.MaxX - VolumeBounds.MinX));
|
|
#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
|
|
if (GeometryShader.IsValid())
|
|
{
|
|
GeometryShader->SetParameters(RHICmdList, VolumeBounds.MinZ);
|
|
}
|
|
#endif
|
|
PixelShader->SetParameters(RHICmdList, ViewportInfo.HDRDisplayOutputFormat, ViewportInfo.HDRDisplayColorGamut);
|
|
|
|
RasterizeToVolumeTexture(RHICmdList, VolumeBounds);
|
|
}
|
|
RHICmdList.EndRenderPass();
|
|
RHICmdList.Transition(FRHITransitionInfo(ViewportInfo.ColorSpaceLUT, ERHIAccess::RTV, ERHIAccess::SRVMask));
|
|
}
|
|
|
|
// Composition pass
|
|
{
|
|
RHICmdList.Transition(FRHITransitionInfo(ViewportInfo.UITargetRT->GetRHI(), ERHIAccess::Unknown, ERHIAccess::SRVMask));
|
|
|
|
if (RHISupportsRenderTargetWriteMask(GMaxRHIShaderPlatform))
|
|
{
|
|
IPooledRenderTarget* RenderTargets[] = { ViewportInfo.UITargetRT.GetReference() };
|
|
FRenderTargetWriteMask::Decode(RHICmdList, ShaderMap, RenderTargets, ViewportInfo.UITargetRTMask, TexCreate_None, TEXT("UIRTWriteMask"));
|
|
}
|
|
|
|
RHICmdList.Transition({
|
|
FRHITransitionInfo(FinalBuffer, ERHIAccess::Unknown, ERHIAccess::RTV),
|
|
FRHITransitionInfo(ViewportInfo.HDRSourceRT->GetRHI(), ERHIAccess::Unknown, ERHIAccess::SRVGraphics)
|
|
});
|
|
FRHIRenderPassInfo RPInfo(FinalBuffer, ERenderTargetActions::Load_Store);
|
|
RHICmdList.BeginRenderPass(RPInfo, TEXT("SlateComposite"));
|
|
{
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
|
|
TShaderMapRef<FScreenVS> VertexShader(ShaderMap);
|
|
|
|
FRHITexture* UITargetRTMaskTexture = RHISupportsRenderTargetWriteMask(GMaxRHIShaderPlatform) ? ViewportInfo.UITargetRTMask->GetRHI() : nullptr;
|
|
if (HDROutputDevice == (int32)EDisplayOutputFormat::HDR_ACES_1000nit_ScRGB || HDROutputDevice == (int32)EDisplayOutputFormat::HDR_ACES_2000nit_ScRGB)
|
|
{
|
|
// ScRGB encoding
|
|
TShaderMapRef<FCompositePS<1>> PixelShader(ShaderMap);
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
|
|
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
|
|
|
|
PixelShader->SetParameters(RHICmdList, ViewportInfo.UITargetRT->GetRHI(), UITargetRTMaskTexture, ViewportInfo.HDRSourceRT->GetRHI(), ViewportInfo.ColorSpaceLUT);
|
|
}
|
|
else
|
|
{
|
|
// ST2084 (PQ) encoding
|
|
TShaderMapRef<FCompositePS<0>> PixelShader(ShaderMap);
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
|
|
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
|
|
|
|
PixelShader->SetParameters(RHICmdList, ViewportInfo.UITargetRT->GetRHI(), UITargetRTMaskTexture, ViewportInfo.HDRSourceRT->GetRHI(), ViewportInfo.ColorSpaceLUT);
|
|
}
|
|
|
|
RendererModule.DrawRectangle(
|
|
RHICmdList,
|
|
0, 0,
|
|
ViewportWidth, ViewportHeight,
|
|
0, 0,
|
|
ViewportWidth, ViewportHeight,
|
|
FIntPoint(ViewportWidth, ViewportHeight),
|
|
FIntPoint(ViewportWidth, ViewportHeight),
|
|
VertexShader,
|
|
EDRF_UseTriangleOptimization);
|
|
}
|
|
RHICmdList.EndRenderPass();
|
|
}
|
|
|
|
// Put the backbuffer back to the correct one.
|
|
BackBuffer = FinalBuffer;
|
|
} //bCompositeUI
|
|
|
|
|
|
#if WITH_EDITOR
|
|
if (bRenderOffscreen)
|
|
{
|
|
const auto FeatureLevel = GMaxRHIFeatureLevel;
|
|
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
|
|
|
|
FRHIRenderPassInfo RPInfo(FinalBuffer, ERenderTargetActions::Load_Store);
|
|
RHICmdList.Transition(FRHITransitionInfo(FinalBuffer, ERHIAccess::Unknown, ERHIAccess::RTV));
|
|
RHICmdList.BeginRenderPass(RPInfo, TEXT("SlateComposite"));
|
|
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
|
|
// ST2084 (PQ) encoding
|
|
TShaderMapRef<FHDREditorConvertPS> PixelShader(ShaderMap);
|
|
TShaderMapRef<FScreenVS> VertexShader(ShaderMap);
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
|
|
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
|
|
|
|
PixelShader->SetParameters(RHICmdList, HDRRenderRT->GetRHI() );
|
|
|
|
static const FName RendererModuleName("Renderer");
|
|
IRendererModule& RendererModule = FModuleManager::GetModuleChecked<IRendererModule>(RendererModuleName);
|
|
|
|
RendererModule.DrawRectangle(
|
|
RHICmdList,
|
|
0, 0,
|
|
ViewportWidth, ViewportHeight,
|
|
0, 0,
|
|
ViewportWidth, ViewportHeight,
|
|
FIntPoint(ViewportWidth, ViewportHeight),
|
|
FIntPoint(ViewportWidth, ViewportHeight),
|
|
VertexShader,
|
|
EDRF_UseTriangleOptimization);
|
|
|
|
RHICmdList.EndRenderPass();
|
|
BackBuffer = FinalBuffer;
|
|
}
|
|
#endif
|
|
if (!bRenderedStereo && GEngine && IsValidRef(ViewportInfo.GetRenderTargetTexture()) && GEngine->StereoRenderingDevice.IsValid())
|
|
{
|
|
const FVector2D WindowSize = WindowElementList.GetWindowSize();
|
|
GEngine->StereoRenderingDevice->RenderTexture_RenderThread(RHICmdList, RHICmdList.GetViewportBackBuffer(ViewportInfo.ViewportRHI), ViewportInfo.GetRenderTargetTexture(), WindowSize);
|
|
}
|
|
RHICmdList.Transition(FRHITransitionInfo(BackBuffer, ERHIAccess::Unknown, ERHIAccess::SRVGraphics));
|
|
|
|
// Fire delegate to inform bound functions the back buffer is ready to be captured.
|
|
OnBackBufferReadyToPresentDelegate.Broadcast(*DrawCommandParams.Window, BackBuffer);
|
|
|
|
if (bRenderedStereo)
|
|
{
|
|
// XR requires the backbuffer to be transitioned back to RTV
|
|
RHICmdList.Transition(FRHITransitionInfo(BackBuffer, ERHIAccess::SRVGraphics, ERHIAccess::RTV));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bTakingAScreenShot && ScreenshotViewportInfo != nullptr && ScreenshotViewportInfo == &ViewportInfo)
|
|
{
|
|
// take screenshot before swapbuffer
|
|
FTexture2DRHIRef BackBuffer = RHICmdList.GetViewportBackBuffer(ViewportInfo.ViewportRHI);
|
|
|
|
// Sanity check to make sure the user specified a valid screenshot rect.
|
|
FIntRect ClampedScreenshotRect;
|
|
ClampedScreenshotRect.Min = ScreenshotRect.Min;
|
|
ClampedScreenshotRect.Max = ScreenshotRect.Max.ComponentMin(BackBuffer->GetSizeXY());
|
|
ClampedScreenshotRect.Max = ScreenshotRect.Min.ComponentMax(ClampedScreenshotRect.Max);
|
|
|
|
if (ClampedScreenshotRect != ScreenshotRect)
|
|
{
|
|
UE_LOG(LogSlate, Warning, TEXT("Slate: Screenshot rect max coordinate had to be clamped from [%d, %d] to [%d, %d]"), ScreenshotRect.Max.X, ScreenshotRect.Max.Y, ClampedScreenshotRect.Max.X, ClampedScreenshotRect.Max.Y);
|
|
}
|
|
|
|
if (!ClampedScreenshotRect.IsEmpty())
|
|
{
|
|
RHICmdList.ReadSurfaceData(BackBuffer, ClampedScreenshotRect, *OutScreenshotData, FReadSurfaceDataFlags());
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogSlate, Warning, TEXT("Slate: Screenshot rect was empty! Skipping readback of back buffer."));
|
|
}
|
|
bTakingAScreenShot = false;
|
|
OutScreenshotData = nullptr;
|
|
ScreenshotViewportInfo = nullptr;
|
|
}
|
|
|
|
// Calculate renderthread time (excluding idle time).
|
|
uint32 StartTime = FPlatformTime::Cycles();
|
|
|
|
RHICmdList.EnqueueLambda([CurrentFrameCounter = GFrameCounterRenderThread](FRHICommandListImmediate& InRHICmdList)
|
|
{
|
|
GEngine->SetPresentLatencyMarkerStart(CurrentFrameCounter);
|
|
});
|
|
|
|
RHICmdList.EndDrawingViewport(ViewportInfo.ViewportRHI, true, DrawCommandParams.bLockToVsync);
|
|
|
|
RHICmdList.EnqueueLambda([CurrentFrameCounter = GFrameCounterRenderThread](FRHICommandListImmediate& InRHICmdList)
|
|
{
|
|
GEngine->SetPresentLatencyMarkerEnd(CurrentFrameCounter);
|
|
});
|
|
|
|
uint32 EndTime = FPlatformTime::Cycles();
|
|
|
|
GSwapBufferTime = EndTime - StartTime;
|
|
SET_CYCLE_COUNTER(STAT_PresentTime, GSwapBufferTime);
|
|
|
|
uint32 ThreadTime = EndTime - LastTimestamp;
|
|
LastTimestamp = EndTime;
|
|
|
|
uint32 RenderThreadIdle = 0;
|
|
|
|
FThreadIdleStats& RenderThread = FThreadIdleStats::Get();
|
|
GRenderThreadIdle[ERenderThreadIdleTypes::WaitingForAllOtherSleep] = RenderThread.Waits;
|
|
GRenderThreadIdle[ERenderThreadIdleTypes::WaitingForGPUPresent] += GSwapBufferTime;
|
|
GRenderThreadNumIdle[ERenderThreadIdleTypes::WaitingForGPUPresent]++;
|
|
RenderThread.Waits = 0;
|
|
|
|
SET_CYCLE_COUNTER(STAT_RenderingIdleTime_RenderThreadSleepTime, GRenderThreadIdle[0]);
|
|
SET_CYCLE_COUNTER(STAT_RenderingIdleTime_WaitingForGPUQuery, GRenderThreadIdle[1]);
|
|
SET_CYCLE_COUNTER(STAT_RenderingIdleTime_WaitingForGPUPresent, GRenderThreadIdle[2]);
|
|
|
|
for (int32 Index = 0; Index < ERenderThreadIdleTypes::Num; Index++)
|
|
{
|
|
RenderThreadIdle += GRenderThreadIdle[Index];
|
|
GRenderThreadIdle[Index] = 0;
|
|
GRenderThreadNumIdle[Index] = 0;
|
|
}
|
|
|
|
SET_CYCLE_COUNTER(STAT_RenderingIdleTime, RenderThreadIdle);
|
|
GRenderThreadTime = (ThreadTime > RenderThreadIdle) ? (ThreadTime - RenderThreadIdle) : ThreadTime;
|
|
|
|
if (IsRunningRHIInSeparateThread())
|
|
{
|
|
RHICmdList.EnqueueLambda([](FRHICommandListImmediate&)
|
|
{
|
|
// Restart the RHI thread timer, so we don't count time spent in Present twice when this command list finishes.
|
|
uint32 ThisCycles = FPlatformTime::Cycles();
|
|
GWorkingRHIThreadTime += (ThisCycles - GWorkingRHIThreadStartCycles);
|
|
GWorkingRHIThreadStartCycles = ThisCycles;
|
|
|
|
uint32 NewVal = GWorkingRHIThreadTime - GWorkingRHIThreadStallTime;
|
|
FPlatformAtomics::AtomicStore((int32*)&GRHIThreadTime, (int32)NewVal);
|
|
GWorkingRHIThreadTime = 0;
|
|
GWorkingRHIThreadStallTime = 0;
|
|
});
|
|
}
|
|
}
|
|
|
|
void FSlateRHIRenderer::DrawWindows(FSlateDrawBuffer& WindowDrawBuffer)
|
|
{
|
|
DrawWindows_Private(WindowDrawBuffer);
|
|
}
|
|
|
|
|
|
void FSlateRHIRenderer::PrepareToTakeScreenshot(const FIntRect& Rect, TArray<FColor>* OutColorData, SWindow* InScreenshotWindow)
|
|
{
|
|
check(OutColorData);
|
|
|
|
bTakingAScreenShot = true;
|
|
ScreenshotRect = Rect;
|
|
OutScreenshotData = OutColorData;
|
|
ScreenshotViewportInfo = *WindowToViewportInfo.Find(InScreenshotWindow);
|
|
}
|
|
|
|
/**
|
|
* Creates necessary resources to render a window and sends draw commands to the rendering thread
|
|
*
|
|
* @param WindowDrawBuffer The buffer containing elements to draw
|
|
*/
|
|
void FSlateRHIRenderer::DrawWindows_Private(FSlateDrawBuffer& WindowDrawBuffer)
|
|
{
|
|
checkSlow(IsThreadSafeForSlateRendering());
|
|
|
|
|
|
FSlateRHIRenderingPolicy* Policy = RenderingPolicy.Get();
|
|
ENQUEUE_RENDER_COMMAND(SlateBeginDrawingWindowsCommand)(
|
|
[Policy](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
Policy->BeginDrawingWindows();
|
|
}
|
|
);
|
|
|
|
// Update texture atlases if needed and safe
|
|
if (DoesThreadOwnSlateRendering())
|
|
{
|
|
ResourceManager->UpdateTextureAtlases();
|
|
}
|
|
|
|
const TSharedRef<FSlateFontCache> FontCache = SlateFontServices->GetFontCache();
|
|
|
|
// Iterate through each element list and set up an RHI window for it if needed
|
|
const TArray<TSharedRef<FSlateWindowElementList>>& WindowElementLists = WindowDrawBuffer.GetWindowElementLists();
|
|
for (int32 ListIndex = 0; ListIndex < WindowElementLists.Num(); ++ListIndex)
|
|
{
|
|
FSlateWindowElementList& ElementList = *WindowElementLists[ListIndex];
|
|
|
|
SWindow* Window = ElementList.GetRenderWindow();
|
|
|
|
if (Window)
|
|
{
|
|
const FVector2f WindowSize = UE::Slate::CastToVector2f(Window->GetViewportSize());
|
|
if (WindowSize.X > 0 && WindowSize.Y > 0)
|
|
{
|
|
// The viewport need to be created at this point
|
|
FViewportInfo* ViewInfo = nullptr;
|
|
{
|
|
FViewportInfo** FoundViewInfo = WindowToViewportInfo.Find(Window);
|
|
if (ensure(FoundViewInfo))
|
|
{
|
|
ViewInfo = *FoundViewInfo;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogSlate, Error, TEXT("The ViewportInfo could not be found for Window."));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
bool bCompositeHDRViewports = ElementBatcher->CompositeHDRViewports();
|
|
ElementBatcher->SetCompositeHDRViewports(CompositeUIWithHdrRenderTarget(ViewInfo));
|
|
// Add all elements for this window to the element batcher
|
|
ElementBatcher->AddElements(ElementList);
|
|
ElementBatcher->SetCompositeHDRViewports(bCompositeHDRViewports);
|
|
|
|
// Update the font cache with new text after elements are batched
|
|
FontCache->UpdateCache();
|
|
|
|
bool bLockToVsync = ElementBatcher->RequiresVsync();
|
|
|
|
bool bForceVsyncFromCVar = false;
|
|
if (GIsEditor)
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSyncEditor"));
|
|
bForceVsyncFromCVar = (CVar->GetInt() != 0);
|
|
}
|
|
else
|
|
{
|
|
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSync"));
|
|
bForceVsyncFromCVar = (CVar->GetInt() != 0);
|
|
}
|
|
|
|
bLockToVsync |= bForceVsyncFromCVar;
|
|
|
|
// All elements for this window have been batched and rendering data updated
|
|
ElementBatcher->ResetBatches();
|
|
|
|
// Cache off the HDR status
|
|
ViewInfo->bHDREnabled = RHIGetColorSpace(ViewInfo->ViewportRHI) != EColorSpaceAndEOTF::ERec709_sRGB;
|
|
Window->SetIsHDR(ViewInfo->bHDREnabled);
|
|
|
|
if (Window->IsViewportSizeDrivenByWindow())
|
|
{
|
|
// Resize the viewport if needed
|
|
ConditionalResizeViewport(ViewInfo, ViewInfo->DesiredWidth, ViewInfo->DesiredHeight, IsViewportFullscreen(*Window), Window);
|
|
}
|
|
|
|
// Tell the rendering thread to draw the windows
|
|
{
|
|
FSlateDrawWindowCommandParams Params;
|
|
|
|
Params.Renderer = this;
|
|
Params.WindowElementList = &ElementList;
|
|
Params.Window = Window;
|
|
#if WANTS_DRAW_MESH_EVENTS
|
|
Params.WindowTitle = Window->GetTitle().ToString();
|
|
#endif
|
|
Params.bLockToVsync = bLockToVsync;
|
|
#if ALPHA_BLENDED_WINDOWS
|
|
Params.bClear = Window->GetTransparencySupport() == EWindowTransparency::PerPixel;
|
|
#else
|
|
Params.bClear = false;
|
|
#endif
|
|
Params.Time = FGameTime::CreateDilated(
|
|
FPlatformTime::Seconds() - GStartTime, FApp::GetDeltaTime(),
|
|
FApp::GetCurrentTime() - GStartTime, FApp::GetDeltaTime());
|
|
|
|
// Skip the actual draw if we're in a headless execution environment
|
|
bool bLocalTakingAScreenShot = bTakingAScreenShot;
|
|
if (GIsClient && !IsRunningCommandlet() && !GUsingNullRHI)
|
|
{
|
|
ENQUEUE_RENDER_COMMAND(SlateDrawWindowsCommand)(
|
|
[Params, ViewInfo](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
Params.Renderer->DrawWindow_RenderThread(RHICmdList, *ViewInfo, *Params.WindowElementList, Params);
|
|
}
|
|
);
|
|
}
|
|
|
|
SlateWindowRendered.Broadcast(*Window, &ViewInfo->ViewportRHI);
|
|
|
|
if (bLocalTakingAScreenShot)
|
|
{
|
|
// immediately flush the RHI command list
|
|
FlushRenderingCommands();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ensureMsgf(false, TEXT("Window isnt valid but being drawn!"));
|
|
}
|
|
}
|
|
|
|
FSlateDrawBuffer* DrawBuffer = &WindowDrawBuffer;
|
|
ENQUEUE_RENDER_COMMAND(SlateEndDrawingWindowsCommand)(
|
|
[DrawBuffer, Policy](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
FSlateEndDrawingWindowsCommand::EndDrawingWindows(RHICmdList, DrawBuffer, *Policy);
|
|
}
|
|
);
|
|
|
|
if (DeferredUpdateContexts.Num() > 0)
|
|
{
|
|
// Intentionally copy the contexts to avoid contention with the game thread
|
|
ENQUEUE_RENDER_COMMAND(DrawWidgetRendererImmediate)(
|
|
[Contexts = DeferredUpdateContexts](FRHICommandListImmediate& RHICmdList) mutable
|
|
{
|
|
for (const FRenderThreadUpdateContext& Context : Contexts)
|
|
{
|
|
Context.Renderer->DrawWindowToTarget_RenderThread(RHICmdList, Context);
|
|
}
|
|
}
|
|
);
|
|
|
|
DeferredUpdateContexts.Empty();
|
|
}
|
|
|
|
if (FastPathRenderingDataCleanupList)
|
|
{
|
|
FastPathRenderingDataCleanupList->Cleanup();
|
|
FastPathRenderingDataCleanupList = nullptr;
|
|
}
|
|
|
|
// flush the cache if needed
|
|
FontCache->ConditionalFlushCache();
|
|
ResourceManager->ConditionalFlushAtlases();
|
|
}
|
|
|
|
|
|
FIntPoint FSlateRHIRenderer::GenerateDynamicImageResource(const FName InTextureName)
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
uint32 Width = 0;
|
|
uint32 Height = 0;
|
|
TArray<uint8> RawData;
|
|
|
|
TSharedPtr<FSlateDynamicTextureResource> TextureResource = ResourceManager->GetDynamicTextureResourceByName(InTextureName);
|
|
if (!TextureResource.IsValid())
|
|
{
|
|
// Load the image from disk
|
|
bool bSucceeded = ResourceManager->LoadTexture(InTextureName, InTextureName.ToString(), Width, Height, RawData);
|
|
if (bSucceeded)
|
|
{
|
|
TextureResource = ResourceManager->MakeDynamicTextureResource(InTextureName, Width, Height, RawData);
|
|
}
|
|
}
|
|
|
|
return TextureResource.IsValid() ? TextureResource->Proxy->ActualSize : FIntPoint(0, 0);
|
|
}
|
|
|
|
bool FSlateRHIRenderer::GenerateDynamicImageResource(FName ResourceName, uint32 Width, uint32 Height, const TArray< uint8 >& Bytes)
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
TSharedPtr<FSlateDynamicTextureResource> TextureResource = ResourceManager->GetDynamicTextureResourceByName(ResourceName);
|
|
if (!TextureResource.IsValid())
|
|
{
|
|
TextureResource = ResourceManager->MakeDynamicTextureResource(ResourceName, Width, Height, Bytes);
|
|
}
|
|
return TextureResource.IsValid();
|
|
}
|
|
|
|
bool FSlateRHIRenderer::GenerateDynamicImageResource(FName ResourceName, FSlateTextureDataRef TextureData)
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
TSharedPtr<FSlateDynamicTextureResource> TextureResource = ResourceManager->GetDynamicTextureResourceByName(ResourceName);
|
|
if (!TextureResource.IsValid())
|
|
{
|
|
TextureResource = ResourceManager->MakeDynamicTextureResource(ResourceName, TextureData);
|
|
}
|
|
return TextureResource.IsValid();
|
|
}
|
|
|
|
FSlateResourceHandle FSlateRHIRenderer::GetResourceHandle(const FSlateBrush& Brush, FVector2f LocalSize, float DrawScale)
|
|
{
|
|
return ResourceManager->GetResourceHandle(Brush, LocalSize, DrawScale);
|
|
}
|
|
|
|
bool FSlateRHIRenderer::CanRenderResource(UObject& InResourceObject) const
|
|
{
|
|
return Cast<UTexture>(&InResourceObject) || Cast<ISlateTextureAtlasInterface>(&InResourceObject) || Cast<UMaterialInterface>(&InResourceObject);
|
|
}
|
|
|
|
void FSlateRHIRenderer::RemoveDynamicBrushResource( TSharedPtr<FSlateDynamicImageBrush> BrushToRemove )
|
|
{
|
|
if (BrushToRemove.IsValid())
|
|
{
|
|
DynamicBrushesToRemove[FreeBufferIndex].Add(BrushToRemove);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gives the renderer a chance to wait for any render commands to be completed before returning/
|
|
*/
|
|
void FSlateRHIRenderer::FlushCommands() const
|
|
{
|
|
if (IsInGameThread() || IsInSlateThread())
|
|
{
|
|
FlushRenderingCommands();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gives the renderer a chance to synchronize with another thread in the event that the renderer runs
|
|
* in a multi-threaded environment. This function does not return until the sync is complete
|
|
*/
|
|
void FSlateRHIRenderer::Sync() const
|
|
{
|
|
// Sync game and render thread. Either total sync or allowing one frame lag.
|
|
static FFrameEndSync FrameEndSync;
|
|
static auto CVarAllowOneFrameThreadLag = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.OneFrameThreadLag"));
|
|
FrameEndSync.Sync(CVarAllowOneFrameThreadLag->GetValueOnAnyThread() != 0);
|
|
}
|
|
|
|
/**
|
|
* Inline issues a BeginFrame to the RHI.
|
|
* This is to handle cases like Modal dialogs in the UI. The game loop stops while
|
|
* the dialog is open but continues to issue draws. The RHI thinks there are all part of one super long
|
|
* frame until the Modal window is closed.
|
|
*/
|
|
void FSlateRHIRenderer::BeginFrame() const
|
|
{
|
|
ENQUEUE_RENDER_COMMAND(SlateRHIBeginFrame)(
|
|
[](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
RHICmdList.BeginFrame();
|
|
}
|
|
);
|
|
}
|
|
|
|
void FSlateRHIRenderer::EndFrame() const
|
|
{
|
|
ENQUEUE_RENDER_COMMAND(SlateRHIEndFrame)(
|
|
[](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
RHICmdList.EndFrame();
|
|
}
|
|
);
|
|
}
|
|
|
|
void FSlateRHIRenderer::ReloadTextureResources()
|
|
{
|
|
ResourceManager->ReloadTextures();
|
|
}
|
|
|
|
void FSlateRHIRenderer::LoadUsedTextures()
|
|
{
|
|
if (ResourceManager.IsValid())
|
|
{
|
|
ResourceManager->LoadUsedTextures();
|
|
}
|
|
}
|
|
|
|
void FSlateRHIRenderer::LoadStyleResources(const ISlateStyle& Style)
|
|
{
|
|
if (ResourceManager.IsValid())
|
|
{
|
|
ResourceManager->LoadStyleResources(Style);
|
|
}
|
|
}
|
|
|
|
void FSlateRHIRenderer::ReleaseDynamicResource(const FSlateBrush& InBrush)
|
|
{
|
|
ensure(IsInGameThread());
|
|
ResourceManager->ReleaseDynamicResource(InBrush);
|
|
}
|
|
|
|
void* FSlateRHIRenderer::GetViewportResource(const SWindow& Window)
|
|
{
|
|
checkSlow(IsThreadSafeForSlateRendering());
|
|
|
|
FViewportInfo** InfoPtr = WindowToViewportInfo.Find(&Window);
|
|
|
|
if (InfoPtr)
|
|
{
|
|
FViewportInfo* ViewportInfo = *InfoPtr;
|
|
|
|
// Create the viewport if it doesnt exist
|
|
if (!IsValidRef(ViewportInfo->ViewportRHI))
|
|
{
|
|
// Sanity check dimensions
|
|
checkf(ViewportInfo->Width <= MAX_VIEWPORT_SIZE && ViewportInfo->Height <= MAX_VIEWPORT_SIZE, TEXT("Invalid window with Width=%u and Height=%u"), ViewportInfo->Width, ViewportInfo->Height);
|
|
|
|
const bool bFullscreen = IsViewportFullscreen(Window);
|
|
|
|
ViewportInfo->ViewportRHI = RHICreateViewport(ViewportInfo->OSWindow, ViewportInfo->Width, ViewportInfo->Height, bFullscreen, ViewportInfo->PixelFormat);
|
|
}
|
|
|
|
return &ViewportInfo->ViewportRHI;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void FSlateRHIRenderer::SetColorVisionDeficiencyType(EColorVisionDeficiency Type, int32 Severity, bool bCorrectDeficiency, bool bShowCorrectionWithDeficiency)
|
|
{
|
|
GSlateColorDeficiencyType = Type;
|
|
GSlateColorDeficiencySeverity = FMath::Clamp(Severity, 0, 10);
|
|
GSlateColorDeficiencyCorrection = bCorrectDeficiency;
|
|
GSlateShowColorDeficiencyCorrectionWithDeficiency = bShowCorrectionWithDeficiency;
|
|
}
|
|
|
|
FSlateUpdatableTexture* FSlateRHIRenderer::CreateUpdatableTexture(uint32 Width, uint32 Height)
|
|
{
|
|
const bool bCreateEmptyTexture = true;
|
|
FSlateTexture2DRHIRef* NewTexture = new FSlateTexture2DRHIRef(Width, Height, GetSlateRecommendedColorFormat(), nullptr, TexCreate_Dynamic, bCreateEmptyTexture);
|
|
if (IsInRenderingThread())
|
|
{
|
|
NewTexture->InitResource();
|
|
}
|
|
else
|
|
{
|
|
BeginInitResource(NewTexture);
|
|
}
|
|
return NewTexture;
|
|
}
|
|
|
|
FSlateUpdatableTexture* FSlateRHIRenderer::CreateSharedHandleTexture(void* SharedHandle)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
void FSlateRHIRenderer::ReleaseUpdatableTexture(FSlateUpdatableTexture* Texture)
|
|
{
|
|
if (IsInRenderingThread())
|
|
{
|
|
Texture->GetRenderResource()->ReleaseResource();
|
|
delete Texture;
|
|
}
|
|
else
|
|
{
|
|
Texture->Cleanup();
|
|
}
|
|
}
|
|
|
|
ISlateAtlasProvider* FSlateRHIRenderer::GetTextureAtlasProvider()
|
|
{
|
|
if (ResourceManager.IsValid())
|
|
{
|
|
return ResourceManager->GetTextureAtlasProvider();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
|
|
int32 FSlateRHIRenderer::RegisterCurrentScene(FSceneInterface* Scene)
|
|
{
|
|
check(IsInGameThread());
|
|
if (Scene && Scene->GetWorld())
|
|
{
|
|
// We only want one scene view per world, (todo per player for split screen)
|
|
CurrentSceneIndex = ActiveScenes.IndexOfByPredicate([&Scene](const FSceneInterface* TestScene) { return TestScene->GetWorld() == Scene->GetWorld(); });
|
|
if (CurrentSceneIndex == INDEX_NONE)
|
|
{
|
|
CurrentSceneIndex = ActiveScenes.Add(Scene);
|
|
|
|
// We need to keep the ActiveScenes array synchronized with the Policy's ActiveScenes array on
|
|
// the render thread.
|
|
FSlateRHIRenderingPolicy* InRenderPolicy = RenderingPolicy.Get();
|
|
int32 LocalCurrentSceneIndex = CurrentSceneIndex;
|
|
ENQUEUE_RENDER_COMMAND(RegisterCurrentSceneOnPolicy)(
|
|
[InRenderPolicy, Scene, LocalCurrentSceneIndex](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
if (LocalCurrentSceneIndex != -1)
|
|
{
|
|
InRenderPolicy->AddSceneAt(Scene, LocalCurrentSceneIndex);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CurrentSceneIndex = -1;
|
|
}
|
|
|
|
return CurrentSceneIndex;
|
|
}
|
|
|
|
int32 FSlateRHIRenderer::GetCurrentSceneIndex() const
|
|
{
|
|
return CurrentSceneIndex;
|
|
}
|
|
|
|
void FSlateRHIRenderer::ClearScenes()
|
|
{
|
|
if (!IsInSlateThread())
|
|
{
|
|
CurrentSceneIndex = -1;
|
|
ActiveScenes.Empty();
|
|
|
|
// We need to keep the ActiveScenes array synchronized with the Policy's ActiveScenes array on
|
|
// the render thread.
|
|
FSlateRenderingPolicy* InRenderPolicy = RenderingPolicy.Get();
|
|
ENQUEUE_RENDER_COMMAND(ClearScenesOnPolicy)(
|
|
[InRenderPolicy](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
InRenderPolicy->ClearScenes();
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
EPixelFormat FSlateRHIRenderer::GetSlateRecommendedColorFormat()
|
|
{
|
|
return bIsStandaloneStereoOnlyDevice ? PF_R8G8B8A8 : PF_B8G8R8A8;
|
|
}
|
|
|
|
FRHICOMMAND_MACRO(FClearCachedRenderingDataCommand)
|
|
{
|
|
public:
|
|
FClearCachedRenderingDataCommand(FSlateCachedFastPathRenderingData* InCachedRenderingData)
|
|
: CachedRenderingData(InCachedRenderingData)
|
|
{
|
|
|
|
}
|
|
|
|
void Execute(FRHICommandListBase& CmdList)
|
|
{
|
|
delete CachedRenderingData;
|
|
}
|
|
|
|
private:
|
|
FSlateCachedFastPathRenderingData* CachedRenderingData;
|
|
};
|
|
|
|
FRHICOMMAND_MACRO(FClearCachedElementDataCommand)
|
|
{
|
|
public:
|
|
FClearCachedElementDataCommand(FSlateCachedElementData* InCachedElementData)
|
|
: CachedElementData(InCachedElementData)
|
|
{
|
|
|
|
}
|
|
|
|
void Execute(FRHICommandListBase& CmdList)
|
|
{
|
|
delete CachedElementData;
|
|
}
|
|
|
|
private:
|
|
FSlateCachedElementData* CachedElementData;
|
|
};
|
|
|
|
void FSlateRHIRenderer::DestroyCachedFastPathRenderingData(FSlateCachedFastPathRenderingData* CachedRenderingData)
|
|
{
|
|
check(CachedRenderingData);
|
|
|
|
if (!FastPathRenderingDataCleanupList)
|
|
{
|
|
// This will be deleted later on the rendering thread
|
|
FastPathRenderingDataCleanupList = new FFastPathRenderingDataCleanupList;
|
|
}
|
|
|
|
FastPathRenderingDataCleanupList->FastPathRenderingDataToRemove.Add(CachedRenderingData);
|
|
}
|
|
|
|
void FSlateRHIRenderer::DestroyCachedFastPathElementData(FSlateCachedElementData* CachedElementData)
|
|
{
|
|
check(CachedElementData);
|
|
|
|
// Cached data should be destroyed in a thread safe way. If there is an rhi thread it could be reading from the data to copy it into a vertex buffer
|
|
// so delete it on the rhi thread if necessary, otherwise delete it on the render thread
|
|
ENQUEUE_RENDER_COMMAND(ClearCachedElementData)(
|
|
[CachedElementData](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
if (!RHICmdList.Bypass())
|
|
{
|
|
new (RHICmdList.AllocCommand<FClearCachedElementDataCommand>()) FClearCachedElementDataCommand(CachedElementData);
|
|
}
|
|
else
|
|
{
|
|
FClearCachedElementDataCommand Cmd(CachedElementData);
|
|
Cmd.Execute(RHICmdList);
|
|
}
|
|
});
|
|
}
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
namespace SlateRendererUtil
|
|
{
|
|
bool bSlateShadersInitialized = false;
|
|
FDelegateHandle GlobalShaderCompilationDelegateHandle;
|
|
|
|
bool AreShadersInitialized()
|
|
{
|
|
if (!bSlateShadersInitialized)
|
|
{
|
|
bSlateShadersInitialized = IsGlobalShaderMapComplete(TEXT("SlateElement"));
|
|
// if shaders are initialized, cache the value until global shaders gets recompiled.
|
|
if (bSlateShadersInitialized)
|
|
{
|
|
GlobalShaderCompilationDelegateHandle = GetOnGlobalShaderCompilation().AddLambda([]()
|
|
{
|
|
bSlateShadersInitialized = false;
|
|
GetOnGlobalShaderCompilation().Remove(GlobalShaderCompilationDelegateHandle);
|
|
});
|
|
}
|
|
}
|
|
return bSlateShadersInitialized;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bool FSlateRHIRenderer::AreShadersInitialized() const
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
return SlateRendererUtil::AreShadersInitialized();
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void FSlateRHIRenderer::InvalidateAllViewports()
|
|
{
|
|
for (TMap< const SWindow*, FViewportInfo*>::TIterator It(WindowToViewportInfo); It; ++It)
|
|
{
|
|
It.Value()->ViewportRHI = nullptr;
|
|
}
|
|
}
|
|
|
|
FCriticalSection* FSlateRHIRenderer::GetResourceCriticalSection()
|
|
{
|
|
return ResourceManager->GetResourceCriticalSection();
|
|
}
|
|
|
|
void FSlateRHIRenderer::ReleaseAccessedResources(bool bImmediatelyFlush)
|
|
{
|
|
// We keep track of the Scene objects from SceneViewports on the SlateRenderer. Make sure that this gets refreshed every frame.
|
|
ClearScenes();
|
|
|
|
if (bImmediatelyFlush)
|
|
{
|
|
// Increment resource version to allow buffers to shrink or cached structures to clean up.
|
|
ResourceVersion++;
|
|
|
|
// Release resources generated specifically by the rendering policy if we are flushing.
|
|
// This should NOT be done unless flushing
|
|
RenderingPolicy->FlushGeneratedResources();
|
|
|
|
//FlushCommands();
|
|
}
|
|
}
|
|
|
|
void FSlateRHIRenderer::RequestResize(const TSharedPtr<SWindow>& Window, uint32 NewWidth, uint32 NewHeight)
|
|
{
|
|
checkSlow(IsThreadSafeForSlateRendering());
|
|
|
|
FViewportInfo* ViewInfo = WindowToViewportInfo.FindRef(Window.Get());
|
|
|
|
if (ViewInfo)
|
|
{
|
|
ViewInfo->DesiredWidth = NewWidth;
|
|
ViewInfo->DesiredHeight = NewHeight;
|
|
}
|
|
}
|
|
|
|
void FSlateRHIRenderer::SetWindowRenderTarget(const SWindow& Window, IViewportRenderTargetProvider* Provider)
|
|
{
|
|
FViewportInfo* ViewInfo = WindowToViewportInfo.FindRef(&Window);
|
|
if (ViewInfo)
|
|
{
|
|
ViewInfo->RTProvider = Provider;
|
|
}
|
|
}
|
|
|
|
void FSlateRHIRenderer::AddWidgetRendererUpdate(const struct FRenderThreadUpdateContext& Context, bool bDeferredRenderTargetUpdate)
|
|
{
|
|
if (bDeferredRenderTargetUpdate)
|
|
{
|
|
DeferredUpdateContexts.Add(Context);
|
|
}
|
|
else
|
|
{
|
|
// Enqueue a command to unlock the draw buffer after all windows have been drawn
|
|
FRenderThreadUpdateContext InContext = Context;
|
|
ENQUEUE_RENDER_COMMAND(DrawWidgetRendererImmediate)(
|
|
[InContext](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
InContext.Renderer->DrawWindowToTarget_RenderThread(RHICmdList, InContext);
|
|
});
|
|
}
|
|
}
|
|
|
|
FSlateEndDrawingWindowsCommand::FSlateEndDrawingWindowsCommand(FSlateRHIRenderingPolicy& InPolicy, FSlateDrawBuffer* InDrawBuffer)
|
|
: Policy(InPolicy)
|
|
, DrawBuffer(InDrawBuffer)
|
|
{}
|
|
|
|
void FSlateEndDrawingWindowsCommand::Execute(FRHICommandListBase& CmdList)
|
|
{
|
|
Policy.EndDrawingWindows();
|
|
}
|
|
|
|
void FSlateEndDrawingWindowsCommand::EndDrawingWindows(FRHICommandListImmediate& RHICmdList, FSlateDrawBuffer* DrawBuffer, FSlateRHIRenderingPolicy& Policy)
|
|
{
|
|
if (!RHICmdList.Bypass())
|
|
{
|
|
ALLOC_COMMAND_CL(RHICmdList, FSlateEndDrawingWindowsCommand)(Policy, DrawBuffer);
|
|
}
|
|
else
|
|
{
|
|
FSlateEndDrawingWindowsCommand Cmd(Policy, DrawBuffer);
|
|
Cmd.Execute(RHICmdList);
|
|
}
|
|
}
|
|
|
|
FSlateReleaseDrawBufferCommand::FSlateReleaseDrawBufferCommand(FSlateDrawBuffer* InDrawBuffer)
|
|
: DrawBuffer(InDrawBuffer)
|
|
{}
|
|
|
|
void FSlateReleaseDrawBufferCommand::Execute(FRHICommandListBase& CmdList)
|
|
{
|
|
DrawBuffer->Unlock();
|
|
}
|
|
|
|
void FSlateReleaseDrawBufferCommand::ReleaseDrawBuffer(FRHICommandListImmediate& RHICmdList, FSlateDrawBuffer* DrawBuffer)
|
|
{
|
|
if (!RHICmdList.Bypass())
|
|
{
|
|
ALLOC_COMMAND_CL(RHICmdList, FSlateReleaseDrawBufferCommand)(DrawBuffer);
|
|
}
|
|
else
|
|
{
|
|
FSlateReleaseDrawBufferCommand Cmd(DrawBuffer);
|
|
Cmd.Execute(RHICmdList);
|
|
}
|
|
}
|
|
|
|
|
|
struct FClearCachedRenderingDataCommand2 final : public FRHICommand < FClearCachedRenderingDataCommand2 >
|
|
{
|
|
public:
|
|
FClearCachedRenderingDataCommand2(FFastPathRenderingDataCleanupList* InCleanupList)
|
|
: CleanupList(InCleanupList)
|
|
{
|
|
|
|
}
|
|
|
|
void Execute(FRHICommandListBase& CmdList)
|
|
{
|
|
delete CleanupList;
|
|
}
|
|
|
|
private:
|
|
FFastPathRenderingDataCleanupList* CleanupList;
|
|
};
|
|
|
|
|
|
FFastPathRenderingDataCleanupList::~FFastPathRenderingDataCleanupList()
|
|
{
|
|
for (FSlateCachedFastPathRenderingData* Data : FastPathRenderingDataToRemove)
|
|
{
|
|
delete Data;
|
|
}
|
|
}
|
|
|
|
void FFastPathRenderingDataCleanupList::Cleanup()
|
|
{
|
|
// Cached data should be destroyed in a thread safe way. If there is an rhi thread it could be reading from the data to copy it into a vertex buffer
|
|
// so delete it on the rhi thread if necessary, otherwise delete it on the render thread
|
|
ENQUEUE_RENDER_COMMAND(ClearCachedRenderingData)(
|
|
[CleanupList = this](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
if (!RHICmdList.Bypass())
|
|
{
|
|
new (RHICmdList.AllocCommand<FClearCachedRenderingDataCommand2>()) FClearCachedRenderingDataCommand2(CleanupList);
|
|
}
|
|
else
|
|
{
|
|
FClearCachedRenderingDataCommand2 Cmd(CleanupList);
|
|
Cmd.Execute(RHICmdList);
|
|
}
|
|
});
|
|
}
|