struct FRelevancePacket : public FSceneRenderingAllocatorObject<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;
#if WITH_EDITOR
FRelevancePrimSet<FPrimitiveSceneInfo*> EditorVisualizeLevelInstancePrimitives;
FRelevancePrimSet<FPrimitiveSceneInfo*> EditorSelectedPrimitives;
#endif
TArray<FMeshDecalBatch> MeshDecalBatches;
TArray<FVolumetricMeshBatch> VolumetricMeshBatches;
TArray<FSkyMeshBatch> SkyMeshBatches;
TArray<FSortedTrianglesMeshBatch> SortedTrianglesMeshBatches;
FDrawCommandRelevancePacket DrawCommandPacket;
TSet<uint32> CustomDepthStencilValues;
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 bUsesCustomDepth;
bool bUsesCustomStencil;
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->Time.GetWorldTimeSeconds())
, DeltaWorldTime(InView.Family->Time.GetDeltaWorldTimeSeconds())
, 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)
, bUsesCustomDepth(false)
, bUsesCustomStencil(false)
, bSceneHasSkyMaterial(false)
, bHasSingleLayerWaterMaterial(false)
, bHasTranslucencySeparateModulation(false)
{
}
void AnyThreadTask()
{
FOptionalTaskTagScope Scope(ETaskTag::EParallelRenderingThread);
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 bEditorVisualizeLevelInstanceRelevance = ViewRelevance.bEditorVisualizeLevelInstanceRelevance;
const bool bEditorSelectionRelevance = ViewRelevance.bEditorStaticSelectionRelevance;
const bool bTranslucentRelevance = ViewRelevance.HasTranslucency();
const bool bHairStrandsEnabled = ViewRelevance.bHairStrands && IsHairStrandsEnabled(EHairStrandsShaderType::All, Scene->GetShaderPlatform());
if (View.bIsReflectionCapture && !PrimitiveSceneInfo->Proxy->IsVisibleInReflectionCaptures())
{
NotDrawRelevant.AddPrim(BitIndex);
continue;
}
if (bStaticRelevance && (bDrawRelevance || bShadowRelevance))
{
RelevantStaticPrimitives.AddPrim(BitIndex);
}
if (!bDrawRelevance)
{
NotDrawRelevant.AddPrim(BitIndex);
continue;
}
#if WITH_EDITOR
if (bEditorVisualizeLevelInstanceRelevance)
{
EditorVisualizeLevelInstancePrimitives.AddPrim(PrimitiveSceneInfo);
}
if (bEditorSelectionRelevance)
{
EditorSelectedPrimitives.AddPrim(PrimitiveSceneInfo);
}
#endif
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);
}
}
else if (bHairStrandsEnabled)
{
// Strands MeshElement
++NumVisibleDynamicPrimitives;
OutHasDynamicMeshElementsMasks[BitIndex] |= ViewBit;
}
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);
}
if (ViewRelevance.bPostMotionBlurTranslucency)
{
TranslucentPrimCount.Add(ETranslucencyPass::TPT_TranslucencyAfterMotionBlur, 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;
bUsesCustomDepth |= (ViewRelevance.CustomDepthStencilUsageMask & 1) > 0;
bUsesCustomStencil |= (ViewRelevance.CustomDepthStencilUsageMask & (1 << 1)) > 0;
bSceneHasSkyMaterial |= ViewRelevance.bUsesSkyMaterial;
bHasSingleLayerWaterMaterial |= ViewRelevance.bUsesSingleLayerWaterMaterial;
bHasTranslucencySeparateModulation |= ViewRelevance.bSeparateTranslucencyModulate;
if (ViewRelevance.bRenderCustomDepth)
{
bHasCustomDepthPrimitives = true;
CustomDepthStencilValues.Add(PrimitiveSceneInfo->Proxy->GetCustomDepthStencilValue());
}
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 bMobileMaskedInEarlyPass = (ShadingPath == EShadingPath::Mobile) && Scene->EarlyZPassMode == DDM_MaskedOnly;
const bool bMobileBasePassAlwaysUsesCSM = (ShadingPath == EShadingPath::Mobile) && MobileBasePassAlwaysUsesCSM(Scene->GetShaderPlatform());
const bool bVelocityPassWritesDepth = Scene->EarlyZPassMode == DDM_AllOpaqueNoVelocity;
const bool bHLODActive = Scene->SceneLODHierarchy.IsActive();
const FHLODVisibilityState* const HLODState = bHLODActive && ViewState ? &ViewState->HLODVisibilityState : nullptr;
float MaxDrawDistanceScale = GetCachedScalabilityCVars().ViewDistanceScale;
MaxDrawDistanceScale *= GetCachedScalabilityCVars().CalculateFieldOfViewDistanceScale(View.DesiredFOV);
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 || ((ShadingPath != EShadingPath::Mobile) && (FMath::Square(Bounds.BoxSphereBounds.SphereRadius) > GMinScreenRadiusForDepthPrepass * GMinScreenRadiusForDepthPrepass * LODFactorDistanceSquared));
const bool bAddLightmapDensityCommands = View.Family->EngineShowFlags.LightMapDensity && AllowDebugViewmodes();
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 (StaticMesh.bOverlayMaterial && !View.Family->EngineShowFlags.DistanceCulledPrimitives)
{
// Overlay mesh can have its own cull distance that is shorter than primitive cull distance
float OverlayMaterialMaxDrawDistance = StaticMeshRelevance.ScreenSize;
if (OverlayMaterialMaxDrawDistance > 1.f && OverlayMaterialMaxDrawDistance != FLT_MAX)
{
if (DistanceSquared > FMath::Square(OverlayMaterialMaxDrawDistance * MaxDrawDistanceScale))
{
// distance culled
continue;
}
}
}
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)
{
// Add velocity commands first to track for case where velocity pass writes depth.
bool bIsMeshInVelocityPass = false;
if (StaticMeshRelevance.bUseForMaterial && ViewRelevance.bRenderInMainPass)
{
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);
bIsMeshInVelocityPass = true;
}
if (ViewRelevance.bOutputsTranslucentVelocity &&
FTranslucentVelocityMeshProcessor::PrimitiveCanHaveVelocity(View.GetShaderPlatform(), PrimitiveSceneProxy) &&
FTranslucentVelocityMeshProcessor::PrimitiveHasVelocityForFrame(PrimitiveSceneProxy))
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::TranslucentVelocity);
}
}
}
}
// Add depth commands.
if (StaticMeshRelevance.bUseForDepthPass && (bDrawDepthOnly || (bMobileMaskedInEarlyPass && ViewRelevance.bMasked)))
{
if (!(bIsMeshInVelocityPass && bVelocityPassWritesDepth))
{
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))
{
// Specific logic for mobile packets
if (ShadingPath == EShadingPath::Mobile)
{
// Skydome must not be added to base pass bucket
if (!StaticMeshRelevance.bUseSkyMaterial)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::BasePass);
if (!bMobileBasePassAlwaysUsesCSM)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::MobileBasePassCSM);
}
}
else
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::SkyPass);
}
// bUseSingleLayerWaterMaterial is added to BasePass on Mobile. No need to add it to SingleLayerWaterPass
MarkMask |= EMarkMaskBits::StaticMeshVisibilityMapMask;
}
else // Regular shading path
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::BasePass);
MarkMask |= EMarkMaskBits::StaticMeshVisibilityMapMask;
if (StaticMeshRelevance.bUseSkyMaterial)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::SkyPass);
}
if (StaticMeshRelevance.bUseSingleLayerWaterMaterial)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::SingleLayerWaterPass);
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::SingleLayerWaterDepthPrepass);
}
}
if (StaticMeshRelevance.bUseAnisotropy)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::AnisotropyPass);
}
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
++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);
}
if (ViewRelevance.bPostMotionBlurTranslucency)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::TranslucencyAfterMotionBlur);
}
}
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.bTranslucentSurfaceLighting)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::LumenTranslucencyRadianceCacheMark);
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::LumenFrontLayerTranslucencyGBuffer);
}
if (ViewRelevance.bDistortion)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::Distortion);
}
}
#if WITH_EDITOR
if (ViewRelevance.bEditorVisualizeLevelInstanceRelevance)
{
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::EditorLevelInstance);
}
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.HasTranslucency() && PrimitiveSceneInfo->Proxy->SupportsSortedTriangles()) // Need to check material as well
{
SortedTrianglesMeshBatches.AddUninitialized(1);
FSortedTrianglesMeshBatch& BatchAndProxy = SortedTrianglesMeshBatches.Last();
BatchAndProxy.Mesh = &StaticMesh;
BatchAndProxy.Proxy = PrimitiveSceneInfo->Proxy;
}
// FIXME: Now if a primitive has one batch with a decal material all primitive mesh batches will be added as decals
// Because ViewRelevance is a sum of all material relevances in the primitive
if (ViewRelevance.bRenderInMainPass && ViewRelevance.bDecal && StaticMeshRelevance.bUseForMaterial)
{
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;
}
#if WITH_EDITOR
auto AddRelevantHitProxiesToArray = [](FRelevancePrimSet<FPrimitiveSceneInfo*>& PrimSet, TArray<uint32>& OutHitProxyArray)
{
int32 TotalHitProxiesToAdd = 0;
for (int32 Idx = 0; Idx < PrimSet.NumPrims; ++Idx)
{
if (PrimSet.Prims[Idx]->NaniteHitProxyIds.Num())
{
TotalHitProxiesToAdd += PrimSet.Prims[Idx]->NaniteHitProxyIds.Num();
}
}
OutHitProxyArray.Reserve(OutHitProxyArray.Num() + TotalHitProxiesToAdd);
for (int32 Idx = 0; Idx < PrimSet.NumPrims; ++Idx)
{
if (PrimSet.Prims[Idx]->NaniteHitProxyIds.Num())
{
for (uint32 IdValue : PrimSet.Prims[Idx]->NaniteHitProxyIds)
{
OutHitProxyArray.Add(IdValue);
}
}
}
};
// Add hit proxies from editing LevelInstance Nanite primitives
AddRelevantHitProxiesToArray(EditorVisualizeLevelInstancePrimitives, WriteView.EditorVisualizeLevelInstanceIds);
// Add hit proxies from selected Nanite primitives.
AddRelevantHitProxiesToArray(EditorSelectedPrimitives, WriteView.EditorSelectedHitProxyIds);
#endif
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.CustomDepthStencilValues.Append(CustomDepthStencilValues);
WriteView.bUsesCustomDepth |= bUsesCustomDepth;
WriteView.bUsesCustomStencil |= bUsesCustomStencil;
DirtyIndirectLightingCacheBufferPrimitives.AppendTo(WriteView.DirtyIndirectLightingCacheBufferPrimitives);
WriteView.MeshDecalBatches.Append(MeshDecalBatches);
WriteView.VolumetricMeshBatches.Append(VolumetricMeshBatches);
WriteView.SkyMeshBatches.Append(SkyMeshBatches);
WriteView.SortedTrianglesMeshBatches.Append(SortedTrianglesMeshBatches);
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);
WriteScene.GPUScene.AddPrimitiveToUpdate(PrimitiveSceneInfo->GetIndex(), EPrimitiveDirtyState::ChangedAll);
}
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);
}
}
}
};
typedef TArray<FVisibleMeshDrawCommand> FPassDrawCommandArray;
typedef TArray<const FStaticMeshBatch*> FPassDrawCommandBuildRequestArray;
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,
FMeshDrawCommandPrimitiveIdInfo(PrimitiveIndex, InPrimitiveSceneInfo->GetInstanceSceneDataOffset()),
CachedMeshDrawCommand.StateBucketId,
CachedMeshDrawCommand.MeshFillMode,
CachedMeshDrawCommand.MeshCullMode,
CachedMeshDrawCommand.Flags,
CachedMeshDrawCommand.SortKey);
}
}
else
{
NumDynamicBuildRequestElements[PassType] += StaticMeshRelevance.NumElements;
DynamicBuildRequests[PassType].Add(&StaticMesh);
}
}
};