[工作积累] UE4 并行渲染的同步 - Sync between FParallelCommandListSet & FRHICommandListImmediate calls
2017-05-27 23:44
1211 查看
UE4 的渲染分为两个模式1.编辑器是同步绘制的 2.游戏里是FParallelCommandListSet并行派发的。
mesh渲染也分两类,static mesh 使用TStaticMeshDrawList 来绘制, skinned mesh是用DrawingPolicyFactory::DrawDynamicMesh来画。这两类绘制不管是异步还是同步都会调用。具体可以参考DepthRendering.cpp
实际上,有在DX12/Vulkan/Metal 这些支持paralle commit的API上才会真正并行派发,否则GRHIThread为nullptr,还是在最后某个时刻把所有Task阻塞式提交的,Task的执行顺序不确定,但不是并发的。
一般来说每个DrawList的DrawVisibleParallel会自己创建一个Task, DrawDynamicMesh都是自己创建的Task。这些task按不确定的顺序执行,因为有pre depth或者depth buffer,所以乱序绘制没有问题,只有半透明物体需需要按顺序绘制,所以只有一个DrawList,对应一个Task。Task内部的绘制都是按先后顺序的。
工作中遇到的问题:
目前自定义的部分流程是
1.draw objects A (parallel)
2.copy scene color (immediate)
3.draw objects B (parallel)
其中第二部必须在第一步结束之后才能开始。因为使用了FParallelCommandListSet, 并仿照DeferredShadingRenderer.cpp 里, 在向CommandList里添加调用以后,使用了ServiceLocalQueue() 来同步。
代码:
发现并不能同步结果, 第二步复制的SceneColor里并没有第一步绘制的物体。
仔细查看代码,发现FParallelCommandListSet的dispatch都是在析构函数里执行的,比如DepthRendering.cpp 绘制depth pre pass: 而我的FCustomParallelCommandListSet也是(部分)抄他的(他注释里说了不要无脑复制粘贴),类似。
DepthRendering.cpp:
也就是说,需要FCustomParallelCommandListSet()析构以后,同步才有用,否则的话,还没有任务dispatch,sync什么。于是代码修改如下:
是的,就是加了个花括号,问题就解决了一半。
另外,第二部Copy SceneColor的时候,可能在其他一个线程的Command还没完全派发到GPU,如果不同步的话,复制出来的SceneColor copy,在采样时会闪烁。
然而使用了ServiceLocalQueue()以后,结果仍然不正确。这样以来ServiceLocalQueue()的意义感觉不明 - 并不是在等待task执行结束。但可以确定的是DrawVisibleParallel/DrawDynamicMesh使用的异步模式,而传入的RHICmdList是FRHICommandListImmediate,也就是立即执行的,两种方式肯定需要同步。
既然ServiceLocalQueue()和预想的等待或者同步不同,所以尝试在ServiceLocalQueue()后面加上
RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread);
结果才正确。
还有一个方式就是把一系列DrawVisibleParallel/DrawDynamic和CopyResovleTarget放在一个Task里,因为Task的内部执行是按顺序的,不需要同步,但是只有一个Task,在支持并行发射Command的GPU下就没有并发了。而且每个DrawVisibleParallel会创建一个Task,需要把这些所有操作合并到一个task里,具体没有试过。
如果把CopyResolveTarget放到另外一个Task,使用异步模式,结果也是不对的。虽然这些Task在非DX12/Vulkan/Metal下是非并发的,按顺序的,但是执行顺序是不确定的。
至于并发+同步开销大还是单一task效率更高,依赖于draw call的数量,具体需要profiling。
mesh渲染也分两类,static mesh 使用TStaticMeshDrawList 来绘制, skinned mesh是用DrawingPolicyFactory::DrawDynamicMesh来画。这两类绘制不管是异步还是同步都会调用。具体可以参考DepthRendering.cpp
实际上,有在DX12/Vulkan/Metal 这些支持paralle commit的API上才会真正并行派发,否则GRHIThread为nullptr,还是在最后某个时刻把所有Task阻塞式提交的,Task的执行顺序不确定,但不是并发的。
一般来说每个DrawList的DrawVisibleParallel会自己创建一个Task, DrawDynamicMesh都是自己创建的Task。这些task按不确定的顺序执行,因为有pre depth或者depth buffer,所以乱序绘制没有问题,只有半透明物体需需要按顺序绘制,所以只有一个DrawList,对应一个Task。Task内部的绘制都是按先后顺序的。
工作中遇到的问题:
目前自定义的部分流程是
1.draw objects A (parallel)
2.copy scene color (immediate)
3.draw objects B (parallel)
其中第二部必须在第一步结束之后才能开始。因为使用了FParallelCommandListSet, 并仿照DeferredShadingRenderer.cpp 里, 在向CommandList里添加调用以后,使用了ServiceLocalQueue() 来同步。
代码:
class FCustomPassDynamicDataThreadTask : public FRenderTask {...}; class FCustomParallelCommandListSet : public FParallelCommandListSet {...}; ... //Step 1 FCustomParallelCommandListSet ParallelSet(View, RHICmdList, true, CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() == 0); //Render Static Mesh Scene->CustomDrawList.DrawVisibleParallel(ParallelSet.View.StaticMeshVisibilityMap, ParallelSet.View.StaticMeshBatchVisibility, ParallelSet); // Render dynamic mesh FRHICommandList* CmdList = ParallelSet.NewParallelCommandList(); FGraphEventRef AnyThreadCompletionEvent = TGraphTask<FCustomPassDynamicDataThreadTask>::CreateTask(ParallelSet.GetPrereqs(), ENamedThreads::RenderThread) .ConstructAndDispatchWhenReady(*this, *CmdList, View, ParallelSet.DrawRenderState); ParallelSet.AddParallelCommandList(CmdList, AnyThreadCompletionEvent); ServiceLocalQueue(); //Step 2 RHICmdList.CopyToResolveTarget(...); // or CopySubTextureRegion ...
发现并不能同步结果, 第二步复制的SceneColor里并没有第一步绘制的物体。
仔细查看代码,发现FParallelCommandListSet的dispatch都是在析构函数里执行的,比如DepthRendering.cpp 绘制depth pre pass: 而我的FCustomParallelCommandListSet也是(部分)抄他的(他注释里说了不要无脑复制粘贴),类似。
DepthRendering.cpp:
class FPrePassParallelCommandListSet : public FParallelCommandListSet { public: FPrePassParallelCommandListSet(const FViewInfo& InView, FRHICommandListImmediate& InParentCmdList, bool bInParallelExecute, bool bInCreateSceneContext) : FParallelCommandListSet(GET_STATID(STAT_CLP_Prepass), InView, InParentCmdList, bInParallelExecute, bInCreateSceneContext) { // Do not copy-paste. this is a very unusual FParallelCommandListSet because it is a prepass and we want to do some work after starting some tasks } virtual ~FPrePassParallelCommandListSet() { // Do not copy-paste. this is a very unusual FParallelCommandListSet because it is a prepass and we want to do some work after starting some tasks SetStateOnCommandList(ParentCmdList); Dispatch(true); } virtual void SetStateOnCommandList(FRHICommandList& CmdList) override { FParallelCommandListSet::SetStateOnCommandList(CmdList); FSceneRenderTargets::Get(CmdList).BeginRenderingPrePass(CmdList, false); SetupPrePassView(CmdList, View, DrawRenderState); } };
也就是说,需要FCustomParallelCommandListSet()析构以后,同步才有用,否则的话,还没有任务dispatch,sync什么。于是代码修改如下:
class FCustomPassDynamicDataThreadTask : public FRenderTask {...}; class FCustomParallelCommandListSet : public FParallelCommandListSet {...}; ... //Step 1 //Note: the local scope is necessary because FCustomParallelCommandListSet dispatches in dector. { FCustomParallelCommandListSet ParallelSet(View, RHICmdList, true, CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() == 0); //Render Static Mesh Scene->CustomDrawList.DrawVisibleParallel(ParallelSet.View.StaticMeshVisibilityMap, ParallelSet.View.StaticMeshBatchVisibility, ParallelSet); // Render dynamic mesh FRHICommandList* CmdList = ParallelSet.NewParallelCommandList(); FGraphEventRef AnyThreadCompletionEvent = TGraphTask<FCustomPassDynamicDataThreadTask>::CreateTask(ParallelSet.GetPrereqs(), ENamedThreads::RenderThread) .ConstructAndDispatchWhenReady(*this, *CmdList, View, ParallelSet.DrawRenderState); ParallelSet.AddParallelCommandList(CmdList, AnyThreadCompletionEvent); } ServiceLocalQueue(); RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread); //Step 2 RHICmdList.CopyToResolveTarget(...); //or CopySubTextureRegion ...
是的,就是加了个花括号,问题就解决了一半。
另外,第二部Copy SceneColor的时候,可能在其他一个线程的Command还没完全派发到GPU,如果不同步的话,复制出来的SceneColor copy,在采样时会闪烁。
然而使用了ServiceLocalQueue()以后,结果仍然不正确。这样以来ServiceLocalQueue()的意义感觉不明 - 并不是在等待task执行结束。但可以确定的是DrawVisibleParallel/DrawDynamicMesh使用的异步模式,而传入的RHICmdList是FRHICommandListImmediate,也就是立即执行的,两种方式肯定需要同步。
既然ServiceLocalQueue()和预想的等待或者同步不同,所以尝试在ServiceLocalQueue()后面加上
RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread);
结果才正确。
还有一个方式就是把一系列DrawVisibleParallel/DrawDynamic和CopyResovleTarget放在一个Task里,因为Task的内部执行是按顺序的,不需要同步,但是只有一个Task,在支持并行发射Command的GPU下就没有并发了。而且每个DrawVisibleParallel会创建一个Task,需要把这些所有操作合并到一个task里,具体没有试过。
如果把CopyResolveTarget放到另外一个Task,使用异步模式,结果也是不对的。虽然这些Task在非DX12/Vulkan/Metal下是非并发的,按顺序的,但是执行顺序是不确定的。
至于并发+同步开销大还是单一task效率更高,依赖于draw call的数量,具体需要profiling。
相关文章推荐
- 工作问题积累(十一)如何解决"应用程序无法启动,因为应用程序的并行配置不正确"问题
- mysql 2014 error (2014) Commands out of sync; You can't run this command now命令不同步错误解决
- Commands out of sync; you can't run this command now
- What's the effect of hashCode() & equals() when adding object to List/Set/Map
- Java基础学习笔记之七(2)--List&Set
- hibernate 中没事不要用映射文件中用<list>定义合集,而要是用Set
- Dissecting The Nutch Crawler -Command "generate": net.nutch.tools.FetchListTool
- log file sync(日志文件同步) 与 Log file parallel write 等待事件(2)
- PHP执行MYSQL存储过程报错:Commands out of sync; you can't run this command now 问题的解决
- Visual Studio 调试技巧[Command Window & Immediate Window ](Tips)
- PHP执行MYSQL存储过程报错:Commands out of sync; you can't run this command now
- log file sync(日志文件同步) 与 Log file parallel write 等待事件
- [Sybase]Character set conversion is not available between client character set 'iso_1' and server character set 'cp936'.
- log file sync(日志文件同步) 与 Log file parallel write 等待事件
- ArrayList、List<T>、HashSet<T>、LinkedList<T>各自优点和缺点,Dictionary<K,V>的内部存储数据方式有什么特殊的?
- 解决error:2014 Commands out of sync; you can't run this command now
- [Linux技术][2010-8-12]setsockopt & getsockopt 函数中操作积累
- log file sync(日志文件同步) 与 Log file parallel write 等待事件
- 黑马程序员--11集合类的学习List&Hash&Array)Set&泛型
- log file sync(日志文件同步) 与 Log file parallel write 等待事件(1)