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

799 lines
32 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CustomDepthRendering.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "SceneUtils.h"
#include "DepthRendering.h"
#include "SceneRendering.h"
#include "SceneCore.h"
#include "ScenePrivate.h"
#include "Materials/Material.h"
#include "MeshPassProcessor.inl"
#include "UnrealEngine.h"
#include "Nanite/NaniteComposition.h"
static TAutoConsoleVariable<int32> CVarCustomDepthOrder(
TEXT("r.CustomDepth.Order"),
2,
TEXT("When CustomDepth (and CustomStencil) is getting rendered\n")
TEXT(" 0: Before Base Pass (Allows samping in DBuffer pass. Can be more efficient with AsyncCompute.)\n")
TEXT(" 1: After Base Pass\n")
TEXT(" 2: Default (Before Base Pass if DBuffer enabled.)\n"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarCustomDepthTemporalAAJitter(
TEXT("r.CustomDepthTemporalAAJitter"),
1,
TEXT("If disabled the Engine will remove the TemporalAA Jitter from the Custom Depth Pass. Only has effect when TemporalAA is used."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<bool> CVarCustomDepthEnableFastClear(
TEXT("r.CustomDepthEnableFastClear"), false,
TEXT("Enable HTile on the custom depth buffer (default:false).\n"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarPSOPrecacheCustomDepth(
TEXT("r.PSOPrecache.CustomDepth"),
1,
TEXT("Also Precache PSOs with for custom depth pass.") \
TEXT(" 0: No PSOs are compiled for this pass.\n") \
TEXT(" 1: PSOs are compiled for all primitives which explicitly request custom depth rendering (default).\n") \
TEXT(" 2: PSOs are compiled for all primitives which also request regular depth rendering.\n"),
ECVF_ReadOnly
);
DECLARE_DWORD_COUNTER_STAT(TEXT("Nanite Custom Depth Instances"), STAT_NaniteCustomDepthInstances, STATGROUP_Nanite);
DECLARE_GPU_DRAWCALL_STAT_NAMED(CustomDepth, TEXT("Custom Depth"));
using FNaniteCustomDepthDrawList = TArray<Nanite::FInstanceDraw, SceneRenderingAllocator>;
ECustomDepthPassLocation GetCustomDepthPassLocation(EShaderPlatform Platform)
{
const int32 CustomDepthOrder = CVarCustomDepthOrder.GetValueOnRenderThread();
const bool bCustomDepthBeforeBasePase = CustomDepthOrder == 0 || (CustomDepthOrder == 2 && IsUsingDBuffers(Platform));
return bCustomDepthBeforeBasePase ? ECustomDepthPassLocation::BeforeBasePass : ECustomDepthPassLocation::AfterBasePass;
}
bool IsCustomDepthPassWritingStencil()
{
return GetCustomDepthMode() == ECustomDepthMode::EnabledWithStencil;
}
FCustomDepthTextures FCustomDepthTextures::Create(FRDGBuilder& GraphBuilder, FIntPoint CustomDepthExtent, EShaderPlatform ShaderPlatform, bool bRequireMultiView, uint16 MobileMultiViewRenderTargetNumLayers)
{
if (!IsCustomDepthPassEnabled())
{
return {};
}
const bool bWritesCustomStencil = IsCustomDepthPassWritingStencil();
FCustomDepthTextures CustomDepthTextures;
ETextureCreateFlags CreateFlags = GFastVRamConfig.CustomDepth | TexCreate_DepthStencilTargetable | TexCreate_ShaderResource;
// For Nanite, check to create the depth texture as a UAV and force HTILE.
if (UseNanite(ShaderPlatform) && UseComputeDepthExport() && Nanite::GetSupportsCustomDepthRendering())
{
CreateFlags |= TexCreate_UAV;
}
else if (!CVarCustomDepthEnableFastClear.GetValueOnRenderThread())
{
CreateFlags |= TexCreate_NoFastClear;
}
FRDGTextureDesc CustomDepthDesc = FRDGTextureDesc::CreateRenderTargetTextureDesc(CustomDepthExtent, PF_DepthStencil, FClearValueBinding::DepthFar, CreateFlags, bRequireMultiView, MobileMultiViewRenderTargetNumLayers);
CustomDepthTextures.Depth = GraphBuilder.CreateTexture(CustomDepthDesc, TEXT("CustomDepth"));
CustomDepthTextures.DepthAction = ERenderTargetLoadAction::EClear;
CustomDepthTextures.StencilAction = bWritesCustomStencil ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ENoAction;
return CustomDepthTextures;
}
BEGIN_SHADER_PARAMETER_STRUCT(FCustomDepthPassParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static FViewShaderParameters CreateViewShaderParametersWithoutJitter(const FViewInfo& View, uint32 ViewIndex, const TArrayView<Nanite::FPackedView>& OutNaniteViews)
{
const auto SetupParameters = [](const FViewInfo& View, FViewUniformShaderParameters& Parameters)
{
FBox VolumeBounds[TVC_MAX];
FViewMatrices ModifiedViewMatrices = View.ViewMatrices;
ModifiedViewMatrices.HackRemoveTemporalAAProjectionJitter();
Parameters = *View.CachedViewUniformShaderParameters;
View.SetupUniformBufferParameters(ModifiedViewMatrices, ModifiedViewMatrices, VolumeBounds, TVC_MAX, Parameters);
};
const auto CopyIntoNaniteParameters = [&](uint32 ViewIndex, const FViewUniformShaderParameters& Parameters)
{
if (OutNaniteViews.IsValidIndex(ViewIndex))
{
OutNaniteViews[ViewIndex].TranslatedWorldToClip = Parameters.TranslatedWorldToClip;
OutNaniteViews[ViewIndex].ViewToClip = Parameters.ViewToClip;
OutNaniteViews[ViewIndex].ClipToRelativeWorld = Parameters.ClipToRelativeWorld;
}
};
FViewUniformShaderParameters ViewUniformParameters;
SetupParameters(View, ViewUniformParameters);
CopyIntoNaniteParameters(ViewIndex, ViewUniformParameters);
FViewShaderParameters Parameters;
Parameters.View = TUniformBufferRef<FViewUniformShaderParameters>::CreateUniformBufferImmediate(ViewUniformParameters, UniformBuffer_SingleFrame);
if (View.bShouldBindInstancedViewUB)
{
FInstancedViewUniformShaderParameters LocalInstancedViewUniformShaderParameters;
InstancedViewParametersUtils::CopyIntoInstancedViewParameters(LocalInstancedViewUniformShaderParameters, ViewUniformParameters, 0);
if (const FViewInfo* InstancedView = View.GetInstancedView())
{
SetupParameters(*InstancedView, ViewUniformParameters);
CopyIntoNaniteParameters(ViewIndex + 1, ViewUniformParameters);
InstancedViewParametersUtils::CopyIntoInstancedViewParameters(LocalInstancedViewUniformShaderParameters, ViewUniformParameters, 1);
}
Parameters.InstancedView = TUniformBufferRef<FInstancedViewUniformShaderParameters>::CreateUniformBufferImmediate(
LocalInstancedViewUniformShaderParameters,
UniformBuffer_SingleFrame);
}
return Parameters;
}
static FNaniteCustomDepthDrawList BuildNaniteCustomDepthDrawList(
const FViewInfo& View,
uint32 NumViews,
const FNaniteVisibilityResults* VisibilityResults)
{
FNaniteCustomDepthDrawList Output;
for (uint32 ViewId = 0; ViewId < NumViews; ++ViewId)
{
for (const FPrimitiveInstanceRange& InstanceRange : View.NaniteCustomDepthInstances)
{
if (!VisibilityResults || VisibilityResults->ShouldRenderCustomDepthPrimitive(InstanceRange.PrimitiveIndex))
{
const uint32 FirstOutputIndex = Output.Num();
Output.AddUninitialized(InstanceRange.NumInstances);
for (uint32 RelativeInstanceIndex = 0; RelativeInstanceIndex < uint32(InstanceRange.NumInstances); ++RelativeInstanceIndex)
{
const uint32 OutputIndex = FirstOutputIndex + RelativeInstanceIndex;
const uint32 InstanceId = InstanceRange.InstanceSceneDataOffset + RelativeInstanceIndex;
const Nanite::FInstanceDraw Draw { InstanceId, ViewId };
Output[OutputIndex] = Draw;
}
}
}
}
return MoveTemp(Output);
}
bool FSceneRenderer::RenderCustomDepthPass(
FRDGBuilder& GraphBuilder,
FCustomDepthTextures& CustomDepthTextures,
const FSceneTextureShaderParameters& SceneTextures,
TConstArrayView<Nanite::FRasterResults> PrimaryNaniteRasterResults,
TConstArrayView<Nanite::FPackedView> PrimaryNaniteViews)
{
if (!CustomDepthTextures.IsValid())
{
return false;
}
struct FTempViewParams
{
FViewShaderParameters ViewParams;
FNaniteCustomDepthDrawList NaniteDrawList;
};
TArray<FTempViewParams, FSceneRenderingArrayAllocator> TempViewParams;
TempViewParams.SetNum(Views.Num());
TArray<Nanite::FPackedView, FSceneRenderingArrayAllocator> TempNaniteViews;
TempNaniteViews.Append(PrimaryNaniteViews);
const bool bWriteCustomStencil = IsCustomDepthPassWritingStencil();
const bool bDrawSceneViewsInOneNanitePass = Views.Num() > 1 && Nanite::ShouldDrawSceneViewsInOneNanitePass(Views[0]);
const bool bRemoveTAAJitter = CVarCustomDepthTemporalAAJitter.GetValueOnRenderThread() == 0;
// Determine if any of the views have custom depth and if any of them have Nanite that is rendering custom depth
bool bAnyCustomDepth = false;
uint32 TotalNaniteInstances = 0;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
FViewInfo& View = Views[ViewIndex];
if (!View.ShouldRenderView() || !View.bHasCustomDepthPrimitives)
{
continue;
}
if (PrimaryNaniteRasterResults.IsValidIndex(ViewIndex))
{
FNaniteVisibilityQuery* VisibilityQuery = PrimaryNaniteRasterResults[ViewIndex].VisibilityQuery;
// Get the Nanite instance draw list for this view.
TempViewParams[ViewIndex].NaniteDrawList = BuildNaniteCustomDepthDrawList(
View,
bDrawSceneViewsInOneNanitePass ? Views.Num() : 1u,
Nanite::GetVisibilityResults(VisibilityQuery)
);
TotalNaniteInstances += TempViewParams[ViewIndex].NaniteDrawList.Num();
}
// User requested jitter-free custom depth.
if (bRemoveTAAJitter && IsTemporalAccumulationBasedMethod(View.AntiAliasingMethod))
{
TempViewParams[ViewIndex].ViewParams = CreateViewShaderParametersWithoutJitter(View, ViewIndex, TempNaniteViews);
}
else
{
TempViewParams[ViewIndex].ViewParams = View.GetShaderParameters();
}
bAnyCustomDepth = true;
}
SET_DWORD_STAT(STAT_NaniteCustomDepthInstances, TotalNaniteInstances);
if (!bAnyCustomDepth)
{
return false;
}
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderCustomDepthPass);
RDG_EVENT_SCOPE_STAT(GraphBuilder, CustomDepth, "CustomDepth");
RDG_GPU_STAT_SCOPE(GraphBuilder, CustomDepth);
// Render non-Nanite Custom Depth primitives
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
FViewInfo& View = Views[ViewIndex];
if (auto* Pass = View.ParallelMeshDrawCommandPasses[EMeshPass::CustomDepth]; Pass && View.ShouldRenderView() && View.bHasCustomDepthPrimitives)
{
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
View.BeginRenderView();
FCustomDepthPassParameters* PassParameters = GraphBuilder.AllocParameters<FCustomDepthPassParameters>();
PassParameters->SceneTextures = SceneTextures;
PassParameters->View = TempViewParams[ViewIndex].ViewParams;
const ERenderTargetLoadAction DepthLoadAction = GetLoadActionIfProduced(CustomDepthTextures.Depth, CustomDepthTextures.DepthAction);
const ERenderTargetLoadAction StencilLoadAction = (View.Family->ViewMode == VMI_VisualizeBuffer) ? ERenderTargetLoadAction::EClear : GetLoadActionIfProduced(CustomDepthTextures.Depth, CustomDepthTextures.StencilAction);
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(
CustomDepthTextures.Depth,
DepthLoadAction,
StencilLoadAction,
FExclusiveDepthStencil::DepthWrite_StencilWrite);
PassParameters->RenderTargets.MultiViewCount = (View.bIsMobileMultiViewEnabled) ? 2 : (View.Aspects.IsMobileMultiViewEnabled() ? 1 : 0);
Pass->BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
GraphBuilder.AddPass(
RDG_EVENT_NAME("CustomDepth"),
PassParameters,
ERDGPassFlags::Raster,
[&View, Pass, PassParameters](FRDGAsyncTask, FRHICommandList& RHICmdList)
{
SetStereoViewport(RHICmdList, View, 1.0f);
Pass->Draw(RHICmdList, &PassParameters->InstanceCullingDrawParams);
});
}
}
if (TotalNaniteInstances > 0)
{
RDG_EVENT_SCOPE(GraphBuilder, "Nanite CustomDepth");
const FIntPoint RasterTextureSize = CustomDepthTextures.Depth->Desc.Extent;
FIntRect RasterTextureRect(0, 0, RasterTextureSize.X, RasterTextureSize.Y);
if (Views.Num() == 1)
{
const FViewInfo& View = Views[0];
if (View.ViewRect.Min.X == 0 && View.ViewRect.Min.Y == 0)
{
RasterTextureRect = View.ViewRect;
}
}
Nanite::FSharedContext SharedContext{};
SharedContext.FeatureLevel = Scene->GetFeatureLevel();
SharedContext.ShaderMap = GetGlobalShaderMap(SharedContext.FeatureLevel);
SharedContext.Pipeline = Nanite::EPipeline::Primary;
// TODO: If !bWriteCustomStencil, we could copy off the depth and rasterize depth-only (probable optimization)
Nanite::FRasterContext RasterContext = Nanite::InitRasterContext(
GraphBuilder,
SharedContext,
ViewFamily,
RasterTextureSize,
RasterTextureRect,
Nanite::EOutputBufferMode::VisBuffer,
true, // bClearTarget
true, // bAsyncCompute
nullptr, // RectMinMaxBufferSRV
0, // NumRects
nullptr, // ExternalDepthBuffer
true // bCustomPass
);
Nanite::FCustomDepthContext CustomDepthContext = Nanite::InitCustomDepthStencilContext(
GraphBuilder,
CustomDepthTextures,
bWriteCustomStencil);
Nanite::FConfiguration CullingConfig = { 0 };
CullingConfig.bUpdateStreaming = true;
const int32 NumViewsToRender = bDrawSceneViewsInOneNanitePass ? 1 : Views.Num();
auto ViewArray = bDrawSceneViewsInOneNanitePass ?
Nanite::FPackedViewArray::Create(GraphBuilder, TempNaniteViews.Num(), MoveTemp(TempNaniteViews)) : nullptr;
for (int32 ViewIndex = 0; ViewIndex < NumViewsToRender; ++ViewIndex)
{
if (TempViewParams[ViewIndex].NaniteDrawList.Num() == 0)
{
continue;
}
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1 && !bDrawSceneViewsInOneNanitePass, "View%d", ViewIndex);
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1 && bDrawSceneViewsInOneNanitePass, "View%d (together with %d more)", ViewIndex, Views.Num() - 1);
FViewInfo& View = Views[ViewIndex];
FIntRect ViewRect = bDrawSceneViewsInOneNanitePass ? FIntRect(0, 0, FamilySize.X, FamilySize.Y) : View.ViewRect;
auto NaniteRenderer = Nanite::IRenderer::Create(
GraphBuilder,
*Scene,
View,
GetSceneUniforms(),
SharedContext,
RasterContext,
CullingConfig,
ViewRect,
/* PrevHZB = */ nullptr
);
if (!bDrawSceneViewsInOneNanitePass)
{
ViewArray = Nanite::FPackedViewArray::Create(GraphBuilder, TempNaniteViews[ViewIndex]);
}
NaniteRenderer->DrawGeometry(
Scene->NaniteRasterPipelines[ENaniteMeshPass::BasePass],
PrimaryNaniteRasterResults[ViewIndex].VisibilityQuery,
*ViewArray,
TempViewParams[ViewIndex].NaniteDrawList
);
Nanite::FRasterResults RasterResults;
NaniteRenderer->ExtractResults( RasterResults );
// Emit depth
Nanite::EmitCustomDepthStencilTargets(
GraphBuilder,
*Scene,
View,
bDrawSceneViewsInOneNanitePass,
RasterResults.PageConstants,
RasterResults.VisibleClustersSWHW,
RasterResults.ViewsBuffer,
RasterContext.VisBuffer64,
CustomDepthContext
);
}
Nanite::FinalizeCustomDepthStencil(GraphBuilder, CustomDepthContext, CustomDepthTextures);
}
else
{
const FSceneTexturesConfig& Config = FSceneTexturesConfig::Get();
// TextureView is not supported in GLES, so we can't lookup CustomDepth and CustomStencil from a single texture
// Do a copy of the CustomDepthStencil texture if CustomStencil is sampled in a shader.
if (IsOpenGLPlatform(ShaderPlatform))
{
if (Config.bSamplesCustomStencil)
{
FRDGTextureRef CustomStencil = GraphBuilder.CreateTexture(CustomDepthTextures.Depth->Desc, TEXT("CustomStencil"));
AddCopyTexturePass(GraphBuilder, CustomDepthTextures.Depth, CustomStencil);
CustomDepthTextures.Stencil = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateWithPixelFormat(CustomStencil, PF_X24_G8));
}
}
else
{
CustomDepthTextures.Stencil = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateWithPixelFormat(CustomDepthTextures.Depth, PF_X24_G8));
}
CustomDepthTextures.bSeparateStencilBuffer = false;
}
return true;
}
class FCustomDepthPassMeshProcessor : public FSceneRenderingAllocatorObject<FCustomDepthPassMeshProcessor>, public FMeshPassProcessor
{
public:
FCustomDepthPassMeshProcessor(const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext);
virtual void AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId = -1) override final;
virtual void CollectPSOInitializers(const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray<FPSOPrecacheData>& PSOInitializers) override final;
private:
bool TryAddMeshBatch(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
int32 StaticMeshId,
const FMaterialRenderProxy& MaterialRenderProxy,
const FMaterial& Material);
template<bool bPositionOnly>
bool Process(
const FMeshBatch& MeshBatch,
uint64 BatchElementMask,
int32 StaticMeshId,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode);
bool UseDefaultMaterial(const FMaterial& Material, bool bMaterialModifiesMeshPosition, bool bSupportPositionOnlyStream, bool bVFTypeSupportsNullPixelShader, bool& bPositionOnly, bool& bIgnoreThisMaterial);
void CollectDefaultMaterialPSOInitializers(
const FSceneTexturesConfig& SceneTexturesConfig,
const FMaterial& Material,
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
TArray<FPSOPrecacheData>& PSOInitializers);
template<bool bPositionOnly>
void CollectPSOInitializersInternal(
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
const FMaterial& RESTRICT MaterialResource,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode,
TArray<FPSOPrecacheData>& PSOInitializers);
FMeshPassProcessorRenderState PassDrawRenderState;
};
FCustomDepthPassMeshProcessor::FCustomDepthPassMeshProcessor(const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
: FMeshPassProcessor(EMeshPass::CustomDepth, Scene, FeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext)
{
PassDrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());
PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
}
void FCustomDepthPassMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId)
{
if (PrimitiveSceneProxy->ShouldRenderCustomDepth())
{
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
while (MaterialRenderProxy)
{
const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
if (Material)
{
if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material))
{
break;
}
}
MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel);
}
}
}
FRHIDepthStencilState* GetCustomDepthStencilState(bool bWriteCustomStencilValues, EStencilMask StencilWriteMask)
{
if (bWriteCustomStencilValues)
{
static FRHIDepthStencilState* StencilStates[EStencilMask::SM_Count] =
{
TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 255, 255>::GetRHI(),
TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Replace, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 255, 255>::GetRHI(),
TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Replace, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 255, 1>::GetRHI(),
TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Replace, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 255, 2>::GetRHI(),
TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Replace, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 255, 4>::GetRHI(),
TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Replace, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 255, 8>::GetRHI(),
TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Replace, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 255, 16>::GetRHI(),
TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Replace, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 255, 32>::GetRHI(),
TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Replace, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 255, 64>::GetRHI(),
TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Replace, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, 255, 128>::GetRHI()
};
checkSlow(EStencilMask::SM_Count == UE_ARRAY_COUNT(StencilStates));
return StencilStates[(int32)StencilWriteMask];
}
else
{
return TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI();
}
}
bool FCustomDepthPassMeshProcessor::UseDefaultMaterial(const FMaterial& Material, bool bMaterialModifiesMeshPosition, bool bSupportPositionOnlyStream, bool bVFTypeSupportsNullPixelShader, bool& bPositionOnly, bool& bIgnoreThisMaterial)
{
bool bUseDefaultMaterial = false;
bIgnoreThisMaterial = false;
const bool bIsOpaque = IsOpaqueBlendMode(Material);
const bool bIsTranslucent = IsTranslucentBlendMode(Material);
if (bIsOpaque
&& bSupportPositionOnlyStream
&& !bMaterialModifiesMeshPosition
&& Material.WritesEveryPixel(false, bVFTypeSupportsNullPixelShader))
{
bUseDefaultMaterial = true;
bPositionOnly = true;
}
else if (!bIsTranslucent || Material.IsTranslucencyWritingCustomDepth())
{
const bool bMaterialMasked = !Material.WritesEveryPixel(false, bVFTypeSupportsNullPixelShader) || Material.IsTranslucencyWritingCustomDepth();
if (!bMaterialMasked && !bMaterialModifiesMeshPosition)
{
bUseDefaultMaterial = true;
bPositionOnly = false;
}
}
else
{
// E.g., ignore translucent materials without allowing custom depth writes.
bIgnoreThisMaterial = true;
}
return bUseDefaultMaterial;
}
bool FCustomDepthPassMeshProcessor::TryAddMeshBatch(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
int32 StaticMeshId,
const FMaterialRenderProxy& MaterialRenderProxy,
const FMaterial& Material)
{
// Setup the depth stencil state
const bool bWriteCustomStencilValues = IsCustomDepthPassWritingStencil();
PassDrawRenderState.SetDepthStencilState(GetCustomDepthStencilState(bWriteCustomStencilValues, PrimitiveSceneProxy->GetStencilWriteMask()));
if (bWriteCustomStencilValues)
{
const uint32 CustomDepthStencilValue = PrimitiveSceneProxy->GetCustomDepthStencilValue();
PassDrawRenderState.SetStencilRef(CustomDepthStencilValue);
}
// Using default material?
bool bIgnoreThisMaterial = false;
bool bPositionOnly = false;
const bool bSupportPositionOnlyStream = MeshBatch.VertexFactory->SupportsPositionOnlyStream();
const bool bVFTypeSupportsNullPixelShader = MeshBatch.VertexFactory->SupportsNullPixelShader();
const bool bModifiesMeshPosition = DoMaterialAndPrimitiveModifyMeshPosition(Material, PrimitiveSceneProxy);
bool bUseDefaultMaterial = UseDefaultMaterial(Material, bModifiesMeshPosition, bSupportPositionOnlyStream, bVFTypeSupportsNullPixelShader, bPositionOnly, bIgnoreThisMaterial);
if (bIgnoreThisMaterial)
{
return true;
}
// Swap to default material
const FMaterialRenderProxy* EffectiveMaterialRenderProxy = &MaterialRenderProxy;
const FMaterial* EffectiveMaterial = &Material;
if (bUseDefaultMaterial)
{
// Override with the default material
EffectiveMaterialRenderProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
EffectiveMaterial = EffectiveMaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
check(EffectiveMaterial);
}
// Get the fill & cull mode
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings);
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(Material, OverrideSettings);
if (bPositionOnly)
{
return Process<true>(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, *EffectiveMaterialRenderProxy, *EffectiveMaterial, MeshFillMode, MeshCullMode);
}
else
{
return Process<false>(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, *EffectiveMaterialRenderProxy, *EffectiveMaterial, MeshFillMode, MeshCullMode);
}
}
template<bool bPositionOnly>
bool FCustomDepthPassMeshProcessor::Process(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
int32 StaticMeshId,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode)
{
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
TMeshProcessorShaders<
TDepthOnlyVS<bPositionOnly>,
FDepthOnlyPS> DepthPassShaders;
FShaderPipelineRef ShaderPipeline;
if (!GetDepthPassShaders<bPositionOnly>(
MaterialResource,
VertexFactory->GetType(),
FeatureLevel,
MaterialResource.MaterialUsesPixelDepthOffset_RenderThread(),
DepthPassShaders.VertexShader,
DepthPassShaders.PixelShader,
ShaderPipeline
))
{
return false;
}
FMeshMaterialShaderElementData ShaderElementData;
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false);
const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(DepthPassShaders.VertexShader, DepthPassShaders.PixelShader);
BuildMeshDrawCommands(
MeshBatch,
BatchElementMask,
PrimitiveSceneProxy,
MaterialRenderProxy,
MaterialResource,
PassDrawRenderState,
DepthPassShaders,
MeshFillMode,
MeshCullMode,
SortKey,
bPositionOnly ? EMeshPassFeatures::PositionOnly : EMeshPassFeatures::Default,
ShaderElementData);
return true;
}
void FCustomDepthPassMeshProcessor::CollectPSOInitializers(const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray<FPSOPrecacheData>& PSOInitializers)
{
int32 CustomDepthPrecacheMode = CVarPSOPrecacheCustomDepth.GetValueOnAnyThread();
if (CustomDepthPrecacheMode == 0)
{
return;
}
// Setup the depth stencil state to use
const bool bWriteCustomStencilValues = IsCustomDepthPassWritingStencil();
PassDrawRenderState.SetDepthStencilState(GetCustomDepthStencilState(bWriteCustomStencilValues, PreCacheParams.GetStencilWriteMask()));
// Are we currently collecting PSO's for the default material
if (PreCacheParams.bDefaultMaterial)
{
CollectDefaultMaterialPSOInitializers(SceneTexturesConfig, Material, VertexFactoryData, PSOInitializers);
return;
}
// assume we can always do this when collecting PSO's for now (vertex factory instance might actually not support it)
const bool bSupportPositionOnlyStream = VertexFactoryData.VertexFactoryType->SupportsPositionOnly();
const bool bVFTypeSupportsNullPixelShader = VertexFactoryData.VertexFactoryType->SupportsNullPixelShader();
bool bIgnoreThisMaterial = false;
bool bPositionOnly = false;
bool bUseDefaultMaterial = UseDefaultMaterial(Material, Material.MaterialModifiesMeshPosition_GameThread(), bSupportPositionOnlyStream, bVFTypeSupportsNullPixelShader, bPositionOnly, bIgnoreThisMaterial);
if (!bIgnoreThisMaterial)
{
const FMaterial* EffectiveMaterial = &Material;
if (bUseDefaultMaterial && !bSupportPositionOnlyStream && VertexFactoryData.CustomDefaultVertexDeclaration)
{
EMaterialQualityLevel::Type ActiveQualityLevel = GetCachedScalabilityCVars().MaterialQualityLevel;
EffectiveMaterial = UMaterial::GetDefaultMaterial(MD_Surface)->GetMaterialResource(FeatureLevel, ActiveQualityLevel);
bUseDefaultMaterial = false;
}
bool bPrecacheCustomDepth = PreCacheParams.bRenderCustomDepth;
// If requested precache for all primitives in depth pass as well
if (CustomDepthPrecacheMode == 2)
{
bPrecacheCustomDepth = bPrecacheCustomDepth || PreCacheParams.bRenderInDepthPass;
}
if (!bUseDefaultMaterial && bPrecacheCustomDepth)
{
check(!bPositionOnly);
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(PreCacheParams);
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings);
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(Material, OverrideSettings);
CollectPSOInitializersInternal<false>(VertexFactoryData, *EffectiveMaterial, MeshFillMode, MeshCullMode, PSOInitializers);
}
}
}
void FCustomDepthPassMeshProcessor::CollectDefaultMaterialPSOInitializers(
const FSceneTexturesConfig& SceneTexturesConfig,
const FMaterial& Material,
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
TArray<FPSOPrecacheData>& PSOInitializers)
{
const ERasterizerFillMode MeshFillMode = FM_Solid;
// TODO: Should do this for each stencil write mask?
// Collect PSOs for all possible default material combinations
{
ERasterizerCullMode MeshCullMode = CM_None;
CollectPSOInitializersInternal<true>(VertexFactoryData, Material, MeshFillMode, MeshCullMode, PSOInitializers);
CollectPSOInitializersInternal<false>(VertexFactoryData, Material, MeshFillMode, MeshCullMode, PSOInitializers);
}
{
ERasterizerCullMode MeshCullMode = CM_CW;
CollectPSOInitializersInternal<true>(VertexFactoryData, Material, MeshFillMode, MeshCullMode, PSOInitializers);
CollectPSOInitializersInternal<false>(VertexFactoryData, Material, MeshFillMode, MeshCullMode, PSOInitializers);
}
{
ERasterizerCullMode MeshCullMode = CM_CCW;
CollectPSOInitializersInternal<true>(VertexFactoryData, Material, MeshFillMode, MeshCullMode, PSOInitializers);
CollectPSOInitializersInternal<false>(VertexFactoryData, Material, MeshFillMode, MeshCullMode, PSOInitializers);
}
}
template<bool bPositionOnly>
void FCustomDepthPassMeshProcessor::CollectPSOInitializersInternal(
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
const FMaterial& RESTRICT MaterialResource,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode,
TArray<FPSOPrecacheData>& PSOInitializers)
{
TMeshProcessorShaders<
TDepthOnlyVS<bPositionOnly>,
FDepthOnlyPS> DepthPassShaders;
FShaderPipelineRef ShaderPipeline;
if (!GetDepthPassShaders<bPositionOnly>(
MaterialResource,
VertexFactoryData.VertexFactoryType,
FeatureLevel,
MaterialResource.MaterialUsesPixelDepthOffset_GameThread(),
DepthPassShaders.VertexShader,
DepthPassShaders.PixelShader,
ShaderPipeline
))
{
return;
}
FGraphicsPipelineRenderTargetsInfo RenderTargetsInfo;
RenderTargetsInfo.NumSamples = 1;
ETextureCreateFlags CustomDepthStencilCreateFlags = GFastVRamConfig.CustomDepth | TexCreate_NoFastClear | TexCreate_DepthStencilTargetable | TexCreate_ShaderResource;
SetupDepthStencilInfo(PF_DepthStencil, CustomDepthStencilCreateFlags, ERenderTargetLoadAction::ELoad,
ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilWrite, RenderTargetsInfo);
AddGraphicsPipelineStateInitializer(
VertexFactoryData,
MaterialResource,
PassDrawRenderState,
RenderTargetsInfo,
DepthPassShaders,
MeshFillMode,
MeshCullMode,
PT_TriangleList,
bPositionOnly ? EMeshPassFeatures::PositionOnly : EMeshPassFeatures::Default,
true /*bRequired*/,
PSOInitializers);
}
FMeshPassProcessor* CreateCustomDepthPassProcessor(ERHIFeatureLevel::Type FeatureLevel, const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
{
return new FCustomDepthPassMeshProcessor(Scene, FeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext);
}
REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(RegisterCustomDepthPass, CreateCustomDepthPassProcessor, EShadingPath::Deferred, EMeshPass::CustomDepth, EMeshPassFlags::MainView);
REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(RegisterMobileCustomDepthPass, CreateCustomDepthPassProcessor, EShadingPath::Mobile, EMeshPass::CustomDepth, EMeshPassFlags::MainView);