【UE4 C++】渲染流水线笔记:从MeshBatch到DrawCommand

【UE4 C++】渲染流水线笔记:从MeshBatch到DrawCommand
https://zhuanlan.zhihu.com/p/564066505

在看完PMC的源码后,对UE4的渲染流水线有了一点浅薄的认知。能够明白其中的FPrimitiveSceneProxy通过GetDynamicMeshElements提交FMeshBatch的过程。接下来就需要继续理清MeshBatch到DrawCommand的过程。

为了继续了解InitViews和MeshPassProcessor,需要先看一下引擎的调度逻辑。

Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp

Windows平台的的入口函数是WinMain,各阶段操作都是调用FEngineLoop的对应方法:

  • WinMain
    • GuardedMain
      • EnginePreInit
      • EngineInit
        • FEngineLoop::Init
          • UGameEngine::Init
          • UGameEngine::Start
      • EngineTick
        • FEngineLoop::Tick
          • UGameEngine::Tick
      • EngineExit

主要聚焦在Tick如何进入渲染流水线的调用:

  • UGameEngine::Tick
    • UGameEngine::RedrawViewports
    • FViewport::Draw
    • UGameViewportClient::Draw
      • FRendererModule::BeginRenderingViewFamily
        • FSceneRenderer::CreateSceneRenderer - FDeferredShadingSceneRenderer
        • RenderViewFamily_RenderThread
          • FDeferredShadingSceneRenderer::Render
          • FDeferredShadingSceneRenderer::InitViews
          • FSceneRenderer::ComputeViewVisibility
          • FSceneRenderer::SetupMeshPass
          • FParallelMeshDrawCommandPass::DispatchPassSetup
          • GenerateDynamicMeshDrawCommands
          • FMeshPassProcessor::AddMeshBatch

找到了FMeshDrawCommand生成的主要流程,接下来先看这部分是如何和FPrimitiveSceneProxy配合的。与FPrimitiveSceneProxy先关的最核心的方法是以下三个:

  1. CreateSceneProxy
  2. GetViewRelevance
  3. GetDynamicMeshElements

方法1在FScene::AddPrimitive被调用,使用MarkRenderStateDirty可以进行重建,会将UPrimitiveComponent在游戏线程中的数据拷贝到渲染线程,两者独立操作保证线程安全。

Engine\Source\Runtime\Renderer\Private\RendererScene.cpp
  • MarkRenderStateDirty
    • DoDeferredRenderUpdates_Concurrent
      • RecreateRenderState_Concurrent
        • CreateRenderState_Concurrent
          • AddPrimitive

方法2和3都发生在FSceneRenderer::ComputeViewVisibility视椎剔除和遮挡剔除之后。方法2用于确定各个Primitive的配置,确定是否渲染及渲染类型归类,调用链:

  • ComputeAndMarkRelevanceForViewParallel
    • AnyThreadTask
      • ComputeRelevance
        • GetViewRelevance

方法3开始收集MeshBatch,对于已经归类记录的DynamicMeshElement,通过Collector收集需要提交的MeshBatch,并在其后计算每个MeshBatch会被哪些MeshPass引用,加到对应View相应MeshPass的计数中,调用链:

  • GatherDynamicMeshElements
    • GetDynamicMeshElements
    • ComputeDynamicMeshRelevance
      • FMeshPassMask
      • NumVisibleDynamicMeshElements

MeshBatch收集完成后,接下来就是转换到DrawCommand的过程,这部分在SetupMeshPass中做了大量的工作。

  • SetupMeshPass
  1. 根据ShadingPath和MeshPass从FPassProcessorManager的JumpTable中获取创建对应PassProcessor的方法。全局注册的方法封装在FRegisterPassProcessorCreateFunction的构造方法中,会将信息填充到JumpTable中。
  2. 获取对应MeshPass的FParallelMeshDrawCommandPass,调用DispatchPassSetup,将得到的信息传入,开始生成DrawCommand。
void FParallelMeshDrawCommandPass::DispatchPassSetup(
	FScene* Scene,
	const FViewInfo& View,
	EMeshPass::Type PassType,
	FExclusiveDepthStencil::Type BasePassDepthStencilAccess,
	FMeshPassProcessor* MeshPassProcessor,
	const TArray<FMeshBatchAndRelevance, SceneRenderingAllocator>& DynamicMeshElements,
	const TArray<FMeshPassMask, SceneRenderingAllocator>* DynamicMeshElementsPassRelevance,
	int32 NumDynamicMeshElements,
	TArray<const FStaticMeshBatch*, SceneRenderingAllocator>& InOutDynamicMeshCommandBuildRequests,
	int32 NumDynamicMeshCommandBuildRequestElements,
	FMeshCommandOneFrameArray& InOutMeshDrawCommands,
	FMeshPassProcessor* MobileBasePassCSMMeshPassProcessor,
	FMeshCommandOneFrameArray* InOutMobileBasePassCSMMeshDrawCommands
)
  • DispatchPassSetup
  1. 这一步开始是逐Pass进行,首先收集生成DrawCommand需要的全部信息,存储在FMeshDrawCommandPassSetupTaskContext中,用于后续并行执行
  2. 创建TaskGraph,类型为FMeshDrawCommandPassSetupTask,传入收集的Context
  • TGraphTask::CreateTask
  • ConstructAndDispatchWhenReady
  • DoTask

3. 调用GenerateDynamicMeshDrawCommands,将MeshBatch依次加入MeshProcessor,以BasePassMeshProcessor为例:

void FBasePassMeshProcessor::AddMeshBatch(
        const FMeshBatch& RESTRICT MeshBatch, 
        uint64 BatchElementMask, 
        const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, 
        int32 StaticMeshId);
  • AddMeshBatch
  1. 获取当前MeshBatch的FMaterial后调用TryAddMeshBatch
  2. TryAddMeshBatch,MeshBatch过滤
  3. Process,Shader获取,渲染状态处理
  4. BuildMeshDrawCommands,Shader资源绑定
  5. FinalizeCommand,生成VisibleMeshDrawCommand,加入DrawList

到这里完成了MeshBatch到DrawCommand的过程,有了初步的认知后,源码里相关的概念已经大概熟悉,接下来就可以尝试针对性的修改源码,增加一个独立的渲染Pass了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值