
一个DrawCall 是如何生成的顺便整理一下GameThread、RenderThread、RHIThread 的接口调用流程一、GameThread//GameThread 的调用流程 void UGameEngine::Tick(float DeltaSeconds, bool bIdleMode) { void FViewport::Draw(bool bShouldPresent) { // 实际上调用的是FEditorViewportClient::Draw() / UGameViewportClient::Draw() void FViewportClient::Draw(FViewport* Viewport, FCanvas* Canvas) { // 计算ViewFamily、View的各种属性 FSceneView* View nullptr; View CalcSceneView(ViewFamily, ....); SetupViewForRendering(ViewFamily, *View); // 发送渲染命令 FRendererModule::BeginRenderingViewFamily(FCanvas * Canvas, FSceneViewFamily * ViewFamily) { World-SendAllEndOfFrameUpdates(); FSceneRenderBuilder SceneRenderBuilder(Scene); // 创建场景渲染器 TArrayFSceneRenderer*SceneRenderers SceneRenderBuilder.CreateLinkedSceneRenderers(ViewFamilies, Canvas-GetHitProxyConsumer()); for (FSceneRenderer* SceneRenderer : SceneRenderers) { // AddRenderer将这个SceneRenderer和Lambda 存入一个 FRenderNode 中 // 通过RenderNode生成一个FOp,存入到Ops SceneRenderBuilder.AddRenderer(SceneRenderer, .....), [](FRDGBuilder GraphBuilder, const FSceneRenderFunctionInputs Inputs) { //这个函数会在 RenderThread 调用 RenderViewFamily_RenderThread(RHICmdList, SceneRenderer) { ...... // 调用渲染器的绘制接口. SceneRenderer-Render(RHICmdList); ...... } return true; }); } //执行Ops SceneRenderBuilder.Execute() { for (FOp Op : Ops) { FRenderNode RenderNode *Op.Data_Render; //将上面的Lambda 变成renderCommand 添加到渲染线程执行 ENQUEUE_RENDER_COMMAND(SceneRenderBuilder_Render)([this, RenderNode](FRHICommandListImmediate RHICmdList) { ......... const bool bRenderCalled RenderNode.Function(GraphBuilder, FunctionInputs); ......... } } } } } } }二、RenderThread// RenderThread 调用流程 static void RenderViewFamily_RenderThread(FRDGBuilder GraphBuilder, FSceneRenderer* Renderer, const FSceneRenderUpdateInputs* SceneUpdateInputs) { // Renderer-Render(GraphBuilder, SceneUpdateInputs); // 实际可能是FDeferredShadingSceneRenderer / FMobileSceneRenderer FSceneRenderer::Render() { //FInitViewTaskDatas InitViewTaskDatas OnRenderBegin(GraphBuilder, SceneUpdateInputs); FSceneRenderer::OnRenderBegin(FRDGBuilder GraphBuilder,....) { //Scene-Update(GraphBuilder, SceneUpdateParameters); FScene::Update(FRDGBuilder GraphBuilder, const FUpdateParameters Parameters) { ...... //处理畸变、 视锥体剔除、 // FVisibilityTaskData::LaunchVisibilityTasks(const UE::Tasks::FTask ...) } } // InitViewTaskDatas.VisibilityTaskData-FinishGatherDynamicMeshElements(BasePassDepthStencilAccess, InstanceCullingManager, VirtualTextureUpdater.Get()); void FVisibilityTaskData::FinishGatherDynamicMeshElements(FExclusiveDepthStencil::Type BasePassDepthStencilAccess, FInstanceCullingManager InstanceCullingManager, FVirtualTextureUpdater* VirtualTextureUpdater) { // ★★★ 开始在一个新的线程执行 FVisibilityTaskData::SetupMeshPasses() Tasks.MeshPassSetup UE::Tasks::Launch(UE_SOURCE_LOCATION, [this, BasePassDepthStencilAccess, InstanceCullingManager] { FTaskTagScope Scope(ETaskTag::EParallelRenderingThread); // ★★★ SetupMeshPasses() 是生成MeshDrawCommand 的入口 SetupMeshPasses(BasePassDepthStencilAccess, InstanceCullingManager); }, TaskConfig.TaskPriority); } //★★★ 遮挡剔除 RenderOcclusion(GraphBuilder, SceneTextures, ....) ...... // ★★★ BasePass, 用来生成GBuffer RenderBasePass() { RenderBasePassInternal(Renderer, GraphBuilder, InViews, SceneTextures, BasePassRenderTargets, ...) { if (bParallelBasePass) { for (int32 ViewIndex 0; ViewIndex InViews.Num(); ViewIndex) { FViewInfo View InViews[ViewIndex]; if (auto* Pass View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass]; Pass bShouldRenderView) { // ★★★ 这个并不是创建command, 创建在上边SetupMeshPasses内部进行 Pass-BuildRenderingCommands(GraphBuilder, Scene-GPUScene, PassParameters-InstanceCullingDrawParams); GraphBuilder.AddPass( RDG_EVENT_NAME(BasePass), PassParameters, ERDGPassFlags::Raster, [View, Pass, PassParameters](FRDGAsyncTask, FRHICommandList RHICmdList) { SetStereoViewport(RHICmdList, View, 1.0f); //★★★★ FMeshDrawCommand 变成 FRHICommand 的过程 一个FMeshDrwaCommand会变成若干个FRHICommand. Pass-Draw(RHICmdList, PassParameters-InstanceCullingDrawParams); }); } } } } } ..... //★★★ 光照計算 RenderLights(GraphBuilder, ....) { for (int32 LightIndex UnbatchedLightStart; LightIndex MegaLightsLightStart; LightIndex) { if (bDirectLighting !bIsHairPass) { for (int32 ViewIndex 0, ViewCount Views.Num(); ViewIndex ViewCount; ViewIndex) { // ★★★ 内部会根据灯光类型选择对应的方式渲染光照 RenderLight( GraphBuilder, Scene, View, SceneTextures, LightSceneInfo,...) } } } } } }FVisibilityTaskData::SetupMeshPasses(这个接口是生成FMeshDrawCommand 的入口// Foreground Worker 线程 void FVisibilityTaskData::SetupMeshPasses(FExclusiveDepthStencil::Type BasePassDepthStencilAccess, FInstanceCullingManager InstanceCullingManager) { for (int32 ViewIndex 0; ViewIndex Views.Num(); ViewIndex) { FViewInfo View *Views[ViewIndex]; ..... //SceneRenderer.SetupMeshPass(View, BasePassDepthStencilAccess, ViewCommands, InstanceCullingManager); void FSceneRenderer::SetupMeshPass(FViewInfo View, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, FViewCommands ViewCommands, FInstanceCullingManager InstanceCullingManager) { // ★★★ 根据不同的Pass 生成对应的 FMeshPassProcessorFParallelMeshDrawCommandPass ★★★ for (int32 PassIndex 0; PassIndex EMeshPass::Num; PassIndex) { const EMeshPass::Type PassType (EMeshPass::Type)PassIndex; FMeshPassProcessor* MeshPassProcessor FPassProcessorManager::CreateMeshPassProcessor(ShadingPath, PassType, Scene-GetFeatureLevel(), Scene, View, nullptr); FParallelMeshDrawCommandPass Pass *View.CreateMeshPass(PassType); //调用FParallelMeshDrawCommandPass::DispatchPassSetup(), 生成各种FMeshDrawCommand Pass.DispatchPassSetup( ......); } } } }FParallelMeshDrawCommandPass:: DispatchPassSetup( .... )// 还是这个 Foreground Worker 线程 // 会生成一个FMeshDrawCommandPassSetupTast, 可以选择 当前线程执行或 异步执行 void FParallelMeshDrawCommandPass::DispatchPassSetup( FScene* Scene, const FViewInfo View, FInstanceCullingContext InstanceCullingContext, EMeshPass::Type PassType, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, FMeshPassProcessor* MeshPassProcessor, const TArrayFMeshBatchAndRelevance, SceneRenderingAllocator DynamicMeshElements, const TArrayFMeshPassMask, SceneRenderingAllocator* DynamicMeshElementsPassRelevance, int32 NumDynamicMeshElements, TArrayconst FStaticMeshBatch*, SceneRenderingAllocator InOutDynamicMeshCommandBuildRequests, TArrayEMeshDrawCommandCullingPayloadFlags, SceneRenderingAllocator InOutDynamicMeshCommandBuildFlags, int32 NumDynamicMeshCommandBuildRequestElements, FMeshCommandOneFrameArray InOutMeshDrawCommands, FMeshPassProcessor* MobileBasePassCSMMeshPassProcessor, FMeshCommandOneFrameArray* InOutMobileBasePassCSMMeshDrawCommands ) { MaxNumDraws InOutMeshDrawCommands.Num() // 已有的缓存Command从FPrimitiveSceneInfo缓存中拿的 NumDynamicMeshElements // 完全动态的网格每帧生成的MeshBatch NumDynamicMeshCommandBuildRequestElements; // 需要重新构建Command的静态网格材质参数变了等 // 处理器 TaskContext.MeshPassProcessor MeshPassProcessor; // ★ 用于生成Command // 数据源 TaskContext.DynamicMeshElements DynamicMeshElements; TaskContext.DynamicMeshElementsPassRelevance DynamicMeshElementsPassRelevance; // 视图和场景信息 TaskContext.View View; TaskContext.Scene Scene; TaskContext.ShadingPath GetFeatureLevelShadingPath(View.GetFeatureLevel()); TaskContext.PassType PassType; // EMeshPass::BasePass 等 // 渲染状态 TaskContext.bUseGPUScene UseGPUScene(...); TaskContext.bDynamicInstancing IsDynamicInstancingEnabled(...); TaskContext.bReverseCulling View.bReverseCulling; TaskContext.BasePassDepthStencilAccess BasePassDepthStencilAccess; // 半透明排序设置 TaskContext.TranslucentSortPolicy View.TranslucentSortPolicy; TaskContext.ViewOrigin View.ViewMatrices.GetViewOrigin(); // ★★★ 把输入数据移动到TaskContext中 ★★★ //这是零拷贝的数据转移调用者的数组被清空数据移到了 TaskContext 中 Swap(TaskContext.MeshDrawCommands, InOutMeshDrawCommands); Swap(TaskContext.DynamicMeshCommandBuildRequests, InOutDynamicMeshCommandBuildRequests); Swap(TaskContext.DynamicMeshCommandBuildFlags, InOutDynamicMeshCommandBuildFlags); if (MaxNumDraws 0) { // 1. 预分配内存 TaskContext.PrimitiveIdBufferData FMemory::Malloc(...); TaskContext.MeshDrawCommands.Reserve(MaxNumDraws); TaskContext.TempVisibleMeshDrawCommands.Reserve(MaxNumDraws); // 2. 决定是否并行执行 const bool bExecuteInParallel FApp::ShouldUseThreadingForPerformance() CVarMeshDrawCommandsParallelPassSetup.GetValueOnRenderThread() 0 GIsThreadedRendering; if (bExecuteInParallel) { // 异步任务 if (IsOnDemandShaderCreationEnabled()) { // ★★★ 异步任务FMeshDrawCommandPassSetupTask ★★★ TaskEventRef TGraphTaskFMeshDrawCommandPassSetupTask::CreateTask() .ConstructAndDispatchWhenReady(TaskContext); } else { FGraphEventArray DependentGraphEvents; DependentGraphEvents. Add(TGraphTaskFMeshDrawCommandPassSetupTask::CreateTask().ConstructAndDispatchWhenReady(TaskContext)); // 在之后进行FMeshDrawCommandInitResourcesTask TaskEventRef TGraphTaskFMeshDrawCommandInitResourcesTask::CreateTask(DependentGraphEvents).ConstructAndDispatchWhenReady(TaskContext); } } else { // 同步执行 FMeshDrawCommandPassSetupTask Task(TaskContext); Task.AnyThreadTask(); // ★★★ 这里真正生成MeshDrawCommand ★★★ } } }FMeshDrawCommandPassSetupTask::DoTask()// *** FMeshDrawCommand 真正生成的地方 *** FMeshDrawCommandPassSetupTask::DoTask() { GenerateDynamicMeshDrawCommands() { // FMeshPassProcessor::AddMeshBatch() // 虚函数各Pass有不同实现 // 如FBasePassMeshProcessor::AddMeshBatch() PassMeshProcessor-AddMeshBatch(MeshBatch, ...) { // 内部通过 TryAddMeshBatch() 调用 ProcessxxxPolicy() ProcessxxxPolicy() { // a、获取着色器 TMeshProcessorShaders TBasePassVertexShaderPolicyParamTypeLightMapPolicyType, TBasePassPixelShaderPolicyParamTypeLightMapPolicyType BasePassShaders; if (!GetBasePassShadersLightMapPolicyType( MaterialResource, VertexFactory-GetType(), LightMapPolicy, FeatureLevel, bRenderSkylight, Get128BitRequirement(), bIsDebug, GBL_Default, BasePassShaders.VertexShader, // 输出: VS BasePassShaders.PixelShader, // 输出: PS bOITBasePass)) { return false; // 获取着色器失败跳过此 MeshBatch } //b、设置深度/模板状态 SetDepthStencilStateForBasePass() //c、设置 Stencil 值用于 Decal/光照通道 //d、半透明混合状态 SetTranslucentRenderState(DrawRenderState, MaterialResource, ...); //e、准备着色器参数数据 TBasePassShaderElementDataLightMapPolicyType ShaderElementData(LightMapElementData); ShaderElementData.InitializeMeshMaterialData( ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, true); //f、计算排序 Key FMeshDrawCommandSortKey SortKey FMeshDrawCommandSortKey::Default; if (bTranslucentBasePass) { // 半透明按距离排序从后往前 SortKey CalculateTranslucentMeshStaticSortKey(PrimitiveSceneProxy, MeshBatch.MeshIdInPrimitive); } else { // 不透明按状态排序减少状态切换 SortKey CalculateBasePassMeshStaticSortKey(EarlyZPassMode, bIsMasked, BasePassShaders.VertexShader.GetShader(), BasePassShaders.PixelShader.GetShader()); } //g、 ★★★ 生成 MeshDrawCommand ★★★ FMeshPassProcessor::BuildMeshDrawCommands( MeshBatch, // 网格数据 BatchElementMask, // 要处理的 Element 掩码 PrimitiveSceneProxy, // 图元代理 MaterialRenderProxy, // 材质代理 MaterialResource, // 材质资源 DrawRenderState, // 渲染状态深度/模板/混合 BasePassShaders, // ★ VS/PS 着色器 MeshFillMode, // 填充模式实心/线框 MeshCullMode, // 剔除模式正面/背面/无 SortKey, // 排序Key EMeshPassFeatures::Default, ShaderElementData); // 着色器参数数据 { for (每个 BatchElement) { // 1. 创建 FMeshDrawCommand FMeshDrawCommand SharedMeshDrawCommand; // 2. 设置着色器和管线状态 FGraphicsMinimalPipelineStateInitializer PipelineState; PipelineState.BoundShaderState.VertexShaderRHI VertexShader; PipelineState.BoundShaderState.PixelShaderRHI PixelShader; PipelineState.BlendState DrawRenderState.GetBlendState(); PipelineState.DepthStencilState DrawRenderState.GetDepthStencilState(); // ... // 3. 设置绘制参数 SharedMeshDrawCommand.SetDrawParametersAndFinalize( MeshBatch, BatchElementIndex, ...); // 4. 提交到 DrawListContext DrawListContext-FinalizeCommand(..., SharedMeshDrawCommand); } } } } } }FParallelMeshDrawCommandPass::BuildRenderingCommands(... )FParallelMeshDrawCommandPass::BuildRenderingCommands(....) { //TaskContext.InstanceCullingContext.BuildRenderingCommands(GraphBuilder, GPUScene, OutInstanceCullingDrawParams); FInstanceCullingContext::BuildRenderingCommands(FRDGBuilder GraphBuilder, const FGPUScene GPUScene, FInstanceCullingDrawParams* InstanceCullingDrawParams) { BuildRenderingCommandsInternal(GraphBuilder, GPUScene, EAsyncProcessingMode::DeferredOrAsync, InstanceCullingDrawParams); } } void FInstanceCullingContext::BuildRenderingCommandsInternal( ....) { // Todo }FParallelMeshDrawCommandPass:: Draw( .....)这里是将FMeshDrawCommand 变成 FRHICommand 的过程 一个FMeshDrwaCommand会变成若干个FRHICommand.//生成RHICommand 同步方式 FParallelMeshDrawCommandPass::Draw { // 等SetupTast 任务结束后才开始下边的操作 WaitForMeshPassSetupTask(); //TaskContext.InstanceCullingContext.SubmitDrawCommands( ....) FInstanceCullingContext::SubmitDrawCommands( ....) { // 遍历 MeshDrawCommand for (int32 DrawCommandIndex StartIndex; DrawCommandIndex StartIndex NumMeshDrawCommands; DrawCommandIndex) { const FVisibleMeshDrawCommand VisibleMeshDrawCommand VisibleMeshDrawCommands[DrawCommandIndex]; // ※※※ SubmitDrawBegin 、SubmitDrawEnd两个函数内部会调用FRHICommandList的各种接口内部再通过ALLOC_COMMAND()生成各种RHICommand,并添加到RHICmdList中。 if(FMeshDrawCommand::SubmitDrawBegin(*VisibleMeshDrawCommand.MeshDrawCommand, GraphicsMinimalPipelineStateSet, SceneArgs, InstanceFactor, RHICmdList, StateCache, bAllowSkipDrawCommand)) { // ※※※ 这个函数最终会调用FRHICommandList::DrawIndexedPrimitive() 这个也会生成一个RHICommand,这个就是DrawCall。 FMeshDrawCommand::SubmitDrawEnd(*VisibleMeshDrawCommand.MeshDrawCommand, SceneArgs, InstanceFactor, RHICmdList); } } } } //生成RHICommand 异步并行方式 FParallelMeshDrawCommandPass::Dispatch { // 前置的SetupTast , 保证SetupTast 任务结束后才开始下边的操作 FGraphEventArray Prereqs; if (TaskEventRef.IsValid()) { Prereqs.Add(TaskEventRef); } for (int32 TaskIndex 0; TaskIndex NumTasks; TaskIndex) { const int32 StartIndex TaskIndex * NumDrawsPerTask; const int32 NumDraws FMath::Min(NumDrawsPerTask, MaxNumDraws - StartIndex); checkSlow(NumDraws 0); // *********** // 会把DrawMeshCommands分为几批 // 每批DrawMeshCommands 对应一个 子FRHICommandList 对应一个SubmitDrawCommands() // SubmitDrawCommands()接口生成的RHICommands 是有序的 // 这样就保证了每批的 DrawMeshCommand 生成的RHICommands 的顺序不会乱 // 最终这些 子FRHICommandList 会合并到最终的 FRHICommandList 中 // ******** FRHICommandList* RHICmdList DispatchPassBuilder.CreateCommandList(); SetupCommandListFunction(*RHICmdList); // 生成RHICommand 的逻辑在 FDrawVisibleMeshCommandsAnyThreadTask::DoTask() 中 // 其实还是FInstanceCullingContext::SubmitDrawCommands( ....), 只不过是异步并行了 // ***** TGraphTaskFDrawVisibleMeshCommandsAnyThreadTask::CreateTask(Prereqs) .ConstructAndDispatchWhenReady(*RHICmdList, TaskContext.InstanceCullingContext, TaskContext.MeshDrawCommands, TaskContext.MinimalPipelineStatePassSet, OverrideArgs, TaskContext.InstanceFactor, TaskIndex, NumTasks); } }ALLOC_COMMAND//这个宏通常会在class FRHIComputeCommandList中调用 并且不是异步的#define ALLOC_COMMAND(...) new ( AllocCommand(sizeof(__VA_ARGS__), alignof(__VA_ARGS__)) ) __VA_ARGS__例如 ALLOC_COMMAND( FRHICommandSetGlobalUniformBuffers) (UniformBuffers);展开后 new ( AllocCommand ( sizeof(FRHICommandSetGlobalUniformBuffers), alignof(FRHICommandSetGlobalUniformBuffers)) ) FRHICommandSetGlobalUniformBuffers ( UniformBuffersAllocCommand(int32 AllocSize, int32 Alignment) 接口的作用是生成一个command的内存空间, 将这个command 添加到commandlist中并返回这个command的指针最后就变成了一个place new : new ( FRHICommandBase* ) FRHICommandSetGlobalUniformBuffers ( UniformBuffers在command这块内存上构造具体的command;三、RHI线程就是从FRHICommandList 中取出命令执行了具体就是调用OpenGL、DirectX 等接口了