// 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 "GlobalRenderResources.h" #include "Interfaces/SlateRHIRenderingPolicyInterface.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 "Materials/MaterialRenderProxy.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 "RenderGraphUtils.h" #include "SceneRenderTargetParameters.h" #include "SceneInterface.h" #include "Containers/StaticBitArray.h" #include "MeshPassProcessor.h" #include "PSOPrecacheValidation.h" #include "VT/VirtualTextureFeedbackResource.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); static int32 GSlateMaterialPSOPrecache = 1; static FAutoConsoleVariableRef CVarGSlateMaterialPSOPrecache( TEXT("r.PSOPrecache.SlateMaterials"), GSlateMaterialPSOPrecache, TEXT("Precache all possible required PSOs for loaded Slate Materials."), ECVF_ReadOnly); static const TCHAR* SlateGlobalPSOCollectorName = TEXT("SlateGlobalPSOCollector"); static const TCHAR* SlateMaterialPSOCollectorName = TEXT("SlateMaterialPSOCollector"); #if WITH_SLATE_DEBUGGING static int32 GSlateEnableDrawEvents = 1; #else static int32 GSlateEnableDrawEvents = 0; #endif static FAutoConsoleVariableRef CVarGSlateEnableDrawEvents(TEXT("Slate.EnableDrawEvents"), GSlateEnableDrawEvents, TEXT("."), ECVF_Default); #define WITH_SLATE_DRAW_EVENTS !(UE_BUILD_SHIPPING || UE_BUILD_TEST) #if WITH_SLATE_DRAW_EVENTS #define SLATE_DRAW_EVENT( RHICmdList, EventName ) SCOPED_CONDITIONAL_DRAW_EVENT( RHICmdList, EventName, (GSlateEnableDrawEvents != 0)); #define SLATE_DRAW_EVENTF(RHICmdList, EventName, Format, ...) SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventName, (GSlateEnableDrawEvents != 0), Format, ##__VA_ARGS__); #else #define SLATE_DRAW_EVENT( RHICmdList, EventName ) #define SLATE_DRAW_EVENTF(RHICmdList, EventName, Format, ...) #endif #if WITH_SLATE_VISUALIZERS extern TAutoConsoleVariable CVarShowSlateOverdraw; extern TAutoConsoleVariable CVarShowSlateBatching; #endif ////////////////////////////////////////////////////////////////////////// FSlateRHIRenderingPolicy::FSlateRHIRenderingPolicy(TSharedRef InSlateFontServices, TSharedRef InResourceManager) : FSlateRenderingPolicy(InSlateFontServices, 0) , ResourceManager(InResourceManager) {} void FSlateRHIRenderingPolicy::AddSceneAt(FSceneInterface* Scene, int32 Index) { ResourceManager->AddSceneAt(Scene, Index); } void FSlateRHIRenderingPolicy::ClearScenes() { ResourceManager->ClearScenes(); } ////////////////////////////////////////////////////////////////////////// TConstArrayView GetTextureLODGroups() { if (UDeviceProfileManager::DeviceProfileManagerSingleton) { if (UDeviceProfile* Profile = UDeviceProfileManager::DeviceProfileManagerSingleton->GetActiveProfile()) { return Profile->GetTextureLODSettings()->TextureLODGroups; } } return {}; } ETextureSamplerFilter GetSamplerFilter(TConstArrayView TextureLODGroups, const UTexture* Texture) { // 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; } FRHISamplerState* GetSamplerState(ESlateBatchDrawFlag DrawFlags, ETextureSamplerFilter Filter) { FRHISamplerState* SamplerState = nullptr; if (EnumHasAllFlags(DrawFlags, (ESlateBatchDrawFlag::TileU | ESlateBatchDrawFlag::TileV))) { switch (Filter) { case ETextureSamplerFilter::Point: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::AnisotropicPoint: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::Trilinear: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::AnisotropicLinear: SamplerState = TStaticSamplerState::GetRHI(); break; default: SamplerState = TStaticSamplerState::GetRHI(); break; } } else if (EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::TileU)) { switch (Filter) { case ETextureSamplerFilter::Point: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::AnisotropicPoint: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::Trilinear: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::AnisotropicLinear: SamplerState = TStaticSamplerState::GetRHI(); break; default: SamplerState = TStaticSamplerState::GetRHI(); break; } } else if (EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::TileV)) { switch (Filter) { case ETextureSamplerFilter::Point: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::AnisotropicPoint: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::Trilinear: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::AnisotropicLinear: SamplerState = TStaticSamplerState::GetRHI(); break; default: SamplerState = TStaticSamplerState::GetRHI(); break; } } else { switch (Filter) { case ETextureSamplerFilter::Point: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::AnisotropicPoint: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::Trilinear: SamplerState = TStaticSamplerState::GetRHI(); break; case ETextureSamplerFilter::AnisotropicLinear: SamplerState = TStaticSamplerState::GetRHI(); break; default: SamplerState = TStaticSamplerState::GetRHI(); break; } } return SamplerState; } /** Returns the pixel shader that should be used for the specified ShaderType and DrawEffects */ TShaderRef GetTexturePixelShader(FGlobalShaderMap* ShaderMap, ESlateShader ShaderType, ESlateDrawEffect DrawEffects, bool bUseTextureGrayscale, bool bIsVirtualTexture) { TShaderRef PixelShader; #if WITH_SLATE_VISUALIZERS if (CVarShowSlateOverdraw.GetValueOnAnyThread() != 0) { PixelShader = TShaderMapRef(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) { if (bUseTextureGrayscale) { PixelShader = TShaderMapRef >(ShaderMap); } else { PixelShader = TShaderMapRef >(ShaderMap); } } else { if (bUseTextureGrayscale) { PixelShader = TShaderMapRef >(ShaderMap); } else { PixelShader = TShaderMapRef >(ShaderMap); } } } else { if (bIsVirtualTexture) { if (bUseTextureGrayscale) { PixelShader = TShaderMapRef >(ShaderMap); } else { PixelShader = TShaderMapRef >(ShaderMap); } } else { if (bUseTextureGrayscale) { PixelShader = TShaderMapRef >(ShaderMap); } else { PixelShader = TShaderMapRef >(ShaderMap); } } } break; case ESlateShader::Border: if ( bUseTextureAlpha ) { PixelShader = TShaderMapRef >(ShaderMap); } else { PixelShader = TShaderMapRef >(ShaderMap); } break; case ESlateShader::GrayscaleFont: PixelShader = TShaderMapRef >(ShaderMap); break; case ESlateShader::ColorFont: PixelShader = TShaderMapRef >(ShaderMap); break; case ESlateShader::LineSegment: PixelShader = TShaderMapRef >(ShaderMap); break; case ESlateShader::RoundedBox: PixelShader = TShaderMapRef >(ShaderMap); break; case ESlateShader::SdfFont: PixelShader = TShaderMapRef >(ShaderMap); break; case ESlateShader::MsdfFont: PixelShader = TShaderMapRef >(ShaderMap); break; } } else { switch (ShaderType) { default: case ESlateShader::Default: if (bUseTextureAlpha) { if (bIsVirtualTexture) { if (bUseTextureGrayscale) { PixelShader = TShaderMapRef >(ShaderMap); } else { PixelShader = TShaderMapRef >(ShaderMap); } } else { if (bUseTextureGrayscale) { PixelShader = TShaderMapRef >(ShaderMap); } else { PixelShader = TShaderMapRef >(ShaderMap); } } } else { if (bIsVirtualTexture) { if (bUseTextureGrayscale) { PixelShader = TShaderMapRef >(ShaderMap); } else { PixelShader = TShaderMapRef >(ShaderMap); } } else { if (bUseTextureGrayscale) { PixelShader = TShaderMapRef >(ShaderMap); } else { PixelShader = TShaderMapRef >(ShaderMap); } } } break; case ESlateShader::Border: if (bUseTextureAlpha) { PixelShader = TShaderMapRef >(ShaderMap); } else { PixelShader = TShaderMapRef >(ShaderMap); } break; case ESlateShader::GrayscaleFont: PixelShader = TShaderMapRef >(ShaderMap); break; case ESlateShader::ColorFont: PixelShader = TShaderMapRef >(ShaderMap); break; case ESlateShader::LineSegment: PixelShader = TShaderMapRef >(ShaderMap); break; case ESlateShader::RoundedBox: PixelShader = TShaderMapRef >(ShaderMap); break; case ESlateShader::SdfFont: PixelShader = TShaderMapRef >(ShaderMap); break; case ESlateShader::MsdfFont: PixelShader = TShaderMapRef >(ShaderMap); break; } } } return PixelShader; } bool ChooseMaterialShaderTypes(ESlateShader ShaderType, bool bUseInstancing, FMaterialShaderTypes& OutShaderTypes) { switch (ShaderType) { case ESlateShader::Default: OutShaderTypes.AddShaderType>(); break; case ESlateShader::Border: OutShaderTypes.AddShaderType>(); break; case ESlateShader::GrayscaleFont: OutShaderTypes.AddShaderType>(); break; case ESlateShader::ColorFont: OutShaderTypes.AddShaderType>(); break; case ESlateShader::Custom: OutShaderTypes.AddShaderType>(); break; case ESlateShader::RoundedBox: OutShaderTypes.AddShaderType>(); break; case ESlateShader::SdfFont: OutShaderTypes.AddShaderType>(); break; case ESlateShader::MsdfFont: OutShaderTypes.AddShaderType>(); break; default: return false; } if (bUseInstancing) { OutShaderTypes.AddShaderType>(); } else { OutShaderTypes.AddShaderType>(); } return true; } EPrimitiveType GetRHIPrimitiveType(ESlateDrawPrimitive SlateType) { switch(SlateType) { case ESlateDrawPrimitive::LineList: return PT_LineList; case ESlateDrawPrimitive::TriangleList: default: return PT_TriangleList; } }; FRHIBlendState* GetMaterialBlendState(FSlateShaderResource* TextureMaskResource, const FMaterial* Material) { if (TextureMaskResource && IsOpaqueOrMaskedBlendMode(*Material)) { // Font materials require some form of translucent blending return TStaticBlendState::GetRHI(); } else { switch (Material->GetBlendMode()) { default: case BLEND_Opaque: return TStaticBlendState<>::GetRHI(); case BLEND_Masked: return TStaticBlendState<>::GetRHI(); case BLEND_Translucent: return TStaticBlendState::GetRHI(); case BLEND_Additive: // Add to the existing scene color return TStaticBlendState::GetRHI(); case BLEND_Modulate: // Modulate with the existing scene color return TStaticBlendState::GetRHI(); case BLEND_AlphaComposite: // Blend with existing scene color. New color is already pre-multiplied by alpha. return TStaticBlendState::GetRHI(); case BLEND_AlphaHoldout: // Blend by holding out the matte shape of the source alpha return TStaticBlendState::GetRHI(); }; } } ////////////////////////////////////////////////////////////////////////// FSlateElementsBuffers BuildSlateElementsBuffers(FRDGBuilder& GraphBuilder, FSlateBatchData& BatchData) { FSlateElementsBuffers ElementsBuffers; if (BatchData.GetRenderBatches().IsEmpty()) { return ElementsBuffers; } { const FSlateVertexArray& Data = BatchData.GetFinalVertexData(); FRDGBufferDesc BufferDesc; BufferDesc.Usage = EBufferUsageFlags::VertexBuffer | EBufferUsageFlags::Volatile; BufferDesc.BytesPerElement = sizeof(Data[0]); BufferDesc.NumElements = BatchData.GetMaxNumFinalVertices(); if (BufferDesc.NumElements > 0) { ElementsBuffers.VertexBuffer = GraphBuilder.CreateBuffer(BufferDesc, TEXT("SlateElementsVertexBuffer")); GraphBuilder.QueueBufferUpload(ElementsBuffers.VertexBuffer, Data.GetData(), Data.Num() * BufferDesc.BytesPerElement, ERDGInitialDataFlags::NoCopy); } } { const FSlateIndexArray& Data = BatchData.GetFinalIndexData(); FRDGBufferDesc BufferDesc; BufferDesc.Usage = EBufferUsageFlags::IndexBuffer | EBufferUsageFlags::Volatile; BufferDesc.BytesPerElement = sizeof(Data[0]); BufferDesc.NumElements = BatchData.GetMaxNumFinalIndices(); if (BufferDesc.NumElements > 0) { ElementsBuffers.IndexBuffer = GraphBuilder.CreateBuffer(BufferDesc, TEXT("SlateElementIndexBuffer")); GraphBuilder.QueueBufferUpload(ElementsBuffers.IndexBuffer, Data.GetData(), Data.Num() * BufferDesc.BytesPerElement, ERDGInitialDataFlags::NoCopy); } } SET_DWORD_STAT(STAT_SlateNumLayers, BatchData.GetNumLayers()); SET_DWORD_STAT(STAT_SlateNumBatches, BatchData.GetNumFinalBatches()); SET_DWORD_STAT(STAT_SlateVertexCount, BatchData.GetFinalVertexData().Num()); return ElementsBuffers; } ////////////////////////////////////////////////////////////////////////// BEGIN_UNIFORM_BUFFER_STRUCT(FSlateViewUniformParameters, ) SHADER_PARAMETER(FMatrix44f, ViewProjection) END_UNIFORM_BUFFER_STRUCT() IMPLEMENT_STATIC_UNIFORM_BUFFER_SLOT(SlateView); IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FSlateViewUniformParameters, "SlateView", SlateView); struct FSlateSceneViewAllocateInputs { FIntPoint TextureExtent = FIntPoint::ZeroValue; FIntRect ViewRect; FMatrix44f ViewProjectionMatrix = FMatrix44f::Identity; FIntPoint CursorPosition = FIntPoint::ZeroValue; FGameTime Time; float ViewportScaleUI = 1.0f; }; struct FSlateSceneView { const FSceneInterface* Scene = nullptr; ERHIFeatureLevel::Type FeatureLevel = ERHIFeatureLevel::Num; }; class FSlateSceneViewAllocator { RDG_FRIEND_ALLOCATOR_FRIEND(FSlateSceneViewAllocator); public: static FSlateSceneViewAllocator* Create(FRDGBuilder& GraphBuilder, FSlateRHIResourceManager& ResourceManager, const FSlateSceneViewAllocateInputs& Inputs) { return GraphBuilder.AllocObject(ResourceManager, Inputs); } const TUniformBufferRef& GetViewUniformBuffer(const FSlateSceneView* View) const { return UniformBuffers[(int32)View->FeatureLevel]; } const FSlateSceneView* BeginAllocateSceneView(FRDGBuilder& GraphBuilder, int32 SceneViewIndex) { if (!SceneViews.IsValidIndex(SceneViewIndex)) { SceneViewIndex = SceneViewWithNullSceneIndex; } const FSlateSceneView& SceneView = SceneViews[SceneViewIndex]; const ERHIFeatureLevel::Type FeatureLevel = SceneView.FeatureLevel; if (TUniformBufferRef& UniformBuffer = UniformBuffers[(int32)FeatureLevel]; !UniformBuffer) { UniformBuffer = CreateUniformBuffer(FeatureLevel, AllocateInputs); } return &SceneView; } private: FSlateSceneViewAllocator(FSlateRHIResourceManager& ResourceManager, const FSlateSceneViewAllocateInputs& Inputs) : SceneViewWithNullSceneIndex(ResourceManager.GetSceneCount()) , NumScenes(SceneViewWithNullSceneIndex + 1) , AllocateInputs(Inputs) { SceneViews.SetNum(NumScenes); SceneViews.Last().FeatureLevel = GMaxRHIFeatureLevel; for (int32 Index = 0; Index < SceneViewWithNullSceneIndex; ++Index) { const FSceneInterface* Scene = ResourceManager.GetSceneAt(Index); SceneViews[Index].Scene = Scene; SceneViews[Index].FeatureLevel = Scene->GetFeatureLevel(); } } static TUniformBufferRef CreateUniformBuffer(ERHIFeatureLevel::Type FeatureLevel, const FSlateSceneViewAllocateInputs& Inputs) { TRACE_CPUPROFILER_EVENT_SCOPE(FSlateSceneViewAllocator::CreateUniformBuffer); static const FEngineShowFlags DefaultShowFlags(ESFIM_Game); FIntRect ViewRect = Inputs.ViewRect; // The window we are rendering to might not have a viewport, so use the full output instead. if (ViewRect.IsEmpty()) { ViewRect.Max = Inputs.TextureExtent; } FViewMatrices::FMinimalInitializer Initializer; Initializer.ProjectionMatrix = FMatrix(Inputs.ViewProjectionMatrix); Initializer.ConstrainedViewRect = ViewRect; const FViewMatrices ViewMatrices(Initializer); const FSetupViewUniformParametersInputs SetupViewUniformParameterInputs = { .EngineShowFlags = &DefaultShowFlags , .UnscaledViewRect = ViewRect , .Time = Inputs.Time , .CursorPosition = Inputs.CursorPosition }; FViewUniformShaderParameters ViewUniformShaderParameters; SetupCommonViewUniformBufferParameters(ViewUniformShaderParameters, Inputs.TextureExtent, 1, ViewRect, ViewMatrices, ViewMatrices, SetupViewUniformParameterInputs); // Update Viewport Scale UI from any external sources (Material editor, UMG zoom scale / etc). ViewUniformShaderParameters.ViewportScaleUI = Inputs.ViewportScaleUI; // Always Update cursor position in realtime for slate ViewUniformShaderParameters.CursorPosition = Inputs.CursorPosition; // Slate materials need this scale to be positive, otherwise it can fail in querying scene textures (e.g., custom stencil) ViewUniformShaderParameters.BufferToSceneTextureScale = FVector2f(1.0f, 1.0f); ViewUniformShaderParameters.MobilePreviewMode = (FeatureLevel == ERHIFeatureLevel::ES3_1) && GMaxRHIFeatureLevel > ERHIFeatureLevel::ES3_1 ? 1.0f : 0.0f; UpdateNoiseTextureParameters(ViewUniformShaderParameters); VirtualTexture::FFeedbackShaderParams VirtualTextureFeedbackShaderParams; VirtualTexture::GetFeedbackShaderParams(VirtualTextureFeedbackShaderParams); VirtualTexture::UpdateViewUniformShaderParameters(VirtualTextureFeedbackShaderParams, ViewUniformShaderParameters); return TUniformBufferRef::CreateUniformBufferImmediate(ViewUniformShaderParameters, UniformBuffer_SingleFrame); } const int32 SceneViewWithNullSceneIndex; const int32 NumScenes; const FSlateSceneViewAllocateInputs AllocateInputs; TArray SceneViews; TStaticArray, (int32)ERHIFeatureLevel::Num> UniformBuffers; }; ////////////////////////////////////////////////////////////////////////// bool GetSlateClippingPipelineState(const FSlateClippingOp* ClippingStateOp, FRHIDepthStencilState*& OutDepthStencilState, uint8& OutStencilRef) { if (ClippingStateOp && ClippingStateOp->Method == EClippingMethod::Stencil) { // 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. OutDepthStencilState = 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(); // Set a StencilRef equal to the number of stenciling/clipping masks, so unless the pixel we're rendering // to 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. OutStencilRef = ClippingStateOp->MaskingId + ClippingStateOp->Data_Stencil.Zones.Num(); return true; } else { OutDepthStencilState = TStaticDepthStencilState::GetRHI(); OutStencilRef = 0; return false; } } void SetSlateClipping(FRHICommandList& RHICmdList, const FSlateClippingOp* ClippingStateOp, FIntRect ViewportRect) { check(RHICmdList.IsInsideRenderPass()); if (ClippingStateOp) { const FVector2f ElementOffset = ClippingStateOp->Offset; const auto ClampRectToViewport = [ElementOffset, ViewportRect] (FSlateRect ScissorRect) { ScissorRect.Left = FMath::Clamp(ScissorRect.Left + ElementOffset.X, (float)ViewportRect.Min.X, (float)ViewportRect.Max.X); ScissorRect.Top = FMath::Clamp(ScissorRect.Top + ElementOffset.Y, (float)ViewportRect.Min.Y, (float)ViewportRect.Max.Y); ScissorRect.Right = FMath::Clamp(ScissorRect.Right + ElementOffset.X, ScissorRect.Left, (float)ViewportRect.Max.X); ScissorRect.Bottom = FMath::Clamp(ScissorRect.Bottom + ElementOffset.Y, ScissorRect.Top, (float)ViewportRect.Max.Y); return ScissorRect; }; if (ClippingStateOp->Method == EClippingMethod::Scissor) { const FSlateRect ScissorRect = ClampRectToViewport(ClippingStateOp->Data_Scissor.Rect); RHICmdList.SetScissorRect(true, ScissorRect.Left, ScissorRect.Top, ScissorRect.Right, ScissorRect.Bottom); } else { check(ClippingStateOp->Method == EClippingMethod::Stencil); const TConstArrayView Zones = ClippingStateOp->Data_Stencil.Zones; check(Zones.Num() > 0); // 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. We also round it, because if we // don't it can over-eagerly slice off pixels it shouldn't. const FSlateRect ScissorRect = ClampRectToViewport(Zones.Last().GetBoundingBox().Round()); RHICmdList.SetScissorRect(true, ScissorRect.Left, ScissorRect.Top, ScissorRect.Right, ScissorRect.Bottom); const uint8 MaskingId = ClippingStateOp->MaskingId; FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(GMaxRHIShaderPlatform); TShaderMapRef VertexShader(ShaderMap); TShaderMapRef PixelShader(ShaderMap); // 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::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; // Draw the first stencil using SO_Replace, so that we stomp any pixel with a MaskingID + 1. SetGraphicsPipelineState(RHICmdList, WriteMaskPSOInit, MaskingId + 1); // Simple 2D orthographic projection from screen space to NDC space. const FVector2f A ( 2.0f / ViewportRect.Width(), 2.0f / -ViewportRect.Height() ); const FVector2f B ( (ViewportRect.Min.X + ViewportRect.Max.X) / -ViewportRect.Width(), (ViewportRect.Min.Y + ViewportRect.Max.Y) / ViewportRect.Height() ); const auto TransformVertex = [A, B, ElementOffset](FVector2f P) { return FVector2f((P.X + ElementOffset.X) * A.X + B.X, (P.Y + ElementOffset.Y) * A.Y + B.Y); }; const auto SetMaskingParameters = [VertexShader, TransformVertex] (FRHIBatchedShaderParameters& BatchedParameters, const FSlateClippingZone& Zone) { FSlateMaskingVS::FParameters Parameters; Parameters.MaskRectPacked[0] = FVector4f(TransformVertex(Zone.TopLeft), TransformVertex(Zone.TopRight)); Parameters.MaskRectPacked[1] = FVector4f(TransformVertex(Zone.BottomLeft), TransformVertex(Zone.BottomRight)); SetShaderParameters(BatchedParameters, VertexShader, Parameters); }; { FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters(); SetMaskingParameters(BatchedParameters, Zones[0]); RHICmdList.SetBatchedShaderParameters(VertexShader.GetVertexShader(), BatchedParameters); RHICmdList.SetStreamSource(0, GSlateStencilClipVertexBuffer.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); // Next write the number of quads representing the number of clipping zones have on top of each other. for (int32 MaskIndex = 1; MaskIndex < Zones.Num(); MaskIndex++) { FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters(); SetMaskingParameters(BatchedParameters, Zones[MaskIndex]); RHICmdList.SetBatchedShaderParameters(VertexShader.GetVertexShader(), BatchedParameters); RHICmdList.SetStreamSource(0, GSlateStencilClipVertexBuffer.VertexBufferRHI, 0); RHICmdList.DrawPrimitive(0, 2, 1); } } } else { RHICmdList.SetScissorRect(false, 0.0f, 0.0f, 0.0f, 0.0f); } } enum class ESlateClippingStencilAction : uint8 { None, Write, Clear }; struct FSlateClippingCreateContext { uint32 NumStencils = 0; uint32 NumScissors = 0; uint32 MaskingId = 0; ESlateClippingStencilAction StencilAction = ESlateClippingStencilAction::None; }; const FSlateClippingOp* CreateSlateClipping(FRDGBuilder& GraphBuilder, const FVector2f ElementsOffset, const FSlateClippingState* ClippingState, FSlateClippingCreateContext& Context) { Context.StencilAction = ESlateClippingStencilAction::None; if (ClippingState) { if (ClippingState->GetClippingMethod() == EClippingMethod::Scissor) { Context.NumScissors++; const FSlateClippingZone& ScissorRect = ClippingState->ScissorRect.GetValue(); return FSlateClippingOp::Scissor(GraphBuilder, ElementsOffset, FSlateRect(ScissorRect.TopLeft.X, ScissorRect.TopLeft.Y, ScissorRect.BottomRight.X, ScissorRect.BottomRight.Y)); } else { Context.NumStencils++; TConstArrayView StencilQuads = ClippingState->StencilQuads; check(StencilQuads.Num() > 0); // Reset the masking ID back to zero if stencil is going to overflow. if (Context.MaskingId + StencilQuads.Num() > 255) { Context.MaskingId = 0; } // Mark stencil for clear when the masking id is 0. Context.StencilAction = Context.MaskingId == 0 ? ESlateClippingStencilAction::Clear : ESlateClippingStencilAction::Write; const FSlateClippingOp* Op = FSlateClippingOp::Stencil(GraphBuilder, ElementsOffset, StencilQuads, Context.MaskingId); Context.MaskingId += StencilQuads.Num(); return Op; } } return nullptr; } ////////////////////////////////////////////////////////////////////////// enum class ESlateRenderBatchType { CustomDrawer, PostProcess, Primitive, MAX }; inline ESlateRenderBatchType GetSlateRenderBatchType(const FSlateRenderBatch& DrawBatch) { if (DrawBatch.CustomDrawer != nullptr) { return ESlateRenderBatchType::CustomDrawer; } if (DrawBatch.ShaderType == ESlateShader::PostProcess) { return ESlateRenderBatchType::PostProcess; } return ESlateRenderBatchType::Primitive; } class FSlateDrawShaderBindings : public FMeshDrawSingleShaderBindings { FSlateDrawShaderBindings(const TShaderRef& InShader, const FMeshDrawShaderBindingsLayout& InLayout, uint8* InData) : FMeshDrawSingleShaderBindings(InLayout, InData) , Shader(InShader) {} public: static FSlateDrawShaderBindings* Create(FRDGBuilder& GraphBuilder, const TShaderRef& Shader) { const FMeshDrawShaderBindingsLayout Layout(Shader); const uint32 DataSize = Layout.GetDataSizeBytes(); uint8* Data = (uint8*)GraphBuilder.Alloc(DataSize); FMemory::Memzero(Data, DataSize); return new (GraphBuilder.Alloc(sizeof(FSlateDrawShaderBindings))) FSlateDrawShaderBindings(Shader, Layout, Data); } void SetOnCommandList(FRHICommandList& RHICmdList) const { FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters(); FReadOnlyMeshDrawSingleShaderBindings::SetShaderBindings(BatchedParameters, FReadOnlyMeshDrawSingleShaderBindings(*this)); RHICmdList.SetBatchedShaderParameters(Shader.GetGraphicsShader(), BatchedParameters); } TShaderRef Shader; }; struct FSlateRenderBatchOp { FSlateRenderBatchOp* Next; const FSlateRenderBatch* RenderBatch; const FSlateClippingOp* ClippingStateOp; FSlateDrawShaderBindings* VertexBindings; FSlateDrawShaderBindings* PixelBindings; FRHIBuffer* InstanceBuffer; FRHIBlendState* BlendState; ESlateShaderResource::Type ShaderResourceType; #if WITH_SLATE_DRAW_EVENTS const FString* MaterialName; #endif }; ////////////////////////////////////////////////////////////////////////// struct FSlateRenderBatchCreateInputs { FGlobalShaderMap* ShaderMap; FSlateSceneViewAllocator* SceneViewAllocator; TConstArrayView TextureLODGroups; float DisplayGamma; float DisplayContrast; float EngineGamma; #if WITH_SLATE_VISUALIZERS FLinearColor BatchColor; #endif }; struct FSlateRenderBatchDrawState { const FSlateClippingOp* LastClippingOp = nullptr; FGraphicsPipelineStateInitializer GraphicsPSOInit; uint8 StencilRef = 0; }; FSlateRenderBatchOp* CreateSlateRenderBatchOp( FRDGBuilder& GraphBuilder, const FSlateRenderBatchCreateInputs& Inputs, const FSlateRenderBatch* RenderBatch, const FSlateClippingOp* ClippingStateOp) { 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; check(ShaderResource == nullptr || !ShaderResource->Debug_IsDestroyed()); const ESlateShaderResource::Type ResourceType = ShaderResource ? ShaderResource->GetType() : ESlateShaderResource::Invalid; const bool bUseInstancing = RenderBatch->InstanceCount > 0 && RenderBatch->InstanceData != nullptr; const float FinalGamma = EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::ReverseGamma) ? (1.0f / Inputs.EngineGamma) : EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::NoGamma) ? 1.0f : Inputs.DisplayGamma; const float FinalContrast = EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::NoGamma) ? 1 : Inputs.DisplayContrast; FRHIBlendState* BlendState = nullptr; FSlateDrawShaderBindings* PixelBindings = nullptr; FSlateDrawShaderBindings* VertexBindings = nullptr; #if WITH_SLATE_DRAW_EVENTS const FString* MaterialName = nullptr; #endif if (ResourceType == ESlateShaderResource::Material) { // Skip material render batches when the engine is not available. if (!GEngine) { return nullptr; } FSlateMaterialResource* MaterialShaderResource = (FSlateMaterialResource*)ShaderResource; MaterialShaderResource->CheckForStaleResources(); const FMaterialRenderProxy* MaterialRenderProxy = MaterialShaderResource->GetRenderProxy(); if (!MaterialRenderProxy) { return nullptr; } const FSlateSceneView* SceneView = Inputs.SceneViewAllocator->BeginAllocateSceneView(GraphBuilder, RenderBatch->SceneIndex); const ERHIFeatureLevel::Type SceneFeatureLevel = SceneView->FeatureLevel; const FSceneInterface* Scene = SceneView->Scene; const TUniformBufferRef& ViewUniformBuffer = Inputs.SceneViewAllocator->GetViewUniformBuffer(SceneView); TShaderRef VertexShader; TShaderRef PixelShader; FMaterialShaderTypes ShaderTypesToGet; if (!ChooseMaterialShaderTypes(ShaderType, bUseInstancing, ShaderTypesToGet)) { checkf(false, TEXT("Unsupported Slate shader type for use with materials")); return nullptr; } const FMaterial* EffectiveMaterial = nullptr; while (MaterialRenderProxy) { const FMaterial* Material = MaterialRenderProxy->UpdateUniformExpressionCacheIfNeeded(GraphBuilder.RHICmdList, SceneFeatureLevel); FMaterialShaders Shaders; if (Material && Material->TryGetShaders(ShaderTypesToGet, nullptr, Shaders)) { EffectiveMaterial = Material; Shaders.TryGetVertexShader(VertexShader); Shaders.TryGetPixelShader(PixelShader); break; } MaterialRenderProxy = MaterialRenderProxy->GetFallback(SceneFeatureLevel); } if (!VertexShader.IsValid() || !PixelShader.IsValid()) { return nullptr; } #if WITH_SLATE_DRAW_EVENTS MaterialName = &MaterialRenderProxy->GetMaterialName(); #endif VertexBindings = FSlateDrawShaderBindings::Create(GraphBuilder, VertexShader); VertexShader->SetMaterialShaderParameters(*VertexBindings, Scene, ViewUniformBuffer, MaterialRenderProxy, EffectiveMaterial); const bool bDrawDisabled = EnumHasAllFlags(RenderBatch->DrawEffects, ESlateDrawEffect::DisabledEffect); PixelBindings = FSlateDrawShaderBindings::Create(GraphBuilder, PixelShader); PixelShader->SetMaterialShaderParameters(*PixelBindings, Scene, ViewUniformBuffer, MaterialRenderProxy, EffectiveMaterial, ShaderParams); PixelShader->SetDisplayGammaAndContrast(*PixelBindings, FinalGamma, FinalContrast); PixelShader->SetDrawFlags(*PixelBindings, bDrawDisabled); auto* MaskResource = static_cast*>(MaterialShaderResource->GetTextureMaskResource()); if (MaskResource) { PixelShader->SetAdditionalTexture(*PixelBindings, MaskResource->GetTypedResource(), TStaticSamplerState::GetRHI()); } BlendState = GetMaterialBlendState(MaskResource, EffectiveMaterial); } else { check(!bUseInstancing); TShaderRef PixelShader; #if WITH_SLATE_VISUALIZERS TShaderRef BatchingPixelShader; if (CVarShowSlateBatching.GetValueOnRenderThread() != 0) { BatchingPixelShader = TShaderMapRef(Inputs.ShaderMap); PixelShader = BatchingPixelShader; } else #endif { bool bIsVirtualTexture = false; // check if texture is using BC4 compression and set shader to render grayscale // @todo Oodle : it seems strange to special-case only BC4/TC_Alpha here ; prefer calling ShouldUseGreyScaleEditorVisualization() bool bUseTextureGrayscale = false; if (ShaderResource != nullptr && ResourceType == ESlateShaderResource::TextureObject) { FSlateBaseUTextureResource* TextureObjectResource = const_cast(static_cast(ShaderResource)); if (UTexture* TextureObj = TextureObjectResource->GetTextureObject()) { bIsVirtualTexture = TextureObj->IsCurrentlyVirtualTextured(); if (TextureObj->CompressionSettings == TC_Alpha) { bUseTextureGrayscale = true; } } } PixelShader = GetTexturePixelShader(Inputs.ShaderMap, ShaderType, DrawEffects, bUseTextureGrayscale, bIsVirtualTexture); } #if WITH_SLATE_VISUALIZERS if (BatchingPixelShader.IsValid()) { BlendState = TStaticBlendState::GetRHI(); } else if (CVarShowSlateOverdraw.GetValueOnRenderThread() != 0) { BlendState = TStaticBlendState::GetRHI(); } else #endif { BlendState = EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::NoBlending) ? TStaticBlendState<>::GetRHI() : (EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::PreMultipliedAlpha) ? TStaticBlendState::GetRHI() : TStaticBlendState::GetRHI()) ; } FRHISamplerState* SamplerState = TStaticSamplerState::GetRHI(); 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(Inputs.TextureLODGroups, TextureObj); bIsVirtualTexture = TextureObj->IsCurrentlyVirtualTextured(); } } else { FRHITexture* NativeTextureRHI = ((TSlateTexture*)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; } SamplerState = GetSamplerState(DrawFlags, Filter); } PixelBindings = FSlateDrawShaderBindings::Create(GraphBuilder, PixelShader); #if WITH_SLATE_VISUALIZERS if (BatchingPixelShader.IsValid()) { BatchingPixelShader->SetBatchColor(*PixelBindings, Inputs.BatchColor); } #endif if (bIsVirtualTexture && TextureResource != nullptr) { PixelShader->SetVirtualTextureParameters(*PixelBindings, static_cast(TextureResource)); } else { PixelShader->SetTexture(*PixelBindings, TextureRHI, SamplerState); } PixelShader->SetShaderParams(*PixelBindings, ShaderParams); PixelShader->SetDisplayGammaAndInvertAlphaAndContrast(*PixelBindings, FinalGamma, EnumHasAllFlags(DrawEffects, ESlateDrawEffect::InvertAlpha) ? 1.0f : 0.0f, FinalContrast); } FSlateRenderBatchOp* RenderBatchOp = GraphBuilder.AllocPOD(); RenderBatchOp->RenderBatch = RenderBatch; RenderBatchOp->ClippingStateOp = ClippingStateOp; RenderBatchOp->ShaderResourceType = ResourceType; RenderBatchOp->VertexBindings = VertexBindings; RenderBatchOp->PixelBindings = PixelBindings; RenderBatchOp->InstanceBuffer = bUseInstancing ? RenderBatch->InstanceData->GetRHI() : nullptr; RenderBatchOp->BlendState = BlendState; RenderBatchOp->Next = nullptr; #if WITH_SLATE_DRAW_EVENTS RenderBatchOp->MaterialName = MaterialName; #endif return RenderBatchOp; } struct FSlateRenderBatchDrawInputs { FGlobalShaderMap* ShaderMap; FSlateElementsBuffers ElementsBuffers; FIntRect ElementsViewRect; bool bWireframe; }; void DrawSlateRenderBatch( FRHICommandList& RHICmdList, FSlateRenderBatchDrawState& State, const FSlateRenderBatchDrawInputs& Inputs, const FSlateRenderBatchOp& RenderBatchOp) { const FSlateClippingOp* ClippingStateOp = RenderBatchOp.ClippingStateOp; const FSlateRenderBatch& RenderBatch = *RenderBatchOp.RenderBatch; if (State.LastClippingOp != ClippingStateOp) { GetSlateClippingPipelineState(ClippingStateOp, State.GraphicsPSOInit.DepthStencilState, State.StencilRef); SetSlateClipping(RHICmdList, ClippingStateOp, Inputs.ElementsViewRect); State.LastClippingOp = ClippingStateOp; } FRHIBuffer* ElementsVertexBuffer = Inputs.ElementsBuffers.VertexBuffer->GetRHI(); FRHIBuffer* ElementsIndexBuffer = Inputs.ElementsBuffers.IndexBuffer->GetRHI(); State.GraphicsPSOInit.BlendState = RenderBatchOp.BlendState; if (EnumHasAllFlags(RenderBatch.DrawFlags, ESlateBatchDrawFlag::Wireframe)) { State.GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); } else { State.GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); } check(RenderBatch.NumIndices > 0); const uint32 PrimitiveCount = RenderBatch.DrawPrimitiveType == ESlateDrawPrimitive::LineList ? RenderBatch.NumIndices / 2 : RenderBatch.NumIndices / 3; if (RenderBatchOp.ShaderResourceType == ESlateShaderResource::Material) { SLATE_DRAW_EVENTF(RHICmdList, MaterialBatch, TEXT("Slate Material: %s"), *RenderBatchOp.MaterialName); State.GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = RenderBatchOp.InstanceBuffer ? GSlateInstancedVertexDeclaration.VertexDeclarationRHI : GSlateVertexDeclaration.VertexDeclarationRHI; State.GraphicsPSOInit.BoundShaderState.VertexShaderRHI = RenderBatchOp.VertexBindings->Shader.GetVertexShader(); State.GraphicsPSOInit.BoundShaderState.PixelShaderRHI = RenderBatchOp.PixelBindings->Shader.GetPixelShader(); State.GraphicsPSOInit.PrimitiveType = GetRHIPrimitiveType(RenderBatch.DrawPrimitiveType); #if PSO_PRECACHING_VALIDATE if (PSOCollectorStats::IsFullPrecachingValidationEnabled()) { static const int32 MaterialPSOCollectorIndex = FPSOCollectorCreateManager::GetIndex(GetFeatureLevelShadingPath(GMaxRHIFeatureLevel), SlateMaterialPSOCollectorName); // Material Render Proxy is not cached in the state, but could be done for better debugging if needed PSOCollectorStats::CheckFullPipelineStateInCache(State.GraphicsPSOInit, EPSOPrecacheResult::Unknown, nullptr, nullptr, nullptr, MaterialPSOCollectorIndex); } #endif // PSO_PRECACHING_VALIDATE SetGraphicsPipelineState(RHICmdList, State.GraphicsPSOInit, State.StencilRef); RenderBatchOp.VertexBindings->SetOnCommandList(RHICmdList); RenderBatchOp.PixelBindings->SetOnCommandList(RHICmdList); RHICmdList.SetStreamSource(0, ElementsVertexBuffer, RenderBatch.VertexOffset * sizeof(FSlateVertex)); if (RenderBatchOp.InstanceBuffer) { RHICmdList.SetStreamSource(1, RenderBatchOp.InstanceBuffer, RenderBatch.InstanceOffset * sizeof(FSlateInstanceBufferData::ElementType)); RHICmdList.DrawIndexedPrimitive(ElementsIndexBuffer, 0, 0, RenderBatch.NumVertices, RenderBatch.IndexOffset, PrimitiveCount, RenderBatch.InstanceCount); } else { RHICmdList.SetStreamSource(1, nullptr, 0); RHICmdList.DrawIndexedPrimitive(ElementsIndexBuffer, 0, 0, RenderBatch.NumVertices, RenderBatch.IndexOffset, PrimitiveCount, 1); } } else { if (EnumHasAllFlags(RenderBatch.DrawFlags, ESlateBatchDrawFlag::Wireframe) || Inputs.bWireframe) { State.GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); if (Inputs.bWireframe) { State.GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI(); } } else { State.GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); } TShaderMapRef GlobalVertexShader(Inputs.ShaderMap); State.GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GSlateVertexDeclaration.VertexDeclarationRHI; State.GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GlobalVertexShader.GetVertexShader(); State.GraphicsPSOInit.BoundShaderState.PixelShaderRHI = RenderBatchOp.PixelBindings->Shader.GetPixelShader(); State.GraphicsPSOInit.PrimitiveType = GetRHIPrimitiveType(RenderBatch.DrawPrimitiveType); #if PSO_PRECACHING_VALIDATE if (PSOCollectorStats::IsFullPrecachingValidationEnabled()) { static const int32 GlobalPSOCollectorIndex = FGlobalPSOCollectorManager::GetIndex(SlateGlobalPSOCollectorName); PSOCollectorStats::CheckGlobalGraphicsPipelineStateInCache(State.GraphicsPSOInit, GlobalPSOCollectorIndex); } #endif // PSO_PRECACHING_VALIDATE SetGraphicsPipelineState(RHICmdList, State.GraphicsPSOInit, State.StencilRef); RenderBatchOp.PixelBindings->SetOnCommandList(RHICmdList); RHICmdList.SetStreamSource(0, ElementsVertexBuffer, RenderBatch.VertexOffset * sizeof(FSlateVertex)); RHICmdList.DrawIndexedPrimitive(ElementsIndexBuffer, 0, 0, RenderBatch.NumVertices, RenderBatch.IndexOffset, PrimitiveCount, RenderBatch.InstanceCount); } } BEGIN_SHADER_PARAMETER_STRUCT(FSlateRenderBatchParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureExtractsParameters, SceneTextures) RDG_BUFFER_ACCESS(ElementsVertexBuffer, ERHIAccess::VertexOrIndexBuffer) RDG_BUFFER_ACCESS(ElementsIndexBuffer, ERHIAccess::VertexOrIndexBuffer) SHADER_PARAMETER_STRUCT_REF(FSlateViewUniformParameters, SlateView) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() void AddSlateDrawElementsPass( FRDGBuilder& GraphBuilder, const FSlateRHIRenderingPolicy& RenderingPolicy, const FSlateDrawElementsPassInputs& Inputs, TConstArrayView RenderBatches, int32 FirstBatchIndex) { TRACE_CPUPROFILER_EVENT_SCOPE(AddSlateDrawElements); const FIntPoint ElementsTextureExtent = Inputs.ElementsTexture->Desc.Extent; const FScreenPassTexture ElementsTexture(Inputs.ElementsTexture); const float EngineGamma = GEngine ? GEngine->GetDisplayGamma() : 2.2f; const float DisplayGamma = Inputs.bAllowGammaCorrection && !Inputs.bElementsTextureIsHDRDisplay ? EngineGamma : 1.0f; FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(GMaxRHIShaderPlatform); FSlateRHIResourceManager& ResourceManager = RenderingPolicy.GetResourceManagerRHI(); const FSlateSceneViewAllocateInputs SceneViewAllocateInputs = { .TextureExtent = ElementsTextureExtent , .ViewRect = Inputs.SceneViewRect , .ViewProjectionMatrix = Inputs.ElementsMatrix , .CursorPosition = Inputs.CursorPosition , .Time = Inputs.Time , .ViewportScaleUI = Inputs.ViewportScaleUI }; FSlateSceneViewAllocator* SceneViewAllocator = FSlateSceneViewAllocator::Create(GraphBuilder, ResourceManager, SceneViewAllocateInputs); #if WITH_SLATE_VISUALIZERS FRandomStream BatchColors(1337); #endif const FSlateRenderBatchCreateInputs RenderBatchCreateInputs { .ShaderMap = ShaderMap , .SceneViewAllocator = SceneViewAllocator , .TextureLODGroups = GetTextureLODGroups() , .DisplayGamma = DisplayGamma , .DisplayContrast = GSlateContrast , .EngineGamma = EngineGamma #if WITH_SLATE_VISUALIZERS , .BatchColor = FLinearColor(BatchColors.GetUnitVector()) #endif }; // Draw inputs are passed into RDG lambdas and need to be allocated by RDG. FSlateRenderBatchDrawInputs* RenderBatchDrawInputs = GraphBuilder.AllocPOD(); *RenderBatchDrawInputs = { .ShaderMap = ShaderMap , .ElementsBuffers = Inputs.ElementsBuffers , .ElementsViewRect = ElementsTexture.ViewRect , .bWireframe = Inputs.bWireframe }; ERenderTargetLoadAction ElementsLoadAction = Inputs.ElementsLoadAction; const auto ConsumeLoadAction = [] (ERenderTargetLoadAction& InOutLoadAction) { ERenderTargetLoadAction LoadAction = InOutLoadAction; InOutLoadAction = ERenderTargetLoadAction::ELoad; return LoadAction; }; TUniformBufferRef SlateViewUniformBuffer; FSlateRenderBatchParameters* NoneStencilActionPassParameters = GraphBuilder.AllocParameters(); NoneStencilActionPassParameters->SceneTextures = GetSceneTextureExtracts().GetShaderParameters(); NoneStencilActionPassParameters->ElementsVertexBuffer = Inputs.ElementsBuffers.VertexBuffer; NoneStencilActionPassParameters->ElementsIndexBuffer = Inputs.ElementsBuffers.IndexBuffer; NoneStencilActionPassParameters->RenderTargets[0] = FRenderTargetBinding(Inputs.ElementsTexture, ERenderTargetLoadAction::ELoad); { FSlateViewUniformParameters UniformParameters; UniformParameters.ViewProjection = Inputs.ElementsMatrix; NoneStencilActionPassParameters->SlateView = TUniformBufferRef::CreateUniformBufferImmediate(UniformParameters, UniformBuffer_SingleFrame); } FSlateRenderBatchParameters* ClearStencilActionPassParameters = nullptr; FSlateRenderBatchParameters* WriteStencilActionPassParameters = nullptr; if (Inputs.StencilTexture) { WriteStencilActionPassParameters = GraphBuilder.AllocParameters(NoneStencilActionPassParameters); WriteStencilActionPassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(Inputs.StencilTexture, ERenderTargetLoadAction::ENoAction, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthNop_StencilWrite); ClearStencilActionPassParameters = GraphBuilder.AllocParameters(WriteStencilActionPassParameters); ClearStencilActionPassParameters->RenderTargets.DepthStencil.SetStencilLoadAction(ERenderTargetLoadAction::EClear); } FSlateRenderBatchParameters* LastPassParameters = NoneStencilActionPassParameters; const FSlateClippingState* LastClippingState = nullptr; const FSlateClippingOp* LastClippingOp = nullptr; FSlateClippingCreateContext ClippingCreateContext; FSlateRenderBatchOp* RenderBatchHeadOp = nullptr; FSlateRenderBatchOp* RenderBatchTailOp = nullptr; int32 NumRenderBatchOps = 0; const auto FlushDrawElementsPass = [&] { if (!NumRenderBatchOps) { return; } if (ERenderTargetLoadAction LoadAction = ConsumeLoadAction(ElementsLoadAction); LoadAction != ERenderTargetLoadAction::ELoad) { // Load action differs from the default read one, so make a copy and modify. LastPassParameters = GraphBuilder.AllocParameters(LastPassParameters); LastPassParameters->RenderTargets[0].SetLoadAction(LoadAction); } FRDGPass* Pass = GraphBuilder.AddPass(RDG_EVENT_NAME("ElementBatch"), LastPassParameters, ERDGPassFlags::Raster, [Inputs = RenderBatchDrawInputs, RenderBatchHeadOp] (FRDGAsyncTask, FRHICommandList& RHICmdList) { RHICmdList.SetViewport(Inputs->ElementsViewRect.Min.X, Inputs->ElementsViewRect.Min.Y, 0.0f, Inputs->ElementsViewRect.Max.X, Inputs->ElementsViewRect.Max.Y, 1.0f); RHICmdList.SetScissorRect(false, 0, 0, 0, 0); FSlateRenderBatchDrawState DrawState; RHICmdList.ApplyCachedRenderTargets(DrawState.GraphicsPSOInit); DrawState.GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); const FSlateRenderBatchOp* RenderBatchOp = RenderBatchHeadOp; const FSlateRenderBatchOp* LastRenderBatchOp = nullptr; for (; RenderBatchOp != nullptr; RenderBatchOp = RenderBatchOp->Next) { DrawSlateRenderBatch(RHICmdList, DrawState, *Inputs, *RenderBatchOp); LastRenderBatchOp = RenderBatchOp; } }); GraphBuilder.SetPassWorkload(Pass, NumRenderBatchOps); RenderBatchHeadOp = RenderBatchTailOp = nullptr; NumRenderBatchOps = 0; }; int32 NextRenderBatchIndex = FirstBatchIndex; while (NextRenderBatchIndex != INDEX_NONE) { const FSlateRenderBatch& NextRenderBatch = RenderBatches[NextRenderBatchIndex]; NextRenderBatchIndex = NextRenderBatch.NextBatchIndex; FSlateRenderBatchParameters* NextPassParameters = LastPassParameters; const FSlateClippingState* NextClippingState = NextRenderBatch.ClippingState; const FSlateClippingOp* NextClippingOp = LastClippingOp; if (NextClippingState != LastClippingState) { NextClippingOp = CreateSlateClipping(GraphBuilder, Inputs.ElementsOffset, NextClippingState, ClippingCreateContext); switch (ClippingCreateContext.StencilAction) { case ESlateClippingStencilAction::Clear: NextPassParameters = ClearStencilActionPassParameters; break; case ESlateClippingStencilAction::Write: NextPassParameters = WriteStencilActionPassParameters; break; case ESlateClippingStencilAction::None: NextPassParameters = NoneStencilActionPassParameters; break; } LastClippingState = NextClippingState; LastClippingOp = NextClippingOp; } const ESlateRenderBatchType NextRenderBatchType = GetSlateRenderBatchType(NextRenderBatch); // Flush all primitive render batches when we encounter one that can't be added. if (NextRenderBatchType != ESlateRenderBatchType::Primitive || NextPassParameters != LastPassParameters) { FlushDrawElementsPass(); } LastPassParameters = NextPassParameters; switch (NextRenderBatchType) { case ESlateRenderBatchType::CustomDrawer: { // Clear the color texture if we haven't done it yet. if (ConsumeLoadAction(ElementsLoadAction) == ERenderTargetLoadAction::EClear) { AddClearRenderTargetPass(GraphBuilder, Inputs.ElementsTexture); } ICustomSlateElement::FDrawPassInputs DrawInputs; DrawInputs.ElementsMatrix = Inputs.ElementsMatrix; DrawInputs.ElementsOffset = Inputs.ElementsOffset; DrawInputs.OutputTexture = ElementsTexture.Texture; DrawInputs.SceneViewRect = Inputs.SceneViewRect; DrawInputs.HDRDisplayColorGamut = Inputs.HDRDisplayColorGamut; DrawInputs.UsedSlatePostBuffers = Inputs.UsedSlatePostBuffers; DrawInputs.bOutputIsHDRDisplay = Inputs.bElementsTextureIsHDRDisplay; DrawInputs.bWireFrame = Inputs.bWireframe; NextRenderBatch.CustomDrawer->Draw_RenderThread(GraphBuilder, DrawInputs); // Reset cached clipping state since custom draws mutate render state. LastClippingState = nullptr; LastClippingOp = nullptr; break; } case ESlateRenderBatchType::PostProcess: { const FShaderParams& ShaderParams = NextRenderBatch.ShaderParams; FSlatePostProcessBlurPassInputs BlurInputs; if (Inputs.SceneViewportTexture && Inputs.SceneViewportTexture != Inputs.ElementsTexture) { // Blur uses the scene viewport texture output as blur input and composites UI separately. BlurInputs.InputTexture = Inputs.SceneViewportTexture; if (HasBeenProduced(Inputs.ElementsTexture)) { BlurInputs.SDRCompositeUITexture = Inputs.ElementsTexture; } } else { // UI elements and scene are already composited together. BlurInputs.InputTexture = Inputs.ElementsTexture; BlurInputs.OutputLoadAction = ConsumeLoadAction(ElementsLoadAction); } BlurInputs.InputRect = FIntRect(ShaderParams.PixelParams.X + Inputs.ElementsOffset.X, ShaderParams.PixelParams.Y + Inputs.ElementsOffset.Y, ShaderParams.PixelParams.Z + Inputs.ElementsOffset.X, ShaderParams.PixelParams.W + Inputs.ElementsOffset.Y); BlurInputs.OutputTexture = Inputs.SceneViewportTexture ? Inputs.SceneViewportTexture : Inputs.ElementsTexture; BlurInputs.OutputRect = BlurInputs.InputRect; BlurInputs.ClippingOp = NextClippingOp; BlurInputs.ClippingStencilBinding = &NextPassParameters->RenderTargets.DepthStencil; BlurInputs.ClippingElementsViewRect = RenderBatchDrawInputs->ElementsViewRect; BlurInputs.KernelSize = ShaderParams.PixelParams2.X; BlurInputs.Strength = ShaderParams.PixelParams2.Y; BlurInputs.DownsampleAmount = ShaderParams.PixelParams2.Z; BlurInputs.CornerRadius = ShaderParams.PixelParams3; AddSlatePostProcessBlurPass(GraphBuilder, BlurInputs); break; } case ESlateRenderBatchType::Primitive: { if (FSlateRenderBatchOp* RenderBatchOp = CreateSlateRenderBatchOp(GraphBuilder, RenderBatchCreateInputs, &NextRenderBatch, NextClippingOp)) { if (!RenderBatchTailOp) { RenderBatchHeadOp = RenderBatchTailOp = RenderBatchOp; } else { RenderBatchTailOp->Next = RenderBatchOp; RenderBatchTailOp = RenderBatchOp; } NumRenderBatchOps++; } break; } default: checkNoEntry(); } } if (NumRenderBatchOps > 0) { FlushDrawElementsPass(); } // If no batches were rendered at all, then we might need to just clear the render target. if (ConsumeLoadAction(ElementsLoadAction) == ERenderTargetLoadAction::EClear) { AddClearRenderTargetPass(GraphBuilder, Inputs.ElementsTexture); } else { // Don't do color correction on iOS or Android, we don't have the GPU overhead for it. #if !(PLATFORM_IOS || PLATFORM_ANDROID) if (Inputs.bAllowColorDeficiencyCorrection && GSlateColorDeficiencyType != EColorVisionDeficiency::NormalVision && GSlateColorDeficiencySeverity > 0) { FSlatePostProcessColorDeficiencyPassInputs ColorDeficiencyInputs; ColorDeficiencyInputs.InputTexture = ElementsTexture; ColorDeficiencyInputs.OutputTexture = ElementsTexture; AddSlatePostProcessColorDeficiencyPass(GraphBuilder, ColorDeficiencyInputs); } #endif } INC_DWORD_STAT_BY(STAT_SlateScissorClips, ClippingCreateContext.NumScissors); INC_DWORD_STAT_BY(STAT_SlateStencilClips, ClippingCreateContext.NumStencils); } static TArray SlateShaderTypesToPrecache = { ESlateShader::Default, ESlateShader::Border, ESlateShader::GrayscaleFont, ESlateShader::Custom, ESlateShader::RoundedBox }; void AddSlatePSOInitializer( FRHIBlendState* BlendState, bool bInstanced, ESlateDrawPrimitive DrawPrimitiveType, FRHIVertexShader* VertexShaderRHI, FRHIPixelShader* PixelShaderRHI, const FSceneTexturesConfig& SceneTexturesConfig, int32 PSOCollectorIndex, TArray& PSOInitializers) { check(VertexShaderRHI && PixelShaderRHI); FGraphicsPipelineStateInitializer GraphicsPSOInit; GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); GraphicsPSOInit.BlendState = BlendState; GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = bInstanced ? GSlateInstancedVertexDeclaration.VertexDeclarationRHI : GSlateVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShaderRHI; GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShaderRHI; GraphicsPSOInit.PrimitiveType = GetRHIPrimitiveType(DrawPrimitiveType); FGraphicsPipelineRenderTargetsInfo RenderTargetsInfo; AddRenderTargetInfo(PF_A2B10G10R10, ETextureCreateFlags::RenderTargetable, RenderTargetsInfo); RenderTargetsInfo.NumSamples = 1; GraphicsPSOInit.StatePrecachePSOHash = RHIComputeStatePrecachePSOHash(GraphicsPSOInit); ApplyTargetsInfo(GraphicsPSOInit, RenderTargetsInfo); FPSOPrecacheData PSOPrecacheData; PSOPrecacheData.bRequired = true; PSOPrecacheData.Type = FPSOPrecacheData::EType::Graphics; PSOPrecacheData.GraphicsPSOInitializer = GraphicsPSOInit; #if PSO_PRECACHING_VALIDATE PSOPrecacheData.PSOCollectorIndex = PSOCollectorIndex; PSOPrecacheData.VertexFactoryType = nullptr; #endif // PSO_PRECACHING_VALIDATE PSOInitializers.Add(MoveTemp(PSOPrecacheData)); } void SlateGlobalPSOCollector(const FSceneTexturesConfig& SceneTexturesConfig, int32 GlobalPSOCollectorIndex, TArray& PSOInitializers) { EShaderPlatform ShaderPlatform = SceneTexturesConfig.ShaderPlatform; FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(ShaderPlatform); bool bRequired = false; // Collect all possible permutations of the FSlateElementPS (out of 5K possible permutations only 10 unique Pixel shaders are found) TSet SlateElementPixelShaders; for (int32 ShaderTypeIndex = 0; ShaderTypeIndex <= (int32)ESlateShader::MsdfFont; ++ShaderTypeIndex) { ESlateShader ShaderType = (ESlateShader) ShaderTypeIndex; for (int32 DrawEffectsIndex = 0; DrawEffectsIndex <= (int32)ESlateDrawEffect::ReverseGamma; ++DrawEffectsIndex) { ESlateDrawEffect DrawEffects = (ESlateDrawEffect) ShaderTypeIndex; for (int32 UseTextureGreyScaleIndex = 0; UseTextureGreyScaleIndex < 2; ++UseTextureGreyScaleIndex) { bool bUseTextureGrayscale = UseTextureGreyScaleIndex > 0; for (int32 IsVirtualTextureIndex = 0; IsVirtualTextureIndex < 2; ++IsVirtualTextureIndex) { bool bIsVirtualTexture = IsVirtualTextureIndex > 0; TShaderRef SlateElementPS = GetTexturePixelShader(GlobalShaderMap, ShaderType, DrawEffects, bUseTextureGrayscale, bIsVirtualTexture); FRHIPixelShader* RHIPixelShader = static_cast(SlateElementPS.GetRHIShaderBase(SF_Pixel, bRequired)); if (RHIPixelShader) { SlateElementPixelShaders.Add(RHIPixelShader); } } } } } TArray SlateElementBlendStates; SlateElementBlendStates.Add(TStaticBlendState<>::GetRHI()); SlateElementBlendStates.Add(TStaticBlendState::GetRHI()); SlateElementBlendStates.Add(TStaticBlendState::GetRHI()); TShaderMapRef SlateElementVertexShader(GlobalShaderMap); FRHIVertexShader* RHIVertexShader = static_cast(SlateElementVertexShader.GetRHIShaderBase(SF_Vertex)); for (FRHIPixelShader* SlateElementPixelShader : SlateElementPixelShaders) { for (FBlendStateRHIRef& BlendState : SlateElementBlendStates) { bool bInstanced = false; AddSlatePSOInitializer(BlendState, bInstanced, ESlateDrawPrimitive::TriangleList, RHIVertexShader, SlateElementPixelShader, SceneTexturesConfig, GlobalPSOCollectorIndex, PSOInitializers); } } } FRegisterGlobalPSOCollectorFunction RegisterSlateGlobalPSOCollector(&SlateGlobalPSOCollector, SlateGlobalPSOCollectorName); class FSlateMaterialPSOCollector : public IPSOCollector { public: FSlateMaterialPSOCollector(ERHIFeatureLevel::Type InFeatureLevel) : IPSOCollector(FPSOCollectorCreateManager::GetIndex(GetFeatureLevelShadingPath(InFeatureLevel), SlateMaterialPSOCollectorName)), FeatureLevel(InFeatureLevel) { } virtual void CollectPSOInitializers( const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray& PSOInitializers ) override final; private: ERHIFeatureLevel::Type FeatureLevel; }; void FSlateMaterialPSOCollector::CollectPSOInitializers( const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray& PSOInitializers ) { if (!Material.IsUIMaterial() || GSlateMaterialPSOPrecache == 0) { return; } bool bRequired = false; for (ESlateShader ShaderType : SlateShaderTypesToPrecache) { int32 UseInstancingCount = ShaderType == ESlateShader::Custom ? 2 : 1; for (int32 UseInstancingIndex = 0; UseInstancingIndex < UseInstancingCount; ++UseInstancingIndex) { bool bUseInstancing = UseInstancingIndex > 0; FMaterialShaderTypes ShaderTypesToGet; if (!ChooseMaterialShaderTypes(ShaderType, bUseInstancing, ShaderTypesToGet)) { continue; } FMaterialShaders Shaders; if (!Material.TryGetShaders(ShaderTypesToGet, nullptr, Shaders)) { continue; } TShaderRef VertexShader; TShaderRef PixelShader; Shaders.TryGetVertexShader(VertexShader); Shaders.TryGetPixelShader(PixelShader); if (!VertexShader.IsValid() || !PixelShader.IsValid()) { continue; } FRHIVertexShader* RHIVertexShader = VertexShader.GetVertexShader(bRequired); FRHIPixelShader* RHIPixelShader = PixelShader.GetPixelShader(bRequired); if (RHIVertexShader == nullptr || RHIPixelShader == nullptr) { continue; } // Don't know if mask resource will be used or not (also precache with blend mode required when mask resource is set?) FSlateShaderResource* MaskResource = nullptr; FRHIBlendState* BlendState = GetMaterialBlendState(MaskResource, &Material); // Only precache TriangleList AddSlatePSOInitializer(BlendState, bUseInstancing, ESlateDrawPrimitive::TriangleList, RHIVertexShader, RHIPixelShader, SceneTexturesConfig, PSOCollectorIndex, PSOInitializers); } } } IPSOCollector* CreateSlateMaterialPSOCollector(ERHIFeatureLevel::Type FeatureLevel) { return new FSlateMaterialPSOCollector(FeatureLevel); } FRegisterPSOCollectorCreateFunction RegisterSlateMaterialPSOCollector(&CreateSlateMaterialPSOCollector, EShadingPath::Deferred, SlateMaterialPSOCollectorName);