void FStaticMesh::AddToDrawLists(FRHICommandListImmediate& RHICmdList, FScene* Scene) { const auto FeatureLevel = Scene->GetFeatureLevel(); if (CastShadow) { FShadowDepthDrawingPolicyFactory::AddStaticMesh(Scene, this); } if (!PrimitiveSceneInfo->Proxy->ShouldRenderInMainPass()) { return; } if (bUseForMaterial && Scene->RequiresHitProxies() && PrimitiveSceneInfo->Proxy->IsSelectable()) { // Add the static mesh to the DPG's hit proxy draw list. FHitProxyDrawingPolicyFactory::AddStaticMesh(Scene, this); } if (IsTranslucent(FeatureLevel)) { return; } if (Scene->ShouldUseDeferredRenderer()) { if (bUseAsOccluder) { // Render non-masked materials in the depth only pass extern TAutoConsoleVariable<int32> CVarEarlyZPass; int32 EarlyZPass = CVarEarlyZPass.GetValueOnRenderThread(); extern int32 GEarlyZPassMovable; // WARNING : If you change this condition, also change the logic in FStaticMeshSceneProxy::DrawStaticElements. if (PrimitiveSceneInfo->Proxy->ShouldUseAsOccluder() && (!IsMasked(FeatureLevel) || EarlyZPass == 2) && (!PrimitiveSceneInfo->Proxy->IsMovable() || GEarlyZPassMovable)) { FDepthDrawingPolicyFactory::AddStaticMesh(Scene,this); } } if (bUseForMaterial) { // Add the static mesh to the DPG's base pass draw list. FBasePassOpaqueDrawingPolicyFactory::AddStaticMesh(RHICmdList, Scene, this); FVelocityDrawingPolicyFactory::AddStaticMesh(Scene, this); } } else { if (bUseForMaterial) { // Add the static mesh to the DPG's base pass draw list. FBasePassForwardOpaqueDrawingPolicyFactory::AddStaticMesh(RHICmdList, Scene, this); } } }
void FTemporalLODState::UpdateTemporalLODTransition(const FViewInfo& View, float LastRenderTime) { bool bOk = false; if (!View.bDisableDistanceBasedFadeTransitions) { bOk = true; TemporalLODLag = CVarLODTemporalLag.GetValueOnRenderThread(); if (TemporalLODTime[1] < LastRenderTime - TemporalLODLag) { if (TemporalLODTime[0] < TemporalLODTime[1]) { TemporalLODViewOrigin[0] = TemporalLODViewOrigin[1]; TemporalDistanceFactor[0] = TemporalDistanceFactor[1]; TemporalLODTime[0] = TemporalLODTime[1]; } TemporalLODViewOrigin[1] = View.ViewMatrices.ViewOrigin; TemporalDistanceFactor[1] = View.GetLODDistanceFactor(); TemporalLODTime[1] = LastRenderTime; if (TemporalLODTime[1] <= TemporalLODTime[0]) { bOk = false; // we are paused or something or otherwise didn't get a good sample } } } if (!bOk) { TemporalLODViewOrigin[0] = View.ViewMatrices.ViewOrigin; TemporalLODViewOrigin[1] = View.ViewMatrices.ViewOrigin; TemporalDistanceFactor[0] = View.GetLODDistanceFactor(); TemporalDistanceFactor[1] = TemporalDistanceFactor[0]; TemporalLODTime[0] = LastRenderTime; TemporalLODTime[1] = LastRenderTime; TemporalLODLag = 0.0f; } }
bool DoScreenSpaceReflections(const FViewInfo& View) { if(!View.Family->EngineShowFlags.ScreenSpaceReflections) { return false; } if(!View.State) { // not view state (e.g. thumbnail rendering?), no HZB (no screen space reflections or occlusion culling) return false; } int SSRQuality = CVarSSRQuality.GetValueOnRenderThread(); if(SSRQuality <= 0) { return false; } if(View.FinalPostProcessSettings.ScreenSpaceReflectionIntensity < 1.0f) { return false; } return true; }
bool ShouldRenderFog(const FSceneViewFamily& Family) { const FEngineShowFlags EngineShowFlags = Family.EngineShowFlags; return EngineShowFlags.Fog && EngineShowFlags.Materials && !Family.UseDebugViewPS() && CVarFog.GetValueOnRenderThread() == 1 && !EngineShowFlags.StationaryLightOverlap && !EngineShowFlags.LightMapDensity; }
// Can be optimized to avoid the virtual function call but it's compiled out for final release anyway RENDERCORE_API int32 GetCVarForceLOD() { int32 Ret = -1; #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) { Ret = CVarForceLOD.GetValueOnRenderThread(); } #endif // !(UE_BUILD_SHIPPING || UE_BUILD_TEST) return Ret; }
bool RenderPreStencil(FRenderingCompositePassContext& Context, const FMaterialShaderMap* MaterialShaderMap, const FMatrix& ComponentToWorldMatrix, const FMatrix& FrustumComponentToClip) { const FSceneView& View = Context.View; float Distance = (View.ViewMatrices.ViewOrigin - ComponentToWorldMatrix.GetOrigin()).Size(); float Radius = ComponentToWorldMatrix.GetMaximumAxisScale(); // if not inside if(Distance > Radius) { float EstimatedDecalSize = Radius / Distance; float StencilSizeThreshold = CVarStencilSizeThreshold.GetValueOnRenderThread(); // Check if it's large enough on screen if(EstimatedDecalSize < StencilSizeThreshold) { return false; } } TShaderMapRef<FDeferredDecalVS> VertexShader(Context.GetShaderMap()); Context.RHICmdList.SetLocalBoundShaderState(Context.RHICmdList.BuildLocalBoundShaderState(GetVertexDeclarationFVector3(), VertexShader->GetVertexShader(), FHullShaderRHIRef(), FDomainShaderRHIRef(), NULL, FGeometryShaderRHIRef())); VertexShader->SetParameters(Context.RHICmdList, View, FrustumComponentToClip); // Set states, the state cache helps us avoiding redundant sets Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<FM_Solid, CM_None>::GetRHI()); // all the same to have DX10 working Context.RHICmdList.SetBlendState(TStaticBlendState< CW_NONE, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One, // Emissive CW_NONE, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One, // Normal CW_NONE, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One, // Metallic, Specular, Roughness CW_NONE, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One // BaseColor >::GetRHI() ); // Carmack's reverse on the bounds //@todo-martinm Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState< false,CF_LessEqual, true,CF_Equal,SO_Keep,SO_Keep,SO_Increment, true,CF_Equal,SO_Keep,SO_Keep,SO_Decrement, 0x80,0x7f >::GetRHI()); // Render decal mask Context.RHICmdList.DrawIndexedPrimitive(GUnitCubeIndexBuffer.IndexBufferRHI, PT_TriangleList, 0, 0, 8, 0, GUnitCubeIndexBuffer.GetIndexCount() / 3, 1); return true; }
static void DoDrawRectangleFlagOverride(EDrawRectangleFlags& Flags) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) // Determine triangle draw mode int Value = CVarDrawRectangleOptimization.GetValueOnRenderThread(); if(!Value) { // don't use triangle optimization Flags = EDRF_Default; } #endif }
void FStaticMesh::AddToDrawLists(FScene* Scene) { if (GRHIFeatureLevel >= ERHIFeatureLevel::SM3) { if (CastShadow) { FShadowDepthDrawingPolicyFactory::AddStaticMesh(Scene,this); } if (!bShadowOnly && PrimitiveSceneInfo->Proxy->ShouldRenderInMainPass()) { // not all platforms need this const bool bRequiresHitProxies = Scene->RequiresHitProxies(); if ( bRequiresHitProxies && PrimitiveSceneInfo->Proxy->IsSelectable() ) { // Add the static mesh to the DPG's hit proxy draw list. FHitProxyDrawingPolicyFactory::AddStaticMesh(Scene,this); } if(!IsTranslucent()) { extern TAutoConsoleVariable<int32> CVarEarlyZPass; int32 EarlyZPass = CVarEarlyZPass.GetValueOnRenderThread(); extern int32 GEarlyZPassMovable; // Render non-masked materials in the depth only pass if (PrimitiveSceneInfo->Proxy->ShouldUseAsOccluder() && (!IsMasked() || EarlyZPass == 2) && (!PrimitiveSceneInfo->Proxy->IsMovable() || GEarlyZPassMovable)) { FDepthDrawingPolicyFactory::AddStaticMesh(Scene,this); } // Add the static mesh to the DPG's base pass draw list. FBasePassOpaqueDrawingPolicyFactory::AddStaticMesh(Scene,this); FVelocityDrawingPolicyFactory::AddStaticMesh(Scene, this); } } } else { if (!bShadowOnly && !IsTranslucent()) { // Add the static mesh to the DPG's base pass draw list. FBasePassForwardOpaqueDrawingPolicyFactory::AddStaticMesh(Scene,this); } } }
static bool DoSpecularCorrection() { bool CVarState = CVarSubsurfaceQuality.GetValueOnRenderThread() > 0; int SceneColorFormat; { static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.SceneColorFormat")); SceneColorFormat = CVar->GetInt(); } // we need an alpha channel for this feature return CVarState && (SceneColorFormat >= 4); }
/** * Evaluates a normal distribution PDF (around 0) at given X. * This function misses the math for scaling the result (faster, not needed if the resulting values are renormalized). * @param X - The X to evaluate the PDF at. * @param Scale - The normal distribution's variance. * @return The value of the normal distribution at X. (unscaled) */ static float NormalDistributionUnscaled(float X,float Scale, EFilterShape FilterShape, float CrossCenterWeight) { float Ret; if(FilterNewMethod.GetValueOnRenderThread()) { float dxUnScaled = FMath::Abs(X); float dxScaled = dxUnScaled * Scale; // Constant is tweaked give a similar look to UE4 before we fix the scale bug (Some content tweaking might be needed). // The value defines how much of the Gaussian clipped by the sample window. // r.Filter.SizeScale allows to tweak that for performance/quality. Ret = FMath::Exp(-16.7f * FMath::Square(dxScaled)); // tweak the gaussian shape e.g. "r.Bloom.Cross 3.5" if (CrossCenterWeight > 1.0f) { Ret = FMath::Max(0.0f, 1.0f - dxUnScaled); Ret = FMath::Pow(Ret, CrossCenterWeight); } else { Ret = FMath::Lerp(Ret, FMath::Max(0.0f, 1.0f - dxUnScaled), CrossCenterWeight); } } else { // will be removed soon float OldVariance = 1.0f / Scale; float dx = FMath::Abs(X); Ret = FMath::Exp(-FMath::Square(dx) / (2.0f * OldVariance)); // tweak the gaussian shape e.g. "r.Bloom.Cross 3.5" if(CrossCenterWeight > 1.0f) { Ret = FMath::Max(0.0f, 1.0f - dx / OldVariance); Ret = FMath::Pow(Ret, CrossCenterWeight); } else { Ret = FMath::Lerp(Ret, FMath::Max(0.0f, 1.0f - dx / OldVariance), CrossCenterWeight); } } return Ret; }
void SetCS(FRHICommandList& RHICmdList, const FRenderingCompositePassContext& Context, const FSceneView& View, FIntPoint WriteMaskDimensions) { const FComputeShaderRHIParamRef ShaderRHI = GetComputeShader(); FGlobalShader::SetParameters(RHICmdList, ShaderRHI, Context.View); //PostprocessParameter.SetCS(ShaderRHI, Context, Context.RHICmdList, TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI()); FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); SetShaderValue(Context.RHICmdList, ShaderRHI, RTWriteMaskDimensions, WriteMaskDimensions); SetSRVParameter(Context.RHICmdList, ShaderRHI, RTWriteMaskInput0, SceneContext.DBufferA->GetRenderTargetItem().RTWriteMaskBufferRHI_SRV); SetSRVParameter(Context.RHICmdList, ShaderRHI, RTWriteMaskInput1, SceneContext.DBufferB->GetRenderTargetItem().RTWriteMaskBufferRHI_SRV); SetSRVParameter(Context.RHICmdList, ShaderRHI, RTWriteMaskInput2, SceneContext.DBufferC->GetRenderTargetItem().RTWriteMaskBufferRHI_SRV); int32 UseMask = CVarGenerateDecalRTWriteMaskTexture.GetValueOnRenderThread(); SetShaderValue(Context.RHICmdList, ShaderRHI, UtilizeMask, UseMask); }
bool RenderPreStencil(FRenderingCompositePassContext& Context, const FMatrix& ComponentToWorldMatrix, const FMatrix& FrustumComponentToClip) { const FViewInfo& View = Context.View; float Distance = (View.ViewMatrices.ViewOrigin - ComponentToWorldMatrix.GetOrigin()).Size(); float Radius = ComponentToWorldMatrix.GetMaximumAxisScale(); // if not inside if(Distance > Radius) { float EstimatedDecalSize = Radius / Distance; float StencilSizeThreshold = CVarStencilSizeThreshold.GetValueOnRenderThread(); // Check if it's large enough on screen if(EstimatedDecalSize < StencilSizeThreshold) { return false; } } FDecalRendering::SetVertexShaderOnly(Context.RHICmdList, View, FrustumComponentToClip); // Set states, the state cache helps us avoiding redundant sets Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<FM_Solid, CM_None>::GetRHI()); // all the same to have DX10 working Context.RHICmdList.SetBlendState(TStaticBlendState< CW_NONE, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One, // Emissive CW_NONE, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One, // Normal CW_NONE, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One, // Metallic, Specular, Roughness CW_NONE, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One // BaseColor >::GetRHI() ); // Carmack's reverse the sandbox stencil bit on the bounds Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState< false,CF_LessEqual, true,CF_Always,SO_Keep,SO_Keep,SO_Invert, true,CF_Always,SO_Keep,SO_Keep,SO_Invert, STENCIL_SANDBOX_MASK,STENCIL_SANDBOX_MASK >::GetRHI(), 0); // Render decal mask Context.RHICmdList.DrawIndexedPrimitive(GetUnitCubeIndexBuffer(), PT_TriangleList, 0, 0, 8, 0, ARRAY_COUNT(GCubeIndices) / 3, 1); return true; }
bool FRCPassPostProcessWeightedSampleSum::DoFastBlur() const { bool bRet = false; // only do the fast blur only with bilinear filtering if(CombineMethod == EFCM_Weighted) { const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0); // input is not hooked up correctly check(InputDesc); if(FilterShape == EFS_Horiz) { FIntPoint SrcSize = InputDesc->Extent; int32 SrcSizeForThisAxis = SrcSize.X; // in texel (input resolution), *2 as we use the diameter // we scale by width because FOV is defined horizontally float EffectiveBlurRadius = SizeScale * SrcSizeForThisAxis * 2 / 100.0f; #if PLATFORM_HTML5 float FastBlurThreshold = CVarFastBlurThreshold.GetValueOnGameThread(); #else float FastBlurThreshold = CVarFastBlurThreshold.GetValueOnRenderThread(); #endif // small radius look too different with this optimization so we only to it for larger radius bRet = EffectiveBlurRadius >= FastBlurThreshold; } else { FIntPoint SrcSize = InputDesc->Extent; FIntPoint BufferSize = GSceneRenderTargets.GetBufferSizeXY(); float InputRatio = SrcSize.X / (float)SrcSize.Y; float BufferRatio = BufferSize.X / (float)BufferSize.Y; // Half res input detected bRet = InputRatio < BufferRatio * 0.75f; } } return bRet; }
void SetParameters(const FRenderingCompositePassContext& Context) { const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader(); FGlobalShader::SetParameters(Context.RHICmdList, ShaderRHI, Context.View); DeferredParameters.Set(Context.RHICmdList, ShaderRHI, Context.View); if(CVarSSSFilter.GetValueOnRenderThread()) { PostprocessParameter.SetPS(ShaderRHI, Context, TStaticSamplerState<SF_Bilinear,AM_Border,AM_Border,AM_Border>::GetRHI()); } else { PostprocessParameter.SetPS(ShaderRHI, Context, TStaticSamplerState<SF_Point,AM_Border,AM_Border,AM_Border>::GetRHI()); } SubsurfaceParameters.SetParameters(Context.RHICmdList, ShaderRHI, Context); }
// @param Quality usually in 0..100 range, default is 50 // @return see CVarSSRQuality, never 0 static int32 ComputeSSRQuality(float Quality) { int32 Ret; if(Quality >= 60.0f) { Ret = (Quality >= 80.0f) ? 4 : 3; } else { Ret = (Quality >= 40.0f) ? 2 : 1; } int SSRQualityCVar = FMath::Max(0, CVarSSRQuality.GetValueOnRenderThread()); return FMath::Min(Ret, SSRQualityCVar); }
EBasePassSort::Type GetSortMode() { int32 SortMode = GSortBasePass.GetValueOnRenderThread(); if (SortMode >= EBasePassSort::FirstForcedMode && SortMode <= EBasePassSort::LastForcedMode) { return (EBasePassSort::Type)SortMode; } // Determine automatically. if (GHardwareHiddenSurfaceRemoval) { return EBasePassSort::None; } else { return EBasePassSort::SortPerMesh; } }
static uint32 Compute1DGaussianFilterKernel(ERHIFeatureLevel::Type InFeatureLevel, float KernelRadius, FVector2D OutOffsetAndWeight[MAX_FILTER_SAMPLES], uint32 MaxFilterSamples, EFilterShape FilterShape, float CrossCenterWeight) { float FilterSizeScale = FMath::Clamp(CVarFilterSizeScale.GetValueOnRenderThread(), 0.1f, 10.0f); float ClampedKernelRadius = FRCPassPostProcessWeightedSampleSum::GetClampedKernelRadius(InFeatureLevel, KernelRadius); int32 IntegerKernelRadius = FRCPassPostProcessWeightedSampleSum::GetIntegerKernelRadius(InFeatureLevel, KernelRadius * FilterSizeScale); float Scale = 1.0f / ClampedKernelRadius; // smallest IntegerKernelRadius will be 1 uint32 NumSamples = 0; float WeightSum = 0.0f; for(int32 SampleIndex = -IntegerKernelRadius; SampleIndex <= IntegerKernelRadius; SampleIndex += 2) { float Weight0 = NormalDistributionUnscaled(SampleIndex, Scale, FilterShape, CrossCenterWeight); float Weight1 = 0.0f; // Because we use bilinear filtering we only require half the sample count. // But we need to fix the last weight. // Example (radius one texel, c is center, a left, b right): // a b c (a is left texel, b center and c right) becomes two lookups one with a*.. + b **, the other with // c * .. but another texel to the right would accidentially leak into this computation. if(SampleIndex != IntegerKernelRadius) { Weight1 = NormalDistributionUnscaled(SampleIndex + 1, Scale, FilterShape, CrossCenterWeight); } float TotalWeight = Weight0 + Weight1; OutOffsetAndWeight[NumSamples].X = (SampleIndex + Weight1 / TotalWeight); OutOffsetAndWeight[NumSamples].Y = TotalWeight; WeightSum += TotalWeight; NumSamples++; } // Normalize blur weights. float InvWeightSum = 1.0f / WeightSum; for(uint32 SampleIndex = 0;SampleIndex < NumSamples; ++SampleIndex) { OutOffsetAndWeight[SampleIndex].Y *= InvWeightSum; } return NumSamples; }
uint32 FRCPassPostProcessWeightedSampleSum::GetMaxNumSamples(ERHIFeatureLevel::Type InFeatureLevel) { if (CVarLoopMode.GetValueOnRenderThread() != 0) { return MAX_FILTER_SAMPLES; } uint32 MaxNumSamples = MAX_FILTER_COMPILE_TIME_SAMPLES; if (InFeatureLevel == ERHIFeatureLevel::SM4) { MaxNumSamples = 16; } else if (InFeatureLevel < ERHIFeatureLevel::SM4) { MaxNumSamples = 7; } return MaxNumSamples; }
void SetPS(const FRenderingCompositePassContext& Context) { const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader(); FGlobalShader::SetParameters(Context.RHICmdList, ShaderRHI, Context.View); FSamplerStateRHIParamRef FilterTable[2]; FilterTable[0] = TStaticSamplerState<SF_Bilinear,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI(); FilterTable[1] = TStaticSamplerState<SF_Point,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI(); PostprocessParameter.SetPS(ShaderRHI, Context, 0, false, FilterTable); DeferredParameters.Set(Context.RHICmdList, ShaderRHI, Context.View); { float UpscaleSoftnessValue = FMath::Clamp(CVarUpscaleSoftness.GetValueOnRenderThread(), 0.0f, 1.0f); SetShaderValue(Context.RHICmdList, ShaderRHI, UpscaleSoftness, UpscaleSoftnessValue); } }
void FLightPrimitiveInteraction::Create(FLightSceneInfo* LightSceneInfo,FPrimitiveSceneInfo* PrimitiveSceneInfo) { // Attach the light to the primitive's static meshes. bool bDynamic = true; bool bRelevant = false; bool bLightMapped = true; bool bShadowMapped = false; // Determine the light's relevance to the primitive. check(PrimitiveSceneInfo->Proxy); PrimitiveSceneInfo->Proxy->GetLightRelevance(LightSceneInfo->Proxy, bDynamic, bRelevant, bLightMapped, bShadowMapped); if (bRelevant && bDynamic // Don't let lights with static shadowing or static lighting affect primitives that should use static lighting, but don't have valid settings (lightmap res 0, etc) // This prevents those components with invalid lightmap settings from causing lighting to remain unbuilt after a build && !(LightSceneInfo->Proxy->HasStaticShadowing() && PrimitiveSceneInfo->Proxy->HasStaticLighting() && !PrimitiveSceneInfo->Proxy->HasValidSettingsForStaticLighting())) { const bool bTranslucentObjectShadow = LightSceneInfo->Proxy->CastsTranslucentShadows() && PrimitiveSceneInfo->Proxy->CastsVolumetricTranslucentShadow(); const bool bInsetObjectShadow = // Currently only supporting inset shadows on directional lights, but could be made to work with any whole scene shadows LightSceneInfo->Proxy->GetLightType() == LightType_Directional && PrimitiveSceneInfo->Proxy->CastsInsetShadow(); // Movable directional lights determine shadow relevance dynamically based on the view and CSM settings. Interactions are only required for per-object cases. if (LightSceneInfo->Proxy->GetLightType() != LightType_Directional || LightSceneInfo->Proxy->HasStaticShadowing() || bTranslucentObjectShadow || bInsetObjectShadow) { // Create the light interaction. FLightPrimitiveInteraction* Interaction = new FLightPrimitiveInteraction(LightSceneInfo, PrimitiveSceneInfo, bDynamic, bLightMapped, bShadowMapped, bTranslucentObjectShadow, bInsetObjectShadow); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) // Treat the light as completely unbuilt if it has more unbuilt interactions than the threshold. // This will result in the light using whole scene shadows instead of many per-object shadows, // Which prevents poor performance when many per-object shadows are created for previewing unbuilt lighting. if (LightSceneInfo->NumUnbuiltInteractions >= CVarWholeSceneShadowUnbuiltInteractionThreshold.GetValueOnRenderThread() && LightSceneInfo->bPrecomputedLightingIsValid) { LightSceneInfo->bPrecomputedLightingIsValid = false; LightSceneInfo->Proxy->InvalidatePrecomputedLighting(true); } #endif } } }
void FDeferredShadingSceneRenderer::RenderVelocities(FRHICommandListImmediate& RHICmdList, TRefCountPtr<IPooledRenderTarget>& VelocityRT) { check(FeatureLevel >= ERHIFeatureLevel::SM4); SCOPE_CYCLE_COUNTER(STAT_RenderVelocities); if (!ShouldRenderVelocities()) { return; } // this is not supported check(!Views[0].bIsSceneCapture); SCOPED_DRAW_EVENT(RHICmdList, RenderVelocities); FPooledRenderTargetDesc Desc = FVelocityRendering::GetRenderTargetDesc(); GRenderTargetPool.FindFreeElement(Desc, VelocityRT, TEXT("Velocity")); GPrevPerBoneMotionBlur.LockData(); BeginVelocityRendering(RHICmdList, VelocityRT, true); if (GRHICommandList.UseParallelAlgorithms() && CVarParallelVelocity.GetValueOnRenderThread()) { RenderVelocitiesInnerParallel(RHICmdList, VelocityRT); } else { RenderVelocitiesInner(RHICmdList, VelocityRT); } RHICmdList.CopyToResolveTarget(VelocityRT->GetRenderTargetItem().TargetableTexture, VelocityRT->GetRenderTargetItem().ShaderResourceTexture, false, FResolveParams()); // restore any color write state changes RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); GPrevPerBoneMotionBlur.UnlockData(); // to be able to observe results with VisualizeTexture GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, VelocityRT); }
void FCompositionLighting::ProcessAfterLighting(FRHICommandListImmediate& RHICmdList, FViewInfo& View) { check(IsInRenderingThread()); FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, SceneContext.ReflectiveShadowMapDiffuse); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, SceneContext.ReflectiveShadowMapNormal); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, SceneContext.ReflectiveShadowMapDepth); { FMemMark Mark(FMemStack::Get()); FRenderingCompositePassContext CompositeContext(RHICmdList, View); FPostprocessContext Context(CompositeContext.Graph, View); FRenderingCompositeOutputRef AmbientOcclusion; // Screen Space Subsurface Scattering { float Radius = CVarSSSScale.GetValueOnRenderThread(); bool bSimpleDynamicLighting = IsSimpleDynamicLightingEnabled(); bool bScreenSpaceSubsurfacePassNeeded = (View.ShadingModelMaskInView & (1 << MSM_SubsurfaceProfile)) != 0; if (bScreenSpaceSubsurfacePassNeeded && Radius > 0 && !bSimpleDynamicLighting && View.Family->EngineShowFlags.SubsurfaceScattering && //@todo-rco: Remove this when we fix the cross-compiler !IsOpenGLPlatform(View.GetShaderPlatform())) { // can be optimized out if we don't do split screen/stereo rendering (should be done after we some post process refactoring) FRenderingCompositePass* PassExtractSpecular = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSubsurfaceExtractSpecular()); PassExtractSpecular->SetInput(ePId_Input0, Context.FinalOutput); FRenderingCompositePass* PassSetup = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSubsurfaceSetup(View)); PassSetup->SetInput(ePId_Input0, Context.FinalOutput); FRenderingCompositePass* Pass0 = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSubsurface(0)); Pass0->SetInput(ePId_Input0, PassSetup); FRenderingCompositePass* Pass1 = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSubsurface(1)); Pass1->SetInput(ePId_Input0, Pass0); Pass1->SetInput(ePId_Input1, PassSetup); // full res composite pass, no blurring (Radius=0) FRenderingCompositePass* RecombinePass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSubsurfaceRecombine()); RecombinePass->SetInput(ePId_Input0, Pass1); RecombinePass->SetInput(ePId_Input1, PassExtractSpecular); SCOPED_DRAW_EVENT(RHICmdList, CompositionLighting_SSSSS); CompositeContext.Process(RecombinePass, TEXT("CompositionLighting_SSSSS")); } } // The graph setup should be finished before this line ---------------------------------------- SCOPED_DRAW_EVENT(RHICmdList, CompositionAfterLighting); // we don't replace the final element with the scenecolor because this is what those passes should do by themself CompositeContext.Process(Context.FinalOutput.GetPass(), TEXT("CompositionLighting")); } // We only release the after the last view was processed (SplitScreen) if(View.Family->Views[View.Family->Views.Num() - 1] == &View) { // The RT should be released as early as possible to allow sharing of that memory for other purposes. // This becomes even more important with some limited VRam (XBoxOne). SceneContext.SetLightAttenuation(0); } }
void FDecalRendering::BuildVisibleDecalList(const FScene& Scene, const FViewInfo& View, EDecalRenderStage DecalRenderStage, FTransientDecalRenderDataList& OutVisibleDecals) { QUICK_SCOPE_CYCLE_COUNTER(BuildVisibleDecalList); OutVisibleDecals.Empty(Scene.Decals.Num()); const float FadeMultiplier = CVarDecalFadeScreenSizeMultiplier.GetValueOnRenderThread(); const EShaderPlatform ShaderPlatform = View.GetShaderPlatform(); const bool bIsPerspectiveProjection = View.IsPerspectiveProjection(); // Build a list of decals that need to be rendered for this view in SortedDecals for (const FDeferredDecalProxy* DecalProxy : Scene.Decals) { bool bIsShown = true; if (!DecalProxy->IsShown(&View)) { bIsShown = false; } const FMatrix ComponentToWorldMatrix = DecalProxy->ComponentTrans.ToMatrixWithScale(); // can be optimized as we test against a sphere around the box instead of the box itself const float ConservativeRadius = FMath::Sqrt( ComponentToWorldMatrix.GetScaledAxis(EAxis::X).SizeSquared() + ComponentToWorldMatrix.GetScaledAxis(EAxis::Y).SizeSquared() + ComponentToWorldMatrix.GetScaledAxis(EAxis::Z).SizeSquared()); // can be optimized as the test is too conservative (sphere instead of OBB) if(ConservativeRadius < SMALL_NUMBER || !View.ViewFrustum.IntersectSphere(ComponentToWorldMatrix.GetOrigin(), ConservativeRadius)) { bIsShown = false; } if (bIsShown) { FTransientDecalRenderData Data(Scene, DecalProxy, ConservativeRadius); // filter out decals with blend modes that are not supported on current platform if (IsBlendModeSupported(ShaderPlatform, Data.DecalBlendMode)) { if (bIsPerspectiveProjection && Data.DecalProxy->Component->FadeScreenSize != 0.0f) { float Distance = (View.ViewMatrices.ViewOrigin - ComponentToWorldMatrix.GetOrigin()).Size(); float Radius = ComponentToWorldMatrix.GetMaximumAxisScale(); float CurrentScreenSize = ((Radius / Distance) * FadeMultiplier); // fading coefficient needs to increase with increasing field of view and decrease with increasing resolution // FadeCoeffScale is an empirically determined constant to bring us back roughly to fraction of screen size for FadeScreenSize const float FadeCoeffScale = 600.0f; float FOVFactor = ((2.0f/View.ViewMatrices.ProjMatrix.M[0][0]) / View.ViewRect.Width()) * FadeCoeffScale; float FadeCoeff = Data.DecalProxy->Component->FadeScreenSize * FOVFactor; float FadeRange = FadeCoeff * 0.5f; float Alpha = (CurrentScreenSize - FadeCoeff) / FadeRange; Data.FadeAlpha = FMath::Min(Alpha, 1.0f); } EDecalRenderStage LocalDecalRenderStage = FDecalRenderingCommon::ComputeRenderStage(ShaderPlatform, Data.DecalBlendMode); // we could do this test earlier to avoid the decal intersection but getting DecalBlendMode also costs if (View.Family->EngineShowFlags.ShaderComplexity || (DecalRenderStage == LocalDecalRenderStage && Data.FadeAlpha>0.0f) ) { OutVisibleDecals.Add(Data); } } } } if (OutVisibleDecals.Num() > 0) { // Sort by sort order to allow control over composited result // Then sort decals by state to reduce render target switches // Also sort by component since Sort() is not stable struct FCompareFTransientDecalRenderData { FORCEINLINE bool operator()(const FTransientDecalRenderData& A, const FTransientDecalRenderData& B) const { if (B.DecalProxy->SortOrder != A.DecalProxy->SortOrder) { return A.DecalProxy->SortOrder < B.DecalProxy->SortOrder; } // bHasNormal here is more important then blend mode because we want to render every decals that output normals before those that read normal. if (B.bHasNormal != A.bHasNormal) { return B.bHasNormal < A.bHasNormal; // < so that those outputting normal are first. } if (B.DecalBlendMode != A.DecalBlendMode) { return (int32)B.DecalBlendMode < (int32)A.DecalBlendMode; } // Batch decals with the same material together if (B.MaterialProxy != A.MaterialProxy) { return B.MaterialProxy < A.MaterialProxy; } return (PTRINT)B.DecalProxy->Component < (PTRINT)A.DecalProxy->Component; } }; // Sort decals by blend mode to reduce render target switches OutVisibleDecals.Sort(FCompareFTransientDecalRenderData()); } }
void ScreenSpaceReflections(FRHICommandListImmediate& RHICmdList, FViewInfo& View, TRefCountPtr<IPooledRenderTarget>& SSROutput) { BuildHZB(RHICmdList, View); FRenderingCompositePassContext CompositeContext(RHICmdList, View); FPostprocessContext Context( CompositeContext.Graph, View ); FSceneViewState* ViewState = (FSceneViewState*)Context.View.State; FRenderingCompositePass* SceneColorInput = Context.Graph.RegisterPass( new FRCPassPostProcessInput( GSceneRenderTargets.GetSceneColor() ) ); FRenderingCompositePass* HZBInput = Context.Graph.RegisterPass( new FRCPassPostProcessInput( ViewState->HZB.Texture ) ); bool bPrevFrame = 0; if( ViewState && ViewState->TemporalAAHistoryRT && !Context.View.bCameraCut ) { SceneColorInput = Context.Graph.RegisterPass( new FRCPassPostProcessInput( ViewState->TemporalAAHistoryRT ) ); bPrevFrame = 1; } { FRenderingCompositePass* TracePass = Context.Graph.RegisterPass( new FRCPassPostProcessScreenSpaceReflections( bPrevFrame ) ); TracePass->SetInput( ePId_Input0, SceneColorInput ); TracePass->SetInput( ePId_Input1, HZBInput ); Context.FinalOutput = FRenderingCompositeOutputRef( TracePass ); } const bool bTemporalFilter = View.FinalPostProcessSettings.AntiAliasingMethod != AAM_TemporalAA || CVarSSRTemporal.GetValueOnRenderThread() != 0; if( ViewState && bTemporalFilter ) { { FRenderingCompositeOutputRef HistoryInput; if( ViewState && ViewState->SSRHistoryRT && !Context.View.bCameraCut ) { HistoryInput = Context.Graph.RegisterPass( new FRCPassPostProcessInput( ViewState->SSRHistoryRT ) ); } else { // No history, use black HistoryInput = Context.Graph.RegisterPass(new FRCPassPostProcessInput(GSystemTextures.BlackDummy)); } FRenderingCompositePass* TemporalAAPass = Context.Graph.RegisterPass( new FRCPassPostProcessSSRTemporalAA ); TemporalAAPass->SetInput( ePId_Input0, Context.FinalOutput ); TemporalAAPass->SetInput( ePId_Input1, HistoryInput ); //TemporalAAPass->SetInput( ePId_Input2, VelocityInput ); Context.FinalOutput = FRenderingCompositeOutputRef( TemporalAAPass ); } if( ViewState ) { FRenderingCompositePass* HistoryOutput = Context.Graph.RegisterPass( new FRCPassPostProcessOutput( &ViewState->SSRHistoryRT ) ); HistoryOutput->SetInput( ePId_Input0, Context.FinalOutput ); Context.FinalOutput = FRenderingCompositeOutputRef( HistoryOutput ); } } { FRenderingCompositePass* ReflectionOutput = Context.Graph.RegisterPass( new FRCPassPostProcessOutput( &SSROutput ) ); ReflectionOutput->SetInput( ePId_Input0, Context.FinalOutput ); Context.FinalOutput = FRenderingCompositeOutputRef( ReflectionOutput ); } CompositeContext.Root->AddDependency( Context.FinalOutput ); CompositeContext.Process(TEXT("ReflectionEnvironments")); }
/** * Renders the view family. */ void FDeferredShadingSceneRenderer::Render() { if(!ViewFamily.EngineShowFlags.Rendering) { return; } SCOPED_DRAW_EVENT(Scene,DEC_SCENE_ITEMS); // Initialize global system textures (pass-through if already initialized). GSystemTextures.InitializeTextures(); // Allocate the maximum scene render target space for the current view family. GSceneRenderTargets.Allocate(ViewFamily); // Find the visible primitives. InitViews(); const bool bIsWireframe = ViewFamily.EngineShowFlags.Wireframe; static const auto ClearMethodCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.ClearSceneMethod")); bool bRequiresRHIClear = true; bool bRequiresFarZQuadClear = false; if (ClearMethodCVar) { switch (ClearMethodCVar->GetValueOnRenderThread()) { case 0: // No clear { bRequiresRHIClear = false; bRequiresFarZQuadClear = false; break; } case 1: // RHIClear { bRequiresRHIClear = true; bRequiresFarZQuadClear = false; break; } case 2: // Clear using far-z quad { bRequiresFarZQuadClear = true; bRequiresRHIClear = false; break; } } } // Always perform a full buffer clear for wireframe, shader complexity view mode, and stationary light overlap viewmode. if (bIsWireframe || ViewFamily.EngineShowFlags.ShaderComplexity || ViewFamily.EngineShowFlags.StationaryLightOverlap) { bRequiresRHIClear = true; } // force using occ queries for wireframe if rendering is parented or frozen in the first view check(Views.Num()); #if (UE_BUILD_SHIPPING || UE_BUILD_TEST) const bool bIsViewFrozen = false; const bool bHasViewParent = false; #else const bool bIsViewFrozen = Views[0].State && ((FSceneViewState*)Views[0].State)->bIsFrozen; const bool bHasViewParent = Views[0].State && ((FSceneViewState*)Views[0].State)->HasViewParent(); #endif const bool bIsOcclusionTesting = DoOcclusionQueries() && (!bIsWireframe || bIsViewFrozen || bHasViewParent); // Dynamic vertex and index buffers need to be committed before rendering. FGlobalDynamicVertexBuffer::Get().Commit(); FGlobalDynamicIndexBuffer::Get().Commit(); // Notify the FX system that the scene is about to be rendered. if (Scene->FXSystem) { Scene->FXSystem->PreRender(); } // Draw the scene pre-pass / early z pass, populating the scene depth buffer and HiZ RenderPrePass(); // Clear scene color buffer if necessary. if ( bRequiresRHIClear ) { ClearView(); // Only clear once. bRequiresRHIClear = false; } // Clear LPVs for all views if ( IsFeatureLevelSupported(GRHIShaderPlatform, ERHIFeatureLevel::SM5) ) { ClearLPVs(); } // only temporarily available after early z pass and until base pass check(!GSceneRenderTargets.DBufferA); check(!GSceneRenderTargets.DBufferB); check(!GSceneRenderTargets.DBufferC); if(IsDBufferEnabled()) { GSceneRenderTargets.ResolveSceneDepthTexture(); // Resolve the scene depth to an auxiliary texture when SM3/SM4 is in use. This needs to happen so the auxiliary texture can be bound as a shader parameter // while the primary scene depth texture can be bound as the target. Simultaneously binding a single DepthStencil resource as a parameter and target // is unsupported in d3d feature level 10. if(!(GRHIFeatureLevel >= ERHIFeatureLevel::SM5) && GRHIFeatureLevel >= ERHIFeatureLevel::SM4) { GSceneRenderTargets.ResolveSceneDepthToAuxiliaryTexture(); } // e.g. ambient cubemaps, ambient occlusion, deferred decals for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++) { SCOPED_CONDITIONAL_DRAW_EVENTF(EventView,Views.Num() > 1, DEC_SCENE_ITEMS, TEXT("View%d"), ViewIndex); GCompositionLighting.ProcessBeforeBasePass(Views[ViewIndex]); } } if(bIsWireframe && FDeferredShadingSceneRenderer::ShouldCompositeEditorPrimitives(Views[0])) { // In Editor we want wire frame view modes to be MSAA for better quality. Resolve will be done with EditorPrimitives RHISetRenderTarget(GSceneRenderTargets.GetEditorPrimitivesColor(), GSceneRenderTargets.GetEditorPrimitivesDepth()); RHIClear(true, FLinearColor(0, 0, 0, 0), true, 0.0f, false, 0, FIntRect()); } else { // Begin rendering to scene color GSceneRenderTargets.BeginRenderingSceneColor(true); } RenderBasePass(); if(ViewFamily.EngineShowFlags.VisualizeLightCulling) { // clear out emissive and baked lighting (not too efficient but simple and only needed for this debug view) GSceneRenderTargets.BeginRenderingSceneColor(false); RHIClear(true, FLinearColor(0, 0, 0, 0), false, 0, false, 0, FIntRect()); } GSceneRenderTargets.DBufferA.SafeRelease(); GSceneRenderTargets.DBufferB.SafeRelease(); GSceneRenderTargets.DBufferC.SafeRelease(); // only temporarily available after early z pass and until base pass check(!GSceneRenderTargets.DBufferA); check(!GSceneRenderTargets.DBufferB); check(!GSceneRenderTargets.DBufferC); if (bRequiresFarZQuadClear) { // Clears view by drawing quad at maximum Z // TODO: if all the platforms have fast color clears, we can replace this with an RHIClear. ClearGBufferAtMaxZ(); bRequiresFarZQuadClear = false; } GSceneRenderTargets.ResolveSceneColor(FResolveRect(0, 0, ViewFamily.FamilySizeX, ViewFamily.FamilySizeY)); GSceneRenderTargets.ResolveSceneDepthTexture(); // Resolve the scene depth to an auxiliary texture when SM3/SM4 is in use. This needs to happen so the auxiliary texture can be bound as a shader parameter // while the primary scene depth texture can be bound as the target. Simultaneously binding a single DepthStencil resource as a parameter and target // is unsupported in d3d feature level 10. if(!GSupportsDepthFetchDuringDepthTest) { GSceneRenderTargets.ResolveSceneDepthToAuxiliaryTexture(); } RenderCustomDepthPass(); // Notify the FX system that opaque primitives have been rendered and we now have a valid depth buffer. if (Scene->FXSystem && Views.IsValidIndex(0)) { Scene->FXSystem->PostRenderOpaque( Views.GetTypedData(), GSceneRenderTargets.GetSceneDepthTexture(), GSceneRenderTargets.GetGBufferATexture() ); } // Update the quarter-sized depth buffer with the current contents of the scene depth texture. // This needs to happen before occlusion tests, which makes use of the small depth buffer. UpdateDownsampledDepthSurface(); // Issue occlusion queries // This is done after the downsampled depth buffer is created so that it can be used for issuing queries if ( bIsOcclusionTesting ) { BeginOcclusionTests(); } // Render lighting. if (ViewFamily.EngineShowFlags.Lighting && GRHIFeatureLevel >= ERHIFeatureLevel::SM4 && ViewFamily.EngineShowFlags.DeferredLighting ) { // Pre-lighting composition lighting stage // e.g. deferred decals, blurred GBuffer for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++) { SCOPED_CONDITIONAL_DRAW_EVENTF(EventView,Views.Num() > 1, DEC_SCENE_ITEMS, TEXT("View%d"), ViewIndex); GCompositionLighting.ProcessAfterBasePass(Views[ViewIndex]); } // Clear the translucent lighting volumes before we accumulate ClearTranslucentVolumeLighting(); RenderLights(); InjectAmbientCubemapTranslucentVolumeLighting(); CompositeIndirectTranslucentVolumeLighting(); // Filter the translucency lighting volume now that it is complete FilterTranslucentVolumeLighting(); // Clear LPVs for all views if ( IsFeatureLevelSupported(GRHIShaderPlatform, ERHIFeatureLevel::SM5) ) { PropagateLPVs(); } // Render reflections that only operate on opaque pixels RenderDeferredReflections(); // Post-lighting composition lighting stage // e.g. ambient cubemaps, ambient occlusion, LPV indirect for(int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { SCOPED_CONDITIONAL_DRAW_EVENTF(EventView,Views.Num() > 1, DEC_SCENE_ITEMS, TEXT("View%d"), ViewIndex); GCompositionLighting.ProcessLighting(Views[ViewIndex]); } } if( ViewFamily.EngineShowFlags.StationaryLightOverlap && GRHIFeatureLevel >= ERHIFeatureLevel::SM4) { RenderStationaryLightOverlap(); } FLightShaftsOutput LightShaftOutput; // Draw Lightshafts if (ViewFamily.EngineShowFlags.LightShafts) { LightShaftOutput = RenderLightShaftOcclusion(); } // Draw atmosphere if(ShouldRenderAtmosphere(ViewFamily)) { if (Scene->AtmosphericFog) { // Update RenderFlag based on LightShaftTexture is valid or not if (LightShaftOutput.bRendered) { Scene->AtmosphericFog->RenderFlag &= EAtmosphereRenderFlag::E_LightShaftMask; } else { Scene->AtmosphericFog->RenderFlag |= EAtmosphereRenderFlag::E_DisableLightShaft; } #if WITH_EDITOR if (Scene->bIsEditorScene) { // Precompute Atmospheric Textures Scene->AtmosphericFog->PrecomputeTextures(Views.GetTypedData(), &ViewFamily); } #endif RenderAtmosphere(LightShaftOutput); } } // Draw fog. if(ShouldRenderFog(ViewFamily)) { RenderFog(LightShaftOutput); } // No longer needed, release LightShaftOutput.LightShaftOcclusion = NULL; // Draw translucency. if(ViewFamily.EngineShowFlags.Translucency) { SCOPE_CYCLE_COUNTER(STAT_TranslucencyDrawTime); if(ViewFamily.EngineShowFlags.Refraction) { // to apply refraction effect by distorting the scene color RenderDistortion(); } RenderTranslucency(); } if (ViewFamily.EngineShowFlags.LightShafts) { RenderLightShaftBloom(); } // Resolve the scene color for post processing. GSceneRenderTargets.ResolveSceneColor(FResolveRect(0, 0, ViewFamily.FamilySizeX, ViewFamily.FamilySizeY)); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if(CVarTestUIBlur.GetValueOnRenderThread() > 0) { Views[0].UIBlurOverrideRectangles.Add(FIntRect(20, 20, 400, 400)); } #endif // Finish rendering for each view. if(ViewFamily.bResolveScene) { SCOPED_DRAW_EVENT(FinishRendering, DEC_SCENE_ITEMS); SCOPE_CYCLE_COUNTER(STAT_FinishRenderViewTargetTime); for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++) { SCOPED_CONDITIONAL_DRAW_EVENTF(EventView, Views.Num() > 1, DEC_SCENE_ITEMS, TEXT("View%d"), ViewIndex); FinishRenderViewTarget(&Views[ViewIndex], ViewIndex == (Views.Num() - 1)); } } RenderFinish(); }
void FRCPassPostProcessBokehDOFRecombine::Process(FRenderingCompositePassContext& Context) { uint32 Method = 2; FRenderingCompositeOutputRef* Input1 = GetInput(ePId_Input1); if(Input1 && Input1->GetPass()) { if(GetInput(ePId_Input2)->GetPass()) { Method = 3; } else { Method = 1; } } else { check(GetInput(ePId_Input2)->GetPass()); } FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(Context.RHICmdList); FIntPoint OutScaledSize; float OutScale; SceneContext.GetSeparateTranslucencyDimensions(OutScaledSize, OutScale); const bool bUseNearestDepthNeighborUpsample = CVarSeparateTranslucencyUpsampleMode.GetValueOnRenderThread() != 0 && FMath::Abs(OutScale - .5f) < .001f; if (Method != 1 && bUseNearestDepthNeighborUpsample) { Method += 2; } const FSceneView& View = Context.View; SCOPED_DRAW_EVENTF(Context.RHICmdList, BokehDOFRecombine, TEXT("BokehDOFRecombine#%d %dx%d"), Method, View.ViewRect.Width(), View.ViewRect.Height()); const FPooledRenderTargetDesc* InputDesc0 = GetInputDesc(ePId_Input0); const FPooledRenderTargetDesc* InputDesc1 = GetInputDesc(ePId_Input1); FIntPoint TexSize = InputDesc1 ? InputDesc1->Extent : InputDesc0->Extent; // usually 1, 2, 4 or 8 uint32 ScaleToFullRes = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / TexSize.X; FIntRect HalfResViewRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleToFullRes); const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context); // Set the view family's render target/viewport. SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef()); // is optimized away if possible (RT size=view size, ) Context.RHICmdList.Clear(true, FLinearColor::Black, false, 1.0f, false, 0, View.ViewRect); Context.SetViewportAndCallRHI(View.ViewRect); // set the state Context.RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI()); switch(Method) { case 1: SetShader<1>(Context); break; case 2: SetShader<2>(Context); break; case 3: SetShader<3>(Context); break; case 4: SetShader<4>(Context); break; case 5: SetShader<5>(Context); break; default: check(0); } TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap()); DrawPostProcessPass( Context.RHICmdList, 0, 0, View.ViewRect.Width(), View.ViewRect.Height(), HalfResViewRect.Min.X, HalfResViewRect.Min.Y, HalfResViewRect.Width(), HalfResViewRect.Height(), View.ViewRect.Size(), TexSize, *VertexShader, View.StereoPass, Context.HasHmdMesh(), EDRF_UseTriangleOptimization); Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams()); }
void FRCPassPostProcessSubsurface::Process(FRenderingCompositePassContext& Context) { const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0); check(InputDesc); { const IPooledRenderTarget* PooledRT = GetSubsufaceProfileTexture_RT(Context.RHICmdList); check(PooledRT); // for debugging GRenderTargetPool.VisualizeTexture.SetCheckPoint(Context.RHICmdList, PooledRT); } const FSceneView& View = Context.View; const FSceneViewFamily& ViewFamily = *(View.Family); FIntPoint SrcSize = InputDesc->Extent; FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent; check(DestSize.X); check(DestSize.Y); check(SrcSize.X); check(SrcSize.Y); FIntRect SrcRect = FIntRect(0, 0, DestSize.X, DestSize.Y); FIntRect DestRect = SrcRect; TRefCountPtr<IPooledRenderTarget> NewSceneColor; const FSceneRenderTargetItem* DestRenderTarget; { DestRenderTarget = &PassOutputs[0].RequestSurface(Context); check(DestRenderTarget); } // Set the view family's render target/viewport. SetRenderTarget(Context.RHICmdList, DestRenderTarget->TargetableTexture, FTextureRHIRef()); Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f ); Context.RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI()); TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap()); SCOPED_DRAW_EVENTF(Context.RHICmdList, SubsurfacePass, TEXT("SubsurfaceDirection#%d"), Direction); uint32 SampleSet = FMath::Clamp(CVarSSSSampleSet.GetValueOnRenderThread(), 0, 2); if (Direction == 0) { SetSubsurfaceShaderSampleSet<0>(Context, VertexShader, SampleSet); } else { SetSubsurfaceShaderSampleSet<1>(Context, VertexShader, SampleSet); } DrawPostProcessPass( Context.RHICmdList, DestRect.Min.X, DestRect.Min.Y, DestRect.Width(), DestRect.Height(), SrcRect.Min.X, SrcRect.Min.Y, SrcRect.Width(), SrcRect.Height(), DestSize, SrcSize, *VertexShader, View.StereoPass, Context.HasHmdMesh(), EDRF_UseTriangleOptimization); Context.RHICmdList.CopyToResolveTarget(DestRenderTarget->TargetableTexture, DestRenderTarget->ShaderResourceTexture, false, FResolveParams()); }
bool FOpenGLES31::SupportsDisjointTimeQueries() { bool bAllowDisjointTimerQueries = false; bAllowDisjointTimerQueries = (CVarDisjointTimerQueries.GetValueOnRenderThread() == 1); return bSupportsDisjointTimeQueries && bAllowDisjointTimerQueries; }
void FRCPassPostProcessScreenSpaceReflections::Process(FRenderingCompositePassContext& Context) { auto& RHICmdList = Context.RHICmdList; FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); const FSceneView& View = Context.View; const auto FeatureLevel = Context.GetFeatureLevel(); int32 SSRQuality = ComputeSSRQuality(View.FinalPostProcessSettings.ScreenSpaceReflectionQuality); uint32 iPreFrame = bPrevFrame ? 1 : 0; SSRQuality = FMath::Clamp(SSRQuality, 1, 4); const bool VisualizeSSR = View.Family->EngineShowFlags.VisualizeSSR; const bool SSRStencilPrePass = CVarSSRStencil.GetValueOnRenderThread() != 0 && !VisualizeSSR; FRenderingCompositeOutputRef* Input2 = GetInput(ePId_Input2); const bool SSRConeTracing = Input2 && Input2->GetOutput(); if (VisualizeSSR) { iPreFrame = 0; SSRQuality = 0; } else if (SSRConeTracing) { SSRQuality = SSRConeQuality; } const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context); if (SSRStencilPrePass) { // ScreenSpaceReflectionsStencil draw event SCOPED_DRAW_EVENT(RHICmdList, ScreenSpaceReflectionsStencil); TShaderMapRef< FPostProcessVS > VertexShader(Context.GetShaderMap()); TShaderMapRef< FPostProcessScreenSpaceReflectionsStencilPS > PixelShader(Context.GetShaderMap()); // bind the dest render target and the depth stencil render target SetRenderTarget(RHICmdList, DestRenderTarget.TargetableTexture, SceneContext.GetSceneDepthSurface(), ESimpleRenderTargetMode::EUninitializedColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilWrite); Context.SetViewportAndCallRHI(View.ViewRect); // Clear stencil to 0 RHICmdList.Clear(false, FLinearColor::White, false, (float)ERHIZBuffer::FarPlane, true, 0, View.ViewRect); // bind shader static FGlobalBoundShaderState BoundShaderState; SetGlobalBoundShaderState(Context.RHICmdList, FeatureLevel, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader); VertexShader->SetParameters(Context); PixelShader->SetParameters(Context, SSRQuality, true); // Clobers the stencil to pixel that should not compute SSR RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always, true, CF_Always, SO_Replace, SO_Replace, SO_Replace>::GetRHI(), 0x80); // Set rasterizer state to solid RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); // disable blend mode RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); // Draw a quad mapping scene color to the view's render target to set stencil to set the stencil mask where it needs to be DrawRectangle( Context.RHICmdList, 0, 0, View.ViewRect.Width(), View.ViewRect.Height(), View.ViewRect.Min.X, View.ViewRect.Min.Y, View.ViewRect.Width(), View.ViewRect.Height(), View.ViewRect.Size(), SceneContext.GetBufferSizeXY(), *VertexShader, EDRF_UseTriangleOptimization); } // ScreenSpaceReflectionsStencil draw event { // ScreenSpaceReflections draw event SCOPED_DRAW_EVENT(Context.RHICmdList, ScreenSpaceReflections); if (SSRStencilPrePass) { // set up the stencil test to match 0, meaning FPostProcessScreenSpaceReflectionsStencilPS has been discarded RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always, true, CF_Equal, SO_Keep, SO_Keep, SO_Keep>::GetRHI(), 0); } else { // bind only the dest render target SetRenderTarget(RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef()); Context.SetViewportAndCallRHI(View.ViewRect); RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI()); } // clear DestRenderTarget only outside of the view's rectangle RHICmdList.Clear(true, FLinearColor::Black, false, (float)ERHIZBuffer::FarPlane, false, 0, View.ViewRect); // set the state RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); TShaderMapRef< FPostProcessVS > VertexShader(Context.GetShaderMap()); #define CASE(A, B) \ case (A + 2 * (B + 3 * 0 )): \ { \ TShaderMapRef< FPostProcessScreenSpaceReflectionsPS<A, B> > PixelShader(Context.GetShaderMap()); \ static FGlobalBoundShaderState BoundShaderState; \ SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader); \ VertexShader->SetParameters(Context); \ PixelShader->SetParameters(Context); \ }; \ break switch (iPreFrame + 2 * (SSRQuality + 3 * 0)) { CASE(0,0); CASE(0,1); CASE(1,1); CASE(0,2); CASE(1,2); CASE(0,3); CASE(1,3); CASE(0,4); CASE(1,4); CASE(0,5); CASE(1,5); //SSRConeQuality default: check(!"Missing case in FRCPassPostProcessScreenSpaceReflections"); } #undef CASE // Draw a quad mapping scene color to the view's render target DrawRectangle( RHICmdList, 0, 0, View.ViewRect.Width(), View.ViewRect.Height(), View.ViewRect.Min.X, View.ViewRect.Min.Y, View.ViewRect.Width(), View.ViewRect.Height(), View.ViewRect.Size(), FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY(), *VertexShader, EDRF_UseTriangleOptimization); RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams()); } // ScreenSpaceReflections }
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; } }