void FRCPassPostProcessDeferredDecals::Process(FRenderingCompositePassContext& Context) { FRHICommandListImmediate& RHICmdList = Context.RHICmdList; FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); const bool bShaderComplexity = Context.View.Family->EngineShowFlags.ShaderComplexity; const bool bDBuffer = IsDBufferEnabled(); const bool bStencilSizeThreshold = CVarStencilSizeThreshold.GetValueOnRenderThread() >= 0; SCOPED_DRAW_EVENTF(RHICmdList, DeferredDecals, TEXT("DeferredDecals %s"), GetStageName(CurrentStage)); if (CurrentStage == DRS_BeforeBasePass) { // before BasePass, only if DBuffer is enabled check(bDBuffer); FPooledRenderTargetDesc GBufferADesc; SceneContext.GetGBufferADesc(GBufferADesc); // DBuffer: Decal buffer FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(GBufferADesc.Extent, PF_B8G8R8A8, FClearValueBinding::None, TexCreate_None, TexCreate_ShaderResource | TexCreate_RenderTargetable, false, 1, true, true)); if (!SceneContext.DBufferA) { Desc.ClearValue = FClearValueBinding::Black; GRenderTargetPool.FindFreeElement(RHICmdList, Desc, SceneContext.DBufferA, TEXT("DBufferA")); } if (!SceneContext.DBufferB) { Desc.ClearValue = FClearValueBinding(FLinearColor(128.0f / 255.0f, 128.0f / 255.0f, 128.0f / 255.0f, 1)); GRenderTargetPool.FindFreeElement(RHICmdList, Desc, SceneContext.DBufferB, TEXT("DBufferB")); } Desc.Format = PF_R8G8; if (!SceneContext.DBufferC) { Desc.ClearValue = FClearValueBinding(FLinearColor(0, 1, 0, 1)); GRenderTargetPool.FindFreeElement(RHICmdList, Desc, SceneContext.DBufferC, TEXT("DBufferC")); } // we assume views are non overlapping, then we need to clear only once in the beginning, otherwise we would need to set scissor rects // and don't get FastClear any more. bool bFirstView = Context.View.Family->Views[0] == &Context.View; if (bFirstView) { SCOPED_DRAW_EVENT(RHICmdList, DBufferClear); FRHIRenderTargetView RenderTargets[3]; RenderTargets[0] = FRHIRenderTargetView(SceneContext.DBufferA->GetRenderTargetItem().TargetableTexture, 0, -1, ERenderTargetLoadAction::EClear, ERenderTargetStoreAction::EStore); RenderTargets[1] = FRHIRenderTargetView(SceneContext.DBufferB->GetRenderTargetItem().TargetableTexture, 0, -1, ERenderTargetLoadAction::EClear, ERenderTargetStoreAction::EStore); RenderTargets[2] = FRHIRenderTargetView(SceneContext.DBufferC->GetRenderTargetItem().TargetableTexture, 0, -1, ERenderTargetLoadAction::EClear, ERenderTargetStoreAction::EStore); FRHIDepthRenderTargetView DepthView(SceneContext.GetSceneDepthTexture(), ERenderTargetLoadAction::ELoad, ERenderTargetStoreAction::ENoAction, ERenderTargetLoadAction::ELoad, ERenderTargetStoreAction::ENoAction, FExclusiveDepthStencil(FExclusiveDepthStencil::DepthRead_StencilWrite)); FRHISetRenderTargetsInfo Info(3, RenderTargets, DepthView); RHICmdList.SetRenderTargetsAndClear(Info); } } // this cast is safe as only the dedicated server implements this differently and this pass should not be executed on the dedicated server const FViewInfo& View = Context.View; const FSceneViewFamily& ViewFamily = *(View.Family); bool bHasValidDBufferMask = false; if(ViewFamily.EngineShowFlags.Decals) { if(CurrentStage == DRS_BeforeBasePass || CurrentStage == DRS_BeforeLighting) { RenderMeshDecals(Context, CurrentStage); } FScene& Scene = *(FScene*)ViewFamily.Scene; //don't early return. Resolves must be run for fast clears to work. if (Scene.Decals.Num()) { FDecalRenderTargetManager RenderTargetManager(RHICmdList, Context.GetShaderPlatform(), CurrentStage); // Build a list of decals that need to be rendered for this view FTransientDecalRenderDataList SortedDecals; FDecalRendering::BuildVisibleDecalList(Scene, View, CurrentStage, SortedDecals); if (SortedDecals.Num() > 0) { SCOPED_DRAW_EVENTF(RHICmdList, DeferredDecalsInner, TEXT("DeferredDecalsInner %d/%d"), SortedDecals.Num(), Scene.Decals.Num()); // optimization to have less state changes EDecalRasterizerState LastDecalRasterizerState = DRS_Undefined; FDecalDepthState LastDecalDepthState; int32 LastDecalBlendMode = -1; int32 LastDecalHasNormal = -1; // Decal state can change based on its normal property.(SM5) FDecalRenderingCommon::ERenderTargetMode LastRenderTargetMode = FDecalRenderingCommon::RTM_Unknown; const ERHIFeatureLevel::Type SMFeatureLevel = Context.GetFeatureLevel(); SCOPED_DRAW_EVENT(RHICmdList, Decals); INC_DWORD_STAT_BY(STAT_Decals, SortedDecals.Num()); for (int32 DecalIndex = 0, DecalCount = SortedDecals.Num(); DecalIndex < DecalCount; DecalIndex++) { const FTransientDecalRenderData& DecalData = SortedDecals[DecalIndex]; const FDeferredDecalProxy& DecalProxy = *DecalData.DecalProxy; const FMatrix ComponentToWorldMatrix = DecalProxy.ComponentTrans.ToMatrixWithScale(); const FMatrix FrustumComponentToClip = FDecalRendering::ComputeComponentToClipMatrix(View, ComponentToWorldMatrix); EDecalBlendMode DecalBlendMode = DecalData.DecalBlendMode; EDecalRenderStage LocalDecalStage = FDecalRenderingCommon::ComputeRenderStage(View.GetShaderPlatform(), DecalBlendMode); bool bStencilThisDecal = IsStencilOptimizationAvailable(LocalDecalStage); FDecalRenderingCommon::ERenderTargetMode CurrentRenderTargetMode = FDecalRenderingCommon::ComputeRenderTargetMode(View.GetShaderPlatform(), DecalBlendMode, DecalData.bHasNormal); if (bShaderComplexity) { CurrentRenderTargetMode = FDecalRenderingCommon::RTM_SceneColor; // we want additive blending for the ShaderComplexity mode DecalBlendMode = DBM_Emissive; } // Here we assume that GBuffer can only be WorldNormal since it is the only GBufferTarget handled correctly. if (RenderTargetManager.bGufferADirty && DecalData.MaterialResource->NeedsGBuffer()) { RHICmdList.CopyToResolveTarget(SceneContext.GBufferA->GetRenderTargetItem().TargetableTexture, SceneContext.GBufferA->GetRenderTargetItem().TargetableTexture, true, FResolveParams()); RenderTargetManager.TargetsToResolve[FDecalRenderTargetManager::GBufferAIndex] = nullptr; RenderTargetManager.bGufferADirty = false; } // fewer rendertarget switches if possible if (CurrentRenderTargetMode != LastRenderTargetMode) { LastRenderTargetMode = CurrentRenderTargetMode; RenderTargetManager.SetRenderTargetMode(CurrentRenderTargetMode, DecalData.bHasNormal); Context.SetViewportAndCallRHI(Context.View.ViewRect); } bool bThisDecalUsesStencil = false; if (bStencilThisDecal && bStencilSizeThreshold) { // note this is after a SetStreamSource (in if CurrentRenderTargetMode != LastRenderTargetMode) call as it needs to get the VB input bThisDecalUsesStencil = RenderPreStencil(Context, ComponentToWorldMatrix, FrustumComponentToClip); LastDecalRasterizerState = DRS_Undefined; LastDecalDepthState = FDecalDepthState(); LastDecalBlendMode = -1; } const bool bBlendStateChange = DecalBlendMode != LastDecalBlendMode;// Has decal mode changed. const bool bDecalNormalChanged = GSupportsSeparateRenderTargetBlendState && // has normal changed for SM5 stain/translucent decals? (DecalBlendMode == DBM_Translucent || DecalBlendMode == DBM_Stain) && (int32)DecalData.bHasNormal != LastDecalHasNormal; // fewer blend state changes if possible if (bBlendStateChange || bDecalNormalChanged) { LastDecalBlendMode = DecalBlendMode; LastDecalHasNormal = (int32)DecalData.bHasNormal; SetDecalBlendState(RHICmdList, SMFeatureLevel, CurrentStage, (EDecalBlendMode)LastDecalBlendMode, DecalData.bHasNormal); } // todo const float ConservativeRadius = DecalData.ConservativeRadius; // const int32 IsInsideDecal = ((FVector)View.ViewMatrices.ViewOrigin - ComponentToWorldMatrix.GetOrigin()).SizeSquared() < FMath::Square(ConservativeRadius * 1.05f + View.NearClippingDistance * 2.0f) + ( bThisDecalUsesStencil ) ? 2 : 0; const bool bInsideDecal = ((FVector)View.ViewMatrices.ViewOrigin - ComponentToWorldMatrix.GetOrigin()).SizeSquared() < FMath::Square(ConservativeRadius * 1.05f + View.NearClippingDistance * 2.0f); // const bool bInsideDecal = !(IsInsideDecal & 1); // update rasterizer state if needed { bool bReverseHanded = false; { // Account for the reversal of handedness caused by negative scale on the decal const auto& Scale3d = DecalProxy.ComponentTrans.GetScale3D(); bReverseHanded = Scale3d[0] * Scale3d[1] * Scale3d[2] < 0.f; } EDecalRasterizerState DecalRasterizerState = ComputeDecalRasterizerState(bInsideDecal, bReverseHanded, View); if (LastDecalRasterizerState != DecalRasterizerState) { LastDecalRasterizerState = DecalRasterizerState; SetDecalRasterizerState(DecalRasterizerState, RHICmdList); } } // update DepthStencil state if needed { FDecalDepthState DecalDepthState = ComputeDecalDepthState(LocalDecalStage, bInsideDecal, bThisDecalUsesStencil); if (LastDecalDepthState != DecalDepthState) { LastDecalDepthState = DecalDepthState; SetDecalDepthState(DecalDepthState, RHICmdList); } } FDecalRendering::SetShader(RHICmdList, View, DecalData, FrustumComponentToClip); RHICmdList.DrawIndexedPrimitive(GetUnitCubeIndexBuffer(), PT_TriangleList, 0, 0, 8, 0, ARRAY_COUNT(GCubeIndices) / 3, 1); RenderTargetManager.bGufferADirty |= (RenderTargetManager.TargetsToResolve[FDecalRenderTargetManager::GBufferAIndex] != nullptr); } // we don't modify stencil but if out input was having stencil for us (after base pass - we need to clear) // Clear stencil to 0, which is the assumed default by other passes RHICmdList.Clear(false, FLinearColor::White, false, (float)ERHIZBuffer::FarPlane, true, 0, FIntRect()); } if (CurrentStage == DRS_BeforeBasePass) { // combine DBuffer RTWriteMasks; will end up in one texture we can load from in the base pass PS and decide whether to do the actual work or not RenderTargetManager.FlushMetaData(); if (GSupportsRenderTargetWriteMask) { DecodeRTWriteMask(Context); bHasValidDBufferMask = true; } } RenderTargetManager.ResolveTargets(); } if (CurrentStage == DRS_BeforeBasePass) { // before BasePass GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, SceneContext.DBufferA); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, SceneContext.DBufferB); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, SceneContext.DBufferC); } } if (CurrentStage == DRS_BeforeBasePass && !bHasValidDBufferMask) { // Return the DBufferMask to the render target pool. // FDeferredPixelShaderParameters will fall back to setting a white dummy mask texture. // This allows us to ignore the DBufferMask on frames without decals, without having to explicitly clear the texture. SceneContext.DBufferMask = nullptr; } }
void FRCPassPostProcessDeferredDecals::Process(FRenderingCompositePassContext& Context) { FRHICommandListImmediate& RHICmdList = Context.RHICmdList; FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); const bool bShaderComplexity = Context.View.Family->EngineShowFlags.ShaderComplexity; const bool bDBuffer = IsDBufferEnabled(); const bool bStencilSizeThreshold = CVarStencilSizeThreshold.GetValueOnRenderThread() >= 0; SCOPED_DRAW_EVENT(RHICmdList, PostProcessDeferredDecals); enum EDecalResolveBufferIndex { SceneColorIndex, GBufferAIndex, GBufferBIndex, GBufferCIndex, DBufferAIndex, DBufferBIndex, DBufferCIndex, ResolveBufferMax, }; FTextureRHIParamRef TargetsToResolve[ResolveBufferMax] = { nullptr }; if(DecalRenderStage == DRS_BeforeBasePass) { // before BasePass, only if DBuffer is enabled check(bDBuffer); // DBuffer: Decal buffer FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(SceneContext.GBufferA->GetDesc().Extent, PF_B8G8R8A8, FClearValueBinding::None, TexCreate_None, TexCreate_ShaderResource | TexCreate_RenderTargetable, false)); if(!SceneContext.DBufferA) { Desc.ClearValue = FClearValueBinding::Black; GRenderTargetPool.FindFreeElement(Desc, SceneContext.DBufferA, TEXT("DBufferA")); } if(!SceneContext.DBufferB) { Desc.ClearValue = FClearValueBinding(FLinearColor(128.0f / 255.0f, 128.0f / 255.0f, 128.0f / 255.0f, 1)); GRenderTargetPool.FindFreeElement(Desc, SceneContext.DBufferB, TEXT("DBufferB")); } Desc.Format = PF_R8G8; if(!SceneContext.DBufferC) { Desc.ClearValue = FClearValueBinding(FLinearColor(0, 1, 0, 1)); GRenderTargetPool.FindFreeElement(Desc, SceneContext.DBufferC, TEXT("DBufferC")); } // we assume views are non overlapping, then we need to clear only once in the beginning, otherwise we would need to set scissor rects // and don't get FastClear any more. bool bFirstView = Context.View.Family->Views[0] == &Context.View; if(bFirstView) { SCOPED_DRAW_EVENT(RHICmdList, DBufferClear); FRHIRenderTargetView RenderTargets[3]; RenderTargets[0] = FRHIRenderTargetView(SceneContext.DBufferA->GetRenderTargetItem().TargetableTexture, 0, -1, ERenderTargetLoadAction::EClear, ERenderTargetStoreAction::EStore); RenderTargets[1] = FRHIRenderTargetView(SceneContext.DBufferB->GetRenderTargetItem().TargetableTexture, 0, -1, ERenderTargetLoadAction::EClear, ERenderTargetStoreAction::EStore); RenderTargets[2] = FRHIRenderTargetView(SceneContext.DBufferC->GetRenderTargetItem().TargetableTexture, 0, -1, ERenderTargetLoadAction::EClear, ERenderTargetStoreAction::EStore); FRHIDepthRenderTargetView DepthView(SceneContext.GetSceneDepthSurface(), ERenderTargetLoadAction::ELoad, ERenderTargetStoreAction::ENoAction, ERenderTargetLoadAction::ELoad, ERenderTargetStoreAction::ENoAction, FExclusiveDepthStencil(FExclusiveDepthStencil::DepthRead_StencilWrite)); FRHISetRenderTargetsInfo Info(3, RenderTargets, DepthView); RHICmdList.SetRenderTargetsAndClear(Info); TargetsToResolve[DBufferAIndex] = SceneContext.DBufferA->GetRenderTargetItem().TargetableTexture; TargetsToResolve[DBufferBIndex] = SceneContext.DBufferB->GetRenderTargetItem().TargetableTexture; TargetsToResolve[DBufferCIndex] = SceneContext.DBufferC->GetRenderTargetItem().TargetableTexture; } } // this cast is safe as only the dedicated server implements this differently and this pass should not be executed on the dedicated server const FViewInfo& View = Context.View; const FSceneViewFamily& ViewFamily = *(View.Family); FScene& Scene = *(FScene*)ViewFamily.Scene; //don't early return. Resolves must be run for fast clears to work. bool bRenderDecal = Scene.Decals.Num() && ViewFamily.EngineShowFlags.Decals; if (bRenderDecal) { // Build a list of decals that need to be rendered for this view FTransientDecalRenderDataList SortedDecals; FDecalRendering::BuildVisibleDecalList(Scene, View, DecalRenderStage, SortedDecals); if (SortedDecals.Num() > 0) { FIntRect SrcRect = View.ViewRect; FIntRect DestRect = View.ViewRect; bool bStencilDecalsInThisStage = true; #if DBUFFER_DONT_USE_STENCIL_YET if (DecalRenderStage != DRS_BeforeLighting) { bStencilDecalsInThisStage = false; } #endif // Setup a stencil mask to prevent certain pixels from receiving deferred decals if (bStencilDecalsInThisStage) { StencilDecalMask(RHICmdList, View, Context.HasHmdMesh()); } // optimization to have less state changes EDecalRasterizerState LastDecalRasterizerState = DRS_Undefined; FDecalDepthState LastDecalDepthState; int32 LastDecalBlendMode = -1; int32 LastDecalHasNormal = -1; // Decal state can change based on its normal property.(SM5) FDecalRendering::ERenderTargetMode LastRenderTargetMode = FDecalRendering::RTM_Unknown; const ERHIFeatureLevel::Type SMFeatureLevel = Context.GetFeatureLevel(); SCOPED_DRAW_EVENT(RHICmdList, Decals); INC_DWORD_STAT_BY(STAT_Decals, SortedDecals.Num()); for (int32 DecalIndex = 0, DecalCount = SortedDecals.Num(); DecalIndex < DecalCount; DecalIndex++) { const FTransientDecalRenderData& DecalData = SortedDecals[DecalIndex]; const FDeferredDecalProxy& DecalProxy = *DecalData.DecalProxy; const FMatrix ComponentToWorldMatrix = DecalProxy.ComponentTrans.ToMatrixWithScale(); const FMatrix FrustumComponentToClip = FDecalRendering::ComputeComponentToClipMatrix(View, ComponentToWorldMatrix); EDecalBlendMode DecalBlendMode = DecalData.DecalBlendMode; bool bStencilThisDecal = bStencilDecalsInThisStage; #if DBUFFER_DONT_USE_STENCIL_YET if (FDecalRendering::ComputeRenderStage(View.GetShaderPlatform(), DecalBlendMode) != DRS_BeforeLighting) { bStencilThisDecal = false; } #endif FDecalRendering::ERenderTargetMode CurrentRenderTargetMode = FDecalRendering::ComputeRenderTargetMode(View.GetShaderPlatform(), DecalBlendMode); if (bShaderComplexity) { CurrentRenderTargetMode = FDecalRendering::RTM_SceneColor; // we want additive blending for the ShaderComplexity mode DecalBlendMode = DBM_Emissive; } // fewer rendertarget switches if possible if (CurrentRenderTargetMode != LastRenderTargetMode) { LastRenderTargetMode = CurrentRenderTargetMode; switch (CurrentRenderTargetMode) { case FDecalRendering::RTM_SceneColorAndGBuffer: { TargetsToResolve[SceneColorIndex] = SceneContext.GetSceneColor()->GetRenderTargetItem().TargetableTexture; TargetsToResolve[GBufferAIndex] = SceneContext.GBufferA->GetRenderTargetItem().TargetableTexture; TargetsToResolve[GBufferBIndex] = SceneContext.GBufferB->GetRenderTargetItem().TargetableTexture; TargetsToResolve[GBufferCIndex] = SceneContext.GBufferC->GetRenderTargetItem().TargetableTexture; SetRenderTargets(RHICmdList, 4, TargetsToResolve, SceneContext.GetSceneDepthSurface(), ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilWrite); } break; case FDecalRendering::RTM_SceneColorAndGBufferDepthWrite: { TargetsToResolve[SceneColorIndex] = SceneContext.GetSceneColor()->GetRenderTargetItem().TargetableTexture; TargetsToResolve[GBufferAIndex] = SceneContext.GBufferA->GetRenderTargetItem().TargetableTexture; TargetsToResolve[GBufferBIndex] = SceneContext.GBufferB->GetRenderTargetItem().TargetableTexture; TargetsToResolve[GBufferCIndex] = SceneContext.GBufferC->GetRenderTargetItem().TargetableTexture; SetRenderTargets(RHICmdList, 4, TargetsToResolve, SceneContext.GetSceneDepthSurface(), ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthWrite_StencilWrite); } break; case FDecalRendering::RTM_GBufferNormal: TargetsToResolve[GBufferAIndex] = SceneContext.GBufferA->GetRenderTargetItem().TargetableTexture; SetRenderTarget(RHICmdList, TargetsToResolve[GBufferAIndex], SceneContext.GetSceneDepthSurface(), ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilWrite); break; case FDecalRendering::RTM_SceneColor: TargetsToResolve[SceneColorIndex] = SceneContext.GetSceneColor()->GetRenderTargetItem().TargetableTexture; SetRenderTarget(RHICmdList, TargetsToResolve[SceneColorIndex], SceneContext.GetSceneDepthSurface(), ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilWrite); break; case FDecalRendering::RTM_DBuffer: { TargetsToResolve[DBufferAIndex] = SceneContext.DBufferA->GetRenderTargetItem().TargetableTexture; TargetsToResolve[DBufferBIndex] = SceneContext.DBufferB->GetRenderTargetItem().TargetableTexture; TargetsToResolve[DBufferCIndex] = SceneContext.DBufferC->GetRenderTargetItem().TargetableTexture; SetRenderTargets(RHICmdList, 3, &TargetsToResolve[DBufferAIndex], SceneContext.GetSceneDepthSurface(), ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilWrite); } break; default: check(0); break; } Context.SetViewportAndCallRHI(DestRect); // we need to reset the stream source after any call to SetRenderTarget (at least for Metal, which doesn't queue up VB assignments) RHICmdList.SetStreamSource(0, GetUnitCubeVertexBuffer(), sizeof(FVector4), 0); } bool bThisDecalUsesStencil = false; if (bStencilThisDecal) { if (bStencilSizeThreshold) { // note this is after a SetStreamSource (in if CurrentRenderTargetMode != LastRenderTargetMode) call as it needs to get the VB input bThisDecalUsesStencil = RenderPreStencil(Context, ComponentToWorldMatrix, FrustumComponentToClip); LastDecalRasterizerState = DRS_Undefined; LastDecalDepthState = FDecalDepthState(); LastDecalBlendMode = -1; } } const bool bBlendStateChange = DecalBlendMode != LastDecalBlendMode;// Has decal mode changed. const bool bDecalNormalChanged = GSupportsSeparateRenderTargetBlendState && // has normal changed for SM5 stain/translucent decals? (DecalBlendMode == DBM_Translucent || DecalBlendMode == DBM_Stain) && (int32)DecalData.bHasNormal != LastDecalHasNormal; // fewer blend state changes if possible if (bBlendStateChange || bDecalNormalChanged) { LastDecalBlendMode = DecalBlendMode; LastDecalHasNormal = (int32)DecalData.bHasNormal; SetDecalBlendState(RHICmdList, SMFeatureLevel, DecalRenderStage, (EDecalBlendMode)LastDecalBlendMode, DecalData.bHasNormal); } // todo const float ConservativeRadius = DecalData.ConservativeRadius; // const int32 IsInsideDecal = ((FVector)View.ViewMatrices.ViewOrigin - ComponentToWorldMatrix.GetOrigin()).SizeSquared() < FMath::Square(ConservativeRadius * 1.05f + View.NearClippingDistance * 2.0f) + ( bThisDecalUsesStencil ) ? 2 : 0; const bool bInsideDecal = ((FVector)View.ViewMatrices.ViewOrigin - ComponentToWorldMatrix.GetOrigin()).SizeSquared() < FMath::Square(ConservativeRadius * 1.05f + View.NearClippingDistance * 2.0f); // const bool bInsideDecal = !(IsInsideDecal & 1); // update rasterizer state if needed { EDecalRasterizerState DecalRasterizerState = ComputeDecalRasterizerState(bInsideDecal, View); if (LastDecalRasterizerState != DecalRasterizerState) { LastDecalRasterizerState = DecalRasterizerState; SetDecalRasterizerState(DecalRasterizerState, RHICmdList); } } // update DepthStencil state if needed { FDecalDepthState DecalDepthState = ComputeDecalDepthState(DecalBlendMode, bInsideDecal, bStencilDecalsInThisStage, bThisDecalUsesStencil); if (LastDecalDepthState != DecalDepthState) { LastDecalDepthState = DecalDepthState; SetDecalDepthState(DecalDepthState, RHICmdList); } } FDecalRendering::SetShader(RHICmdList, View, bShaderComplexity, DecalData, FrustumComponentToClip); RHICmdList.DrawIndexedPrimitive(GetUnitCubeIndexBuffer(), PT_TriangleList, 0, 0, 8, 0, ARRAY_COUNT(GCubeIndices) / 3, 1); } // we don't modify stencil but if out input was having stencil for us (after base pass - we need to clear) // Clear stencil to 0, which is the assumed default by other passes RHICmdList.Clear(false, FLinearColor::White, false, (float)ERHIZBuffer::FarPlane, true, 0, FIntRect()); if (DecalRenderStage == DRS_BeforeBasePass) { // before BasePass GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, SceneContext.DBufferA); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, SceneContext.DBufferB); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, SceneContext.DBufferC); } } } // resolve the targets we wrote to. FResolveParams ResolveParams; for (int32 i = 0; i < ResolveBufferMax; ++i) { if (TargetsToResolve[i]) { RHICmdList.CopyToResolveTarget(TargetsToResolve[i], TargetsToResolve[i], true, ResolveParams); } } }