[Unity] 我的像素去哪儿了?当Unity的帧调试器不够用时

56 篇文章 2 订阅
27 篇文章 3 订阅

英文原文:https://thegamedev.guru/unity-gpu-performance/where-did-my-pixel-go-when-unity-frame-debugger-is-not-enough/

  从第5版开始,Unity提供了一个新的工具,用于直观地调试你的框架:Frame Debugger。这使你能够找出你将面临的许多图形问题的原因。Z-fighting,奇怪的GPU状态,错误的渲染队列,不正确的混合操作,大量的绘制调用,低性能,等等。与游戏视图中的统计布局相比,它提供了更详细的信息。通过与它互动并检查渲染事件/步骤,你也会大量了解GPU管道。真的,每个开发者都应该了解它。

  在这篇文章中,我将简要介绍如何使用帧调试器,之后我将指出通过其他工具可以解决的缺点。我的目标不是要对每一点都进行彻底的描述,而是要对上述技术进行概述。

在这里插入图片描述

1. 帧调试器: 使用方法

  通过Window->Frame Debugger打开我们的主窗口。我建议同时看到框架调试器和游戏视图,所以你可能需要调整一下你的布局。

  让你的游戏进入Play模式,进入让你头痛的区域。在帧调试器窗口,点击启用,这样你就能得到最后渲染的帧的细节。

  这里有两个主要区域:事件和右边的详细信息。事件基本上是CPU发送给GPU的命令。它们肯定被简化了,因为你不能真正看到正在调用的API函数。但它足够简单,可以保持整个过程的时间框架。大多数事件是关于清除和绘制(不透明/透明)几何体的,但你可能会看到更多的信息,如GUI渲染、阴影、图像效果等等,沿着这部分的回调数量。

  每当你选择一个事件,你会立即在细节面板上得到关于它的更详细的信息。这些信息包括:绑定的着色器及其标志,事件的渲染输出和着色器的属性。同时,游戏视图将显示在该事件之前(包括)已经渲染的内容。下面是一个例子。

在这里插入图片描述

2. 帧调试器。为了什么?

  帧调试器免费为我们提供了一些有用的提示。绘制事件的顺序(即渲染队列)对于正确的渲染至关重要。如果你有元素没有被正确显示,你可能会发现它被渲染在错误的渲染队列中,因此太早/太晚了,特别是当你在玩自定义着色器和参数,如深度写入和深度测试时。我通过使用这个工具解决了很多问题;例如,我发现天空盒覆盖了我的着色器的输出,因为我忘记了设置正确的标志和渲染队列。

  其次,你可以通过查看顶点/索引计数,相对地看到有多少个绘制调用,并间接地看到它们有多 “昂贵”。着色器通道的数量也有帮助,但是这里没有显示它们的复杂性,所以你必须使用一些常识。掌握这些信息将有助于你提高场景的性能;例如,你可能会发现一些网格由于某种原因没有进行静态/动态的批处理。在我的例子中,你可能已经注意到了两个用于渲染两个sprite的draw call,这些sprite可以很容易地通过使用 texture atlas 而合并成一个。

  第三,通过与帧调试器的互动,你可以迅速了解GPU架构,发现Unity是如何处理渲染过程的。你可以使用小键盘来浏览不同的事件,看看场景是如何一步步被渲染的。在前面提到的例子中,你看到场景从清除三个缓冲区(颜色、Z、模板)开始,然后是渲染不透明的几何体(从前到后)、天空盒和透明的几何体(从后到前)。

  最后,你可以访问着色器属性,以获得有关材料和着色器的更多信息。你还可以参考该对象正在使用的数据,如纹理。没有多少人知道Unity中的高级视图;你可以在检查器中通过点击右上方区域的段落图标并选择 "调试 "来访问它们。

在这里插入图片描述

3. 帧调试器的注意事项

  在为我们的一个客户工作时,我经历了一些由帧调试器的极端简单性引起的缺点。我在Unite 2016欧洲会议上和一些Unity开发者交谈过,他们似乎知道这些问题,尽管他们没有透露是否有进一步的计划来扩展它。根据我的经验,其中一些人是这样的。

  • 你不会得到关于这些事件背后的API调用的低级信息。在这种情况下,Unity是一个黑盒子,你不能(很容易地)在特定情况下进行改进。
  • 在每个事件中很难得到GPU管线状态的概述。你只能得到关于几何和纹理的信息,但没有关于不同阶段的数据,如顶点/几何/碎片着色器和光栅器。
  • 你无法真正调试着色器。要么你输出颜色来直观地调试你的代码(甚至比打印更复杂),要么你使用外部软件如GLSL-Debugger
  • 很难将产生的像素与写入它们的负责事件联系起来。你必须一步一步地检查哪个事件在你的像素中写入了错误的信息;如果你有大量的绘制调用,这可能需要很长的时间。
  • 没有准确的指标来衡量每次绘制调用的性能。顶点/indices的数量不一定是最好的一个。好吧,发送更多的顶点需要更多的内存带宽,但在该几何体上执行复杂的像素着色器或昂贵的状态变化可能会更糟糕。

如果你(仍然)在读这篇文章,你可能也在寻找答案。那么,用其他互补的工具进行实验呢?

4. RenderDoc

  在测试了几个软件后,我可以肯定地推荐几个。你的工具链中应该有的主要是RenderDoc,这是一个最初在Crytek开发的免费工具,用于解决低级别的问题。它是一个使用许多作弊者使用的经典方法的工具:它创建了一个虚拟图形驱动,并作为中间人(MITM)来捕捉应用程序的DX/GL/Vulkan调用,以便提供详细的调试信息。好消息是:已经有一个Unity本地集成。你只需要在你的windows机器上安装RenderDoc并重新启动Unity编辑器。

  我在这一节的目的是给你一个关于这个工具的特点的概述,以便你感到有足够的兴趣开始使用它,并使你的玩家通过顺利的游戏和支付更少的电费而更加快乐。

  让我们进入正题。如果安装成功,你应该能够右击游戏并选择加载RenderDoc。

在这里插入图片描述
  几秒钟后,进入Play模式,每当你对你最后呈现的帧感到满意时,点击 "播放时最大化 "左侧的新图标,它看起来像一个秃头。
在这里插入图片描述
  然后切换到新打开的RenderDoc窗口,双击自动捕获的日志来分析它。

在这里插入图片描述
  Wow WTF! 这么多信息。平静下来,一切都会好起来的。现在,你会在一千多个窗口中看到所有框架收集的数据。不用担心,我们将在接下来的段落中强调我认为非常有趣的几个要点。

4.1 事件浏览器+API调用。

  这个工具的一个优点是能够获得每个事件中正在发生的低层次信息。事件浏览器很像一个扩展的Unity的帧调试器。它包括计时(以微秒为单位)和其他提示,几乎可以帮助测量性能。RenderDoc是上下文敏感的,这意味着,任何时候你选择一个特定的事件,其他大多数窗口和区域将只显示该事件的相关信息。它下面的API调用窗口列出了处理该事件时调用的函数。
在这里插入图片描述
  得到这些时间是多么好的事情啊!当然,它们可能不是绝对精确的,但它们是相对准确的。当然,它们可能不是绝对精确的,但它们是相对准确的,这意味着,通过比较这些数字,你会发现哪些 draw calls 是最昂贵的。

4.2. Pipeline state.

  我一直想知道一个管道是怎样的。小巧可爱的几何图形如何进入它,并在你的50英寸屏幕上轻轻地转化为像素。好吧,你不可能从这个工具中得到这样一个用户友好的动画,对不起。

  反正有很多免费的资源可以做这个。但无论如何,这个工具会让你获得关于每个事件中的管道状态的广泛事实:输入装配器、顶点/壳/域/几何/碎片/计算着色器、光栅器和输出合并。相信我,那是好东西。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.3. 网格输出

  另一个功能是通过顶点着色器获得该事件中渲染的网格数据:进出位置、法线、纹理坐标等。事实上,你可以从其他3D程序/游戏中捕捉和保存正在渲染的网格,但我不会在这篇文章中处理道德问题🙂。

在这里插入图片描述

4.4. 纹理查看器

  我的最爱之一。将事件中使用的所有输入/输出纹理可视化,包括渲染到纹理和其他中间缓冲区。如果你在处理图像效果时使用中间缓冲区,这真的很有趣。同样,你可以保存纹理。

在这里插入图片描述

4.5. 像素调试

  在我们的帧缓冲器中看到邪恶的像素覆盖了好的像素是很常见的。我们中的许多人都做过这样的噩梦,因为他们留下了一种奇怪的感觉,就像我们的场景中出现了问题,但我们并不确定它的来源。它只是感觉不对。如果我们能确认我们的怀疑,并确定那里发生了什么…

  嗯,我们可以。选择一个像素,然后点击历史(获得在帧缓冲器中写入该像素的事件列表)或调试(用于调试其像素着色器)。调试它们是为坚韧的人准备的:你将需要汇编知识来理解像素着色器的反汇编版本。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5. 例子

  可能有人想知道,profiling在现实生活中是否有用。让我告诉你:这就是现实生活,它是有用的。每当你不确定一个功能的性能影响时,就进行研究。使用剖析器、frame调试器,如果需要的话,使用RenderDoc或类似工具。

  让我们考虑一个例子。并非所有人都知道,在你的脚本中访问材质和访问渲染器的shaderMaterial是有区别的。但是没有多少人知道这样做的具体性能影响。而这对于大多数情况来说还是可以的。但是,既然我们有了工具,就让我们来找出答案吧。

  我们的测试场景包括一个单一的 sprite 被实例化10000次,并使用相同的tranform。它几乎占据了整个屏幕,所以大量的过度绘制是可以预期的。代码如下。

void Start () {
  for (int i = 0; i < NumberSprites; ++i)
  {
    var sprite = Instantiate(Sprite);
    sprite.transform.parent = transform;
    var sharedMaterial = sprite.sharedMaterial;

    // Uncomment me for creating a material copy per sprite.
    // var mat = sprite.material;
  }
}

  访问sharedMaterial是可以的;你将访问所有 sprite 实例中共享的唯一材质。如果你访问material,你将为每个 sprite 创建一个独特的材质,因此Unity将无法批量调用绘制。用RenderDoc分析Render.TransparentGeometry的结果,结果是。

在这里插入图片描述
  我不太相信RenderDoc的时间,它的读数似乎一点都不准确,而且我也不知道有什么可靠的硬件计数器来处理这件事。我宁愿检查Unity的分析器。但无论如何,共享材质或灌输新的材质确实有区别。后者阻碍了在一次调用中绘制所有的东西,因此你有更多的驱动开销和内存带宽的惩罚。

  真正昂贵的是在绘制调用之间的状态变化,而不是绘制调用的数量本身。在通过Material.SetColor为Start上的每个sprite设置不同的随机颜色后,我注意到额外的2 FPS下降。这是最近推出的Vulkan API正在解决的问题。

6. 总结

  RenderDoc既不是一个完美的工具,也不是Unity的帧调试器的替代品,而是一个补充工具,如果你需要那种低级别的信息,你可以轻松使用。它还很有趣,可以看到一个人可以走多远;但你走得越远,就越复杂,因为你需要对GPU硬件结构有更好的了解。

  我承认,使用RenderDoc可能相当耗费时间,所以记住:在优化之前先进行profile。在95%的情况下,使用Unity的帧调试器可能刚刚好,但你应该为剩下的5%做好准备,这将使你的生活像杯具一样艰难。

  在使用profiler的同时使用这些工具。要知道瓶颈来自何处并非易事,尤其是在针对其他调试能力下降的平台时。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值