// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= MobileShadingRenderer.cpp: Scene rendering code for ES3/3.1 feature level. =============================================================================*/ #include "CoreMinimal.h" #include "Stats/Stats.h" #include "Misc/MemStack.h" #include "HAL/IConsoleManager.h" #include "EngineGlobals.h" #include "RHIDefinitions.h" #include "RHI.h" #include "RenderResource.h" #include "RendererInterface.h" #include "SceneUtils.h" #include "UniformBuffer.h" #include "Engine/BlendableInterface.h" #include "ShaderParameters.h" #include "RHIStaticStates.h" #include "Shader.h" #include "StaticBoundShaderState.h" #include "PostProcess/SceneRenderTargets.h" #include "GlobalShader.h" #include "SceneRendering.h" #include "ScenePrivate.h" #include "SceneProxies/SkyLightSceneProxy.h" #include "PostProcess/SceneFilterRendering.h" #include "FXSystem.h" #include "PostProcess/PostProcessing.h" #include "PostProcess/PostProcessMobile.h" #include "PostProcess/PostProcessUpscale.h" #include "PostProcess/PostProcessCompositeEditorPrimitives.h" #include "PostProcess/PostProcessHMD.h" #include "PostProcess/PostProcessAmbientOcclusionMobile.h" #include "PostProcess/PostProcessCombineLUTs.h" #include "PostProcess/PostProcessTonemap.h" #include "PostProcess/AlphaInvert.h" #include "IHeadMountedDisplay.h" #include "IXRTrackingSystem.h" #include "SceneViewExtension.h" #include "ScreenRendering.h" #include "ShaderPrint.h" #include "PipelineStateCache.h" #include "ClearQuad.h" #include "MobileSeparateTranslucencyPass.h" #include "MobileDistortionPass.h" #include "VisualizeTexturePresent.h" #include "RendererModule.h" #include "EngineModule.h" #include "GPUScene.h" #include "Materials/MaterialRenderProxy.h" #include "MaterialSceneTextureId.h" #include "SkyAtmosphereRendering.h" #include "VisualizeTexture.h" #include "VT/VirtualTextureFeedbackResource.h" #include "VT/VirtualTextureSystem.h" #include "GPUSortManager.h" #include "MobileBasePassRendering.h" #include "MobileDeferredShadingPass.h" #include "PlanarReflectionSceneProxy.h" #include "InstanceCulling/InstanceCullingManager.h" #include "InstanceCulling/InstanceCullingOcclusionQuery.h" #include "SceneOcclusion.h" #include "VariableRateShadingImageManager.h" #include "SceneTextureReductions.h" #include "GPUMessaging.h" #include "Substrate/Substrate.h" #include "RenderCore.h" #include "RectLightTextureManager.h" #include "IESTextureManager.h" #include "SceneUniformBuffer.h" #include "Engine/SpecularProfile.h" #include "LocalFogVolumeRendering.h" #include "SceneCaptureRendering.h" #include "WaterInfoTextureRendering.h" #include "Rendering/CustomRenderPass.h" #include "GenerateMips.h" #include "MobileSSR.h" #include "ViewData.h" #include "DistanceFieldAmbientOcclusion.h" #include "DistanceFieldLightingShared.h" uint32 GetShadowQuality(); static TAutoConsoleVariable CVarMobileForceDepthResolve( TEXT("r.Mobile.ForceDepthResolve"), 0, TEXT("0: Depth buffer is resolved by switching out render targets. (Default)\n") TEXT("1: Depth buffer is resolved by switching out render targets and drawing with the depth texture.\n"), ECVF_Scalability | ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarMobileAdrenoOcclusionMode( TEXT("r.Mobile.AdrenoOcclusionMode"), 0, TEXT("0: Render occlusion queries after the base pass (default).\n") TEXT("1: Render occlusion queries after translucency and a flush, which can help Adreno devices in GL mode."), ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarMobileCustomDepthForTranslucency( TEXT("r.Mobile.CustomDepthForTranslucency"), 1, TEXT(" Whether to render custom depth/stencil if any tranclucency in the scene uses it. \n") TEXT(" 0 = Off \n") TEXT(" 1 = On [default]"), ECVF_Scalability | ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarMobileXRMSAAMode( TEXT("r.Mobile.XRMSAAMode"), 0, TEXT(" Whether to modify how mobile XR msaa support works\n") TEXT(" 0 = Standard depth pass/swapchain mode [default]\n") TEXT(" 1 = Perform a copy of depth to the depth resolve target") TEXT(" 2 = Make the depth swap chain be MSAA and use it directly as scene depth"), ECVF_ReadOnly | ECVF_RenderThreadSafe); TAutoConsoleVariable GAdrenoOcclusionUseFDM( TEXT("r.AdrenoOcclusionUseFDM"), 0, TEXT("Use FDM with adreno occlusion mode"), ECVF_RenderThreadSafe); DECLARE_GPU_STAT_NAMED(MobileSceneRender, TEXT("Mobile Scene Render")); extern bool IsMobileEyeAdaptationEnabled(const FViewInfo& View); struct FMobileCustomDepthStencilUsage { bool bUsesCustomDepthStencil = false; // whether CustomStencil is sampled as a textures bool bSamplesCustomStencil = false; }; static FMobileCustomDepthStencilUsage GetCustomDepthStencilUsage(const FViewInfo& View) { FMobileCustomDepthStencilUsage CustomDepthStencilUsage; // Find out whether there are primitives will render in custom depth pass or just always render custom depth if ((View.bHasCustomDepthPrimitives || GetCustomDepthMode() == ECustomDepthMode::EnabledWithStencil)) { // Find out whether CustomDepth/Stencil used in translucent materials if (CVarMobileCustomDepthForTranslucency.GetValueOnAnyThread() != 0) { CustomDepthStencilUsage.bUsesCustomDepthStencil = View.bUsesCustomDepth || View.bUsesCustomStencil; CustomDepthStencilUsage.bSamplesCustomStencil = View.bUsesCustomStencil; } if (!CustomDepthStencilUsage.bSamplesCustomStencil) { // Find out whether post-process materials use CustomDepth/Stencil lookups const FBlendableManager& BlendableManager = View.FinalPostProcessSettings.BlendableManager; FBlendableEntry* BlendableIt = nullptr; while (FPostProcessMaterialNode* DataPtr = BlendableManager.IterateBlendables(BlendableIt)) { if (DataPtr->IsValid()) { FMaterialRenderProxy* Proxy = DataPtr->GetMaterialInterface()->GetRenderProxy(); check(Proxy); const FMaterial& Material = Proxy->GetIncompleteMaterialWithFallback(View.GetFeatureLevel()); const FMaterialShaderMap* MaterialShaderMap = Material.GetRenderingThreadShaderMap(); bool bUsesCustomDepth = MaterialShaderMap->UsesSceneTexture(PPI_CustomDepth); bool bUsesCustomStencil = MaterialShaderMap->UsesSceneTexture(PPI_CustomStencil); if (Material.IsStencilTestEnabled() || bUsesCustomDepth || bUsesCustomStencil) { CustomDepthStencilUsage.bUsesCustomDepthStencil |= true; } if (bUsesCustomStencil) { CustomDepthStencilUsage.bSamplesCustomStencil |= true; break; } } } } } return CustomDepthStencilUsage; } static void RenderOpaqueFX( FRDGBuilder& GraphBuilder, TConstStridedView Views, FSceneUniformBuffer &SceneUniformBuffer, FFXSystemInterface* FXSystem, TRDGUniformBufferRef MobileSceneTexturesUniformBuffer) { // Notify the FX system that opaque primitives have been rendered and we now have a valid depth buffer. if (FXSystem && Views.Num() > 0) { FXSystem->PostRenderOpaque(GraphBuilder, Views, SceneUniformBuffer, true /*bAllowGPUParticleUpdate*/); if (FGPUSortManager* GPUSortManager = FXSystem->GetGPUSortManager()) { GPUSortManager->OnPostRenderOpaque(GraphBuilder); } } } static void BuildMeshRenderingCommands(FRDGBuilder& GraphBuilder, EMeshPass::Type MeshPass, FViewInfo& View, const FGPUScene& GPUScene, FInstanceCullingManager& InstanceCullingManager, FInstanceCullingDrawParams& OutParams) { if (auto* Pass = View.ParallelMeshDrawCommandPasses[MeshPass]) { Pass->BuildRenderingCommands(GraphBuilder, GPUScene, OutParams); // When batching is disabled instead of a single UniformBuffer we get a separate buffers for each mesh pass // Because mobile renderer manually merges several mesh passes into a single RDG pass we can't specify InstanceCullingDrawParams for each mesh pass through RDG pass parameters (only one) // We do these dummy RDG passes to make sure InstanceCullingDrawParams are initialized for each mesh pass if (!FInstanceCullingManager::AllowBatchedBuildRenderingCommands(GPUScene)) { GraphBuilder.AddPass( RDG_EVENT_NAME("SetupInstanceCullingDrawParams"), &OutParams, ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass | ERDGPassFlags::NeverCull, [](FRHICommandList&){}); } } else { InstanceCullingManager.SetDummyCullingParams(GraphBuilder, OutParams); } } BEGIN_SHADER_PARAMETER_STRUCT(FMobileRenderPassParameters,) SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View) SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FMobileBasePassUniformParameters, MobileBasePass) SHADER_PARAMETER_STRUCT_REF(FMobileReflectionCaptureShaderData, ReflectionCapture) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, LocalFogVolumeInstances) RDG_BUFFER_ACCESS(LocalFogVolumeTileDrawIndirectBuffer, ERHIAccess::IndirectArgs) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2DArray, LocalFogVolumeTileDataTexture) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, LocalFogVolumeTileDataBuffer) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, HalfResLocalFogVolumeViewSRV) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, HalfResLocalFogVolumeDepthSRV) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, BentNormalAOTexture) RDG_TEXTURE_ACCESS(ColorGradingLUT, ERHIAccess::SRVGraphics) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() static bool PostProcessUsesSceneDepth(const FViewInfo& View) { if ((View.FinalPostProcessSettings.DepthOfFieldScale > 0.0f && View.Family->EngineShowFlags.DepthOfField) || View.MobileLightShaft.IsSet()) { return true; } // Find out whether post-process materials use CustomDepth/Stencil lookups const FBlendableManager& BlendableManager = View.FinalPostProcessSettings.BlendableManager; FBlendableEntry* BlendableIt = nullptr; while (FPostProcessMaterialNode* DataPtr = BlendableManager.IterateBlendables(BlendableIt)) { if (DataPtr->IsValid()) { FMaterialRenderProxy* Proxy = DataPtr->GetMaterialInterface()->GetRenderProxy(); check(Proxy); const FMaterial& Material = Proxy->GetIncompleteMaterialWithFallback(View.GetFeatureLevel()); const FMaterialShaderMap* MaterialShaderMap = Material.GetRenderingThreadShaderMap(); if (MaterialShaderMap->UsesSceneTexture(PPI_SceneDepth)) { return true; } } } return IsMobileDistortionActive(View) || View.bIsSceneCapture; } struct FRenderViewContext { FViewInfo* ViewInfo; int32 ViewIndex; bool bIsFirstView; bool bIsLastView; }; using FRenderViewContextArray = TArray>; static void GetRenderViews(TArrayView InViews, FRenderViewContextArray& RenderViews) { for (int32 ViewIndex = 0; ViewIndex < InViews.Num(); ViewIndex++) { FViewInfo& View = InViews[ViewIndex]; if (View.ShouldRenderView()) { FRenderViewContext RenderView; RenderView.ViewInfo = &View; RenderView.ViewIndex = ViewIndex; RenderView.bIsFirstView = (RenderViews.Num() == 0); RenderView.bIsLastView = false; RenderViews.Add(RenderView); } } if (RenderViews.Num()) { RenderViews.Last().bIsLastView = true; } } FMobileSceneRenderer::FMobileSceneRenderer(const FSceneViewFamily* InViewFamily, FHitProxyConsumer* HitProxyConsumer) : FSceneRenderer(InViewFamily, HitProxyConsumer) , bGammaSpace(!IsMobileHDR()) , bDeferredShading(IsMobileDeferredShadingEnabled(ShaderPlatform)) , bRequiresDBufferDecals(IsUsingDBuffers(ShaderPlatform)) , bUseVirtualTexturing(UseVirtualTexturing(ShaderPlatform) && GetRendererOutput() != FSceneRenderer::ERendererOutput::DepthPrepassOnly) , bSupportsSimpleLights(bDeferredShading || MobileForwardEnableParticleLights(ShaderPlatform)) { bRenderToSceneColor = false; bRequiresMultiPass = false; bKeepDepthContent = false; bModulatedShadowsInUse = false; bShouldRenderCustomDepth = false; bRequiresAmbientOcclusionPass = false; bRequiresShadowProjections = false; bEnableDistanceFieldAO = false; bIsFullDepthPrepassEnabled = (Scene->EarlyZPassMode == DDM_AllOpaque || Scene->EarlyZPassMode == DDM_AllOpaqueNoVelocity); bIsMaskedOnlyDepthPrepassEnabled = Scene->EarlyZPassMode == DDM_MaskedOnly; bEnableClusteredLocalLights = MobileForwardEnableLocalLights(ShaderPlatform); bEnableClusteredReflections = MobileForwardEnableClusteredReflections(ShaderPlatform); bRequiresScreenSpaceReflections = AreMobileScreenSpaceReflectionsEnabled(ShaderPlatform); StandardTranslucencyPass = ViewFamily.AllowTranslucencyAfterDOF() ? ETranslucencyPass::TPT_TranslucencyStandard : ETranslucencyPass::TPT_AllTranslucency; StandardTranslucencyMeshPass = TranslucencyPassToMeshPass(StandardTranslucencyPass); // Don't do occlusion queries when doing scene captures for (FViewInfo& View : Views) { if (View.bIsSceneCapture) { View.bDisableQuerySubmissions = true; View.bIgnoreExistingQueries = true; } } NumMSAASamples = GetDefaultMSAACount(ERHIFeatureLevel::ES3_1); // As of UE 5.4 only vulkan supports inline (single pass) tonemap bTonemapSubpass = IsMobileTonemapSubpassEnabled(ShaderPlatform, ViewFamily.bRequireMultiView) && ViewFamily.bResolveScene && GetRendererOutput() != FSceneRenderer::ERendererOutput::DepthPrepassOnly; bTonemapSubpassInline = IsMobileTonemapSubpassEnabledInline(ShaderPlatform, ViewFamily.bRequireMultiView, NumMSAASamples) && bTonemapSubpass; bRequiresSceneDepthAux = MobileRequiresSceneDepthAux(ShaderPlatform) && !bTonemapSubpass; // Initialize scene renderer extensions here, after the rest of the renderer has been initialized InitSceneExtensionsRenderers(ViewFamily.EngineShowFlags, true); } class FMobileDirLightShaderParamsRenderResource : public FRenderResource { public: using MobileDirLightUniformBufferRef = TUniformBufferRef; virtual void InitRHI(FRHICommandListBase& RHICmdList) override { UniformBufferRHI = MobileDirLightUniformBufferRef::CreateUniformBufferImmediate( FMobileDirectionalLightShaderParameters(), UniformBuffer_MultiFrame); } virtual void ReleaseRHI() override { UniformBufferRHI.SafeRelease(); } MobileDirLightUniformBufferRef UniformBufferRHI; }; TUniformBufferRef& GetNullMobileDirectionalLightShaderParameters() { static TGlobalResource* NullLightParams; if (!NullLightParams) { NullLightParams = new TGlobalResource(); } check(!!NullLightParams->UniformBufferRHI); return NullLightParams->UniformBufferRHI; } void FMobileSceneRenderer::PrepareViewVisibilityLists() { // Prepare view's visibility lists. // TODO: only do this when CSM + static is required. for (auto& View : Views) { FMobileCSMVisibilityInfo& MobileCSMVisibilityInfo = View.MobileCSMVisibilityInfo; // Init list of primitives that can receive Dynamic CSM. MobileCSMVisibilityInfo.MobilePrimitiveCSMReceiverVisibilityMap.Init(false, View.PrimitiveVisibilityMap.Num()); // Init static mesh visibility info for CSM drawlist MobileCSMVisibilityInfo.MobileCSMStaticMeshVisibilityMap.Init(false, View.StaticMeshVisibilityMap.Num()); // Init static mesh visibility info for default drawlist that excludes meshes in CSM only drawlist. MobileCSMVisibilityInfo.MobileNonCSMStaticMeshVisibilityMap = View.StaticMeshVisibilityMap; } } void FMobileSceneRenderer::SetupMobileBasePassAfterShadowInit(FExclusiveDepthStencil::Type BasePassDepthStencilAccess, TArrayView ViewCommandsPerView, FInstanceCullingManager& InstanceCullingManager) { // Sort front to back on all platforms, even HSR benefits from it //const bool bWantsFrontToBackSorting = (GHardwareHiddenSurfaceRemoval == false); // compute keys for front to back sorting and dispatch pass setup. for (int32 ViewIndex = 0; ViewIndex < AllViews.Num(); ++ViewIndex) { FViewInfo& View = *AllViews[ViewIndex]; FViewCommands& ViewCommands = ViewCommandsPerView[ViewIndex]; FMeshPassProcessor* MeshPassProcessor = FPassProcessorManager::CreateMeshPassProcessor(EShadingPath::Mobile, EMeshPass::BasePass, Scene->GetFeatureLevel(), Scene, &View, nullptr); FMeshPassProcessor* BasePassCSMMeshPassProcessor = FPassProcessorManager::CreateMeshPassProcessor(EShadingPath::Mobile, EMeshPass::MobileBasePassCSM, Scene->GetFeatureLevel(), Scene, &View, nullptr); TArray > ViewIds; ViewIds.Add(View.SceneRendererPrimaryViewId); // Only apply instancing for ISR to main view passes EInstanceCullingMode InstanceCullingMode = View.IsInstancedStereoPass() ? EInstanceCullingMode::Stereo : EInstanceCullingMode::Normal; if (InstanceCullingMode == EInstanceCullingMode::Stereo) { check(View.GetInstancedView() != nullptr); ViewIds.Add(View.GetInstancedView()->SceneRendererPrimaryViewId); } // Run sorting on BasePass, as it's ignored inside FSceneRenderer::SetupMeshPass, so it can be done after shadow init on mobile. FParallelMeshDrawCommandPass& Pass = *View.CreateMeshPass(EMeshPass::BasePass); if (ShouldDumpMeshDrawCommandInstancingStats()) { Pass.SetDumpInstancingStats(GetMeshPassName(EMeshPass::BasePass)); } Pass.DispatchPassSetup( Scene, View, FInstanceCullingContext(GetMeshPassName(EMeshPass::BasePass), ShaderPlatform, &InstanceCullingManager, ViewIds, View.PrevViewInfo.HZB, InstanceCullingMode), EMeshPass::BasePass, BasePassDepthStencilAccess, MeshPassProcessor, View.DynamicMeshElements, &View.DynamicMeshElementsPassRelevance, View.NumVisibleDynamicMeshElements[EMeshPass::BasePass], ViewCommands.DynamicMeshCommandBuildRequests[EMeshPass::BasePass], ViewCommands.DynamicMeshCommandBuildFlags[EMeshPass::BasePass], ViewCommands.NumDynamicMeshCommandBuildRequestElements[EMeshPass::BasePass], ViewCommands.MeshCommands[EMeshPass::BasePass], BasePassCSMMeshPassProcessor, &ViewCommands.MeshCommands[EMeshPass::MobileBasePassCSM]); } } /** * Initialize scene's views. * Check visibility, sort translucent items, etc. */ void FMobileSceneRenderer::InitViews( FRDGBuilder& GraphBuilder, FSceneTexturesConfig& SceneTexturesConfig, FInstanceCullingManager& InstanceCullingManager, FVirtualTextureUpdater* VirtualTextureUpdater, FInitViewTaskDatas& TaskDatas) { FRHICommandListImmediate& RHICmdList = GraphBuilder.RHICmdList; SCOPED_DRAW_EVENT(RHICmdList, InitViews); SCOPE_CYCLE_COUNTER(STAT_InitViewsTime); RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, InitViews_Scene); check(Scene); const bool bRendererOutputFinalSceneColor = (GetRendererOutput() != ERendererOutput::DepthPrepassOnly); PreVisibilityFrameSetup(GraphBuilder); if (InstanceCullingManager.IsEnabled() && Scene->InstanceCullingOcclusionQueryRenderer && Scene->InstanceCullingOcclusionQueryRenderer->InstanceOcclusionQueryBuffer) { InstanceCullingManager.InstanceOcclusionQueryBuffer = GraphBuilder.RegisterExternalBuffer(Scene->InstanceCullingOcclusionQueryRenderer->InstanceOcclusionQueryBuffer); InstanceCullingManager.InstanceOcclusionQueryBufferFormat = Scene->InstanceCullingOcclusionQueryRenderer->InstanceOcclusionQueryBufferFormat; } for (FViewInfo& ViewInfo : Views) { uint32 InstanceFactor = ViewInfo.bIsInstancedStereoEnabled && IStereoRendering::IsStereoEyeView(ViewInfo) && GEngine->StereoRenderingDevice.IsValid() ? GEngine->StereoRenderingDevice->GetDesiredNumberOfViews(true) : 1; ViewInfo.InstanceFactor = InstanceFactor > 0 ? InstanceFactor : 1; } FILCUpdatePrimTaskData* ILCTaskData = nullptr; const FExclusiveDepthStencil::Type BasePassDepthStencilAccess = FExclusiveDepthStencil::DepthWrite_StencilWrite; if (FXSystem && FXSystem->RequiresEarlyViewUniformBuffer() && Views.IsValidIndex(0) && bRendererOutputFinalSceneColor) { // This is to init the ViewUniformBuffer before rendering for the Niagara compute shader. // This needs to run before ComputeViewVisibility() is called, but the views normally initialize the ViewUniformBuffer after that (at the end of this method). // during ISR, instanced view RHI resources need to be initialized first. if (FViewInfo* InstancedView = const_cast(Views[0].GetInstancedView())) { InstancedView->InitRHIResources(); } Views[0].InitRHIResources(); FXSystem->PostInitViews(GraphBuilder, GetSceneViews(), !ViewFamily.EngineShowFlags.HitProxies); } TaskDatas.VisibilityTaskData->ProcessRenderThreadTasks(); TaskDatas.VisibilityTaskData->FinishGatherDynamicMeshElements(BasePassDepthStencilAccess, InstanceCullingManager, VirtualTextureUpdater); if (ShouldRenderVolumetricFog() && bRendererOutputFinalSceneColor) { SetupVolumetricFog(); } PostVisibilityFrameSetup(ILCTaskData); FIntPoint RenderTargetSize = ViewFamily.RenderTarget->GetSizeXY(); EPixelFormat RenderTargetPixelFormat = PF_Unknown; if (ViewFamily.RenderTarget->GetRenderTargetTexture().IsValid()) { RenderTargetSize = ViewFamily.RenderTarget->GetRenderTargetTexture()->GetSizeXY(); RenderTargetPixelFormat = ViewFamily.RenderTarget->GetRenderTargetTexture()->GetFormat(); } // Upscaling is not supported in Mobile LDR since we don't have a post-processing pass. // The only time we support FamilySize != RenderTargetSize in Mobile LDR is when using Dynamic Resolution + OpenXR, where we render to the upper-left // corner of the render target and upscale in the OpenXR compositor. Enabled when xr.MobileLDRDynamicResolution = 1. const bool bIsMobileLDR = ViewFamily.GetFeatureLevel() <= ERHIFeatureLevel::ES3_1 && !IsMobileHDR(); const bool bRequiresUpscale = ((int32)RenderTargetSize.X > FamilySize.X || (int32)RenderTargetSize.Y > FamilySize.Y) && !bIsMobileLDR; // ES requires that the back buffer and depth match dimensions. // For the most part this is not the case when using scene captures. Thus scene captures always render to scene color target. const bool bShouldCompositeEditorPrimitives = FSceneRenderer::ShouldCompositeEditorPrimitives(Views[0]); const bool bStereoRenderingAndHMD = ViewFamily.EngineShowFlags.StereoRendering && ViewFamily.EngineShowFlags.HMDDistortion; bRenderToSceneColor = !bGammaSpace || bStereoRenderingAndHMD || bRequiresUpscale || bShouldCompositeEditorPrimitives || Views[0].bIsSceneCapture || Views[0].bIsReflectionCapture // If the resolve texture is not the same as the MSAA texture, we need to render to scene color and copy to back buffer. || (NumMSAASamples > 1 && !RHISupportsSeparateMSAAAndResolveTextures(ShaderPlatform)) || (NumMSAASamples > 1 && (RenderTargetPixelFormat != PF_Unknown && RenderTargetPixelFormat != SceneTexturesConfig.ColorFormat)) || bIsFullDepthPrepassEnabled; bool bSceneDepthCapture = ( ViewFamily.SceneCaptureSource == SCS_SceneColorSceneDepth || ViewFamily.SceneCaptureSource == SCS_SceneDepth || ViewFamily.SceneCaptureSource == SCS_DeviceDepth); // Check if any of the custom render passes outputs depth texture, used to decide whether to enable bPreciseDepthAux. for (FViewInfo* View : AllViews) { if (View->CustomRenderPass) { ESceneCaptureSource CaptureSource = View->CustomRenderPass->GetSceneCaptureSource(); if (CaptureSource == SCS_SceneColorSceneDepth || CaptureSource == SCS_SceneDepth || CaptureSource == SCS_DeviceDepth) { bSceneDepthCapture = true; break; } } } const FPlanarReflectionSceneProxy* PlanarReflectionSceneProxy = Scene ? Scene->GetForwardPassGlobalPlanarReflection() : nullptr; bRequiresAmbientOcclusionPass = IsUsingMobileAmbientOcclusion(ShaderPlatform) && Views[0].FinalPostProcessSettings.AmbientOcclusionIntensity > 0 && (Views[0].FinalPostProcessSettings.AmbientOcclusionStaticFraction >= 1 / 100.0f || (Scene && Scene->SkyLight && Scene->SkyLight->ProcessedTexture && Views[0].Family->EngineShowFlags.SkyLighting)) && ViewFamily.EngineShowFlags.Lighting && !Views[0].bIsReflectionCapture && !Views[0].bIsPlanarReflection && !ViewFamily.EngineShowFlags.HitProxies && !ViewFamily.EngineShowFlags.VisualizeLightCulling && !ViewFamily.UseDebugViewPS() && bRendererOutputFinalSceneColor; bShouldRenderVelocities = ShouldRenderVelocities(); bRequiresShadowProjections = MobileUsesShadowMaskTexture(ShaderPlatform) && ViewFamily.EngineShowFlags.Lighting && !Views[0].bIsReflectionCapture && !Views[0].bIsPlanarReflection && !ViewFamily.EngineShowFlags.HitProxies && !ViewFamily.EngineShowFlags.VisualizeLightCulling && !ViewFamily.UseDebugViewPS() && bRendererOutputFinalSceneColor; bShouldRenderHZB = ShouldRenderHZB(Views) && bRendererOutputFinalSceneColor; // Wait for visibilityTaskData to finish as IsMobileSeparateTranslucencyActive depends on results from SetupMeshPasses. TaskDatas.VisibilityTaskData->Finish(); bool bUsingOcclusionFeedback = Views[0].ViewState && Views[0].ViewState->OcclusionFeedback.IsInitialized(); bAdrenoOcclusionMode = (DoOcclusionQueries() && !bUsingOcclusionFeedback && !Views[0].bDisableQuerySubmissions && CVarMobileAdrenoOcclusionMode.GetValueOnAnyThread() != 0); // Whether we need to store depth for post-processing // On PowerVR we see flickering of shadows and depths not updating correctly if targets are discarded. const bool bForceDepthResolve = (CVarMobileForceDepthResolve.GetValueOnRenderThread() == 1); const bool bSeparateTranslucencyActive = IsMobileSeparateTranslucencyActive(Views.GetData(), Views.Num()); const bool bPostProcessUsesSceneDepth = PostProcessUsesSceneDepth(Views[0]); const bool bRequireSeparateViewPass = Views.Num() > 1 && !Views[0].bIsMobileMultiViewEnabled; bRequiresMultiPass = RequiresMultiPass(NumMSAASamples, ShaderPlatform); bKeepDepthContent = bRequiresMultiPass || bForceDepthResolve || bSeparateTranslucencyActive || Views[0].bIsReflectionCapture || (bDeferredShading && bPostProcessUsesSceneDepth) || (bDeferredShading && bSceneDepthCapture) || Views[0].AntiAliasingMethod == AAM_TemporalAA || bRequireSeparateViewPass || bIsFullDepthPrepassEnabled || bShouldRenderHZB || (bAdrenoOcclusionMode && IsVulkanPlatform(ShaderPlatform)) || GraphBuilder.IsDumpingFrame(); // never keep MSAA depth if SceneDepthAux is enabled bKeepDepthContent = ((NumMSAASamples > 1) && bRequiresSceneDepthAux) ? false : bKeepDepthContent; extern int32 GDistanceFieldAOApplyToStaticIndirect; bEnableDistanceFieldAO = Scene->SkyLight && Scene->SkyLight->bCastShadows && ShouldRenderDeferredDynamicSkyLight(Scene, ViewFamily) && AnyViewHasGIMethodSupportingDFAO() && (Views[0].GlobalDistanceFieldInfo.Clipmaps.Num() > 0) && !GDistanceFieldAOApplyToStaticIndirect && ShouldRenderDistanceFieldAO(Views, ViewFamily.EngineShowFlags) && ShouldRenderDistanceFieldLighting(Scene->DistanceFieldSceneData, Views) && ViewFamily.EngineShowFlags.AmbientOcclusion && !Views[0].bIsReflectionCapture; // Depth is needed for Editor Primitives if (bShouldCompositeEditorPrimitives) { bKeepDepthContent = true; } // In the editor RHIs may split a render-pass into several cmd buffer submissions, so all targets need to Store if (IsSimulatedPlatform(ShaderPlatform)) { bKeepDepthContent = true; } // Update the bKeepDepthContent based on the mobile renderer status. SceneTexturesConfig.bKeepDepthContent = bKeepDepthContent; // If we render in a single pass MSAA targets can be memoryless SceneTexturesConfig.bMemorylessMSAA = !(bRequiresMultiPass || bShouldCompositeEditorPrimitives || bRequireSeparateViewPass); SceneTexturesConfig.NumSamples = NumMSAASamples; SceneTexturesConfig.ExtraSceneColorCreateFlags |= (bTonemapSubpassInline ? TexCreate_InputAttachmentRead : TexCreate_None); SceneTexturesConfig.BuildSceneColorAndDepthFlags(); if (bDeferredShading) { SceneTexturesConfig.SetupMobileGBufferFlags(bRequiresMultiPass || GraphBuilder.IsDumpingFrame() || bRequireSeparateViewPass); } SceneTexturesConfig.bRequiresDepthAux = bRequiresSceneDepthAux; // When we capturing scene depth, use a more precise format for SceneDepthAux as it will be used as a source DepthTexture if (bSceneDepthCapture) { SceneTexturesConfig.bPreciseDepthAux = true; } // Find out whether custom depth pass should be rendered. { bool bCouldUseCustomDepthStencil = (!Scene->World || (Scene->World->WorldType != EWorldType::Inactive)); FMobileCustomDepthStencilUsage CustomDepthStencilUsage; for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { CustomDepthStencilUsage = GetCustomDepthStencilUsage(Views[ViewIndex]); Views[ViewIndex].bCustomDepthStencilValid = bCouldUseCustomDepthStencil && CustomDepthStencilUsage.bUsesCustomDepthStencil; bShouldRenderCustomDepth |= Views[ViewIndex].bCustomDepthStencilValid; SceneTexturesConfig.bSamplesCustomStencil |= bShouldRenderCustomDepth && CustomDepthStencilUsage.bSamplesCustomStencil; } } // Finalize and set the scene textures config. FSceneTexturesConfig::Set(SceneTexturesConfig); bool bShouldRenderSkyAtmosphere = false; if (bRendererOutputFinalSceneColor) { // This must happen before we start initialising and using views (allocating Scene->SkyIrradianceEnvironmentMap). UpdateSkyIrradianceGpuBuffer(GraphBuilder, ViewFamily.EngineShowFlags, Scene->SkyLight, Scene->SkyIrradianceEnvironmentMap); // Initialise Sky/View resources before the view global uniform buffer is built. bShouldRenderSkyAtmosphere = ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags); if (bShouldRenderSkyAtmosphere) { InitSkyAtmosphereForViews(RHICmdList, GraphBuilder); } if (bRequiresShadowProjections) { FViewInfo* MainView = Views.Num() > 0 ? &Views[0] : nullptr; bool bIsMobileMultiView = SceneTexturesConfig.bRequireMultiView || (MainView && MainView->Aspects.IsMobileMultiViewEnabled()); InitMobileShadowProjectionOutputs(RHICmdList, SceneTexturesConfig.Extent, bIsMobileMultiView); } else { ReleaseMobileShadowProjectionOutputs(); } } FRDGExternalAccessQueue ExternalAccessQueue; // initialize per-view uniform buffer. Pass in shadow info as necessary. for (int32 ViewIndex = Views.Num() - 1; ViewIndex >= 0; --ViewIndex) { FViewInfo& View = Views[ViewIndex]; View.UpdatePreExposure(); // Initialize the view's RHI resources. View.InitRHIResources(); } for (int32 i = 0; i < CustomRenderPassInfos.Num(); ++i) { for (FViewInfo& View : CustomRenderPassInfos[i].Views) { View.InitRHIResources(); } } if (bRendererOutputFinalSceneColor) { const bool bDynamicShadows = ViewFamily.EngineShowFlags.DynamicShadows; if (bDynamicShadows) { // Setup dynamic shadows. TaskDatas.DynamicShadows = InitDynamicShadows(GraphBuilder, InstanceCullingManager); } else { // TODO: only do this when CSM + static is required. PrepareViewVisibilityLists(); } } if (bRendererOutputFinalSceneColor) { SetupMobileBasePassAfterShadowInit(BasePassDepthStencilAccess, TaskDatas.VisibilityTaskData->GetViewCommandsPerView(), InstanceCullingManager); // if we kicked off ILC update via task, wait and finalize. if (ILCTaskData) { Scene->IndirectLightingCache.FinalizeCacheUpdates(Scene, *this, *ILCTaskData); } } ExternalAccessQueue.Submit(GraphBuilder); extern TSet PersistentViewUniformBufferExtensions; for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions) { Extension->BeginFrame(); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { // Must happen before RHI thread flush so any tasks we dispatch here can land in the idle gap during the flush Extension->PrepareView(&Views[ViewIndex]); } } if (bRendererOutputFinalSceneColor) { if (bDeferredShading || bEnableClusteredLocalLights || bEnableClusteredReflections) { SetupSceneReflectionCaptureBuffer(RHICmdList); } UpdateSkyReflectionUniformBuffer(RHICmdList); // Now that the indirect lighting cache is updated, we can update the uniform buffers. UpdatePrimitiveIndirectLightingCacheBuffers(RHICmdList); UpdateDirectionalLightUniformBuffers(GraphBuilder, Views[0]); } } static void BeginOcclusionScope(FRDGBuilder& GraphBuilder, TArray& Views) { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { FViewInfo& View = Views[ViewIndex]; if (View.ShouldRenderView() && View.ViewState && View.ViewState->OcclusionFeedback.IsInitialized()) { View.ViewState->OcclusionFeedback.BeginOcclusionScope(GraphBuilder); } } } static void EndOcclusionScope(FRDGBuilder& GraphBuilder, TArray& Views) { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { FViewInfo& View = Views[ViewIndex]; if (View.ShouldRenderView() && View.ViewState && View.ViewState->OcclusionFeedback.IsInitialized()) { View.ViewState->OcclusionFeedback.EndOcclusionScope(GraphBuilder); } } } /* * Renders the Full Depth Prepass */ void FMobileSceneRenderer::RenderFullDepthPrepass(FRDGBuilder& GraphBuilder, TArrayView InViews, FSceneTextures& SceneTextures, FInstanceCullingManager& InstanceCullingManager, bool bIsSceneCaptureRenderPass) { FRenderViewContextArray RenderViews; GetRenderViews(InViews, RenderViews); TRDGUniformBufferBinding LastViewMobileBasePassUB; for (FRenderViewContext& ViewContext : RenderViews) { FViewInfo& View = *ViewContext.ViewInfo; View.BeginRenderView(); const bool bRenderVelocityInDepthPrePass = (Scene->EarlyZPassMode == DDM_AllOpaqueNoVelocity); struct FFullDepthPrepassParameterCollection { FMobileRenderPassParameters PassParameters; FInstanceCullingDrawParams VelocityInstanceCullingDrawParams; }; auto ParameterCollection = GraphBuilder.AllocParameters(); auto PassParameters = &ParameterCollection->PassParameters; if (bShouldRenderVelocities) { PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneTextures.Velocity, ERenderTargetLoadAction::EClear); } PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::EClear, FExclusiveDepthStencil::DepthWrite_StencilWrite); PassParameters->View = View.GetShaderParameters(); PassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::DepthPrePass, EMobileSceneTextureSetupMode::None); //if the scenecolor isn't multiview but the app is, need to render as a single-view multiview due to shaders PassParameters->RenderTargets.MultiViewCount = (View.bIsMobileMultiViewEnabled) ? 2 : (View.Aspects.IsMobileMultiViewEnabled() ? 1 : 0); if (ViewContext.bIsLastView) { LastViewMobileBasePassUB = PassParameters->MobileBasePass; } if (!ViewContext.bIsFirstView) { PassParameters->RenderTargets.DepthStencil.SetDepthLoadAction(ERenderTargetLoadAction::ELoad); PassParameters->RenderTargets.DepthStencil.SetStencilLoadAction(ERenderTargetLoadAction::ELoad); PassParameters->RenderTargets.DepthStencil.SetDepthStencilAccess(FExclusiveDepthStencil::DepthWrite_StencilWrite); if (bShouldRenderVelocities) { PassParameters->RenderTargets[0].SetLoadAction(ERenderTargetLoadAction::ELoad); } } BuildMeshRenderingCommands(GraphBuilder, EMeshPass::DepthPass, View, Scene->GPUScene, InstanceCullingManager, PassParameters->InstanceCullingDrawParams); FInstanceCullingDrawParams* VelocityInstanceCullingDrawParams = nullptr; if (bRenderVelocityInDepthPrePass) { VelocityInstanceCullingDrawParams = View.ParallelMeshDrawCommandPasses[EMeshPass::DepthPass] ? &ParameterCollection->VelocityInstanceCullingDrawParams : &PassParameters->InstanceCullingDrawParams; BuildMeshRenderingCommands(GraphBuilder, EMeshPass::Velocity, View, Scene->GPUScene, InstanceCullingManager, *VelocityInstanceCullingDrawParams); } GraphBuilder.AddPass( RDG_EVENT_NAME("FullDepthPrepass"), PassParameters, ERDGPassFlags::Raster, [this, ParameterCollection, &View, VelocityInstanceCullingDrawParams](FRHICommandList& RHICmdList) { auto PassParameters = &ParameterCollection->PassParameters; RenderPrePass(RHICmdList, View, &PassParameters->InstanceCullingDrawParams); if (VelocityInstanceCullingDrawParams) { RenderVelocityPass(RHICmdList, View, VelocityInstanceCullingDrawParams); } }); } for (FRenderViewContext& ViewContext : RenderViews) { FViewInfo& View = *ViewContext.ViewInfo; // Render occlusion at the last view pass only, as they already loop through all views // If this is scene capture render pass, don't render occlusion. bool bDoOcclusionQueries = (ViewContext.bIsLastView && DoOcclusionQueries() && !bIsSceneCaptureRenderPass); if (bDoOcclusionQueries) { auto* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilRead); PassParameters->View = View.GetShaderParameters(); PassParameters->MobileBasePass = LastViewMobileBasePassUB; PassParameters->RenderTargets.NumOcclusionQueries = ComputeNumOcclusionQueriesToBatch(); GraphBuilder.AddPass( RDG_EVENT_NAME("RenderOcclusion"), PassParameters, ERDGPassFlags::Raster | ERDGPassFlags::NeverCull, [this](FRHICommandList& RHICmdList) { RenderOcclusion(RHICmdList); }); } } if (DoOcclusionQueries() && !bIsSceneCaptureRenderPass) { EndOcclusionScope(GraphBuilder, Views); FenceOcclusionTests(GraphBuilder); } } void FMobileSceneRenderer::RenderMaskedPrePass(FRHICommandList& RHICmdList, const FViewInfo& View, const FInstanceCullingDrawParams* DepthPassInstanceCullingDrawParams) { if (bIsMaskedOnlyDepthPrepassEnabled) { RenderPrePass(RHICmdList, View, DepthPassInstanceCullingDrawParams); } } void FMobileSceneRenderer::RenderCustomRenderPassBasePass(FRDGBuilder& GraphBuilder, TArrayView InViews, FRDGTextureRef ViewFamilyTexture, FSceneTextures& SceneTextures, FInstanceCullingManager& InstanceCullingManager, bool bIncludeTranslucent) { FRenderTargetBindingSlots BasePassRenderTargets; // Use the same subpass hints as main render, to avoid generating new PSOs int32 NumAdditionalSubpasses = 0; if (bDeferredShading) { FColorTargets ColorTargets = GetColorTargets_Deferred(SceneTextures); BasePassRenderTargets = InitRenderTargetBindings_Deferred(SceneTextures, ColorTargets); BasePassRenderTargets.SubpassHint = ESubpassHint::DeferredShadingSubpass; NumAdditionalSubpasses = 2; } else { BasePassRenderTargets = InitRenderTargetBindings_Forward(ViewFamilyTexture, SceneTextures); BasePassRenderTargets.SubpassHint = ESubpassHint::DepthReadSubpass; NumAdditionalSubpasses = 1; } FRenderViewContextArray RenderViews; GetRenderViews(InViews, RenderViews); for (FRenderViewContext& ViewContext : RenderViews) { FViewInfo& View = *ViewContext.ViewInfo; struct FCustomPassParameterCollection { FMobileRenderPassParameters PassParameters; FInstanceCullingDrawParams SkyPassInstanceCullingDrawParams; }; auto ParameterCollection = GraphBuilder.AllocParameters(); EMobileSceneTextureSetupMode SetupMode = bIsFullDepthPrepassEnabled ? EMobileSceneTextureSetupMode::SceneDepth : EMobileSceneTextureSetupMode::None; auto PassParameters = &ParameterCollection->PassParameters; PassParameters->View = View.GetShaderParameters(); PassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::Opaque, SetupMode); PassParameters->RenderTargets = BasePassRenderTargets; FMobileRenderPassParameters* TranslucencyPassParameters = nullptr; if (bIncludeTranslucent) { TranslucencyPassParameters = GraphBuilder.AllocParameters(); TranslucencyPassParameters->View = View.GetShaderParameters(); TranslucencyPassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::Translucent, SetupMode); TranslucencyPassParameters->RenderTargets[0] = BasePassRenderTargets[0]; TranslucencyPassParameters->RenderTargets[0].SetLoadAction(ERenderTargetLoadAction::ELoad); TranslucencyPassParameters->RenderTargets.DepthStencil = BasePassRenderTargets.DepthStencil; TranslucencyPassParameters->RenderTargets.DepthStencil.SetDepthLoadAction(ERenderTargetLoadAction::ELoad); } if (Scene->GPUScene.IsEnabled()) { BuildMeshRenderingCommands(GraphBuilder, EMeshPass::BasePass, View, Scene->GPUScene, InstanceCullingManager, PassParameters->InstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, EMeshPass::SkyPass, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->SkyPassInstanceCullingDrawParams); if (bIncludeTranslucent) { BuildMeshRenderingCommands(GraphBuilder, EMeshPass::TranslucencyAll, View, Scene->GPUScene, InstanceCullingManager, TranslucencyPassParameters->InstanceCullingDrawParams); } } GraphBuilder.AddPass( RDG_EVENT_NAME("RenderMobileBasePass"), PassParameters, ERDGPassFlags::Raster, [this, ParameterCollection, ViewContext, NumAdditionalSubpasses](FRHICommandList& RHICmdList) { FViewInfo& View = *ViewContext.ViewInfo; auto PassParameters = &ParameterCollection->PassParameters; RenderMobileBasePass(RHICmdList, View, &PassParameters->InstanceCullingDrawParams, &ParameterCollection->SkyPassInstanceCullingDrawParams); // TODO: Should this render decals? Deferred shading custom render passes do. for (int32 AddSubpass = 0; AddSubpass < NumAdditionalSubpasses; ++AddSubpass) { RHICmdList.NextSubpass(); } }); if (bIncludeTranslucent) { GraphBuilder.AddPass( RDG_EVENT_NAME("RenderMobileTranslucentPass"), TranslucencyPassParameters, ERDGPassFlags::Raster, [this, TranslucencyPassParameters, ViewContext, InViews, &SceneTextures](FRHICommandList& RHICmdList) { // Custom render passes run all translucency in a single pass FViewInfo& View = *ViewContext.ViewInfo; RenderTranslucency(RHICmdList, View, InViews, ETranslucencyPass::TPT_AllTranslucency, EMeshPass::TranslucencyAll, &TranslucencyPassParameters->InstanceCullingDrawParams); }); } if (!bIsFullDepthPrepassEnabled) { AddResolveSceneDepthPass(GraphBuilder, View, SceneTextures.Depth); } if (bRequiresSceneDepthAux) { AddResolveSceneColorPass(GraphBuilder, View, SceneTextures.DepthAux); } } } void FMobileSceneRenderer::Render(FRDGBuilder& GraphBuilder, const FSceneRenderUpdateInputs* SceneUpdateInputs) { if (!ViewFamily.EngineShowFlags.Rendering) { return; } const ERendererOutput RendererOutput = GetRendererOutput(); const bool bRendererOutputFinalSceneColor = (RendererOutput != ERendererOutput::DepthPrepassOnly); RDG_RHI_EVENT_SCOPE_STAT(GraphBuilder, MobileSceneRender, MobileSceneRender); RDG_RHI_GPU_STAT_SCOPE(GraphBuilder, MobileSceneRender); IVisibilityTaskData* VisibilityTaskData = OnRenderBegin(GraphBuilder, SceneUpdateInputs); FRDGExternalAccessQueue ExternalAccessQueue; if (SceneUpdateInputs) { static auto CVarDistanceFieldShadowQuality = IConsoleManager::Get().FindConsoleVariable(TEXT("r.DFShadowQuality")); if (IsMobileDistanceFieldEnabled(ShaderPlatform) && CVarDistanceFieldShadowQuality != nullptr && CVarDistanceFieldShadowQuality->GetInt() > 0 && bRendererOutputFinalSceneColor) { for (FViewFamilyInfo* Family : SceneUpdateInputs->ViewFamilies) { const FEngineShowFlags& EngineShowFlags = Family->EngineShowFlags; const FSceneView& View = *Family->Views[0]; if (EngineShowFlags.Lighting && !EngineShowFlags.VisualizeLightCulling && !Family->UseDebugViewPS() && !View.bIsReflectionCapture && !View.bIsPlanarReflection) { PrepareDistanceFieldScene(GraphBuilder, ExternalAccessQueue, *SceneUpdateInputs); break; } } } } ExternalAccessQueue.Submit(GraphBuilder); GPU_MESSAGE_SCOPE(GraphBuilder); // Establish scene primitive count (must be done after UpdateAllPrimitiveSceneInfos) FGPUSceneScopeBeginEndHelper GPUSceneScopeBeginEndHelper(GraphBuilder, Scene->GPUScene, GPUSceneDynamicContext); if (bRendererOutputFinalSceneColor) { if (ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags)) { for (int32 LightIndex = 0; LightIndex < NUM_ATMOSPHERE_LIGHTS; ++LightIndex) { if (Scene->AtmosphereLights[LightIndex]) { PrepareSunLightProxy(*Scene->GetSkyAtmosphereSceneInfo(), LightIndex, *Scene->AtmosphereLights[LightIndex]); } } } else { Scene->ResetAtmosphereLightsProperties(); } } CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderOther); QUICK_SCOPE_CYCLE_COUNTER(STAT_FMobileSceneRenderer_Render); FSceneTexturesConfig& SceneTexturesConfig = GetActiveSceneTexturesConfig(); // Initialize global system textures (pass-through if already initialized). GSystemTextures.InitializeTextures(GraphBuilder.RHICmdList, FeatureLevel); FRDGSystemTextures::Create(GraphBuilder); ShaderPrint::BeginViews(GraphBuilder, Views); ON_SCOPE_EXIT { ShaderPrint::EndViews(Views); }; TUniquePtr VirtualTextureUpdater; if (bUseVirtualTexturing) { FVirtualTextureUpdateSettings Settings; Settings.EnableThrottling(!ViewFamily.bOverrideVirtualTextureThrottle); VirtualTextureUpdater = FVirtualTextureSystem::Get().BeginUpdate(GraphBuilder, FeatureLevel, this, Settings); VirtualTextureFeedbackBegin(GraphBuilder, Views, SceneTexturesConfig.Extent); } // Substrate initialization is always run even when not enabled. if (Substrate::IsSubstrateEnabled()) { for (FViewInfo& View : Views) { ShadingEnergyConservation::Init(GraphBuilder, View); FGlintShadingLUTsStateData::Init(GraphBuilder, View); } } Substrate::InitialiseSubstrateFrameSceneData(GraphBuilder, *this); if (bRendererOutputFinalSceneColor) { // Force the subsurface profile & specular profile textures to be updated. SubsurfaceProfile::UpdateSubsurfaceProfileTexture(GraphBuilder, ShaderPlatform); SpecularProfile::UpdateSpecularProfileTextureAtlas(GraphBuilder, ShaderPlatform); if (bDeferredShading) { RectLightAtlas::UpdateAtlasTexture(GraphBuilder, FeatureLevel); } IESAtlas::UpdateAtlasTexture(GraphBuilder, ShaderPlatform); // Important that this uses consistent logic throughout the frame, so evaluate once and pass in the flag from here // NOTE: Must be done after system texture initialization VirtualShadowMapArray.Initialize(GraphBuilder, Scene->GetVirtualShadowMapCache(), UseVirtualShadowMaps(ShaderPlatform, FeatureLevel), ViewFamily.EngineShowFlags); } GetSceneExtensionsRenderers().PreInitViews(GraphBuilder); FInitViewTaskDatas InitViewTaskDatas(VisibilityTaskData); FRendererViewDataManager& ViewDataManager = *GraphBuilder.AllocObject(GraphBuilder, *Scene, GetSceneUniforms(), AllViews); FInstanceCullingManager& InstanceCullingManager = *GraphBuilder.AllocObject(GraphBuilder, *Scene, GetSceneUniforms(), ViewDataManager); // Find the visible primitives and prepare targets and buffers for rendering InitViews(GraphBuilder, SceneTexturesConfig, InstanceCullingManager, VirtualTextureUpdater.Get(), InitViewTaskDatas); if (bRendererOutputFinalSceneColor && DoOcclusionQueries()) { BeginOcclusionScope(GraphBuilder, Views); } // Notify the FX system that the scene is about to be rendered. // TODO: These should probably be moved to scene extensions if (FXSystem) { FXSystem->PreRender(GraphBuilder, GetSceneViews(), GetSceneUniforms(), true /*bAllowGPUParticleUpdate*/); if (FGPUSortManager* GPUSortManager = FXSystem->GetGPUSortManager()) { // if GPUSortManager::OnPostRenderOpaque is called below (from RenderOpaqueFX) we must also call OnPreRender (as it sets up // the internal state of the GPUSortManager). Any optimization to skip this block needs to take that into consideration. GPUSortManager->OnPreRender(GraphBuilder); } } { RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, UpdateGPUScene); for (int32 ViewIndex = 0; ViewIndex < AllViews.Num(); ViewIndex++) { FViewInfo& View = *AllViews[ViewIndex]; Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, View); Scene->GPUScene.DebugRender(GraphBuilder, GetSceneUniforms(), View); } } GetSceneExtensionsRenderers().UpdateViewData(GraphBuilder, ViewDataManager); // Allow scene extensions to affect the scene uniform buffer GetSceneExtensionsRenderers().UpdateSceneUniformBuffer(GraphBuilder, GetSceneUniforms()); InstanceCullingManager.BeginDeferredCulling(GraphBuilder); GetSceneExtensionsRenderers().PreRender(GraphBuilder); GEngine->GetPreRenderDelegateEx().Broadcast(GraphBuilder); FSceneTextures::InitializeViewFamily(GraphBuilder, ViewFamily, FamilySize); FSceneTextures& SceneTextures = GetActiveSceneTextures(); FSortedLightSetSceneInfo& SortedLightSet = *GraphBuilder.AllocObject(); SceneTextures.MobileSetupMode = EMobileSceneTextureSetupMode::None; SceneTextures.MobileUniformBuffer = CreateMobileSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, SceneTextures.MobileSetupMode); // We must have a full depth buffer in order to render half res and upsample const bool bUseHalfResLocalFogVolume = bIsFullDepthPrepassEnabled && IsLocalFogVolumeHalfResolution(); if (bRendererOutputFinalSceneColor) { #if WITH_DEBUG_VIEW_MODES if (ViewFamily.UseDebugViewPS() && ViewFamily.EngineShowFlags.ShaderComplexity && SceneTextures.QuadOverdraw) { AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(SceneTextures.QuadOverdraw), FUintVector4(0, 0, 0, 0)); } #endif if (bUseVirtualTexturing) { FVirtualTextureSystem::Get().EndUpdate(GraphBuilder, MoveTemp(VirtualTextureUpdater), FeatureLevel); FVirtualTextureSystem::Get().FinalizeRequests(GraphBuilder, this); } if (bDeferredShading || bEnableClusteredLocalLights || bEnableClusteredReflections) { RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, SortLights); // Shadows are applied in clustered shading on mobile forward and separately on mobile deferred. bool bShadowedLightsInClustered = bRequiresShadowProjections && !bDeferredShading; // This task needs to run before any other functions gathering lights for upload on GPU, for light function indices to be assigned to lights. UpdateLightFunctionAtlasTaskFunction(); GatherAndSortLights(SortedLightSet, bShadowedLightsInClustered); const int32 NumReflectionCaptures = Views[0].NumBoxReflectionCaptures + Views[0].NumSphereReflectionCaptures; const bool bCullLightsToGrid = (((bEnableClusteredReflections || bDeferredShading) && NumReflectionCaptures > 0) || bEnableClusteredLocalLights); PrepareForwardLightData(GraphBuilder, bCullLightsToGrid, SortedLightSet); LightFunctionAtlas.RenderLightFunctionAtlas(GraphBuilder, Views); } else { SetDummyForwardLightUniformBufferOnViews(GraphBuilder, ShaderPlatform, Views); } // Generate the Sky/Atmosphere look up tables const bool bShouldRenderSkyAtmosphere = ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags); if (bShouldRenderSkyAtmosphere) { FSkyAtmospherePendingRDGResources PendingRDGResources; RenderSkyAtmosphereLookUpTables(GraphBuilder, /* out */ PendingRDGResources); PendingRDGResources.CommitToSceneAndViewUniformBuffers(GraphBuilder, /* out */ ExternalAccessQueue); } // Run local fog volume initialization before base pass and volumetric fog for all the culled instance instance data to be ready. InitLocalFogVolumesForViews(Scene, Views, ViewFamily, GraphBuilder, ShouldRenderVolumetricFog(), bUseHalfResLocalFogVolume); if (bRendererOutputFinalSceneColor && Views.Num() > 0) { FViewInfo& MainView = Views[0]; const bool bRealTimeSkyCaptureEnabled = (bShouldRenderSkyAtmosphere || MainView.SkyMeshBatches.Num() > 0) && Scene->SkyLight && Scene->SkyLight->bRealTimeCaptureEnabled && ViewFamily.EngineShowFlags.SkyLighting; if (bRealTimeSkyCaptureEnabled) { // We must execute the submit for transition of SkyAtmosphere resources to happen (see CommitToSceneAndViewUniformBuffers) and avoid validation error. ExternalAccessQueue.Submit(GraphBuilder); const bool bShouldRenderVolumetricCloud = false; // Not supported on the mobile renderer. Scene->AllocateAndCaptureFrameSkyEnvMap(GraphBuilder, *this, MainView, bShouldRenderSkyAtmosphere, bShouldRenderVolumetricCloud, InstanceCullingManager, ExternalAccessQueue); UpdateSkyReflectionUniformBuffer(GraphBuilder.RHICmdList); } } // Hair update if (IsHairStrandsEnabled(EHairStrandsShaderType::All, Scene->GetShaderPlatform()) && RendererOutput != ERendererOutput::DepthPrepassOnly) { FHairStrandsBookmarkParameters& HairStrandsBookmarkParameters = *GraphBuilder.AllocObject(); CreateHairStrandsBookmarkParameters(Scene, Views, AllViews, HairStrandsBookmarkParameters); check(Scene->HairStrandsSceneData.TransientResources); HairStrandsBookmarkParameters.TransientResources = Scene->HairStrandsSceneData.TransientResources; // Not need for hair uniform buffer, as this is only used for strands rendering // If some shader refers to it, we can create a default one with HairStrands::CreateDefaultHairStrandsViewUniformBuffer(GraphBuilder, View); for (FViewInfo& View : Views) { View.HairStrandsViewData.UniformBuffer = nullptr; } // Interpolation needs to happen after the skin cache run as there is a dependency // on the skin cache output. const bool bRunHairStrands = HairStrandsBookmarkParameters.HasInstances() && (Views.Num() > 0); if (bRunHairStrands) { // 1. Update groom visible in primary views RunHairStrandsBookmark(GraphBuilder, EHairStrandsBookmark::ProcessCardsAndMeshesInterpolation_PrimaryView, HairStrandsBookmarkParameters); // 2. Update groom only visible in shadow // For now, not running on mobile to keep computation light // UpdateHairStrandsBookmarkParameters(Scene, Views, HairStrandsBookmarkParameters); // RunHairStrandsBookmark(GraphBuilder, EHairStrandsBookmark::ProcessCardsAndMeshesInterpolation_ShadowView, HairStrandsBookmarkParameters); } } RenderShadowDepthMaps(GraphBuilder, nullptr, InstanceCullingManager, ExternalAccessQueue); GraphBuilder.AddDispatchHint(); if (ShouldRenderVolumetricFog()) { ComputeVolumetricFog(GraphBuilder, SceneTextures); } ExternalAccessQueue.Submit(GraphBuilder); // Custom depth // bShouldRenderCustomDepth has been initialized in InitViews on mobile platform if (bShouldRenderCustomDepth) { RenderCustomDepthPass(GraphBuilder, SceneTextures.CustomDepth, SceneTextures.GetSceneTextureShaderParameters(FeatureLevel), {}, {}); } } else { SetDummyLocalFogVolumeForViews(GraphBuilder, Views); } // Sort objects' triangles for (FViewInfo& View : Views) { if (View.ShouldRenderView() && OIT::IsSortedTrianglesEnabled(View.GetShaderPlatform())) { OIT::AddSortTrianglesPass(GraphBuilder, View, Scene->OITSceneData, FTriangleSortingOrder::BackToFront); } } FRDGTextureRef ViewFamilyTexture = TryCreateViewFamilyTexture(GraphBuilder, ViewFamily); const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder); RenderWaterInfoTexture(GraphBuilder, *this, Scene); if (CustomRenderPassInfos.Num() > 0) { QUICK_SCOPE_CYCLE_COUNTER(STAT_CustomRenderPasses); RDG_EVENT_SCOPE_STAT(GraphBuilder, CustomRenderPasses, "CustomRenderPasses"); RDG_GPU_STAT_SCOPE(GraphBuilder, CustomRenderPasses); // We want to reset the scene texture uniform buffer to its original state after custom render passes, // so they can't affect downstream rendering. EMobileSceneTextureSetupMode OriginalSceneTextureSetupMode = SceneTextures.MobileSetupMode; TRDGUniformBufferRef OriginalSceneTextureUniformBuffer = SceneTextures.MobileUniformBuffer; for (int32 i = 0; i < CustomRenderPassInfos.Num(); ++i) { FCustomRenderPassBase* CustomRenderPass = CustomRenderPassInfos[i].CustomRenderPass; TArray& CustomRenderPassViews = CustomRenderPassInfos[i].Views; check(CustomRenderPass); CustomRenderPass->BeginPass(GraphBuilder); { QUICK_SCOPE_CYCLE_COUNTER(STAT_CustomRenderPass); RDG_EVENT_SCOPE(GraphBuilder, "CustomRenderPass[%d] %s", i, *CustomRenderPass->GetDebugName()); CustomRenderPass->PreRender(GraphBuilder); // Setup dummy uniform buffer parameters for fog volume. SetDummyLocalFogVolumeForViews(GraphBuilder, CustomRenderPassViews); if (bIsFullDepthPrepassEnabled) { RenderFullDepthPrepass(GraphBuilder, CustomRenderPassViews, SceneTextures, InstanceCullingManager, true); if (!bRequiresSceneDepthAux) { AddResolveSceneDepthPass(GraphBuilder, CustomRenderPassViews, SceneTextures.Depth); } } // Render base pass if the custom pass requires it. Otherwise if full depth prepass is not enabled, then depth is generated in the base pass. if (CustomRenderPass->GetRenderMode() == FCustomRenderPassBase::ERenderMode::DepthAndBasePass || (CustomRenderPass->GetRenderMode() == FCustomRenderPassBase::ERenderMode::DepthPass && !bIsFullDepthPrepassEnabled)) { RenderCustomRenderPassBasePass(GraphBuilder, CustomRenderPassViews, ViewFamilyTexture, SceneTextures, InstanceCullingManager, CustomRenderPass->IsTranslucentIncluded()); } SceneTextures.MobileSetupMode = EMobileSceneTextureSetupMode::SceneColor | EMobileSceneTextureSetupMode::SceneDepth | EMobileSceneTextureSetupMode::SceneDepthAux; SceneTextures.MobileUniformBuffer = CreateMobileSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, SceneTextures.MobileSetupMode); CopySceneCaptureComponentToTarget(GraphBuilder, SceneTextures, CustomRenderPass->GetRenderTargetTexture(), ViewFamily, CustomRenderPassViews); CustomRenderPass->PostRender(GraphBuilder); // Mips are normally generated in UpdateSceneCaptureContentMobile_RenderThread, but that doesn't run when the // scene capture runs as a custom render pass. The function does nothing if the render target doesn't have mips. if (CustomRenderPassViews[0].bIsSceneCapture) { FGenerateMips::Execute(GraphBuilder, FeatureLevel, CustomRenderPass->GetRenderTargetTexture(), FGenerateMipsParams()); } } CustomRenderPass->EndPass(GraphBuilder); SceneTextures.MobileSetupMode = OriginalSceneTextureSetupMode; SceneTextures.MobileUniformBuffer = OriginalSceneTextureUniformBuffer; } } FDBufferTextures DBufferTextures{}; if (bIsFullDepthPrepassEnabled) { RenderFullDepthPrepass(GraphBuilder, Views, SceneTextures, InstanceCullingManager); if (!bRequiresSceneDepthAux) { AddResolveSceneDepthPass(GraphBuilder, Views, SceneTextures.Depth); } SceneTextures.MobileSetupMode = EMobileSceneTextureSetupMode::SceneDepth; SceneTextures.MobileUniformBuffer = CreateMobileSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, SceneTextures.MobileSetupMode); // When renderer is in ERendererOutput::DepthPrepassOnly mode, bShouldRenderHZB is set to false in InitViews() if (bShouldRenderHZB) { RenderHZB(GraphBuilder, SceneTextures.Depth.Resolve); } // When renderer is in ERendererOutput::DepthPrepassOnly mode, bRequiresAmbientOcclusionPass is set to false in InitViews() if (bRequiresAmbientOcclusionPass) { RenderAmbientOcclusion(GraphBuilder, SceneTextures.Depth.Resolve, SceneTextures.ScreenSpaceAO); } if (bEnableDistanceFieldAO && (!bDeferredShading || !bRequiresMultiPass)) { TArray DynamicBentNormalAOTextures; const float OcclusionMaxDistance = Scene->SkyLight && !Scene->SkyLight->bWantsStaticShadowing ? Scene->SkyLight->OcclusionMaxDistance : Scene->DefaultMaxDistanceFieldOcclusionDistance; RenderDistanceFieldLighting(GraphBuilder, SceneTextures, FDistanceFieldAOParameters(OcclusionMaxDistance), DynamicBentNormalAOTextures, false, false, true); } // When renderer is in ERendererOutput::DepthPrepassOnly mode, bRequiresShadowProjections is set to false in InitViews() if (bRequiresShadowProjections) { RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderMobileShadowProjections); RDG_EVENT_SCOPE_STAT(GraphBuilder, ShadowProjection, "ShadowProjection"); RDG_GPU_STAT_SCOPE(GraphBuilder, ShadowProjection); RenderMobileShadowProjections(GraphBuilder); } // Local Light prepass if (bRendererOutputFinalSceneColor) { RenderMobileLocalLightsBuffer(GraphBuilder, SceneTextures, SortedLightSet); } if (bRendererOutputFinalSceneColor) { if (bRequiresDBufferDecals) { FViewInfo* MainView = Views.Num() > 0 ? &Views[0] : nullptr; bool bIsMobileMultiView = SceneTextures.Config.bRequireMultiView || (MainView && MainView->Aspects.IsMobileMultiViewEnabled()); DBufferTextures = CreateDBufferTextures(GraphBuilder, SceneTextures.Config.Extent, ShaderPlatform, bIsMobileMultiView); RenderDBuffer(GraphBuilder, SceneTextures, DBufferTextures, InstanceCullingManager); } } // Render half res local fog volume here for (FViewInfo& View : Views) { if (View.LocalFogVolumeViewData.bUseHalfResLocalFogVolume) { RenderLocalFogVolumeHalfResMobile(GraphBuilder, View); } } } for (FSceneViewExtensionRef& ViewExtension : ViewFamily.ViewExtensions) { ViewExtension->PreRenderBasePass_RenderThread(GraphBuilder, bIsFullDepthPrepassEnabled /*bDepthBufferIsPopulated*/); } if (bRendererOutputFinalSceneColor) { if (bDeferredShading) { if (bRequiresMultiPass) { RenderDeferredMultiPass(GraphBuilder, SceneTextures, SortedLightSet, DBufferTextures, InstanceCullingManager); } else { RenderDeferredSinglePass(GraphBuilder, SceneTextures, SortedLightSet, DBufferTextures, InstanceCullingManager); } } else { RenderForward(GraphBuilder, ViewFamilyTexture, SceneTextures, DBufferTextures, InstanceCullingManager); } if (DoOcclusionQueries() && !bIsFullDepthPrepassEnabled) { EndOcclusionScope(GraphBuilder, Views); FenceOcclusionTests(GraphBuilder); } SceneTextures.MobileSetupMode = EMobileSceneTextureSetupMode::All; SceneTextures.MobileSetupMode &= ~EMobileSceneTextureSetupMode::SceneVelocity; SceneTextures.MobileUniformBuffer = CreateMobileSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, SceneTextures.MobileSetupMode); if (bShouldRenderVelocities) { // Render the velocities of movable objects EDepthDrawingMode EarlyZPassMode = Scene ? Scene->EarlyZPassMode : DDM_None; if (EarlyZPassMode != DDM_AllOpaqueNoVelocity) { RenderVelocities(GraphBuilder, Views, SceneTextures, EVelocityPass::Opaque, false); } RenderVelocities(GraphBuilder, Views, SceneTextures, EVelocityPass::Translucent, false); SceneTextures.MobileSetupMode = EMobileSceneTextureSetupMode::All; SceneTextures.MobileUniformBuffer = CreateMobileSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, SceneTextures.MobileSetupMode); } FRendererModule& RendererModule = static_cast(GetRendererModule()); RendererModule.RenderPostOpaqueExtensions(GraphBuilder, Views, SceneTextures); RenderOpaqueFX(GraphBuilder, GetSceneViews(), GetSceneUniforms(), FXSystem, SceneTextures.MobileUniformBuffer); if (ViewFamily.EngineShowFlags.VisualizeMeshDistanceFields || ViewFamily.EngineShowFlags.VisualizeGlobalDistanceField) { RenderMeshDistanceFieldVisualization(GraphBuilder, SceneTextures); } if (ViewFamily.EngineShowFlags.VisualizeInstanceOcclusionQueries && Scene->InstanceCullingOcclusionQueryRenderer) { for (FViewInfo& View : Views) { Scene->InstanceCullingOcclusionQueryRenderer->RenderDebug(GraphBuilder, Scene->GPUScene, View, SceneTextures); } } if (ViewFamily.bResolveScene) { if (bRenderToSceneColor && !bTonemapSubpassInline) { // Finish rendering for each view, or the full stereo buffer if enabled { RDG_EVENT_SCOPE_STAT(GraphBuilder, Postprocessing, "PostProcessing"); RDG_GPU_STAT_SCOPE(GraphBuilder, Postprocessing); SCOPE_CYCLE_COUNTER(STAT_FinishRenderViewTargetTime); FMobilePostProcessingInputs PostProcessingInputs; PostProcessingInputs.ViewFamilyTexture = ViewFamilyTexture; PostProcessingInputs.SceneTextures = CreateMobileSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, EMobileSceneTextureSetupMode::All); for (int32 ViewExt = 0; ViewExt < ViewFamily.ViewExtensions.Num(); ++ViewExt) { for (int32 ViewIndex = 0; ViewIndex < ViewFamily.Views.Num(); ++ViewIndex) { FViewInfo& View = Views[ViewIndex]; RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask); ViewFamily.ViewExtensions[ViewExt]->PrePostProcessPassMobile_RenderThread(GraphBuilder, View, PostProcessingInputs); } } for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { if (Views[ViewIndex].ShouldRenderView()) { RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex); if (bTonemapSubpass) { AddMobileCustomResolvePass(GraphBuilder, Views[ViewIndex], SceneTextures, ViewFamilyTexture); } else { AddMobilePostProcessingPasses(GraphBuilder, Scene, Views[ViewIndex], ViewIndex, GetSceneUniforms(), PostProcessingInputs, InstanceCullingManager); } if (CVarMobileXRMSAAMode.GetValueOnAnyThread() == 1) { AddDrawTexturePass(GraphBuilder, Views[ViewIndex], SceneTextures.Depth.Target, SceneTextures.Depth.Resolve); } } } } } } } GEngine->GetPostRenderDelegateEx().Broadcast(GraphBuilder); GetSceneExtensionsRenderers().PostRender(GraphBuilder); if (bUseVirtualTexturing) { VirtualTexture::EndFeedback(GraphBuilder); } if (bRendererOutputFinalSceneColor && bShouldRenderHZB && !bRequiresMultiPass) { RenderHZB(GraphBuilder, SceneTextures.Depth.Resolve); } OnRenderFinish(GraphBuilder, ViewFamilyTexture); QueueSceneTextureExtractions(GraphBuilder, SceneTextures); if (Scene->InstanceCullingOcclusionQueryRenderer) { Scene->InstanceCullingOcclusionQueryRenderer->EndFrame(GraphBuilder); } } FRenderTargetBindingSlots FMobileSceneRenderer::InitRenderTargetBindings_Forward(FRDGTextureRef ViewFamilyTexture, FSceneTextures& SceneTextures) { FRDGTextureRef SceneColor = nullptr; FRDGTextureRef SceneColorResolve = nullptr; FRDGTextureRef SceneDepth = nullptr; FRDGTextureRef SceneDepthResolve = nullptr; // Verify using both MSAA sample count AND the scene color surface sample count, since on GLES you can't have MSAA color targets, // so the color target would be created without MSAA, and MSAA is achieved through magical means (the framebuffer, being MSAA, // tells the GPU "execute this renderpass as MSAA, and when you're done, automatically resolve and copy into this non-MSAA texture"). bool bMobileMSAA = NumMSAASamples > 1; if (!bRenderToSceneColor) { if (bMobileMSAA) { SceneColor = SceneTextures.Color.Target; SceneColorResolve = ViewFamilyTexture; } else { SceneColor = ViewFamilyTexture; } } else { SceneColor = SceneTextures.Color.Target; SceneColorResolve = bMobileMSAA ? SceneTextures.Color.Resolve : nullptr; } SceneDepth = SceneTextures.Depth.Target; SceneDepthResolve = GRHISupportsDepthStencilResolve && bMobileMSAA && SceneTextures.Depth.IsSeparate() ? SceneTextures.Depth.Resolve : nullptr; FRenderTargetBindingSlots BasePassRenderTargets; BasePassRenderTargets[0] = FRenderTargetBinding(SceneColor, SceneColorResolve, ERenderTargetLoadAction::EClear); if (bRequiresSceneDepthAux) { BasePassRenderTargets[1] = FRenderTargetBinding(SceneTextures.DepthAux.Target, SceneTextures.DepthAux.Resolve, ERenderTargetLoadAction::EClear); } if (bTonemapSubpassInline) { // DepthAux is not used with tonemap subpass, since there are no post-processing passes // Backbuffer surface provided as a second render target instead of resolve target. BasePassRenderTargets[0].SetResolveTexture(nullptr); BasePassRenderTargets[1] = FRenderTargetBinding(ViewFamilyTexture, nullptr, ERenderTargetLoadAction::EClear); } BasePassRenderTargets.DepthStencil = bIsFullDepthPrepassEnabled ? FDepthStencilBinding(SceneDepth, SceneDepthResolve, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite) : FDepthStencilBinding(SceneDepth, SceneDepthResolve, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::EClear, FExclusiveDepthStencil::DepthWrite_StencilWrite); BasePassRenderTargets.SubpassHint = ESubpassHint::None; BasePassRenderTargets.NumOcclusionQueries = 0u; return BasePassRenderTargets; } void FMobileSceneRenderer::RenderForward(FRDGBuilder& GraphBuilder, FRDGTextureRef ViewFamilyTexture, FSceneTextures& SceneTextures, FDBufferTextures& DBufferTextures, FInstanceCullingManager& InstanceCullingManager) { const FViewInfo& MainView = Views[0]; GVRSImageManager.PrepareImageBasedVRS(GraphBuilder, ViewFamily, SceneTextures); FRDGTextureRef NewShadingRateTarget = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, MainView, FVariableRateShadingImageManager::EVRSPassType::BasePass); FRenderTargetBindingSlots BasePassRenderTargets = InitRenderTargetBindings_Forward(ViewFamilyTexture, SceneTextures); BasePassRenderTargets.ShadingRateTexture = (!MainView.bIsSceneCapture && !MainView.bIsReflectionCapture && (NewShadingRateTarget != nullptr)) ? NewShadingRateTarget : nullptr; //if the scenecolor isn't multiview but the app is, need to render as a single-view multiview due to shaders BasePassRenderTargets.MultiViewCount = (MainView.bIsMobileMultiViewEnabled) ? 2 : (MainView.Aspects.IsMobileMultiViewEnabled() ? 1 : 0); const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder); FRenderViewContextArray RenderViews; GetRenderViews(Views, RenderViews); for (FRenderViewContext& ViewContext : RenderViews) { FViewInfo& View = *ViewContext.ViewInfo; SCOPED_GPU_MASK(GraphBuilder.RHICmdList, !View.IsInstancedStereoPass() ? View.GPUMask : (View.GPUMask | View.GetInstancedView()->GPUMask)); SCOPED_CONDITIONAL_DRAW_EVENTF(GraphBuilder.RHICmdList, EventView, RenderViews.Num() > 1, TEXT("View%d"), ViewContext.ViewIndex); if (!ViewContext.bIsFirstView) { BasePassRenderTargets[0].SetLoadAction(ERenderTargetLoadAction::ELoad); if (bRequiresSceneDepthAux) { BasePassRenderTargets[1].SetLoadAction(ERenderTargetLoadAction::ELoad); } BasePassRenderTargets.DepthStencil.SetDepthLoadAction(ERenderTargetLoadAction::ELoad); BasePassRenderTargets.DepthStencil.SetStencilLoadAction(ERenderTargetLoadAction::ELoad); BasePassRenderTargets.DepthStencil.SetDepthStencilAccess(bIsFullDepthPrepassEnabled ? FExclusiveDepthStencil::DepthRead_StencilWrite : FExclusiveDepthStencil::DepthWrite_StencilWrite); } View.BeginRenderView(); UpdateDirectionalLightUniformBuffers(GraphBuilder, View); FMobileBasePassTextures MobileBasePassTextures{}; MobileBasePassTextures.DBufferTextures = DBufferTextures; EMobileSceneTextureSetupMode SetupMode = (bIsFullDepthPrepassEnabled ? EMobileSceneTextureSetupMode::SceneDepth : EMobileSceneTextureSetupMode::None) | EMobileSceneTextureSetupMode::CustomDepth; FMobileRenderPassParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.GetShaderParameters(); PassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::Opaque, SetupMode, MobileBasePassTextures); PassParameters->ReflectionCapture = View.MobileReflectionCaptureUniformBuffer; PassParameters->RenderTargets = BasePassRenderTargets; PassParameters->LocalFogVolumeInstances = View.LocalFogVolumeViewData.GPUInstanceDataBufferSRV; PassParameters->LocalFogVolumeTileDrawIndirectBuffer = View.LocalFogVolumeViewData.GPUTileDrawIndirectBuffer; PassParameters->LocalFogVolumeTileDataTexture = View.LocalFogVolumeViewData.TileDataTextureArraySRV; PassParameters->LocalFogVolumeTileDataBuffer = View.LocalFogVolumeViewData.GPUTileDataBufferSRV; PassParameters->HalfResLocalFogVolumeViewSRV = View.LocalFogVolumeViewData.HalfResLocalFogVolumeViewSRV; PassParameters->HalfResLocalFogVolumeDepthSRV = View.LocalFogVolumeViewData.HalfResLocalFogVolumeDepthSRV; // Split if we need to render translucency in a separate render pass if (bRequiresMultiPass) { RenderForwardMultiPass(GraphBuilder, PassParameters, ViewContext, SceneTextures, InstanceCullingManager); } else { RenderForwardSinglePass(GraphBuilder, PassParameters, ViewContext, SceneTextures, InstanceCullingManager); } } if (ViewFamily.EngineShowFlags.AlphaInvert) { AlphaInvert::AddAlphaInvertPass(GraphBuilder, MainView, SceneTextures); } } void FMobileSceneRenderer::RenderForwardSinglePass(FRDGBuilder& GraphBuilder, FMobileRenderPassParameters* PassParameters, FRenderViewContext& ViewContext, FSceneTextures& SceneTextures, FInstanceCullingManager& InstanceCullingManager) { struct FForwardSinglePassParameterCollection { FInstanceCullingDrawParams DepthPassInstanceCullingDrawParams; FInstanceCullingDrawParams SkyPassInstanceCullingDrawParams; FInstanceCullingDrawParams DebugViewModeInstanceCullingDrawParams; FInstanceCullingDrawParams MeshDecalSceneColorInstanceCullingDrawParams; FInstanceCullingDrawParams TranslucencyInstanceCullingDrawParams; }; auto ParameterCollection = GraphBuilder.AllocParameters(); FViewInfo& View = *ViewContext.ViewInfo; if (Scene->GPUScene.IsEnabled()) { if (!bIsFullDepthPrepassEnabled) { BuildMeshRenderingCommands(GraphBuilder, EMeshPass::DepthPass, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->DepthPassInstanceCullingDrawParams); } BuildMeshRenderingCommands(GraphBuilder, EMeshPass::BasePass, View, Scene->GPUScene, InstanceCullingManager, PassParameters->InstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, EMeshPass::SkyPass, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->SkyPassInstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, EMeshPass::DebugViewMode, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->DebugViewModeInstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, EMeshPass::MeshDecal_SceneColor, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->MeshDecalSceneColorInstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, StandardTranslucencyMeshPass, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->TranslucencyInstanceCullingDrawParams); } if (bTonemapSubpassInline) { // tonemapping LUT pass before we start main render pass. The texture is needed by the custom resolve pass which does tonemapping PassParameters->ColorGradingLUT = AddCombineLUTPass(GraphBuilder, *ViewContext.ViewInfo); } PassParameters->RenderTargets.SubpassHint = bTonemapSubpassInline ? ESubpassHint::CustomResolveSubpass : ESubpassHint::DepthReadSubpass; const bool bDoOcclusionQueries = (!bIsFullDepthPrepassEnabled && ViewContext.bIsLastView && DoOcclusionQueries()); const int32 NumOcclusionQueries = bDoOcclusionQueries ? ComputeNumOcclusionQueriesToBatch() : 0u; const bool bVulkanAdrenoOcclusionMode = bAdrenoOcclusionMode && IsVulkanPlatform(ShaderPlatform); const bool bDoOcclusionInMainPass = bDoOcclusionQueries && !bVulkanAdrenoOcclusionMode; PassParameters->RenderTargets.NumOcclusionQueries = bVulkanAdrenoOcclusionMode ? 0u : NumOcclusionQueries; GraphBuilder.AddPass( RDG_EVENT_NAME("SceneColorRendering"), PassParameters, // the second view pass should not be merged with the first view pass on mobile since the subpass would not work properly. ERDGPassFlags::Raster | ERDGPassFlags::NeverMerge, [this, PassParameters, ParameterCollection, ViewContext, bDoOcclusionInMainPass, &SceneTextures](FRHICommandList& RHICmdList) { FViewInfo& View = *ViewContext.ViewInfo; if (GIsEditor && !View.bIsSceneCapture && ViewContext.bIsFirstView) { DrawClearQuad(RHICmdList, View.BackgroundColor); } // Depth pre-pass RenderMaskedPrePass(RHICmdList, View, &ParameterCollection->DepthPassInstanceCullingDrawParams); // Opaque and masked RenderMobileBasePass(RHICmdList, View, &PassParameters->InstanceCullingDrawParams, &ParameterCollection->SkyPassInstanceCullingDrawParams); RenderMobileDebugView(RHICmdList, View, &ParameterCollection->DebugViewModeInstanceCullingDrawParams); PostRenderBasePass(RHICmdList, View); // scene depth is read only and can be fetched RHICmdList.NextSubpass(); RenderDecals(RHICmdList, View, &ParameterCollection->MeshDecalSceneColorInstanceCullingDrawParams); RenderModulatedShadowProjections(RHICmdList, ViewContext.ViewIndex, View); if (GMaxRHIShaderPlatform != SP_METAL_SIM) { RenderFog(RHICmdList, View); } // Draw translucency. RenderTranslucency(RHICmdList, View, Views, StandardTranslucencyPass, StandardTranslucencyMeshPass, &ParameterCollection->TranslucencyInstanceCullingDrawParams); #if UE_ENABLE_DEBUG_DRAWING if ((!IsMobileHDR() || bTonemapSubpass) && FSceneRenderer::ShouldCompositeDebugPrimitivesInPostProcess(View)) { // Draw debug primitives after translucency for LDR as we do not have a post processing pass RenderMobileDebugPrimitives(RHICmdList, View); } #endif if (bDoOcclusionInMainPass) { // Issue occlusion queries if (bAdrenoOcclusionMode && IsOpenGLPlatform(ShaderPlatform)) { // flush RHICmdList.SubmitCommandsHint(); } RenderOcclusion(RHICmdList); } // Pre-tonemap before MSAA resolve (iOS only) PreTonemapMSAA(RHICmdList, SceneTextures); if (bTonemapSubpassInline) { RHICmdList.NextSubpass(); RenderMobileCustomResolve(RHICmdList, View, NumMSAASamples, SceneTextures); } }); // resolve MSAA depth if (!GRHISupportsDepthStencilResolve && !bIsFullDepthPrepassEnabled) { AddResolveSceneDepthPass(GraphBuilder, *ViewContext.ViewInfo, SceneTextures.Depth); } if (bDoOcclusionQueries && bVulkanAdrenoOcclusionMode) { FMobileRenderPassParameters* OcclusionPassParams = GraphBuilder.AllocParameters(); OcclusionPassParams->View = PassParameters->View; OcclusionPassParams->RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Resolve, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilNop); OcclusionPassParams->RenderTargets.MultiViewCount = PassParameters->RenderTargets.MultiViewCount; OcclusionPassParams->RenderTargets.NumOcclusionQueries = NumOcclusionQueries; if (GAdrenoOcclusionUseFDM.GetValueOnRenderThread()) { OcclusionPassParams->RenderTargets.ShadingRateTexture = PassParameters->RenderTargets.ShadingRateTexture; } GraphBuilder.AddPass( RDG_EVENT_NAME("VulkanAdrenoOcclusion"), OcclusionPassParams, // The occlusion pass needs to be unique to be optimized properly by the driver, don't merge it. // This pass has no observable outputs on the RenderGraph, so it needs to be marked as NeverCull. ERDGPassFlags::Raster | ERDGPassFlags::NeverMerge | ERDGPassFlags::NeverCull, [this](FRHICommandListImmediate& RHICmdList) { RenderOcclusion(RHICmdList); }); } } void FMobileSceneRenderer::RenderForwardMultiPass(FRDGBuilder& GraphBuilder, FMobileRenderPassParameters* PassParameters, FRenderViewContext& ViewContext, FSceneTextures& SceneTextures, FInstanceCullingManager& InstanceCullingManager) { struct FForwardFirstPassParameterCollection { FInstanceCullingDrawParams DepthPassInstanceCullingDrawParams; FInstanceCullingDrawParams SkyPassInstanceCullingDrawParams; FInstanceCullingDrawParams DebugViewModeInstanceCullingDrawParams; }; auto ParameterCollection = GraphBuilder.AllocParameters(); FViewInfo& View = *ViewContext.ViewInfo; if (Scene->GPUScene.IsEnabled()) { if (!bIsFullDepthPrepassEnabled) { BuildMeshRenderingCommands(GraphBuilder, EMeshPass::DepthPass, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->DepthPassInstanceCullingDrawParams); } BuildMeshRenderingCommands(GraphBuilder, EMeshPass::BasePass, View, Scene->GPUScene, InstanceCullingManager, PassParameters->InstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, EMeshPass::SkyPass, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->SkyPassInstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, EMeshPass::DebugViewMode, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->DebugViewModeInstanceCullingDrawParams); } GraphBuilder.AddPass( RDG_EVENT_NAME("SceneColorRendering"), PassParameters, ERDGPassFlags::Raster, [this, PassParameters, ParameterCollection, ViewContext, &SceneTextures](FRHICommandList& RHICmdList) { FViewInfo& View = *ViewContext.ViewInfo; if (GIsEditor && !View.bIsSceneCapture && ViewContext.bIsFirstView) { DrawClearQuad(RHICmdList, View.BackgroundColor); } // Depth pre-pass RenderMaskedPrePass(RHICmdList, View, &ParameterCollection->DepthPassInstanceCullingDrawParams); // Opaque and masked RenderMobileBasePass(RHICmdList, View, &PassParameters->InstanceCullingDrawParams, &ParameterCollection->SkyPassInstanceCullingDrawParams); RenderMobileDebugView(RHICmdList, View, &ParameterCollection->DebugViewModeInstanceCullingDrawParams); PostRenderBasePass(RHICmdList, View); }); // resolve MSAA depth if (!bIsFullDepthPrepassEnabled) { AddResolveSceneDepthPass(GraphBuilder, View, SceneTextures.Depth); } if (bRequiresSceneDepthAux) { AddResolveSceneColorPass(GraphBuilder, View, SceneTextures.DepthAux); } if (bShouldRenderHZB && !bIsFullDepthPrepassEnabled) { RenderHZB(GraphBuilder, SceneTextures.Depth.Resolve); } FExclusiveDepthStencil::Type ExclusiveDepthStencil = FExclusiveDepthStencil::DepthRead_StencilRead; if (bModulatedShadowsInUse) { // FIXME: modulated shadows write to stencil ExclusiveDepthStencil = FExclusiveDepthStencil::DepthRead_StencilWrite; } EMobileSceneTextureSetupMode SetupMode = EMobileSceneTextureSetupMode::SceneDepth | EMobileSceneTextureSetupMode::SceneDepthAux | EMobileSceneTextureSetupMode::CustomDepth; struct FForwardSecondPassParameterCollection { FMobileRenderPassParameters PassParameters; FInstanceCullingDrawParams MeshDecalSceneColorInstanceCullingDrawParams; }; auto SecondParameterCollection = GraphBuilder.AllocParameters(); auto SecondPassParameters = &SecondParameterCollection->PassParameters; *SecondPassParameters = *PassParameters; SecondPassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::Translucent, SetupMode); SecondPassParameters->ReflectionCapture = View.MobileReflectionCaptureUniformBuffer; SecondPassParameters->RenderTargets[0].SetLoadAction(ERenderTargetLoadAction::ELoad); SecondPassParameters->RenderTargets[1] = FRenderTargetBinding(); SecondPassParameters->RenderTargets.DepthStencil.SetDepthLoadAction(ERenderTargetLoadAction::ELoad); SecondPassParameters->RenderTargets.DepthStencil.SetStencilLoadAction(ERenderTargetLoadAction::ELoad); SecondPassParameters->RenderTargets.DepthStencil.SetDepthStencilAccess(ExclusiveDepthStencil); const bool bDoOcclusionQueries = (!bIsFullDepthPrepassEnabled && ViewContext.bIsLastView && DoOcclusionQueries()); SecondPassParameters->RenderTargets.NumOcclusionQueries = bDoOcclusionQueries ? ComputeNumOcclusionQueriesToBatch() : 0u; if (Scene->GPUScene.IsEnabled()) { BuildMeshRenderingCommands(GraphBuilder, EMeshPass::MeshDecal_SceneColor, View, Scene->GPUScene, InstanceCullingManager, SecondParameterCollection->MeshDecalSceneColorInstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, StandardTranslucencyMeshPass, View, Scene->GPUScene, InstanceCullingManager, SecondPassParameters->InstanceCullingDrawParams); } GraphBuilder.AddPass( RDG_EVENT_NAME("DecalsAndTranslucency"), SecondPassParameters, ERDGPassFlags::Raster, [this, SecondParameterCollection, ViewContext, bDoOcclusionQueries, &SceneTextures](FRHICommandList& RHICmdList) { FViewInfo& View = *ViewContext.ViewInfo; auto SecondPassParameters = &SecondParameterCollection->PassParameters; // scene depth is read only and can be fetched RenderDecals(RHICmdList, View, &SecondParameterCollection->MeshDecalSceneColorInstanceCullingDrawParams); RenderModulatedShadowProjections(RHICmdList, ViewContext.ViewIndex, View); RenderFog(RHICmdList, View); // Draw translucency. RenderTranslucency(RHICmdList, View, Views, StandardTranslucencyPass, StandardTranslucencyMeshPass, &SecondPassParameters->InstanceCullingDrawParams); if (bDoOcclusionQueries) { // Issue occlusion queries RenderOcclusion(RHICmdList); } // Pre-tonemap before MSAA resolve (iOS only) PreTonemapMSAA(RHICmdList, SceneTextures); }); AddResolveSceneColorPass(GraphBuilder, View, SceneTextures.Color); } class FMobileDeferredCopyPLSPS : public FGlobalShader { DECLARE_SHADER_TYPE(FMobileDeferredCopyPLSPS, Global); static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsMobilePlatform(Parameters.Platform) && IsMobileDeferredShadingEnabled(Parameters.Platform); } /** Default constructor. */ FMobileDeferredCopyPLSPS() {} /** Initialization constructor. */ FMobileDeferredCopyPLSPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { } }; IMPLEMENT_SHADER_TYPE(, FMobileDeferredCopyPLSPS, TEXT("/Engine/Private/MobileDeferredUtils.usf"), TEXT("MobileDeferredCopyPLSPS"), SF_Pixel); class FMobileDeferredCopyDepthPS : public FGlobalShader { DECLARE_SHADER_TYPE(FMobileDeferredCopyDepthPS, Global); static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsMobilePlatform(Parameters.Platform) && IsMobileDeferredShadingEnabled(Parameters.Platform); } /** Default constructor. */ FMobileDeferredCopyDepthPS() {} /** Initialization constructor. */ FMobileDeferredCopyDepthPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { } }; IMPLEMENT_SHADER_TYPE(, FMobileDeferredCopyDepthPS, TEXT("/Engine/Private/MobileDeferredUtils.usf"), TEXT("MobileDeferredCopyDepthPS"), SF_Pixel); template void MobileDeferredCopyBuffer(FRHICommandList& RHICmdList, const FViewInfo& View) { FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI(); GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); TShaderMapRef VertexShader(View.ShaderMap); TShaderMapRef PixelShader(View.ShaderMap); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0u); 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()), View.GetSceneTexturesConfig().Extent, VertexShader); } static bool UsingPixelLocalStorage(const FStaticShaderPlatform ShaderPlatform) { return IsAndroidOpenGLESPlatform(ShaderPlatform) && GSupportsPixelLocalStorage && GSupportsShaderDepthStencilFetch; } FColorTargets FMobileSceneRenderer::GetColorTargets_Deferred(FSceneTextures& SceneTextures) { FColorTargets ColorTargets; // If we are using GL and don't have FBF support, use PLS bool bUsingPixelLocalStorage = UsingPixelLocalStorage(ShaderPlatform); if (bUsingPixelLocalStorage) { ColorTargets.Add(SceneTextures.Color.Target); } else { ColorTargets.Add(SceneTextures.Color.Target); ColorTargets.Add(SceneTextures.GBufferA); ColorTargets.Add(SceneTextures.GBufferB); ColorTargets.Add(SceneTextures.GBufferC); if (MobileUsesExtenedGBuffer(ShaderPlatform)) { ColorTargets.Add(SceneTextures.GBufferD); } if (bRequiresSceneDepthAux) { ColorTargets.Add(SceneTextures.DepthAux.Target); } } return ColorTargets; } FRenderTargetBindingSlots FMobileSceneRenderer::InitRenderTargetBindings_Deferred(FSceneTextures& SceneTextures, TArray>& ColorTargets) { TArrayView BasePassTexturesView = MakeArrayView(ColorTargets); FRenderTargetBindingSlots BasePassRenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::EClear, BasePassTexturesView); BasePassRenderTargets.DepthStencil = bIsFullDepthPrepassEnabled ? FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite) : FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::EClear, FExclusiveDepthStencil::DepthWrite_StencilWrite); BasePassRenderTargets.SubpassHint = ESubpassHint::None; BasePassRenderTargets.NumOcclusionQueries = 0u; BasePassRenderTargets.ShadingRateTexture = nullptr; //if the scenecolor isn't multiview but the app is, need to render as a single-view multiview due to shaders BasePassRenderTargets.MultiViewCount = (Views[0].bIsMobileMultiViewEnabled) ? 2 : (Views[0].Aspects.IsMobileMultiViewEnabled() ? 1 : 0); return BasePassRenderTargets; } void FMobileSceneRenderer::RenderDeferredSinglePass(FRDGBuilder& GraphBuilder, FSceneTextures& SceneTextures, const FSortedLightSetSceneInfo& SortedLightSet, FDBufferTextures& DBufferTextures, FInstanceCullingManager& InstanceCullingManager) { bool bUsingPixelLocalStorage = UsingPixelLocalStorage(ShaderPlatform); FColorTargets ColorTargets = GetColorTargets_Deferred(SceneTextures); FRenderTargetBindingSlots BasePassRenderTargets = InitRenderTargetBindings_Deferred(SceneTextures, ColorTargets); const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder); FRenderViewContextArray RenderViews; GetRenderViews(Views, RenderViews); for (FRenderViewContext& ViewContext : RenderViews) { FViewInfo& View = *ViewContext.ViewInfo; SCOPED_GPU_MASK(GraphBuilder.RHICmdList, !View.IsInstancedStereoPass() ? View.GPUMask : (View.GPUMask | View.GetInstancedView()->GPUMask)); SCOPED_CONDITIONAL_DRAW_EVENTF(GraphBuilder.RHICmdList, EventView, RenderViews.Num() > 1, TEXT("View%d"), ViewContext.ViewIndex); if (!ViewContext.bIsFirstView) { // Load targets for a non-first view for (int32 i = 0; i < ColorTargets.Num(); ++i) { BasePassRenderTargets[i].SetLoadAction(ERenderTargetLoadAction::ELoad); } BasePassRenderTargets.DepthStencil.SetDepthLoadAction(ERenderTargetLoadAction::ELoad); BasePassRenderTargets.DepthStencil.SetStencilLoadAction(ERenderTargetLoadAction::ELoad); BasePassRenderTargets.DepthStencil.SetDepthStencilAccess(bIsFullDepthPrepassEnabled ? FExclusiveDepthStencil::DepthRead_StencilWrite : FExclusiveDepthStencil::DepthWrite_StencilWrite); } View.BeginRenderView(); UpdateDirectionalLightUniformBuffers(GraphBuilder, View); EMobileSceneTextureSetupMode SetupMode = (bIsFullDepthPrepassEnabled ? EMobileSceneTextureSetupMode::SceneDepth : EMobileSceneTextureSetupMode::None) | EMobileSceneTextureSetupMode::CustomDepth; struct FDeferredSinglePassParameterCollection { FMobileRenderPassParameters PassParameters; FInstanceCullingDrawParams DepthPassInstanceCullingDrawParams; FInstanceCullingDrawParams SkyPassInstanceCullingDrawParams; FInstanceCullingDrawParams DebugViewModeInstanceCullingDrawParams; FInstanceCullingDrawParams MeshDecalSceneColorAndGBufferInstanceCullingDrawParams; FInstanceCullingDrawParams TranslucencyInstanceCullingDrawParams; }; auto ParameterCollection = GraphBuilder.AllocParameters(); FMobileBasePassTextures MobileBasePassTextures{}; MobileBasePassTextures.DBufferTextures = DBufferTextures; auto PassParameters = &ParameterCollection->PassParameters; PassParameters->View = View.GetShaderParameters(); PassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::Opaque, SetupMode, MobileBasePassTextures); PassParameters->ReflectionCapture = View.MobileReflectionCaptureUniformBuffer; PassParameters->LocalFogVolumeInstances = View.LocalFogVolumeViewData.GPUInstanceDataBufferSRV; PassParameters->LocalFogVolumeTileDrawIndirectBuffer = View.LocalFogVolumeViewData.GPUTileDrawIndirectBuffer; PassParameters->LocalFogVolumeTileDataTexture = View.LocalFogVolumeViewData.TileDataTextureArraySRV; PassParameters->LocalFogVolumeTileDataBuffer = View.LocalFogVolumeViewData.GPUTileDataBufferSRV; PassParameters->HalfResLocalFogVolumeViewSRV = View.LocalFogVolumeViewData.HalfResLocalFogVolumeViewSRV; PassParameters->HalfResLocalFogVolumeDepthSRV = View.LocalFogVolumeViewData.HalfResLocalFogVolumeDepthSRV; PassParameters->RenderTargets = BasePassRenderTargets; PassParameters->RenderTargets.SubpassHint = ESubpassHint::DeferredShadingSubpass; const EMobileSSRQuality MobileSSRQuality = ActiveMobileSSRQuality(View, bShouldRenderVelocities); const bool bDoOcclusionQueires = (!bIsFullDepthPrepassEnabled && ViewContext.bIsLastView && DoOcclusionQueries()); PassParameters->RenderTargets.NumOcclusionQueries = bDoOcclusionQueires ? ComputeNumOcclusionQueriesToBatch() : 0u; if (Scene->GPUScene.IsEnabled()) { if (!bIsFullDepthPrepassEnabled) { BuildMeshRenderingCommands(GraphBuilder, EMeshPass::DepthPass, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->DepthPassInstanceCullingDrawParams); } BuildMeshRenderingCommands(GraphBuilder, EMeshPass::BasePass, View, Scene->GPUScene, InstanceCullingManager, PassParameters->InstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, EMeshPass::SkyPass, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->SkyPassInstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, EMeshPass::DebugViewMode, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->DebugViewModeInstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, EMeshPass::MeshDecal_SceneColorAndGBuffer, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->MeshDecalSceneColorAndGBufferInstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, StandardTranslucencyMeshPass, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->TranslucencyInstanceCullingDrawParams); } GraphBuilder.AddPass( RDG_EVENT_NAME("SceneColorRendering"), PassParameters, // the second view pass should not be merged with the first view pass on mobile since the subpass would not work properly. ERDGPassFlags::Raster | ERDGPassFlags::NeverMerge, [this, ParameterCollection, ViewContext, bDoOcclusionQueires, MobileSSRQuality, &SortedLightSet, bUsingPixelLocalStorage](FRHICommandList& RHICmdList) { FViewInfo& View = *ViewContext.ViewInfo; auto PassParameters = &ParameterCollection->PassParameters; // Depth pre-pass RenderMaskedPrePass(RHICmdList, View, &ParameterCollection->DepthPassInstanceCullingDrawParams); // Opaque and masked RenderMobileBasePass(RHICmdList, View, &PassParameters->InstanceCullingDrawParams, &ParameterCollection->SkyPassInstanceCullingDrawParams); RenderMobileDebugView(RHICmdList, View, &ParameterCollection->DebugViewModeInstanceCullingDrawParams); PostRenderBasePass(RHICmdList, View); // SceneColor + GBuffer write, SceneDepth is read only RHICmdList.NextSubpass(); RenderDecals(RHICmdList, View, &ParameterCollection->MeshDecalSceneColorAndGBufferInstanceCullingDrawParams); // SceneColor write, SceneDepth is read only RHICmdList.NextSubpass(); MobileDeferredShadingPass(RHICmdList, ViewContext.ViewIndex, Views.Num(), View, *Scene, SortedLightSet, VisibleLightInfos, MobileSSRQuality); if (bUsingPixelLocalStorage) { MobileDeferredCopyBuffer(RHICmdList, View); } RenderFog(RHICmdList, View); // Draw translucency. RenderTranslucency(RHICmdList, View, Views, StandardTranslucencyPass, StandardTranslucencyMeshPass, &ParameterCollection->TranslucencyInstanceCullingDrawParams); if (bDoOcclusionQueires) { // Issue occlusion queries RenderOcclusion(RHICmdList); } }); } } void FMobileSceneRenderer::RenderDeferredMultiPass(FRDGBuilder& GraphBuilder, FSceneTextures& SceneTextures, const FSortedLightSetSceneInfo& SortedLightSet, FDBufferTextures& DBufferTextures, FInstanceCullingManager& InstanceCullingManager) { FColorTargets ColorTargets = GetColorTargets_Deferred(SceneTextures); FRenderTargetBindingSlots BasePassRenderTargets = InitRenderTargetBindings_Deferred(SceneTextures, ColorTargets); const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder); FRenderViewContextArray RenderViews; GetRenderViews(Views, RenderViews); for (FRenderViewContext& ViewContext : RenderViews) { FViewInfo& View = *ViewContext.ViewInfo; SCOPED_GPU_MASK(GraphBuilder.RHICmdList, !View.IsInstancedStereoPass() ? View.GPUMask : (View.GPUMask | View.GetInstancedView()->GPUMask)); SCOPED_CONDITIONAL_DRAW_EVENTF(GraphBuilder.RHICmdList, EventView, RenderViews.Num() > 1, TEXT("View%d"), ViewContext.ViewIndex); View.BeginRenderView(); struct FDeferredMultiPassParameterCollection { FMobileRenderPassParameters PassParameters; FInstanceCullingDrawParams DepthPassInstanceCullingDrawParams; FInstanceCullingDrawParams SkyPassInstanceCullingDrawParams; FInstanceCullingDrawParams DebugViewModeInstanceCullingDrawParams; FInstanceCullingDrawParams MeshDecalSceneColorAndGBufferInstanceCullingDrawParams; }; auto ParameterCollection = GraphBuilder.AllocParameters(); auto PassParameters = &ParameterCollection->PassParameters; PassParameters->View = View.GetShaderParameters(); EMobileSceneTextureSetupMode SetupMode = bIsFullDepthPrepassEnabled ? EMobileSceneTextureSetupMode::SceneDepth : EMobileSceneTextureSetupMode::None; FMobileBasePassTextures MobileBasePassTextures{}; MobileBasePassTextures.DBufferTextures = DBufferTextures; PassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::Opaque, SetupMode, MobileBasePassTextures); PassParameters->RenderTargets = BasePassRenderTargets; if (!ViewContext.bIsFirstView) { // Load targets for a non-first view for (int32 i = 0; i < ColorTargets.Num(); ++i) { PassParameters->RenderTargets[i].SetLoadAction(ERenderTargetLoadAction::ELoad); } PassParameters->RenderTargets.DepthStencil.SetDepthLoadAction(ERenderTargetLoadAction::ELoad); PassParameters->RenderTargets.DepthStencil.SetStencilLoadAction(ERenderTargetLoadAction::ELoad); PassParameters->RenderTargets.DepthStencil.SetDepthStencilAccess(bIsFullDepthPrepassEnabled ? FExclusiveDepthStencil::DepthRead_StencilWrite : FExclusiveDepthStencil::DepthWrite_StencilWrite); } const bool bDoOcclusionQueires = (!bIsFullDepthPrepassEnabled && ViewContext.bIsLastView && DoOcclusionQueries()); PassParameters->RenderTargets.NumOcclusionQueries = bDoOcclusionQueires ? ComputeNumOcclusionQueriesToBatch() : 0u; if (Scene->GPUScene.IsEnabled()) { if (!bIsFullDepthPrepassEnabled) { BuildMeshRenderingCommands(GraphBuilder, EMeshPass::DepthPass, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->DepthPassInstanceCullingDrawParams); } BuildMeshRenderingCommands(GraphBuilder, EMeshPass::BasePass, View, Scene->GPUScene, InstanceCullingManager, PassParameters->InstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, EMeshPass::SkyPass, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->SkyPassInstanceCullingDrawParams); BuildMeshRenderingCommands(GraphBuilder, EMeshPass::DebugViewMode, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->DebugViewModeInstanceCullingDrawParams); if (bIsFullDepthPrepassEnabled) { BuildMeshRenderingCommands(GraphBuilder, EMeshPass::MeshDecal_SceneColorAndGBuffer, View, Scene->GPUScene, InstanceCullingManager, ParameterCollection->MeshDecalSceneColorAndGBufferInstanceCullingDrawParams); } } GraphBuilder.AddPass( RDG_EVENT_NAME("BasePass"), PassParameters, ERDGPassFlags::Raster, [this, ParameterCollection, ViewContext, &SceneTextures, bDoOcclusionQueires](FRHICommandList& RHICmdList) { auto PassParameters = &ParameterCollection->PassParameters; FViewInfo& View = *ViewContext.ViewInfo; // Depth pre-pass RenderMaskedPrePass(RHICmdList, View, &ParameterCollection->DepthPassInstanceCullingDrawParams); // Opaque and masked RenderMobileBasePass(RHICmdList, View, &PassParameters->InstanceCullingDrawParams, &ParameterCollection->SkyPassInstanceCullingDrawParams); RenderMobileDebugView(RHICmdList, View, &ParameterCollection->DebugViewModeInstanceCullingDrawParams); PostRenderBasePass(RHICmdList, View); if (bDoOcclusionQueires) { // Issue occlusion queries RenderOcclusion(RHICmdList); } if (bIsFullDepthPrepassEnabled) { RenderDecals(RHICmdList, View, &ParameterCollection->MeshDecalSceneColorAndGBufferInstanceCullingDrawParams); } }); } if (bShouldRenderHZB && !bIsFullDepthPrepassEnabled) { RenderHZB(GraphBuilder, SceneTextures.Depth.Target); } BasePassRenderTargets.Enumerate([](FRenderTargetBinding& RenderTarget) { RenderTarget.SetLoadAction(ERenderTargetLoadAction::ELoad); }); BasePassRenderTargets.DepthStencil.SetDepthLoadAction(ERenderTargetLoadAction::ELoad); BasePassRenderTargets.DepthStencil.SetStencilLoadAction(ERenderTargetLoadAction::ELoad); BasePassRenderTargets.DepthStencil.SetDepthStencilAccess(FExclusiveDepthStencil::DepthRead_StencilWrite); // Decals if (!bIsFullDepthPrepassEnabled) { for (FRenderViewContext& ViewContext : RenderViews) { FViewInfo& View = *ViewContext.ViewInfo; SCOPED_GPU_MASK(GraphBuilder.RHICmdList, !View.IsInstancedStereoPass() ? View.GPUMask : (View.GPUMask | View.GetInstancedView()->GPUMask)); SCOPED_CONDITIONAL_DRAW_EVENTF(GraphBuilder.RHICmdList, EventView, RenderViews.Num() > 1, TEXT("View%d"), ViewContext.ViewIndex); View.BeginRenderView(); auto PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.GetShaderParameters(); PassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::Opaque, EMobileSceneTextureSetupMode::SceneDepth); PassParameters->RenderTargets = BasePassRenderTargets; if (Scene->GPUScene.IsEnabled()) { BuildMeshRenderingCommands(GraphBuilder, EMeshPass::MeshDecal_SceneColorAndGBuffer, View, Scene->GPUScene, InstanceCullingManager, PassParameters->InstanceCullingDrawParams); } GraphBuilder.AddPass( RDG_EVENT_NAME("Decals"), PassParameters, ERDGPassFlags::Raster, [this, PassParameters, &View](FRHICommandList& RHICmdList) { RenderDecals(RHICmdList, View, &PassParameters->InstanceCullingDrawParams); }); } } TArray DynamicBentNormalAOTextures; if(bEnableDistanceFieldAO) { SceneTextures.MobileSetupMode = EMobileSceneTextureSetupMode::SceneDepth | EMobileSceneTextureSetupMode::GBuffers; SceneTextures.MobileUniformBuffer = CreateMobileSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, SceneTextures.MobileSetupMode); FDistanceFieldAOParameters Parameters(Scene->SkyLight->OcclusionMaxDistance, Scene->SkyLight->Contrast); RenderDistanceFieldLighting(GraphBuilder, SceneTextures, Parameters, DynamicBentNormalAOTextures, false, false); } // Lighting and translucency uint32 ViewIndex = 0; for (FRenderViewContext& ViewContext : RenderViews) { FViewInfo& View = *ViewContext.ViewInfo; const uint32 CurrentViewIndex = ViewIndex++; SCOPED_GPU_MASK(GraphBuilder.RHICmdList, !View.IsInstancedStereoPass() ? View.GPUMask : (View.GPUMask | View.GetInstancedView()->GPUMask)); SCOPED_CONDITIONAL_DRAW_EVENTF(GraphBuilder.RHICmdList, EventView, RenderViews.Num() > 1, TEXT("View%d"), ViewContext.ViewIndex); View.BeginRenderView(); UpdateDirectionalLightUniformBuffers(GraphBuilder, View); FRDGTextureRef DynamicBentNormalAOTexture = DynamicBentNormalAOTextures.IsEmpty() ? nullptr : DynamicBentNormalAOTextures[CurrentViewIndex];; auto* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.GetShaderParameters(); EMobileSceneTextureSetupMode SetupMode = EMobileSceneTextureSetupMode::SceneDepth | EMobileSceneTextureSetupMode::CustomDepth | EMobileSceneTextureSetupMode::GBuffers; PassParameters->MobileBasePass = CreateMobileBasePassUniformBuffer(GraphBuilder, View, EMobileBasePass::Translucent, SetupMode); PassParameters->ReflectionCapture = View.MobileReflectionCaptureUniformBuffer; PassParameters->LocalFogVolumeInstances = View.LocalFogVolumeViewData.GPUInstanceDataBufferSRV; PassParameters->LocalFogVolumeTileDrawIndirectBuffer = View.LocalFogVolumeViewData.GPUTileDrawIndirectBuffer; PassParameters->LocalFogVolumeTileDataTexture = View.LocalFogVolumeViewData.TileDataTextureArraySRV; PassParameters->LocalFogVolumeTileDataBuffer = View.LocalFogVolumeViewData.GPUTileDataBufferSRV; PassParameters->HalfResLocalFogVolumeViewSRV = View.LocalFogVolumeViewData.HalfResLocalFogVolumeViewSRV; PassParameters->HalfResLocalFogVolumeDepthSRV = View.LocalFogVolumeViewData.HalfResLocalFogVolumeDepthSRV; PassParameters->BentNormalAOTexture = DynamicBentNormalAOTexture; // Only SceneColor and Depth PassParameters->RenderTargets[0] = BasePassRenderTargets[0]; PassParameters->RenderTargets.DepthStencil = BasePassRenderTargets.DepthStencil; if (Scene->GPUScene.IsEnabled()) { BuildMeshRenderingCommands(GraphBuilder, StandardTranslucencyMeshPass, View, Scene->GPUScene, InstanceCullingManager, PassParameters->InstanceCullingDrawParams); } const EMobileSSRQuality MobileSSRQuality = ActiveMobileSSRQuality(View, bShouldRenderVelocities); GraphBuilder.AddPass( RDG_EVENT_NAME("LightingAndTranslucency"), PassParameters, ERDGPassFlags::Raster, [this, PassParameters, ViewContext, MobileSSRQuality, &SceneTextures, &SortedLightSet](FRHICommandList& RHICmdList) { FViewInfo& View = *ViewContext.ViewInfo; MobileDeferredShadingPass(RHICmdList, ViewContext.ViewIndex, Views.Num(), View, *Scene, SortedLightSet, VisibleLightInfos, MobileSSRQuality, PassParameters->BentNormalAOTexture); RenderFog(RHICmdList, View); // Draw translucency. RenderTranslucency(RHICmdList, View, Views, StandardTranslucencyPass, StandardTranslucencyMeshPass, &PassParameters->InstanceCullingDrawParams); }); } } void FMobileSceneRenderer::PostRenderBasePass(FRHICommandList& RHICmdList, FViewInfo& View) { if (ViewFamily.ViewExtensions.Num() > 1) { CSV_SCOPED_TIMING_STAT_EXCLUSIVE(ViewExtensionPostRenderBasePass); QUICK_SCOPE_CYCLE_COUNTER(STAT_FMobileSceneRenderer_ViewExtensionPostRenderBasePass); for (int32 ViewExt = 0; ViewExt < ViewFamily.ViewExtensions.Num(); ++ViewExt) { ViewFamily.ViewExtensions[ViewExt]->PostRenderBasePassMobile_RenderThread(RHICmdList, View); } } } void FMobileSceneRenderer::RenderMobileDebugView(FRHICommandList& RHICmdList, const FViewInfo& View, const FInstanceCullingDrawParams* DebugViewModeInstanceCullingDrawParams) { #if WITH_DEBUG_VIEW_MODES if (ViewFamily.UseDebugViewPS()) { CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderDebugView); SCOPED_DRAW_EVENT(RHICmdList, MobileDebugView); SCOPE_CYCLE_COUNTER(STAT_BasePassDrawTime); // Here we use the base pass depth result to get z culling for opaque and masque. // The color needs to be cleared at this point since shader complexity renders in additive. DrawClearQuad(RHICmdList, FLinearColor::Black); RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1); if (auto* Pass = View.ParallelMeshDrawCommandPasses[EMeshPass::DebugViewMode]) { Pass->Draw(RHICmdList, DebugViewModeInstanceCullingDrawParams); } } #endif // WITH_DEBUG_VIEW_MODES } int32 FMobileSceneRenderer::ComputeNumOcclusionQueriesToBatch() const { int32 NumQueriesForBatch = 0; for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { const FViewInfo& View = Views[ViewIndex]; const FSceneViewState* ViewState = (FSceneViewState*)View.State; #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (!ViewState || (!ViewState->bIsFrozen)) #endif { NumQueriesForBatch += View.IndividualOcclusionQueries.GetNumBatchOcclusionQueries(); NumQueriesForBatch += View.GroupedOcclusionQueries.GetNumBatchOcclusionQueries(); } } return NumQueriesForBatch; } // Whether we need a separate render-passes for translucency, decals etc bool FMobileSceneRenderer::RequiresMultiPass(int32 NumMSAASamples, EShaderPlatform ShaderPlatform) { // Vulkan uses subpasses if (IsVulkanPlatform(ShaderPlatform)) { return false; } // All iOS support frame_buffer_fetch if (IsMetalMobilePlatform(ShaderPlatform) && GSupportsShaderFramebufferFetch) { return false; } // Some Androids support frame_buffer_fetch if (IsAndroidOpenGLESPlatform(ShaderPlatform) && (GSupportsShaderFramebufferFetch || GSupportsShaderDepthStencilFetch)) { return false; } // Only Vulkan, iOS and some GL can do a single pass deferred shading, otherwise multipass if (IsMobileDeferredShadingEnabled(ShaderPlatform)) { return true; } // Always render LDR in single pass if (!IsMobileHDR() && !IsSimulatedPlatform(ShaderPlatform)) { return false; } // MSAA depth can't be sampled or resolved, unless we are on PC (no vulkan) if (NumMSAASamples > 1 && !IsSimulatedPlatform(ShaderPlatform)) { return false; } return true; } void FMobileSceneRenderer::UpdateDirectionalLightUniformBuffers(FRDGBuilder& GraphBuilder, const FViewInfo& View) { if (CachedView == &View) { return; } CachedView = &View; AddPass(GraphBuilder, RDG_EVENT_NAME("UpdateDirectionalLightUniformBuffers"), [this, &View](FRHICommandListImmediate& RHICmdList) { const bool bDynamicShadows = ViewFamily.EngineShowFlags.DynamicShadows; // Fill in the other entries based on the lights for (int32 ChannelIdx = 0; ChannelIdx < UE_ARRAY_COUNT(Scene->MobileDirectionalLights); ChannelIdx++) { FMobileDirectionalLightShaderParameters Params; SetupMobileDirectionalLightUniformParameters(*Scene, View, VisibleLightInfos, ChannelIdx, bDynamicShadows, Params); Scene->UniformBuffers.MobileDirectionalLightUniformBuffers[ChannelIdx + 1].UpdateUniformBufferImmediate(RHICmdList, Params); } }); } void FMobileSceneRenderer::UpdateSkyReflectionUniformBuffer(FRHICommandListBase& RHICmdList) { FSkyLightSceneProxy* SkyLight = nullptr; if (Scene->SkyLight && ( ( Scene->SkyLight->ProcessedTexture && Scene->SkyLight->ProcessedTexture->TextureRHI // Don't use skylight reflection if it is a static sky light for keeping coherence with PC. && !Scene->SkyLight->bHasStaticLighting ) || Scene->CanSampleSkyLightRealTimeCaptureData() )) { SkyLight = Scene->SkyLight; } // Make sure we don't try to use the skylight when doing a scene capture since it might contain uninitialized data if (ViewFamily.EngineShowFlags.SkyLighting == 0 && Views.Num() > 0 && Views[0].bIsReflectionCapture) { SkyLight = nullptr; } FMobileReflectionCaptureShaderParameters Parameters; SetupMobileSkyReflectionUniformParameters(Scene, SkyLight, Parameters); Scene->UniformBuffers.MobileSkyReflectionUniformBuffer.UpdateUniformBufferImmediate(RHICmdList, Parameters); } class FPreTonemapMSAA_Mobile : public FGlobalShader { DECLARE_SHADER_TYPE(FPreTonemapMSAA_Mobile, Global); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsMetalMobilePlatform(Parameters.Platform); } FPreTonemapMSAA_Mobile() {} public: FPreTonemapMSAA_Mobile(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { } }; IMPLEMENT_SHADER_TYPE(, FPreTonemapMSAA_Mobile,TEXT("/Engine/Private/PostProcessMobile.usf"),TEXT("PreTonemapMSAA_Mobile"),SF_Pixel); void FMobileSceneRenderer::PreTonemapMSAA(FRHICommandList& RHICmdList, const FMinimalSceneTextures& SceneTextures) { // iOS only bool bOnChipPP = GSupportsRenderTargetFormat_PF_FloatRGBA && GSupportsShaderFramebufferFetch && ViewFamily.EngineShowFlags.PostProcessing; bool bOnChipPreTonemapMSAA = bOnChipPP && IsMetalMobilePlatform(ViewFamily.GetShaderPlatform()) && (NumMSAASamples > 1); if (!bOnChipPreTonemapMSAA || bGammaSpace) { return; } const FIntPoint TargetSize = SceneTextures.Config.Extent; const auto ShaderMap = GetGlobalShaderMap(FeatureLevel); TShaderMapRef VertexShader(ShaderMap); TShaderMapRef PixelShader(ShaderMap); FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); RHICmdList.SetViewport(0, 0, 0.0f, TargetSize.X, TargetSize.Y, 1.0f); DrawRectangle( RHICmdList, 0, 0, TargetSize.X, TargetSize.Y, 0, 0, TargetSize.X, TargetSize.Y, TargetSize, TargetSize, VertexShader, EDRF_UseTriangleOptimization); } bool FMobileSceneRenderer::ShouldRenderHZB(TArrayView InViews) { static const auto MobileAmbientOcclusionTechniqueCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.AmbientOcclusionTechnique")); // Mobile SSAO requests HZB bool bIsFeatureRequested = bRequiresAmbientOcclusionPass && MobileAmbientOcclusionTechniqueCVar->GetValueOnRenderThread() == 1; // Instance occlusion culling requires HZB if (FInstanceCullingContext::IsOcclusionCullingEnabled()) { bIsFeatureRequested = true; } bool bNeedsHZB = bIsFeatureRequested; if (!bNeedsHZB) { for (const FViewInfo& View : InViews) { if (IsMobileSSREnabled(View)) { bNeedsHZB = true; break; } } } return bNeedsHZB; } void FMobileSceneRenderer::RenderHZB(FRHICommandListImmediate& RHICmdList, const TRefCountPtr& SceneDepthZ) { checkSlow(bShouldRenderHZB); FRDGBuilder GraphBuilder(RHICmdList); { FRDGTextureRef SceneDepthTexture = GraphBuilder.RegisterExternalTexture(SceneDepthZ, TEXT("SceneDepthTexture")); RenderHZB(GraphBuilder, SceneDepthTexture); } GraphBuilder.Execute(); } void FMobileSceneRenderer::RenderHZB(FRDGBuilder& GraphBuilder, FRDGTextureRef SceneDepthTexture) { RDG_EVENT_SCOPE_STAT(GraphBuilder, HZB, "HZB"); RDG_GPU_STAT_SCOPE(GraphBuilder, HZB); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { FViewInfo& View = Views[ViewIndex]; if (View.ShouldRenderView()) { RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask); { RDG_EVENT_SCOPE(GraphBuilder, "BuildHZB(ViewId=%d)", ViewIndex); FRDGTextureRef FurthestHZBTexture = nullptr; BuildHZBFurthest( GraphBuilder, SceneDepthTexture, /* VisBufferTexture = */ nullptr, View.ViewRect, View.GetFeatureLevel(), View.GetShaderPlatform(), TEXT("MobileHZBFurthest"), &FurthestHZBTexture); View.HZBMipmap0Size = FurthestHZBTexture->Desc.Extent; View.HZB = FurthestHZBTexture; if (View.ViewState) { if (FInstanceCullingContext::IsOcclusionCullingEnabled() || (AreMobileScreenSpaceReflectionsEnabled(ShaderPlatform) && !bIsFullDepthPrepassEnabled)) { GraphBuilder.QueueTextureExtraction(FurthestHZBTexture, &View.ViewState->PrevFrameViewInfo.HZB); } else { View.ViewState->PrevFrameViewInfo.HZB = nullptr; } } } if (Scene->InstanceCullingOcclusionQueryRenderer && View.ViewState) { // Render per-instance occlusion queries and save the mask to interpret results on the next frame const uint32 OcclusionQueryMaskForThisView = Scene->InstanceCullingOcclusionQueryRenderer->Render(GraphBuilder, Scene->GPUScene, View); View.ViewState->PrevFrameViewInfo.InstanceOcclusionQueryMask = OcclusionQueryMaskForThisView; } } } } bool FMobileSceneRenderer::AllowSimpleLights() const { return FSceneRenderer::AllowSimpleLights() && bSupportsSimpleLights; }