
Godot 分享渲染器 CPU 端代码优化实践
来源标题:Optimizing CPU-Side Rendering Code
Godot 渲染器团队公开了今年针对 CPU 端渲染代码的优化过程,说明渲染优化并非高不可攀。文章以 2D 批处理为例:该技术略微牺牲 GPU 性能但大幅提升 CPU 性能,因 2D 游戏通常先遇到 CPU 瓶颈而成为净收益;3D 游戏则更多受 GPU 限制,因此 Godot 把遮挡剔除放在 CPU 上执行以减轻 GPU 负载。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
我想让大家稍微了解一下 Godot 渲染器的优化过程是什么样子。从外部看,渲染优化看起来有点像黑魔法。我想展示几个我们今年所做的优化示例,以此说明优化渲染代码并没有听起来那么可怕。
在本文中,我只讨论 CPU 侧的代码优化。在做渲染器时,你必须同时谨慎地优化 CPU 和 GPU 代码,因为最终性能取决于两者中较慢的那一方。再多的 CPU 优化也救不了低效的 shader。
在编写渲染器时,你还会不断做出取舍,以惠及一方或另一方。例如,2D 中的合批(batching)是一种会略微损害 GPU 性能、但能大幅提升 CPU 性能的技术。由于 2D 游戏往往先遇到 CPU 瓶颈再遇到 GPU 瓶颈,所以在大多数情况下合批最终是净收益。反过来,3D 游戏更常遇到 GPU 瓶颈,所以在 Godot 中我们把遮挡剔除放在 CPU 上做,以减轻 GPU 的负载。
无论你在 CPU 和 GPU 工作之间最终如何平衡,优化引擎总是有益的,因为对引擎的任何优化都为游戏开发者留出了更多使用 CPU 和 GPU 的空间,并为受功耗限制的平台节省电量。
CPU 性能优化的过程大致如下:
这是性能优化中最困难的部分。当你的游戏或应用开始运行不佳时,很难准确追查原因。通常,一旦你看到性能不佳的来源,你立刻就能明白为什么性能差。常见的情况是你在热循环中不小心调用了昂贵的 API,或者留下了一些本不该出现的调试代码。
对于我们引擎开发者来说,还有一个额外的挑战:需要针对各种各样的潜在游戏进行优化。我们不只有一个目标,我们有成千上万个移动的目标,只有当人们提交 bug 报告时我们才知道存在严重问题。话虽如此,仅使用我们现有的演示和开源游戏进行测试,我们就能发现并做出许多改进。
识别 CPU 瓶颈的最佳工具是 CPU 性能分析器。它们形态各异、大小不一。Godot 甚至在编辑器内置了两个(一个用于分析 GDScript 代码,另一个专门用于分析渲染器)。然而,在处理引擎代码本身时,你需要使用外部性能分析器。Godot 支持使用 Tracy 构建以进行跟踪分析,但你也可以使用外部采样分析器。
我选择的工具是 Superluminal,它是一个外部采样分析器。它附加到正在运行的 Godot 实例上,在你运行引擎时记录性能数据。然后它提供非常直观的可视化,展示什么代码在何时运行。它最近还获得了 Linux 支持!本文下面的示例将使用 Superluminal。
如上所述,有时理解瓶颈很容易。特别是当性能不佳是由于 bug 而非糟糕的设计选择时。通常,理解瓶颈需要理解周围的代码。归根结底,性能优化归结为“不做不需要做的事”。理解代码和理解系统对于理解哪些事情需要做、哪些事情不必要非常有帮助。
通常,一旦你理解了瓶颈,解决方案看起来就很明显。但有时你需要尝试几种不同的方法,才能弄清楚到底需要什么。例如,你可能看到一个包含大量计算的重循环,最初认为解决方案是减少每次迭代的计算量。但问题可能来自内存带宽,因此重构数据可能会带来更好的性能提升。
最终,你会做出直接减少瓶颈影响的更改。
你总是需要重新测量。计算机非常复杂,游戏引擎非常复杂,有时你认为会更快的东西并不更快。优化往往反直觉。例如,在 for 循环中添加一个分支来跳过某些计算,直觉上似乎总是更快,但在许多情况下,它会阻止编译器对循环进行向量化,从而导致性能更差。或者,一个更常见的例子,你可能会缓存一个值以避免计算两次,但如果该代码路径中的瓶颈是内存读取,那么缓存该值会让你的代码更慢,还不如计算两次。
除非你是绝对的 C++ 编译器天才,否则你很可能无法猜出代码更改对性能的确切影响。所以最好总是重新测量并确认你的结果。
这个 PR 源于我与 Aurélien Condomines 的一次对话,他正在开发 Heidi's Legacy: Mountains Calling。他指出他发现 Godot 中的动画代码非常慢,这限制了他为游戏添加内容的能力。他发现,当有 20 个左右的动画角色时,他的游戏性能就开始下降。
我对这些数字感到惊讶,觉得有些不对劲。我们的动画代码不应该在只有 20 个左右动画角色时成为瓶颈,所以我决定调查。我请他做一个最小项目,在屏幕上放一堆动画精灵,以便我进行分析。
第一个线索是,他使用 AnimationPlayer 来为 Polygon2D 的顶点做动画,而该 Polygon2D 显示在 Sprite3D 上的 Viewport 中,以便在 3D 世界中获得好看的 2D 动画。
这听起来有点独特,但不应该导致任何显著的问题。所以我们的下一步是启动 Superluminal,看看发生了什么。
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。