// Copyright Epic Games, Inc. All Rights Reserved. #include "ClearQuad.h" #include "RenderUtils.h" #include "Shader.h" #include "RHIStaticStates.h" #include "OneColorShader.h" #include "PipelineStateCache.h" #include "RendererInterface.h" #include "Logging/LogMacros.h" #include "RHIResourceUtils.h" static const FVector4f GClearVertexBufferVertices[] = { FVector4f(-1.0f, 1.0f, 0.0f, 1.0f), FVector4f(1.0f, 1.0f, 0.0f, 1.0f), FVector4f(-1.0f, -1.0f, 0.0f, 1.0f), FVector4f(1.0f, -1.0f, 0.0f, 1.0f), }; void FClearVertexBuffer::InitRHI(FRHICommandListBase& RHICmdList) { // create a static vertex buffer VertexBufferRHI = UE::RHIResourceUtils::CreateVertexBufferFromArray(RHICmdList, TEXT("FClearVertexBuffer"), EBufferUsageFlags::Static, MakeConstArrayView(GClearVertexBufferVertices)); } TGlobalResource GClearVertexBuffer; DEFINE_LOG_CATEGORY_STATIC(LogClearQuad, Log, Log) template static void ClearQuadSetup(FRHICommandList& RHICmdList, int32 NumClearColors, const FLinearColor* ClearColorArray, bool bClearDepth, float Depth, bool bClearStencil , uint32 Stencil, TFunction PSOModifier = nullptr , uint8 NumUintOutput = 0) { if (UNLIKELY(!FApp::CanEverRender())) { return; } check(ColorWriteMask == CW_NONE || NumClearColors > 0); // Set new states FRHIBlendState* BlendStateRHI = TStaticBlendStateWriteMask::GetRHI(); FRHIDepthStencilState* DepthStencilStateRHI = (bClearDepth && bClearStencil) ? TStaticDepthStencilState< true, CF_Always, true,CF_Always,SO_Replace,SO_Replace,SO_Replace, false,CF_Always,SO_Replace,SO_Replace,SO_Replace, 0xff,0xff >::GetRHI() : bClearDepth ? TStaticDepthStencilState::GetRHI() : bClearStencil ? TStaticDepthStencilState< false, CF_Always, true,CF_Always,SO_Replace,SO_Replace,SO_Replace, false,CF_Always,SO_Replace,SO_Replace,SO_Replace, 0xff,0xff >::GetRHI() : TStaticDepthStencilState::GetRHI(); FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); GraphicsPSOInit.BlendState = BlendStateRHI; GraphicsPSOInit.DepthStencilState = DepthStencilStateRHI; auto* ShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel); // Set the new shaders TShaderMapRef > VertexShader(ShaderMap); // Set the shader to write to the appropriate number of render targets // On AMD PC hardware, outputting to a color index in the shader without a matching render target set has a significant performance hit TOneColorPixelShaderMRT::FPermutationDomain PermutationVector; PermutationVector.Set(NumClearColors ? NumClearColors : 1); PermutationVector.Set(PlatformRequires128bitRT((EPixelFormat)GraphicsPSOInit.RenderTargetFormats[0])); PermutationVector.Set(NumUintOutput); TShaderMapRef PixelShader(ShaderMap, PermutationVector); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4(); GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PT_TriangleStrip; if (PSOModifier) { PSOModifier(GraphicsPSOInit); } SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, Stencil); SetShaderParametersLegacyVS(RHICmdList, VertexShader, Depth); TOneColorPixelShaderMRT::FParameters PixelParameters; PixelShader->FillParameters(PixelParameters, ClearColorArray, NumClearColors); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PixelParameters); } void DrawClearQuadAlpha(FRHICommandList& RHICmdList, float Alpha) { FLinearColor Color(0, 0, 0, Alpha); ClearQuadSetup(RHICmdList, 1, &Color, false, 0, false, 0); RHICmdList.SetStreamSource(0, GClearVertexBuffer.VertexBufferRHI, 0); RHICmdList.DrawPrimitive(0, 2, 1); } void DrawClearQuadMRT(FRHICommandList& RHICmdList, bool bClearColor, int32 NumClearColors, const FLinearColor* ClearColorArray, bool bClearDepth, float Depth, bool bClearStencil, uint32 Stencil) { if (bClearColor) { ClearQuadSetup(RHICmdList, NumClearColors, ClearColorArray, bClearDepth, Depth, bClearStencil, Stencil); } else { ClearQuadSetup(RHICmdList, NumClearColors, ClearColorArray, bClearDepth, Depth, bClearStencil, Stencil); } RHICmdList.SetStreamSource(0, GClearVertexBuffer.VertexBufferRHI, 0); RHICmdList.DrawPrimitive(0, 2, 1); } void DrawClearQuadMRTWithUints(FRHICommandList& RHICmdList, bool bClearColor, int32 NumClearColors, const FLinearColor* ClearColorArray, bool bClearDepth, float Depth, bool bClearStencil, uint32 Stencil, uint8 NumUintOutput) { check(NumUintOutput <= NumClearColors); if (bClearColor) { ClearQuadSetup(RHICmdList, NumClearColors, ClearColorArray, bClearDepth, Depth, bClearStencil, Stencil, nullptr , NumUintOutput); } else { ClearQuadSetup(RHICmdList, NumClearColors, ClearColorArray, bClearDepth, Depth, bClearStencil, Stencil, nullptr, NumUintOutput); } RHICmdList.SetStreamSource(0, GClearVertexBuffer.VertexBufferRHI, 0); RHICmdList.DrawPrimitive(0, 2, 1); } void DrawClearQuadMRT(FRHICommandList& RHICmdList, bool bClearColor, int32 NumClearColors, const FLinearColor* ClearColorArray, bool bClearDepth, float Depth, bool bClearStencil, uint32 Stencil, FClearQuadCallbacks ClearQuadCallbacks) { if (bClearColor) { ClearQuadSetup(RHICmdList, NumClearColors, ClearColorArray, bClearDepth, Depth, bClearStencil, Stencil, ClearQuadCallbacks.PSOModifier); } else { ClearQuadSetup(RHICmdList, NumClearColors, ClearColorArray, bClearDepth, Depth, bClearStencil, Stencil, ClearQuadCallbacks.PSOModifier); } if (ClearQuadCallbacks.PreClear) { ClearQuadCallbacks.PreClear(RHICmdList); } // Draw a fullscreen quad without a hole RHICmdList.SetStreamSource(0, GClearVertexBuffer.VertexBufferRHI, 0); RHICmdList.DrawPrimitive(0, 2, 1); if (ClearQuadCallbacks.PostClear) { ClearQuadCallbacks.PostClear(RHICmdList); } } void DrawClearQuadMRT(FRHICommandList& RHICmdList, bool bClearColor, int32 NumClearColors, const FLinearColor* ClearColorArray, bool bClearDepth, float Depth, bool bClearStencil, uint32 Stencil, FIntPoint ViewSize, FIntRect ExcludeRect) { if (ExcludeRect.Min == FIntPoint::ZeroValue && ExcludeRect.Max == ViewSize) { // Early out if the entire surface is excluded return; } if (bClearColor) { ClearQuadSetup(RHICmdList, NumClearColors, ClearColorArray, bClearDepth, Depth, bClearStencil, Stencil); } else { ClearQuadSetup(RHICmdList, NumClearColors, ClearColorArray, bClearDepth, Depth, bClearStencil, Stencil); } // Draw a fullscreen quad if (ExcludeRect.Width() > 0 && ExcludeRect.Height() > 0) { // with a hole in it FVector4f OuterVertices[4]; OuterVertices[0].Set(-1.0f, 1.0f, Depth, 1.0f); OuterVertices[1].Set(1.0f, 1.0f, Depth, 1.0f); OuterVertices[2].Set(1.0f, -1.0f, Depth, 1.0f); OuterVertices[3].Set(-1.0f, -1.0f, Depth, 1.0f); float InvViewWidth = 1.0f / ViewSize.X; float InvViewHeight = 1.0f / ViewSize.Y; FVector4f FractionRect = FVector4f(ExcludeRect.Min.X * InvViewWidth, ExcludeRect.Min.Y * InvViewHeight, (ExcludeRect.Max.X - 1) * InvViewWidth, (ExcludeRect.Max.Y - 1) * InvViewHeight); FVector4f InnerVertices[4]; InnerVertices[0].Set(FMath::Lerp(-1.0f, 1.0f, FractionRect.X), FMath::Lerp(1.0f, -1.0f, FractionRect.Y), Depth, 1.0f); InnerVertices[1].Set(FMath::Lerp(-1.0f, 1.0f, FractionRect.Z), FMath::Lerp(1.0f, -1.0f, FractionRect.Y), Depth, 1.0f); InnerVertices[2].Set(FMath::Lerp(-1.0f, 1.0f, FractionRect.Z), FMath::Lerp(1.0f, -1.0f, FractionRect.W), Depth, 1.0f); InnerVertices[3].Set(FMath::Lerp(-1.0f, 1.0f, FractionRect.X), FMath::Lerp(1.0f, -1.0f, FractionRect.W), Depth, 1.0f); const FVector4f Vertices[] = { OuterVertices[0], InnerVertices[0], OuterVertices[1], InnerVertices[1], OuterVertices[2], InnerVertices[2], OuterVertices[3], InnerVertices[3], OuterVertices[0], InnerVertices[0], }; FBufferRHIRef VertexBufferRHI = UE::RHIResourceUtils::CreateVertexBufferFromArray(RHICmdList, TEXT("DrawClearQuadMRT"), EBufferUsageFlags::Volatile, MakeConstArrayView(Vertices)); RHICmdList.SetStreamSource(0, VertexBufferRHI, 0); RHICmdList.DrawPrimitive(0, 8, 1); } else { // without a hole RHICmdList.SetStreamSource(0, GClearVertexBuffer.VertexBufferRHI, 0); RHICmdList.DrawPrimitive(0, 2, 1); } }