來自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等。