《图解UE4渲染体系》Part 1 多线程渲染
上回书《Part 0 引擎基础》说到,我们粗略地知道UE4是以哪些类来管理一个游戏场景里的数据的,但这仅仅是我们开始探索UE4渲染体系的一小步。
本回主要介绍UE4渲染体系中比较宏观顶层的一部分——多线程渲染,具体的多线程中,又分为:
- 游戏线程(GameThread)
- 渲染线程(RenderThread)
- RHI线程(Render Hardware Interface Thread)

为什么是多线程?

用来描述“渲染”的最基础的理论就是像图上的那样,CPU调用图形API提供的DrawCall命令(也叫绘制指令),在命令中说明需要渲染的数据、属性等等,然后CPU等待GPU返回渲染结果,完成渲染。对于那些渲染频率不高的场景,这种方式并没有什么问题,但在游戏这种需要实时性渲染的高频率场景下,问题就显现出来了。
游戏引擎完成渲染不只有提交DrawCall这一个任务,除了这个以外,CPU要花费非常多的时间在处理游戏逻辑运算和准备渲染数据上,比如处理用户的输入、执行游戏脚本、更新物理和动画、可见性剔除等等等等。

假如引擎把所有的事都交由GameThread来完成,当GameThread把当前这一帧该做的事都做完了,准备好要渲染的数据,提交到GPU后,GameThread就只能等待渲染结果,但GameThread接受到当前这一帧的用户输入后,完全可以去执行下一帧的各种任务,但单线程的机制并不允许这样的事情。
多核心的CPU和多线程并发并行的操作系统在今天已经不是什么稀罕事了,将与渲染相关的任务从GameThread中剥离出来,让GameThread专注处理游戏逻辑上的的各种计算任务,让RenderThread专门和GPU来完成渲染任务,就成了自然而然的事情。

加入RenderThread后,每次GameThread处理完各种任务,准备好渲染数据,把数据发送给RenderThread,然后就继续处理下一帧的任务了,RenderThread收到数据,进行一些数据处理后(比如可见性剔除),向GPU提交DrawCall,等待渲染结果,完成渲染。
那RHIThread是什么呢?UE4中RHI的提出可能有很多原因:
- 支持跨平台多种图形API
- 并行提交DrawCall
- 其他各种各样的性能优化
首先是针对跨平台多种图形API,由于不同平台支持的图形API不同,Windows限定的Direct3D、MacOS限定的Metal以及跨平台(包括移动端)的OpenGL和Vulkan,在有RHIThread之前,RenderThread会根据不同的图形API来选择DrawCall,这肯定会增加不少工作量,维护也更加复杂。
"All problems in computer science can be solved by another level of indirection." —— Jay Black
如果把这件事交由单独的一个线程来做,岂不美哉?这不,RHIThread就来了。

RenderThread准备好渲染数据后,向RHIThread提交一个与图形API无关的RHIDrawCommand,RHIThread掏出来一个表,查找当前平台的图形API里哪一句是对应的DrawCall,然后再向GPU提交DrawCall,等待渲染结果,完成渲染。这样一来,RenderThread就可以在自己的任务上专注(方便优化),在RHIThread上完成对各个平台的图形API版本迭代维护。

当然这是从工程优化角度上RHIThread存在的理由,当然RHI还有一些更加直接的存在理由,那就是为了支持并行化提交DrawCall。在一些比较旧的图形API里,DrawCall都是阻塞的,即一个线程提交DrawCall时,不允许其他线程提交。图形API调用GPU计算后,GPU本身计算渲染是需要时间的,而在这时间里,图形API如果能准备好下一次DrawCall,那必然是更好的。

随着技术更新,一些新的图形API开始提供一些并行化提交DrawCall的方式,在没有RHI的时候,难道让UE4跑多个RenderThread吗?好像也不太合理,RenderThread里面除了提交DrawCall的其他部分也不需要多个线程来完成,那需要单独提出来多线程化的任务就顺理成章地变成了RHIThread了。
总结
可以看到UE4渲染体系中多线程渲染的设计并不是一开始就是这样,而是跟随着技术的需求在不断发展进步的(新的UE5里面估计又改了不少了)。
本回并没有着重讨论各种线程内部细节的任务,也没有非常深入的讲解各个线程之间是如何传递具体的命令和数据的,因为讲起来那篇幅真的就太长了,之后再慢慢地整理吧,网络上的资料也很多,大家可以自行拓展阅读。
参考文献
- [1] 可可西, UE4之Game、Render、RHI多线程架构, 博客园
- [2] 0向往0, 剖析虚幻渲染体系(02)- 多线程渲染, 博客园
- [3] 醉里挑灯看剑, Unreal Engine中的RHI线程, 知乎
- [4] leonwei, 基于UE4的多RHI线程实现, CSDN
《图解UE4渲染体系》Part 1 多线程渲染的更多相关文章
- BGFX多线程渲染
BGFX多线程渲染 1. 多线程基础 1. 并发概念 1. 并发任务简介 在多年以前,在手机.pc端.游戏机等,都是一个单核的CPU.这样,在硬件层面上,处理多个任务的时候,也是把一些任务切分成一些小 ...
- 《图解UE4渲染体系》Part 0 引擎基础
在介绍UE4渲染体系前,我们有必要来先看一下UE4是用什么样的方式来构建游戏场景数据的. 1 Object 在UE4中当我们说Object,通常是指代引擎代码中的UObject类,它是引擎里管理绝大部 ...
- 剖析虚幻渲染体系(06)- UE5特辑Part 1(特性和Nanite)
目录 6.1 本篇概述 6.1.1 本篇内容 6.1.2 基础概念 6.2 UE5新特性 6.2.1 UE5编辑器 6.2.1.1 下载编辑器及资源 6.2.1.2 启动示例工程 6.2.1.3 编辑 ...
- 剖析虚幻渲染体系(10)- RHI
目录 10.1 本篇概述 10.2 RHI基础 10.2.1 FRenderResource 10.2.2 FRHIResource 10.2.3 FRHICommand 10.2.4 FRHICom ...
- 剖析虚幻渲染体系(11)- RDG
目录 11.1 本篇概述 11.2 RDG基础 11.2.1 RDG基础类型 11.2.2 RDG资源 11.2.3 RDG Pass 11.2.4 FRDGBuilder 11.3 RDG机制 11 ...
- 剖析虚幻渲染体系(12)- 移动端专题Part 1(UE移动端渲染分析)
目录 12.1 本篇概述 12.1.1 移动设备的特点 12.2 UE移动端渲染特性 12.2.1 Feature Level 12.2.2 Deferred Shading 12.2.3 Groun ...
- 剖析虚幻渲染体系(12)- 移动端专题Part 3(渲染优化)
目录 12.6 移动端渲染优化 12.6.1 渲染管线优化 12.6.1.1 使用新特性 12.6.1.2 管线优化 12.6.1.3 带宽优化 12.6.2 资源优化 12.6.2.1 纹理优化 1 ...
- 剖析虚幻渲染体系(13)- RHI补充篇:现代图形API之奥义与指南
目录 13.1 本篇概述 13.1.1 本篇内容 13.1.2 概念总览 13.1.3 现代图形API特点 13.2 设备上下文 13.2.1 启动流程 13.2.2 Device 13.2.3 Sw ...
- 剖析虚幻渲染体系(14)- 延展篇:现代渲染引擎演变史Part 1(萌芽期)
目录 14.1 本篇概述 14.1.1 游戏引擎简介 14.1.2 游戏引擎模块 14.1.3 游戏引擎列表 14.1.3.1 Unreal Engine 14.1.3.2 Unity 14.1.3. ...
随机推荐
- 半吊子菜鸟学Web开发5 -- PHP开发环境配置
本文参考自:http://blog.csdn.net/angon823/article/details/54415855 Ubuntu16.04 默认 apt-get install apache2 ...
- 使用Redis实现关注好友的功能
现在很多社交都有关注或者添加粉丝的功能, 类似于这样的功能我们如果采用数据库做的话只是单纯得到用户的一些粉丝或者关注列表的话是很简单也很容易实现, 但是如果我想要查出两个甚至多个用户共同关注了哪些人或 ...
- JDK,JRE,JVM的作用及关系
1.作用 JVM:Java虚拟机,保证Java语言跨平台 JRE:Java程序的运行环境 JDK:Java程序的开发环境 2.关系 JRE:JVM+类库 JDK:JRE+工具
- ServletConfig对象和ServletContext对象有什么区别?
一个Servlet对应有一个ServletConfig对象,可以用来读取初始化参数. 一个webapp对应一个ServletContext对象. ServletContext对象获取初始化定义的参数. ...
- 重载(Overload)和重写(Override)的区别。重载的 方法能否根据返回类型进行区分?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性.重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同.参数个数不同或者二者都不同)则视 ...
- 列举 Spring Framework 的优点?
由于 Spring Frameworks 的分层架构,用户可以自由选择自己需要的组件. Spring Framework 支持 POJO(Plain Old Java Object) 编程,从而具备持 ...
- ElasticSearch-学习笔记01-docker安装
安装ElasticSearch docker 安装请参考: https://www.cnblogs.com/youxiu326/p/docker-01.html docker run -d --nam ...
- char向wchar的转换-MultiByteToWideChar
问题产生 使用CreateFile函数,如下: CreateFile(lpcTheFile, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NO ...
- 一个html标签到底包含了多少信息(1)
先来看一段代码: var dom = document.querySelector('body'); for(var i in dom){ console.log(i,dom[i]) } 可以看到很多 ...
- js如何获取iframe页面内的对象
简单介绍iframe标签,所有的浏览器都支持<iframe>标签,iframe 元素会创建包含另外一个文档的内联框架(即行内框架).通常我们常用的iframe标签的属性有:width(if ...