导语如何高效准确详细的对性能进行剖析?腾讯游戏学院专家Leonn将从RHI(渲染提交)开始,归纳总结在UE下对每一性能指标的剖析方法。

基于UE的手游客户端的性能主要由这七大部分构成:CPU逻辑、CPU渲染、图形API(提交)、GPU渲染、内存、带宽、加载时间。这几个基本元素又会合力衍生出一些新的性能指标,例如功耗(往往同GPU负载和带宽紧密相关)。同时这七部分又构成一个闭合的木桶,最长的一块是主要瓶颈,并且瓶颈可以在这几块转移流动。

作为开发者,我们解决性能问题的步骤一般都是按照做性能剖析、解读结果、定位问题、增加剖析代码、优化问题、重复剖析的迭代过程来执行。而高效准确详细的对性能进行剖析得到结果是第一步,在任何引擎上,只要我们能做到在任意时刻准确的获取想要的性能剖析结果,那么才会胸有成竹不会慌,该系列文章将归纳总结在UE下对每一性能指标的剖析方法,做深入分析,我们需要工具的帮助,也需要程序员理解引擎并知道如何去编写合适的剖析代码。

最近刚好做过一轮RHI线程的剖析,第一篇就从RHI开始,我会坚持把后面几篇写下去。

渲染API瓶颈

渲染API瓶颈是3D手游的常见瓶颈,我们常说的drawcall过多了,卡渲染就是指的卡在这里,其实这个卡渲染卡的是CPU。为什么drawcall会卡,因为CPU需要通过对渲染API的调用来驱动GPU做事情,1个drawcall的背后是一堆渲染API的调用,下面是一个常见的drawcall过程。

可以看到为了一次绘制(1个drawcall),要设置shader,创建buffer等等,这些相比最后的draw那一步来说都是相对更费时的。

当测试反馈给我们卡drawcall的时候,作为程序我们需要一种手段来衡量出确切的当前做哪些drawcall,或者说绘制哪些东西更耗,最好是精确到耗在绘制哪个模型的哪个API调用上,我们才能真正的给美术予以优化指导。

UE中精确定位RHI瓶颈

在UE中,PC和android平台通常渲染API的调用会放在一个单独的线程,叫做RHI线程,这个线程专门负责渲染指令的提交,即调用显卡的API。我们分析渲染提交的卡顿就是要分析这个RHI线程。

1.多线程渲染工作模型

但是RHI线程不是单独存在的,它需要同Game,Render线程协作,RHI的卡顿可能不只是RHI的卡顿,首先需要清楚UE里面RHI线程和其他线程的工作模式:

这里面Game、Render、RHI、GPU分别在4个并行的工作线上,有这样几个特点:

1.Game thread最多可以等渲染一帧,也就是说渲染如果第N帧的渲染在第N+1帧的Game tick结束时还没有完成,那么渲染就会把Game卡住,Render和RHI不会有帧延迟。

2.Game是Render和RHI的源驱动者,Game的卡顿可能会卡住渲染。

3.Render负责产生drawcall,RHI负责提交drawcall,因此Render的卡顿也可能卡住RHI提交。

4.渲染的最后一步要swapbuffer,即等待GPU完成,所以GPU的卡顿也可能会卡住RHI。

5.除了Gamethread本身,Render RHI和GPU的工作都是存在间隙的,即Game逻辑喂给渲染任务的时机会影响渲染工作的密度,也会影响到渲染的时间,小量多次会浪费渲染效率。

2.UE中RHI的瓶颈来源

现在我们知道RHI的卡顿可能来自于以下几种情况:

a.RHI指令自身的卡顿,即通常所说的卡drawcall,过多的dc,过多的渲染状态切换,过多的渲染资源创建,等等;

b.Game或者Render thread的卡顿;

c.GPU的卡顿。

对于情况b,我们可以通过UE的status看当前的Game和Render的线程执行时间来容易的判断出来,来排除是RHI上出了问题。

对于情况c,UE的status中在RHI线程上会统计一个叫做swapbuffer的时间,如果这个时间过长,那么就是GPU瓶颈了。

真正比较麻烦的是定位情况a,即对于RHI指令本身的卡顿瓶颈。对于这种情况UE自带的stat工具通常不能给出比较有力的分析结果,自带的方法只能统计一帧在RHI上做几种给定操作的时间,但是在复杂的线程条件下,有时很难确定这些卡顿的幕后原因,有时RHI问题只是一个表象,为了得到rhi线程瓶颈的确切原因,我们至少要能够明确以下几个事情:

1.RHI线程的执行是由一堆有序的RHI command组成的,我们要能捕捉到具体的那一个RHI command的执行时间比较长,比如是创建场景中哪个房子的vb?

2.是在Render thread的哪一个步骤塞入的渲染数据导致了这个RHI command执行的时间比较长,是在渲染阴影的时候,还是渲染basepass,还是做遮挡剔除?

3.是在Game thread的哪一个步骤塞入的渲染数据导致了这个RHI command执行的时间比较长?是在加载场景?还是在绘制UI的时候?

笔者在项目中遇到过一个问题,在一些低端机,RHI会有时突然卡顿几秒以上,看stat文件如下:

我只能看到在RHI线程的Thcik begin阶段发生了巨大的卡顿,然后就没有细节了,不知道是具体哪个RHI command,然后看Game thread在wait,也不知道是Game thread的哪一步触发了这个RHI瓶颈。我们需要一些办法。

定位UE中RHI线程的瓶颈

我们需要分别将上面三种原因捕捉到,手机游戏买号平台就能解开这个问题。

首先定义一个宏,只有我们需要捕捉这些详细的RHI瓶颈时开启,因为这些操作会存在较大的overhead。

1.定位具体RHI Command的时间

对于RHI Command的具体执行时间,我在FRHICommand的最终执行阶段ExecuteAndDestruct中创建一个FScopeCycleCounter,counter的名字就直接rtti当前command的typename。

有时候我们需要更细节的知道这个Command除了类型外的信息,例如如果这是一个createvb的Command,那么vb的原始模型名字是什么,vb大小等,我在一些Command处额外传了一些debug用的string,然后在这些Command的执行前补上一个FScopeCycleCounter。这样我们就能拿到精确到具体RHI ommand的提交耗时了。经过这个补充,我能拿到这样的RHI线程执行时间统计:

这样谜底就清晰了很多,原来这时候存在大量的vb创建,数了一下,有几百个,在同一帧内几百个vb的创建,在低端android上会产生5秒钟的超级卡顿,那么问题来了,为何在这一帧会同时产生这么多的vb卡顿,是Game或者Render上发生了什么事情,如果我们查看当前的Game thread,它显示的是wait,是不知道原因的,因为Game,Render,RHI是分开工作的,我们现在RHI处于瓶颈已经不是事故的“第一现场”了,我们需要进一步让你发生在第一现场。

2.定位在Render哪个阶段发生了RHI瓶颈

UE的RHIcommandlist自带了一个函数FRHICommandListImmediate::SetCurrentStat,可以用来让Render给RHI加一个标记,这个标记就可以认为是Render的某个阶段的名字,UE自带了在Render的很多阶段下了这个标记,我们还可以自己补充,这个函数的原理如下:

这个status本身也是以command的形式插入队列,所以每一条RHI执行的cmd会被统计到它之前最近的那个status tag下面,通过不断的细分插入这些tag,我们可以跟踪到RHI的cmd从是在Render的哪个阶段被产生。需要注意的是这个tag只能在Render thread里插入。我为Render thread补充了一些细化的tag后,如前面的图,我发现这个大量的vb创建发生在渲染线程的一帧渲染结束到下一帧渲染开始之前,在这个阶段有Game逻辑往Render里面堆入了大量创建vb的指令,所以问题还要继续往Game thread上找“第一现场”。

3.定位在Game哪个阶段产生RHI瓶颈

其实我们仍然可以模仿Renderthread一样在Game thread上给RHI的command list里面插入tag,但是有个问题,Renderthread是一种相对简单的Render command的队列的顺序执行,tag量有限相对容易操作,但是Game thread里面逻辑极其复杂,我们希望可以复用Game thread上面已经埋好的一些scope counter,不过Game和RHI是两条并行的thread,需要在我们关心的scope处让二者能够强行同步住,才能容易的使用Game自己的scope counter抓住RHI的执行。我们这样去实现,假设下面是我们关心的一个Game thread的区段,在前后加上代码如下:

  1. #if STAT_RHI_ADVANCED
  2. FlushRenderingCommands(true)
  3. DECLARE_SCOPE_CYCLE_COUNTER(TEXT("XXX"), STAT_XXX, STATGROUP_RHI_GAME_SYNC);
  4. #endif
  5. //game 代码段
  6. //
  7. #if STAT_RHI_ADVANCED
  8. FlushRenderingCommands(true);
  9. #endif

复制代码

FlushRenderingCommands(true)的意思是在这个位置强制将所有当前的RHIcommand执行完毕,阻塞住当前线程,所以上面这个代码段的原有的XXX统计的时间将包括这段时间内因为Game thread上发生的渲染事件的渲染而花费的时间。

通过在Game thread的主要逻辑处,插入这些同步RHI线程的代码,当RHI线程发生瓶颈的时候,我们只要查看当前Gamethread在哪里停住(wait event),就可以判断是什么Game逻辑导致了RHI线程的瓶颈。有了这个机制,我们接着截stat文件,会看到当RHI处于巨大瓶颈时,Game thread停在了这里:

凶手被抓住了,是一个资源正在被缓存池预加载!

这个奇怪的RHI上的卡顿的真正原因其实是Game线程上在加载一个模型资源!如果只依靠UE本来的stat分析,是无论如何都不可能猜到这个幕后的凶手的。

那么问题来了,一个资源的加载为何会导致海量的vb同时创建?通过进一步的分析代码,会发现因为这里用的是同步加载,而UE的同步加载的机制,是创建一个加载任务堆到同异步加载一样的加载队列里,因为不能保证依赖关系,所以要等待当前所有队列中的任务完成才能继续下去,也就是说当前的同步加载的时间绝不仅仅是加载完你要的这个模型而已,他需要将当前异步加载任务在队列中的所有资源加载完!而这个时候恰恰处于场景在level streaming的阶段,最后发现此事加载队列中的资源上百个,这个同步加载遇上level streaming的结果就是,在这一帧要完成上百个模型的创建,模型的postload会初始化RHI资源,导致一帧内大量vb的创建,卡死RHI,所以罪魁祸首是同步加载,同步加载将level streaming的过程也强行同步了,找到了问题,我们就可以通过相关的优化手段来排除这个瓶颈。

RHI上的问题可能往往不只是RHI上的问题那么简单,通过上面说的一些方法我们可以清楚的看到各种RHI上瓶颈的真正原因。

腾讯游戏学院专家:UE高级性能剖析技术之RHI相关推荐

  1. UE高级性能剖析技术(1)-- RHI线程(渲染提交)

    在最前面 基于UE的手游客户端的性能主要由这七大部分构成:CPU逻辑,CPU渲染,图形API(提交),GPU渲染,内存,带宽,加载时间.这几个基本元素又会合力衍生出一些新的性能指标,例如功耗(往往同g ...

  2. 腾讯游戏学院专家实例剖析:如何优化休闲游戏的美术风格?

    导语:随着人们生活节奏逐渐加快,碎片化时间越来越多,休闲类手游成为了一部分人上下班途中的一大选择.这类游戏玩法不会过于复杂,有时会带有一些收集元素.除此之外,游戏的美术表现也是吸引玩家长期体验游戏的关 ...

  3. mie散射理论方程_腾讯游戏学院专家:PBR渲染模型的理论及具体应用

    编者按 PBR,基于物理的渲染模型,是当前主流游戏引擎使用的真实感3D渲染模型.腾讯游戏学院专家Leonn,将和大家分享PBR的理论知识以及在Disney和UE中的典型应用. 文 | Leonn 腾讯 ...

  4. 省带宽、耗电小,腾讯游戏学院专家解析手游渲染架构

    编者按 如何让手游更省带宽,耗电量更少?渲染或是其中一个可突破的点.本文中,腾讯游戏学院专家Hailong将从为大家解析TBR渲染架构的特点. 什么是TBR? 全称是Tile Based Render ...

  5. 腾讯游戏学院专家:如何避免出海游戏服务器水土不服?

    曾参加2018GWB腾讯游戏创意大赛的<风暴岛>,获得了WeGame的签约以及腾讯游戏学院专家团的指导,现已于4月在WeGame和Steam双平台上线.本文采访了负责指导服务器方面的专家J ...

  6. scratch做简单跑酷游戏_腾讯游戏学院专家:做一个多线程游戏框架可以多简单?...

    导语 如何做一个多线程游戏框架?腾讯游戏学院专家Tao将在本文通过一个demo来说说游戏逻辑的多线程化. 众所周知现在各种游戏终端的发展十分迅猛.其中一个共同的特征是"多核化",由 ...

  7. 腾讯游戏学院专家分析:Unity在移动设备的GPU内存机制

    导语CPU和GPU是共享一份内存的吗?腾讯游戏学院专家Donald将在本文尝试以一张贴图纹理的虚拟内存占用为例,解答一些内存方面的问题.本篇主要分析iOS系统,后续会更新安卓篇. 开发手机游戏时,常听 ...

  8. 1709 ltsb 内存占用_腾讯游戏学院专家分析:Unity在移动设备的GPU内存机制

    导语 CPU和GPU是共享一份内存的吗?腾讯游戏学院专家Donald将在本文尝试以一张贴图纹理的虚拟内存占用为例,解答一些内存方面的问题.本篇主要分析iOS系统,后续会更新安卓篇. 开发手机游戏时,常 ...

  9. 如何应对CPU帧率瓶颈和卡顿?腾讯游戏学院专家带你剖析

    CPU上帧率低和卡顿是性能优化中最易出现的一部分,尤其对于手游,提到卡,就大概率是在CPU上出现的问题,CPU上的卡顿一般是卡逻辑或是卡渲染,本篇将详细系统的介绍基于UE的手游对CPU瓶颈的剖析方法. ...

最新文章

  1. URI、URL以及URN的区别
  2. winform中构造函数与Form_Load
  3. 变分模态分解_Android小部件示例中的模态对话框(弹出)
  4. 从重采样到数据合成:如何处理机器学习中的不平衡分类问题?
  5. python脚本编写_如何用Python包编写一个简单的脚本,表达你对父母的爱?
  6. orm框架选型问题_ORM问题
  7. 从windows计算机中卸载office,如何彻底卸载电脑中的Office组件|Office办公软件卸载不干净怎么办...
  8. WDM驱动程序入门(1)-Hello WDM
  9. DFS-20190206
  10. 【OJ8462】大盗阿福
  11. U盘启动盘恢复为普通盘
  12. php 安装mysql扩展注意事项
  13. [Hive]看懂Hive的执行计划
  14. mac下chrome浏览器设置ajax跨域调试
  15. 【百度之星程序设计大赛】2020年初赛第三场第四题Intersection
  16. C++项目学习(机器人方向)
  17. etree解析xml_在python中使用Etree解析XML配置文件
  18. C语言符号优先级速查
  19. 解决Java下载Zip压缩文件,解压后报不可预料的压缩文件末端
  20. JavaScript复制内容到剪贴板 1

热门文章

  1. 使用css动画实现网易云音乐播放界面波浪动画效果
  2. IE访问Form Builder网站的碰到的几个问题
  3. L298P控制引脚测试报告
  4. 一种简单的关闭win10自动更新的方法
  5. Error: Flash Download failed - “Cortex-M3“错误解决办法
  6. 极客星球 | 阿里:云原生数据湖构建与分析实践
  7. 安徽省计算机二级c语言分数分配,计算机二级C语言题型和评分标准
  8. 2021年熔化焊接与热切割复审考试及熔化焊接与热切割
  9. 头歌平台(EduCoder)————软件测试(测试过程与策略)
  10. 怎么打开计算机的硬盘,如何检测电脑硬盘是否运行_怎么进bios设置看硬盘是否被电脑识别...