Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/MobileShadingRenderer.cpp
2025-05-18 13:04:45 +08:00

2766 lines
116 KiB
C++

// 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<int32> 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<int32> 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<int32> 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<int32> 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<int32> 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<FPostProcessMaterialNode>(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<FSceneView> Views,
FSceneUniformBuffer &SceneUniformBuffer,
FFXSystemInterface* FXSystem,
TRDGUniformBufferRef<FMobileSceneTextureUniformParameters> 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<float4>, LocalFogVolumeInstances)
RDG_BUFFER_ACCESS(LocalFogVolumeTileDrawIndirectBuffer, ERHIAccess::IndirectArgs)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2DArray<uint>, LocalFogVolumeTileDataTexture)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, LocalFogVolumeTileDataBuffer)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D<float4>, HalfResLocalFogVolumeViewSRV)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D<float>, 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<FPostProcessMaterialNode>(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<FRenderViewContext, TInlineAllocator<2, SceneRenderingAllocator>>;
static void GetRenderViews(TArrayView<FViewInfo> 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<FMobileDirectionalLightShaderParameters>;
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
{
UniformBufferRHI =
MobileDirLightUniformBufferRef::CreateUniformBufferImmediate(
FMobileDirectionalLightShaderParameters(),
UniformBuffer_MultiFrame);
}
virtual void ReleaseRHI() override
{
UniformBufferRHI.SafeRelease();
}
MobileDirLightUniformBufferRef UniformBufferRHI;
};
TUniformBufferRef<FMobileDirectionalLightShaderParameters>& GetNullMobileDirectionalLightShaderParameters()
{
static TGlobalResource<FMobileDirLightShaderParamsRenderResource>* NullLightParams;
if (!NullLightParams)
{
NullLightParams = new TGlobalResource<FMobileDirLightShaderParamsRenderResource>();
}
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<FViewCommands> 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<int32, TInlineAllocator<2> > 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<FViewInfo*>(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<IPersistentViewUniformBufferExtension*> 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<FViewInfo>& 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<FViewInfo>& 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<FViewInfo> InViews, FSceneTextures& SceneTextures, FInstanceCullingManager& InstanceCullingManager, bool bIsSceneCaptureRenderPass)
{
FRenderViewContextArray RenderViews;
GetRenderViews(InViews, RenderViews);
TRDGUniformBufferBinding<FMobileBasePassUniformParameters> 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<FFullDepthPrepassParameterCollection>();
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<FMobileRenderPassParameters>();
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<FViewInfo> 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<FCustomPassParameterCollection>();
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<FMobileRenderPassParameters>();
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<FVirtualTextureUpdater> 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<FRendererViewDataManager>(GraphBuilder, *Scene, GetSceneUniforms(), AllViews);
FInstanceCullingManager& InstanceCullingManager = *GraphBuilder.AllocObject<FInstanceCullingManager>(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<FSortedLightSetSceneInfo>();
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<FHairStrandsBookmarkParameters>();
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<FMobileSceneTextureUniformParameters> OriginalSceneTextureUniformBuffer = SceneTextures.MobileUniformBuffer;
for (int32 i = 0; i < CustomRenderPassInfos.Num(); ++i)
{
FCustomRenderPassBase* CustomRenderPass = CustomRenderPassInfos[i].CustomRenderPass;
TArray<FViewInfo>& 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<FRDGTextureRef> 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<FRendererModule&>(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<FMobileRenderPassParameters>();
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<FForwardSinglePassParameterCollection>();
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<FMobileRenderPassParameters>();
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<FForwardFirstPassParameterCollection>();
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<FForwardSecondPassParameterCollection>();
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<class T>
void MobileDeferredCopyBuffer(FRHICommandList& RHICmdList, const FViewInfo& View)
{
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
TShaderMapRef<FPostProcessVS> VertexShader(View.ShaderMap);
TShaderMapRef<T> 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<FRDGTextureRef, TInlineAllocator<6>>& ColorTargets)
{
TArrayView<FRDGTextureRef> 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<FDeferredSinglePassParameterCollection>();
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<FMobileDeferredCopyPLSPS>(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<FDeferredMultiPassParameterCollection>();
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<FMobileRenderPassParameters>();
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<FRDGTextureRef> 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<FMobileRenderPassParameters>();
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<FScreenVS> VertexShader(ShaderMap);
TShaderMapRef<FPreTonemapMSAA_Mobile> PixelShader(ShaderMap);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero, CW_NONE>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::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<FViewInfo> 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<IPooledRenderTarget>& 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;
}