ue4 new pipelines finishing

来自uod2019 refactoring the mesh drawing pipeline for unreal engine 4.22

FMeshDrawCommand rhi stores all information in a mesh pass rendering calls need to know

It includes pipeline state, shader and resource binding, rendering command parameters.

/** 
 * 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
};

When FPrimitive of AddToScene, it is a static geometry created FMeshDrawCommand.

 在void FScene::UpdatePrimitiveTransform(UPrimitiveComponent* Primitive)中,

In the void FScene :: AddPrimitive (UPrimitiveComponent * Primitive), call the

  In the void FScene :: AddPrimitiveSceneInfo_RenderThread (FRHICommandListImmediate & RHICmdList, FPrimitiveSceneInfo * PrimitiveSceneInfo) function in:

    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 of AddStaticMeshes function:

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);
    }
}

 Above this code, CacheMeshDrawCommands, created 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
            }
        }
    }
}

 

We have a mesh batch, want to get one of its Mesh Draw Command, we FMeshPassProcessor (aim is to create meshDrawCommand): By FMeshBatch, create one or more FMeshDrawCommands, it chooses shader, collect pass binding, vertex factory binding, binding material, and so on. We generate a subclass of it, to add a new mesh pass. It is not the type of template shader class-based. Cache and the same set of codes can be shared dynamically 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;
};

  For example FDepthPassMeshProcessor class

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);
                }
            }
        }
    }
}

The above code, if (bIsTranslucent ...) This condition is not to render the semipermeable pass filter object, if (blendMode == blend_opaque ...) is selected shader.

It is a function of 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);
}

The above code, selecting a second portion of the shader, GetDepthPassShaders function. buildMeshDrawCommands were collected binding. buildMeshDrawCommands function is common to all the depth pass. GetDepthPassShaders function is a function template.

A set of parameters to FMeshDrawSingleShaderBindings years, is no longer directly to RHICmdList, directly asked rhi to set the parameters, float 4 offsets. They get the shader binding table. Cached.

You can get these sort of command. Between static and dynamic past can not be sorted, now I can.

 

The new version also adds: automatic merge Drawcall (instance), but some restrictions, such as lightmap, vertex color, mobile platform (dx11 to use), sparse volume and so on.

Guess you like

Origin www.cnblogs.com/Shaojunping/p/11791358.html