[工作积累] UE4 并行渲染的同步 - Sync between FParallelCommandListSet & FRHICommandListImmediate calls
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() 来同步。
代码:
class FCustomPassDynamicDataThreadTask : public FRenderTask {...};
class FCustomParallelCommandListSet : public FParallelCommandListSet {...};
...
//Step 1
FCustomParallelCommandListSet ParallelSet(View, RHICmdList, true, CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() == );
//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() == );
//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。
[工作积累] UE4 并行渲染的同步 - Sync between FParallelCommandListSet & FRHICommandListImmediate calls的更多相关文章
- [工作积累] UE4 TAA ReProjection的精度处理
先贴一个UE4 TAA的slidehttps://de45xmedrsdbp.cloudfront.net/Resources/files/TemporalAA_small-59732822.pdf ...
- Centos7.5部署MySQL5.7基于GTID主从复制+并行复制+半同步复制+读写分离(ProxySQL) 环境- 运维笔记 (完整版)
之前已经详细介绍了Mysql基于GTID主从复制的概念,原理和配置,下面整体记录下MySQL5.7基于GTID主从复制+并行复制+增强半同步复制+读写分离环境的实现过程,以便加深对mysql新特性GT ...
- MySQL 5.7 基于GTID主从复制+并行复制+半同步复制
环境准备 IP HOSTNAME SERVICE SYSTEM 192.168.131.129 mysql-master1 mysql CentOS7.6 192.168.131.130 mysql- ...
- C#并行编程-线程同步原语
菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...
- Android源码浅析(三)——Android AOSP 5.1.1源码的同步sync和编译make,搭建Samba服务器进行更便捷的烧录刷机
Android源码浅析(三)--Android AOSP 5.1.1源码的同步sync和编译make,搭建Samba服务器进行更便捷的烧录刷机 最近比较忙,而且又要维护自己的博客,视频和公众号,也就没 ...
- 转载 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等
随笔 - 353, 文章 - 1, 评论 - 5, 引用 - 0 三.并行编程 - Task同步机制.TreadLocal类.Lock.Interlocked.Synchronization.Conc ...
- 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等
在并行计算中,不可避免的会碰到多个任务共享变量,实例,集合.虽然task自带了两个方法:task.ContinueWith()和Task.Factory.ContinueWhenAll()来实现任务串 ...
- 同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式
1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端: 同步: ...
- [工作积累] Tricks with UE4 PerInstanceRandom
最近在用UE4的Instancing, 发现限制很多. Unity有instancing的attribute array (uniform/constant buffer),通过InstanceID来 ...
随机推荐
- spring的bean创建过程
Spring的bean创建过程 步骤 执行过程 描述 1 ThreadLocal.set bean创建之前将beanName的一些属性放进ThreadLocal,避免多线程创建bean导致问题,并发创 ...
- OTP&ETS
最近觉得实在是该梳理梳理erlang的框架内容了,所以整理了下. OTP(开放电信平台):并发系统平台, 特点:容错(erlang:get_stacktrace(),try-catch,trap_ex ...
- windows分驱
步骤/方法 1 鼠标右击“计算机” 2 选择“管理”标签 3 打开“计算机管理”窗口 选择“磁盘“>>”存储管理“,打开”磁盘管理“页面 如图: 右键单击选择要压缩的磁盘(本例 ...
- java基础知识—类和对象
1.对象的特征---类的属性 每个对象的每个属性都有特定的值 对象的操作---类的方法 2.封装 对象同时具有属性和方法两项属性. 对象的属性和方法同时被封装在一起,共同体现事物的特性,二者相辅相成, ...
- 1019. General Palindromic Number (20)
生词以及在文中意思 forward 向前地 backward 向后地 palindromic 回文的 base 基数(如十进制的10 和二进制的2) numeral system 数制 decimal ...
- Linux 环境下umount, 报 device is busy 的问题分析与解决方法
在Linux环境中,有时候需要挂载外部目录或硬盘等,但当想umount时,却提示类似“umount:/home/oracle-server/backup:device is busy”这种提示. 出现 ...
- 简单透析cookies,sessionStorage和localStorage
首先大致说一下 1.sessionStorage是会话层的一种存储方式,当会话关闭或者退出,sessionStorage就会被清除,有效期较短 2.localStorage是浏览器提供的本地存储方式, ...
- 第一次靶场练习:SQL注入(1)
SQL注入1 本文章目的是对相关的黑客内容进一步了解,如有人违反相关的法律法规,本人概不负责 一.学习目的: 利用手工注入网站 利用sqlmab注入 二.附件说明 靶场网址:http://117.41 ...
- 介质共享型局域网中的介质访问控制(MAC)协议需要具体解决的3个问题,CSMA/CD介质访问控制的基本思想
1,在某一时刻,那个节点可以发送数据 2,发送时是否会出现冲突 3,出现冲突时如何处理 CSMA/CD介质访问控制的基本思想:先监听,再发送.边发送,边监听,如发生冲突,则等待一段时间后再次发送
- docker 容器跑一个应用
虽然是个前端,但是公司比较推崇docker,感觉挺神奇,就也学了一些,再此做个笔记,以供日后回想. 我的想法是在一个centos环境中运行一个nginx服务.docker的安装就不说了,网上已经有好多 ...