// Copyright Epic Games, Inc. All Rights Reserved. #include "MobileDeferredShadingPass.h" #include "BasePassRendering.h" #include "PSOPrecacheValidation.h" #include "SceneView.h" #include "ScenePrivate.h" #include "SceneProxies/SkyLightSceneProxy.h" #include "PostProcess/PostProcessing.h" #include "PostProcess/SceneFilterRendering.h" #include "PipelineStateCache.h" #include "PlanarReflectionRendering.h" #include "LightFunctionRendering.h" #include "LightRendering.h" #include "LocalLightSceneProxy.h" #include "Materials/MaterialRenderProxy.h" #include "MobileSSR.h" #include "DistanceFieldLightingShared.h" #include "DistanceFieldAmbientOcclusion.h" #include "DistanceFieldLightingShared.h" DECLARE_GPU_STAT(DeferredShading); static const TCHAR* DeferredMobileLightMaterialPSOCollectorName = TEXT("DeferredMobileLightMaterialPSOCollector"); static FAutoConsoleVariableDeprecated CVarMobileUseClusteredDeferredShadingDep(TEXT("r.Mobile.UseClusteredDeferredShading"), TEXT("r.Mobile.UseClusteredDeferredShading_ToBeRemoved"), TEXT("5.7")); int32 GMobileUseClusteredDeferredShading = 0; static FAutoConsoleVariableRef CVarMobileUseClusteredDeferredShading( TEXT("r.Mobile.UseClusteredDeferredShading_ToBeRemoved"), GMobileUseClusteredDeferredShading, TEXT("NOTE: The mobile clustered deferred shading implementation will be removed in a future release due to low utility and use.\n") TEXT("Toggle use of clustered deferred shading for lights that support it. 0 is off (default), 1 is on. (requires LightGrid: r.Mobile.Forward.EnableLocalLights=1)"), ECVF_RenderThreadSafe ); static bool UseClusteredDeferredShading(const FStaticShaderPlatform Platform) { // Needs LightGrid to function return GMobileUseClusteredDeferredShading != 0 && MobileForwardEnableLocalLights(Platform); } static bool MobileDeferredEnableAmbientOcclusion(const FStaticShaderPlatform Platform) { // AO requires a full depth before shading return MobileUsesFullDepthPrepass(Platform) || !MobileAllowFramebufferFetch(Platform); } int32 GMobileUseLightStencilCulling = 0; static FAutoConsoleVariableRef CVarMobileUseLightStencilCulling( TEXT("r.Mobile.UseLightStencilCulling"), GMobileUseLightStencilCulling, TEXT("Whether to use stencil to cull local lights. 0 is off (default), 1 is on"), ECVF_RenderThreadSafe ); int32 GMobileIgnoreDeferredShadingSkyLightChannels = 0; static FAutoConsoleVariableRef CVarMobileIgnoreDeferredShadingSkyLightChannels( TEXT("r.Mobile.IgnoreDeferredShadingSkyLightChannels"), GMobileIgnoreDeferredShadingSkyLightChannels, TEXT("Whether to ignore primitive lighting channels when applying SkyLighting in a mobile deferred shading.\n" "This may improve GPU performance at the cost of incorrect lighting for a primitves with non-default lighting channels"), ECVF_RenderThreadSafe ); BEGIN_SHADER_PARAMETER_STRUCT(FMobileDeferredPassParameters, ) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FMobileSceneTextureUniformParameters, MobileSceneTextures) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() BEGIN_SHADER_PARAMETER_STRUCT(FMobileDeferredCommonParameters, ) SHADER_PARAMETER(FMatrix44f, TranslatedWorldToLight) SHADER_PARAMETER(FVector4f, LightFunctionParameters) SHADER_PARAMETER(FVector2f, LightFunctionParameters2) SHADER_PARAMETER(FVector3f, CameraRelativeLightPosition) END_SHADER_PARAMETER_STRUCT() class FMobileDirectionalLightFunctionPS : public FMaterialShader { DECLARE_SHADER_TYPE(FMobileDirectionalLightFunctionPS, Material); SHADER_USE_PARAMETER_STRUCT_WITH_LEGACY_BASE(FMobileDirectionalLightFunctionPS, FMaterialShader) class FEnableShadingModelSupport : SHADER_PERMUTATION_BOOL("ENABLE_SHADINGMODEL_SUPPORT_MOBILE_DEFERRED"); class FEnableClustredLights : SHADER_PERMUTATION_BOOL("ENABLE_CLUSTERED_LIGHTS"); class FEnableSkyLight : SHADER_PERMUTATION_BOOL("ENABLE_SKY_LIGHT"); class FEnableScreenSpaceShadowMask : SHADER_PERMUTATION_BOOL("ENABLE_SHADOWMASKTEXTURE"); class FEnableCSM : SHADER_PERMUTATION_BOOL("ENABLE_MOBILE_CSM"); class FShadowQuality : SHADER_PERMUTATION_RANGE_INT("MOBILE_SHADOW_QUALITY", 1, 3); // not using Quality=0 class FSkyShadowing : SHADER_PERMUTATION_BOOL("APPLY_SKY_SHADOWING"); using FPermutationDomain = TShaderPermutationDomain< FEnableShadingModelSupport, FEnableClustredLights, FEnableSkyLight, FEnableScreenSpaceShadowMask, FEnableCSM, FShadowQuality, FSkyShadowing>; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FMobileDirectionalLightShaderParameters, MobileDirectionalLight) SHADER_PARAMETER_STRUCT_INCLUDE(FMobileDeferredCommonParameters, MobileDeferredCommonParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FDFAOUpsampleParameters, DFAOUpsampleParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FSkyDiffuseLightingParameters, SkyDiffuseLighting) END_SHADER_PARAMETER_STRUCT() static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FPermutationDomain PermutationVector(Parameters.PermutationId); FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); FForwardLightingParameters::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("USE_LIGHT_FUNCTION"), Parameters.MaterialParameters.bIsDefaultMaterial ? 0 : 1); OutEnvironment.SetDefine(TEXT("MATERIAL_SHADER"), 1); OutEnvironment.SetDefine(TEXT("IS_MOBILE_DEFERREDSHADING_SUBPASS"), 1u); const bool bMobileForceDepthRead = MobileUsesFullDepthPrepass(Parameters.Platform); OutEnvironment.SetDefine(TEXT("FORCE_DEPTH_TEXTURE_READS"), bMobileForceDepthRead ? 1u : 0u); OutEnvironment.SetDefine(TEXT("ENABLE_AMBIENT_OCCLUSION"), MobileDeferredEnableAmbientOcclusion(Parameters.Platform) ? 1u : 0u); } static FPermutationDomain RemapPermutationVector(FPermutationDomain PermutationVector, EShaderPlatform Platform) { if (MobileUsesShadowMaskTexture(Platform) || PermutationVector.Get() == true) { PermutationVector.Set(false); } if (PermutationVector.Get() == false) { PermutationVector.Set(1); } if (!MobileUsesGBufferCustomData(Platform)) { PermutationVector.Set(false); } if (!IsMobileDistanceFieldAOEnabled(Platform)) { PermutationVector.Set(false); } return PermutationVector; } static bool ShouldCompilePermutation(const FMaterialShaderPermutationParameters& Parameters) { if (Parameters.MaterialParameters.MaterialDomain != MD_LightFunction || !IsMobilePlatform(Parameters.Platform) || !IsMobileDeferredShadingEnabled(Parameters.Platform)) { return false; } FPermutationDomain PermutationVector(Parameters.PermutationId); // Compile out the shader if this permutation gets remapped. if (RemapPermutationVector(PermutationVector, Parameters.Platform) != PermutationVector) { return false; } return true; } static FPermutationDomain BuildPermutationVector( const FViewInfo& View, bool bInlineReflectionAndSky, bool bShadingModelSupport, bool bDynamicShadows, bool bSkyLight, bool bScreenSpaceShadowMask, bool bApplySkyShadowing) { EShaderPlatform ShaderPlatform = View.GetShaderPlatform(); bool bUseClusteredLights = UseClusteredDeferredShading(ShaderPlatform); bool bEnableSkyLight = bInlineReflectionAndSky && bSkyLight; int32 ShadowQuality = bDynamicShadows && !bScreenSpaceShadowMask ? (int32)GetShadowQuality() : 0; FPermutationDomain PermutationVector; PermutationVector.Set(bShadingModelSupport); PermutationVector.Set(bUseClusteredLights); PermutationVector.Set(bEnableSkyLight); PermutationVector.Set(bScreenSpaceShadowMask); PermutationVector.Set(ShadowQuality > 0); PermutationVector.Set(FMath::Clamp(ShadowQuality, 1, 3)); PermutationVector.Set(bInlineReflectionAndSky && bApplySkyShadowing); return PermutationVector; } void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FViewInfo& View, const FMaterialRenderProxy* Proxy, const FMaterial& Material) { FMaterialShader::SetParameters(BatchedParameters, Proxy, Material, View); // LightFunctions can use primitive data, set identity so we do not crash on a missing binding auto& PrimitivePS = GetUniformBufferParameter(); SetUniformBufferParameter(BatchedParameters, PrimitivePS, GIdentityPrimitiveUniformBuffer); } }; IMPLEMENT_MATERIAL_SHADER_TYPE(, FMobileDirectionalLightFunctionPS, TEXT("/Engine/Private/MobileDeferredShading.usf"), TEXT("MobileDirectionalLightPS"), SF_Pixel); /** * A pixel shader for projecting a light function onto the scene. */ class FMobileRadialLightFunctionPS : public FMaterialShader { public: DECLARE_SHADER_TYPE(FMobileRadialLightFunctionPS,Material); SHADER_USE_PARAMETER_STRUCT_WITH_LEGACY_BASE(FMobileRadialLightFunctionPS, FMaterialShader) class FEnableShadingModelSupport: SHADER_PERMUTATION_BOOL("ENABLE_SHADINGMODEL_SUPPORT_MOBILE_DEFERRED"); class FRadialLightTypeDim : SHADER_PERMUTATION_RANGE_INT("RADIAL_LIGHT_TYPE", LIGHT_TYPE_POINT, LIGHT_TYPE_RECT); class FIESProfileDim : SHADER_PERMUTATION_BOOL("USE_IES_PROFILE"); class FSpotLightShadowDim : SHADER_PERMUTATION_BOOL("SUPPORT_SPOTLIGHTS_SHADOW"); class FSimpleLightDim : SHADER_PERMUTATION_BOOL("SIMPLE_LIGHT"); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT(FLightShaderParameters, Light) SHADER_PARAMETER_STRUCT_INCLUDE(FMobileMovableLocalLightShadowParameters, MobileMovableLocalLightShadow) SHADER_PARAMETER_STRUCT_INCLUDE(FMobileDeferredCommonParameters, MobileDeferredCommonParameters) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FMaterialShaderPermutationParameters& Parameters) { if (Parameters.MaterialParameters.MaterialDomain != MD_LightFunction || !IsMobilePlatform(Parameters.Platform) || !IsMobileDeferredShadingEnabled(Parameters.Platform)) { return false; } FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get() == true && !Parameters.MaterialParameters.bIsDefaultMaterial) { return false; } // Compile out the shader if this permutation gets remapped. if (RemapPermutationVector(PermutationVector, Parameters.Platform) != PermutationVector) { return false; } return true; } static FPermutationDomain RemapPermutationVector(FPermutationDomain PermutationVector, EShaderPlatform Platform) { if (!IsMobileMovableSpotlightShadowsEnabled(Platform)) { PermutationVector.Set(false); } if (!MobileUsesGBufferCustomData(Platform)) { PermutationVector.Set(false); } if (PermutationVector.Get() == true) { PermutationVector.Set(false); PermutationVector.Set(LIGHT_TYPE_POINT); PermutationVector.Set(false); PermutationVector.Set(false); } return PermutationVector; } static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("USE_LIGHT_FUNCTION"), Parameters.MaterialParameters.bIsDefaultMaterial ? 0 : 1); OutEnvironment.SetDefine(TEXT("MATERIAL_SHADER"), 1); OutEnvironment.SetDefine(TEXT("ENABLE_SHADOWMASKTEXTURE"), 0); OutEnvironment.SetDefine(TEXT("ENABLE_CLUSTERED_LIGHTS"), 0); OutEnvironment.SetDefine(TEXT("IS_MOBILE_DEFERREDSHADING_SUBPASS"), 1u); const bool bMobileForceDepthRead = MobileUsesFullDepthPrepass(Parameters.Platform); OutEnvironment.SetDefine(TEXT("FORCE_DEPTH_TEXTURE_READS"), bMobileForceDepthRead ? 1u : 0u); const bool bSupportCapsule = MobileSupportsSM5MaterialNodes(Parameters.Platform); OutEnvironment.SetDefine(TEXT("MOBILE_SHADING_PATH_SUPPORT_CAPSULE_LIGHT"), bSupportCapsule ? 1u : 0u); } void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FViewInfo& View, const FMaterialRenderProxy* Proxy, const FMaterial& Material) { FMaterialShader::SetViewParameters(BatchedParameters, View, View.ViewUniformBuffer); FMaterialShader::SetParameters(BatchedParameters, Proxy, Material, View); // LightFunctions can use primitive data, set identity so we do not crash on a missing binding auto& PrimitivePS = GetUniformBufferParameter(); SetUniformBufferParameter(BatchedParameters, PrimitivePS, GIdentityPrimitiveUniformBuffer); } }; IMPLEMENT_MATERIAL_SHADER_TYPE(,FMobileRadialLightFunctionPS, TEXT("/Engine/Private/MobileDeferredShading.usf"), TEXT("MobileRadialLightPS"), SF_Pixel); /** * A pixel shader for reflection env and sky lighting. */ class FMobileReflectionEnvironmentSkyLightingPS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FMobileReflectionEnvironmentSkyLightingPS); SHADER_USE_PARAMETER_STRUCT(FMobileReflectionEnvironmentSkyLightingPS, FGlobalShader); class FEnableShadingModelSupport : SHADER_PERMUTATION_BOOL("ENABLE_SHADINGMODEL_SUPPORT_MOBILE_DEFERRED"); class FEnableClustredReflection : SHADER_PERMUTATION_BOOL("ENABLE_CLUSTERED_REFLECTION"); class FEnablePlanarReflection : SHADER_PERMUTATION_BOOL("ENABLE_PLANAR_REFLECTION"); class FEnableSkyLight : SHADER_PERMUTATION_BOOL("ENABLE_SKY_LIGHT"); class FMobileSSRQuality : SHADER_PERMUTATION_ENUM_CLASS("MOBILE_SSR_QUALITY", EMobileSSRQuality); class FSkyShadowing : SHADER_PERMUTATION_BOOL("APPLY_SKY_SHADOWING"); using FPermutationDomain = TShaderPermutationDomain< FEnableShadingModelSupport, FEnableClustredReflection, FEnablePlanarReflection, FEnableSkyLight, FMobileSSRQuality, FSkyShadowing >; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_STRUCT_REF(FMobileReflectionCaptureShaderData, MobileReflectionCaptureData) SHADER_PARAMETER_STRUCT_INCLUDE(FDFAOUpsampleParameters, DFAOUpsampleParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FSkyDiffuseLightingParameters, SkyDiffuseLighting) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { if (!IsMobilePlatform(Parameters.Platform) || !IsMobileDeferredShadingEnabled(Parameters.Platform)) { return false; } FPermutationDomain PermutationVector(Parameters.PermutationId); if (!MobileUsesGBufferCustomData(Parameters.Platform) && PermutationVector.Get()) { return false; } if (PermutationVector.Get() && !IsMobileDistanceFieldAOEnabled(Parameters.Platform)) { return false; } return true; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FPermutationDomain PermutationVector(Parameters.PermutationId); FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); FForwardLightingParameters::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("IS_MOBILE_DEFERREDSHADING_SUBPASS"), 1u); const bool bMobileForceDepthRead = MobileUsesFullDepthPrepass(Parameters.Platform); OutEnvironment.SetDefine(TEXT("FORCE_DEPTH_TEXTURE_READS"), bMobileForceDepthRead ? 1u : 0u); OutEnvironment.SetDefine(TEXT("ENABLE_AMBIENT_OCCLUSION"), MobileDeferredEnableAmbientOcclusion(Parameters.Platform) ? 1u : 0u); OutEnvironment.SetDefine(TEXT("MOBILE_SSR_ENABLED"), PermutationVector.Get() != EMobileSSRQuality::Disabled ? 1u : 0u); } }; IMPLEMENT_GLOBAL_SHADER(FMobileReflectionEnvironmentSkyLightingPS, "/Engine/Private/MobileDeferredShading.usf", "MobileReflectionEnvironmentSkyLightingPS", SF_Pixel); constexpr uint32 GetLightingChannel(uint32 LightingChannelMask) { return (LightingChannelMask & 0x1) ? 0u : ((LightingChannelMask & 0x2) ? 1u : 2u); } constexpr uint8 GetLightingChannelStencilValue(uint32 LightingChannel) { // LightingChannel_0 has an inverted bit in the stencil. 0 - means LightingChannel_0 is enabled. See FPrimitiveSceneProxy::GetLightingChannelStencilValue() return (LightingChannel == 0u ? 0u : (1u << LightingChannel)); } constexpr bool IsOnlyDefaultLitShadingModel(uint32 ShadingModelMask) { constexpr uint32 LitOpaqueMask = ~(1u << MSM_Unlit | 1u << MSM_SingleLayerWater | 1u << MSM_ThinTranslucent); constexpr uint32 DefaultLitMask = (1u << MSM_DefaultLit); return (ShadingModelMask & LitOpaqueMask) == DefaultLitMask; } struct FCachedLightMaterial { const FMaterial* Material; const FMaterialRenderProxy* MaterialProxy; }; template static void GetLightMaterial(const FCachedLightMaterial& DefaultLightMaterial, const FMaterialRenderProxy* MaterialProxy, int32 PermutationId, FCachedLightMaterial& OutLightMaterial, TShaderRef& OutShader) { FMaterialShaderTypes ShaderTypes; ShaderTypes.AddShaderType(PermutationId); FMaterialShaders Shaders; if (MaterialProxy) { const FMaterial* Material = MaterialProxy->GetMaterialNoFallback(ERHIFeatureLevel::ES3_1); if (Material && Material->IsLightFunction()) { OutLightMaterial.Material = Material; OutLightMaterial.MaterialProxy = MaterialProxy; if (Material->TryGetShaders(ShaderTypes, nullptr, Shaders)) { Shaders.TryGetPixelShader(OutShader); return; } } } // use default material OutLightMaterial.Material = DefaultLightMaterial.Material; OutLightMaterial.MaterialProxy = DefaultLightMaterial.MaterialProxy; // Perform a TryGetShaders to allow ODSC to record a shader recompile request when enabled if (DefaultLightMaterial.Material->TryGetShaders(ShaderTypes, nullptr, Shaders)) { Shaders.TryGetPixelShader(OutShader); return; } const FMaterialShaderMap* MaterialShaderMap = OutLightMaterial.Material->GetRenderingThreadShaderMap(); OutShader = MaterialShaderMap->GetShader(PermutationId); } uint8 PassShadingModelStencilValue(bool bEnableShadingModelSupport) { return bEnableShadingModelSupport ? GET_STENCIL_BIT_MASK(MOBILE_SHADINGMODELS, 1) : GET_STENCIL_BIT_MASK(MOBILE_DEFAULTLIT, 1); } void RenderReflectionEnvironmentSkyLighting( FRHICommandList& RHICmdList, const FScene& Scene, const FViewInfo& View, const EMobileSSRQuality MobileSSRQuality, FRDGTextureRef DynamicBentNormalAOTexture) { // Skylights with static lighting already had their diffuse contribution baked into lightmaps const bool bDynamicSkyLight = Scene.SkyLight && (!Scene.SkyLight->bHasStaticLighting || !IsStaticLightingAllowed()); const bool bEnableSkyLight = bDynamicSkyLight && View.Family->EngineShowFlags.SkyLighting; const bool bClustredReflection = (View.NumBoxReflectionCaptures + View.NumSphereReflectionCaptures) > 0; const bool bPlanarReflection = Scene.GetForwardPassGlobalPlanarReflection() != nullptr; if (!(bEnableSkyLight || bClustredReflection || bPlanarReflection || MobileSSRQuality != EMobileSSRQuality::Disabled)) { return; } SCOPED_DRAW_EVENT(RHICmdList, ReflectionEnvironmentSkyLighting); FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); // Add to emissive in SceneColor if (!bDynamicSkyLight) { // pre-multiply SceneColor with AO. Only need it for a static skylights GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } else { GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); FMobileReflectionEnvironmentSkyLightingPS::FParameters PassParameters; PassParameters.View = GetShaderBinding(View.ViewUniformBuffer); PassParameters.MobileReflectionCaptureData = GetShaderBinding(View.MobileReflectionCaptureUniformBuffer); // DFAO if (DynamicBentNormalAOTexture != nullptr) { PassParameters.DFAOUpsampleParameters = DistanceField::SetupAOUpsampleParameters(View, DynamicBentNormalAOTexture); float DynamicBentNormalAO = 1.0f; PassParameters.SkyDiffuseLighting = GetSkyDiffuseLightingParameters(Scene.SkyLight, DynamicBentNormalAO); } TShaderMapRef VertexShader(View.ShaderMap); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState< false, CF_Always, true, CF_Equal, SO_Keep, SO_Keep, SO_Keep, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, GET_STENCIL_MOBILE_SM_MASK(0xff), 0x00>::GetRHI(); uint8 StencilRef = PassShadingModelStencilValue(/*bEnableShadingModelSupport=*/false); bool bEnableShadingModelSupport = false; if (!IsOnlyDefaultLitShadingModel(View.ShadingModelMaskInView) && MobileUsesGBufferCustomData(Scene.GetShaderPlatform())) { GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState< false, CF_Always, true, CF_NotEqual, SO_Keep, SO_Keep, SO_Keep, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, GET_STENCIL_MOBILE_SM_MASK(0xff), 0x00>::GetRHI(); // Apply all shading models StencilRef = 0; bEnableShadingModelSupport = true; } FMobileReflectionEnvironmentSkyLightingPS::FPermutationDomain PermutationVector; PermutationVector.Set(bEnableShadingModelSupport); PermutationVector.Set(bClustredReflection); PermutationVector.Set(bPlanarReflection); PermutationVector.Set(bEnableSkyLight); PermutationVector.Set(MobileSSRQuality); extern bool UseDistanceFieldAO(); PermutationVector.Set(DynamicBentNormalAOTexture && UseDistanceFieldAO() && IsMobileDistanceFieldAOEnabled(View.GetShaderPlatform())); TShaderMapRef PixelShader(View.ShaderMap, PermutationVector); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PassParameters); const FIntPoint TargetSize = View.GetSceneTexturesConfig().Extent; DrawRectangle( RHICmdList, 0, 0, View.ViewRect.Width(), View.ViewRect.Height(), View.ViewRect.Min.X, View.ViewRect.Min.Y, View.ViewRect.Width(), View.ViewRect.Height(), FIntPoint(View.ViewRect.Width(), View.ViewRect.Height()), TargetSize, VertexShader); } template static void SetDirectionalLightDepthStencilState(FGraphicsPipelineStateInitializer& GraphicsPSOInit) { GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState< false, CF_Always, true, CF_Equal, SO_Keep, SO_Keep, SO_Keep, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, GET_STENCIL_MOBILE_SM_MASK(0xff) | STENCIL_LIGHTING_CHANNELS_MASK(1u << LightingChannelIdx), 0x00>::GetRHI(); } static void SetDirectionalLightDepthStencilState(FGraphicsPipelineStateInitializer& GraphicsPSOInit, uint32 LightingChannelIdx) { switch (LightingChannelIdx) { default: SetDirectionalLightDepthStencilState<0>(GraphicsPSOInit); break; case 1: SetDirectionalLightDepthStencilState<1>(GraphicsPSOInit); break; case 2: SetDirectionalLightDepthStencilState<2>(GraphicsPSOInit); break; } } static void RenderDirectionalLight( FRHICommandList& RHICmdList, const FScene& Scene, const FViewInfo& View, const FCachedLightMaterial& DefaultLightMaterial, const FLightSceneInfo& DirectionalLight, uint32 LightingChannel, bool bInlineReflectionAndSky, FRDGTextureRef DynamicBentNormalAOTexture) { FString LightNameWithLevel; FSceneRenderer::GetLightNameForDrawEvent(DirectionalLight.Proxy, LightNameWithLevel); SCOPED_DRAW_EVENTF(RHICmdList, DirectionalLight, TEXT("%s"), LightNameWithLevel); FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); TShaderMapRef VertexShader(View.ShaderMap); const FMaterialRenderProxy* LightFunctionMaterialProxy = nullptr; if (View.Family->EngineShowFlags.LightFunctions) { LightFunctionMaterialProxy = DirectionalLight.Proxy->GetLightFunctionMaterial(); } FMobileDirectionalLightFunctionPS::FParameters PassParameters; PassParameters.MobileDirectionalLight = Scene.UniformBuffers.MobileDirectionalLightUniformBuffers[LightingChannel + 1]; PassParameters.MobileDeferredCommonParameters.LightFunctionParameters = FVector4f(1.0f, 1.0f, 0.0f, 0.0f); PassParameters.MobileDeferredCommonParameters.CameraRelativeLightPosition = GetCamRelativeLightPosition(View.ViewMatrices, DirectionalLight); { PassParameters.MobileDeferredCommonParameters.LightFunctionParameters2 = FVector2f(DirectionalLight.Proxy->GetLightFunctionFadeDistance(), DirectionalLight.Proxy->GetLightFunctionDisabledBrightness()); const FVector Scale = DirectionalLight.Proxy->GetLightFunctionScale(); // Switch x and z so that z of the user specified scale affects the distance along the light direction const FVector InverseScale = FVector(1.f / Scale.Z, 1.f / Scale.Y, 1.f / Scale.X); const FMatrix WorldToLight = DirectionalLight.Proxy->GetWorldToLight() * FScaleMatrix(FVector(InverseScale)); PassParameters.MobileDeferredCommonParameters.TranslatedWorldToLight = FMatrix44f(FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation()) * WorldToLight); } // DFAO if (DynamicBentNormalAOTexture != nullptr) { PassParameters.DFAOUpsampleParameters = DistanceField::SetupAOUpsampleParameters(View, DynamicBentNormalAOTexture); float DynamicBentNormalAO = 1.0f; PassParameters.SkyDiffuseLighting = GetSkyDiffuseLightingParameters(Scene.SkyLight, DynamicBentNormalAO); } // Skylights with static lighting already had their diffuse contribution baked into lightmaps const bool bDynamicSkyLight = Scene.SkyLight && (!Scene.SkyLight->bHasStaticLighting || !IsStaticLightingAllowed()); const bool bEnableSkyLight = bDynamicSkyLight && View.Family->EngineShowFlags.SkyLighting; const bool bDynamicShadows = DirectionalLight.Proxy->CastsDynamicShadow() && View.Family->EngineShowFlags.DynamicShadows; // Add to emissive in SceneColor if (bInlineReflectionAndSky && !bDynamicSkyLight) { // pre-multiply SceneColor with AO GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } else { GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } // Do two passes, first masking DefautLit, second masking all other shading models const bool bOnlyDefaultLitInView = IsOnlyDefaultLitShadingModel(View.ShadingModelMaskInView); int32 NumPasses = 1; uint32 PassEnableShadingModelSupport = 0; uint32 ShadingModelStencilRef[2] = {}; ShadingModelStencilRef[0] = PassShadingModelStencilValue(/*bEnableShadingModelSupport=*/false); if (!bOnlyDefaultLitInView && MobileUsesGBufferCustomData(Scene.GetShaderPlatform())) { const int32 PassIndex = NumPasses++; PassEnableShadingModelSupport |= (1 << PassIndex); ShadingModelStencilRef[PassIndex] = PassShadingModelStencilValue(/*bEnableShadingModelSupport=*/true); } static auto CVarMobileSupportInsetShadows = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.SupportInsetShadows")); const bool bInsetShadows = (CVarMobileSupportInsetShadows != nullptr && CVarMobileSupportInsetShadows->GetInt() != 0); const bool bMobileUsesShadowMaskTexture = (MobileUsesShadowMaskTexture(View.GetShaderPlatform()) || bInsetShadows); const uint8 LightingChannelStencilValue = GetLightingChannelStencilValue(LightingChannel); SetDirectionalLightDepthStencilState(GraphicsPSOInit, LightingChannel); for (int32 PassIndex = 0; PassIndex < NumPasses; ++PassIndex) { FMobileDirectionalLightFunctionPS::FPermutationDomain PermutationVector = FMobileDirectionalLightFunctionPS::BuildPermutationVector( View, bInlineReflectionAndSky, PassEnableShadingModelSupport & (1 << PassIndex), bDynamicShadows, bEnableSkyLight, bMobileUsesShadowMaskTexture, DynamicBentNormalAOTexture && UseDistanceFieldAO() && IsMobileDistanceFieldAOEnabled(View.GetShaderPlatform()) ); FCachedLightMaterial LightMaterial; TShaderRef PixelShader; GetLightMaterial(DefaultLightMaterial, LightFunctionMaterialProxy, PermutationVector.ToDimensionValueId(), LightMaterial, PixelShader); const uint8 StencilRef = ShadingModelStencilRef[PassIndex] | STENCIL_LIGHTING_CHANNELS_MASK(LightingChannelStencilValue); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; #if PSO_PRECACHING_VALIDATE if (PSOCollectorStats::IsFullPrecachingValidationEnabled()) { static const int32 MaterialPSOCollectorIndex = FPSOCollectorCreateManager::GetIndex(GetFeatureLevelShadingPath(GMaxRHIFeatureLevel), DeferredMobileLightMaterialPSOCollectorName); PSOCollectorStats::CheckFullPipelineStateInCache(GraphicsPSOInit, EPSOPrecacheResult::Unknown, LightMaterial.Material, nullptr, nullptr, MaterialPSOCollectorIndex); } #endif // PSO_PRECACHING_VALIDATE SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef); SetShaderParametersMixedPS(RHICmdList, PixelShader, PassParameters, View, LightMaterial.MaterialProxy, *LightMaterial.Material); const FIntPoint TargetSize = View.GetSceneTexturesConfig().Extent; DrawRectangle( RHICmdList, 0, 0, View.ViewRect.Width(), View.ViewRect.Height(), View.ViewRect.Min.X, View.ViewRect.Min.Y, View.ViewRect.Width(), View.ViewRect.Height(), FIntPoint(View.ViewRect.Width(), View.ViewRect.Height()), TargetSize, VertexShader); } } static int RenderDirectionalLights( FRHICommandList& RHICmdList, const FScene& Scene, const FViewInfo& View, const FCachedLightMaterial& DefaultLightMaterial, EMobileSSRQuality MobileSSRQuality, FRDGTextureRef DynamicBentNormalAOTexture) { uint32 NumLights = 0; for (uint32 ChannelIdx = 0; ChannelIdx < UE_ARRAY_COUNT(Scene.MobileDirectionalLights); ChannelIdx++) { NumLights += (Scene.MobileDirectionalLights[ChannelIdx] ? 1 : 0); } // We can merge reflection and skylight pass with a sole directional light pass and if all primitives and the directional light use the default lighting channel bool bPrimitivesUseLightingChannels = (View.bUsesLightingChannels && GMobileIgnoreDeferredShadingSkyLightChannels == 0); bool bPlanarReflection = Scene.GetForwardPassGlobalPlanarReflection() != nullptr; bool bClusteredReflection = (View.NumBoxReflectionCaptures + View.NumSphereReflectionCaptures) > 0; bool bSSR = MobileSSRQuality != EMobileSSRQuality::Disabled; bool bInlineReflectionAndSky = (NumLights == 1) && !bPrimitivesUseLightingChannels && (Scene.MobileDirectionalLights[0] != nullptr) && !(bPlanarReflection || bClusteredReflection || bSSR); if (!bInlineReflectionAndSky) { RenderReflectionEnvironmentSkyLighting(RHICmdList, Scene, View, MobileSSRQuality, DynamicBentNormalAOTexture); } for (uint32 ChannelIdx = 0; ChannelIdx < UE_ARRAY_COUNT(Scene.MobileDirectionalLights); ChannelIdx++) { FLightSceneInfo* DirectionalLight = Scene.MobileDirectionalLights[ChannelIdx]; if (DirectionalLight) { RenderDirectionalLight(RHICmdList, Scene, View, DefaultLightMaterial, *DirectionalLight, ChannelIdx, bInlineReflectionAndSky, DynamicBentNormalAOTexture); } } return NumLights; } template static void SetLocalLightRasterizerAndDepthState(FGraphicsPipelineStateInitializer& GraphicsPSOInit, bool bReverseCulling, bool bCameraInsideLightGeometry) { if (GMobileUseLightStencilCulling != 0) { // Render backfaces with depth and stencil tests // and clear stencil to zero for next light mask GraphicsPSOInit.RasterizerState = bReverseCulling ? TStaticRasterizerState::GetRHI() : TStaticRasterizerState::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState< false, CF_LessEqual, false, CF_Equal, SO_Keep, SO_Keep, SO_Keep, true, CF_Equal, SO_Zero, SO_Keep, SO_Zero, GET_STENCIL_MOBILE_SM_MASK(0xff) | STENCIL_LIGHTING_CHANNELS_MASK(1u << LightingChannel) | STENCIL_SANDBOX_MASK, STENCIL_SANDBOX_MASK >::GetRHI(); } else { if (bCameraInsideLightGeometry) { // Render backfaces with depth tests disabled since the camera is inside (or close to inside) the light geometry GraphicsPSOInit.RasterizerState = bReverseCulling ? TStaticRasterizerState::GetRHI() : TStaticRasterizerState::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState< false, CF_Always, true, CF_Equal, SO_Keep, SO_Keep, SO_Keep, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, GET_STENCIL_MOBILE_SM_MASK(0xff) | STENCIL_LIGHTING_CHANNELS_MASK(1u << LightingChannel), 0x00>::GetRHI(); } else { // Render frontfaces with depth tests on to get the speedup from HiZ since the camera is outside the light geometry GraphicsPSOInit.RasterizerState = bReverseCulling ? TStaticRasterizerState::GetRHI() : TStaticRasterizerState::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState< false, CF_DepthNearOrEqual, true, CF_Equal, SO_Keep, SO_Keep, SO_Keep, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, GET_STENCIL_MOBILE_SM_MASK(0xff) | STENCIL_LIGHTING_CHANNELS_MASK(1u << LightingChannel), 0x00>::GetRHI(); } } } template static void SetLocalLightRasterizerAndDepthState(FGraphicsPipelineStateInitializer& GraphicsPSOInit, bool bReverseCulling, bool bCameraInsideLightGeometry, uint32 LightingChannel) { // TODO: support multi-channel ligths? switch (LightingChannel) { default: SetLocalLightRasterizerAndDepthState<0, bEnableShadingModelSupport>(GraphicsPSOInit, bReverseCulling, bCameraInsideLightGeometry); break; case 1: SetLocalLightRasterizerAndDepthState<1, bEnableShadingModelSupport>(GraphicsPSOInit, bReverseCulling, bCameraInsideLightGeometry); break; case 2: SetLocalLightRasterizerAndDepthState<2, bEnableShadingModelSupport>(GraphicsPSOInit, bReverseCulling, bCameraInsideLightGeometry); break; } } static void SetLocalLightRasterizerAndDepthState(FGraphicsPipelineStateInitializer& GraphicsPSOInit, bool bReverseCulling, bool bCameraInsideLightGeometry, uint32 LightingChannel, bool bEnableShadingModelSupport) { if (bEnableShadingModelSupport) { SetLocalLightRasterizerAndDepthState(GraphicsPSOInit, bReverseCulling, bCameraInsideLightGeometry, LightingChannel); } else { SetLocalLightRasterizerAndDepthState(GraphicsPSOInit, bReverseCulling, bCameraInsideLightGeometry, LightingChannel); } } static void SetLocalLightRasterizerAndDepthState(FGraphicsPipelineStateInitializer& GraphicsPSOInit, const FViewInfo& View, const FSphere& LightBounds, uint32 LightingChannel, bool bEnableShadingModelSupport) { const bool bCameraInsideLightGeometry = (GMobileUseLightStencilCulling == 0) && (((FVector)View.ViewMatrices.GetViewOrigin() - LightBounds.Center).SizeSquared() < FMath::Square(LightBounds.W * 1.05f + View.NearClippingDistance * 2.0f) // Always draw backfaces in ortho //@todo - accurate ortho camera / light intersection || !View.IsPerspectiveProjection()); if (bEnableShadingModelSupport) { SetLocalLightRasterizerAndDepthState(GraphicsPSOInit, View.bReverseCulling, bCameraInsideLightGeometry, LightingChannel); } else { SetLocalLightRasterizerAndDepthState(GraphicsPSOInit, View.bReverseCulling, bCameraInsideLightGeometry, LightingChannel); } } static void RenderLocalLight_StencilMask(FRHICommandList& RHICmdList, const FScene& Scene, const FViewInfo& View, const FLightSceneInfo& LightSceneInfo) { const uint8 LightType = LightSceneInfo.Proxy->GetLightType(); FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.PrimitiveType = PT_TriangleList; GraphicsPSOInit.BlendState = TStaticBlendStateWriteMask::GetRHI(); GraphicsPSOInit.RasterizerState = View.bReverseCulling ? TStaticRasterizerState::GetRHI() : TStaticRasterizerState::GetRHI(); // set stencil to 1 where depth test fails GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState< false, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Replace, SO_Keep, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 0x00, STENCIL_SANDBOX_MASK>::GetRHI(); FDeferredLightVS::FPermutationDomain PermutationVector; PermutationVector.Set(true); TShaderMapRef VertexShader(View.ShaderMap, PermutationVector); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4(); GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = nullptr; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 1); FDeferredLightVS::FParameters ParametersVS = FDeferredLightVS::GetParameters(View, &LightSceneInfo); SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), ParametersVS); if (LightType == LightType_Point || LightType == LightType_Rect) { StencilingGeometry::DrawSphere(RHICmdList); } else // LightType_Spot { StencilingGeometry::DrawCone(RHICmdList); } } static void RenderLocalLight( FRHICommandList& RHICmdList, const FScene& Scene, const FViewInfo& View, const FLightSceneInfo& LightSceneInfo, const FCachedLightMaterial& DefaultLightMaterial, const TArray& VisibleLightInfos) { uint8 LightingChannelMask = LightSceneInfo.Proxy->GetLightingChannelMask(); if (!LightSceneInfo.ShouldRenderLight(View) || LightingChannelMask == 0) { return; } const uint8 LightType = LightSceneInfo.Proxy->GetLightType(); const bool bIsSpotLight = LightType == LightType_Spot; const bool bIsPointLight = LightType == LightType_Point; const bool bIsRectLight = LightType == LightType_Rect; if (!bIsSpotLight && !bIsPointLight && !bIsRectLight) { return; } FString LightNameWithLevel; FSceneRenderer::GetLightNameForDrawEvent(LightSceneInfo.Proxy, LightNameWithLevel); SCOPED_DRAW_EVENTF(RHICmdList, LocalLight, TEXT("%s"), LightNameWithLevel); check(LightSceneInfo.Proxy->IsLocalLight()); if (GMobileUseLightStencilCulling != 0) { RenderLocalLight_StencilMask(RHICmdList, Scene, View, LightSceneInfo); } const bool bUseIESTexture = View.Family->EngineShowFlags.TexturedLightProfiles && LightSceneInfo.Proxy->GetIESTextureResource(); FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; const FSphere LightBounds = LightSceneInfo.Proxy->GetBoundingSphere(); const uint32 LightingChannel = GetLightingChannel(LightingChannelMask); const uint8 LightingChannelStencilValue = GetLightingChannelStencilValue(LightingChannel); FDeferredLightVS::FPermutationDomain PermutationVectorVS; PermutationVectorVS.Set(true); TShaderMapRef VertexShader(View.ShaderMap, PermutationVectorVS); FDeferredLightVS::FParameters ParametersVS = FDeferredLightVS::GetParameters(View, &LightSceneInfo); const FMaterialRenderProxy* LightFunctionMaterialProxy = nullptr; if (View.Family->EngineShowFlags.LightFunctions) { LightFunctionMaterialProxy = LightSceneInfo.Proxy->GetLightFunctionMaterial(); } FMobileRadialLightFunctionPS::FParameters PassParameters; const bool bShouldCastShadow = LightSceneInfo.SetupMobileMovableLocalLightShadowParameters(View, VisibleLightInfos, PassParameters.MobileMovableLocalLightShadow); PassParameters.Light = GetDeferredLightParameters(View, LightSceneInfo).LightParameters; const float TanOuterAngle = bIsSpotLight ? FMath::Tan(LightSceneInfo.Proxy->GetOuterConeAngle()) : 1.0f; PassParameters.MobileDeferredCommonParameters.LightFunctionParameters = FVector4f(TanOuterAngle, 1.0f /*ShadowFadeFraction*/, bIsSpotLight ? 1.0f : 0.0f, bIsPointLight ? 1.0f : 0.0f); PassParameters.MobileDeferredCommonParameters.LightFunctionParameters2 = FVector2f(LightSceneInfo.Proxy->GetLightFunctionFadeDistance(), LightSceneInfo.Proxy->GetLightFunctionDisabledBrightness()); const FVector Scale = LightSceneInfo.Proxy->GetLightFunctionScale(); // Switch x and z so that z of the user specified scale affects the distance along the light direction const FVector InverseScale = FVector(1.f / Scale.Z, 1.f / Scale.Y, 1.f / Scale.X); const FMatrix WorldToLight = LightSceneInfo.Proxy->GetWorldToLight() * FScaleMatrix(FVector(InverseScale)); PassParameters.MobileDeferredCommonParameters.TranslatedWorldToLight = FMatrix44f(FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation()) * WorldToLight); PassParameters.MobileDeferredCommonParameters.CameraRelativeLightPosition = GetCamRelativeLightPosition(View.ViewMatrices, LightSceneInfo); // Do two passes, first masking DefautLit, second masking all other shading models const bool bOnlyDefaultLitInView = IsOnlyDefaultLitShadingModel(View.ShadingModelMaskInView); int32 NumPasses = !bOnlyDefaultLitInView && MobileUsesGBufferCustomData(Scene.GetShaderPlatform()) ? 2 : 1; for (int32 PassIndex = 0; PassIndex < NumPasses; PassIndex++) { const bool bEnableShadingModelSupport = (PassIndex > 0); SetLocalLightRasterizerAndDepthState(GraphicsPSOInit, View, LightBounds, LightingChannel, bEnableShadingModelSupport); FMobileRadialLightFunctionPS::FPermutationDomain PermutationVector; PermutationVector.Set(bEnableShadingModelSupport); PermutationVector.Set(LightType); PermutationVector.Set(bUseIESTexture); PermutationVector.Set(bShouldCastShadow); FCachedLightMaterial LightMaterial; TShaderRef PixelShader; GetLightMaterial(DefaultLightMaterial, LightFunctionMaterialProxy, PermutationVector.ToDimensionValueId(), LightMaterial, PixelShader); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4(); GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); const uint8 StencilRef = PassShadingModelStencilValue(bEnableShadingModelSupport) | STENCIL_LIGHTING_CHANNELS_MASK(LightingChannelStencilValue); SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef); SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), ParametersVS); SetShaderParametersMixedPS(RHICmdList, PixelShader, PassParameters, View, LightMaterial.MaterialProxy, *LightMaterial.Material); if (LightType == LightType_Point || LightType == LightType_Rect) { StencilingGeometry::DrawSphere(RHICmdList); } else // LightType_Spot { StencilingGeometry::DrawCone(RHICmdList); } } } static void RenderSimpleLights( FRHICommandList& RHICmdList, const FScene& Scene, int32 ViewIndex, int32 NumViews, const FViewInfo& View, const FSortedLightSetSceneInfo &SortedLightSet, const FCachedLightMaterial& DefaultMaterial) { const FSimpleLightArray& SimpleLights = SortedLightSet.SimpleLights; if (SimpleLights.InstanceData.Num() == 0) { return; } SCOPED_DRAW_EVENT(RHICmdList, SimpleLights); FDeferredLightVS::FPermutationDomain PermutationVectorVS; PermutationVectorVS.Set(true); TShaderMapRef VertexShader(View.ShaderMap, PermutationVectorVS); FGraphicsPipelineStateInitializer GraphicsPSOLight; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOLight); // Use additive blending for color GraphicsPSOLight.BlendState = TStaticBlendState::GetRHI(); GraphicsPSOLight.PrimitiveType = PT_TriangleList; GraphicsPSOLight.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4(); GraphicsPSOLight.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOLight.RasterizerState = View.bReverseCulling ? TStaticRasterizerState::GetRHI() : TStaticRasterizerState::GetRHI(); GraphicsPSOLight.DepthStencilState = TStaticDepthStencilState< false, CF_DepthNearOrEqual, true, CF_NotEqual, SO_Keep, SO_Keep, SO_Keep, // Render where ShadingModel Mask is not zero false, CF_Always, SO_Keep, SO_Keep, SO_Keep, GET_STENCIL_MOBILE_SM_MASK(0xff), 0x00>::GetRHI(); TShaderRef PixelShader; FMobileRadialLightFunctionPS::FPermutationDomain PermutationVector; PermutationVector.Set(true); { FCachedLightMaterial LightMaterial; GetLightMaterial(DefaultMaterial, nullptr, PermutationVector.ToDimensionValueId(), LightMaterial, PixelShader); } GraphicsPSOLight.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); const uint8 StencilRef = 0; SetGraphicsPipelineState(RHICmdList, GraphicsPSOLight, StencilRef); if (NumViews > 1) { // set viewports only we we have more than one // otherwise it is set at the start of the pass RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f); } for (int32 LightIndex = 0; LightIndex < SimpleLights.InstanceData.Num(); LightIndex++) { const FSimpleLightEntry& SimpleLight = SimpleLights.InstanceData[LightIndex]; const FSimpleLightPerViewEntry& SimpleLightPerViewData = SimpleLights.GetViewDependentData(LightIndex, ViewIndex, NumViews); const FSphere LightBounds(SimpleLightPerViewData.Position, SimpleLight.Radius); FDeferredLightVS::FParameters ParametersVS = FDeferredLightVS::GetParameters(View, LightBounds); // Render light FMobileRadialLightFunctionPS::FParameters ParametersPS; ParametersPS.Light = GetSimpleDeferredLightParameters(View, SimpleLight, SimpleLightPerViewData).LightParameters; SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), ParametersVS); SetShaderParametersMixedPS(RHICmdList, PixelShader, ParametersPS, View, DefaultMaterial.MaterialProxy, *DefaultMaterial.Material); // Apply the point or spot light with some approximately bounding geometry, // So we can get speedups from depth testing and not processing pixels outside of the light's influence. StencilingGeometry::DrawSphere(RHICmdList); } } void MobileDeferredShadingPass( FRHICommandList& RHICmdList, int32 ViewIndex, int32 NumViews, const FViewInfo& View, const FScene& Scene, const FSortedLightSetSceneInfo& SortedLightSet, const TArray& VisibleLightInfos, EMobileSSRQuality MobileSSRQuality, FRDGTextureRef DynamicBentNormalAOTexture) { RHI_BREADCRUMB_EVENT_STAT(RHICmdList, DeferredShading, "DeferredShading"); SCOPED_GPU_STAT(RHICmdList, DeferredShading); RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f); // Default material for light rendering FCachedLightMaterial DefaultMaterial; DefaultMaterial.MaterialProxy = UMaterial::GetDefaultMaterial(MD_LightFunction)->GetRenderProxy(); DefaultMaterial.Material = DefaultMaterial.MaterialProxy->GetMaterialNoFallback(ERHIFeatureLevel::ES3_1); check(DefaultMaterial.Material); int NumDirLights = RenderDirectionalLights(RHICmdList, Scene, View, DefaultMaterial, MobileSSRQuality, DynamicBentNormalAOTexture); const bool bMobileUseClusteredDeferredShading = UseClusteredDeferredShading(View.GetShaderPlatform()) && NumDirLights > 0; if (!bMobileUseClusteredDeferredShading) { // Render non-clustered simple lights RenderSimpleLights(RHICmdList, Scene, ViewIndex, NumViews, View, SortedLightSet, DefaultMaterial); } // Render non-clustered local lights int32 NumLights = SortedLightSet.SortedLights.Num(); const int32 UnbatchedLightStart = SortedLightSet.UnbatchedLightStart; int32 StandardDeferredStart = SortedLightSet.SimpleLightsEnd; if (bMobileUseClusteredDeferredShading) { StandardDeferredStart = SortedLightSet.ClusteredSupportedEnd; } // Draw non-shadowed non-light function lights for (int32 LightIdx = StandardDeferredStart; LightIdx < UnbatchedLightStart; ++LightIdx) { const FSortedLightSceneInfo& SortedLight = SortedLightSet.SortedLights[LightIdx]; const FLightSceneInfo& LightSceneInfo = *SortedLight.LightSceneInfo; RenderLocalLight(RHICmdList, Scene, View, LightSceneInfo, DefaultMaterial, VisibleLightInfos); } // Draw shadowed and light function lights for (int32 LightIdx = UnbatchedLightStart; LightIdx < NumLights; ++LightIdx) { const FSortedLightSceneInfo& SortedLight = SortedLightSet.SortedLights[LightIdx]; const FLightSceneInfo& LightSceneInfo = *SortedLight.LightSceneInfo; RenderLocalLight(RHICmdList, Scene, View, LightSceneInfo, DefaultMaterial, VisibleLightInfos); } } class FDeferredMobileLightMaterialPSOCollector : public IPSOCollector { public: FDeferredMobileLightMaterialPSOCollector(ERHIFeatureLevel::Type InFeatureLevel) : IPSOCollector(FPSOCollectorCreateManager::GetIndex(GetFeatureLevelShadingPath(InFeatureLevel), DeferredMobileLightMaterialPSOCollectorName)) { } virtual void CollectPSOInitializers( const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray& PSOInitializers ) override final; private: void CollectPSOInitializersDirectional( const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, TArray& PSOInitializers); void CollectPSOInitializersLocal( const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, TArray& PSOInitializers); }; void FDeferredMobileLightMaterialPSOCollector::CollectPSOInitializers( const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray& PSOInitializers) { if (Material.GetMaterialDomain() == MD_LightFunction) { CollectPSOInitializersDirectional(SceneTexturesConfig, Material, PSOInitializers); CollectPSOInitializersLocal(SceneTexturesConfig, Material, PSOInitializers); } } void FDeferredMobileLightMaterialPSOCollector::CollectPSOInitializersDirectional( const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, TArray& PSOInitializers) { FMaterialShaderTypes ShaderTypesToGetAnyPermutation; ShaderTypesToGetAnyPermutation.AddShaderType(); FMaterialShaders ShadersAnyPermutation; if (!Material.TryGetShaders(ShaderTypesToGetAnyPermutation, nullptr, ShadersAnyPermutation)) { return; } auto AddPSOInitializer = [&](int32 PassIndex, bool bInlineReflectionAndSky, bool bOnlyDefaultLitInView, int32 ShadowQuality, bool bHasBoxSphere) { const uint32 CustomPassIndex = 1; const int PassEnableShadingModelSupport = (!bOnlyDefaultLitInView && MobileUsesGBufferCustomData(GMaxRHIShaderPlatform)) ? (1 << CustomPassIndex) : 0; const bool bDynamicSkyLight = true; const bool bShadingModelSupport = PassEnableShadingModelSupport & (1 << PassIndex); const bool bUseClusteredLights = UseClusteredDeferredShading(GMaxRHIShaderPlatform); const bool bClustredReflection = bInlineReflectionAndSky && bHasBoxSphere; const bool bPlanarReflection = false; const bool bEnableSkyLight = bInlineReflectionAndSky && bDynamicSkyLight; const bool bMobileUsesShadowMaskTexture = MobileUsesShadowMaskTexture(GMaxRHIShaderPlatform); const bool bApplySkyShadowing = false; const int32 LightingChannel = 0; FMobileDirectionalLightFunctionPS::FPermutationDomain PermutationVector; PermutationVector.Set(bShadingModelSupport); PermutationVector.Set(bUseClusteredLights); PermutationVector.Set(bEnableSkyLight); PermutationVector.Set(ShadowQuality > 0); PermutationVector.Set(FMath::Clamp(ShadowQuality, 1, 3)); PermutationVector.Set(bInlineReflectionAndSky && bApplySkyShadowing); FMaterialShaderTypes ShaderTypesToGet; ShaderTypesToGet.AddShaderType(PermutationVector.ToDimensionValueId()); FMaterialShaders Shaders; if (!Material.TryGetShaders(ShaderTypesToGet, nullptr, Shaders)) { return; } TShaderRef PixelShader; Shaders.TryGetPixelShader(PixelShader); if (!PixelShader.IsValid()) { return; } const bool bRequired = false; FRHIPixelShader* RHIPixelShader = PixelShader.GetPixelShader(bRequired); if (RHIPixelShader == nullptr) { return; } TShaderMapRef VertexShader(GetGlobalShaderMap(GMaxRHIShaderPlatform)); FRHIVertexShader* RHIVertexShader = VertexShader.GetVertexShader(bRequired); if (RHIVertexShader == nullptr) { return; } FGraphicsPipelineStateInitializer GraphicsPSOInit; GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); // Add to emissive in SceneColor if (bInlineReflectionAndSky && !bDynamicSkyLight) { // pre-multiply SceneColor with AO GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } else { GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } SetDirectionalLightDepthStencilState(GraphicsPSOInit, LightingChannel); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = RHIVertexShader; GraphicsPSOInit.BoundShaderState.PixelShaderRHI = RHIPixelShader; GraphicsPSOInit.PrimitiveType = PT_TriangleList; FGraphicsPipelineRenderTargetsInfo RenderTargetsInfo; RenderTargetsInfo.NumSamples = 1; if (MobileAllowFramebufferFetch(GMaxRHIShaderPlatform)) { SetupGBufferRenderTargetInfo(SceneTexturesConfig, RenderTargetsInfo, false /*bSetupDepthStencil*/); } else { AddRenderTargetInfo(SceneTexturesConfig.ColorFormat, SceneTexturesConfig.ColorCreateFlags, RenderTargetsInfo); } SetupDepthStencilInfo(PF_DepthStencil, SceneTexturesConfig.DepthCreateFlags, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite, RenderTargetsInfo); GraphicsPSOInit.StatePrecachePSOHash = RHIComputeStatePrecachePSOHash(GraphicsPSOInit); ApplyTargetsInfo(GraphicsPSOInit, RenderTargetsInfo); GraphicsPSOInit.SubpassIndex = 0; GraphicsPSOInit.SubpassHint = ESubpassHint::None; if (MobileAllowFramebufferFetch(GMaxRHIShaderPlatform)) { GraphicsPSOInit.SubpassIndex = 2; GraphicsPSOInit.SubpassHint = ESubpassHint::DeferredShadingSubpass; } FPSOPrecacheData PSOPrecacheData; PSOPrecacheData.bRequired = true; PSOPrecacheData.Type = FPSOPrecacheData::EType::Graphics; PSOPrecacheData.GraphicsPSOInitializer = GraphicsPSOInit; #if PSO_PRECACHING_VALIDATE PSOPrecacheData.PSOCollectorIndex = PSOCollectorIndex; PSOPrecacheData.VertexFactoryType = nullptr; #endif // PSO_PRECACHING_VALIDATE PSOInitializers.Add(MoveTemp(PSOPrecacheData)); }; // int32 PassIndex, bool bInlineReflectionAndSky, bool bOnlyDefaultLitInView, int32 ShadowQuality, bool bHasBoxSphere AddPSOInitializer(0, true, false, 3, true); AddPSOInitializer(1, true, false, 3, true); AddPSOInitializer(0, false, true, 0, false); AddPSOInitializer(0, true, false, 0, false); AddPSOInitializer(1, true, false, 0, false); AddPSOInitializer(0, false, false, 0, false); AddPSOInitializer(1, false, false, 0, false); AddPSOInitializer(0, true, true, 0, false); } void FDeferredMobileLightMaterialPSOCollector::CollectPSOInitializersLocal( const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, TArray& PSOInitializers) { FMaterialShaderTypes ShaderTypesToGetAnyPermutation; ShaderTypesToGetAnyPermutation.AddShaderType(); FMaterialShaders ShadersAnyPermutation; if (!Material.TryGetShaders(ShaderTypesToGetAnyPermutation, nullptr, ShadersAnyPermutation)) { return; } auto AddPSOInitializer = [&](int32 PassIndex, uint8 LightType, bool bUseIESTexture, bool bCameraInsideLightGeometry) { const bool bEnableShadingModelSupport = (PassIndex > 0); const bool bShouldCastShadow = false; const int32 LightingChannel = 0; const bool bReverseCulling = false; FMobileRadialLightFunctionPS::FPermutationDomain PermutationVector; PermutationVector.Set(bEnableShadingModelSupport); PermutationVector.Set(LightType); PermutationVector.Set(bUseIESTexture); PermutationVector.Set(bShouldCastShadow); FMaterialShaderTypes ShaderTypesToGet; ShaderTypesToGet.AddShaderType(PermutationVector.ToDimensionValueId()); FMaterialShaders Shaders; if (!Material.TryGetShaders(ShaderTypesToGet, nullptr, Shaders)) { return; } TShaderRef PixelShader; Shaders.TryGetPixelShader(PixelShader); if (!PixelShader.IsValid()) { return; } const bool bRequired = false; FRHIPixelShader* RHIPixelShader = PixelShader.GetPixelShader(bRequired); if (RHIPixelShader == nullptr) { return; } FDeferredLightVS::FPermutationDomain PermutationVectorVS; PermutationVectorVS.Set(true); TShaderMapRef VertexShader(GetGlobalShaderMap(GMaxRHIShaderPlatform), PermutationVectorVS); FRHIVertexShader* RHIVertexShader = VertexShader.GetVertexShader(bRequired); if (RHIVertexShader == nullptr) { return; } FGraphicsPipelineStateInitializer GraphicsPSOInit; GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); SetLocalLightRasterizerAndDepthState(GraphicsPSOInit, bReverseCulling, bCameraInsideLightGeometry, LightingChannel, bEnableShadingModelSupport); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = RHIVertexShader; GraphicsPSOInit.BoundShaderState.PixelShaderRHI = RHIPixelShader; GraphicsPSOInit.PrimitiveType = PT_TriangleList; FGraphicsPipelineRenderTargetsInfo RenderTargetsInfo; RenderTargetsInfo.NumSamples = 1; if (MobileAllowFramebufferFetch(GMaxRHIShaderPlatform)) { SetupGBufferRenderTargetInfo(SceneTexturesConfig, RenderTargetsInfo, false /*bSetupDepthStencil*/); } else { AddRenderTargetInfo(SceneTexturesConfig.ColorFormat, SceneTexturesConfig.ColorCreateFlags, RenderTargetsInfo); } SetupDepthStencilInfo(PF_DepthStencil, SceneTexturesConfig.DepthCreateFlags, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite, RenderTargetsInfo); GraphicsPSOInit.StatePrecachePSOHash = RHIComputeStatePrecachePSOHash(GraphicsPSOInit); ApplyTargetsInfo(GraphicsPSOInit, RenderTargetsInfo); GraphicsPSOInit.SubpassIndex = 0; GraphicsPSOInit.SubpassHint = ESubpassHint::None; if (MobileAllowFramebufferFetch(GMaxRHIShaderPlatform)) { GraphicsPSOInit.SubpassIndex = 2; GraphicsPSOInit.SubpassHint = ESubpassHint::DeferredShadingSubpass; } FPSOPrecacheData PSOPrecacheData; PSOPrecacheData.bRequired = true; PSOPrecacheData.Type = FPSOPrecacheData::EType::Graphics; PSOPrecacheData.GraphicsPSOInitializer = GraphicsPSOInit; #if PSO_PRECACHING_VALIDATE PSOPrecacheData.PSOCollectorIndex = PSOCollectorIndex; PSOPrecacheData.VertexFactoryType = nullptr; #endif // PSO_PRECACHING_VALIDATE PSOInitializers.Add(MoveTemp(PSOPrecacheData)); }; // int32 PassIndex, uint8 LightType, bool bUseIESTexture, bool bCameraInsideLightGeometry AddPSOInitializer(0, LIGHT_TYPE_POINT, false, false); AddPSOInitializer(1, LIGHT_TYPE_POINT, false, false); AddPSOInitializer(0, LIGHT_TYPE_POINT, true, false); AddPSOInitializer(1, LIGHT_TYPE_POINT, true, false); AddPSOInitializer(0, LIGHT_TYPE_POINT, true, true); AddPSOInitializer(1, LIGHT_TYPE_POINT, true, true); AddPSOInitializer(0, LIGHT_TYPE_SPOT, false, false); AddPSOInitializer(1, LIGHT_TYPE_SPOT, false, false); } IPSOCollector* CreateDeferredMobileLightMaterialPSOCollector(ERHIFeatureLevel::Type FeatureLevel) { return new FDeferredMobileLightMaterialPSOCollector(FeatureLevel); } FRegisterPSOCollectorCreateFunction RegisterDeferredMobileLightMaterialPSOCollector(&CreateDeferredMobileLightMaterialPSOCollector, EShadingPath::Mobile, DeferredMobileLightMaterialPSOCollectorName);