// Copyright Epic Games, Inc. All Rights Reserved. #include "BasePassRendering.h" #include "DeferredShadingRenderer.h" #include "DistortionRendering.h" #include "DynamicPrimitiveDrawing.h" #include "ScenePrivate.h" #include "MeshPassProcessor.inl" #include "EditorPrimitivesRendering.h" #include "TranslucentRendering.h" #include "SingleLayerWaterRendering.h" #include "Rendering/SkyAtmosphereCommonData.h" #include "SceneTextureParameters.h" #include "CompositionLighting/CompositionLighting.h" #include "CompositionLighting/PostProcessAmbientOcclusion.h" #include "SceneViewExtension.h" #include "VariableRateShadingImageManager.h" #include "OneColorShader.h" #include "ClearQuad.h" #include "ProfilingDebugging/CpuProfilerTrace.h" #include "DebugProbeRendering.h" #include "AnisotropyRendering.h" #include "Nanite/NaniteVisualize.h" // Changing this causes a full shader recompile static TAutoConsoleVariable CVarSelectiveBasePassOutputs( TEXT("r.SelectiveBasePassOutputs"), 0, TEXT("Enables shaders to only export to relevant rendertargets.\n") \ TEXT(" 0: Export in all rendertargets.\n") \ TEXT(" 1: Export only into relevant rendertarget.\n"), ECVF_ReadOnly | ECVF_RenderThreadSafe); // Changing this causes a full shader recompile static TAutoConsoleVariable CVarGlobalClipPlane( TEXT("r.AllowGlobalClipPlane"), 0, TEXT("Enables mesh shaders to support a global clip plane, needed for planar reflections, which adds about 15% BasePass GPU cost on PS4."), ECVF_ReadOnly | ECVF_RenderThreadSafe); // Changing this causes a full shader recompile static TAutoConsoleVariable CVarVertexFoggingForOpaque( TEXT("r.VertexFoggingForOpaque"), 1, TEXT("Causes opaque materials to use per-vertex fogging, which costs less and integrates properly with MSAA. Only supported with forward shading."), ECVF_ReadOnly | ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarRHICmdFlushRenderThreadTasksBasePass( TEXT("r.RHICmdFlushRenderThreadTasksBasePass"), 0, TEXT("Wait for completion of parallel render thread tasks at the end of the base pass. A more granular version of r.RHICmdFlushRenderThreadTasks. If either r.RHICmdFlushRenderThreadTasks or r.RHICmdFlushRenderThreadTasksBasePass is > 0 we will flush.")); static TAutoConsoleVariable CVarSupportStationarySkylight( TEXT("r.SupportStationarySkylight"), 1, TEXT("Enables Stationary and Dynamic Skylight shader permutations."), ECVF_ReadOnly | ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarSupportLowQualityLightmaps( TEXT("r.SupportLowQualityLightmaps"), 1, TEXT("Support low quality lightmap shader permutations"), ECVF_ReadOnly | ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarSupportAllShaderPermutations( TEXT("r.SupportAllShaderPermutations"), 0, TEXT("Local user config override to force all shader permutation features on."), ECVF_ReadOnly | ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarParallelBasePass( TEXT("r.ParallelBasePass"), 1, TEXT("Toggles parallel base pass rendering. Parallel rendering must be enabled for this to have an effect."), ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarClearGBufferDBeforeBasePass( TEXT("r.ClearGBufferDBeforeBasePass"), 1, TEXT("Whether to clear GBuffer D before basepass"), ECVF_RenderThreadSafe); // Scene color alpha is used during scene captures and planar reflections. 1 indicates background should be shown, 0 indicates foreground is fully present. static const float kSceneColorClearAlpha = 1.0f; IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FSharedBasePassUniformParameters, "BasePass"); IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FOpaqueBasePassUniformParameters, "OpaqueBasePass", SceneTextures); IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FTranslucentBasePassUniformParameters, "TranslucentBasePass", SceneTextures); // Typedef is necessary because the C preprocessor thinks the comma in the template parameter list is a comma in the macro parameter list. #define IMPLEMENT_BASEPASS_VERTEXSHADER_TYPE(LightMapPolicyType,LightMapPolicyName) \ typedef TBasePassVS< LightMapPolicyType > TBasePassVS##LightMapPolicyName ; \ IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TBasePassVS##LightMapPolicyName,TEXT("/Engine/Private/BasePassVertexShader.usf"),TEXT("Main"),SF_Vertex); #define IMPLEMENT_BASEPASS_PIXELSHADER_TYPE(LightMapPolicyType,LightMapPolicyName,bEnableSkyLight,SkyLightName) \ typedef TBasePassPS TBasePassPS##LightMapPolicyName##SkyLightName; \ IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TBasePassPS##LightMapPolicyName##SkyLightName,TEXT("/Engine/Private/BasePassPixelShader.usf"),TEXT("MainPS"),SF_Pixel); // Implement a pixel shader type for skylights and one without, and one vertex shader that will be shared between them #define IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE(LightMapPolicyType,LightMapPolicyName) \ IMPLEMENT_BASEPASS_VERTEXSHADER_TYPE(LightMapPolicyType,LightMapPolicyName) \ IMPLEMENT_BASEPASS_PIXELSHADER_TYPE(LightMapPolicyType,LightMapPolicyName,true,Skylight) \ IMPLEMENT_BASEPASS_PIXELSHADER_TYPE(LightMapPolicyType,LightMapPolicyName,false,); // Implement shader types per lightmap policy // If renaming or refactoring these, remember to update FMaterialResource::GetRepresentativeInstructionCounts and FPreviewMaterial::ShouldCache(). IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( FSelfShadowedTranslucencyPolicy, FSelfShadowedTranslucencyPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( FSelfShadowedCachedPointIndirectLightingPolicy, FSelfShadowedCachedPointIndirectLightingPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( FSelfShadowedVolumetricLightmapPolicy, FSelfShadowedVolumetricLightmapPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, FNoLightMapPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, FPrecomputedVolumetricLightmapLightingPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, FCachedVolumeIndirectLightingPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, FCachedPointIndirectLightingPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, FSimpleNoLightmapLightingPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, FSimpleLightmapOnlyLightingPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, FSimpleDirectionalLightLightingPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, FSimpleStationaryLightPrecomputedShadowsLightingPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, FSimpleStationaryLightSingleSampleShadowsLightingPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, FSimpleStationaryLightVolumetricLightmapShadowsLightingPolicy ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, TLightMapPolicyLQ ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, TLightMapPolicyHQ ); IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE( TUniformLightMapPolicy, TDistanceFieldShadowsAndLightMapPolicyHQ ); IMPLEMENT_MATERIAL_SHADER_TYPE(, F128BitRTBasePassPS, TEXT("/Engine/Private/BasePassPixelShader.usf"), TEXT("MainPS"), SF_Pixel); DEFINE_GPU_DRAWCALL_STAT(Basepass); DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer ClearGBufferAtMaxZ"), STAT_FDeferredShadingSceneRenderer_ClearGBufferAtMaxZ, STATGROUP_SceneRendering); DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer ViewExtensionPostRenderBasePass"), STAT_FDeferredShadingSceneRenderer_ViewExtensionPostRenderBasePass, STATGROUP_SceneRendering); DECLARE_CYCLE_STAT(TEXT("BasePass"), STAT_CLM_BasePass, STATGROUP_CommandListMarkers); DECLARE_CYCLE_STAT(TEXT("AfterBasePass"), STAT_CLM_AfterBasePass, STATGROUP_CommandListMarkers); DECLARE_CYCLE_STAT(TEXT("AnisotropyPass"), STAT_CLM_AnisotropyPass, STATGROUP_CommandListMarkers); DECLARE_CYCLE_STAT(TEXT("AfterAnisotropyPass"), STAT_CLM_AfterAnisotropyPass, STATGROUP_CommandListMarkers); DECLARE_CYCLE_STAT(TEXT("BasePass"), STAT_CLP_BasePass, STATGROUP_ParallelCommandListMarkers); static bool IsBasePassWaitForTasksEnabled() { return CVarRHICmdFlushRenderThreadTasksBasePass.GetValueOnRenderThread() > 0 || CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() > 0; } void SetTranslucentRenderState(FMeshPassProcessorRenderState& DrawRenderState, const FMaterial& Material, const EShaderPlatform Platform, ETranslucencyPass::Type InTranslucencyPassType) { if (Material.IsStrataMaterial()) { if (Material.IsDualBlendingEnabled(Platform)) { if (InTranslucencyPassType == ETranslucencyPass::TPT_StandardTranslucency || InTranslucencyPassType == ETranslucencyPass::TPT_AllTranslucency) { // If we are in the transparancy pass (before DoF) we do standard dual blending, and the alpha gets ignored // Blend by putting add in target 0 and multiply by background in target 1. DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } else if (InTranslucencyPassType == ETranslucencyPass::TPT_TranslucencyAfterDOF) { // In the separate pass (after DoF), we want let alpha pass through, and then multiply our color modulation in the after DoF Modulation pass. // Alpha is BF_Zero for source and BF_One for dest, which leaves alpha unchanged DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } else if (InTranslucencyPassType == ETranslucencyPass::TPT_TranslucencyAfterDOFModulate) { // In the separate pass (after DoF) modulate, we want to only darken the target by our multiplication term, and ignore the addition term. // For regular dual blending, our function is: // FrameBuffer = MRT0 + MRT1 * FrameBuffer; // So we can just remove the MRT0 component and it will modulate as expected. // Alpha we will leave unchanged. DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } else if (InTranslucencyPassType == ETranslucencyPass::TPT_TranslucencyAfterMotionBlur) { // We don't actually currently support color modulation in the post-motion blur pass at the moment, so just do the same as post-DOF for now DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } } else { if (Material.GetStrataBlendMode() == SBM_ColoredTransmittanceOnly) { // Modulate with the existing scene color, preserve destination alpha. DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } else if (Material.GetStrataBlendMode() == SBM_AlphaHoldout) { // Blend by holding out the matte shape of the source alpha DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } else { // We always use premultipled alpha for translucent rendering. // If a material was requesting dual source blending, the shader will use static platofm knowledge to convert colored transmittance to a grey scale transmittance. DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } } } else if (Material.GetShadingModels().HasShadingModel(MSM_ThinTranslucent)) { // Special case for dual blending, which is not exposed as a parameter in the material editor if (Material.IsDualBlendingEnabled(Platform)) { if (InTranslucencyPassType == ETranslucencyPass::TPT_StandardTranslucency || InTranslucencyPassType == ETranslucencyPass::TPT_AllTranslucency) { // If we are in the transparancy pass (before DoF) we do standard dual blending, and the alpha gets ignored // Blend by putting add in target 0 and multiply by background in target 1. DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } else if (InTranslucencyPassType == ETranslucencyPass::TPT_TranslucencyAfterDOF) { // In the separate pass (after DoF), we want let alpha pass through, and then multiply our color modulation in the after DoF Modulation pass. // Alpha is BF_Zero for source and BF_One for dest, which leaves alpha unchanged DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } else if (InTranslucencyPassType == ETranslucencyPass::TPT_TranslucencyAfterDOFModulate) { // In the separate pass (after DoF) modulate, we want to only darken the target by our multiplication term, and ignore the addition term. // For regular dual blending, our function is: // FrameBuffer = MRT0 + MRT1 * FrameBuffer; // So we can just remove the MRT0 component and it will modulate as expected. // Alpha we will leave unchanged. DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } else if (InTranslucencyPassType == ETranslucencyPass::TPT_TranslucencyAfterMotionBlur) { // We don't actually currently support color modulation in the post-motion blur pass at the moment, so just do the same as post-DOF for now DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } } else { // If unsupported, we still use premultipled alpha but the shader will use the variation converting color transmittance to a grey scale transmittance. DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); } } else { switch (Material.GetBlendMode()) { default: case BLEND_Opaque: // Opaque materials are rendered together in the base pass, where the blend state is set at a higher level break; case BLEND_Masked: // Masked materials are rendered together in the base pass, where the blend state is set at a higher level break; case BLEND_Translucent: // Note: alpha channel used by separate translucency, storing how much of the background should be added when doing the final composite // The Alpha channel is also used by non-separate translucency when rendering to scene captures, which store the final opacity DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); break; case BLEND_Additive: // Add to the existing scene color // Note: alpha channel used by separate translucency, storing how much of the background should be added when doing the final composite // The Alpha channel is also used by non-separate translucency when rendering to scene captures, which store the final opacity DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); break; case BLEND_Modulate: // Modulate with the existing scene color, preserve destination alpha. DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); break; case BLEND_AlphaComposite: // Blend with existing scene color. New color is already pre-multiplied by alpha. DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); break; case BLEND_AlphaHoldout: // Blend by holding out the matte shape of the source alpha DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); break; }; } const bool bDisableDepthTest = Material.ShouldDisableDepthTest(); const bool bEnableResponsiveAA = Material.ShouldEnableResponsiveAA(); const bool bIsPostMotionBlur = Material.IsTranslucencyAfterMotionBlurEnabled(); if (bEnableResponsiveAA && !bIsPostMotionBlur) { if (bDisableDepthTest) { DrawRenderState.SetDepthStencilState(TStaticDepthStencilState< false, CF_Always, true, CF_Always, SO_Keep, SO_Keep, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, STENCIL_TEMPORAL_RESPONSIVE_AA_MASK, STENCIL_TEMPORAL_RESPONSIVE_AA_MASK >::GetRHI()); DrawRenderState.SetStencilRef(STENCIL_TEMPORAL_RESPONSIVE_AA_MASK); } else { DrawRenderState.SetDepthStencilState(TStaticDepthStencilState< false, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, STENCIL_TEMPORAL_RESPONSIVE_AA_MASK, STENCIL_TEMPORAL_RESPONSIVE_AA_MASK >::GetRHI()); DrawRenderState.SetStencilRef(STENCIL_TEMPORAL_RESPONSIVE_AA_MASK); } } else if (bDisableDepthTest) { DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); } } FMeshDrawCommandSortKey CalculateTranslucentMeshStaticSortKey(const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, uint16 MeshIdInPrimitive) { uint16 SortKeyPriority = 0; float DistanceOffset = 0.0f; if (PrimitiveSceneProxy) { const FPrimitiveSceneInfo* PrimitiveSceneInfo = PrimitiveSceneProxy->GetPrimitiveSceneInfo(); SortKeyPriority = (uint16)((int32)PrimitiveSceneInfo->Proxy->GetTranslucencySortPriority() - (int32)SHRT_MIN); DistanceOffset = PrimitiveSceneInfo->Proxy->GetTranslucencySortDistanceOffset(); } FMeshDrawCommandSortKey SortKey; SortKey.Translucent.MeshIdInPrimitive = MeshIdInPrimitive; SortKey.Translucent.Priority = SortKeyPriority; SortKey.Translucent.Distance = *(uint32*)(&DistanceOffset); // View specific, so will be filled later inside VisibleMeshCommands. return SortKey; } FMeshDrawCommandSortKey CalculateBasePassMeshStaticSortKey(EDepthDrawingMode EarlyZPassMode, EBlendMode BlendMode, const FMeshMaterialShader* VertexShader, const FMeshMaterialShader* PixelShader) { FMeshDrawCommandSortKey SortKey; SortKey.BasePass.VertexShaderHash = (VertexShader ? VertexShader->GetSortKey() : 0) & 0xFFFF; SortKey.BasePass.PixelShaderHash = PixelShader ? PixelShader->GetSortKey() : 0; if (EarlyZPassMode != DDM_None) { SortKey.BasePass.Masked = BlendMode == EBlendMode::BLEND_Masked ? 0 : 1; } else { SortKey.BasePass.Masked = BlendMode == EBlendMode::BLEND_Masked ? 1 : 0; } return SortKey; } void SetDepthStencilStateForBasePass( const FSceneView* ViewIfDynamicMeshCommand, FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FMeshBatch& Mesh, int32 StaticMeshId, const FPrimitiveSceneProxy* PrimitiveSceneProxy, const FMaterial& MaterialResource, bool bEnableReceiveDecalOutput) { const bool bMaskedInEarlyPass = (MaterialResource.IsMasked() || Mesh.bDitheredLODTransition) && MaskedInEarlyPass(GShaderPlatformForFeatureLevel[FeatureLevel]); if (bEnableReceiveDecalOutput) { // Set stencil value for this draw call // This is effectively extending the GBuffer using the stencil bits const uint8 StencilValue = GET_STENCIL_BIT_MASK(RECEIVE_DECAL, PrimitiveSceneProxy ? !!PrimitiveSceneProxy->ReceivesDecals() : 0x00) | GET_STENCIL_BIT_MASK(DISTANCE_FIELD_REPRESENTATION, PrimitiveSceneProxy ? PrimitiveSceneProxy->HasDistanceFieldRepresentation() : 0x00) | STENCIL_LIGHTING_CHANNELS_MASK(PrimitiveSceneProxy ? PrimitiveSceneProxy->GetLightingChannelStencilValue() : 0x00); if (bMaskedInEarlyPass) { DrawRenderState.SetDepthStencilState(TStaticDepthStencilState< false, CF_Equal, true, CF_Always, SO_Keep, SO_Keep, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 0xFF, GET_STENCIL_BIT_MASK(RECEIVE_DECAL, 1) | GET_STENCIL_BIT_MASK(DISTANCE_FIELD_REPRESENTATION, 1) | STENCIL_LIGHTING_CHANNELS_MASK(0x7) >::GetRHI()); DrawRenderState.SetStencilRef(StencilValue); } else if (DrawRenderState.GetDepthStencilAccess() & FExclusiveDepthStencil::DepthWrite) { DrawRenderState.SetDepthStencilState(TStaticDepthStencilState< true, CF_GreaterEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 0xFF, GET_STENCIL_BIT_MASK(RECEIVE_DECAL, 1) | GET_STENCIL_BIT_MASK(DISTANCE_FIELD_REPRESENTATION, 1) | STENCIL_LIGHTING_CHANNELS_MASK(0x7) >::GetRHI()); DrawRenderState.SetStencilRef(StencilValue); } else { DrawRenderState.SetDepthStencilState(TStaticDepthStencilState< false, CF_GreaterEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 0xFF, GET_STENCIL_BIT_MASK(RECEIVE_DECAL, 1) | GET_STENCIL_BIT_MASK(DISTANCE_FIELD_REPRESENTATION, 1) | STENCIL_LIGHTING_CHANNELS_MASK(0x7) >::GetRHI()); DrawRenderState.SetStencilRef(StencilValue); } } else if (bMaskedInEarlyPass) { DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); } if (ViewIfDynamicMeshCommand && StaticMeshId >= 0 && Mesh.bDitheredLODTransition) { checkSlow(ViewIfDynamicMeshCommand->bIsViewInfo); const FViewInfo* ViewInfo = static_cast(ViewIfDynamicMeshCommand); if (ViewInfo->bAllowStencilDither) { if (ViewInfo->StaticMeshFadeOutDitheredLODMap[StaticMeshId] || ViewInfo->StaticMeshFadeInDitheredLODMap[StaticMeshId]) { const uint32 RestoreStencilRef = DrawRenderState.GetStencilRef(); DrawRenderState.SetDepthStencilState( TStaticDepthStencilState< false, CF_Equal, true, CF_Always, SO_Keep, SO_Keep, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 0xFF, GET_STENCIL_BIT_MASK(RECEIVE_DECAL, 1) | GET_STENCIL_BIT_MASK(DISTANCE_FIELD_REPRESENTATION, 1) | STENCIL_LIGHTING_CHANNELS_MASK(0x7) >::GetRHI()); DrawRenderState.SetStencilRef(RestoreStencilRef); } } } } void SetupBasePassState(FExclusiveDepthStencil::Type BasePassDepthStencilAccess, const bool bShaderComplexity, FMeshPassProcessorRenderState& DrawRenderState) { DrawRenderState.SetDepthStencilAccess(BasePassDepthStencilAccess); if (bShaderComplexity) { // Additive blending when shader complexity viewmode is enabled. DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); // Disable depth writes as we have a full depth prepass. DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); } else { // Opaque blending for all G buffer targets, depth tests and writes. static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.BasePassOutputsVelocityDebug")); if (CVar && CVar->GetValueOnRenderThread() == 2) { DrawRenderState.SetBlendState(TStaticBlendStateWriteMask::GetRHI()); } else { DrawRenderState.SetBlendState(TStaticBlendStateWriteMask::GetRHI()); } if (DrawRenderState.GetDepthStencilAccess() & FExclusiveDepthStencil::DepthWrite) { DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); } else { DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); } } } /** * Get shader templates allowing to redirect between compatible shaders. */ template bool GetUniformBasePassShaders( const FMaterial& Material, FVertexFactoryType* VertexFactoryType, ERHIFeatureLevel::Type FeatureLevel, bool bEnableSkyLight, bool bUse128bitRT, TShaderRef>* VertexShader, TShaderRef>* PixelShader ) { FMaterialShaderTypes ShaderTypes; if(VertexShader) { ShaderTypes.AddShaderType>>(); } if(PixelShader) { if (bEnableSkyLight) { ShaderTypes.AddShaderType, true>>(); } else { if (bUse128bitRT && (Policy == LMP_NO_LIGHTMAP)) { ShaderTypes.AddShaderType(); } else { ShaderTypes.AddShaderType, false>>(); } } } FMaterialShaders Shaders; if (!Material.TryGetShaders(ShaderTypes, VertexFactoryType, Shaders)) { return false; } Shaders.TryGetVertexShader(VertexShader); Shaders.TryGetPixelShader(PixelShader); return true; } template <> bool GetBasePassShaders( const FMaterial& Material, FVertexFactoryType* VertexFactoryType, FUniformLightMapPolicy LightMapPolicy, ERHIFeatureLevel::Type FeatureLevel, bool bEnableSkyLight, bool bUse128bitRT, TShaderRef>* VertexShader, TShaderRef>* PixelShader ) { switch (LightMapPolicy.GetIndirectPolicy()) { case LMP_PRECOMPUTED_IRRADIANCE_VOLUME_INDIRECT_LIGHTING: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_CACHED_VOLUME_INDIRECT_LIGHTING: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_CACHED_POINT_INDIRECT_LIGHTING: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_SIMPLE_DIRECTIONAL_LIGHT_LIGHTING: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_SIMPLE_NO_LIGHTMAP: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_SIMPLE_LIGHTMAP_ONLY_LIGHTING: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_SIMPLE_STATIONARY_PRECOMPUTED_SHADOW_LIGHTING: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_SIMPLE_STATIONARY_SINGLESAMPLE_SHADOW_LIGHTING: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_SIMPLE_STATIONARY_VOLUMETRICLIGHTMAP_SHADOW_LIGHTING: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_LQ_LIGHTMAP: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_HQ_LIGHTMAP: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_DISTANCE_FIELD_SHADOWS_AND_HQ_LIGHTMAP: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); case LMP_NO_LIGHTMAP: return GetUniformBasePassShaders(Material, VertexFactoryType, FeatureLevel, bEnableSkyLight, bUse128bitRT, VertexShader, PixelShader); default: check(false); return false; } } void SetupSharedBasePassParameters( FRDGBuilder& GraphBuilder, const FViewInfo& View, bool bLumenGIEnabled, FSharedBasePassUniformParameters& SharedParameters) { SharedParameters.Forward = *View.ForwardLightingResources.ForwardLightData; SetupFogUniformParameters(GraphBuilder, View, SharedParameters.Fog); if (View.IsInstancedStereoPass()) { const FViewInfo& InstancedView = *View.GetInstancedView(); SharedParameters.ForwardISR = *InstancedView.ForwardLightingResources.ForwardLightData; SetupFogUniformParameters(GraphBuilder, (FViewInfo&)InstancedView, SharedParameters.FogISR); } else { SharedParameters.ForwardISR = *View.ForwardLightingResources.ForwardLightData; SharedParameters.FogISR = SharedParameters.Fog; } const FScene* Scene = View.Family->Scene ? View.Family->Scene->GetRenderScene() : nullptr; const FPlanarReflectionSceneProxy* ReflectionSceneProxy = Scene ? Scene->GetForwardPassGlobalPlanarReflection() : nullptr; SetupReflectionUniformParameters(View, SharedParameters.Reflection); SetupPlanarReflectionUniformParameters(View, ReflectionSceneProxy, SharedParameters.PlanarReflection); // Skip base pass skylight if Lumen GI is enabled, as Lumen handles the skylight. // Ideally we would choose a different shader permutation to skip skylight, but Lumen GI is only known per-view SharedParameters.UseBasePassSkylight = bLumenGIEnabled ? 0 : 1; } TRDGUniformBufferRef CreateOpaqueBasePassUniformBuffer( FRDGBuilder& GraphBuilder, const FViewInfo& View, const int32 ViewIndex, const FForwardBasePassTextures& ForwardBasePassTextures, const FDBufferTextures& DBufferTextures, const FSceneWithoutWaterTextures* SceneWithoutWaterTextures, bool bLumenGIEnabled) { FOpaqueBasePassUniformParameters& BasePassParameters = *GraphBuilder.AllocParameters(); SetupSharedBasePassParameters(GraphBuilder, View, bLumenGIEnabled, BasePassParameters.Shared); const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder); // Forward shading { BasePassParameters.UseForwardScreenSpaceShadowMask = 0; BasePassParameters.ForwardScreenSpaceShadowMaskTexture = SystemTextures.White; BasePassParameters.IndirectOcclusionTexture = SystemTextures.White; BasePassParameters.ResolvedSceneDepthTexture = SystemTextures.White; if (ForwardBasePassTextures.ScreenSpaceShadowMask) { BasePassParameters.UseForwardScreenSpaceShadowMask = 1; BasePassParameters.ForwardScreenSpaceShadowMaskTexture = ForwardBasePassTextures.ScreenSpaceShadowMask; } if (HasBeenProduced(ForwardBasePassTextures.ScreenSpaceAO)) { BasePassParameters.IndirectOcclusionTexture = ForwardBasePassTextures.ScreenSpaceAO; } if (ForwardBasePassTextures.SceneDepthIfResolved) { BasePassParameters.ResolvedSceneDepthTexture = ForwardBasePassTextures.SceneDepthIfResolved; } BasePassParameters.Is24BitUnormDepthStencil = ForwardBasePassTextures.bIs24BitUnormDepthStencil ? 1 : 0; } // DBuffer Decals BasePassParameters.DBuffer = GetDBufferParameters(GraphBuilder, DBufferTextures, View.GetShaderPlatform()); // Single Layer Water BasePassParameters.SceneWithoutSingleLayerWaterMinMaxUV = FVector4f(0.0f, 0.0f, 1.0f, 1.0f); BasePassParameters.SceneColorWithoutSingleLayerWaterTexture = SystemTextures.Black; BasePassParameters.SceneDepthWithoutSingleLayerWaterTexture = SystemTextures.Black; BasePassParameters.SceneColorWithoutSingleLayerWaterSampler = TStaticSamplerState::GetRHI(); BasePassParameters.SceneDepthWithoutSingleLayerWaterSampler = TStaticSamplerState::GetRHI(); if (SceneWithoutWaterTextures) { BasePassParameters.SceneWithoutSingleLayerWaterMinMaxUV = SceneWithoutWaterTextures->Views[ViewIndex].MinMaxUV; BasePassParameters.SceneColorWithoutSingleLayerWaterTexture = SceneWithoutWaterTextures->ColorTexture; BasePassParameters.SceneDepthWithoutSingleLayerWaterTexture = SceneWithoutWaterTextures->DepthTexture; } BasePassParameters.PreIntegratedGFTexture = GSystemTextures.PreintegratedGF->GetRHI(); BasePassParameters.PreIntegratedGFSampler = TStaticSamplerState::GetRHI(); SetupDistortionParams(BasePassParameters.DistortionParams, View); // Strata Strata::BindStrataBasePassUniformParameters(GraphBuilder, View, BasePassParameters.Strata); // Misc BasePassParameters.EyeAdaptationTexture = GetEyeAdaptationTexture(GraphBuilder, View); return GraphBuilder.CreateUniformBuffer(&BasePassParameters); } static void ClearGBufferAtMaxZ( FRDGBuilder& GraphBuilder, TArrayView Views, const FRenderTargetBindingSlots& BasePassRenderTargets, FLinearColor ClearColor0) { check(Views.Num() > 0); SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_ClearGBufferAtMaxZ); RDG_EVENT_SCOPE(GraphBuilder, "ClearGBufferAtMaxZ"); const uint32 ActiveTargetCount = BasePassRenderTargets.GetActiveCount(); FGlobalShaderMap* ShaderMap = Views[0].ShaderMap; TShaderMapRef > VertexShader(ShaderMap); TOneColorPixelShaderMRT::FPermutationDomain PermutationVector; PermutationVector.Set(ActiveTargetCount); TShaderMapRefPixelShader(ShaderMap, PermutationVector); auto* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets = BasePassRenderTargets; // Clear each viewport by drawing background color at MaxZ depth for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { const FViewInfo& View = Views[ViewIndex]; RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask); RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex); GraphBuilder.AddPass( {}, PassParameters, ERDGPassFlags::Raster, [&View, VertexShader, PixelShader, ActiveTargetCount, ClearColor0](FRHICommandList& RHICmdList) { const FLinearColor ClearColors[MaxSimultaneousRenderTargets] = { ClearColor0, FLinearColor(0.5f,0.5f,0.5f,0), FLinearColor(0,0,0,1), FLinearColor(0,0,0,0), FLinearColor(0,1,1,1), FLinearColor(1,1,1,1), FLinearColor::Transparent, FLinearColor::Transparent }; FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); // Opaque rendering, depth test but no depth writes GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); GraphicsPSOInit.BlendState = TStaticBlendStateWriteMask<>::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4(); GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PT_TriangleStrip; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); VertexShader->SetDepthParameter(RHICmdList, float(ERHIZBuffer::FarPlane)); RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1); PixelShader->SetColors(RHICmdList, PixelShader, ClearColors, ActiveTargetCount); RHICmdList.SetStreamSource(0, GClearVertexBuffer.VertexBufferRHI, 0); RHICmdList.DrawPrimitive(0, 2, 1); }); } } BEGIN_SHADER_PARAMETER_STRUCT(FPostBasePassViewExtensionParameters, ) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() void FDeferredShadingSceneRenderer::RenderBasePass( FRDGBuilder& GraphBuilder, FSceneTextures& SceneTextures, const FDBufferTextures& DBufferTextures, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, FRDGTextureRef ForwardShadowMaskTexture, FInstanceCullingManager& InstanceCullingManager, bool bNaniteEnabled, const TArrayView& NaniteRasterResults) { TRACE_CPUPROFILER_EVENT_SCOPE(FDeferredShadingSceneRenderer::RenderBasePass); const bool bEnableParallelBasePasses = GRHICommandList.UseParallelAlgorithms() && CVarParallelBasePass.GetValueOnRenderThread(); static const auto ClearMethodCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.ClearSceneMethod")); bool bRequiresRHIClear = true; bool bRequiresFarZQuadClear = false; if (ClearMethodCVar) { int32 ClearMethod = ClearMethodCVar->GetValueOnRenderThread(); if (ClearMethod == 0 && !ActiveViewFamily->EngineShowFlags.Game) { // Do not clear the scene only if the view family is in game mode. ClearMethod = 1; } switch (ClearMethod) { case 0: // No clear bRequiresRHIClear = false; bRequiresFarZQuadClear = false; break; case 1: // RHICmdList.Clear 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 (ActiveViewFamily->EngineShowFlags.Wireframe || ActiveViewFamily->EngineShowFlags.ShaderComplexity || ActiveViewFamily->EngineShowFlags.StationaryLightOverlap) { bRequiresRHIClear = true; bRequiresFarZQuadClear = false; } const bool bIsWireframeRenderpass = ActiveViewFamily->EngineShowFlags.Wireframe && FSceneRenderer::ShouldCompositeEditorPrimitives(Views[0]); const bool bDebugViewMode = ActiveViewFamily->UseDebugViewPS(); const bool bRenderLightmapDensity = ActiveViewFamily->EngineShowFlags.LightMapDensity && AllowDebugViewmodes(); const bool bRenderSkyAtmosphereEditorNotifications = ShouldRenderSkyAtmosphereEditorNotifications(); const bool bDoParallelBasePass = bEnableParallelBasePasses && !bDebugViewMode && !bRenderLightmapDensity; // DebugView and LightmapDensity are non-parallel substitutions inside BasePass const bool bNeedsBeginRender = AllowDebugViewmodes() && (ActiveViewFamily->EngineShowFlags.RequiredTextureResolution || ActiveViewFamily->EngineShowFlags.VirtualTexturePendingMips || ActiveViewFamily->EngineShowFlags.MaterialTextureScaleAccuracy || ActiveViewFamily->EngineShowFlags.MeshUVDensityAccuracy || ActiveViewFamily->EngineShowFlags.PrimitiveDistanceAccuracy || ActiveViewFamily->EngineShowFlags.ShaderComplexity || ActiveViewFamily->EngineShowFlags.LODColoration || ActiveViewFamily->EngineShowFlags.HLODColoration); const bool bForwardShadingEnabled = IsForwardShadingEnabled(SceneTextures.Config.ShaderPlatform); const FExclusiveDepthStencil ExclusiveDepthStencil(BasePassDepthStencilAccess); TStaticArray BasePassTextures; uint32 BasePassTextureCount = SceneTextures.GetGBufferRenderTargets(BasePassTextures); Strata::AppendStrataMRTs(*this, BasePassTextureCount, BasePassTextures); TArrayView BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount); FRDGTextureRef BasePassDepthTexture = SceneTextures.Depth.Target; FLinearColor SceneColorClearValue; if (bRequiresRHIClear) { if (ActiveViewFamily->EngineShowFlags.ShaderComplexity && SceneTextures.QuadOverdraw) { AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(SceneTextures.QuadOverdraw), FUintVector4(0, 0, 0, 0)); } if (ActiveViewFamily->EngineShowFlags.ShaderComplexity || ActiveViewFamily->EngineShowFlags.StationaryLightOverlap) { SceneColorClearValue = FLinearColor(0, 0, 0, kSceneColorClearAlpha); } else { SceneColorClearValue = FLinearColor(Views[0].BackgroundColor.R, Views[0].BackgroundColor.G, Views[0].BackgroundColor.B, kSceneColorClearAlpha); } ERenderTargetLoadAction ColorLoadAction = ERenderTargetLoadAction::ELoad; if (SceneTextures.Color.Target->Desc.ClearValue.GetClearColor() == SceneColorClearValue) { ColorLoadAction = ERenderTargetLoadAction::EClear; } else { ColorLoadAction = ERenderTargetLoadAction::ENoAction; } auto* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets = GetRenderTargetBindings(ColorLoadAction, BasePassTexturesView); if (!CVarClearGBufferDBeforeBasePass.GetValueOnRenderThread() && SceneTextures.Config.GBufferD.Index > 0 && SceneTextures.Config.GBufferD.Index < (int32)BasePassTextureCount) { PassParameters->RenderTargets[SceneTextures.Config.GBufferD.Index].SetLoadAction(ERenderTargetLoadAction::ENoAction); } GraphBuilder.AddPass(RDG_EVENT_NAME("GBufferClear"), PassParameters, ERDGPassFlags::Raster, [PassParameters, ColorLoadAction, SceneColorClearValue](FRHICommandList& RHICmdList) { // If no fast-clear action was used, we need to do an MRT shader clear. if (ColorLoadAction == ERenderTargetLoadAction::ENoAction) { const FRenderTargetBindingSlots& RenderTargets = PassParameters->RenderTargets; FLinearColor ClearColors[MaxSimultaneousRenderTargets]; FRHITexture* Textures[MaxSimultaneousRenderTargets]; int32 TextureIndex = 0; RenderTargets.Enumerate([&](const FRenderTargetBinding& RenderTarget) { FRHITexture* TextureRHI = RenderTarget.GetTexture()->GetRHI(); ClearColors[TextureIndex] = TextureIndex == 0 ? SceneColorClearValue : TextureRHI->GetClearColor(); Textures[TextureIndex] = TextureRHI; ++TextureIndex; }); // Clear color only; depth-stencil is fast cleared. DrawClearQuadMRT(RHICmdList, true, TextureIndex, ClearColors, false, 0, false, 0); } }); if (bRenderSkyAtmosphereEditorNotifications) { // We only render this warning text when bRequiresRHIClear==true to make sure the scene color buffer is allocated at this stage. // When false, the option specifies that all pixels must be written to by a sky dome anyway. RenderSkyAtmosphereEditorNotifications(GraphBuilder, SceneTextures.Color.Target); } } #if WITH_EDITOR if (ActiveViewFamily->EngineShowFlags.Wireframe) { checkf(ExclusiveDepthStencil.IsDepthWrite(), TEXT("Wireframe base pass requires depth-write, but it is set to read-only.")); BasePassTextureCount = 1; BasePassTextures[0] = SceneTextures.EditorPrimitiveColor; BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount); BasePassDepthTexture = SceneTextures.EditorPrimitiveDepth; auto* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::EClear, BasePassTexturesView); PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::EClear, ExclusiveDepthStencil); GraphBuilder.AddPass(RDG_EVENT_NAME("WireframeClear"), PassParameters, ERDGPassFlags::Raster, [](FRHICommandList&) {}); } #endif // Render targets bindings should remain constant at this point. FRenderTargetBindingSlots BasePassRenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::ELoad, BasePassTexturesView); BasePassRenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil); BasePassRenderTargets.ShadingRateTexture = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, *ActiveViewFamily, nullptr, EVRSType::None); FForwardBasePassTextures ForwardBasePassTextures{}; if (bForwardShadingEnabled) { ForwardBasePassTextures.SceneDepthIfResolved = SceneTextures.Depth.IsSeparate() ? SceneTextures.Depth.Resolve : nullptr; ForwardBasePassTextures.ScreenSpaceAO = SceneTextures.ScreenSpaceAO; ForwardBasePassTextures.ScreenSpaceShadowMask = ForwardShadowMaskTexture; } else if (!ExclusiveDepthStencil.IsDepthWrite()) { // If depth write is not enabled, we can bound the depth texture as read only ForwardBasePassTextures.SceneDepthIfResolved = SceneTextures.Depth.Resolve; } ForwardBasePassTextures.bIs24BitUnormDepthStencil = ForwardBasePassTextures.SceneDepthIfResolved ? GPixelFormats[ForwardBasePassTextures.SceneDepthIfResolved->Desc.Format].bIs24BitUnormDepthStencil : 1; GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_BasePass)); RenderBasePassInternal(GraphBuilder, SceneTextures, BasePassRenderTargets, BasePassDepthStencilAccess, ForwardBasePassTextures, DBufferTextures, bDoParallelBasePass, bRenderLightmapDensity, InstanceCullingManager, bNaniteEnabled, NaniteRasterResults); GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_AfterBasePass)); if (ActiveViewFamily->ViewExtensions.Num() > 0) { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_ViewExtensionPostRenderBasePass); RDG_EVENT_SCOPE(GraphBuilder, "BasePass_ViewExtensions"); auto* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets = BasePassRenderTargets; PassParameters->SceneTextures = CreateSceneTextureUniformBuffer(GraphBuilder, &GetActiveSceneTextures(), FeatureLevel, ESceneTextureSetupMode::None); GraphBuilder.AddPass( {}, PassParameters, ERDGPassFlags::Raster, [this](FRHICommandListImmediate& RHICmdList) { for (auto& ViewExtension : ActiveViewFamily->ViewExtensions) { for (FViewInfo& View : Views) { SCOPED_GPU_MASK(RHICmdList, View.GPUMask); ViewExtension->PostRenderBasePass_RenderThread(RHICmdList, View); } } }); } if (bRequiresFarZQuadClear) { ClearGBufferAtMaxZ(GraphBuilder, Views, BasePassRenderTargets, SceneColorClearValue); } if (ShouldRenderAnisotropyPass(Views)) { GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_AnisotropyPass)); RenderAnisotropyPass(GraphBuilder, SceneTextures, bEnableParallelBasePasses); GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_AfterAnisotropyPass)); } #if !(UE_BUILD_SHIPPING) if (!bForwardShadingEnabled) { StampDeferredDebugProbeMaterialPS(GraphBuilder, Views, BasePassRenderTargets, SceneTextures); } #endif } BEGIN_SHADER_PARAMETER_STRUCT(FOpaqueBasePassParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View) SHADER_PARAMETER_STRUCT_REF(FReflectionCaptureShaderData, ReflectionCapture) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FOpaqueBasePassUniformParameters, BasePass) SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() static void RenderEditorPrimitivesForDPG( FRDGBuilder& GraphBuilder, const FViewInfo& View, FOpaqueBasePassParameters* PassParameters, const FMeshPassProcessorRenderState& DrawRenderState, ESceneDepthPriorityGroup DepthPriorityGroup, FInstanceCullingManager& InstanceCullingManager) { const FScene* Scene = View.Family->Scene->GetRenderScene(); GraphBuilder.AddPass( RDG_EVENT_NAME("%s", *UEnum::GetValueAsString(DepthPriorityGroup)), PassParameters, ERDGPassFlags::Raster, [&View, DrawRenderState, DepthPriorityGroup](FRHICommandListImmediate& RHICmdList) { RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f); View.SimpleElementCollector.DrawBatchedElements(RHICmdList, DrawRenderState, View, EBlendModeFilter::OpaqueAndMasked, DepthPriorityGroup); if (!View.Family->EngineShowFlags.CompositeEditorPrimitives) { const bool bNeedToSwitchVerticalAxis = RHINeedsToSwitchVerticalAxis(View.GetShaderPlatform()); DrawDynamicMeshPass(View, RHICmdList, [&View, &DrawRenderState](FDynamicPassMeshDrawListContext* DynamicMeshPassContext) { FEditorPrimitivesBasePassMeshProcessor PassMeshProcessor( View.Family->Scene->GetRenderScene(), View.GetFeatureLevel(), &View, DrawRenderState, false, DynamicMeshPassContext); const uint64 DefaultBatchElementMask = ~0ull; for (int32 MeshIndex = 0; MeshIndex < View.ViewMeshElements.Num(); MeshIndex++) { const FMeshBatch& MeshBatch = View.ViewMeshElements[MeshIndex]; PassMeshProcessor.AddMeshBatch(MeshBatch, DefaultBatchElementMask, nullptr); } }); const FBatchedElements& BatchedViewElements = DepthPriorityGroup == SDPG_World ? View.BatchedViewElements : View.TopBatchedViewElements; DrawDynamicMeshPass(View, RHICmdList, [&View, &DrawRenderState](FDynamicPassMeshDrawListContext* DynamicMeshPassContext) { FEditorPrimitivesBasePassMeshProcessor PassMeshProcessor( View.Family->Scene->GetRenderScene(), View.GetFeatureLevel(), &View, DrawRenderState, false, DynamicMeshPassContext); const uint64 DefaultBatchElementMask = ~0ull; for (int32 MeshIndex = 0; MeshIndex < View.TopViewMeshElements.Num(); MeshIndex++) { const FMeshBatch& MeshBatch = View.TopViewMeshElements[MeshIndex]; PassMeshProcessor.AddMeshBatch(MeshBatch, DefaultBatchElementMask, nullptr); } }); // Draw the view's batched simple elements(lines, sprites, etc). View.TopBatchedViewElements.Draw(RHICmdList, DrawRenderState, View.GetFeatureLevel(), bNeedToSwitchVerticalAxis, View, false); } }); } static bool HasEditorPrimitivesForDPG(const FViewInfo& View, ESceneDepthPriorityGroup DepthPriorityGroup) { bool bHasPrimitives = View.SimpleElementCollector.HasPrimitives(DepthPriorityGroup); if (!View.Family->EngineShowFlags.CompositeEditorPrimitives) { const TIndirectArray& ViewMeshElementList = (DepthPriorityGroup == SDPG_Foreground ? View.TopViewMeshElements : View.ViewMeshElements); bHasPrimitives |= ViewMeshElementList.Num() > 0; const FBatchedElements& BatchedViewElements = DepthPriorityGroup == SDPG_World ? View.BatchedViewElements : View.TopBatchedViewElements; bHasPrimitives |= BatchedViewElements.HasPrimsToDraw(); } return bHasPrimitives; } static void RenderEditorPrimitives( FRDGBuilder& GraphBuilder, FOpaqueBasePassParameters* PassParameters, const FViewInfo& View, const FMeshPassProcessorRenderState& DrawRenderState, FInstanceCullingManager& InstanceCullingManager) { RDG_EVENT_SCOPE(GraphBuilder, "EditorPrimitives"); RenderEditorPrimitivesForDPG(GraphBuilder, View, PassParameters, DrawRenderState, SDPG_World, InstanceCullingManager); if (HasEditorPrimitivesForDPG(View, SDPG_Foreground)) { // Write foreground primitives into depth buffer without testing { auto* DepthWritePassParameters = GraphBuilder.AllocParameters(); *DepthWritePassParameters = *PassParameters; // Change to depth writable DepthWritePassParameters->RenderTargets.DepthStencil.SetDepthStencilAccess(FExclusiveDepthStencil::DepthWrite_StencilWrite); FMeshPassProcessorRenderState NoDepthTestDrawRenderState(DrawRenderState); NoDepthTestDrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); NoDepthTestDrawRenderState.SetDepthStencilAccess(FExclusiveDepthStencil::DepthWrite_StencilWrite); RenderEditorPrimitivesForDPG(GraphBuilder, View, DepthWritePassParameters, NoDepthTestDrawRenderState, SDPG_Foreground, InstanceCullingManager); } // Render foreground primitives with depth testing RenderEditorPrimitivesForDPG(GraphBuilder, View, PassParameters, DrawRenderState, SDPG_Foreground, InstanceCullingManager); } } void FDeferredShadingSceneRenderer::RenderBasePassInternal( FRDGBuilder& GraphBuilder, const FSceneTextures& SceneTextures, const FRenderTargetBindingSlots& BasePassRenderTargets, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, const FForwardBasePassTextures& ForwardBasePassTextures, const FDBufferTextures& DBufferTextures, bool bParallelBasePass, bool bRenderLightmapDensity, FInstanceCullingManager& InstanceCullingManager, bool bNaniteEnabled, const TArrayView& NaniteRasterResults) { RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderBasePass); SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_RenderBasePass, FColor::Emerald); #if WITH_DEBUG_VIEW_MODES Nanite::EDebugViewMode NaniteDebugViewMode = Nanite::EDebugViewMode::None; if (ActiveViewFamily->EngineShowFlags.Wireframe) { NaniteDebugViewMode = Nanite::EDebugViewMode::Wireframe; } else if (bRenderLightmapDensity) { NaniteDebugViewMode = Nanite::EDebugViewMode::LightmapDensity; } else if (ActiveViewFamily->UseDebugViewPS()) { switch (ActiveViewFamily->GetDebugViewShaderMode()) { case DVSM_ShaderComplexity: // Default shader complexity viewmode case DVSM_ShaderComplexityContainedQuadOverhead: // Show shader complexity with quad overdraw scaling the PS instruction count. case DVSM_ShaderComplexityBleedingQuadOverhead: // Show shader complexity with quad overdraw bleeding the PS instruction count over the quad. case DVSM_QuadComplexity: // Show quad overdraw only. NaniteDebugViewMode = Nanite::EDebugViewMode::ShaderComplexity; break; default: break; } } #endif FRDGTextureRef NaniteColorTarget = SceneTextures.Color.Target; FRDGTextureRef NaniteDepthTarget = SceneTextures.Depth.Target; #if WITH_EDITOR && WITH_DEBUG_VIEW_MODES if (NaniteDebugViewMode == Nanite::EDebugViewMode::Wireframe) { NaniteColorTarget = SceneTextures.EditorPrimitiveColor; NaniteDepthTarget = SceneTextures.EditorPrimitiveDepth; } #endif auto RenderNaniteDepthPass = [&](FViewInfo& View, int32 ViewIndex) { // Emit Nanite depth if there was not an earlier depth pre-pass if (!ShouldRenderPrePass()) { Nanite::FRasterResults& RasterResults = NaniteRasterResults[ViewIndex]; // Emit velocity with depth if not writing it in base pass. FRDGTexture* VelocityBuffer = !IsUsingBasePassVelocity(ShaderPlatform) ? SceneTextures.Velocity : nullptr; const bool bEmitStencilMask = NANITE_MATERIAL_STENCIL != 0; Nanite::EmitDepthTargets( GraphBuilder, *Scene, View, RasterResults.PageConstants, RasterResults.VisibleClustersSWHW, RasterResults.ViewsBuffer, SceneTextures.Depth.Target, RasterResults.VisBuffer64, VelocityBuffer, RasterResults.MaterialDepth, RasterResults.MaterialResolve, ShouldRenderPrePass(), bEmitStencilMask ); } }; auto RenderNaniteBasePass = [&](FViewInfo& View, int32 ViewIndex) { Nanite::FRasterResults& RasterResults = NaniteRasterResults[ViewIndex]; #if WITH_DEBUG_VIEW_MODES if (NaniteDebugViewMode != Nanite::EDebugViewMode::None) { Nanite::RenderDebugViewMode( GraphBuilder, NaniteDebugViewMode, *Scene, View, *ActiveViewFamily, RasterResults, NaniteColorTarget, NaniteDepthTarget, SceneTextures.QuadOverdraw ); } else #endif { Nanite::DrawBasePass( GraphBuilder, View.NaniteMaterialPassCommands, *this, SceneTextures, DBufferTextures, *Scene, View, RasterResults ); } }; if (bRenderLightmapDensity || ActiveViewFamily->UseDebugViewPS()) { if (bRenderLightmapDensity) { // Override the base pass with the lightmap density pass if the viewmode is enabled. RenderLightMapDensities(GraphBuilder, Views, BasePassRenderTargets); } else if (ActiveViewFamily->UseDebugViewPS()) { // Override the base pass with one of the debug view shader mode (see EDebugViewShaderMode) if required. RenderDebugViewMode(GraphBuilder, Views, SceneTextures.QuadOverdraw, BasePassRenderTargets); } // Debug view support for Nanite if (bNaniteEnabled) { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { FViewInfo& View = Views[ViewIndex]; RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask); RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex); RenderNaniteDepthPass(View, ViewIndex); RenderNaniteBasePass(View, ViewIndex); } } } else { SCOPE_CYCLE_COUNTER(STAT_BasePassDrawTime); RDG_EVENT_SCOPE(GraphBuilder, "BasePass"); RDG_GPU_STAT_SCOPE(GraphBuilder, Basepass); const bool bNeedsPrePass = ShouldRenderPrePass(); if (bParallelBasePass) { RDG_WAIT_FOR_TASKS_CONDITIONAL(GraphBuilder, IsBasePassWaitForTasksEnabled()); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { FViewInfo& View = Views[ViewIndex]; RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask); RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex); View.BeginRenderView(); const bool bLumenGIEnabled = GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen; FMeshPassProcessorRenderState DrawRenderState; SetupBasePassState(BasePassDepthStencilAccess, ActiveViewFamily->EngineShowFlags.ShaderComplexity, DrawRenderState); FOpaqueBasePassParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.GetShaderParameters(); PassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer; PassParameters->BasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View, ViewIndex, ForwardBasePassTextures, DBufferTextures, nullptr, bLumenGIEnabled); PassParameters->RenderTargets = BasePassRenderTargets; const bool bShouldRenderView = View.ShouldRenderView(); if (bShouldRenderView) { View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams); GraphBuilder.AddPass( RDG_EVENT_NAME("BasePassParallel"), PassParameters, ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass, [this, &View, PassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList) { FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_BasePass), *this, View, FParallelCommandListBindings(PassParameters)); View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].DispatchDraw(&ParallelCommandListSet, RHICmdList, &PassParameters->InstanceCullingDrawParams); }); } if (bNaniteEnabled) { RenderNaniteDepthPass(View, ViewIndex); RenderNaniteBasePass(View, ViewIndex); } RenderEditorPrimitives(GraphBuilder, PassParameters, View, DrawRenderState, InstanceCullingManager); if (bShouldRenderView && View.Family->EngineShowFlags.Atmosphere) { FOpaqueBasePassParameters* SkyPassPassParameters = GraphBuilder.AllocParameters(); SkyPassPassParameters->BasePass = PassParameters->BasePass; SkyPassPassParameters->RenderTargets = BasePassRenderTargets; SkyPassPassParameters->View = View.GetShaderParameters(); SkyPassPassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer; View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, SkyPassPassParameters->InstanceCullingDrawParams); GraphBuilder.AddPass( RDG_EVENT_NAME("SkyPassParallel"), SkyPassPassParameters, ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass, [this, &View, SkyPassPassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList) { FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_BasePass), *this, View, FParallelCommandListBindings(SkyPassPassParameters)); View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].DispatchDraw(&ParallelCommandListSet, RHICmdList, &SkyPassPassParameters->InstanceCullingDrawParams); }); } } } else { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { FViewInfo& View = Views[ViewIndex]; RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask); RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex); View.BeginRenderView(); const bool bLumenGIEnabled = GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen; FMeshPassProcessorRenderState DrawRenderState; SetupBasePassState(BasePassDepthStencilAccess, ActiveViewFamily->EngineShowFlags.ShaderComplexity, DrawRenderState); FOpaqueBasePassParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.GetShaderParameters(); PassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer; PassParameters->BasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View, ViewIndex, ForwardBasePassTextures, DBufferTextures, nullptr, bLumenGIEnabled); PassParameters->RenderTargets = BasePassRenderTargets; const bool bShouldRenderView = View.ShouldRenderView(); if (bShouldRenderView) { View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams); GraphBuilder.AddPass( RDG_EVENT_NAME("BasePass"), PassParameters, ERDGPassFlags::Raster, [this, &View, PassParameters](FRHICommandList& RHICmdList) { SetStereoViewport(RHICmdList, View, 1.0f); View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams); } ); } if (bNaniteEnabled) { RenderNaniteDepthPass(View, ViewIndex); RenderNaniteBasePass(View, ViewIndex); } RenderEditorPrimitives(GraphBuilder, PassParameters, View, DrawRenderState, InstanceCullingManager); if (bShouldRenderView && View.Family->EngineShowFlags.Atmosphere) { FOpaqueBasePassParameters* SkyPassParameters = GraphBuilder.AllocParameters(); SkyPassParameters->BasePass = PassParameters->BasePass; SkyPassParameters->RenderTargets = BasePassRenderTargets; SkyPassParameters->View = View.GetShaderParameters(); SkyPassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer; View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, SkyPassParameters->InstanceCullingDrawParams); GraphBuilder.AddPass( RDG_EVENT_NAME("SkyPass"), SkyPassParameters, ERDGPassFlags::Raster, [this, &View, SkyPassParameters](FRHICommandList& RHICmdList) { SetStereoViewport(RHICmdList, View, 1.0f); View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].DispatchDraw(nullptr, RHICmdList, &SkyPassParameters->InstanceCullingDrawParams); } ); } } } } } template bool FBasePassMeshProcessor::Process( const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, int32 StaticMeshId, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, const FMaterialRenderProxy& RESTRICT MaterialRenderProxy, const FMaterial& RESTRICT MaterialResource, EBlendMode BlendMode, FMaterialShadingModelField ShadingModels, const LightMapPolicyType& RESTRICT LightMapPolicy, const typename LightMapPolicyType::ElementDataType& RESTRICT LightMapElementData, ERasterizerFillMode MeshFillMode, ERasterizerCullMode MeshCullMode) { const FVertexFactory* VertexFactory = MeshBatch.VertexFactory; const bool bRenderSkylight = Scene && Scene->ShouldRenderSkylightInBasePass(BlendMode) && ShadingModels.IsLit(); TMeshProcessorShaders< TBasePassVertexShaderPolicyParamType, TBasePassPixelShaderPolicyParamType> BasePassShaders; if (!GetBasePassShaders( MaterialResource, VertexFactory->GetType(), LightMapPolicy, FeatureLevel, bRenderSkylight, Get128BitRequirement(), &BasePassShaders.VertexShader, &BasePassShaders.PixelShader )) { return false; } FMeshPassProcessorRenderState DrawRenderState(PassDrawRenderState); SetDepthStencilStateForBasePass( ViewIfDynamicMeshCommand, DrawRenderState, FeatureLevel, MeshBatch, StaticMeshId, PrimitiveSceneProxy, MaterialResource, bEnableReceiveDecalOutput); if (bTranslucentBasePass) { SetTranslucentRenderState(DrawRenderState, MaterialResource, GShaderPlatformForFeatureLevel[FeatureLevel], TranslucencyPassType); } TBasePassShaderElementData ShaderElementData(LightMapElementData); ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, true); FMeshDrawCommandSortKey SortKey = FMeshDrawCommandSortKey::Default; if (bTranslucentBasePass) { SortKey = CalculateTranslucentMeshStaticSortKey(PrimitiveSceneProxy, MeshBatch.MeshIdInPrimitive); } else { SortKey = CalculateBasePassMeshStaticSortKey(EarlyZPassMode, BlendMode, BasePassShaders.VertexShader.GetShader(), BasePassShaders.PixelShader.GetShader()); } BuildMeshDrawCommands( MeshBatch, BatchElementMask, PrimitiveSceneProxy, MaterialRenderProxy, MaterialResource, DrawRenderState, BasePassShaders, MeshFillMode, MeshCullMode, SortKey, EMeshPassFeatures::Default, ShaderElementData); return true; } bool FBasePassMeshProcessor::AddMeshBatchForSimpleForwardShading( const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, int32 StaticMeshId, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, const FMaterialRenderProxy& MaterialRenderProxy, const FMaterial& Material, const FLightMapInteraction& LightMapInteraction, bool bIsLitMaterial, bool bAllowStaticLighting, bool bUseVolumetricLightmap, bool bAllowIndirectLightingCache, ERasterizerFillMode MeshFillMode, ERasterizerCullMode MeshCullMode) { const EBlendMode BlendMode = Material.GetBlendMode(); const FMaterialShadingModelField ShadingModels = Material.GetShadingModels(); bool bResult = false; if (bAllowStaticLighting && LightMapInteraction.GetType() == LMIT_Texture) { const FShadowMapInteraction ShadowMapInteraction = (MeshBatch.LCI && bIsLitMaterial) ? MeshBatch.LCI->GetShadowMapInteraction(FeatureLevel) : FShadowMapInteraction(); if (ShadowMapInteraction.GetType() == SMIT_Texture) { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_SIMPLE_STATIONARY_PRECOMPUTED_SHADOW_LIGHTING), MeshBatch.LCI, MeshFillMode, MeshCullMode); } else { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_SIMPLE_LIGHTMAP_ONLY_LIGHTING), MeshBatch.LCI, MeshFillMode, MeshCullMode); } } else if (bIsLitMaterial && bAllowStaticLighting && bUseVolumetricLightmap && PrimitiveSceneProxy) { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_SIMPLE_STATIONARY_VOLUMETRICLIGHTMAP_SHADOW_LIGHTING), MeshBatch.LCI, MeshFillMode, MeshCullMode); } else if (bIsLitMaterial && IsIndirectLightingCacheAllowed(FeatureLevel) && bAllowIndirectLightingCache && PrimitiveSceneProxy) { const FIndirectLightingCacheAllocation* IndirectLightingCacheAllocation = PrimitiveSceneProxy->GetPrimitiveSceneInfo()->IndirectLightingCacheAllocation; const bool bPrimitiveIsMovable = PrimitiveSceneProxy->IsMovable(); const bool bPrimitiveUsesILC = PrimitiveSceneProxy->GetIndirectLightingCacheQuality() != ILCQ_Off; // Use the indirect lighting cache shaders if the object has a cache allocation // This happens for objects with unbuilt lighting if (bPrimitiveUsesILC && ((IndirectLightingCacheAllocation && IndirectLightingCacheAllocation->IsValid()) // Use the indirect lighting cache shaders if the object is movable, it may not have a cache allocation yet because that is done in InitViews // And movable objects are sometimes rendered in the static draw lists || bPrimitiveIsMovable)) { // Use a lightmap policy that supports reading indirect lighting from a single SH sample bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_SIMPLE_STATIONARY_SINGLESAMPLE_SHADOW_LIGHTING), MeshBatch.LCI, MeshFillMode, MeshCullMode); } else { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_SIMPLE_NO_LIGHTMAP), MeshBatch.LCI, MeshFillMode, MeshCullMode); } } else if (bIsLitMaterial) { // Always choosing shaders to support dynamic directional even if one is not present bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_SIMPLE_DIRECTIONAL_LIGHT_LIGHTING), MeshBatch.LCI, MeshFillMode, MeshCullMode); } else { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_SIMPLE_NO_LIGHTMAP), MeshBatch.LCI, MeshFillMode, MeshCullMode); } return bResult; } void FBasePassMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId) { if (MeshBatch.bUseForMaterial) { // Determine the mesh's material and blend mode. const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy; while (MaterialRenderProxy) { const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel); if (Material && Material->GetRenderingThreadShaderMap()) { if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material)) { break; } } MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel); } } } bool AllowStaticLighting() { static const auto AllowStaticLightingVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting")); return (!AllowStaticLightingVar || AllowStaticLightingVar->GetValueOnRenderThread() != 0); } bool FBasePassMeshProcessor::TryAddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId, const FMaterialRenderProxy& MaterialRenderProxy, const FMaterial& Material) { // Determine the mesh's material and blend mode. const EBlendMode BlendMode = Material.GetBlendMode(); const EStrataBlendMode StrataBlendMode = Material.GetStrataBlendMode(); const FMaterialShadingModelField ShadingModels = Material.GetShadingModels(); const bool bIsTranslucent = IsTranslucentBlendMode(BlendMode); const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch); const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MeshBatch, Material, OverrideSettings); const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(MeshBatch, Material, OverrideSettings); bool bShouldDraw = false; if (bTranslucentBasePass) { if (bIsTranslucent && !Material.IsDeferredDecal()) { switch (TranslucencyPassType) { case ETranslucencyPass::TPT_StandardTranslucency: bShouldDraw = !Material.IsTranslucencyAfterDOFEnabled() && !Material.IsTranslucencyAfterMotionBlurEnabled(); break; case ETranslucencyPass::TPT_TranslucencyAfterDOF: bShouldDraw = Material.IsTranslucencyAfterDOFEnabled() && StrataBlendMode != SBM_ColoredTransmittanceOnly; break; // only dual blended or modulate surfaces need background modulation case ETranslucencyPass::TPT_TranslucencyAfterDOFModulate: bShouldDraw = Material.IsTranslucencyAfterDOFEnabled() && (Material.IsDualBlendingEnabled(GetFeatureLevelShaderPlatform(FeatureLevel)) || BlendMode == BLEND_Modulate || StrataBlendMode == SBM_ColoredTransmittanceOnly); break; case ETranslucencyPass::TPT_TranslucencyAfterMotionBlur: bShouldDraw = Material.IsTranslucencyAfterMotionBlurEnabled(); break; case ETranslucencyPass::TPT_AllTranslucency: bShouldDraw = true; break; } } } else { bShouldDraw = !bIsTranslucent; } // Only draw opaque materials. bool bResult = true; if (bShouldDraw && (!PrimitiveSceneProxy || PrimitiveSceneProxy->ShouldRenderInMainPass()) && ShouldIncludeDomainInMeshPass(Material.GetMaterialDomain()) && ShouldIncludeMaterialInDefaultOpaquePass(Material)) { // Check for a cached light-map. const bool bIsLitMaterial = ShadingModels.IsLit(); const bool bAllowStaticLighting = AllowStaticLighting(); const FLightMapInteraction LightMapInteraction = (bAllowStaticLighting && MeshBatch.LCI && bIsLitMaterial) ? MeshBatch.LCI->GetLightMapInteraction(FeatureLevel) : FLightMapInteraction(); // force LQ lightmaps based on system settings const bool bPlatformAllowsHighQualityLightMaps = AllowHighQualityLightmaps(FeatureLevel); const bool bAllowHighQualityLightMaps = bPlatformAllowsHighQualityLightMaps && LightMapInteraction.AllowsHighQualityLightmaps(); const bool bAllowIndirectLightingCache = Scene && Scene->PrecomputedLightVolumes.Num() > 0; const bool bUseVolumetricLightmap = Scene && Scene->VolumetricLightmapSceneData.HasData(); FMeshMaterialShaderElementData MeshMaterialShaderElementData; MeshMaterialShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, true); if (IsSimpleForwardShadingEnabled(GetFeatureLevelShaderPlatform(FeatureLevel))) { // Only compiling simple lighting shaders for HQ lightmaps to save on permutations check(bPlatformAllowsHighQualityLightMaps); bResult = AddMeshBatchForSimpleForwardShading( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, LightMapInteraction, bIsLitMaterial, bAllowStaticLighting, bUseVolumetricLightmap, bAllowIndirectLightingCache, MeshFillMode, MeshCullMode); } // Render volumetric translucent self-shadowing only for >= SM4 and fallback to non-shadowed for lesser shader models else if (bIsLitMaterial && bIsTranslucent && PrimitiveSceneProxy && PrimitiveSceneProxy->CastsVolumetricTranslucentShadow()) { checkSlow(ViewIfDynamicMeshCommand && ViewIfDynamicMeshCommand->bIsViewInfo); const FViewInfo* ViewInfo = (FViewInfo*)ViewIfDynamicMeshCommand; const int32 PrimitiveIndex = PrimitiveSceneProxy->GetPrimitiveSceneInfo()->GetIndex(); const FUniformBufferRHIRef* UniformBufferPtr = ViewInfo->TranslucentSelfShadowUniformBufferMap.Find(PrimitiveIndex); FSelfShadowLightCacheElementData ElementData; ElementData.LCI = MeshBatch.LCI; ElementData.SelfShadowTranslucencyUniformBuffer = UniformBufferPtr ? (*UniformBufferPtr).GetReference() : GEmptyTranslucentSelfShadowUniformBuffer.GetUniformBufferRHI(); if (bIsLitMaterial && bAllowStaticLighting && bUseVolumetricLightmap && PrimitiveSceneProxy) { bResult = Process< FSelfShadowedVolumetricLightmapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FSelfShadowedVolumetricLightmapPolicy(), ElementData, MeshFillMode, MeshCullMode); } else if (IsIndirectLightingCacheAllowed(FeatureLevel) && bAllowIndirectLightingCache && PrimitiveSceneProxy) { // Apply cached point indirect lighting as well as self shadowing if needed bResult = Process< FSelfShadowedCachedPointIndirectLightingPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FSelfShadowedCachedPointIndirectLightingPolicy(), ElementData, MeshFillMode, MeshCullMode); } else { bResult = Process< FSelfShadowedTranslucencyPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FSelfShadowedTranslucencyPolicy(), ElementData.SelfShadowTranslucencyUniformBuffer, MeshFillMode, MeshCullMode); } } else { static const auto CVarSupportLowQualityLightmap = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.SupportLowQualityLightmaps")); const bool bAllowLowQualityLightMaps = (!CVarSupportLowQualityLightmap) || (CVarSupportLowQualityLightmap->GetValueOnAnyThread() != 0); switch (LightMapInteraction.GetType()) { case LMIT_Texture: if (bAllowHighQualityLightMaps) { const FShadowMapInteraction ShadowMapInteraction = (bAllowStaticLighting && MeshBatch.LCI && bIsLitMaterial) ? MeshBatch.LCI->GetShadowMapInteraction(FeatureLevel) : FShadowMapInteraction(); if (ShadowMapInteraction.GetType() == SMIT_Texture) { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_DISTANCE_FIELD_SHADOWS_AND_HQ_LIGHTMAP), MeshBatch.LCI, MeshFillMode, MeshCullMode); } else { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_HQ_LIGHTMAP), MeshBatch.LCI, MeshFillMode, MeshCullMode); } } else if (bAllowLowQualityLightMaps) { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_LQ_LIGHTMAP), MeshBatch.LCI, MeshFillMode, MeshCullMode); } else { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_NO_LIGHTMAP), MeshBatch.LCI, MeshFillMode, MeshCullMode); } break; default: if (bIsLitMaterial && bAllowStaticLighting && bUseVolumetricLightmap && PrimitiveSceneProxy && (PrimitiveSceneProxy->IsMovable() || PrimitiveSceneProxy->NeedsUnbuiltPreviewLighting() || PrimitiveSceneProxy->GetLightmapType() == ELightmapType::ForceVolumetric)) { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_PRECOMPUTED_IRRADIANCE_VOLUME_INDIRECT_LIGHTING), MeshBatch.LCI, MeshFillMode, MeshCullMode); } else if (bIsLitMaterial && IsIndirectLightingCacheAllowed(FeatureLevel) && Scene && Scene->PrecomputedLightVolumes.Num() > 0 && PrimitiveSceneProxy) { const FIndirectLightingCacheAllocation* IndirectLightingCacheAllocation = PrimitiveSceneProxy->GetPrimitiveSceneInfo()->IndirectLightingCacheAllocation; const bool bPrimitiveIsMovable = PrimitiveSceneProxy->IsMovable(); const bool bPrimitiveUsesILC = PrimitiveSceneProxy->GetIndirectLightingCacheQuality() != ILCQ_Off; // Use the indirect lighting cache shaders if the object has a cache allocation // This happens for objects with unbuilt lighting if (bPrimitiveUsesILC && ((IndirectLightingCacheAllocation && IndirectLightingCacheAllocation->IsValid()) // Use the indirect lighting cache shaders if the object is movable, it may not have a cache allocation yet because that is done in InitViews // And movable objects are sometimes rendered in the static draw lists || bPrimitiveIsMovable)) { if (CanIndirectLightingCacheUseVolumeTexture(FeatureLevel) // Translucency forces point sample for pixel performance && !bIsTranslucent && ((IndirectLightingCacheAllocation && !IndirectLightingCacheAllocation->bPointSample) || (bPrimitiveIsMovable && PrimitiveSceneProxy->GetIndirectLightingCacheQuality() == ILCQ_Volume))) { // Use a lightmap policy that supports reading indirect lighting from a volume texture for dynamic objects bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_CACHED_VOLUME_INDIRECT_LIGHTING), MeshBatch.LCI, MeshFillMode, MeshCullMode); } else { // Use a lightmap policy that supports reading indirect lighting from a single SH sample bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_CACHED_POINT_INDIRECT_LIGHTING), MeshBatch.LCI, MeshFillMode, MeshCullMode); } } else { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_NO_LIGHTMAP), MeshBatch.LCI, MeshFillMode, MeshCullMode); } } else { bResult = Process< FUniformLightMapPolicy >( MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, FUniformLightMapPolicy(LMP_NO_LIGHTMAP), MeshBatch.LCI, MeshFillMode, MeshCullMode); } break; }; } } return bResult; } FBasePassMeshProcessor::FBasePassMeshProcessor( const FScene* Scene, ERHIFeatureLevel::Type InFeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, const FMeshPassProcessorRenderState& InDrawRenderState, FMeshPassDrawListContext* InDrawListContext, EFlags Flags, ETranslucencyPass::Type InTranslucencyPassType) : FMeshPassProcessor(Scene, InFeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext) , PassDrawRenderState(InDrawRenderState) , TranslucencyPassType(InTranslucencyPassType) , bTranslucentBasePass(InTranslucencyPassType != ETranslucencyPass::TPT_MAX) , bEnableReceiveDecalOutput((Flags & EFlags::CanUseDepthStencil) == EFlags::CanUseDepthStencil) , EarlyZPassMode(Scene ? Scene->EarlyZPassMode : DDM_None) , bRequiresExplicit128bitRT((Flags & EFlags::bRequires128bitRT) == EFlags::bRequires128bitRT) { } FMeshPassProcessor* CreateBasePassProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext) { FMeshPassProcessorRenderState PassDrawRenderState; SetupBasePassState(Scene->DefaultBasePassDepthStencilAccess, false, PassDrawRenderState); const FBasePassMeshProcessor::EFlags Flags = FBasePassMeshProcessor::EFlags::CanUseDepthStencil; return new(FMemStack::Get()) FBasePassMeshProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, PassDrawRenderState, InDrawListContext, Flags); } FMeshPassProcessor* CreateTranslucencyStandardPassProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext) { FMeshPassProcessorRenderState PassDrawRenderState; PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); const FBasePassMeshProcessor::EFlags Flags = FBasePassMeshProcessor::EFlags::CanUseDepthStencil; return new(FMemStack::Get()) FBasePassMeshProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, PassDrawRenderState, InDrawListContext, Flags, ETranslucencyPass::TPT_StandardTranslucency); } FMeshPassProcessor* CreateTranslucencyAfterDOFProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext) { FMeshPassProcessorRenderState PassDrawRenderState; PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); const FBasePassMeshProcessor::EFlags Flags = FBasePassMeshProcessor::EFlags::CanUseDepthStencil; return new(FMemStack::Get()) FBasePassMeshProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, PassDrawRenderState, InDrawListContext, Flags, ETranslucencyPass::TPT_TranslucencyAfterDOF); } FMeshPassProcessor* CreateTranslucencyAfterDOFModulateProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext) { FMeshPassProcessorRenderState PassDrawRenderState; PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); const FBasePassMeshProcessor::EFlags Flags = FBasePassMeshProcessor::EFlags::CanUseDepthStencil; return new(FMemStack::Get()) FBasePassMeshProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, PassDrawRenderState, InDrawListContext, Flags, ETranslucencyPass::TPT_TranslucencyAfterDOFModulate); } FMeshPassProcessor* CreateTranslucencyAfterMotionBlurProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext) { FMeshPassProcessorRenderState PassDrawRenderState; PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); PassDrawRenderState.SetDepthStencilAccess(FExclusiveDepthStencil::DepthNop_StencilNop); const FBasePassMeshProcessor::EFlags Flags = FBasePassMeshProcessor::EFlags::None; return new(FMemStack::Get()) FBasePassMeshProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, PassDrawRenderState, InDrawListContext, Flags, ETranslucencyPass::TPT_TranslucencyAfterMotionBlur); } FMeshPassProcessor* CreateTranslucencyAllPassProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext) { FMeshPassProcessorRenderState PassDrawRenderState; PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); const FBasePassMeshProcessor::EFlags Flags = FBasePassMeshProcessor::EFlags::CanUseDepthStencil; return new(FMemStack::Get()) FBasePassMeshProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, PassDrawRenderState, InDrawListContext, Flags, ETranslucencyPass::TPT_AllTranslucency); } FRegisterPassProcessorCreateFunction RegisterBasePass(&CreateBasePassProcessor, EShadingPath::Deferred, EMeshPass::BasePass, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView); FRegisterPassProcessorCreateFunction RegisterTranslucencyStandardPass(&CreateTranslucencyStandardPassProcessor, EShadingPath::Deferred, EMeshPass::TranslucencyStandard, EMeshPassFlags::MainView); FRegisterPassProcessorCreateFunction RegisterTranslucencyAfterDOFPass(&CreateTranslucencyAfterDOFProcessor, EShadingPath::Deferred, EMeshPass::TranslucencyAfterDOF, EMeshPassFlags::MainView); FRegisterPassProcessorCreateFunction RegisterTranslucencyAfterDOFModulatePass(&CreateTranslucencyAfterDOFModulateProcessor, EShadingPath::Deferred, EMeshPass::TranslucencyAfterDOFModulate, EMeshPassFlags::MainView); FRegisterPassProcessorCreateFunction RegisterTranslucencyAfterMotionBlurPass(&CreateTranslucencyAfterMotionBlurProcessor, EShadingPath::Deferred, EMeshPass::TranslucencyAfterMotionBlur, EMeshPassFlags::MainView); FRegisterPassProcessorCreateFunction RegisterTranslucencyAllPass(&CreateTranslucencyAllPassProcessor, EShadingPath::Deferred, EMeshPass::TranslucencyAll, EMeshPassFlags::MainView);