英文原文:https://resources.unity.com/games/ultimate-guide-to-profiling-unity-games?utm_source=demand-gen&utm_medium=pdf&utm_campaign=render-with-quality-and-flexibility&utm_content=introduction-to-urp-ebook
本节确定了分析时的一些有用目标。 它还着眼于常见的性能瓶颈,例如受 CPU 限制或 GPU 限制,并详细说明如何识别这些情况并更详细地调查它们。
最后,它深入研究了内存分析,这在很大程度上与运行时性能无关,但很重要,因为它可以防止游戏崩溃。
设置帧预算
以每秒帧数 (fps) 为单位衡量游戏的帧速率并不适合为玩家提供一致的体验。 考虑以下简化场景:
在运行时,您的游戏在 0.75 秒内渲染 59 帧。 但是,下一帧渲染需要 0.25 秒。 60 fps 的平均传输帧速率听起来不错,但实际上玩家会注意到卡顿效果,因为最后一帧需要 1/4 秒才能渲染。
这是为每帧设定特定时间预算很重要的原因之一。 这为您在分析和优化游戏时提供了一个坚实的目标,并最终帮助您为玩家创造更流畅、更一致的体验。
每帧都会有一个基于您的目标 fps 的时间预算。 以 30 fps 为目标的应用程序每帧花费的时间应始终少于 33.33 ms (1000 ms / 30 fps)。 同样,60 fps 的目标每帧为 16.66 ms。
您可以在非交互式序列期间超出此预算,例如,在显示 UI 菜单或场景加载时,但不能在游戏期间。 即使是超过目标帧预算的单帧也会导致卡顿。
在 VR 游戏中始终保持高帧率对于避免让玩家感到恶心或不适至关重要。 没有它,您可能会在游戏认证期间被平台持有者拒绝。
每秒帧数:一个欺骗性的指标
游戏玩家衡量性能的常用方法是使用帧速率或每秒帧数。 但是,建议您改用以毫秒为单位的帧时间。 要了解原因,请查看 fps 与帧时间的关系图。
考虑这些数字:
1000 ms/sec / 900 fps = 1.111 ms per frame
1000 ms/sec / 450 fps = 2.222 ms per frame
1000 ms/sec / 60 fps = 16.666 ms per frame
1000 ms/sec / 56.25 fps = 17.777 ms per frame
如果您的应用程序以 900 fps 的速度运行,这将转换为每帧 1.111 毫秒的帧时间。 在 450 fps 时,这是每帧 2.222 毫秒。 这表示每帧仅相差 1.111 毫秒,即使帧速率似乎下降了一半。
如果您查看 60 fps 和 56.25 fps 之间的差异,则分别转换为每帧 16.666 毫秒和每帧 17.777 毫秒。 这也代表每帧多出 1.111 毫秒,但在这里,帧速率下降的百分比感觉远没有那么显着。
这就是为什么开发人员使用平均帧时间而不是 fps 来衡量游戏速度的原因。
除非您低于目标帧速率,否则不要担心 fps。 专注于帧时间来衡量您的游戏运行速度,然后保持在您的帧预算范围内。
阅读原始文章“Robert Dunlop’s fps versus frame time”,了解更多信息。
移动挑战:热控制和电池寿命
在为移动设备开发应用程序时,热控制是最重要的优化领域之一。 如果 CPU 或 GPU 由于代码效率低下而花太多时间全速工作,这些芯片就会变热。 为了避免损坏芯片(并可能烧伤玩家的手!),操作系统将降低设备的时钟速度以使其冷却,从而导致帧卡顿和糟糕的用户体验。 这种性能降低称为热节流。
更高的帧速率和增加的代码执行(或 DRAM 访问操作)会导致电池消耗和发热增加。 糟糕的性能还可能会切断低端移动设备的整个细分市场,从而导致错失市场机会。
在处理散热问题时,请将您必须使用的预算视为系统范围的预算。
通过利用早期分析技术从一开始就优化您的游戏,从而对抗热节流和电池消耗。 为您的目标平台硬件拨入您的项目设置,以解决散热和电池消耗问题。
在移动设备上调整帧预算
将帧空闲时间保留在 35% 左右是在延长播放时间内解决设备散热问题的典型建议。 这使移动芯片有时间冷却下来,并有助于防止过度消耗电池电量。 使用每帧 33.33 毫秒的目标帧时间(对于 30 fps),移动设备的典型帧预算约为每帧 22 毫秒。
计算如下: (1000 ms / 30) * 0.65 = 21.66 ms
要使用相同的计算在移动设备上实现 60 fps,需要 (1000 ms / 60) * 0.65 = 10.83 ms 的目标帧时间。 这在许多移动设备上很难实现,而且电池消耗的速度是目标 30 fps 的两倍。 由于这些原因,大多数手机游戏的目标是 30 fps 而不是 60。使用 Application.targetFrameRate 来控制此设置,有关帧时间的更多详细信息,请参阅设置帧预算部分。
移动芯片上的频率缩放会使分析时识别帧空闲时间预算分配变得很棘手。 您的改进和优化可以产生净积极影响,但移动设备可能会降低频率,因此运行温度更低。 使用 FTrace 或 Perfetto 等自定义工具来监控优化前后的移动芯片频率、空闲时间和缩放。
只要您保持在目标 fps 的总帧时间预算内(30 fps 为 33.33 毫秒)并且看到您的设备工作较少或记录较低的温度以保持此帧速率,那么您就在正确的轨道上。
使用 FTrace 或 Perfetto 等工具监控 CPU 频率和空闲状态,以帮助识别帧预算余量优化的结果。
为移动设备的帧预算增加喘息空间的另一个原因是考虑到现实世界的温度波动。 在炎热的天气里,移动设备会发热并且无法散热,这可能会导致热节流和游戏性能不佳。 预留一定百分比的框架预算将有助于避免此类情况。
减少内存访问操作
DRAM 访问通常是移动设备上的耗电操作。 Arm 针对移动设备上的图形内容的优化建议称,LPDDR4 内存访问成本约为每字节 100 皮焦耳。
通过以下方式减少每帧的内存访问操作数:
- 降低帧速率
- 尽可能降低显示分辨率
- 使用更简单的网格,减少顶点数和属性精度
- 使用纹理压缩和 mipmapping
当您需要专注于利用 Arm 或 Arm Mali 硬件的设备时,Arm Mobile Studio 工具(特别是 Streamline Performance Analyzer)包括一些出色的性能计数器,用于识别内存带宽问题。 针对每一代 Arm GPU(例如 Mali-G78)列出并解释了计数器。 请注意,Mobile Studio GPU 分析需要 Arm Mali。
Arm 的 Streamline 性能分析器包含大量性能计数器信息,可以在目标 Arm 硬件上的实时分析会话期间捕获这些信息。 这对于识别过度绘制导致的内存带宽饱和等性能问题非常有用。
为基准测试建立硬件层
除了使用特定于平台的分析工具之外,还可以为您希望支持的每个平台和质量等级建立等级或最低规格的设备,然后针对这些规范中的每一个进行分析和优化性能。
例如,如果您的目标是移动平台,您可能决定支持具有质量控制的三层,这些质量控制可以根据目标硬件打开或关闭功能。 然后,您针对每一层中的最低设备规格进行优化。 再举一个例子,如果您正在为 PlayStation 4 和 PlayStation 5 开发游戏,请确保您在两者上都有剖面。
如需完整的移动优化指南,请查看优化您的移动游戏性能。 这包含许多提示和技巧,可帮助您减少热节流并延长运行游戏的移动设备的电池寿命。
从高级到低级分析
从上到下的方法在分析时效果很好,从禁用深度分析开始。 使用这种高级方法来收集数据并记录哪些场景会在您的核心游戏循环区域中导致不需要的托管分配或过多的 CPU 时间。
您需要首先收集 GC.Alloc 标记的调用堆栈。 如果您不熟悉此过程,请在本指南后面的“在应用程序生命周期内定位重复的内存分配”部分中找到一些提示和技巧。
如果报告的调用堆栈不够详细,无法追踪分配的来源或其他减速,那么您可以在启用深度分析的情况下执行第二次分析会话,以找到分配的来源。
在收集有关帧时间“违规者”的注释时,请务必注意它们与帧其余部分的比较。 这种相对影响将受到启用深度分析的影响。
在本指南中进一步了解有关深度分析的更多信息
更早的介入分析
当您在项目开发生命周期的早期开始时,可以从分析中获得最大收益。
尽早并经常进行概要分析,以便您和您的团队了解并记住项目的“性能签名”。 如果性能急剧下降,您将能够轻松发现何时出现问题并解决问题。
最准确的分析结果始终来自在目标设备上运行和分析构建,以及利用特定于平台的工具来挖掘每个平台的硬件特性。 这种组合将为您提供跨所有目标设备的应用程序性能的整体视图。
找到瓶颈
在某些平台上,确定您的应用程序是受 CPU 限制还是受 GPU 限制很容易。 例如,当从 Xcode 运行 iOS 游戏时,fps 面板会显示一个条形图,其中包含总 CPU 和 GPU 时间,以便您查看哪个是最高的。 请注意,CPU 时间包括等待 VSync 所花费的时间,该功能始终在移动设备上启用。
Xcode 的 fps 视图,显示游戏在 CPU 和 GPU 上均在 33.3 毫秒内舒适地运行。
什么是垂直同步?
VSync 将应用程序的帧率与显示器的刷新率同步。 这意味着,如果您有一个 60Hz 的显示器并且您的游戏在 16.66 毫秒的帧预算内运行,那么它将被迫以 60 fps 的速度运行,而不是被允许运行得更快。 将 fps 与显示器的刷新率同步可以减轻 GPU 的负担并防止出现屏幕撕裂等视觉伪影。 在 Unity 中,您可以将 VSync Count 配置为质量设置(编辑 > 项目设置 > 质量)中的属性。
但是,在某些平台上,获取 GPU 时序数据可能具有挑战性。 幸运的是,Unity Profiler 显示了足够的信息来识别性能瓶颈的位置。 下面的流程图说明了初始分析过程,其后的部分提供了每个步骤的详细信息。 他们还展示了来自真实 Unity 项目的 Profiler 捕获,以说明要查找的内容类型。
按照此流程图并使用 Profiler 来帮助确定优化工作的重点:
要全面了解所有 CPU 活动,包括等待 GPU 的时间,请使用 Profiler 的 CPU Usage 模块中的 Timeline 视图。 熟悉常用的 Profiler 标记以正确解释捕获。 根据您的目标平台,某些 Profiler 标记的显示可能会有所不同,因此请花时间探索每个目标平台上的游戏捕获,以了解您的项目的“正常”捕获是什么样的。
项目的性能受耗时最长的芯片和/或线程的约束。 这是优化工作应该关注的领域。 例如,想象一个目标帧时间预算为 33.33 毫秒并启用 VSync 的游戏:
- 如果 CPU 帧时间(不包括 VSync)为 25 ms,GPU 时间为 20 ms,没问题! 你受 CPU 限制,但一切都在预算之内,优化并不会提高帧速率(除非你的 CPU 和 GPU 都低于 16.66 毫秒并跃升至 60 fps)。
- 如果 CPU 帧时间是 40 毫秒,GPU 是 20 毫秒,那么您就受 CPU 限制,需要优化 CPU 性能。 优化 GPU 性能无济于事; 实际上,您可能希望将一些 CPU 工作转移到 GPU 上,例如通过使用计算着色器而不是 C# 代码来处理某些事情,以平衡事情。
- 如果 CPU 帧时间为 20 毫秒,GPU 为 40 毫秒,则您受 GPU 限制,需要优化 GPU 工作。
- 如果 CPU 和 GPU 都在 40 毫秒,您会受到两者的约束,需要将两者都优化到 33.33 毫秒以下才能达到 30 fps。
请参阅这些进一步探索受 CPU 或 GPU 限制的资源:
- 帧结构、CPU 和 GPU
- 你的游戏draw call 超预算了吗?
你在帧预算之内吗?
尽早并经常在整个开发过程中对项目进行分析和优化将帮助您确保应用程序的所有 CPU 线程和整体 GPU 帧时间都在帧预算范围内。
下面是一个来自 Unity 手机游戏的分析捕获图像,该游戏由一个正在进行分析和优化的团队开发。 该游戏在高规格手机上以 60 fps 为目标,在中/低规格手机上以 30 fps 为目标,例如本次捕获的手机。
游戏在 30 fps 所需的约 22 毫秒帧预算内舒适地运行而不会过热。 请注意 WaitForTargetfps 填充主线程时间直到 VSync 以及渲染线程和工作线程中的灰色空闲时间。 另请注意,可以通过查看 Gfx.Present 帧的结束时间来观察 VBlank 间隔,并且您可以在时间轴区域或顶部的时间标尺上绘制时间刻度以测量其中之一到 下一个。
请注意,黄色 WaitForTargetfps Profiler 标记占用了所选帧上近一半的时间。 应用程序已设置应用程序。 targetFrameRate 为 30 fps,并且启用了 VSync。 主线程上的实际处理工作在大约 19 毫秒标记处完成,其余时间用于等待 33.33 毫秒的剩余时间过去,然后开始下一帧。 虽然这段时间用 Profiler 标记表示,但主 CPU 线程在这段时间内基本上是空闲的,允许 CPU 冷却并使用最少的电池电量。
要注意的标记在其他平台上可能会有所不同,或者如果 VSync 被禁用。 重要的是检查主线程是否在您的帧预算范围内运行,或者是否在您的帧预算范围内使用某种标记指示应用程序正在等待 VSync 以及其他线程是否有空闲时间。
空闲时间由灰色或黄色 Profiler 标记表示。 上面的屏幕截图显示渲染线程在 Gfx 中处于空闲状态。 WaitForGfxCommandsFromMainThread,表示它在一帧完成向 GPU 发送绘图调用的时间,并在下一帧等待来自 CPU 的更多绘图调用请求。 同样,尽管 Job Worker 0 线程在 Canvas 中花费了一些时间。 GeometryJob,大部分时间都是空闲的。 这些都是应用程序在帧预算范围内的迹象。
如果您的游戏在帧预算内
如果您在帧预算范围内,包括为考虑电池使用和热节流而对预算进行的任何调整,那么您已经完成了性能分析,直到下一次——恭喜。 考虑运行 Memory Profiler 以确保应用程序也在其内存预算范围内。
CPU 密集型
如果你的游戏不在 CPU 帧预算之内,下一步就是调查 CPU 的哪一部分是瓶颈——换句话说,哪个线程最忙。 分析的重点是将瓶颈识别为优化目标; 如果您依靠猜测,您最终可能会优化游戏中不是瓶颈的部分,从而导致整体性能几乎没有改善。 一些“优化”甚至可能会降低游戏的整体性能。
整个 CPU 工作负载很少成为瓶颈。 现代 CPU 具有许多不同的内核,能够独立和同时执行工作。 每个 CPU 内核上可以运行不同的线程。 一个完整的 Unity 应用程序使用一系列线程用于不同的目的,但最常见的用于查找性能问题的线程是:
- 主线程:这是所有游戏逻辑/脚本默认执行工作的地方,大部分时间都花在物理、动画、UI 和渲染等特性和系统上。
- 渲染线程:在渲染过程中,主线程检查场景并执行相机剔除、深度排序和绘制调用批处理,从而生成要渲染的事物列表。 这个列表被传递给渲染线程,它将它从 Unity 的内部平台无关表示转换为在特定平台上指示 GPU 所需的特定图形 API 调用。
- Job 工作线程:开发人员可以利用 C# Job System 安排某些类型的工作在工作线程上运行,从而减少主线程的工作量。 Unity 的一些系统和功能也使用了作业系统,例如物理、动画和渲染。
主线程
下图显示了受主线程受限的项目中的情况。 该项目在 Meta Quest 2 上运行,它通常以 13.88 毫秒 (72 fps) 甚至 8.33 毫秒 (120 fps) 的帧预算为目标,因为高帧率对于避免 VR 设备中的晕动病很重要。 然而,即使这个游戏的目标是 30 fps,很明显这个项目遇到了麻烦。
从主线程过载的项目中截图
尽管渲染线程和工作线程看起来与帧预算内的示例相似,但主线程显然在整个帧期间都在忙于工作。 即使考虑到帧末尾的少量分析器开销,主线程也忙碌了超过 45 毫秒,这意味着该项目实现的帧速率低于 22 fps。 没有标记显示主线程空闲等待 VSync; 整个帧都很忙。
下一阶段的调查是确定帧中花费时间最长的部分,并了解为什么会这样。 在这个框架上,PostLateUpdate。 FinishFrameRendering 需要 16.23 毫秒,超过整个帧预算。 仔细检查发现有五个称为 Inl_RenderCameraStack 的标记实例,表明有五个摄像机处于活动状态并正在渲染场景。 由于 Unity 中的每个摄像头都会调用整个渲染管道,包括剔除、排序和批处理,因此该项目的最高优先级任务是减少活动摄像头的数量,理想情况下减少到一个。
BehaviourUpdate 是包含所有 MonoBehaviour Update() 方法的标记,需要 7.27 毫秒,时间线的洋红色部分指示脚本分配托管堆内存的位置。 切换到 Hierarchy 视图并通过在搜索栏中键入 GC.Alloc 进行过滤显示在此帧中分配此内存大约需要 0.33 ms。 但是,这是对内存分配对 CPU 性能影响的不准确测量。
GC.Alloc 标记实际上并不是通过测量从 Begin 到 End 点的时间来计时的。 为了保持它们的开销很小,它们只被记录为它们的开始时间戳,加上它们的分配大小。 Profiler 为它们分配最少的时间以确保它们可见。 实际分配可能需要更长的时间,特别是如果需要从系统请求新的内存范围。 为了更清楚地看到影响,请在执行分配的代码周围放置 Profiler 标记,在深度分析中,时间轴视图中洋红色 GC.Alloc 样本之间的间隙提供了一些指示它们可能花费了多长时间。
此外,分配新内存可能会对性能产生负面影响,难以直接衡量和归因于它们:
- 从系统请求新内存可能会影响移动设备的功率预算,这可能会导致系统降低 CPU 或 GPU 的速度。
- 新内存可能需要加载到 CPU 的 L1 Cache 中,从而推出现有的 Cache 行。
- 增量或同步垃圾收集可以直接触发,也可以延迟触发,因为最终超出了托管内存中的现有可用空间。
在帧开始时,四个 Physics.FixedUpdate 实例加起来为 4.57 毫秒。 稍后,LateBehaviourUpdate(对 MonoBehaviour.LateUpdate() 的调用)需要 4 毫秒,而Animators大约需要 1 毫秒。
为确保该项目达到其所需的帧预算和速率,需要调查所有这些主线程问题以找到合适的优化。 通过优化耗时最长的事情,将获得最大的性能提升。
在受主线程约束的项目中,以下领域通常是寻找优化的有效地方:
- 物理
- MonoBehaviour 脚本Update
- 垃圾分配和/或收集
- 相机剔除和渲染
- 糟糕的drawcall调用批处理
- UI 更新、布局和重建
- 动画
根据您要调查的问题,其他工具也可能会有所帮助:
-
对于花费很长时间但没有向您确切说明为什么会这样的 MonoBehaviour 脚本,请将 Profiler Markers 添加到代码中或尝试深度分析以查看完整的调用堆栈。
-
对于分配托管内存的脚本,启用分配调用堆栈以准确查看分配的来源。 或者,启用 Deep Profiling 或使用 Project Auditor,它显示按内存过滤的代码问题,因此您可以识别导致托管分配的所有代码行。
-
使用 Frame Debugger 调查绘制调用批处理不良的原因
渲染线程
这是一个渲染线程受限的项目。 这是一款具有等距视角和目标帧预算为 33.33 毫秒的主机游戏。
渲染线程受限场景
分析器捕获显示,在当前帧开始渲染之前,主线程等待渲染线程,如 Gfx 所示。 WaitForPresentOnGfxThread 标记。 渲染线程仍在从前一帧提交绘制调用命令,并没有准备好接受来自主线程的新绘制调用; 渲染线程在 Camera.Render 中花费时间。
您可以区分与当前帧相关的标记和来自其他帧的标记,因为后者看起来更暗。 您还可以看到,一旦主线程能够继续并开始发出绘制调用以供渲染线程处理,渲染线程会花费超过 100 毫秒来处理当前帧,这也会在下一帧期间造成瓶颈。
进一步的调查表明,这款游戏有一个复杂的渲染设置,涉及九个不同的相机和许多由替换着色器引起的额外通道。 游戏还使用前向渲染路径渲染了 130 多个点光源,可以为每个光源添加多个额外的透明绘制调用。 总的来说,这些问题加起来每帧产生了超过 3000 个绘制调用。
以下是调查渲染线程受限项目的常见原因:
-
绘制调用批处理不佳,尤其是在 OpenGL 或 DirectX 11 等较旧的图形 API 上
-
摄像头太多。 除非您正在制作分屏多人游戏,否则您很可能只应该拥有一个活动的相机。
-
剔除不佳,导致绘制的东西太多。 调查您的相机的视锥体尺寸和剔除图层蒙版。 考虑启用遮挡剔除。 甚至可以根据您对对象在您的世界中的布局方式的了解来创建您自己的简单遮挡剔除系统。 看看场景中有多少阴影投射对象——阴影剔除发生在“常规”剔除的单独通道中。
Rendering Profiler 模块显示每帧绘制调用批次和 SetPass 调用数量的概览。 调查渲染线程向 GPU 发出的绘图调用批次的最佳工具是帧调试器。
工作线程
由除主线程或渲染线程之外的 CPU 线程受限的项目并不常见。 但是,如果您的项目使用面向数据的技术堆栈 (DOTS),则可能会出现这种情况,尤其是当使用 C# Job system 将工作从主线程转移到工作线程时。
这是编辑器中播放模式的截图,显示了在 CPU 上运行粒子流体模拟的 DOTS 项目。
乍一看,这似乎是成功的。 工作线程与 Burst 编译的作业紧密结合,表明大量工作已从主线程移出。 通常,这是一个合理的决定。
但是,在这种情况下,主线程上 48.14 毫秒的帧时间和 35.57 毫秒的灰色 WaitForJobGroupID 标记表明一切都不好。 WaitForJobGroupID 表示主线程已安排作业在工作线程上异步运行,但在工作线程完成运行之前它需要这些作业的结果。 WaitForJobGroupID 下方的蓝色 Profiler 标记显示主线程在等待时正在运行作业,以确保作业更快完成。
尽管这些作业是 Burst 编译的,但它们仍在做很多工作。 也许该项目用于快速查找哪些粒子彼此接近的空间查询结构应该优化或交换为更有效的结构。 或者,空间查询作业可以安排在帧结束而不是开始,直到下一帧开始才需要结果。 也许这个项目试图模拟太多的粒子。 需要对作业代码进行进一步分析才能找到解决方案,因此添加更细粒度的 Profiler 标记可以帮助识别它们最慢的部分。
您项目中的作业可能不像本示例中那样并行化。 也许您只是在单个工作线程中运行了一项长期工作。 这很好,只要计划的作业和需要完成的时间之间的时间足够长以使作业运行。 如果不是,您将看到主线程在等待作业完成时停止,如上面的屏幕截图所示。
同步点和工作线程瓶颈的常见原因包括:
- Job 未被 Burst 编译器编译
- 在单个工作线程上长时间运行的作业,而不是跨多个工作线程并行化
- 帧中安排作业的时间点和需要结果的时间点之间的时间不足
- 帧中的多个“同步点”,要求所有作业立即完成
您可以使用 CPU Usage Profiler 模块的 Timeline 视图中的 Flow Events 功能来调查作业何时调度以及主线程何时预期其结果。 有关编写高效 DOTS 代码的更多信息,请参阅 DOTS 最佳实践指南。
受GPU限制
如果主线程在诸如 Gfx.WaitForPresentOnGfxThread 之类的 Profiler 标记中花费大量时间,并且您的渲染线程同时显示诸如 Gfx.PresentFrame 或 .WaitForLastPresent 之类的标记,则您的应用程序是 GPU 受限的。
以下截图是在三星 Galaxy S7 上使用 Vulkan 图形 API 拍摄的。 尽管在此示例中花费在 Gfx.PresentFrame 中的一些时间可能与等待 VSync 有关,但此 Profiler 标记的极端长度表明大部分时间用于等待 GPU 完成前一帧的渲染。
在这款游戏中,某些游戏事件触发了着色器的使用,该着色器使 GPU 渲染的绘制调用数量增加了三倍。 分析 GPU 性能时要调查的常见问题包括:
-
昂贵的全屏后处理效果,包括常见的罪魁祸首,如 Ambient Occlusion 和 Bloom
-
昂贵的 fragment 着色器由以下原因引起:
- 分支逻辑
- 使用全浮点精度而不是半精度
- 过度使用影响 GPU 波前占用的寄存器
-
由于低效的 UI、粒子系统或后处理效果导致透明渲染队列中的过度绘制
-
屏幕分辨率过高,例如 4K 显示器或移动设备上的 Retina 显示器
-
由密集网格几何或缺少 LOD 引起的微三角形,这是移动 GPU 上的一个特殊问题,但也会影响 PC 和主机 GPU
-
未压缩的纹理或没有 mipmap 的高分辨率纹理导致缓存未命中和浪费 GPU 内存带宽
-
几何或Tesselation 着色器,如果启用动态阴影,每帧可能会运行多次
如果您的应用程序似乎受 GPU 限制,您可以使用帧调试器作为快速了解发送到 GPU 的绘图调用批处理的方法。 但是,这个工具无法呈现任何特定的 GPU 时序信息,只能呈现整体场景的构建方式。
调查 GPU 瓶颈原因的最佳方法是检查来自合适的 GPU 分析器的 GPU profiler。 您使用哪种工具取决于目标硬件和选择的图形 API。 有关详细信息,请参阅本指南的分析和调试工具部分。