《图解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. ...
随机推荐
- js正则表达式处理表单
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...
- 使用 JWT 来保护你的 SpringBoot 应用
关键词 写在前面 Spring Boot 创建Spring Boot应用 创建一个Web 应用 使用JWT保护你的Spring Boot应用 添加Spring Security 本文代码 关键词 Sp ...
- js file对象 文件大小转换可视容易阅读的单位
function returnFileSize(number) { if(number < 1024) { return number + 'bytes'; } else if(number & ...
- homebrew 安装nginx+php+mysql
转:https://juejin.im/post/5c8fb28a6fb9a07103548318 brew search nginxbrew install nginx /usr/local/etc ...
- solr服务的搭建
首先你需要一台已经搭建好的虚拟机,下面的步骤才可以执行 安装java 安装完Centos6.5的Base Server版会默认安装OpenJDK,首先需要删除OpenJDK 1.查看以前是不是安装了o ...
- python面向对象--快速入门
1面向对象基本用法 1.1基本格式 class 类名: def __init__(self,x): self.x = x def 方法名字 (self): #函数在类里称为方法,self就是固定参数, ...
- 打败算法 —— 删除链表的倒数第n个结点
本文参考 出自LeetCode上的题库 -- 删除链表的倒数第n个结点,官方的双指针解法没有完全符合"只遍历一遍链表"的要求,本文给出另一种双指针解法 https://leetco ...
- Thoughtworks Technology Radar #26 技术雷达26期
Thoughtworks Technology Radar #26 Techniques Adopt Four key metrics Google Cloud's DevOps Research a ...
- 设备像素,CSS像素,设备独立像素
1.概念 设备像素(device pixel)简写DP 设备像素又称 **物理像素** ,是设备能控制显示的最小单位,我们可以把它看做显示器上的一个点.我们常说的 1920x1080像素分辨率就是用的 ...
- python-你好
你的程序会读入一个名字,比如John,然后输出"Hello John". 输入格式: 一行文字. 输出格式: 一行文字. 输入样例: Mary Johnson 输出样例: Hell ...