研发实战四:如何通过RenderDoc优化Oculus Quest应用
用RenderDoc来优化Quest应用
(映维网 2020年01月13日)Oculus开发者关系团队的一个主要任务是帮助开发者优化游戏,令其能够有效地支持所有的Oculus硬件。所述团队中的开发者关系工程师克里斯蒂亚诺·费雷拉(Cristiano Ferreira)正在通过系列博文来介绍如何通过RenderDoc来优化你的Quest应用,包括如何轻松将其集成至你的工作流程,相关的基础知识,以及允许你在未来利用这项有用工具的技巧经验。对于第四篇博文,费雷拉继续介绍了相关的关键使用情景和优化技巧,下面是映维网的具体整理:
1. 正向渲染和延迟渲染的区别
1.1正向渲染
正向渲染非常简单,它仅绘制通过绘制调用分配的网格,然后直接渲染到帧缓冲区。照明是由片段和/或顶点着色器计算。这种方法的缺点是,动态/重要照明可能需要额外的通道。GPU成本取决于每个模型的几何图形数量,所需通道的数量,以及照明模型的复杂性。在延迟渲染中,照明是在屏幕空间中进行计算。
1.2 延迟渲染
基本上,延迟渲染意味着你将照明计算推迟到完成基础通道之后。对于延迟渲染中的基础通道与正向渲染中的基础通道,两者的不同之处在于,你正在写入名为Gbuffer(几何缓冲区)的多个渲染目标。Gbuffer包含多个渲染目标,其定义反照率(颜色),法线,粗糙度值等,亦即你计算照明所需的任何内容,无需通过图形管道重新运行模型。写入值后的下一个阶段是,使用先前生成的g缓冲区并在屏幕空间中运行照明通道。这种方法的优势在于,可以在屏幕空间中进行照明计算,从而将几何复杂度与照明分离。延迟渲染的问题在于,在照明通道阶段,你需要解析GBuffer中存在的众多渲染目标,而它们占用了帧预算的近一半。这甚至不包括相当高负载的照明通道,因为你需要计算Quest高分辨率屏幕的每个像素的照明。正向渲染正是我们的答案。
上面的RenderDoc截图是使用延迟渲染捕获的游戏。箭头显示了每个单独绘制调用所绘制的渲染目标。然后渲染目标用作照明计算的输入纹理。为了进行比较,请参阅下面使用正向渲染捕获的游戏:
2. 着色器复杂度
可以在RenderDoc中查看为每个绘制调用生成的着色器字节码,从而确定着色器编译的内容。最佳的优化方法是查看绘制调用,如果几何图形数量少且像素覆盖率不高,则需要花费相当长的时间才能完成。使用RenderDoc的次数越多,你的第六感就会越来越强。与我合作的开发者发现,使用25条-50条指令通常是着色器在保持最佳性能的同时获得理想画面的最佳选择,但你确实需要考虑复杂的数学指令,纹理查找和潜在像素覆盖的数量。通常,我们建议制作一组有限的uber着色器,以最大程度地增加批处理并最小化setpass/管道改动。附带的好处是,你只需要担心一次为一小组着色器生成的代码。当使用有限的超级着色器集时,只有少数几个着色器的性能增强会传播到逻辑组中的所有网格。例如,如果你的游戏将城市作为背景,则可以为建筑物,混凝土人行道,屋顶等提供环境着色器,为蒙皮角色提供一个环境着色器。这同时允许你轻松合并网格并创建网格LOD/ HLOD,不必担心管道状态会影响它们。
建议:所有对象都采用不同的输入纹理,但你可以使用纹理数组和查找ID(或通过创建纹理图集),尽可能为更多的对象使用相同的着色器,从而最小化setpass并最大化批处理。
下面是使用RenderDoc查看当前选定的绘制调用的输入纹理/uniforms的方法:
3. LOD和HLOD
LOD系统允许你置换超复杂的详细网格。当用完好的LOD置换模型时,你看不到模型弹出的原因是,只要比例尺保持不变,网格覆盖的像素数量就会随着距离的增加而减少。你是否真的希望在仅覆盖少数像素的情况下以500个世界单位的距离渲染100K顶点的网格对象呢?当然不是,请充分利用现代商业游戏引擎为你提供的内置LOD系统。
要检查LOD系统,你可以通过检查RenderDoc中的顶点数,并将其与对象的像素覆盖率进行匹配,从而确保出现在较远裁剪平面的对象使用最低的LOD。然后,你可以回溯到你的asset,并确保LOD使用适当的camera距离来置换它们。
在下面的图片中,我设将几何密集的炉灶模型放置在离camera很远的地方:
HLOD是LOD系统的扩展,可帮助节省大型对象的绘制调用。我们最好用一个示例进行解释:
假设你尝试渲染一座具有众多街区的巨大城市,而每个对象都用自己的绘制调进行渲染。即便最遥远的街区包含最低的LOD,简单绘制每个低LOD都会令绘制调用计数大大增加。利用HLOD系统,你可以离线合并遥远街区中的所有最低LOD网格,然后告诉引擎用一个绘制调用渲染整个街区。请记住,如果你拥有一个使用纹理图集或纹理阵列查找的超级环境着色器,你只需合并网格并重新映射UV即可。HLOD系统的缺点在于,它们必须利用更多的内存,因为你实际上是在为构成HLOD的所有低LOD复制几何图形,所以请确保你拥有足够的可用内存。
4. 剔除验证
对于通过跳过不必要的绘制调用来尽可能高效地渲染游戏,你可以选择数种不同类型的绘制调用/剔除系统。下面我将介绍主要的工具。
4.1 视锥剔除
视锥剔除是指跳过掉在camera视锥之外的绘制调用。如果对象位于视锥之外,它将不会投影到屏幕空间中的任何像素,所以我们可以放弃,从而避免GPU处理所述绘制调用和CPU建立所述绘制调用的成本。开发者可以设置一定的参数来确定视锥的大小(近/远剪切平面+ FOV),但对于Quest,最好维持OVRCameraRig提供的默认设置。这对于保持舒适度十分重要。
在RenderDoc中,你可以通过Mesh Viewer选项卡查看输入的网格和生成的边界体积。你甚至可以通过选择VS输出页面来查看将网格对象投影到屏幕空间的方式。
4.2 绘制距离剔除
绘制距离剔除意味着你将跳过与camera相距一定距离的绘制对象。如果一个对象的位置超出了视锥剔除中的远方剪切平面的范围,引擎将自动执行这个操作,但对于特定对象,避免为camera视锥内的小型对象分配绘制可能十分合理。跳过这种对视觉无关紧要的绘制调用将节省主线程/渲染线程的CPU时间,并避免处理所述对象的几何形状。对于上文提及的“炉灶”示例,如果网格对象距离太远并且对游戏的意义不大,我们可以予以禁用:
4.3 遮挡剔除
这是在视锥内进行的剔除步骤。遮挡剔除会跳过完全遮挡在另一个网格后面的网格的绘制调用。一个常见的示例是,如果你站在树林小木屋的外面,小木屋没有窗户且大门紧闭。你是否要渲染小木屋中的每个对象呢?显然不会。你可以要求CPU进行一定的计算,并确定哪个对象遮挡了哪个对象,然后再开始设置渲染线程。这将为你节省GPU的其他绘制调用和几何处理时间。我在渲染队列一节中简要提到了遮挡剔除,因为它们存在一定的相关性。两者之间的主要区别是,z-rejection将依然处理几何体并为网格添加绘制调用,而遮挡剔除将为你节省整个遮挡的网格的处理,如绘制调用准备,几何处理,以及所有片段着色器操作。
在RenderDoc中,你可以查看一帧中的所有渲染对象,并依靠常识来确定游戏是否将从实现遮挡剔除中受益。下面两张屏幕截图分别是头显看到的画面,以及一系列根本无需绘制的对象渲染。
CPU计算遮挡剔除算法确实需要一定的处理能力来确定遮挡网格,但对于特定的游戏类型和情况而言,这十分值得。你有一系列的技术选择,而Unity + UE4都提供内置的解决方案,可允许你对时序和效率进行测试。有时候某些工具的效果要好于其他,所以在选择使用或创建哪种实现时,请仔细考虑场景布局/游戏风格。
对于Unity,请确保尽可能利用DOTS。这是一项高度可并行化的工作,而你将会看到巨大的增益。
对于UE4,请确保利用并行容器,从而享受多核使用的增益。
4.4 剔除系统的通用优化建议
为确保上述的每个剔除系统都尽可能有效,请确保批处理没有稀疏区域,并且批处理中的几何范围不会太大。如果你合并了在集中区域的几何密集网格,但有一些长而细的网格突出,这会将令批处理生成的边界体积很大,从而导致在剔除算法中产生大量误报。在大多情况下,这会令批处理中的所有几何图形的像素覆盖范围从零到很少。确保网格批处理周围生成的边界体积尽可能集中。请想一想,如果我必须将这个巨大的网格装在一个玻璃盒子中,空白空间又会有多少呢?请确保空白空间始终尽可能地小,并在必要时分成多个批处理。
这是一个普通的示例(没有太多几何),但它确实说明了如何查看提交的批处理并将其拆分。下图是一个批处理调用。如果玩家站在边界空间的中间,我们可以将其分为批处理1和批处理2,从而节省几何投影成本。最终,如果位于边界体积的簇中存在非常密集的对象,这将变得更加重要:
5. 追击浪费的调用
有时候,当你在材质上使用透明混合状态时,你需要完全淡出对象。这些年来,我发现有时候对象一旦完全淡出(alpha=0),开发者就会忘记禁用网格渲染器组件。这可能会产生轻微的成本影响,范围从轻微到极端。如果在完全淡出对象后依然启用了网格渲染器组件,则你可能会认为引擎会自动在后台跳过绘制调用,但大多数情况下都并非如此,因为它们不知道你的意图,这意味着你的绘制调用将贯穿图形管道,不会更新任何像素,这完全浪费了CPU的绘制调用以及GPU的所有渲染工作。如果alpha=0,某些像素着色器会提前进行条件检查,但你仍然是在浪费宝贵的GPU时间来处理几何。
概要:在帧中查找对最终颜色缓冲区无用的绘制调用,并将其回溯到引擎。如果脚本/蓝图代码中的alpha=0,则可以进行简单的条件检查以禁用关联的渲染器。
下面是带有透明材质的拉伸立方体。我通过漫反射纹理Alpha通道调低Alpha值,从而淡出对象。
在RenderDoc中,我们可以看到完全透明的对象依然在渲染,但由于alpha值为0,所以没有输出任何颜色变化。
在Unity和Unreal中,请确保为完全透明的对象禁用关联的渲染器。
6. 查找错误和视觉伪影源
如果找到渲染伪影,则可以找到引起问题的绘制调用并帮助缩小搜索范围。RenderDoc .rdc文件的另一个好处是,你可以共享相同gfx驱动版本的任何Oculus Quest中重新运行它们。 如果你希望QA找出性能问题所在,这将非常有用。
7. 总结
我希望这个系列指南能够帮助你优化游戏和应用的性能。RenderDoc是功能最强大的工具之一,它可以确定GPU中实际发生的事情,并帮助你精简绘制调用准备工作/CPU的渲染线程。请记住,即便预算不足,你依然可以优化,腾出空间来容纳其他效果和图形,从而为玩家带来更多的沉浸感。
延伸阅读:研发实战:如何通过RenderDoc优化Quest应用
延伸阅读:研发实战:用RenderDoc+固定注视点等渲染技术优化Quest应用
延伸阅读:研发实战:用RenderDoc验证有效渲染方式,优化Quest应用