上回书《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里面估计又改了不少了)。

本回并没有着重讨论各种线程内部细节的任务,也没有非常深入的讲解各个线程之间是如何传递具体的命令和数据的,因为讲起来那篇幅真的就太长了,之后再慢慢地整理吧,网络上的资料也很多,大家可以自行拓展阅读。

参考文献

《图解UE4渲染体系》Part 1 多线程渲染的更多相关文章

  1. BGFX多线程渲染

    BGFX多线程渲染 1. 多线程基础 1. 并发概念 1. 并发任务简介 在多年以前,在手机.pc端.游戏机等,都是一个单核的CPU.这样,在硬件层面上,处理多个任务的时候,也是把一些任务切分成一些小 ...

  2. 《图解UE4渲染体系》Part 0 引擎基础

    在介绍UE4渲染体系前,我们有必要来先看一下UE4是用什么样的方式来构建游戏场景数据的. 1 Object 在UE4中当我们说Object,通常是指代引擎代码中的UObject类,它是引擎里管理绝大部 ...

  3. 剖析虚幻渲染体系(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 编辑 ...

  4. 剖析虚幻渲染体系(10)- RHI

    目录 10.1 本篇概述 10.2 RHI基础 10.2.1 FRenderResource 10.2.2 FRHIResource 10.2.3 FRHICommand 10.2.4 FRHICom ...

  5. 剖析虚幻渲染体系(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 ...

  6. 剖析虚幻渲染体系(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 ...

  7. 剖析虚幻渲染体系(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 ...

  8. 剖析虚幻渲染体系(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 ...

  9. 剖析虚幻渲染体系(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. ...

随机推荐

  1. 有序全排列c++实现(递归)

    1 #include <iostream> 2 #include <algorithm> 3 #include <iterator> 4 #include < ...

  2. Oracle的数据优化(经常被问到)?

    以Oracle数据库举例:(a-G要求掌握,H一般为DBA操作,了解就可以了) a. 建库:已知将保存海量数据的时候,因为Oracle是通过用户来管理数据的, 第一步我们先建一个tableaspace ...

  3. 编写 Java 程序时, 如何在 Java 中创建死锁并修复它?

    经典但核心Java面试问题之一.如果你没有参与过多线程并发 Java 应用程序的编码,你可能会失败.

  4. phpstorm chrome 添加xdebug扩展

    转:https://blog.csdn.net/ltcm_sakura/article/details/102967859 一.Xdebug helper:php调试插件 http://chromec ...

  5. SpringBoot项目单元测试不经过过滤器问题

    SpringBoot使用MockMvc:https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-featu ...

  6. memcached 最大的优势是什么?

    Memcached 最大的好处就是它带来了极佳的水平可扩展性,特别是在一个巨大的 系统中.由于客户端自己做了一次哈希,那么我们很容易增加大量 memcached 到集群中.memcached 之间没有 ...

  7. 【Matlab】简单的滑模控制程序及Simulink仿真

    文章: [控制理论]滑模控制最强解析 滑模控制程序及Simulink仿真 这篇文章仿真和输出U的推到有些问题,博主根据此篇文章进行修改进行对sin(t)曲线的追踪(使用滑模控制) 使用滑模控制对sin ...

  8. 1kb 的 placeholder.js 增加 img 标签使用方式

    预览 官方网站示例. 项目 github 地址 使用 引入 placeholder.js 到你的前段代码中: <script src="placeholder.js"> ...

  9. Angular2入门系列(五)———— 路由参数设置

    Angular2入门系列(五)---- 路由参数设置路由配置: { path: '', component: CarProFile, children: [ { path: 'add', compon ...

  10. H5使用Canvas绘图

    一.什么是Canvas Canvas 是H5的一部分,允许脚本语言动态渲染图像.Canvas 定义一个区域,可以由html属性定义该区域的宽高,javascript代码可以访问该区域,通过一整套完整的 ...