You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
-FPostProcessRectParams was still using the old FTexture2DRHIRef/GetTexture2D pattern rather than using FTextureRHIRef directly. This meant that it did not support 2DArray textures, like the VR swapchain, and caused an assert during a post-process blur on Lyra. #review-20297934 #rb Arciel.Reckman #preflight 6287e5ff9b764703c31a4077 [CL 20300846 by Jeff Fisher in ue5-main branch]
1523 lines
58 KiB
C++
1523 lines
58 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SlateRHIRenderingPolicy.h"
|
|
#include "UniformBuffer.h"
|
|
#include "Shader.h"
|
|
#include "ShowFlags.h"
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "Misc/App.h"
|
|
#include "EngineGlobals.h"
|
|
#include "RHIStaticStates.h"
|
|
#include "RHIUtilities.h"
|
|
#include "SceneView.h"
|
|
#include "SceneUtils.h"
|
|
#include "Engine/Engine.h"
|
|
#include "SlateShaders.h"
|
|
#include "Rendering/SlateRenderer.h"
|
|
#include "SlateRHIRenderer.h"
|
|
#include "SlateMaterialShader.h"
|
|
#include "SlateUTextureResource.h"
|
|
#include "SlateMaterialResource.h"
|
|
#include "SlateUpdatableBuffer.h"
|
|
#include "SlatePostProcessor.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "PipelineStateCache.h"
|
|
#include "Math/RandomStream.h"
|
|
#include "DeviceProfiles/DeviceProfile.h"
|
|
#include "DeviceProfiles/DeviceProfileManager.h"
|
|
#include "Types/SlateConstants.h"
|
|
#include "RenderGraphResources.h"
|
|
#include "SceneRenderTargetParameters.h"
|
|
|
|
extern void UpdateNoiseTextureParameters(FViewUniformShaderParameters& ViewUniformShaderParameters);
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("Update Buffers RT"), STAT_SlateUpdateBufferRTTime, STATGROUP_Slate);
|
|
DECLARE_CYCLE_STAT(TEXT("Update Buffers RT"), STAT_SlateUpdateBufferRTTimeLambda, STATGROUP_Slate);
|
|
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Num Layers"), STAT_SlateNumLayers, STATGROUP_Slate);
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Num Batches"), STAT_SlateNumBatches, STATGROUP_Slate);
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Num Vertices"), STAT_SlateVertexCount, STATGROUP_Slate);
|
|
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Clips (Scissor)"), STAT_SlateScissorClips, STATGROUP_Slate);
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Clips (Stencil)"), STAT_SlateStencilClips, STATGROUP_Slate);
|
|
|
|
#if WITH_SLATE_DEBUGGING
|
|
int32 SlateEnableDrawEvents = 1;
|
|
#else
|
|
int32 SlateEnableDrawEvents = 0;
|
|
#endif
|
|
static FAutoConsoleVariableRef CVarSlateEnableDrawEvents(TEXT("Slate.EnableDrawEvents"), SlateEnableDrawEvents, TEXT("."), ECVF_Default);
|
|
|
|
#if WITH_SLATE_DEBUGGING
|
|
int32 BatchToDraw = -1;
|
|
static FAutoConsoleVariableRef CVarSlateDrawBatchNum(TEXT("Slate.DrawBatchNum"), BatchToDraw, TEXT("."), ECVF_Default);
|
|
#endif
|
|
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
#define SLATE_DRAW_EVENT(RHICmdList, EventName) SCOPED_CONDITIONAL_DRAW_EVENT(RHICmdList, EventName, SlateEnableDrawEvents);
|
|
#else
|
|
#define SLATE_DRAW_EVENT(RHICmdList, EventName)
|
|
#endif
|
|
|
|
TAutoConsoleVariable<int32> CVarSlateAbsoluteIndices(
|
|
TEXT("Slate.AbsoluteIndices"),
|
|
0,
|
|
TEXT("0: Each element first vertex index starts at 0 (default), 1: Use absolute indices, simplifies draw call setup on RHIs that do not support BaseVertex"),
|
|
ECVF_Default
|
|
);
|
|
|
|
FSlateRHIRenderingPolicy::FSlateRHIRenderingPolicy(TSharedRef<FSlateFontServices> InSlateFontServices, TSharedRef<FSlateRHIResourceManager> InResourceManager, TOptional<int32> InitialBufferSize)
|
|
: FSlateRenderingPolicy(InSlateFontServices, 0)
|
|
, PostProcessor(new FSlatePostProcessor)
|
|
, ResourceManager(InResourceManager)
|
|
, bGammaCorrect(true)
|
|
, bApplyColorDeficiencyCorrection(true)
|
|
, InitialBufferSizeOverride(InitialBufferSize)
|
|
, LastDeviceProfile(nullptr)
|
|
{
|
|
InitResources();
|
|
}
|
|
|
|
void FSlateRHIRenderingPolicy::InitResources()
|
|
{
|
|
int32 NumVertices = 100;
|
|
|
|
if ( InitialBufferSizeOverride.IsSet() )
|
|
{
|
|
NumVertices = InitialBufferSizeOverride.GetValue();
|
|
}
|
|
else if ( GConfig )
|
|
{
|
|
int32 NumVertsInConfig = 0;
|
|
if ( GConfig->GetInt(TEXT("SlateRenderer"), TEXT("NumPreallocatedVertices"), NumVertsInConfig, GEngineIni) )
|
|
{
|
|
NumVertices = NumVertsInConfig;
|
|
}
|
|
}
|
|
|
|
// Always create a little space but never allow it to get too high
|
|
#if !SLATE_USE_32BIT_INDICES
|
|
NumVertices = FMath::Clamp(NumVertices, 100, 65535);
|
|
#else
|
|
NumVertices = FMath::Clamp(NumVertices, 100, 1000000);
|
|
#endif
|
|
|
|
UE_LOG(LogSlate, Verbose, TEXT("Allocating space for %d vertices"), NumVertices);
|
|
|
|
MasterVertexBuffer.Init(NumVertices);
|
|
MasterIndexBuffer.Init(NumVertices);
|
|
|
|
BeginInitResource(&StencilVertexBuffer);
|
|
}
|
|
|
|
void FSlateRHIRenderingPolicy::ReleaseResources()
|
|
{
|
|
MasterVertexBuffer.Destroy();
|
|
MasterIndexBuffer.Destroy();
|
|
|
|
BeginReleaseResource(&StencilVertexBuffer);
|
|
}
|
|
|
|
void FSlateRHIRenderingPolicy::BeginDrawingWindows()
|
|
{
|
|
check( IsInRenderingThread() );
|
|
}
|
|
|
|
void FSlateRHIRenderingPolicy::EndDrawingWindows()
|
|
{
|
|
check( IsInParallelRenderingThread() );
|
|
}
|
|
|
|
void FSlateRHIRenderingPolicy::BuildRenderingBuffers(FRHICommandListImmediate& RHICmdList, FSlateBatchData& InBatchData)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_SlateUpdateBufferRTTime);
|
|
|
|
// Should only be called by the rendering thread
|
|
check(IsInRenderingThread());
|
|
|
|
// Merge together batches for less draw calls
|
|
InBatchData.MergeRenderBatches();
|
|
|
|
const FSlateVertexArray& FinalVertexData = InBatchData.GetFinalVertexData();
|
|
const FSlateIndexArray& FinalIndexData = InBatchData.GetFinalIndexData();
|
|
|
|
const int32 NumVertices = FinalVertexData.Num();
|
|
const int32 NumIndices = FinalIndexData.Num();
|
|
|
|
if (InBatchData.GetRenderBatches().Num() > 0 && NumVertices > 0 && NumIndices > 0)
|
|
{
|
|
bool bShouldShrinkResources = false;
|
|
bool bAbsoluteIndices = CVarSlateAbsoluteIndices.GetValueOnRenderThread() != 0;
|
|
|
|
MasterVertexBuffer.PreFillBuffer(NumVertices, bShouldShrinkResources);
|
|
MasterIndexBuffer.PreFillBuffer(NumIndices, bShouldShrinkResources);
|
|
|
|
RHICmdList.EnqueueLambda([
|
|
VertexBuffer = MasterVertexBuffer.VertexBufferRHI.GetReference(),
|
|
IndexBuffer = MasterIndexBuffer.IndexBufferRHI.GetReference(),
|
|
&InBatchData,
|
|
bAbsoluteIndices
|
|
](FRHICommandListImmediate& InRHICmdList)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_SlateUpdateBufferRTTimeLambda);
|
|
|
|
// Note: Use "Lambda" prefix to prevent clang/gcc warnings of '-Wshadow' warning
|
|
const FSlateVertexArray& LambdaFinalVertexData = InBatchData.GetFinalVertexData();
|
|
const FSlateIndexArray& LambdaFinalIndexData = InBatchData.GetFinalIndexData();
|
|
|
|
const int32 NumBatchedVertices = LambdaFinalVertexData.Num();
|
|
const int32 NumBatchedIndices = LambdaFinalIndexData.Num();
|
|
|
|
uint32 RequiredVertexBufferSize = NumBatchedVertices * sizeof(FSlateVertex);
|
|
uint8* VertexBufferData = (uint8*)InRHICmdList.LockBuffer(VertexBuffer, 0, RequiredVertexBufferSize, RLM_WriteOnly);
|
|
|
|
uint32 RequiredIndexBufferSize = NumBatchedIndices * sizeof(SlateIndex);
|
|
uint8* IndexBufferData = (uint8*)InRHICmdList.LockBuffer(IndexBuffer, 0, RequiredIndexBufferSize, RLM_WriteOnly);
|
|
|
|
FMemory::Memcpy(VertexBufferData, LambdaFinalVertexData.GetData(), RequiredVertexBufferSize);
|
|
FMemory::Memcpy(IndexBufferData, LambdaFinalIndexData.GetData(), RequiredIndexBufferSize);
|
|
|
|
InRHICmdList.UnlockBuffer(VertexBuffer);
|
|
InRHICmdList.UnlockBuffer(IndexBuffer);
|
|
});
|
|
|
|
RHICmdList.RHIThreadFence(true);
|
|
}
|
|
|
|
checkSlow(MasterVertexBuffer.GetBufferUsageSize() <= MasterVertexBuffer.GetBufferSize());
|
|
checkSlow(MasterIndexBuffer.GetBufferUsageSize() <= MasterIndexBuffer.GetBufferSize());
|
|
|
|
SET_DWORD_STAT(STAT_SlateNumLayers, InBatchData.GetNumLayers());
|
|
SET_DWORD_STAT(STAT_SlateNumBatches, InBatchData.GetNumFinalBatches());
|
|
SET_DWORD_STAT(STAT_SlateVertexCount, InBatchData.GetFinalVertexData().Num());
|
|
}
|
|
|
|
static FSceneView* CreateSceneView( FSceneViewFamilyContext* ViewFamilyContext, FSlateBackBuffer& BackBuffer, const FMatrix& ViewProjectionMatrix )
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_Slate_CreateSceneView);
|
|
// In loading screens, the engine is NULL, so we skip out.
|
|
if (GEngine == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
FIntRect ViewRect(FIntPoint(0, 0), BackBuffer.GetSizeXY());
|
|
|
|
// make a temporary view
|
|
FSceneViewInitOptions ViewInitOptions;
|
|
ViewInitOptions.ViewFamily = ViewFamilyContext;
|
|
ViewInitOptions.SetViewRectangle(ViewRect);
|
|
ViewInitOptions.ViewOrigin = FVector::ZeroVector;
|
|
ViewInitOptions.ViewRotationMatrix = FMatrix::Identity;
|
|
ViewInitOptions.ProjectionMatrix = ViewProjectionMatrix;
|
|
ViewInitOptions.BackgroundColor = FLinearColor::Black;
|
|
ViewInitOptions.OverlayColor = FLinearColor::White;
|
|
|
|
FSceneView* View = new FSceneView( ViewInitOptions );
|
|
ViewFamilyContext->Views.Add( View );
|
|
|
|
const FIntPoint BufferSize = BackBuffer.GetSizeXY();
|
|
|
|
// Create the view's uniform buffer.
|
|
FViewUniformShaderParameters ViewUniformShaderParameters;
|
|
ViewUniformShaderParameters.VTFeedbackBuffer = GEmptyVertexBufferWithUAV->UnorderedAccessViewRHI;
|
|
|
|
View->SetupCommonViewUniformBufferParameters(
|
|
ViewUniformShaderParameters,
|
|
BufferSize,
|
|
1,
|
|
ViewRect,
|
|
View->ViewMatrices,
|
|
FViewMatrices()
|
|
);
|
|
|
|
// TODO LWC
|
|
ViewUniformShaderParameters.RelativeWorldViewOrigin = (FVector3f)View->ViewMatrices.GetViewOrigin();
|
|
|
|
|
|
ERHIFeatureLevel::Type RHIFeatureLevel = View->GetFeatureLevel();
|
|
|
|
ViewUniformShaderParameters.MobilePreviewMode =
|
|
(GIsEditor &&
|
|
(RHIFeatureLevel == ERHIFeatureLevel::ES3_1) &&
|
|
GMaxRHIFeatureLevel > ERHIFeatureLevel::ES3_1) ? 1.0f : 0.0f;
|
|
|
|
UpdateNoiseTextureParameters(ViewUniformShaderParameters);
|
|
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_Slate_CreateViewUniformBufferImmediate);
|
|
View->ViewUniformBuffer = TUniformBufferRef<FViewUniformShaderParameters>::CreateUniformBufferImmediate(ViewUniformShaderParameters, UniformBuffer_SingleFrame);
|
|
}
|
|
|
|
return View;
|
|
}
|
|
|
|
static const FName RendererModuleName("Renderer");
|
|
|
|
static bool UpdateScissorRect(
|
|
FRHICommandList& RHICmdList,
|
|
#if STATS
|
|
int32& ScissorClips,
|
|
int32& StencilClips,
|
|
#endif
|
|
uint32& StencilRef,
|
|
uint32& MaskingID,
|
|
FSlateBackBuffer& BackBuffer,
|
|
const FSlateRenderBatch& RenderBatch,
|
|
FTexture2DRHIRef& ColorTarget,
|
|
FTexture2DRHIRef& DepthStencilTarget,
|
|
const FSlateClippingState*& LastClippingState,
|
|
const FVector2D& ViewTranslation2D,
|
|
bool bSwitchVerticalAxis,
|
|
FGraphicsPipelineStateInitializer& InGraphicsPSOInit,
|
|
FSlateStencilClipVertexBuffer& StencilVertexBuffer,
|
|
const FMatrix& ViewProjection,
|
|
bool bForceStateChange)
|
|
{
|
|
check(RHICmdList.IsInsideRenderPass());
|
|
bool bDidRestartRenderpass = false;
|
|
|
|
if (RenderBatch.ClippingState != LastClippingState || bForceStateChange)
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_Slate_UpdateScissorRect);
|
|
|
|
if (RenderBatch.ClippingState)
|
|
{
|
|
const FSlateClippingState& ClipState = *RenderBatch.ClippingState;
|
|
if (ClipState.GetClippingMethod() == EClippingMethod::Scissor)
|
|
{
|
|
#if STATS
|
|
ScissorClips++;
|
|
#endif
|
|
|
|
if (bForceStateChange && MaskingID > 0)
|
|
{
|
|
// #todo-renderpasses this is very gross. If/when this gets refactored we can detect a simple clear or batch up elements by rendertarget (and other stuff)
|
|
RHICmdList.EndRenderPass();
|
|
bDidRestartRenderpass = true;
|
|
ERenderTargetActions StencilAction = IsMemorylessTexture(DepthStencilTarget) ? ERenderTargetActions::DontLoad_DontStore : ERenderTargetActions::Load_Store;
|
|
|
|
FRHIRenderPassInfo RPInfo(ColorTarget, ERenderTargetActions::Load_Store);
|
|
RPInfo.DepthStencilRenderTarget.Action = MakeDepthStencilTargetActions(ERenderTargetActions::DontLoad_DontStore, StencilAction);
|
|
RPInfo.DepthStencilRenderTarget.DepthStencilTarget = DepthStencilTarget;
|
|
RPInfo.DepthStencilRenderTarget.ExclusiveDepthStencil = FExclusiveDepthStencil::DepthNop_StencilWrite;
|
|
|
|
RHICmdList.BeginRenderPass(RPInfo, TEXT("SlateUpdateScissorRect"));
|
|
}
|
|
|
|
const FSlateClippingZone& ScissorRect = ClipState.ScissorRect.GetValue();
|
|
|
|
const FIntPoint SizeXY = BackBuffer.GetSizeXY();
|
|
const FVector2D ViewSize((float) SizeXY.X, (float) SizeXY.Y);
|
|
|
|
// Clamp scissor rect to BackBuffer size
|
|
const FVector2D TopLeft = FMath::Min(FMath::Max(FVector2D(ScissorRect.TopLeft) + ViewTranslation2D, FVector2D(0.0f, 0.0f)), ViewSize);
|
|
const FVector2D BottomRight = FMath::Min(FMath::Max(FVector2D(ScissorRect.BottomRight) + ViewTranslation2D, FVector2D(0.0f, 0.0f)), ViewSize);
|
|
|
|
if (bSwitchVerticalAxis)
|
|
{
|
|
const int32 MinY = (ViewSize.Y - BottomRight.Y);
|
|
const int32 MaxY = (ViewSize.Y - TopLeft.Y);
|
|
RHICmdList.SetScissorRect(true, TopLeft.X, MinY, BottomRight.X, MaxY);
|
|
}
|
|
else
|
|
{
|
|
RHICmdList.SetScissorRect(true, TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y);
|
|
}
|
|
|
|
// Disable depth/stencil testing by default
|
|
InGraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
StencilRef = 0;
|
|
}
|
|
else
|
|
{
|
|
#if STATS
|
|
StencilClips++;
|
|
#endif
|
|
|
|
SLATE_DRAW_EVENT(RHICmdList, StencilClipping);
|
|
|
|
check(ClipState.StencilQuads.Num() > 0);
|
|
|
|
const TArray<FSlateClippingZone>& StencilQuads = ClipState.StencilQuads;
|
|
|
|
// We're going to overflow the masking ID this time, we need to reset the MaskingID to 0.
|
|
// this will cause us to clear the stencil buffer so that we can begin fresh.
|
|
if ((MaskingID + StencilQuads.Num()) > 255)
|
|
{
|
|
MaskingID = 0;
|
|
}
|
|
|
|
// We only clear the stencil the first time, and if some how the user draws more than 255 masking quads
|
|
// in a single frame.
|
|
bool bClearStencil = false;
|
|
if (MaskingID == 0)
|
|
{
|
|
bClearStencil = true;
|
|
|
|
// We don't want there to be any scissor rect when we clear the stencil
|
|
RHICmdList.SetScissorRect(false, 0, 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
// There might be some large - useless stencils, especially in the first couple of stencils if large
|
|
// widgets that clip also contain render targets, so, by setting the scissor to the AABB of the final
|
|
// stencil, we can cut out a lot of work that can't possibly be useful.
|
|
//
|
|
// NOTE - We also round it, because if we don't it can over-eagerly slice off pixels it shouldn't.
|
|
const FSlateClippingZone& MaskQuad = StencilQuads.Last();
|
|
const FSlateRect LastStencilBoundingBox = MaskQuad.GetBoundingBox().Round();
|
|
|
|
FSlateRect ScissorRect = LastStencilBoundingBox.OffsetBy(ViewTranslation2D);
|
|
|
|
// Chosen stencil quad might have some coordinates outside the viewport.
|
|
// After turning it into a bounding box, this box must be clamped to the current viewport,
|
|
// as scissors outside the viewport don't make sense (and cause assertions to fail).
|
|
const FIntPoint BackBufferSize = BackBuffer.GetSizeXY();
|
|
ScissorRect.Left = FMath::Clamp(ScissorRect.Left, 0.0f, static_cast<float>(BackBufferSize.X));
|
|
ScissorRect.Top = FMath::Clamp(ScissorRect.Top, 0.0f, static_cast<float>(BackBufferSize.Y));
|
|
ScissorRect.Right = FMath::Clamp(ScissorRect.Right, ScissorRect.Left, static_cast<float>(BackBufferSize.X));
|
|
ScissorRect.Bottom = FMath::Clamp(ScissorRect.Bottom, ScissorRect.Top, static_cast<float>(BackBufferSize.Y));
|
|
|
|
if (bSwitchVerticalAxis)
|
|
{
|
|
const FIntPoint ViewSize = BackBuffer.GetSizeXY();
|
|
const int32 MinY = (ViewSize.Y - ScissorRect.Bottom);
|
|
const int32 MaxY = (ViewSize.Y - ScissorRect.Top);
|
|
RHICmdList.SetScissorRect(true, ScissorRect.Left, MinY, ScissorRect.Right, MaxY);
|
|
}
|
|
else
|
|
{
|
|
RHICmdList.SetScissorRect(true, ScissorRect.Left, ScissorRect.Top, ScissorRect.Right, ScissorRect.Bottom);
|
|
}
|
|
}
|
|
|
|
// Don't bother setting the render targets unless we actually need to clear them.
|
|
if (bClearStencil || bForceStateChange)
|
|
{
|
|
// #todo-renderpasses Similar to above this is gross. Would require a refactor to really fix.
|
|
RHICmdList.EndRenderPass();
|
|
bDidRestartRenderpass = true;
|
|
|
|
// Clear current stencil buffer, we use ELoad/EStore, because we need to keep the stencil around.
|
|
ERenderTargetLoadAction StencilLoadAction = bClearStencil ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad;
|
|
ERenderTargetActions StencilAction = MakeRenderTargetActions(StencilLoadAction, ERenderTargetStoreAction::EStore);
|
|
if (IsMemorylessTexture(DepthStencilTarget))
|
|
{
|
|
// We can't preserve content for memoryless targets
|
|
StencilAction = bClearStencil ? ERenderTargetActions::Clear_DontStore : ERenderTargetActions::DontLoad_DontStore;
|
|
}
|
|
|
|
FRHIRenderPassInfo RPInfo(ColorTarget, ERenderTargetActions::Load_Store);
|
|
RPInfo.DepthStencilRenderTarget.Action = MakeDepthStencilTargetActions(ERenderTargetActions::DontLoad_DontStore, StencilAction);
|
|
RPInfo.DepthStencilRenderTarget.DepthStencilTarget = DepthStencilTarget;
|
|
RPInfo.DepthStencilRenderTarget.ExclusiveDepthStencil = FExclusiveDepthStencil::DepthNop_StencilWrite;
|
|
TransitionRenderPassTargets(RHICmdList, RPInfo);
|
|
RHICmdList.BeginRenderPass(RPInfo, TEXT("SlateUpdateScissorRect_ClearStencil"));
|
|
}
|
|
|
|
FGlobalShaderMap* MaxFeatureLevelShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel);
|
|
|
|
// Set the new shaders
|
|
TShaderMapRef<FSlateMaskingVS> VertexShader(MaxFeatureLevelShaderMap);
|
|
TShaderMapRef<FSlateMaskingPS> PixelShader(MaxFeatureLevelShaderMap);
|
|
|
|
// Start by setting up the stenciling states so that we can write representations of the clipping zones into the stencil buffer only.
|
|
{
|
|
FGraphicsPipelineStateInitializer WriteMaskPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(WriteMaskPSOInit);
|
|
WriteMaskPSOInit.BlendState = TStaticBlendStateWriteMask<CW_NONE>::GetRHI();
|
|
WriteMaskPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
|
|
WriteMaskPSOInit.DepthStencilState =
|
|
TStaticDepthStencilState<
|
|
/*bEnableDepthWrite*/ false
|
|
, /*DepthTest*/ CF_Always
|
|
, /*bEnableFrontFaceStencil*/ true
|
|
, /*FrontFaceStencilTest*/ CF_Always
|
|
, /*FrontFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*FrontFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*FrontFacePassStencilOp*/ SO_Replace
|
|
, /*bEnableBackFaceStencil*/ true
|
|
, /*BackFaceStencilTest*/ CF_Always
|
|
, /*BackFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*BackFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*BackFacePassStencilOp*/ SO_Replace
|
|
, /*StencilReadMask*/ 0xFF
|
|
, /*StencilWriteMask*/ 0xFF>::GetRHI();
|
|
|
|
WriteMaskPSOInit.BoundShaderState.VertexDeclarationRHI = GSlateMaskingVertexDeclaration.VertexDeclarationRHI;
|
|
WriteMaskPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
WriteMaskPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
WriteMaskPSOInit.PrimitiveType = PT_TriangleStrip;
|
|
|
|
SetGraphicsPipelineState(RHICmdList, WriteMaskPSOInit, MaskingID + 1);
|
|
|
|
VertexShader->SetViewProjection(RHICmdList, FMatrix44f(ViewProjection));
|
|
VertexShader->SetVerticalAxisMultiplier(RHICmdList, bSwitchVerticalAxis ? -1.0f : 1.0f);
|
|
|
|
// Draw the first stencil using SO_Replace, so that we stomp any pixel with a MaskingID + 1.
|
|
{
|
|
const FSlateClippingZone& MaskQuad = StencilQuads[0];
|
|
|
|
VertexShader->SetMaskRect(RHICmdList, FVector2D(MaskQuad.TopLeft), FVector2D(MaskQuad.TopRight), FVector2D(MaskQuad.BottomLeft), FVector2D(MaskQuad.BottomRight));
|
|
|
|
RHICmdList.SetStreamSource(0, StencilVertexBuffer.VertexBufferRHI, 0);
|
|
RHICmdList.DrawPrimitive(0, 2, 1);
|
|
}
|
|
|
|
// Now setup the pipeline to use SO_SaturatedIncrement, since we've established the initial
|
|
// stencil with SO_Replace, we can safely use SO_SaturatedIncrement, to build up the stencil
|
|
// to the required mask of MaskingID + StencilQuads.Num(), thereby ensuring only the union of
|
|
// all stencils will render pixels.
|
|
{
|
|
WriteMaskPSOInit.DepthStencilState =
|
|
TStaticDepthStencilState<
|
|
/*bEnableDepthWrite*/ false
|
|
, /*DepthTest*/ CF_Always
|
|
, /*bEnableFrontFaceStencil*/ true
|
|
, /*FrontFaceStencilTest*/ CF_Always
|
|
, /*FrontFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*FrontFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*FrontFacePassStencilOp*/ SO_SaturatedIncrement
|
|
, /*bEnableBackFaceStencil*/ true
|
|
, /*BackFaceStencilTest*/ CF_Always
|
|
, /*BackFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*BackFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*BackFacePassStencilOp*/ SO_SaturatedIncrement
|
|
, /*StencilReadMask*/ 0xFF
|
|
, /*StencilWriteMask*/ 0xFF>::GetRHI();
|
|
|
|
|
|
SetGraphicsPipelineState(RHICmdList, WriteMaskPSOInit, 0);
|
|
|
|
VertexShader->SetViewProjection(RHICmdList, FMatrix44f(ViewProjection));
|
|
VertexShader->SetVerticalAxisMultiplier(RHICmdList, bSwitchVerticalAxis ? -1.0f : 1.0f);
|
|
}
|
|
}
|
|
|
|
MaskingID += StencilQuads.Num();
|
|
|
|
// Next write the number of quads representing the number of clipping zones have on top of each other.
|
|
for (int32 MaskIndex = 1; MaskIndex < StencilQuads.Num(); MaskIndex++)
|
|
{
|
|
const FSlateClippingZone& MaskQuad = StencilQuads[MaskIndex];
|
|
|
|
VertexShader->SetMaskRect(RHICmdList, FVector2D(MaskQuad.TopLeft), FVector2D(MaskQuad.TopRight), FVector2D(MaskQuad.BottomLeft), FVector2D(MaskQuad.BottomRight));
|
|
|
|
RHICmdList.SetStreamSource(0, StencilVertexBuffer.VertexBufferRHI, 0);
|
|
RHICmdList.DrawPrimitive(0, 2, 1);
|
|
}
|
|
|
|
// Setup the stenciling state to be read only now, disable depth writes, and restore the color buffer
|
|
// because we're about to go back to rendering widgets "normally", but with the added effect that now
|
|
// we have the stencil buffer bound with a bunch of clipping zones rendered into it.
|
|
{
|
|
FRHIDepthStencilState* DSMaskRead =
|
|
TStaticDepthStencilState<
|
|
/*bEnableDepthWrite*/ false
|
|
, /*DepthTest*/ CF_Always
|
|
, /*bEnableFrontFaceStencil*/ true
|
|
, /*FrontFaceStencilTest*/ CF_Equal
|
|
, /*FrontFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*FrontFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*FrontFacePassStencilOp*/ SO_Keep
|
|
, /*bEnableBackFaceStencil*/ true
|
|
, /*BackFaceStencilTest*/ CF_Equal
|
|
, /*BackFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*BackFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*BackFacePassStencilOp*/ SO_Keep
|
|
, /*StencilReadMask*/ 0xFF
|
|
, /*StencilWriteMask*/ 0xFF>::GetRHI();
|
|
|
|
InGraphicsPSOInit.DepthStencilState = DSMaskRead;
|
|
|
|
// We set a StencilRef equal to the number of stenciling/clipping masks,
|
|
// so unless the pixel we're rendering two is on top of a stencil pixel with the same number
|
|
// it's going to get rejected, thereby clipping everything except for the cross-section of
|
|
// all the stenciling quads.
|
|
StencilRef = MaskingID;
|
|
}
|
|
}
|
|
|
|
RHICmdList.ApplyCachedRenderTargets(InGraphicsPSOInit);
|
|
}
|
|
else
|
|
{
|
|
RHICmdList.SetScissorRect(false, 0, 0, 0, 0);
|
|
|
|
// Disable depth/stencil testing
|
|
InGraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
StencilRef = 0;
|
|
}
|
|
|
|
LastClippingState = RenderBatch.ClippingState;
|
|
}
|
|
|
|
return bDidRestartRenderpass;
|
|
}
|
|
|
|
void FSlateRHIRenderingPolicy::DrawElements(
|
|
FRHICommandListImmediate& RHICmdList,
|
|
FSlateBackBuffer& BackBuffer,
|
|
FTexture2DRHIRef& ColorTarget,
|
|
FTexture2DRHIRef& PostProcessTexture,
|
|
FTexture2DRHIRef& DepthStencilTarget,
|
|
int32 FirstBatchIndex,
|
|
const TArray<FSlateRenderBatch>& RenderBatches,
|
|
const FSlateRenderingParams& Params)
|
|
{
|
|
// Should only be called by the rendering thread
|
|
check(IsInRenderingThread());
|
|
check(RHICmdList.IsInsideRenderPass());
|
|
|
|
// Cache the TextureLODGroups so that we can look them up for texture filtering.
|
|
if (UDeviceProfileManager::DeviceProfileManagerSingleton)
|
|
{
|
|
if (UDeviceProfile* Profile = UDeviceProfileManager::Get().GetActiveProfile())
|
|
{
|
|
if (Profile != LastDeviceProfile)
|
|
{
|
|
TextureLODGroups = Profile->GetTextureLODSettings()->TextureLODGroups;
|
|
LastDeviceProfile = Profile;
|
|
}
|
|
}
|
|
}
|
|
|
|
IRendererModule& RendererModule = FModuleManager::GetModuleChecked<IRendererModule>(RendererModuleName);
|
|
|
|
static const FEngineShowFlags DefaultShowFlags(ESFIM_Game);
|
|
|
|
// Disable gammatization when back buffer is in float 16 format.
|
|
// Note that the final editor rendering won't compare 1:1 with 8/10 bit RGBA since blending
|
|
// of "manually" gammatized values is wrong as there is no de-gammatization of the destination buffer
|
|
// and re-gammatization of the resulting blending operation in the 8/10 bit RGBA path.
|
|
// For Editor running in HDR then the gamma needs to be 2.2 and have a float back buffer format.
|
|
|
|
const float EngineGamma = (!GIsEditor && (BackBuffer.GetRenderTargetTexture()->GetFormat() == PF_FloatRGBA) && (Params.bIsHDR==false)) ? 1.0 : GEngine ? GEngine->GetDisplayGamma() : 2.2f;
|
|
const float DisplayGamma = bGammaCorrect ? EngineGamma : 1.0f;
|
|
const float DisplayContrast = GSlateContrast;
|
|
|
|
int32 ScissorClips = 0;
|
|
int32 StencilClips = 0;
|
|
|
|
// In order to support MaterialParameterCollections, we need to create multiple FSceneViews for
|
|
// each possible Scene that we encounter. The following code creates these as separate arrays, where the first
|
|
// N entries map directly to entries from ActiveScenes. The final entry is added to represent the absence of a
|
|
// valid scene, i.e. a -1 in the SceneIndex parameter of the batch.
|
|
int32 NumScenes = ResourceManager->GetSceneCount() + 1;
|
|
TArray<FSceneView*, TInlineAllocator<3> > SceneViews;
|
|
SceneViews.SetNum(NumScenes);
|
|
TArray<FSceneViewFamilyContext*, TInlineAllocator<3> > SceneViewFamilyContexts;
|
|
SceneViewFamilyContexts.SetNum(NumScenes);
|
|
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_Slate_CreateScenes);
|
|
for (int32 i = 0; i < ResourceManager->GetSceneCount(); i++)
|
|
{
|
|
SceneViewFamilyContexts[i] = new FSceneViewFamilyContext
|
|
(
|
|
FSceneViewFamily::ConstructionValues
|
|
(
|
|
&BackBuffer,
|
|
ResourceManager->GetSceneAt(i),
|
|
DefaultShowFlags
|
|
)
|
|
.SetTime(Params.Time)
|
|
.SetGammaCorrection(DisplayGamma)
|
|
.SetRealtimeUpdate(true)
|
|
);
|
|
SceneViews[i] = CreateSceneView(SceneViewFamilyContexts[i], BackBuffer, FMatrix(Params.ViewProjectionMatrix));
|
|
}
|
|
|
|
SceneViewFamilyContexts[NumScenes - 1] = new FSceneViewFamilyContext
|
|
(
|
|
FSceneViewFamily::ConstructionValues
|
|
(
|
|
&BackBuffer,
|
|
nullptr,
|
|
DefaultShowFlags
|
|
)
|
|
.SetTime(Params.Time)
|
|
.SetGammaCorrection(DisplayGamma)
|
|
.SetRealtimeUpdate(true)
|
|
);
|
|
SceneViews[NumScenes - 1] = CreateSceneView(SceneViewFamilyContexts[NumScenes - 1], BackBuffer, FMatrix(Params.ViewProjectionMatrix));
|
|
}
|
|
|
|
TShaderMapRef<FSlateElementVS> GlobalVertexShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
|
|
|
|
FSamplerStateRHIRef BilinearClamp = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
|
|
TSlateElementVertexBuffer<FSlateVertex>* VertexBufferPtr = &MasterVertexBuffer;
|
|
FSlateElementIndexBuffer* IndexBufferPtr = &MasterIndexBuffer;
|
|
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
|
|
const FSlateRenderDataHandle* LastHandle = nullptr;
|
|
|
|
const ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel;
|
|
FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(FeatureLevel);
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
FRandomStream BatchColors(1337);
|
|
#endif
|
|
|
|
const bool bAbsoluteIndices = CVarSlateAbsoluteIndices.GetValueOnRenderThread() != 0;
|
|
const bool bSwitchVerticalAxis = Params.bAllowSwitchVerticalAxis && RHINeedsToSwitchVerticalAxis(GShaderPlatformForFeatureLevel[GMaxRHIFeatureLevel]);
|
|
|
|
// This variable tracks the last clipping state, so that if multiple batches have the same clipping state, we don't have to do any work.
|
|
const FSlateClippingState* LastClippingState;
|
|
|
|
// This is the stenciling ref variable we set any time we draw, so that any stencil comparisons use the right mask id.
|
|
uint32 StencilRef = 0;
|
|
// This is an accumulating maskID that we use to track the between batch usage of the stencil buffer, when at 0, or over 255
|
|
// this signals that we need to reset the masking ID, and clear the stencil buffer, as we've used up the available scratch range.
|
|
uint32 MaskingID = 0;
|
|
|
|
RHICmdList.SetScissorRect(false, 0, 0, 0, 0);
|
|
// Disable depth/stencil testing by default
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
|
|
FVector2D ViewTranslation2D = Params.ViewOffset;
|
|
|
|
// Draw each element
|
|
#if WITH_SLATE_DEBUGGING
|
|
int32 NextRenderBatchIndex = BatchToDraw == -1 ? FirstBatchIndex : BatchToDraw;
|
|
#else
|
|
int32 NextRenderBatchIndex = FirstBatchIndex;
|
|
#endif
|
|
|
|
|
|
/*
|
|
#todo-renderpasses This loop ends up with ugly logic.
|
|
CustomDrawers will draw in their own renderpass. So we must remember to reopen the renderpass with the passed in Color/DepthStencil targets.
|
|
*/
|
|
while (NextRenderBatchIndex != INDEX_NONE)
|
|
{
|
|
VertexBufferPtr = &MasterVertexBuffer;
|
|
IndexBufferPtr = &MasterIndexBuffer;
|
|
|
|
if (!RHICmdList.IsInsideRenderPass())
|
|
{
|
|
// Restart the renderpass since the CustomDrawer or post-process may have changed it in last iteration
|
|
FRHIRenderPassInfo RPInfo(BackBuffer.GetRenderTargetTexture(), ERenderTargetActions::Load_Store);
|
|
RPInfo.DepthStencilRenderTarget.DepthStencilTarget = DepthStencilTarget;
|
|
if (DepthStencilTarget)
|
|
{
|
|
RPInfo.DepthStencilRenderTarget.Action = IsMemorylessTexture(DepthStencilTarget) ? EDepthStencilTargetActions::DontLoad_DontStore : EDepthStencilTargetActions::LoadDepthStencil_StoreDepthStencil;
|
|
RPInfo.DepthStencilRenderTarget.ExclusiveDepthStencil = FExclusiveDepthStencil::DepthWrite_StencilWrite;
|
|
}
|
|
else
|
|
{
|
|
RPInfo.DepthStencilRenderTarget.Action = EDepthStencilTargetActions::DontLoad_DontStore;
|
|
RPInfo.DepthStencilRenderTarget.ExclusiveDepthStencil = FExclusiveDepthStencil::DepthNop_StencilNop;
|
|
}
|
|
TransitionRenderPassTargets(RHICmdList, RPInfo);
|
|
RHICmdList.BeginRenderPass(RPInfo, TEXT("RestartingSlateDrawElements"));
|
|
|
|
// Something may have messed with the viewport size so set it back to the full target.
|
|
RHICmdList.SetViewport(0, 0, 0, BackBuffer.GetSizeXY().X, BackBuffer.GetSizeXY().Y, 0.0f);
|
|
|
|
// Re-apply render target states to the PSO initializer, since we've changed the depth/stencil target.
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
}
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
FLinearColor BatchColor = FLinearColor(BatchColors.GetUnitVector());
|
|
#endif
|
|
const FSlateRenderBatch& RenderBatch = RenderBatches[NextRenderBatchIndex];
|
|
|
|
NextRenderBatchIndex = RenderBatch.NextBatchIndex;
|
|
|
|
#if WITH_SLATE_DEBUGGING
|
|
if (BatchToDraw != -1)
|
|
{
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
const FSlateShaderResource* ShaderResource = RenderBatch.ShaderResource;
|
|
const ESlateBatchDrawFlag DrawFlags = RenderBatch.DrawFlags;
|
|
const ESlateDrawEffect DrawEffects = RenderBatch.DrawEffects;
|
|
const ESlateShader ShaderType = RenderBatch.ShaderType;
|
|
const FShaderParams& ShaderParams = RenderBatch.ShaderParams;
|
|
|
|
if (EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::Wireframe))
|
|
{
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Wireframe, CM_None, false>::GetRHI();
|
|
}
|
|
else
|
|
{
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None, false>::GetRHI();
|
|
}
|
|
|
|
if (!RenderBatch.CustomDrawer)
|
|
{
|
|
FMatrix DynamicOffset = FTranslationMatrix::Make(FVector(RenderBatch.DynamicOffset.X, RenderBatch.DynamicOffset.Y, 0));
|
|
const FMatrix ViewProjection = DynamicOffset * FMatrix(Params.ViewProjectionMatrix);
|
|
|
|
UpdateScissorRect(
|
|
RHICmdList,
|
|
#if STATS
|
|
ScissorClips,
|
|
StencilClips,
|
|
#endif
|
|
StencilRef,
|
|
MaskingID,
|
|
BackBuffer,
|
|
RenderBatch,
|
|
ColorTarget,
|
|
DepthStencilTarget,
|
|
LastClippingState,
|
|
ViewTranslation2D,
|
|
bSwitchVerticalAxis,
|
|
GraphicsPSOInit,
|
|
StencilVertexBuffer,
|
|
ViewProjection,
|
|
false);
|
|
|
|
const uint32 PrimitiveCount = RenderBatch.DrawPrimitiveType == ESlateDrawPrimitive::LineList ? RenderBatch.NumIndices / 2 : RenderBatch.NumIndices / 3;
|
|
check(ShaderResource == nullptr || !ShaderResource->Debug_IsDestroyed());
|
|
ESlateShaderResource::Type ResourceType = ShaderResource ? ShaderResource->GetType() : ESlateShaderResource::Invalid;
|
|
if (ResourceType != ESlateShaderResource::Material && ShaderType != ESlateShader::PostProcess)
|
|
{
|
|
check(RHICmdList.IsInsideRenderPass());
|
|
check(RenderBatch.NumIndices > 0);
|
|
TShaderRef<FSlateElementPS> PixelShader;
|
|
|
|
const bool bUseInstancing = RenderBatch.InstanceCount > 1 && RenderBatch.InstanceData != nullptr;
|
|
check(bUseInstancing == false);
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
TShaderRef<FSlateDebugBatchingPS> BatchingPixelShader;
|
|
if (CVarShowSlateBatching.GetValueOnRenderThread() != 0)
|
|
{
|
|
BatchingPixelShader = TShaderMapRef<FSlateDebugBatchingPS>(ShaderMap);
|
|
PixelShader = BatchingPixelShader;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
bool bIsVirtualTexture = false;
|
|
|
|
if ((ShaderResource != nullptr) && (ResourceType == ESlateShaderResource::TextureObject))
|
|
{
|
|
FSlateBaseUTextureResource* TextureObjectResource = const_cast<FSlateBaseUTextureResource*>(static_cast<const FSlateBaseUTextureResource*>(ShaderResource));
|
|
|
|
if (UTexture* TextureObj = TextureObjectResource->GetTextureObject())
|
|
{
|
|
bIsVirtualTexture = TextureObj->IsCurrentlyVirtualTextured();
|
|
}
|
|
}
|
|
|
|
PixelShader = GetTexturePixelShader(ShaderMap, ShaderType, DrawEffects, bIsVirtualTexture);
|
|
}
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
if (CVarShowSlateBatching.GetValueOnRenderThread() != 0)
|
|
{
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI();
|
|
}
|
|
else if (CVarShowSlateOverdraw.GetValueOnRenderThread() != 0)
|
|
{
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_InverseSourceAlpha>::GetRHI();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
GraphicsPSOInit.BlendState =
|
|
EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::NoBlending)
|
|
? TStaticBlendState<>::GetRHI()
|
|
: (EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::PreMultipliedAlpha)
|
|
? TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI()
|
|
: TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI())
|
|
;
|
|
}
|
|
|
|
if (EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::Wireframe) || Params.bWireFrame)
|
|
{
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Wireframe, CM_None, false>::GetRHI();
|
|
|
|
if (Params.bWireFrame)
|
|
{
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None, false>::GetRHI();
|
|
}
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GSlateVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GlobalVertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
GraphicsPSOInit.PrimitiveType = GetRHIPrimitiveType(RenderBatch.DrawPrimitiveType);
|
|
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef);
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
if (CVarShowSlateBatching.GetValueOnRenderThread() != 0)
|
|
{
|
|
BatchingPixelShader->SetBatchColor(RHICmdList, BatchColor);
|
|
}
|
|
#endif
|
|
|
|
FRHISamplerState* SamplerState = BilinearClamp;
|
|
FRHITexture* TextureRHI = GWhiteTexture->TextureRHI;
|
|
bool bIsVirtualTexture = false;
|
|
FTextureResource* TextureResource = nullptr;
|
|
|
|
if (ShaderResource)
|
|
{
|
|
ETextureSamplerFilter Filter = ETextureSamplerFilter::Bilinear;
|
|
|
|
if (ResourceType == ESlateShaderResource::TextureObject)
|
|
{
|
|
FSlateBaseUTextureResource* TextureObjectResource = (FSlateBaseUTextureResource*)ShaderResource;
|
|
if (UTexture* TextureObj = TextureObjectResource->GetTextureObject())
|
|
{
|
|
TextureObjectResource->CheckForStaleResources();
|
|
|
|
TextureRHI = TextureObjectResource->AccessRHIResource();
|
|
|
|
// This can upset some RHIs, so use transparent black texture until it's valid.
|
|
// these can be temporarily invalid when recreating them / invalidating their streaming
|
|
// state.
|
|
if (TextureRHI == nullptr)
|
|
{
|
|
// We use transparent black here, because it's about to become valid - probably, and flashing white
|
|
// wouldn't be ideal.
|
|
TextureRHI = GTransparentBlackTexture->TextureRHI;
|
|
}
|
|
|
|
TextureResource = TextureObj->GetResource();
|
|
|
|
Filter = GetSamplerFilter(TextureObj);
|
|
bIsVirtualTexture = TextureObj->IsCurrentlyVirtualTextured();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FRHITexture* NativeTextureRHI = ((TSlateTexture<FTexture2DRHIRef>*)ShaderResource)->GetTypedResource();
|
|
// Atlas textures that have no content are never initialized but null textures are invalid on many platforms.
|
|
TextureRHI = NativeTextureRHI ? NativeTextureRHI : (FRHITexture*)GWhiteTexture->TextureRHI;
|
|
}
|
|
|
|
if (EnumHasAllFlags(DrawFlags, (ESlateBatchDrawFlag::TileU | ESlateBatchDrawFlag::TileV)))
|
|
{
|
|
switch (Filter)
|
|
{
|
|
case ETextureSamplerFilter::Point:
|
|
SamplerState = TStaticSamplerState<SF_Point, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicPoint:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicPoint, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::Trilinear:
|
|
SamplerState = TStaticSamplerState<SF_Trilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicLinear:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicLinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
default:
|
|
SamplerState = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
}
|
|
}
|
|
else if (EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::TileU))
|
|
{
|
|
switch (Filter)
|
|
{
|
|
case ETextureSamplerFilter::Point:
|
|
SamplerState = TStaticSamplerState<SF_Point, AM_Wrap, AM_Clamp, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicPoint:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicPoint, AM_Wrap, AM_Clamp, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::Trilinear:
|
|
SamplerState = TStaticSamplerState<SF_Trilinear, AM_Wrap, AM_Clamp, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicLinear:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicLinear, AM_Wrap, AM_Clamp, AM_Wrap>::GetRHI();
|
|
break;
|
|
default:
|
|
SamplerState = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Clamp, AM_Wrap>::GetRHI();
|
|
break;
|
|
}
|
|
}
|
|
else if (EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::TileV))
|
|
{
|
|
switch (Filter)
|
|
{
|
|
case ETextureSamplerFilter::Point:
|
|
SamplerState = TStaticSamplerState<SF_Point, AM_Clamp, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicPoint:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicPoint, AM_Clamp, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::Trilinear:
|
|
SamplerState = TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicLinear:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicLinear, AM_Clamp, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
default:
|
|
SamplerState = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (Filter)
|
|
{
|
|
case ETextureSamplerFilter::Point:
|
|
SamplerState = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicPoint:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicPoint, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::Trilinear:
|
|
SamplerState = TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicLinear:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicLinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
break;
|
|
default:
|
|
SamplerState = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
GlobalVertexShader->SetViewProjection(RHICmdList, FMatrix44f(ViewProjection));
|
|
GlobalVertexShader->SetVerticalAxisMultiplier(RHICmdList, bSwitchVerticalAxis ? -1.0f : 1.0f);
|
|
|
|
if (bIsVirtualTexture && (TextureResource != nullptr))
|
|
{
|
|
PixelShader->SetVirtualTextureParameters(RHICmdList, static_cast<FVirtualTexture2DResource*>(TextureResource));
|
|
}
|
|
else
|
|
{
|
|
PixelShader->SetTexture(RHICmdList, TextureRHI, SamplerState);
|
|
}
|
|
|
|
PixelShader->SetShaderParams(RHICmdList, ShaderParams);
|
|
const float FinalGamma = EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::ReverseGamma) ? (1.0f / EngineGamma) : EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::NoGamma) ? 1.0f : DisplayGamma;
|
|
const float FinalContrast = EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::NoGamma) ? 1 : DisplayContrast;
|
|
PixelShader->SetDisplayGammaAndInvertAlphaAndContrast(RHICmdList, FinalGamma, EnumHasAllFlags(DrawEffects, ESlateDrawEffect::InvertAlpha) ? 1.0f : 0.0f, FinalContrast);
|
|
}
|
|
|
|
{
|
|
// for RHIs that can't handle VertexOffset, we need to offset the stream source each time
|
|
RHICmdList.SetStreamSource(0, VertexBufferPtr->VertexBufferRHI, RenderBatch.VertexOffset * sizeof(FSlateVertex));
|
|
RHICmdList.DrawIndexedPrimitive(IndexBufferPtr->IndexBufferRHI, 0, 0, RenderBatch.NumVertices, RenderBatch.IndexOffset, PrimitiveCount, RenderBatch.InstanceCount);
|
|
}
|
|
}
|
|
else if (GEngine && ShaderResource && ShaderResource->GetType() == ESlateShaderResource::Material && ShaderType != ESlateShader::PostProcess)
|
|
{
|
|
SLATE_DRAW_EVENT(RHICmdList, MaterialBatch);
|
|
check(RHICmdList.IsInsideRenderPass());
|
|
|
|
check(RenderBatch.NumIndices > 0);
|
|
// Note: This code is only executed if the engine is loaded (in early loading screens attempting to use a material is unsupported
|
|
int32 ActiveSceneIndex = (int32)RenderBatch.SceneIndex;
|
|
|
|
// We are assuming at this point that the SceneIndex from the batch is either -1, meaning no scene or a valid scene.
|
|
// We set up the "no scene" option as the last SceneView in the array above.
|
|
if (RenderBatch.SceneIndex == -1)
|
|
{
|
|
ActiveSceneIndex = NumScenes - 1;
|
|
}
|
|
else if (RenderBatch.SceneIndex >= ResourceManager->GetSceneCount())
|
|
{
|
|
// Ideally we should never hit this scenario, but given that Paragon may be using cached
|
|
// render batches and is running into this daily, for this branch we should
|
|
// just ignore the scene if the index is invalid. Note that the
|
|
// MaterialParameterCollections will not be correct for this scene, should they be
|
|
// used.
|
|
ActiveSceneIndex = NumScenes - 1;
|
|
#if UE_BUILD_DEBUG && WITH_EDITOR
|
|
UE_LOG(LogSlate, Error, TEXT("Invalid scene index in batch: %d of %d known scenes!"), RenderBatch.SceneIndex, ResourceManager->GetSceneCount());
|
|
#endif
|
|
}
|
|
|
|
// Handle the case where we skipped out early above
|
|
if (SceneViews[ActiveSceneIndex] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FSceneView& ActiveSceneView = *SceneViews[ActiveSceneIndex];
|
|
|
|
FSlateMaterialResource* MaterialShaderResource = (FSlateMaterialResource*)ShaderResource;
|
|
if (const FMaterialRenderProxy* MaterialRenderProxy = MaterialShaderResource->GetRenderProxy())
|
|
{
|
|
MaterialShaderResource->CheckForStaleResources();
|
|
|
|
const bool bUseInstancing = RenderBatch.InstanceCount > 0 && RenderBatch.InstanceData != nullptr;
|
|
|
|
TShaderRef<FSlateMaterialShaderVS> VertexShader;
|
|
TShaderRef<FSlateMaterialShaderPS> PixelShader;
|
|
|
|
FMaterialShaderTypes ShaderTypesToGet;
|
|
ChooseMaterialShaderTypes(ShaderType, bUseInstancing, ShaderTypesToGet);
|
|
const FMaterial* EffectiveMaterial = nullptr;
|
|
|
|
const ERHIFeatureLevel::Type ViewFeatureLevel = ActiveSceneView.GetFeatureLevel();
|
|
while(MaterialRenderProxy)
|
|
{
|
|
const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(ViewFeatureLevel);
|
|
FMaterialShaders Shaders;
|
|
if (Material && Material->TryGetShaders(ShaderTypesToGet, nullptr, Shaders))
|
|
{
|
|
EffectiveMaterial = Material;
|
|
Shaders.TryGetVertexShader(VertexShader);
|
|
Shaders.TryGetPixelShader(PixelShader);
|
|
break;
|
|
}
|
|
|
|
MaterialRenderProxy = MaterialRenderProxy->GetFallback(ViewFeatureLevel);
|
|
}
|
|
|
|
FRHIUniformBuffer* SceneTextureUniformBuffer = GetSceneTextureExtracts().GetUniformBuffer();
|
|
|
|
if (VertexShader.IsValid() && PixelShader.IsValid() && SceneTextureUniformBuffer)
|
|
{
|
|
check(EffectiveMaterial);
|
|
const FUniformBufferStaticBindings StaticUniformBuffers(SceneTextureUniformBuffer);
|
|
SCOPED_UNIFORM_BUFFER_STATIC_BINDINGS(RHICmdList, StaticUniformBuffers);
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
if (CVarShowSlateBatching.GetValueOnRenderThread() != 0)
|
|
{
|
|
TShaderMapRef<FSlateDebugBatchingPS> BatchingPixelShader(ShaderMap);
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = bUseInstancing ? GSlateInstancedVertexDeclaration.VertexDeclarationRHI : GSlateVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GlobalVertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = BatchingPixelShader.GetPixelShader();
|
|
|
|
BatchingPixelShader->SetBatchColor(RHICmdList, BatchColor);
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_InverseSourceAlpha>::GetRHI();
|
|
}
|
|
else if (CVarShowSlateOverdraw.GetValueOnRenderThread() != 0)
|
|
{
|
|
TShaderMapRef<FSlateDebugOverdrawPS> OverdrawPixelShader(ShaderMap);
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = bUseInstancing ? GSlateInstancedVertexDeclaration.VertexDeclarationRHI : GSlateVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GlobalVertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = OverdrawPixelShader.GetPixelShader();
|
|
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_InverseSourceAlpha>::GetRHI();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
PixelShader->SetBlendState(GraphicsPSOInit, EffectiveMaterial);
|
|
FSlateShaderResource* MaskResource = MaterialShaderResource->GetTextureMaskResource();
|
|
if (MaskResource && (EffectiveMaterial->GetBlendMode() == EBlendMode::BLEND_Opaque || EffectiveMaterial->GetBlendMode() == EBlendMode::BLEND_Masked))
|
|
{
|
|
// Font materials require some form of translucent blending
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_InverseDestAlpha, BF_One>::GetRHI();
|
|
}
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = bUseInstancing ? GSlateInstancedVertexDeclaration.VertexDeclarationRHI : GSlateVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
GraphicsPSOInit.PrimitiveType = GetRHIPrimitiveType(RenderBatch.DrawPrimitiveType);
|
|
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef);
|
|
|
|
{
|
|
VertexShader->SetViewProjection(RHICmdList, FMatrix44f(ViewProjection));
|
|
VertexShader->SetVerticalAxisMultiplier(RHICmdList, bSwitchVerticalAxis ? -1.0f : 1.0f);
|
|
VertexShader->SetMaterialShaderParameters(RHICmdList, ActiveSceneView, MaterialRenderProxy, EffectiveMaterial);
|
|
|
|
PixelShader->SetParameters(RHICmdList, ActiveSceneView, MaterialRenderProxy, EffectiveMaterial, ShaderParams);
|
|
const float FinalGamma = EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::ReverseGamma) ? 1.0f / EngineGamma : EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::NoGamma) ? 1.0f : DisplayGamma;
|
|
const float FinalContrast = EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::NoGamma) ? 1 : DisplayContrast;
|
|
PixelShader->SetDisplayGammaAndContrast(RHICmdList, FinalGamma, FinalContrast);
|
|
const bool bDrawDisabled = EnumHasAllFlags(DrawEffects, ESlateDrawEffect::DisabledEffect);
|
|
PixelShader->SetDrawFlags(RHICmdList, bDrawDisabled);
|
|
|
|
if (MaskResource)
|
|
{
|
|
FTexture2DRHIRef TextureRHI;
|
|
TextureRHI = ((TSlateTexture<FTexture2DRHIRef>*)MaskResource)->GetTypedResource();
|
|
|
|
PixelShader->SetAdditionalTexture(RHICmdList, TextureRHI, BilinearClamp);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
if (bUseInstancing)
|
|
{
|
|
uint32 InstanceCount = RenderBatch.InstanceCount;
|
|
|
|
RenderBatch.InstanceData->BindStreamSource(RHICmdList, 1, RenderBatch.InstanceOffset);
|
|
|
|
// for RHIs that can't handle VertexOffset, we need to offset the stream source each time
|
|
|
|
RHICmdList.SetStreamSource(0, VertexBufferPtr->VertexBufferRHI, RenderBatch.VertexOffset * sizeof(FSlateVertex));
|
|
RHICmdList.DrawIndexedPrimitive(IndexBufferPtr->IndexBufferRHI, 0, 0, RenderBatch.NumVertices, RenderBatch.IndexOffset, PrimitiveCount, InstanceCount);
|
|
}
|
|
else
|
|
{
|
|
RHICmdList.SetStreamSource(1, nullptr, 0);
|
|
|
|
// for RHIs that can't handle VertexOffset, we need to offset the stream source each time
|
|
RHICmdList.SetStreamSource(0, VertexBufferPtr->VertexBufferRHI, RenderBatch.VertexOffset * sizeof(FSlateVertex));
|
|
RHICmdList.DrawIndexedPrimitive(IndexBufferPtr->IndexBufferRHI, 0, 0, RenderBatch.NumVertices, RenderBatch.IndexOffset, PrimitiveCount, 1);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ShaderType == ESlateShader::PostProcess)
|
|
{
|
|
SLATE_DRAW_EVENT(RHICmdList, PostProcess);
|
|
RHICmdList.EndRenderPass();
|
|
|
|
const FVector4f QuadPositionData = ShaderParams.PixelParams;
|
|
|
|
FPostProcessRectParams RectParams;
|
|
RectParams.SourceTexture = PostProcessTexture;
|
|
RectParams.SourceRect = FSlateRect(0, 0, PostProcessTexture->GetSizeX(), PostProcessTexture->GetSizeY());
|
|
RectParams.DestRect = FSlateRect(QuadPositionData.X, QuadPositionData.Y, QuadPositionData.Z, QuadPositionData.W);
|
|
RectParams.SourceTextureSize = PostProcessTexture->GetSizeXY();
|
|
RectParams.CornerRadius = (FVector4)ShaderParams.PixelParams3;
|
|
|
|
RectParams.RestoreStateFunc = [&](FRHICommandListImmediate&InRHICmdList, FGraphicsPipelineStateInitializer& InGraphicsPSOInit) {
|
|
return UpdateScissorRect(
|
|
InRHICmdList,
|
|
#if STATS
|
|
ScissorClips,
|
|
StencilClips,
|
|
#endif
|
|
StencilRef,
|
|
MaskingID,
|
|
BackBuffer,
|
|
RenderBatch,
|
|
ColorTarget,
|
|
DepthStencilTarget,
|
|
LastClippingState,
|
|
ViewTranslation2D,
|
|
bSwitchVerticalAxis,
|
|
InGraphicsPSOInit,
|
|
StencilVertexBuffer,
|
|
FMatrix(Params.ViewProjectionMatrix),
|
|
true);
|
|
};
|
|
|
|
RectParams.StencilRef = StencilRef;
|
|
|
|
FBlurRectParams BlurParams;
|
|
BlurParams.KernelSize = ShaderParams.PixelParams2.X;
|
|
BlurParams.Strength = ShaderParams.PixelParams2.Y;
|
|
BlurParams.DownsampleAmount = ShaderParams.PixelParams2.Z;
|
|
|
|
PostProcessor->BlurRect(RHICmdList, RendererModule, BlurParams, RectParams);
|
|
|
|
check(RHICmdList.IsOutsideRenderPass());
|
|
// Render pass for slate elements will be restarted on a next loop iteration if any
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ICustomSlateElement* CustomDrawer = RenderBatch.CustomDrawer;
|
|
if (CustomDrawer)
|
|
{
|
|
// CustomDrawers will change the rendertarget. So we must close any outstanding renderpasses.
|
|
// Render pass for slate elements will be restarted on a next loop iteration if any
|
|
RHICmdList.EndRenderPass();
|
|
|
|
SLATE_DRAW_EVENT(RHICmdList, CustomDrawer);
|
|
|
|
// Disable scissor rect. A previous draw element may have had one
|
|
RHICmdList.SetScissorRect(false, 0, 0, 0, 0);
|
|
LastClippingState = nullptr;
|
|
|
|
// This element is custom and has no Slate geometry. Tell it to render itself now
|
|
CustomDrawer->DrawRenderThread(RHICmdList, &BackBuffer.GetRenderTargetTexture());
|
|
|
|
//We reset the maskingID here because otherwise the RT might not get re-set in the lines above see: if (bClearStencil || bForceStateChange)
|
|
MaskingID = 0;
|
|
}
|
|
} // CustomDrawer
|
|
}
|
|
|
|
// Don't do color correction on iOS or Android, we don't have the GPU overhead for it.
|
|
#if !(PLATFORM_IOS || PLATFORM_ANDROID)
|
|
if (bApplyColorDeficiencyCorrection && GSlateColorDeficiencyType != EColorVisionDeficiency::NormalVision && GSlateColorDeficiencySeverity > 0)
|
|
{
|
|
if (RHICmdList.IsInsideRenderPass())
|
|
{
|
|
RHICmdList.EndRenderPass();
|
|
}
|
|
|
|
FPostProcessRectParams RectParams;
|
|
RectParams.SourceTexture = BackBuffer.GetRenderTargetTexture();
|
|
RectParams.SourceRect = FSlateRect(0, 0, BackBuffer.GetSizeXY().X, BackBuffer.GetSizeXY().Y);
|
|
RectParams.DestRect = FSlateRect(0, 0, BackBuffer.GetSizeXY().X, BackBuffer.GetSizeXY().Y);
|
|
RectParams.SourceTextureSize = BackBuffer.GetSizeXY();
|
|
RectParams.CornerRadius = FVector4(0, 0, 0, 0);
|
|
|
|
PostProcessor->ColorDeficiency(RHICmdList, RendererModule, RectParams);
|
|
|
|
FRHIRenderPassInfo RPInfo(ColorTarget, ERenderTargetActions::Load_Store);
|
|
RPInfo.DepthStencilRenderTarget.DepthStencilTarget = DepthStencilTarget;
|
|
|
|
// @todo refactor this.
|
|
// ColorDeficiency has self-contained renderpasses. To avoid starting an empty renderpass we do not
|
|
// restart the renderpass here.
|
|
}
|
|
#endif
|
|
|
|
// Disable scissor rect we no longer need this.
|
|
RHICmdList.SetScissorRect(false, 0, 0, 0, 0);
|
|
// Disable depth/stencil testing once we're done also.
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
|
|
for (int i = 0; i < NumScenes; i++)
|
|
{
|
|
// Don't need to delete SceneViews b/c the SceneViewFamily will delete it when it goes away.
|
|
delete SceneViewFamilyContexts[i];
|
|
}
|
|
|
|
SceneViews.Empty();
|
|
SceneViewFamilyContexts.Empty();
|
|
|
|
INC_DWORD_STAT_BY(STAT_SlateScissorClips, ScissorClips);
|
|
INC_DWORD_STAT_BY(STAT_SlateStencilClips, StencilClips);
|
|
|
|
// Disable scissor rect.
|
|
// This fixes drawing on Metal when the last drawn element used a valid scissor rect
|
|
RHICmdList.SetScissorRect(false, 0, 0, 0, 0);
|
|
}
|
|
|
|
ETextureSamplerFilter FSlateRHIRenderingPolicy::GetSamplerFilter(const UTexture* Texture) const
|
|
{
|
|
// Default to point filtering.
|
|
ETextureSamplerFilter Filter = ETextureSamplerFilter::Point;
|
|
|
|
switch (Texture->Filter)
|
|
{
|
|
case TF_Nearest:
|
|
Filter = ETextureSamplerFilter::Point;
|
|
break;
|
|
case TF_Bilinear:
|
|
Filter = ETextureSamplerFilter::Bilinear;
|
|
break;
|
|
case TF_Trilinear:
|
|
Filter = ETextureSamplerFilter::Trilinear;
|
|
break;
|
|
|
|
// TF_Default
|
|
default:
|
|
// Use LOD group value to find proper filter setting.
|
|
if (Texture->LODGroup < TextureLODGroups.Num())
|
|
{
|
|
Filter = TextureLODGroups[Texture->LODGroup].Filter;
|
|
}
|
|
}
|
|
|
|
return Filter;
|
|
}
|
|
|
|
TShaderRef<FSlateElementPS> FSlateRHIRenderingPolicy::GetTexturePixelShader( FGlobalShaderMap* ShaderMap, ESlateShader ShaderType, ESlateDrawEffect DrawEffects, bool bIsVirtualTexture )
|
|
{
|
|
TShaderRef<FSlateElementPS> PixelShader;
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
if ( CVarShowSlateOverdraw.GetValueOnRenderThread() != 0 )
|
|
{
|
|
PixelShader = TShaderMapRef<FSlateDebugOverdrawPS>(ShaderMap);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
const bool bDrawDisabled = EnumHasAllFlags( DrawEffects, ESlateDrawEffect::DisabledEffect );
|
|
const bool bUseTextureAlpha = !EnumHasAllFlags( DrawEffects, ESlateDrawEffect::IgnoreTextureAlpha );
|
|
|
|
if ( bDrawDisabled )
|
|
{
|
|
switch ( ShaderType )
|
|
{
|
|
default:
|
|
case ESlateShader::Default:
|
|
if ( bUseTextureAlpha )
|
|
{
|
|
if ( bIsVirtualTexture )
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, true, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, true, false> >(ShaderMap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( bIsVirtualTexture )
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, false, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, false, false> >(ShaderMap);
|
|
}
|
|
}
|
|
break;
|
|
case ESlateShader::Border:
|
|
if ( bUseTextureAlpha )
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Border, true, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Border, true, false> >(ShaderMap);
|
|
}
|
|
break;
|
|
case ESlateShader::GrayscaleFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::GrayscaleFont, true> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::ColorFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::ColorFont, true> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::LineSegment:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::LineSegment, true> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::RoundedBox:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::RoundedBox, true> >(ShaderMap);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch ( ShaderType )
|
|
{
|
|
default:
|
|
case ESlateShader::Default:
|
|
if ( bUseTextureAlpha )
|
|
{
|
|
if ( bIsVirtualTexture )
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, true, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, true, false> >(ShaderMap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( bIsVirtualTexture )
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, false, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, false, false> >(ShaderMap);
|
|
}
|
|
}
|
|
break;
|
|
case ESlateShader::Border:
|
|
if ( bUseTextureAlpha )
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Border, false, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Border, false, false> >(ShaderMap);
|
|
}
|
|
break;
|
|
case ESlateShader::GrayscaleFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::GrayscaleFont, false> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::ColorFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::ColorFont, false> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::LineSegment:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::LineSegment, false> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::RoundedBox:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::RoundedBox, false> >(ShaderMap);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef PixelShaderLookupTable
|
|
|
|
return PixelShader;
|
|
}
|
|
|
|
void FSlateRHIRenderingPolicy::ChooseMaterialShaderTypes(ESlateShader ShaderType, bool bUseInstancing, FMaterialShaderTypes& OutShaderTypes)
|
|
{
|
|
switch (ShaderType)
|
|
{
|
|
case ESlateShader::RoundedBox:
|
|
// Todo rounded box not supported in materials currently, intentional fall-through to Default
|
|
case ESlateShader::Default:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::Default>>();
|
|
break;
|
|
case ESlateShader::Border:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::Border>>();
|
|
break;
|
|
case ESlateShader::GrayscaleFont:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::GrayscaleFont>>();
|
|
break;
|
|
case ESlateShader::ColorFont:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::ColorFont>>();
|
|
break;
|
|
case ESlateShader::Custom:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::Custom>>();
|
|
break;
|
|
default:
|
|
checkf(false, TEXT("Unsupported Slate shader type for use with materials"));
|
|
break;
|
|
}
|
|
|
|
if (bUseInstancing)
|
|
{
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderVS<true>>();
|
|
}
|
|
else
|
|
{
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderVS<false>>();
|
|
}
|
|
}
|
|
|
|
EPrimitiveType FSlateRHIRenderingPolicy::GetRHIPrimitiveType(ESlateDrawPrimitive SlateType)
|
|
{
|
|
switch(SlateType)
|
|
{
|
|
case ESlateDrawPrimitive::LineList:
|
|
return PT_LineList;
|
|
case ESlateDrawPrimitive::TriangleList:
|
|
default:
|
|
return PT_TriangleList;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
void FSlateRHIRenderingPolicy::AddSceneAt(FSceneInterface* Scene, int32 Index)
|
|
{
|
|
ResourceManager->AddSceneAt(Scene, Index);
|
|
}
|
|
|
|
void FSlateRHIRenderingPolicy::ClearScenes()
|
|
{
|
|
ResourceManager->ClearScenes();
|
|
}
|
|
|
|
void FSlateRHIRenderingPolicy::FlushGeneratedResources()
|
|
{
|
|
PostProcessor->ReleaseRenderTargets();
|
|
}
|
|
|