PostProcessSelectionOutline.usf
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Common.ush"
#include "ScreenPass.ush"
#if MSAA_SAMPLE_COUNT > 1
Texture2DMS<float, MSAA_SAMPLE_COUNT> EditorPrimitivesDepth;
Texture2DMS<uint2, MSAA_SAMPLE_COUNT> EditorPrimitivesStencil;
#else
// Note: opengl compiler doesn't like Texture2D<float>
Texture2D EditorPrimitivesDepth;
Texture2D<uint2> EditorPrimitivesStencil;
#endif
Texture2D ColorTexture;
SamplerState ColorSampler;
Texture2D DepthTexture;
SamplerState DepthSampler;
SCREEN_PASS_TEXTURE_VIEWPORT(Color)
SCREEN_PASS_TEXTURE_VIEWPORT(Depth)
SCREEN_PASS_TEXTURE_VIEWPORT_TRANSFORM(ColorToDepth)
float3 OutlineColor;
float3 SubduedOutlineColor;
float SelectionHighlightIntensity;
float BSPSelectionIntensity;
struct FPixelInfo
{
// if there is an selected object
bool bObjectMask;
int ObjectId;
// if the current pixel is not occluded by some other object in front of it
float fVisible;
// hard version of fVisible
bool bVisible;
};
// @param DeviceZPlane plane in screenspace .x: ddx(DeviceZ), y: ddy(DeviceZ) z:DeviceZ
float ReconstructDeviceZ(float3 DeviceZPlane, float2 PixelOffset)
{
return dot(DeviceZPlane, float3(PixelOffset.xy, 1));
}
// @param DeviceZPlane plane in screenspace .x: ddx(DeviceZ), y: ddy(DeviceZ) z:DeviceZ
FPixelInfo GetPixelInfo(int2 PixelPos, int SampleID, int OffsetX, int OffsetY, float3 DeviceZPlane, float2 DeviceZMinMax)
{
FPixelInfo Ret;
PixelPos += int2(OffsetX, OffsetY);
// more stable on the silhouette
float2 ReconstructionOffset = 0.5f - View.TemporalAAParams.zw;
#if MSAA_SAMPLE_COUNT > 1
float DeviceZ = EditorPrimitivesDepth.Load(PixelPos, SampleID).r;
int Stencil = EditorPrimitivesStencil.Load(PixelPos, SampleID) STENCIL_COMPONENT_SWIZZLE;
#if !COMPILER_GLSL && !PS4_PROFILE && !COMPILER_METAL && !COMPILER_HLSLCC
// not yet supported on OpenGL, slightly better quality
ReconstructionOffset += EditorPrimitivesDepth.GetSamplePosition(SampleID);
#endif
#else
float DeviceZ = EditorPrimitivesDepth.Load(int3(PixelPos, 0)).r;
int Stencil = EditorPrimitivesStencil.Load(int3(PixelPos, 0)) STENCIL_COMPONENT_SWIZZLE;
#endif
float SceneDeviceZ = ReconstructDeviceZ(DeviceZPlane, ReconstructionOffset);
// clamp SceneDeviceZ in (DeviceZMinMax.x .. DeviceZMinMax.z)
// this avoids flicking artifacts on the silhouette by limiting the depth reconstruction error
SceneDeviceZ = max(SceneDeviceZ, DeviceZMinMax.x);
SceneDeviceZ = min(SceneDeviceZ, DeviceZMinMax.y);
// test against far plane
Ret.bObjectMask = DeviceZ != 0.0f;
// outline even between multiple selected objects (best usability)
Ret.ObjectId = Stencil;
#if (COMPILER_GLSL == 1 && FEATURE_LEVEL < FEATURE_LEVEL_SM4) || (COMPILER_METAL && FEATURE_LEVEL < FEATURE_LEVEL_SM4)
// Stencil read in opengl is not supported on older versions
Ret.ObjectId = Ret.bObjectMask ? 2 : 0;
#endif
// Soft Bias with DeviceZ for best quality (larger bias than usual because SceneDeviceZ is only approximated)
const float DeviceDepthFade = 0.00005f;
// 2 to always bias over the current plane
Ret.fVisible = saturate(2.0f - (SceneDeviceZ - DeviceZ) / DeviceDepthFade);
Ret.bVisible = Ret.fVisible >= 0.5f;
return Ret;
}
bool BoolXor(bool bA, bool bB)
{
int a = (int)bA;
int b = (int)bB;
return (a ^ b) != 0;
}
// Computes min and max at once.
void MinMax(inout int2 Var, int Value)
{
Var.x = min(Var.x, Value);
Var.y = max(Var.y, Value);
}
float4 PerSample(int2 PixelPos, int SampleID, FPixelInfo CenterPixelInfo, float3 DeviceZPlane, float2 DeviceZMinMax)
{
float4 Color = 0;
// [0]:center, [1..4]:borders
FPixelInfo PixelInfo[5];
PixelInfo[0] = CenterPixelInfo;
// Diagonal cross is thicker than vertical/horizontal cross.
PixelInfo[1] = GetPixelInfo(PixelPos, SampleID, 1, 1, DeviceZPlane, DeviceZMinMax);
PixelInfo[2] = GetPixelInfo(PixelPos, SampleID, -1, -1, DeviceZPlane, DeviceZMinMax);
PixelInfo[3] = GetPixelInfo(PixelPos, SampleID, 1, -1, DeviceZPlane, DeviceZMinMax);
PixelInfo[4] = GetPixelInfo(PixelPos, SampleID, -1, 1, DeviceZPlane, DeviceZMinMax);
// With (.x != .y) we can detect a border around and between each object.
int2 BorderMinMax = int2(255,0);
{
MinMax(BorderMinMax, PixelInfo[1].ObjectId);
MinMax(BorderMinMax, PixelInfo[2].ObjectId);
MinMax(BorderMinMax, PixelInfo[3].ObjectId);
MinMax(BorderMinMax, PixelInfo[4].ObjectId);
}
int2 VisibleBorderMinMax = int2(255,0);
{
FLATTEN if (PixelInfo[1].bVisible) MinMax(VisibleBorderMinMax, PixelInfo[1].ObjectId);
FLATTEN if (PixelInfo[2].bVisible) MinMax(VisibleBorderMinMax, PixelInfo[2].ObjectId);
FLATTEN if (PixelInfo[3].bVisible) MinMax(VisibleBorderMinMax, PixelInfo[3].ObjectId);
FLATTEN if (PixelInfo[4].bVisible) MinMax(VisibleBorderMinMax, PixelInfo[4].ObjectId);
}
// this border around the object
bool bVisibleBorder = VisibleBorderMinMax.y != 0;
bool bBorder = BorderMinMax.x != BorderMinMax.y;
bool bInnerBorder = BorderMinMax.y == 4;
// moving diagonal lines
float PatternMask = ((PixelPos.x/2 + PixelPos.y/2) % 2) * 0.6f;
// the contants express the two opacity values we see when the primitive is hidden
float LowContrastPatternMask = lerp(0.2, 1.0f, PatternMask);
float3 SelectionColor;
FLATTEN if (VisibleBorderMinMax.x >= 128 )
{
SelectionColor = SubduedOutlineColor.rgb;
}
else
{
SelectionColor = OutlineColor.rgb;
}
FLATTEN if (bBorder)
{
FLATTEN if (bVisibleBorder)
{
// unoccluded border
Color = float4(SelectionColor, 1);
}
else
{
// occluded border
Color = lerp(float4(0, 0, 0, 0), float4(SelectionColor, 1), LowContrastPatternMask);
}
}
FLATTEN if (bInnerBorder)
{
// even occluded object is filled
// occluded object is rendered differently(flickers with TemporalAA)
float VisibleMask = lerp(PatternMask, 1.0f, PixelInfo[0].fVisible);
// darken occluded parts
Color = lerp(Color, float4(SelectionColor * VisibleMask, 1), SelectionHighlightIntensity);
}
// highlight inner part of the object
if (PixelInfo[0].bObjectMask)
{
float VisibleMask = lerp(PatternMask, 1.0f, PixelInfo[0].fVisible);
float InnerHighlightAmount = PixelInfo[0].ObjectId == 1 ? BSPSelectionIntensity : SelectionHighlightIntensity;
Color = lerp(Color, float4(SelectionColor, 1), VisibleMask * InnerHighlightAmount);
}
return Color;
}
void MainPS(
noperspective float4 UVAndScreenPos : TEXCOORD0,
float4 SvPosition : SV_POSITION,
out float4 OutColor : SV_Target0)
{
const float2 ColorUV = UVAndScreenPos.xy;
const int2 ColorPixelPos = int2(ColorUV * Color_Extent);
// Scout outwards from the center pixel to determine if any neighboring pixels are selected
const int ScoutStep = 2;
#if MSAA_SAMPLE_COUNT > 1
const int4 StencilScout = int4(
EditorPrimitivesStencil.Load(ColorPixelPos + int2( ScoutStep, 0), 0) STENCIL_COMPONENT_SWIZZLE,
EditorPrimitivesStencil.Load(ColorPixelPos + int2(-ScoutStep, 0), 0) STENCIL_COMPONENT_SWIZZLE,
EditorPrimitivesStencil.Load(ColorPixelPos + int2( 0, ScoutStep), 0) STENCIL_COMPONENT_SWIZZLE,
EditorPrimitivesStencil.Load(ColorPixelPos + int2( 0, -ScoutStep), 0) STENCIL_COMPONENT_SWIZZLE
);
#else
const int4 StencilScout = int4(
EditorPrimitivesStencil.Load(int3(ColorPixelPos + int2( ScoutStep, 0), 0)) STENCIL_COMPONENT_SWIZZLE,
EditorPrimitivesStencil.Load(int3(ColorPixelPos + int2(-ScoutStep, 0), 0)) STENCIL_COMPONENT_SWIZZLE,
EditorPrimitivesStencil.Load(int3(ColorPixelPos + int2( 0, ScoutStep), 0)) STENCIL_COMPONENT_SWIZZLE,
EditorPrimitivesStencil.Load(int3(ColorPixelPos + int2( 0, -ScoutStep), 0)) STENCIL_COMPONENT_SWIZZLE
);
#endif
const int ScoutSum = dot(saturate(StencilScout), 1);
// If this sum is zero, none of our neighbors are selected pixels and we can skip all the heavy processing.
if (ScoutSum > 0)
{
const float2 DepthUV = ColorUV * ColorToDepth_Scale + ColorToDepth_Bias;
float Center = Texture2DSampleLevel(DepthTexture, DepthSampler, DepthUV, 0).r;
float Left = Texture2DSampleLevel(DepthTexture, DepthSampler, DepthUV + float2(-1, 0) * Depth_ExtentInverse, 0).r;
float Right = Texture2DSampleLevel(DepthTexture, DepthSampler, DepthUV + float2( 1, 0) * Depth_ExtentInverse, 0).r;
float Top = Texture2DSampleLevel(DepthTexture, DepthSampler, DepthUV + float2( 0, -1) * Depth_ExtentInverse, 0).r;
float Bottom = Texture2DSampleLevel(DepthTexture, DepthSampler, DepthUV + float2( 0, 1) * Depth_ExtentInverse, 0).r;
// This allows to reconstruct depth with a small pixel offset without many texture lookups (4xMSAA * 5 neighbors -> 20 samples)
// It's an approximation assuming the surface is a plane.
float3 DeviceZPlane;
DeviceZPlane.x = (Right - Left) / 2;
DeviceZPlane.y = (Bottom - Top) / 2;
DeviceZPlane.z = Center;
float2 DeviceZMinMax;
DeviceZMinMax.x = min(Center, min(min(Left, Right), min(Top, Bottom)));
DeviceZMinMax.y = max(Center, max(max(Left, Right), max(Top, Bottom)));
FPixelInfo CenterPixelInfo = GetPixelInfo(ColorPixelPos, 0, 0, 0, DeviceZPlane, DeviceZMinMax);
float4 Sum = 0;
#if COMPILER_GLSL && FEATURE_LEVEL >= FEATURE_LEVEL_SM4
UNROLL
#endif
for(int SampleID = 0; SampleID < MSAA_SAMPLE_COUNT; ++SampleID)
{
Sum += PerSample(ColorPixelPos, SampleID, CenterPixelInfo, DeviceZPlane, DeviceZMinMax);
}
float4 Avg = Sum / MSAA_SAMPLE_COUNT;
OutColor = Texture2DSample(ColorTexture, ColorSampler, ColorUV);
// Scene color has gamma applied. Need to remove and re-apply after linear operation.
OutColor.rgb = pow(OutColor.rgb, 2.2f );
OutColor.rgb = lerp(OutColor.rgb, Avg.rgb, Avg.a);
OutColor.rgb = pow(OutColor.rgb, 1.0f / 2.2f);
}
else
{
OutColor = Texture2DSample(ColorTexture, ColorSampler, ColorUV);
}
}
PostProcessing.cpp
#if WITH_EDITOR
if (PassSequence.IsEnabled(EPass::SelectionOutline))
{
FSelectionOutlineInputs PassInputs;
PassSequence.AcceptOverrideIfLastPass(EPass::SelectionOutline, PassInputs.OverrideOutput);
PassInputs.SceneColor = SceneColor;
PassInputs.SceneDepth = SceneDepth;
PassInputs.SceneTextures.SceneTextures = Inputs.SceneTextures;
SceneColor = AddSelectionOutlinePass(GraphBuilder, View, PassInputs);
}
if (PassSequence.IsEnabled(EPass::EditorPrimitive))
{
FEditorPrimitiveInputs PassInputs;
PassSequence.AcceptOverrideIfLastPass(EPass::EditorPrimitive, PassInputs.OverrideOutput);
PassInputs.SceneColor = SceneColor;
PassInputs.SceneDepth = SceneDepth;
PassInputs.BasePassType = FEditorPrimitiveInputs::EBasePassType::Deferred;
SceneColor = AddEditorPrimitivePass(GraphBuilder, View, PassInputs);
}
#endif
PostProcessSelectionOutline.cpp
FScreenPassTexture AddSelectionOutlinePass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSelectionOutlineInputs& Inputs)
{
check(Inputs.SceneColor.IsValid());
check(Inputs.SceneDepth.IsValid());
RDG_EVENT_SCOPE(GraphBuilder, "EditorSelectionOutlines");
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(GraphBuilder.RHICmdList);
FPersistentUniformBuffers& SceneUniformBuffers = View.Family->Scene->GetRenderScene()->UniformBuffers;
const uint32 MsaaSampleCount = SceneContext.GetEditorMSAACompositingSampleCount();
// Patch uniform buffers with updated state for rendering the outline mesh draw commands.
const FViewInfo* EditorView = UpdateEditorPrimitiveView(SceneUniformBuffers, SceneContext, View, Inputs.SceneColor.ViewRect);
FRDGTextureRef DepthStencilTexture = nullptr;
// Generate custom depth / stencil for outline shapes.
{
{
FRDGTextureDesc DepthStencilDesc = Inputs.SceneColor.Texture->Desc;
DepthStencilDesc.Reset();
DepthStencilDesc.Format = PF_DepthStencil;
// This is a reversed Z depth surface, so 0.0f is the far plane.
DepthStencilDesc.ClearValue = FClearValueBinding((float)ERHIZBuffer::FarPlane, 0);
DepthStencilDesc.Flags = TexCreate_DepthStencilTargetable | TexCreate_ShaderResource;
DepthStencilDesc.NumSamples = MsaaSampleCount;
DepthStencilTexture = GraphBuilder.CreateTexture(DepthStencilDesc, TEXT("SelectionOutline"));
}
FSelectionOutlinePassParameters* PassParameters = GraphBuilder.AllocParameters<FSelectionOutlinePassParameters>();
PassParameters->SceneTextures = Inputs.SceneTextures;
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(
DepthStencilTexture,
ERenderTargetLoadAction::EClear,
ERenderTargetLoadAction::EClear,
FExclusiveDepthStencil::DepthWrite_StencilWrite);
const FScreenPassTextureViewport SceneColorViewport(Inputs.SceneColor);
GraphBuilder.AddPass(
RDG_EVENT_NAME("OutlineDepth %dx%d", SceneColorViewport.Rect.Width(), SceneColorViewport.Rect.Height()),
PassParameters,
ERDGPassFlags::Raster,
[&SceneUniformBuffers, EditorView, &View, SceneColorViewport](FRHICommandList& RHICmdList)
{
RHICmdList.SetViewport(SceneColorViewport.Rect.Min.X, SceneColorViewport.Rect.Min.Y, 0.0f, SceneColorViewport.Rect.Max.X, SceneColorViewport.Rect.Max.Y, 1.0f);
SceneUniformBuffers.UpdateViewUniformBufferImmediate(*EditorView->CachedViewUniformShaderParameters);
// Run selection pass on static elements
View.ParallelMeshDrawCommandPasses[EMeshPass::EditorSelection].DispatchDraw(nullptr, RHICmdList);
// to get an outline around the objects if it's partly outside of the screen
{
FIntRect InnerRect = SceneColorViewport.Rect;
// 1 as we have an outline that is that thick
InnerRect.InflateRect(-1);
// top
RHICmdList.SetScissorRect(true, SceneColorViewport.Rect.Min.X, SceneColorViewport.Rect.Min.Y, SceneColorViewport.Rect.Max.X, InnerRect.Min.Y);
DrawClearQuad(RHICmdList, false, FLinearColor(), true, (float)ERHIZBuffer::FarPlane, true, 0, SceneColorViewport.Extent, FIntRect());
// bottom
RHICmdList.SetScissorRect(true, SceneColorViewport.Rect.Min.X, InnerRect.Max.Y, SceneColorViewport.Rect.Max.X, SceneColorViewport.Rect.Max.Y);
DrawClearQuad(RHICmdList, false, FLinearColor(), true, (float)ERHIZBuffer::FarPlane, true, 0, SceneColorViewport.Extent, FIntRect());
// left
RHICmdList.SetScissorRect(true, SceneColorViewport.Rect.Min.X, SceneColorViewport.Rect.Min.Y, InnerRect.Min.X, SceneColorViewport.Rect.Max.Y);
DrawClearQuad(RHICmdList, false, FLinearColor(), true, (float)ERHIZBuffer::FarPlane, true, 0, SceneColorViewport.Extent, FIntRect());
// right
RHICmdList.SetScissorRect(true, InnerRect.Max.X, SceneColorViewport.Rect.Min.Y, SceneColorViewport.Rect.Max.X, SceneColorViewport.Rect.Max.Y);
DrawClearQuad(RHICmdList, false, FLinearColor(), true, (float)ERHIZBuffer::FarPlane, true, 0, SceneColorViewport.Extent, FIntRect());
RHICmdList.SetScissorRect(false, 0, 0, 0, 0);
}
});
}
FScreenPassRenderTarget Output = Inputs.OverrideOutput;
if (!Output.IsValid())
{
Output = FScreenPassRenderTarget::CreateFromInput(GraphBuilder, Inputs.SceneColor, View.GetOverwriteLoadAction(), TEXT("SelectionOutlineColor"));
}
// Render selection outlines.
{
const FScreenPassTextureViewport OutputViewport(Output);
const FScreenPassTextureViewport ColorViewport(Inputs.SceneColor);
const FScreenPassTextureViewport DepthViewport(Inputs.SceneDepth);
FRHISamplerState* PointClampSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
FSelectionOutlinePS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSelectionOutlinePS::FParameters>();
PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding();
PassParameters->View = View.ViewUniformBuffer;
PassParameters->Color = GetScreenPassTextureViewportParameters(ColorViewport);
PassParameters->Depth = GetScreenPassTextureViewportParameters(DepthViewport);
PassParameters->ColorToDepth = GetScreenPassTextureViewportTransform(PassParameters->Color, PassParameters->Depth);
PassParameters->ColorTexture = Inputs.SceneColor.Texture;
PassParameters->ColorSampler = PointClampSampler;
PassParameters->DepthTexture = Inputs.SceneDepth.Texture;
PassParameters->DepthSampler = PointClampSampler;
PassParameters->EditorPrimitivesDepth = DepthStencilTexture;
PassParameters->EditorPrimitivesStencil = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateWithPixelFormat(DepthStencilTexture, PF_X24_G8));
PassParameters->OutlineColor = FVector(View.SelectionOutlineColor);
PassParameters->SelectionHighlightIntensity = GEngine->SelectionHighlightIntensity;
PassParameters->SubduedOutlineColor = FVector(View.SubduedSelectionOutlineColor);
PassParameters->BSPSelectionIntensity = GEngine->BSPSelectionHighlightIntensity;
FSelectionOutlinePS::FPermutationDomain PermutationVector;
PermutationVector.Set<FSelectionOutlinePS::FSampleCountDimension>(MsaaSampleCount);
TShaderMapRef<FSelectionOutlinePS> PixelShader(View.ShaderMap, PermutationVector);
AddDrawScreenPass(
GraphBuilder,
RDG_EVENT_NAME("OutlineColor %dx%d", OutputViewport.Rect.Width(), OutputViewport.Rect.Height()),
View,
OutputViewport,
ColorViewport,
PixelShader,
PassParameters);
}
return MoveTemp(Output);
}
template <typename PixelShaderType>
FORCEINLINE void AddDrawScreenPass(
FRDGBuilder& GraphBuilder,
FRDGEventName&& PassName,
const FViewInfo& View,
const FScreenPassTextureViewport& OutputViewport,
const FScreenPassTextureViewport& InputViewport,
const TShaderRef<PixelShaderType>& PixelShader,
typename PixelShaderType::FParameters* PixelShaderParameters,
EScreenPassDrawFlags Flags = EScreenPassDrawFlags::None)
{
TShaderMapRef<FScreenPassVS> VertexShader(View.ShaderMap);
FRHIBlendState* BlendState = FScreenPassPipelineState::FDefaultBlendState::GetRHI();
FRHIDepthStencilState* DepthStencilState = FScreenPassPipelineState::FDefaultDepthStencilState::GetRHI();
AddDrawScreenPass(GraphBuilder, Forward<FRDGEventName&&>(PassName), View, OutputViewport, InputViewport, VertexShader, PixelShader, BlendState, DepthStencilState, PixelShaderParameters, Flags);
}
/** Render graph variant of simpler DrawScreenPass function. Clears graph resources unused by the
* pixel shader prior to adding the pass.
*/
template <typename PixelShaderType>
FORCEINLINE void AddDrawScreenPass(
FRDGBuilder& GraphBuilder,
FRDGEventName&& PassName,
const FViewInfo& View,
const FScreenPassTextureViewport& OutputViewport,
const FScreenPassTextureViewport& InputViewport,
const TShaderRef<FShader>& VertexShader,
const TShaderRef<PixelShaderType>& PixelShader,
FRHIBlendState* BlendState,
FRHIDepthStencilState* DepthStencilState,
typename PixelShaderType::FParameters* PixelShaderParameters,
EScreenPassDrawFlags Flags = EScreenPassDrawFlags::None)
{
check(VertexShader.IsValid());
check(PixelShader.IsValid());
check(PixelShaderParameters);
ClearUnusedGraphResources(PixelShader, PixelShaderParameters);
const FScreenPassPipelineState PipelineState(VertexShader, PixelShader, BlendState, DepthStencilState);
GraphBuilder.AddPass(
Forward<FRDGEventName&&>(PassName),
PixelShaderParameters,
ERDGPassFlags::Raster,
[&View, OutputViewport, InputViewport, PipelineState, PixelShader, PixelShaderParameters, Flags](FRHICommandListImmediate& RHICmdList)
{
DrawScreenPass(RHICmdList, View, OutputViewport, InputViewport, PipelineState, Flags, [&](FRHICommandListImmediate&)
{
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PixelShaderParameters);
});
});
}
/** More advanced variant of screen pass drawing. Supports overriding blend / depth stencil
* pipeline state, and providing a custom vertex shader. Shader parameters are not bound by
* this method, instead the user provides a setup function that is called prior to draw, but
* after setting the PSO. This setup function should assign shader parameters.
*/
template<typename TSetupFunction>
void DrawScreenPass(
FRHICommandListImmediate& RHICmdList,
const FViewInfo& View,
const FScreenPassTextureViewport& OutputViewport,
const FScreenPassTextureViewport& InputViewport,
const FScreenPassPipelineState& PipelineState,
EScreenPassDrawFlags Flags,
TSetupFunction SetupFunction)
{
PipelineState.Validate();
const FIntRect InputRect = InputViewport.Rect;
const FIntPoint InputSize = InputViewport.Extent;
const FIntRect OutputRect = OutputViewport.Rect;
const FIntPoint OutputSize = OutputRect.Size();
RHICmdList.SetViewport(OutputRect.Min.X, OutputRect.Min.Y, 0.0f, OutputRect.Max.X, OutputRect.Max.Y, 1.0f);
SetScreenPassPipelineState(RHICmdList, PipelineState);
SetupFunction(RHICmdList);
FIntPoint LocalOutputPos(FIntPoint::ZeroValue);
FIntPoint LocalOutputSize(OutputSize);
EDrawRectangleFlags DrawRectangleFlags = EDRF_UseTriangleOptimization;
const bool bFlipYAxis = (Flags & EScreenPassDrawFlags::FlipYAxis) == EScreenPassDrawFlags::FlipYAxis;
if (bFlipYAxis)
{
// Draw the quad flipped. Requires that the cull mode be disabled.
LocalOutputPos.Y = OutputSize.Y;
LocalOutputSize.Y = -OutputSize.Y;
// Triangle optimization currently doesn't work when flipped.
DrawRectangleFlags = EDRF_Default;
}
const bool bUseHMDHiddenAreaMask = (Flags & EScreenPassDrawFlags::AllowHMDHiddenAreaMask) == EScreenPassDrawFlags::AllowHMDHiddenAreaMask
? View.bHMDHiddenAreaMaskActive
: false;
DrawPostProcessPass(
RHICmdList,
LocalOutputPos.X, LocalOutputPos.Y, LocalOutputSize.X, LocalOutputSize.Y,
InputRect.Min.X, InputRect.Min.Y, InputRect.Width(), InputRect.Height(),
OutputSize,
InputSize,
PipelineState.VertexShader,
View.StereoPass,
bUseHMDHiddenAreaMask,
DrawRectangleFlags);
}
void DrawPostProcessPass(
FRHICommandList& RHICmdList,
float X,
float Y,
float SizeX,
float SizeY,
float U,
float V,
float SizeU,
float SizeV,
FIntPoint TargetSize,
FIntPoint TextureSize,
const TShaderRef<FShader>& VertexShader,
EStereoscopicPass StereoView,
bool bHasCustomMesh,
EDrawRectangleFlags Flags)
{
if (bHasCustomMesh && IStereoRendering::IsStereoEyePass(StereoView))
{
DrawHmdMesh(RHICmdList, X, Y, SizeX, SizeY, U, V, SizeU, SizeV, TargetSize, TextureSize, StereoView, VertexShader);
}
else
{
DrawRectangle(RHICmdList, X, Y, SizeX, SizeY, U, V, SizeU, SizeV, TargetSize, TextureSize, VertexShader, Flags);
}
}
EMeshPass::EditorSelection
/**
* Initialize scene's views.
* Check visibility, build visible mesh commands, etc.
*/
bool FDeferredShadingSceneRenderer::InitViews(FRHICommandListImmediate& RHICmdList, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, struct FILCUpdatePrimTaskData& ILCTaskData)
{
SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_InitViews, FColor::Emerald);
SCOPE_CYCLE_COUNTER(STAT_InitViewsTime);
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(InitViews_Scene);
check(RHICmdList.IsOutsideRenderPass());
PreVisibilityFrameSetup(RHICmdList);
RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread);
{
// 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).
if (FXSystem && FXSystem->RequiresEarlyViewUniformBuffer() && Views.IsValidIndex(0))
{
Views[0].InitRHIResources();
FXSystem->PostInitViews(RHICmdList, Views[0].ViewUniformBuffer, Views[0].AllowGPUParticleUpdate() && !ViewFamily.EngineShowFlags.HitProxies);
}
}
FViewVisibleCommandsPerView ViewCommandsPerView;
ViewCommandsPerView.SetNum(Views.Num());
ComputeViewVisibility(RHICmdList, BasePassDepthStencilAccess, ViewCommandsPerView, DynamicIndexBufferForInitViews, DynamicVertexBufferForInitViews, DynamicReadBufferForInitViews);
RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread);
}
void FSceneRenderer::ComputeViewVisibility(FRHICommandListImmediate& RHICmdList, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, FViewVisibleCommandsPerView& ViewCommandsPerView,
FGlobalDynamicIndexBuffer& DynamicIndexBuffer, FGlobalDynamicVertexBuffer& DynamicVertexBuffer, FGlobalDynamicReadBuffer& DynamicReadBuffer)
{
SCOPE_CYCLE_COUNTER(STAT_ViewVisibilityTime);
SCOPED_NAMED_EVENT(FSceneRenderer_ComputeViewVisibility, FColor::Magenta);
STAT(int32 NumProcessedPrimitives = 0);
STAT(int32 NumCulledPrimitives = 0);
STAT(int32 NumOccludedPrimitives = 0);
// ISR views can't compute relevance until all views are frustum culled
if (!bIsInstancedStereo)
{
SCOPE_CYCLE_COUNTER(STAT_ViewRelevance);
ComputeAndMarkRelevanceForViewParallel(RHICmdList, Scene, View, ViewCommands, ViewBit, HasDynamicMeshElementsMasks, HasDynamicEditorMeshElementsMasks);
}
SetupMeshPass(View, BasePassDepthStencilAccess, ViewCommands);
}
static void ComputeAndMarkRelevanceForViewParallel(
FRHICommandListImmediate& RHICmdList,
const FScene* Scene,
FViewInfo& View,
FViewCommands& ViewCommands,
uint8 ViewBit,
FPrimitiveViewMasks& OutHasDynamicMeshElementsMasks,
FPrimitiveViewMasks& OutHasDynamicEditorMeshElementsMasks
)
{
SCOPED_NAMED_EVENT(FSceneRenderer_ComputeAndMarkRelevanceForViewParallel, FColor::Blue);
check(OutHasDynamicMeshElementsMasks.Num() == Scene->Primitives.Num());
FFrozenSceneViewMatricesGuard FrozenMatricesGuard(View);
const FMarkRelevantStaticMeshesForViewData ViewData(View);
int32 NumMesh = View.StaticMeshVisibilityMap.Num();
uint8* RESTRICT MarkMasks = (uint8*)FMemStack::Get().Alloc(NumMesh + 31 , 8); // some padding to simplify the high speed transpose
FMemory::Memzero(MarkMasks, NumMesh + 31);
int32 EstimateOfNumPackets = NumMesh / (FRelevancePrimSet<int32>::MaxInputPrims * 4);
TArray<FRelevancePacket*,SceneRenderingAllocator> Packets;
Packets.Reserve(EstimateOfNumPackets);
bool WillExecuteInParallel = FApp::ShouldUseThreadingForPerformance() && CVarParallelInitViews.GetValueOnRenderThread() > 0 && IsInActualRenderingThread();
{
FSceneSetBitIterator BitIt(View.PrimitiveVisibilityMap);
if (BitIt)
{
FRelevancePacket* Packet = new(FMemStack::Get()) FRelevancePacket(
RHICmdList,
Scene,
View,
ViewCommands,
ViewBit,
ViewData,
OutHasDynamicMeshElementsMasks,
OutHasDynamicEditorMeshElementsMasks,
MarkMasks);
Packets.Add(Packet);
while (1)
{
Packet->Input.AddPrim(BitIt.GetIndex());
++BitIt;
if (Packet->Input.IsFull() || !BitIt)
{
if (!BitIt)
{
break;
}
else
{
Packet = new(FMemStack::Get()) FRelevancePacket(
RHICmdList,
Scene,
View,
ViewCommands,
ViewBit,
ViewData,
OutHasDynamicMeshElementsMasks,
OutHasDynamicEditorMeshElementsMasks,
MarkMasks);
Packets.Add(Packet);
}
}
}
}
}
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_ComputeAndMarkRelevanceForViewParallel_ParallelFor);
ParallelFor(Packets.Num(),
[&Packets](int32 Index)
{
Packets[Index]->AnyThreadTask();
},
!WillExecuteInParallel
);
}
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_ComputeAndMarkRelevanceForViewParallel_RenderThreadFinalize);
for (int32 PassIndex = 0; PassIndex < EMeshPass::Num; PassIndex++)
{
int32 NumVisibleCachedMeshDrawCommands = 0;
int32 NumDynamicBuildRequests = 0;
for (auto Packet : Packets)
{
NumVisibleCachedMeshDrawCommands += Packet->DrawCommandPacket.VisibleCachedDrawCommands[PassIndex].Num();
NumDynamicBuildRequests += Packet->DrawCommandPacket.DynamicBuildRequests[PassIndex].Num();
}
ViewCommands.MeshCommands[PassIndex].Reserve(NumVisibleCachedMeshDrawCommands);
ViewCommands.DynamicMeshCommandBuildRequests[PassIndex].Reserve(NumDynamicBuildRequests);
}
for (auto Packet : Packets)
{
Packet->RenderThreadFinalize();
Packet->~FRelevancePacket();
}
Packets.Empty();
}
QUICK_SCOPE_CYCLE_COUNTER(STAT_ComputeAndMarkRelevanceForViewParallel_TransposeMeshBits);
check(View.StaticMeshVisibilityMap.Num() == NumMesh &&
View.StaticMeshFadeOutDitheredLODMap.Num() == NumMesh &&
View.StaticMeshFadeInDitheredLODMap.Num() == NumMesh
);
uint32* RESTRICT StaticMeshVisibilityMap_Words = View.StaticMeshVisibilityMap.GetData();
uint32* RESTRICT StaticMeshFadeOutDitheredLODMap_Words = View.StaticMeshFadeOutDitheredLODMap.GetData();
uint32* RESTRICT StaticMeshFadeInDitheredLODMap_Words = View.StaticMeshFadeInDitheredLODMap.GetData();
const uint64* RESTRICT MarkMasks64 = (const uint64* RESTRICT)MarkMasks;
const uint8* RESTRICT MarkMasks8 = MarkMasks;
for (int32 BaseIndex = 0; BaseIndex < NumMesh; BaseIndex += 32)
{
uint32 StaticMeshVisibilityMap_Word = 0;
uint32 StaticMeshFadeOutDitheredLODMap_Word = 0;
uint32 StaticMeshFadeInDitheredLODMap_Word = 0;
uint32 Mask = 1;
bool bAny = false;
for (int32 QWordIndex = 0; QWordIndex < 4; QWordIndex++)
{
if (*MarkMasks64++)
{
for (int32 ByteIndex = 0; ByteIndex < 8; ByteIndex++, Mask <<= 1, MarkMasks8++)
{
uint8 MaskMask = *MarkMasks8;
StaticMeshVisibilityMap_Word |= (MaskMask & EMarkMaskBits::StaticMeshVisibilityMapMask) ? Mask : 0;
StaticMeshFadeOutDitheredLODMap_Word |= (MaskMask & EMarkMaskBits::StaticMeshFadeOutDitheredLODMapMask) ? Mask : 0;
StaticMeshFadeInDitheredLODMap_Word |= (MaskMask & EMarkMaskBits::StaticMeshFadeInDitheredLODMapMask) ? Mask : 0;
}
bAny = true;
}
else
{
MarkMasks8 += 8;
Mask <<= 8;
}
}
if (bAny)
{
checkSlow(!*StaticMeshVisibilityMap_Words && !*StaticMeshFadeOutDitheredLODMap_Words && !*StaticMeshFadeInDitheredLODMap_Words);
*StaticMeshVisibilityMap_Words = StaticMeshVisibilityMap_Word;
*StaticMeshFadeOutDitheredLODMap_Words = StaticMeshFadeOutDitheredLODMap_Word;
*StaticMeshFadeInDitheredLODMap_Words = StaticMeshFadeInDitheredLODMap_Word;
}
StaticMeshVisibilityMap_Words++;
StaticMeshFadeOutDitheredLODMap_Words++;
StaticMeshFadeInDitheredLODMap_Words++;
}
}
struct FRelevancePacket
{
const float CurrentWorldTime;
const float DeltaWorldTime;
FRHICommandListImmediate& RHICmdList;
const FScene* Scene;
const FViewInfo& View;
const FViewCommands& ViewCommands;
const uint8 ViewBit;
const FMarkRelevantStaticMeshesForViewData& ViewData;
FPrimitiveViewMasks& OutHasDynamicMeshElementsMasks;
FPrimitiveViewMasks& OutHasDynamicEditorMeshElementsMasks;
uint8* RESTRICT MarkMasks;
FRelevancePrimSet<int32> Input;
FRelevancePrimSet<int32> RelevantStaticPrimitives;
FRelevancePrimSet<int32> NotDrawRelevant;
FRelevancePrimSet<int32> TranslucentSelfShadowPrimitives;
FRelevancePrimSet<FPrimitiveSceneInfo*> VisibleDynamicPrimitivesWithSimpleLights;
int32 NumVisibleDynamicPrimitives;
int32 NumVisibleDynamicEditorPrimitives;
FMeshPassMask VisibleDynamicMeshesPassMask;
FTranslucenyPrimCount TranslucentPrimCount;
bool bHasDistortionPrimitives;
bool bHasCustomDepthPrimitives;
FRelevancePrimSet<FPrimitiveSceneInfo*> LazyUpdatePrimitives;
FRelevancePrimSet<FPrimitiveSceneInfo*> DirtyIndirectLightingCacheBufferPrimitives;
FRelevancePrimSet<FPrimitiveSceneInfo*> RecachedReflectionCapturePrimitives;
TArray<FMeshDecalBatch> MeshDecalBatches;
TArray<FVolumetricMeshBatch> VolumetricMeshBatches;
TArray<FSkyMeshBatch> SkyMeshBatches;
FDrawCommandRelevancePacket DrawCommandPacket;
struct FPrimitiveLODMask
{
FPrimitiveLODMask()
: PrimitiveIndex(INDEX_NONE)
{}
FPrimitiveLODMask(const int32 InPrimitiveIndex, const FLODMask& InLODMask)
: PrimitiveIndex(InPrimitiveIndex)
, LODMask(InLODMask)
{}
int32 PrimitiveIndex;
FLODMask LODMask;
};
FRelevancePrimSet<FPrimitiveLODMask> PrimitivesLODMask; // group both lod mask with primitive index to be able to properly merge them in the view
uint16 CombinedShadingModelMask;
bool bUsesGlobalDistanceField;
bool bUsesLightingChannels;
bool bTranslucentSurfaceLighting;
bool bUsesSceneDepth;
bool bUsesCustomDepthStencil;
bool bSceneHasSkyMaterial;
bool bHasSingleLayerWaterMaterial;
bool bHasTranslucencySeparateModulation;
FRelevancePacket(
FRHICommandListImmediate& InRHICmdList,
const FScene* InScene,
const FViewInfo& InView,
const FViewCommands& InViewCommands,
uint8 InViewBit,
const FMarkRelevantStaticMeshesForViewData& InViewData,
FPrimitiveViewMasks& InOutHasDynamicMeshElementsMasks,
FPrimitiveViewMasks& InOutHasDynamicEditorMeshElementsMasks,
uint8* InMarkMasks)
: CurrentWorldTime(InView.Family->CurrentWorldTime)
, DeltaWorldTime(InView.Family->DeltaWorldTime)
, RHICmdList(InRHICmdList)
, Scene(InScene)
, View(InView)
, ViewCommands(InViewCommands)
, ViewBit(InViewBit)
, ViewData(InViewData)
, OutHasDynamicMeshElementsMasks(InOutHasDynamicMeshElementsMasks)
, OutHasDynamicEditorMeshElementsMasks(InOutHasDynamicEditorMeshElementsMasks)
, MarkMasks(InMarkMasks)
, NumVisibleDynamicPrimitives(0)
, NumVisibleDynamicEditorPrimitives(0)
, bHasDistortionPrimitives(false)
, bHasCustomDepthPrimitives(false)
, CombinedShadingModelMask(0)
, bUsesGlobalDistanceField(false)
, bUsesLightingChannels(false)
, bTranslucentSurfaceLighting(false)
, bUsesSceneDepth(false)
, bUsesCustomDepthStencil(false)
, bSceneHasSkyMaterial(false)
, bHasSingleLayerWaterMaterial(false)
, bHasTranslucencySeparateModulation(false)
{
}
void AnyThreadTask()
{
ComputeRelevance();
MarkRelevant();
}
void ComputeRelevance()
{
CombinedShadingModelMask = 0;
bSceneHasSkyMaterial = 0;
bHasSingleLayerWaterMaterial = 0;
bHasTranslucencySeparateModulation = 0;
bUsesGlobalDistanceField = false;
bUsesLightingChannels = false;
bTranslucentSurfaceLighting = false;
const EShadingPath ShadingPath = Scene->GetShadingPath();
const bool bAddLightmapDensityCommands = View.Family->EngineShowFlags.LightMapDensity && AllowDebugViewmodes();
SCOPE_CYCLE_COUNTER(STAT_ComputeViewRelevance);
for (int32 Index = 0; Index < Input.NumPrims; Index++)
{
int32 BitIndex = Input.Prims[Index];
FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->Primitives[BitIndex];
FPrimitiveViewRelevance& ViewRelevance = const_cast<FPrimitiveViewRelevance&>(View.PrimitiveViewRelevanceMap[BitIndex]);
ViewRelevance = PrimitiveSceneInfo->Proxy->GetViewRelevance(&View);
ViewRelevance.bInitializedThisFrame = true;
const bool bStaticRelevance = ViewRelevance.bStaticRelevance;
const bool bDrawRelevance = ViewRelevance.bDrawRelevance;
const bool bDynamicRelevance = ViewRelevance.bDynamicRelevance;
const bool bShadowRelevance = ViewRelevance.bShadowRelevance;
const bool bEditorRelevance = ViewRelevance.bEditorPrimitiveRelevance;
const bool bEditorSelectionRelevance = ViewRelevance.bEditorStaticSelectionRelevance;
const bool bTranslucentRelevance = ViewRelevance.HasTranslucency();
const bool bHairStrandsEnabled = ViewRelevance.bHairStrands && IsHairStrandsEnabled(EHairStrandsShaderType::All, Scene->GetShaderPlatform());
if (!bEditorRelevance && bHairStrandsEnabled)
{
++NumVisibleDynamicPrimitives;
OutHasDynamicMeshElementsMasks[BitIndex] |= ViewBit;
}
if (View.bIsReflectionCapture && !PrimitiveSceneInfo->Proxy->IsVisibleInReflectionCaptures())
{
NotDrawRelevant.AddPrim(BitIndex);
continue;
}
if (bStaticRelevance && (bDrawRelevance || bShadowRelevance))
{
RelevantStaticPrimitives.AddPrim(BitIndex);
}
if (!bDrawRelevance)
{
NotDrawRelevant.AddPrim(BitIndex);
continue;
}
if (bEditorRelevance)
{
++NumVisibleDynamicEditorPrimitives;
if (GIsEditor)
{
OutHasDynamicEditorMeshElementsMasks[BitIndex] |= ViewBit;
}
}
else if(bDynamicRelevance)
{
// Keep track of visible dynamic primitives.
++NumVisibleDynamicPrimitives;
OutHasDynamicMeshElementsMasks[BitIndex] |= ViewBit;
if (ViewRelevance.bHasSimpleLights)
{
VisibleDynamicPrimitivesWithSimpleLights.AddPrim(PrimitiveSceneInfo);
}
}
if (bTranslucentRelevance && !bEditorRelevance && ViewRelevance.bRenderInMainPass)
{
if (View.Family->AllowTranslucencyAfterDOF())
{
if (ViewRelevance.bNormalTranslucency)
{
TranslucentPrimCount.Add(ETranslucencyPass::TPT_StandardTranslucency, ViewRelevance.bUsesSceneColorCopy);
}
if (ViewRelevance.bSeparateTranslucency)
{
TranslucentPrimCount.Add(ETranslucencyPass::TPT_TranslucencyAfterDOF, ViewRelevance.bUsesSceneColorCopy);
}
if (ViewRelevance.bSeparateTranslucencyModulate)
{
TranslucentPrimCount.Add(ETranslucencyPass::TPT_TranslucencyAfterDOFModulate, ViewRelevance.bUsesSceneColorCopy);
}
}
else // Otherwise, everything is rendered in a single bucket. This is not related to whether DOF is currently enabled or not.
{
// When using all translucency, Standard and AfterDOF are sorted together instead of being rendered like 2 buckets.
TranslucentPrimCount.Add(ETranslucencyPass::TPT_AllTranslucency, ViewRelevance.bUsesSceneColorCopy);
}
if (ViewRelevance.bDistortion)
{
bHasDistortionPrimitives = true;
}
}
CombinedShadingModelMask |= ViewRelevance.ShadingModelMask;
bUsesGlobalDistanceField |= ViewRelevance.bUsesGlobalDistanceField;
bUsesLightingChannels |= ViewRelevance.bUsesLightingChannels;
bTranslucentSurfaceLighting |= ViewRelevance.bTranslucentSurfaceLighting;
bUsesSceneDepth |= ViewRelevance.bUsesSceneDepth;
bUsesCustomDepthStencil |= ViewRelevance.bUsesCustomDepthStencil;
bSceneHasSkyMaterial |= ViewRelevance.bUsesSkyMaterial;
bHasSingleLayerWaterMaterial |= ViewRelevance.bUsesSingleLayerWaterMaterial;
bHasTranslucencySeparateModulation |= ViewRelevance.bSeparateTranslucencyModulate;
if (ViewRelevance.bRenderCustomDepth)
{
bHasCustomDepthPrimitives = true;
}
extern bool GUseTranslucencyShadowDepths;
if (GUseTranslucencyShadowDepths && ViewRelevance.bTranslucentSelfShadow)
{
TranslucentSelfShadowPrimitives.AddPrim(BitIndex);
}
// INITVIEWS_TODO: Do this in a separate pass? There are no dependencies
// here except maybe ParentPrimitives. This could be done in a
// low-priority background task and forgotten about.
PrimitiveSceneInfo->LastRenderTime = CurrentWorldTime;
// If the primitive is definitely unoccluded or if in Wireframe mode and the primitive is estimated
// to be unoccluded, then update the primitive components's LastRenderTime
// on the game thread. This signals that the primitive is visible.
if (View.PrimitiveDefinitelyUnoccludedMap[BitIndex] || (View.Family->EngineShowFlags.Wireframe && View.PrimitiveVisibilityMap[BitIndex]))
{
PrimitiveSceneInfo->UpdateComponentLastRenderTime(CurrentWorldTime, /*bUpdateLastRenderTimeOnScreen=*/true);
}
// Cache the nearest reflection proxy if needed
if (PrimitiveSceneInfo->NeedsReflectionCaptureUpdate())
{
// mobile should not have any outstanding reflection capture update requests at this point, except for when lighting isn't rebuilt
PrimitiveSceneInfo->CacheReflectionCaptures();
// With forward shading we need to track reflection capture cache updates
// in order to update primitive's uniform buffer's closest reflection capture id.
if (IsForwardShadingEnabled(Scene->GetShaderPlatform()))
{
RecachedReflectionCapturePrimitives.AddPrim(PrimitiveSceneInfo);
}
}
if (PrimitiveSceneInfo->NeedsUniformBufferUpdate())
{
LazyUpdatePrimitives.AddPrim(PrimitiveSceneInfo);
}
if (PrimitiveSceneInfo->NeedsIndirectLightingCacheBufferUpdate())
{
DirtyIndirectLightingCacheBufferPrimitives.AddPrim(PrimitiveSceneInfo);
}
}
}
void MarkRelevant()
{
SCOPE_CYCLE_COUNTER(STAT_StaticRelevance);
// using a local counter to reduce memory traffic
int32 NumVisibleStaticMeshElements = 0;
FViewInfo& WriteView = const_cast<FViewInfo&>(View);
const FSceneViewState* ViewState = (FSceneViewState*)View.State;
const EShadingPath ShadingPath = Scene->GetShadingPath();
const bool bHLODActive = Scene->SceneLODHierarchy.IsActive();
const FHLODVisibilityState* const HLODState = bHLODActive && ViewState ? &ViewState->HLODVisibilityState : nullptr;
for (int32 StaticPrimIndex = 0, Num = RelevantStaticPrimitives.NumPrims; StaticPrimIndex < Num; ++StaticPrimIndex)
{
int32 PrimitiveIndex = RelevantStaticPrimitives.Prims[StaticPrimIndex];
const FPrimitiveSceneInfo* RESTRICT PrimitiveSceneInfo = Scene->Primitives[PrimitiveIndex];
const FPrimitiveBounds& Bounds = Scene->PrimitiveBounds[PrimitiveIndex];
const FPrimitiveViewRelevance& ViewRelevance = View.PrimitiveViewRelevanceMap[PrimitiveIndex];
const bool bIsPrimitiveDistanceCullFading = View.PrimitiveFadeUniformBufferMap[PrimitiveIndex];
const int8 CurFirstLODIdx = PrimitiveSceneInfo->Proxy->GetCurrentFirstLODIdx_RenderThread();
check(CurFirstLODIdx >= 0);
float MeshScreenSizeSquared = 0;
FLODMask LODToRender = ComputeLODForMeshes(PrimitiveSceneInfo->StaticMeshRelevances, View, Bounds.BoxSphereBounds.Origin, Bounds.BoxSphereBounds.SphereRadius, ViewData.ForcedLODLevel, MeshScreenSizeSquared, CurFirstLODIdx, ViewData.LODScale);
PrimitivesLODMask.AddPrim(FRelevancePacket::FPrimitiveLODMask(PrimitiveIndex, LODToRender));
const bool bIsHLODFading = HLODState ? HLODState->IsNodeFading(PrimitiveIndex) : false;
const bool bIsHLODFadingOut = HLODState ? HLODState->IsNodeFadingOut(PrimitiveIndex) : false;
const bool bIsLODDithered = LODToRender.IsDithered();
float DistanceSquared = (Bounds.BoxSphereBounds.Origin - ViewData.ViewOrigin).SizeSquared();
const float LODFactorDistanceSquared = DistanceSquared * FMath::Square(ViewData.LODScale);
const bool bDrawShadowDepth = FMath::Square(Bounds.BoxSphereBounds.SphereRadius) > ViewData.MinScreenRadiusForCSMDepthSquared * LODFactorDistanceSquared;
const bool bDrawDepthOnly = ViewData.bFullEarlyZPass || FMath::Square(Bounds.BoxSphereBounds.SphereRadius) > GMinScreenRadiusForDepthPrepass * GMinScreenRadiusForDepthPrepass * LODFactorDistanceSquared;
const bool bAddLightmapDensityCommands = View.Family->EngineShowFlags.LightMapDensity && AllowDebugViewmodes();
const bool bMobileMaskedInEarlyPass = MaskedInEarlyPass(Scene->GetShaderPlatform()) && Scene->EarlyZPassMode == DDM_MaskedOnly;
const int32 NumStaticMeshes = PrimitiveSceneInfo->StaticMeshRelevances.Num();
for(int32 MeshIndex = 0;MeshIndex < NumStaticMeshes;MeshIndex++)
{
const FStaticMeshBatchRelevance& StaticMeshRelevance = PrimitiveSceneInfo->StaticMeshRelevances[MeshIndex];
const FStaticMeshBatch& StaticMesh = PrimitiveSceneInfo->StaticMeshes[MeshIndex];
if (LODToRender.ContainsLOD(StaticMeshRelevance.LODIndex))
{
uint8 MarkMask = 0;
bool bHiddenByHLODFade = false; // Hide mesh LOD levels that HLOD is substituting
if (bIsHLODFading)
{
if (bIsHLODFadingOut)
{
if (bIsLODDithered && LODToRender.DitheredLODIndices[1] == StaticMeshRelevance.LODIndex)
{
bHiddenByHLODFade = true;
}
else
{
MarkMask |= EMarkMaskBits::StaticMeshFadeOutDitheredLODMapMask;
}
}
else
{
if (bIsLODDithered && LODToRender.DitheredLODIndices[0] == StaticMeshRelevance.LODIndex)
{
bHiddenByHLODFade = true;
}
else
{
MarkMask |= EMarkMaskBits::StaticMeshFadeInDitheredLODMapMask;
}
}
}
else if (bIsLODDithered)
{
if (LODToRender.DitheredLODIndices[0] == StaticMeshRelevance.LODIndex)
{
MarkMask |= EMarkMaskBits::StaticMeshFadeOutDitheredLODMapMask;
}
else
{
MarkMask |= EMarkMaskBits::StaticMeshFadeInDitheredLODMapMask;
}
}
// Don't cache if it requires per view per mesh state for LOD dithering or distance cull fade.
const bool bIsMeshDitheringLOD = StaticMeshRelevance.bDitheredLODTransition && (MarkMask & (EMarkMaskBits::StaticMeshFadeOutDitheredLODMapMask | EMarkMaskBits::StaticMeshFadeInDitheredLODMapMask));
const bool bCanCache = !bIsPrimitiveDistanceCullFading && !bIsMeshDitheringLOD;
if (ViewRelevance.bDrawRelevance)
{
if ((StaticMeshRelevance.bUseForMaterial || StaticMeshRelevance.bUseAsOccluder)
&& (ViewRelevance.bRenderInMainPass || ViewRelevance.bRenderCustomDepth || ViewRelevance.bRenderInDepthPass)
&& !bHiddenByHLODFade)
{
bool bMobileIsInDepthPassMaskedMesh = (bMobileMaskedInEarlyPass && ViewRelevance.bMasked) && ShadingPath == EShadingPath::Mobile;
if ((StaticMeshRelevance.bUseForDepthPass && bDrawDepthOnly && ShadingPath != EShadingPath::Mobile) || bMobileIsInDepthPassMaskedMesh)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::DepthPass);
#if RHI_RAYTRACING
if (IsRayTracingEnabled())
{
if (MarkMask & EMarkMaskBits::StaticMeshFadeOutDitheredLODMapMask)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::DitheredLODFadingOutMaskPass);
}
}
#endif
}
// Mark static mesh as visible for rendering
if (StaticMeshRelevance.bUseForMaterial && (ViewRelevance.bRenderInMainPass || ViewRelevance.bRenderCustomDepth))
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::BasePass);
MarkMask |= EMarkMaskBits::StaticMeshVisibilityMapMask;
if (StaticMeshRelevance.bUseAnisotropy)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::AnisotropyPass);
}
if (ShadingPath == EShadingPath::Mobile)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::MobileBasePassCSM);
}
else if(StaticMeshRelevance.bUseSkyMaterial)
{
// Not needed on Mobile path as in this case everything goes into the regular base pass
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::SkyPass);
}
else if(StaticMeshRelevance.bUseSingleLayerWaterMaterial)
{
// Not needed on Mobile path as in this case everything goes into the regular base pass
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::SingleLayerWaterPass);
}
if (ViewRelevance.bRenderCustomDepth)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::CustomDepth);
}
if (bAddLightmapDensityCommands)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::LightmapDensity);
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
else if (View.Family->UseDebugViewPS())
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::DebugViewMode);
}
#endif
#if WITH_EDITOR
if (StaticMeshRelevance.bSelectable)
{
if (View.bAllowTranslucentPrimitivesInHitProxy)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::HitProxy);
}
else
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::HitProxyOpaqueOnly);
}
}
#endif
if (ViewRelevance.HasVelocity())
{
const FPrimitiveSceneProxy* PrimitiveSceneProxy = PrimitiveSceneInfo->Proxy;
if (FVelocityMeshProcessor::PrimitiveHasVelocityForView(View, PrimitiveSceneProxy))
{
if (ViewRelevance.bVelocityRelevance &&
FOpaqueVelocityMeshProcessor::PrimitiveCanHaveVelocity(View.GetShaderPlatform(), PrimitiveSceneProxy) &&
FOpaqueVelocityMeshProcessor::PrimitiveHasVelocityForFrame(PrimitiveSceneProxy))
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::Velocity);
}
if (ViewRelevance.bOutputsTranslucentVelocity &&
FTranslucentVelocityMeshProcessor::PrimitiveCanHaveVelocity(View.GetShaderPlatform(), PrimitiveSceneProxy) &&
FTranslucentVelocityMeshProcessor::PrimitiveHasVelocityForFrame(PrimitiveSceneProxy))
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::TranslucentVelocity);
}
}
}
++NumVisibleStaticMeshElements;
INC_DWORD_STAT_BY(STAT_StaticMeshTriangles, StaticMesh.GetNumPrimitives());
}
}
if (StaticMeshRelevance.bUseForMaterial
&& ViewRelevance.HasTranslucency()
&& !ViewRelevance.bEditorPrimitiveRelevance
&& ViewRelevance.bRenderInMainPass)
{
if (View.Family->AllowTranslucencyAfterDOF())
{
if (ViewRelevance.bNormalTranslucency)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::TranslucencyStandard);
}
if (ViewRelevance.bSeparateTranslucency)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::TranslucencyAfterDOF);
}
if (ViewRelevance.bSeparateTranslucencyModulate)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::TranslucencyAfterDOFModulate);
}
}
else
{
// Otherwise, everything is rendered in a single bucket. This is not related to whether DOF is currently enabled or not.
// When using all translucency, Standard and AfterDOF are sorted together instead of being rendered like 2 buckets.
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::TranslucencyAll);
}
if (ViewRelevance.bDistortion)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::Distortion);
}
if (ShadingPath == EShadingPath::Mobile && View.bIsSceneCapture)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::MobileInverseOpacity);
}
}
#if WITH_EDITOR
if (ViewRelevance.bEditorStaticSelectionRelevance)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::EditorSelection);
}
#endif
if (ViewRelevance.bHasVolumeMaterialDomain)
{
VolumetricMeshBatches.AddUninitialized(1);
FVolumetricMeshBatch& BatchAndProxy = VolumetricMeshBatches.Last();
BatchAndProxy.Mesh = &StaticMesh;
BatchAndProxy.Proxy = PrimitiveSceneInfo->Proxy;
}
if (ViewRelevance.bUsesSkyMaterial)
{
SkyMeshBatches.AddUninitialized(1);
FSkyMeshBatch& BatchAndProxy = SkyMeshBatches.Last();
BatchAndProxy.Mesh = &StaticMesh;
BatchAndProxy.Proxy = PrimitiveSceneInfo->Proxy;
BatchAndProxy.bVisibleInMainPass = ViewRelevance.bRenderInMainPass;
BatchAndProxy.bVisibleInRealTimeSkyCapture = PrimitiveSceneInfo->bVisibleInRealTimeSkyCapture;
}
if (ViewRelevance.bRenderInMainPass && ViewRelevance.bDecal)
{
MeshDecalBatches.AddUninitialized(1);
FMeshDecalBatch& BatchAndProxy = MeshDecalBatches.Last();
BatchAndProxy.Mesh = &StaticMesh;
BatchAndProxy.Proxy = PrimitiveSceneInfo->Proxy;
BatchAndProxy.SortKey = PrimitiveSceneInfo->Proxy->GetTranslucencySortPriority();
}
}
if (MarkMask)
{
MarkMasks[StaticMeshRelevance.Id] = MarkMask;
}
}
}
}
static_assert(sizeof(WriteView.NumVisibleStaticMeshElements) == sizeof(int32), "Atomic is the wrong size");
FPlatformAtomics::InterlockedAdd((volatile int32*)&WriteView.NumVisibleStaticMeshElements, NumVisibleStaticMeshElements);
}
void RenderThreadFinalize()
{
FViewInfo& WriteView = const_cast<FViewInfo&>(View);
FViewCommands& WriteViewCommands = const_cast<FViewCommands&>(ViewCommands);
for (int32 Index = 0; Index < NotDrawRelevant.NumPrims; Index++)
{
WriteView.PrimitiveVisibilityMap[NotDrawRelevant.Prims[Index]] = false;
}
WriteView.ShadingModelMaskInView |= CombinedShadingModelMask;
WriteView.bUsesGlobalDistanceField |= bUsesGlobalDistanceField;
WriteView.bUsesLightingChannels |= bUsesLightingChannels;
WriteView.bTranslucentSurfaceLighting |= bTranslucentSurfaceLighting;
WriteView.bUsesSceneDepth |= bUsesSceneDepth;
WriteView.bSceneHasSkyMaterial |= bSceneHasSkyMaterial;
WriteView.bHasSingleLayerWaterMaterial |= bHasSingleLayerWaterMaterial;
WriteView.bHasTranslucencySeparateModulation |= bHasTranslucencySeparateModulation;
VisibleDynamicPrimitivesWithSimpleLights.AppendTo(WriteView.VisibleDynamicPrimitivesWithSimpleLights);
WriteView.NumVisibleDynamicPrimitives += NumVisibleDynamicPrimitives;
WriteView.NumVisibleDynamicEditorPrimitives += NumVisibleDynamicEditorPrimitives;
WriteView.TranslucentPrimCount.Append(TranslucentPrimCount);
WriteView.bHasDistortionPrimitives |= bHasDistortionPrimitives;
WriteView.bHasCustomDepthPrimitives |= bHasCustomDepthPrimitives;
WriteView.bUsesCustomDepthStencilInTranslucentMaterials |= bUsesCustomDepthStencil;
DirtyIndirectLightingCacheBufferPrimitives.AppendTo(WriteView.DirtyIndirectLightingCacheBufferPrimitives);
WriteView.MeshDecalBatches.Append(MeshDecalBatches);
WriteView.VolumetricMeshBatches.Append(VolumetricMeshBatches);
WriteView.SkyMeshBatches.Append(SkyMeshBatches);
for (int32 Index = 0; Index < RecachedReflectionCapturePrimitives.NumPrims; ++Index)
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = RecachedReflectionCapturePrimitives.Prims[Index];
PrimitiveSceneInfo->SetNeedsUniformBufferUpdate(true);
PrimitiveSceneInfo->ConditionalUpdateUniformBuffer(RHICmdList);
FScene& WriteScene = *const_cast<FScene*>(Scene);
AddPrimitiveToUpdateGPU(WriteScene, PrimitiveSceneInfo->GetIndex());
}
for (int32 Index = 0; Index < LazyUpdatePrimitives.NumPrims; Index++)
{
LazyUpdatePrimitives.Prims[Index]->ConditionalUpdateUniformBuffer(RHICmdList);
}
for (int32 i = 0; i < PrimitivesLODMask.NumPrims; ++i)
{
WriteView.PrimitivesLODMask[PrimitivesLODMask.Prims[i].PrimitiveIndex] = PrimitivesLODMask.Prims[i].LODMask;
}
for (int32 PassIndex = 0; PassIndex < EMeshPass::Num; PassIndex++)
{
FPassDrawCommandArray& SrcCommands = DrawCommandPacket.VisibleCachedDrawCommands[PassIndex];
FMeshCommandOneFrameArray& DstCommands = WriteViewCommands.MeshCommands[PassIndex];
if (SrcCommands.Num() > 0)
{
static_assert(sizeof(SrcCommands[0]) == sizeof(DstCommands[0]), "Memcpy sizes must match.");
const int32 PrevNum = DstCommands.AddUninitialized(SrcCommands.Num());
FMemory::Memcpy(&DstCommands[PrevNum], &SrcCommands[0], SrcCommands.Num() * sizeof(SrcCommands[0]));
}
FPassDrawCommandBuildRequestArray& SrcRequests = DrawCommandPacket.DynamicBuildRequests[PassIndex];
TArray<const FStaticMeshBatch*, SceneRenderingAllocator>& DstRequests = WriteViewCommands.DynamicMeshCommandBuildRequests[PassIndex];
if (SrcRequests.Num() > 0)
{
static_assert(sizeof(SrcRequests[0]) == sizeof(DstRequests[0]), "Memcpy sizes must match.");
const int32 PrevNum = DstRequests.AddUninitialized(SrcRequests.Num());
FMemory::Memcpy(&DstRequests[PrevNum], &SrcRequests[0], SrcRequests.Num() * sizeof(SrcRequests[0]));
}
WriteViewCommands.NumDynamicMeshCommandBuildRequestElements[PassIndex] += DrawCommandPacket.NumDynamicBuildRequestElements[PassIndex];
}
// Prepare translucent self shadow uniform buffers.
for (int32 Index = 0; Index < TranslucentSelfShadowPrimitives.NumPrims; ++Index)
{
const int32 PrimitiveIndex = TranslucentSelfShadowPrimitives.Prims[Index];
FUniformBufferRHIRef& UniformBuffer = WriteView.TranslucentSelfShadowUniformBufferMap.FindOrAdd(PrimitiveIndex);
if (!UniformBuffer)
{
FTranslucentSelfShadowUniformParameters Parameters;
SetupTranslucentSelfShadowUniformParameters(nullptr, Parameters);
UniformBuffer = FTranslucentSelfShadowUniformParameters::CreateUniformBuffer(Parameters, EUniformBufferUsage::UniformBuffer_SingleFrame);
}
}
}
};
struct FDrawCommandRelevancePacket
{
FDrawCommandRelevancePacket()
{
bUseCachedMeshDrawCommands = UseCachedMeshDrawCommands();
for (int32 PassIndex = 0; PassIndex < EMeshPass::Num; ++PassIndex)
{
NumDynamicBuildRequestElements[PassIndex] = 0;
}
}
FPassDrawCommandArray VisibleCachedDrawCommands[EMeshPass::Num];
FPassDrawCommandBuildRequestArray DynamicBuildRequests[EMeshPass::Num];
int32 NumDynamicBuildRequestElements[EMeshPass::Num];
bool bUseCachedMeshDrawCommands;
void AddCommandsForMesh(
int32 PrimitiveIndex,
const FPrimitiveSceneInfo* InPrimitiveSceneInfo,
const FStaticMeshBatchRelevance& RESTRICT StaticMeshRelevance,
const FStaticMeshBatch& RESTRICT StaticMesh,
const FScene* RESTRICT Scene,
bool bCanCache,
EMeshPass::Type PassType)
{
const EShadingPath ShadingPath = Scene->GetShadingPath();
const bool bUseCachedMeshCommand = bUseCachedMeshDrawCommands
&& !!(FPassProcessorManager::GetPassFlags(ShadingPath, PassType) & EMeshPassFlags::CachedMeshCommands)
&& StaticMeshRelevance.bSupportsCachingMeshDrawCommands
&& bCanCache;
if (bUseCachedMeshCommand)
{
const int32 StaticMeshCommandInfoIndex = StaticMeshRelevance.GetStaticMeshCommandInfoIndex(PassType);
if (StaticMeshCommandInfoIndex >= 0)
{
const FCachedMeshDrawCommandInfo& CachedMeshDrawCommand = InPrimitiveSceneInfo->StaticMeshCommandInfos[StaticMeshCommandInfoIndex];
const FCachedPassMeshDrawList& SceneDrawList = Scene->CachedDrawLists[PassType];
// AddUninitialized_GetRef()
VisibleCachedDrawCommands[(uint32)PassType].AddUninitialized();
FVisibleMeshDrawCommand& NewVisibleMeshDrawCommand = VisibleCachedDrawCommands[(uint32)PassType].Last();
const FMeshDrawCommand* MeshDrawCommand = CachedMeshDrawCommand.StateBucketId >= 0
? &Scene->CachedMeshDrawCommandStateBuckets[PassType].GetByElementId(CachedMeshDrawCommand.StateBucketId).Key
: &SceneDrawList.MeshDrawCommands[CachedMeshDrawCommand.CommandIndex];
NewVisibleMeshDrawCommand.Setup(
MeshDrawCommand,
PrimitiveIndex,
PrimitiveIndex,
CachedMeshDrawCommand.StateBucketId,
CachedMeshDrawCommand.MeshFillMode,
CachedMeshDrawCommand.MeshCullMode,
CachedMeshDrawCommand.SortKey);
}
}
else
{
NumDynamicBuildRequestElements[PassType] += StaticMeshRelevance.NumElements;
DynamicBuildRequests[PassType].Add(&StaticMesh);
}
}
};
void FSceneRenderer::SetupMeshPass(FViewInfo& View, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, FViewCommands& ViewCommands)
{
SCOPE_CYCLE_COUNTER(STAT_SetupMeshPass);
const EShadingPath ShadingPath = Scene->GetShadingPath();
for (int32 PassIndex = 0; PassIndex < EMeshPass::Num; PassIndex++)
{
const EMeshPass::Type PassType = (EMeshPass::Type)PassIndex;
if ((FPassProcessorManager::GetPassFlags(ShadingPath, PassType) & EMeshPassFlags::MainView) != EMeshPassFlags::None)
{
// Mobile: BasePass and MobileBasePassCSM lists need to be merged and sorted after shadow pass.
if (ShadingPath == EShadingPath::Mobile && (PassType == EMeshPass::BasePass || PassType == EMeshPass::MobileBasePassCSM))
{
continue;
}
if (ViewFamily.UseDebugViewPS() && ShadingPath == EShadingPath::Deferred)
{
switch (PassType)
{
case EMeshPass::DepthPass:
case EMeshPass::CustomDepth:
case EMeshPass::DebugViewMode:
#if WITH_EDITOR
case EMeshPass::HitProxy:
case EMeshPass::HitProxyOpaqueOnly:
case EMeshPass::EditorSelection:
#endif
break;
default:
continue;
}
}
PassProcessorCreateFunction CreateFunction = FPassProcessorManager::GetCreateFunction(ShadingPath, PassType);
FMeshPassProcessor* MeshPassProcessor = CreateFunction(Scene, &View, nullptr);
FParallelMeshDrawCommandPass& Pass = View.ParallelMeshDrawCommandPasses[PassIndex];
if (ShouldDumpMeshDrawCommandInstancingStats())
{
Pass.SetDumpInstancingStats(GetMeshPassName(PassType));
}
Pass.DispatchPassSetup(
Scene,
View,
PassType,
BasePassDepthStencilAccess,
MeshPassProcessor,
View.DynamicMeshElements,
&View.DynamicMeshElementsPassRelevance,
View.NumVisibleDynamicMeshElements[PassType],
ViewCommands.DynamicMeshCommandBuildRequests[PassType],
ViewCommands.NumDynamicMeshCommandBuildRequestElements[PassType],
ViewCommands.MeshCommands[PassIndex]);
}
}
}