来自uod2019 refactoring the mesh drawing pipeline for unreal engine 4.22
FMeshDrawCommand 存储了rhi在一个mesh pass渲染调用需要知道的所有信息
包含管线状态、shader及资源绑定、渲染命令参数等。
/** * FMeshDrawCommand fully describes a mesh pass draw call, captured just above the RHI. FMeshDrawCommand should contain only data needed to draw. For InitViews payloads, use FVisibleMeshDrawCommand. * FMeshDrawCommands are cached at Primitive AddToScene time for vertex factories that support it (no per-frame or per-view shader binding changes). * Dynamic Instancing operates at the FMeshDrawCommand level for robustness. Adding per-command shader bindings will reduce the efficiency of Dynamic Instancing, but rendering will always be correct. * Any resources referenced by a command must be kept alive for the lifetime of the command. FMeshDrawCommand is not responsible for lifetime management of resources. For uniform buffers referenced by cached FMeshDrawCommand's, RHIUpdateUniformBuffer makes it possible to access per-frame data in the shader without changing bindings. */ class FMeshDrawCommand { public: /** * Resource bindings */ FMeshDrawShaderBindings ShaderBindings; FVertexInputStreamArray VertexStreams; FIndexBufferRHIParamRef IndexBuffer; /** * PSO */ FGraphicsMinimalPipelineStateId CachedPipelineId; /** * Draw command parameters */ uint32 FirstIndex; uint32 NumPrimitives; uint32 NumInstances; union { struct { uint32 BaseVertexIndex; uint32 NumVertices; } VertexParams; FVertexBufferRHIParamRef IndirectArgsBuffer; }; int8 PrimitiveIdStreamIndex; /** Non-pipeline state */ uint8 StencilRef; FMeshDrawCommand() {} bool MatchesForDynamicInstancing(const FMeshDrawCommand& Rhs) const { return CachedPipelineId == Rhs.CachedPipelineId && StencilRef == Rhs.StencilRef && ShaderBindings.MatchesForDynamicInstancing(Rhs.ShaderBindings) && VertexStreams == Rhs.VertexStreams && PrimitiveIdStreamIndex == Rhs.PrimitiveIdStreamIndex && IndexBuffer == Rhs.IndexBuffer && FirstIndex == Rhs.FirstIndex && NumPrimitives == Rhs.NumPrimitives && NumInstances == Rhs.NumInstances && ((NumPrimitives > 0 && VertexParams.BaseVertexIndex == Rhs.VertexParams.BaseVertexIndex && VertexParams.NumVertices == Rhs.VertexParams.NumVertices) || (NumPrimitives == 0 && IndirectArgsBuffer == Rhs.IndirectArgsBuffer)); } /** Sets shaders on the mesh draw command and allocates room for the shader bindings. */ RENDERER_API void SetShaders(FVertexDeclarationRHIParamRef VertexDeclaration, const FMeshProcessorShaders& Shaders, FGraphicsMinimalPipelineStateInitializer& PipelineState); inline void SetStencilRef(uint32 InStencilRef) { StencilRef = InStencilRef; // Verify no overflow checkSlow((uint32)StencilRef == InStencilRef); } /** Called when the mesh draw command is complete. */ RENDERER_API void SetDrawParametersAndFinalize( const FMeshBatch& MeshBatch, int32 BatchElementIndex, FGraphicsMinimalPipelineStateId PipelineId, const FMeshProcessorShaders* ShadersForDebugging); void Finalize(FGraphicsMinimalPipelineStateId PipelineId, const FMeshProcessorShaders* ShadersForDebugging) { CachedPipelineId = PipelineId; ShaderBindings.Finalize(ShadersForDebugging); } /** Submits commands to the RHI Commandlist to draw the MeshDrawCommand. */ static void SubmitDraw( const FMeshDrawCommand& RESTRICT MeshDrawCommand, FVertexBufferRHIParamRef ScenePrimitiveIdsBuffer, int32 PrimitiveIdOffset, uint32 InstanceFactor, FRHICommandList& CommandList, class FMeshDrawCommandStateCache& RESTRICT StateCache); FORCENOINLINE friend uint32 GetTypeHash( const FMeshDrawCommand& Other ) { return Other.CachedPipelineId.GetId(); } void SetDebugData(const FPrimitiveSceneProxy* PrimitiveSceneProxy, const FMaterial* Material, const FMaterialRenderProxy* MaterialRenderProxy, const FMeshProcessorShaders& UntypedShaders) { #if MESH_DRAW_COMMAND_DEBUG_DATA DebugData.PrimitiveSceneProxy = PrimitiveSceneProxy; DebugData.Material = Material; DebugData.MaterialRenderProxy = MaterialRenderProxy; DebugData.VertexShader = UntypedShaders.VertexShader; DebugData.PixelShader = UntypedShaders.PixelShader; #endif } SIZE_T GetAllocatedSize() const { return ShaderBindings.GetAllocatedSize() + VertexStreams.GetAllocatedSize(); } SIZE_T GetDebugDataSize() const { #if MESH_DRAW_COMMAND_DEBUG_DATA return sizeof(DebugData); #endif return 0; } #if MESH_DRAW_COMMAND_DEBUG_DATA private: FMeshDrawCommandDebugData DebugData; #endif };
在FPrimitive的AddToScene时,就为静态几何体创建了FMeshDrawCommand。
在void FScene::UpdatePrimitiveTransform(UPrimitiveComponent* Primitive)中,
在void FScene::AddPrimitive(UPrimitiveComponent* Primitive)中,调用了
在void FScene::AddPrimitiveSceneInfo_RenderThread(FRHICommandListImmediate& RHICmdList, FPrimitiveSceneInfo* PrimitiveSceneInfo)函数里:
FPrimitiveSceneInfo::AddToScene中:
void FPrimitiveSceneInfo::AddToScene(FRHICommandListImmediate& RHICmdList, bool bUpdateStaticDrawLists, bool bAddToStaticDrawLists) { check(IsInRenderingThread()); // Create an indirect lighting cache uniform buffer if we attaching a primitive that may require it, as it may be stored inside a cached mesh command. if (IsIndirectLightingCacheAllowed(Scene->GetFeatureLevel()) && Proxy->WillEverBeLit() && ((Proxy->HasStaticLighting() && Proxy->NeedsUnbuiltPreviewLighting()) || (Proxy->IsMovable() && Proxy->GetIndirectLightingCacheQuality() != ILCQ_Off))) { if (!IndirectLightingCacheUniformBuffer) { FIndirectLightingCacheUniformParameters Parameters; GetIndirectLightingCacheParameters( Scene->GetFeatureLevel(), Parameters, nullptr, nullptr, FVector(0.0f, 0.0f, 0.0f), 0, nullptr); IndirectLightingCacheUniformBuffer = TUniformBufferRef<FIndirectLightingCacheUniformParameters>::CreateUniformBufferImmediate(Parameters, UniformBuffer_MultiFrame, EUniformBufferValidation::None); } } // If we are attaching a primitive that should be statically lit but has unbuilt lighting, // Allocate space in the indirect lighting cache so that it can be used for previewing indirect lighting if (Proxy->HasStaticLighting() && Proxy->NeedsUnbuiltPreviewLighting() && IsIndirectLightingCacheAllowed(Scene->GetFeatureLevel())) { FIndirectLightingCacheAllocation* PrimitiveAllocation = Scene->IndirectLightingCache.FindPrimitiveAllocation(PrimitiveComponentId); if (PrimitiveAllocation) { IndirectLightingCacheAllocation = PrimitiveAllocation; PrimitiveAllocation->SetDirty(); } else { PrimitiveAllocation = Scene->IndirectLightingCache.AllocatePrimitive(this, true); PrimitiveAllocation->SetDirty(); IndirectLightingCacheAllocation = PrimitiveAllocation; } } MarkIndirectLightingCacheBufferDirty(); FPrimitiveSceneProxy::FLCIArray LCIs; Proxy->GetLCIs(LCIs); for (int32 i = 0; i < LCIs.Num(); ++i) { FLightCacheInterface* LCI = LCIs[i]; if (LCI) { LCI->CreatePrecomputedLightingUniformBuffer_RenderingThread(Scene->GetFeatureLevel()); } } NumLightmapDataEntries = LCIs.Num(); if (NumLightmapDataEntries > 0 && UseGPUScene(GMaxRHIShaderPlatform, Scene->GetFeatureLevel())) { LightmapDataOffset = Scene->GPUScene.LightmapDataAllocator.Allocate(NumLightmapDataEntries); } // Cache the nearest reflection proxy if needed if (NeedsReflectionCaptureUpdate()) { CacheReflectionCaptures(); } if (bUpdateStaticDrawLists) { AddStaticMeshes(RHICmdList, bAddToStaticDrawLists); } // create potential storage for our compact info FPrimitiveSceneInfoCompact CompactPrimitiveSceneInfo(this); // Add the primitive to the octree. check(!OctreeId.IsValidId()); Scene->PrimitiveOctree.AddElement(CompactPrimitiveSceneInfo); check(OctreeId.IsValidId()); if (Proxy->CastsDynamicIndirectShadow()) { Scene->DynamicIndirectCasterPrimitives.Add(this); } Scene->PrimitiveSceneProxies[PackedIndex] = Proxy; Scene->PrimitiveTransforms[PackedIndex] = Proxy->GetLocalToWorld(); // Set bounds. FPrimitiveBounds& PrimitiveBounds = Scene->PrimitiveBounds[PackedIndex]; FBoxSphereBounds BoxSphereBounds = Proxy->GetBounds(); PrimitiveBounds.BoxSphereBounds = BoxSphereBounds; PrimitiveBounds.MinDrawDistanceSq = FMath::Square(Proxy->GetMinDrawDistance()); PrimitiveBounds.MaxDrawDistance = Proxy->GetMaxDrawDistance(); PrimitiveBounds.MaxCullDistance = PrimitiveBounds.MaxDrawDistance; Scene->PrimitiveFlagsCompact[PackedIndex] = FPrimitiveFlagsCompact(Proxy); // Store precomputed visibility ID. int32 VisibilityBitIndex = Proxy->GetVisibilityId(); FPrimitiveVisibilityId& VisibilityId = Scene->PrimitiveVisibilityIds[PackedIndex]; VisibilityId.ByteIndex = VisibilityBitIndex / 8; VisibilityId.BitMask = (1 << (VisibilityBitIndex & 0x7)); // Store occlusion flags. uint8 OcclusionFlags = EOcclusionFlags::None; if (Proxy->CanBeOccluded()) { OcclusionFlags |= EOcclusionFlags::CanBeOccluded; } if (Proxy->HasSubprimitiveOcclusionQueries()) { OcclusionFlags |= EOcclusionFlags::HasSubprimitiveQueries; } if (Proxy->AllowApproximateOcclusion() // Allow approximate occlusion if attached, even if the parent does not have bLightAttachmentsAsGroup enabled || LightingAttachmentRoot.IsValid()) { OcclusionFlags |= EOcclusionFlags::AllowApproximateOcclusion; } if (VisibilityBitIndex >= 0) { OcclusionFlags |= EOcclusionFlags::HasPrecomputedVisibility; } Scene->PrimitiveOcclusionFlags[PackedIndex] = OcclusionFlags; // Store occlusion bounds. FBoxSphereBounds OcclusionBounds = BoxSphereBounds; if (Proxy->HasCustomOcclusionBounds()) { OcclusionBounds = Proxy->GetCustomOcclusionBounds(); } OcclusionBounds.BoxExtent.X = OcclusionBounds.BoxExtent.X + OCCLUSION_SLOP; OcclusionBounds.BoxExtent.Y = OcclusionBounds.BoxExtent.Y + OCCLUSION_SLOP; OcclusionBounds.BoxExtent.Z = OcclusionBounds.BoxExtent.Z + OCCLUSION_SLOP; OcclusionBounds.SphereRadius = OcclusionBounds.SphereRadius + OCCLUSION_SLOP; Scene->PrimitiveOcclusionBounds[PackedIndex] = OcclusionBounds; // Store the component. Scene->PrimitiveComponentIds[PackedIndex] = PrimitiveComponentId; { FMemMark MemStackMark(FMemStack::Get()); // Find lights that affect the primitive in the light octree. for (FSceneLightOctree::TConstElementBoxIterator<SceneRenderingAllocator> LightIt(Scene->LightOctree, Proxy->GetBounds().GetBox()); LightIt.HasPendingElements(); LightIt.Advance()) { const FLightSceneInfoCompact& LightSceneInfoCompact = LightIt.GetCurrentElement(); if (LightSceneInfoCompact.AffectsPrimitive(CompactPrimitiveSceneInfo.Bounds, CompactPrimitiveSceneInfo.Proxy)) { FLightPrimitiveInteraction::Create(LightSceneInfoCompact.LightSceneInfo,this); } } } INC_MEMORY_STAT_BY(STAT_PrimitiveInfoMemory, sizeof(*this) + StaticMeshes.GetAllocatedSize() + StaticMeshRelevances.GetAllocatedSize() + Proxy->GetMemoryFootprint()); }
FPrimitiveSceneInfo的AddStaticMeshes函数中:
void FPrimitiveSceneInfo::AddStaticMeshes(FRHICommandListImmediate& RHICmdList, bool bAddToStaticDrawLists) { // Cache the primitive's static mesh elements. FBatchingSPDI BatchingSPDI(this); BatchingSPDI.SetHitProxy(DefaultDynamicHitProxy); Proxy->DrawStaticElements(&BatchingSPDI); StaticMeshes.Shrink(); StaticMeshRelevances.Shrink(); check(StaticMeshRelevances.Num() == StaticMeshes.Num()); for(int32 MeshIndex = 0;MeshIndex < StaticMeshes.Num();MeshIndex++) { FStaticMeshBatchRelevance& MeshRelevance = StaticMeshRelevances[MeshIndex]; FStaticMeshBatch& Mesh = StaticMeshes[MeshIndex]; // Add the static mesh to the scene's static mesh list. FSparseArrayAllocationInfo SceneArrayAllocation = Scene->StaticMeshes.AddUninitialized(); Scene->StaticMeshes[SceneArrayAllocation.Index] = &Mesh; Mesh.Id = SceneArrayAllocation.Index; MeshRelevance.Id = SceneArrayAllocation.Index; if (Mesh.bRequiresPerElementVisibility) { // Use a separate index into StaticMeshBatchVisibility, since most meshes don't use it Mesh.BatchVisibilityId = Scene->StaticMeshBatchVisibility.AddUninitialized().Index; Scene->StaticMeshBatchVisibility[Mesh.BatchVisibilityId] = true; } } if (bAddToStaticDrawLists) { CacheMeshDrawCommands(RHICmdList); } }
上面这句代码,CacheMeshDrawCommands, 创建了FMeshDrawCommand,
void FPrimitiveSceneInfo::CacheMeshDrawCommands(FRHICommandListImmediate& RHICmdList) { check(StaticMeshCommandInfos.Num() == 0); int32 MeshWithCachedCommandsNum = 0; for (int32 MeshIndex = 0; MeshIndex < StaticMeshes.Num(); MeshIndex++) { const FStaticMeshBatch& Mesh = StaticMeshes[MeshIndex]; if (SupportsCachingMeshDrawCommands(Mesh.VertexFactory, Proxy)) { ++MeshWithCachedCommandsNum; } } #if RHI_RAYTRACING if (IsRayTracingEnabled()) { int MaxLOD = -1; for (int32 MeshIndex = 0; MeshIndex < StaticMeshes.Num(); MeshIndex++) { FStaticMeshBatch& Mesh = StaticMeshes[MeshIndex]; MaxLOD = MaxLOD < Mesh.LODIndex ? Mesh.LODIndex : MaxLOD; } if (StaticMeshes.Num() > 0) { CachedRayTracingMeshCommandIndicesPerLOD.Empty(MaxLOD + 1); CachedRayTracingMeshCommandIndicesPerLOD.AddDefaulted(MaxLOD + 1); } } #endif if (MeshWithCachedCommandsNum > 0) { //@todo - only need material uniform buffers to be created since we are going to cache pointers to them // Any updates (after initial creation) don't need to be forced here FMaterialRenderProxy::UpdateDeferredCachedUniformExpressions(); // Reserve based on assumption that we have on average 2 cached mesh draw commands per mesh. StaticMeshCommandInfos.Reserve(MeshWithCachedCommandsNum * 2); QUICK_SCOPE_CYCLE_COUNTER(STAT_CacheMeshDrawCommands); FMemMark Mark(FMemStack::Get()); const EShadingPath ShadingPath = Scene->GetShadingPath(); for (int32 MeshIndex = 0; MeshIndex < StaticMeshes.Num(); MeshIndex++) { FStaticMeshBatchRelevance& MeshRelevance = StaticMeshRelevances[MeshIndex]; FStaticMeshBatch& Mesh = StaticMeshes[MeshIndex]; check(MeshRelevance.CommandInfosMask.IsEmpty()); MeshRelevance.CommandInfosBase = StaticMeshCommandInfos.Num(); if (SupportsCachingMeshDrawCommands(Mesh.VertexFactory, Proxy)) { for (int32 PassIndex = 0; PassIndex < EMeshPass::Num; PassIndex++) { EMeshPass::Type PassType = (EMeshPass::Type)PassIndex; if ((FPassProcessorManager::GetPassFlags(ShadingPath, PassType) & EMeshPassFlags::CachedMeshCommands) != EMeshPassFlags::None) { FCachedMeshDrawCommandInfo CommandInfo; CommandInfo.MeshPass = PassType; FCachedPassMeshDrawList& SceneDrawList = Scene->CachedDrawLists[PassType]; FCachedPassMeshDrawListContext CachedPassMeshDrawListContext(CommandInfo, SceneDrawList, *Scene); PassProcessorCreateFunction CreateFunction = FPassProcessorManager::GetCreateFunction(ShadingPath, PassType); FMeshPassProcessor* PassMeshProcessor = CreateFunction(Scene, nullptr, &CachedPassMeshDrawListContext); if (PassMeshProcessor != nullptr) { check(!Mesh.bRequiresPerElementVisibility); uint64 BatchElementMask = ~0ull; PassMeshProcessor->AddMeshBatch(Mesh, BatchElementMask, Proxy); PassMeshProcessor->~FMeshPassProcessor(); } if (CommandInfo.CommandIndex != -1 || CommandInfo.StateBucketId != -1) { static_assert(sizeof(MeshRelevance.CommandInfosMask) * 8 >= EMeshPass::Num, "CommandInfosMask is too small to contain all mesh passes."); MeshRelevance.CommandInfosMask.Set(PassType); StaticMeshCommandInfos.Add(CommandInfo); #if DO_GUARD_SLOW if (ShadingPath == EShadingPath::Deferred) { FMeshDrawCommand* MeshDrawCommand = CommandInfo.StateBucketId >= 0 ? &Scene->CachedMeshDrawCommandStateBuckets[FSetElementId::FromInteger(CommandInfo.StateBucketId)].MeshDrawCommand : &SceneDrawList.MeshDrawCommands[CommandInfo.CommandIndex]; ensureMsgf(MeshDrawCommand->VertexStreams.GetAllocatedSize() == 0, TEXT("Cached Mesh Draw command overflows VertexStreams. VertexStream inline size should be tweaked.")); if (PassType == EMeshPass::BasePass || PassType == EMeshPass::DepthPass || PassType == EMeshPass::CSMShadowDepth) { TArray<EShaderFrequency, TInlineAllocator<SF_NumFrequencies>> ShaderFrequencies; MeshDrawCommand->ShaderBindings.GetShaderFrequencies(ShaderFrequencies); for (int32 i = 0; i < ShaderFrequencies.Num(); i++) { FMeshDrawSingleShaderBindings SingleShaderBindings = MeshDrawCommand->ShaderBindings.GetSingleShaderBindings(ShaderFrequencies[i]); ensureMsgf(SingleShaderBindings.ParameterMapInfo.LooseParameterBuffers.Num() == 0, TEXT("Cached Mesh Draw command uses loose parameters. This will break dynamic instancing in performance critical pass. Use Uniform Buffers instead.")); ensureMsgf(SingleShaderBindings.ParameterMapInfo.SRVs.Num() == 0, TEXT("Cached Mesh Draw command uses individual SRVs. This will break dynamic instancing in performance critical pass. Use Uniform Buffers instead.")); ensureMsgf(SingleShaderBindings.ParameterMapInfo.TextureSamplers.Num() == 0, TEXT("Cached Mesh Draw command uses individual Texture Samplers. This will break dynamic instancing in performance critical pass. Use Uniform Buffers instead.")); } } } #endif } } } #if RHI_RAYTRACING if (IsRayTracingEnabled()) { FCachedRayTracingMeshCommandContext CommandContext(Scene->CachedRayTracingMeshCommands); FRayTracingMeshProcessor RayTracingMeshProcessor(&CommandContext, Scene, nullptr); check(!Mesh.bRequiresPerElementVisibility); RayTracingMeshProcessor.AddMeshBatch(Mesh, ~0ull, Proxy); CachedRayTracingMeshCommandIndicesPerLOD[Mesh.LODIndex].Add(CommandContext.CommandIndex); } #endif } } } }
我们有一个mesh batch,想要得到一个它的Mesh Draw Command,我们用FMeshPassProcessor(目的是创建meshDrawCommand):通过FMeshBatch,创建一个或者多个FMeshDrawCommands,它选择shader,收集pass绑定,vertex factory绑定,材质绑定,等等。我们生成一个它的子类,来添加一个新的mesh pass。它不是基于shader类的模板类型。同一套代码能被缓存和动态pass共用。
/** * Base class of mesh processors, whose job is to transform FMeshBatch draw descriptions received from scene proxy implementations into FMeshDrawCommands ready for the RHI command list */ class FMeshPassProcessor { public: const FScene* RESTRICT Scene; ERHIFeatureLevel::Type FeatureLevel; const FSceneView* ViewIfDynamicMeshCommand; FMeshPassDrawListContext* DrawListContext; RENDERER_API FMeshPassProcessor(const FScene* InScene, ERHIFeatureLevel::Type InFeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext); virtual ~FMeshPassProcessor() {} void SetDrawListContext(FMeshPassDrawListContext* InDrawListContext) { DrawListContext = InDrawListContext; } // FMeshPassProcessor interface // Add a FMeshBatch to the pass virtual void AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId = -1) = 0; static FORCEINLINE_DEBUGGABLE ERasterizerCullMode InverseCullMode(ERasterizerCullMode CullMode) { return CullMode == CM_None ? CM_None : (CullMode == CM_CCW ? CM_CW : CM_CCW); } RENDERER_API ERasterizerFillMode ComputeMeshFillMode(const FMeshBatch& Mesh, const FMaterial& InMaterialResource) const; RENDERER_API ERasterizerCullMode ComputeMeshCullMode(const FMeshBatch& Mesh, const FMaterial& InMaterialResource) const; template<typename PassShadersType, typename ShaderElementDataType> void BuildMeshDrawCommands( const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, const FMaterialRenderProxy& RESTRICT MaterialRenderProxy, const FMaterial& RESTRICT MaterialResource, const FMeshPassProcessorRenderState& RESTRICT DrawRenderState, PassShadersType PassShaders, ERasterizerFillMode MeshFillMode, ERasterizerCullMode MeshCullMode, FMeshDrawCommandSortKey SortKey, EMeshPassFeatures MeshPassFeatures, const ShaderElementDataType& ShaderElementData); private: RENDERER_API int32 GetDrawCommandPrimitiveId(const FPrimitiveSceneInfo* RESTRICT PrimitiveSceneInfo, const FMeshBatchElement& BatchElement) const; };
比如FDepthPassMeshProcessor类
void FDepthPassMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId) { bool bDraw = MeshBatch.bUseForDepthPass; // Filter by occluder flags and settings if required. if (bDraw && bRespectUseAsOccluderFlag && !MeshBatch.bUseAsOccluder && EarlyZPassMode < DDM_AllOpaque) { if (PrimitiveSceneProxy) { // Only render primitives marked as occluders. bDraw = PrimitiveSceneProxy->ShouldUseAsOccluder() // Only render static objects unless movable are requested. && (!PrimitiveSceneProxy->IsMovable() || bEarlyZPassMovable); // Filter dynamic mesh commands by screen size. if (ViewIfDynamicMeshCommand) { extern float GMinScreenRadiusForDepthPrepass; const float LODFactorDistanceSquared = (PrimitiveSceneProxy->GetBounds().Origin - ViewIfDynamicMeshCommand->ViewMatrices.GetViewOrigin()).SizeSquared() * FMath::Square(ViewIfDynamicMeshCommand->LODDistanceFactor); bDraw = bDraw && FMath::Square(PrimitiveSceneProxy->GetBounds().SphereRadius) > GMinScreenRadiusForDepthPrepass * GMinScreenRadiusForDepthPrepass * LODFactorDistanceSquared; } } else { bDraw = false; } } if (bDraw) { // Determine the mesh's material and blend mode. const FMaterialRenderProxy* FallbackMaterialRenderProxyPtr = nullptr; const FMaterial& Material = MeshBatch.MaterialRenderProxy->GetMaterialWithFallback(FeatureLevel, FallbackMaterialRenderProxyPtr); const FMaterialRenderProxy& MaterialRenderProxy = FallbackMaterialRenderProxyPtr ? *FallbackMaterialRenderProxyPtr : *MeshBatch.MaterialRenderProxy; const EBlendMode BlendMode = Material.GetBlendMode(); const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MeshBatch, Material); const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(MeshBatch, Material); const bool bIsTranslucent = IsTranslucentBlendMode(BlendMode); if (!bIsTranslucent && (!PrimitiveSceneProxy || PrimitiveSceneProxy->ShouldRenderInMainPass()) && ShouldIncludeDomainInMeshPass(Material.GetMaterialDomain())) { if (BlendMode == BLEND_Opaque && MeshBatch.VertexFactory->SupportsPositionOnlyStream() && !Material.MaterialModifiesMeshPosition_RenderThread() && Material.WritesEveryPixel()) { const FMaterialRenderProxy& DefaultProxy = *UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy(); const FMaterial& DefaultMaterial = *DefaultProxy.GetMaterial(FeatureLevel); Process<true>(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, DefaultProxy, DefaultMaterial, MeshFillMode, MeshCullMode); } else { const bool bMaterialMasked = !Material.WritesEveryPixel() || Material.IsTranslucencyWritingCustomDepth(); if (!bMaterialMasked || EarlyZPassMode != DDM_NonMaskedOnly) { const FMaterialRenderProxy* EffectiveMaterialRenderProxy = &MaterialRenderProxy; const FMaterial* EffectiveMaterial = &Material; if (!bMaterialMasked && !Material.MaterialModifiesMeshPosition_RenderThread()) { // Override with the default material for opaque materials that are not two sided EffectiveMaterialRenderProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy(); EffectiveMaterial = EffectiveMaterialRenderProxy->GetMaterial(FeatureLevel); } Process<false>(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, *EffectiveMaterialRenderProxy, *EffectiveMaterial, MeshFillMode, MeshCullMode); } } } } }
上面代码中,if(bIsTranslucent ...)这个条件是pass filter 不渲染半透物体 ,if(blendMode==blend_opaque ...)是在选择shader。
它的process函数
template<bool bPositionOnly> void FDepthPassMeshProcessor::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>, FDepthOnlyHS, FDepthOnlyDS, FDepthOnlyPS> DepthPassShaders; FShaderPipeline* ShaderPipeline = nullptr; GetDepthPassShaders<bPositionOnly>( MaterialResource, VertexFactory->GetType(), FeatureLevel, DepthPassShaders.HullShader, DepthPassShaders.DomainShader, DepthPassShaders.VertexShader, DepthPassShaders.PixelShader, ShaderPipeline, false ); FMeshPassProcessorRenderState DrawRenderState(PassDrawRenderState); SetDepthPassDitheredLODTransitionState(ViewIfDynamicMeshCommand, MeshBatch, StaticMeshId, DrawRenderState); FDepthOnlyShaderElementData ShaderElementData(0.0f); ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, true); const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(DepthPassShaders.VertexShader, DepthPassShaders.PixelShader); BuildMeshDrawCommands( MeshBatch, BatchElementMask, PrimitiveSceneProxy, MaterialRenderProxy, MaterialResource, DrawRenderState, DepthPassShaders, MeshFillMode, MeshCullMode, SortKey, bPositionOnly ? EMeshPassFeatures::PositionOnly : EMeshPassFeatures::Default, ShaderElementData); }
上面代码,选择shader的第二部分,GetDepthPassShaders函数。buildMeshDrawCommands进行了绑定的收集。buildMeshDrawCommands函数是所有depth pass公共的。GetDepthPassShaders函数是模板函数。
参数都集合到了FMeshDrawSingleShaderBindings里,不再直接设置给RHICmdList,直接问rhi来设置参数,float 4个偏移。把他们弄到shader binding 表里。缓存起来。
得到这些command就可以排序了。以前动态和静态之间不能排序,现在可以了。
新版本还增加了:自动merge Drawcall(instance),但有些限制,比如lightmap,vertex color,mobile platform(dx11才能用),sparse volume等。