大多数浏览器和
Developer App 均支持流媒体播放。
-
Metal Game Performance Optimization
Realize the full potential of your Metal-based games by tackling common issues that cause frame rate slowdowns, stutters, and stalls. Discover how to clear up jitter and maintain a silky-smooth frame rate with simple changes in frame pacing. Get introduced to new tools for analyzing rendering passes and pinpoint expensive or unexpected work. Learn how to avoid thread stalls and get specific advice about handling thermal notifications.
资源
- Analyzing resource dependencies
- Debugging the shaders within a draw command or compute dispatch
- Optimizing GPU performance
- 演示幻灯片 (PDF)
相关视频
WWDC23
WWDC18
-
下载
(Metal游戏性能优化) (演讲612)
早上好 欢迎来到本次演讲 我叫Guillem Vinals Gangolells 在Apple的 GPU软件性能团队工作
你们这样的游戏开发者 使iOS成为一个优秀的游戏平台 在Apple 我们当然想助一臂之力 所以今年 我们检查了一些顶级的iOS游戏 并发现了一些常见的性能问题 我们分析了很多数据 作为调查的结果 我们决定做这个谈话
所以这将是今天的主题 开发出色的游戏
但是 我只能在这里提供技术指导 所以我们只能满足于此 (开发技术出色的游戏) 在开始之前 我想借机感谢Croteam的朋友 他们是《塔洛斯法则》背后的开发者 这是一款非常棒的游戏 你将在这些幻灯片 和两个演示中看到它
请注意 它具有令人惊叹的视觉效果 但它确实在性能上没变差 这就是我们今天的目标 让我们快速过一下今天的议题
我将首先介绍这些工具 这是一个非常好的起步 然后我们将讨论实际的性能问题 有关帧速 线程优先级 热状态 和不必要的GPU工作
尽管所有这些问题似乎互不相关 它们会互相复合和影响 所以解决所有这些问题很重要
让我们从工具开始 (分析工具) (及早分析及经常分析) 这是最重要的建议 你应该尽早建档分析并经常更新
除非你已经对游戏进行了分析 否则不要推出游戏 为此 你需要了解这些工具 今天 我将关注其中两个
首先 我们看Instruments 这是我们的主要分析工具 你会想用它来理解性能 延迟和总体时间 其次 我们有Metal框架调试器 这也是一个非常强大的工具 你希望用它来调试GPU工作负载
我们从哪里开始呢? 这是我们经常遇到的问题 嗯 今年我们让你们更轻松 我们正在推出一种新的仪器模板 这将是一个很好的起点 游戏性能模板 这是现有工具的组合 例如系统跟踪 时间分析仪 和Metal系统跟踪 我们为你配置好了 它会记录与你的游戏相关的 所有CPU和GPU数据 所以你可以用它调试
那么我们如何推出呢? 我们怎么到那里? 嗯… 只需打开Instruments 它就在屏幕中心
选择后 你将像设任何其它模板一样设置
开始记录后 你将在窗口模式下执行此操作 允许你任意地玩游戏 只有最后几秒的数据会被记录下来 这就是最后几秒数据的样子
有很多信息 所以我们简单概述一下
(系统跟踪和时间分析仪) 首先 我们有系统跟踪和时间分析仪 它将为你提供系统负载的概述 以及你的app CPU使用情况 例如 用户交互式加载 将记录给定时间内的所有活动线程 在这种情况下 你看到的橙色 表示可运行的线程数量 大于CPU核心数 所以有一些意外情况 这将提供系统的绝佳视图
有几个很棒的谈话 更深入地探讨这个工具 请进一步关注这些
下一个是Metal系统跟踪 我们的GPU分析工具 它提供了图形堆栈的绝佳视图 从Metal框架 一直到到显示
特别注意 我们要密切关注GPU硬件时间 分为顶点 片段 及计算 如果你的游戏用到
还要注意显示监视器 将是我们许多调查的起点 我们将识别长框架或启动器 我们将从那里开始一直调查下去 所以这是一个非常自然的起点
关于这个工具有很多信息 因为它确实是一个非常强大的工具 我鼓励大家去熟悉它
这几个演讲 将为你提供一个很好的起点
好 接下来要谈的是 我们今年推出了一个线程状态视图 此视图显示游戏中每个线程的状态 在这种情况下 每种颜色代表一种可能的线程状态 例如被抢占以橙色表示 已屏蔽以灰色表示
我们专门为你 游戏开发者 设计了此视图 因为我们知道 现代游戏中的线程系统非常复杂 我们希望这对你有所帮助
我们还为每个CPU核心 提供了一个轨道 它将显示在该核心上运行的线程 以及以颜色编码的该线程的优先级
用这个工具 你将能够一目了然地看到系统的状态
还有… 这是一个简短但相当宽泛的工具介绍 现在我们该转向实际的性能问题了 第一个是关于帧速度
让我们先看一下它 为此 我们用了Fox II SceneKit演示的修改版本 这将有助于我们更好地说明问题
你能猜出哪个游戏生成得更快吗?
嗯… 你们中的一些人可能没有猜到 左边的游戏试图以 每秒60帧的速度生成 但它只达到40 所以它不流畅而有抖动 另一方面 右边的游戏 它的目标是每秒30帧 可以一贯地实现 这就是为何它看起来更顺畅
但这有点违反直觉 为什么生成速度更快的游戏 反倒看起来不太顺畅? 嗯 这个问题被称为微型口吃 或不一致帧速
它出现于帧时间 高于显示刷新间隔 例如 我们的游戏可能要25毫秒生成一帧 或每秒40帧 而显示屏可能会每16.6毫秒刷新 或每秒60帧 与我们刚看到的视频相同 这会产生一些视觉上的不一致
这种情况是怎么造成的? 我们在这种情况下做了什么? 嗯… 我们没有做什么 这就是问题所在
生成帧后 我们从显示链请求下一个可绘图 一旦我们得到了可绘图 我们完成了最后一步并立即展示 我们明确地告诉了系统 在下一个刷新间隔 尽快呈现可绘图 毕竟 我们的目标是每秒60帧 对吗?
还有另一类问题会导致微型口吃 有些游戏已经设定较低的帧率 但我们也发现了很多这样的游戏 在主线程或 图像生成线程上用usleep (勿用usleep() 控制帧速!) 这在iOS中是一种很糟糕的做法 所以请不要那样做 耐心等我几分钟 我会告诉你在iOS中的正确方法
现在… 让我们深入了解系统中发生了什么 微型口吃是可见的
在这种情况下 我们在这里看到 生成中涉及的所有组件的时间表 我们开始正常生成我们的游戏 注意 这是一个三重缓冲区 在iOS中常见 在这种情况下 每个可绘图都用字母和颜色表示 注意这里的前提 生成可绘图B 历时超过一个显示刷新间隔 即Vsyncs之间的时间
在这种情况下 生成到B可能需要25毫秒 显示刷新间隔之间为16.6毫秒
所以 既然这是前提 这意味着我们需要在显示屏上保持A 为下一个间隔给出时间以便完成
我们会这样做 在那个时间段内 B实际上会完成 并准备好呈现
但注意我们刚在这里隐藏了一个问题 在此间隔期间 我们还完成了C的生成 我们已准备好立即呈现它
所以从这个时间段开始 我们会遇到不一致的帧速 我们陷入了这种模式 每隔一帧都不一致 用户将看到微型口吃
现在… 现实世界中 这可能在以不同的形状和形式出现 所以我们现在做的就是快速演示 我将向你展示《塔洛斯法则》 的仪器跟踪 我们会用它来观察 看能否识别现实世界中的微型口吃情况
好 我们在这里看到的 与我之前展示的信息相同 我们的游戏性能模板 已在默认状态下将此信息捕获 注意我谈到的所有相同的工具 显示在左侧
所有游戏线程都在中间 尤其我们现在正在寻找微型口吃 所以这很直观地会让我们看显示轨道 因为微型口吃的定义 即是不一致的帧显示 在这种情况下 我们在这里有显示轨道
注意 该显示轨道中有一些提示 我们添加了它们 这些是提示 这里 它们会告诉你 当表面显示 比正常生成时预期更长 也许这是一个开始观察的好地方 有一些堆砌 让我们放大一个 要放大 我们将长按选项键 并将指针拖动到感兴趣的区域
这种情况下 如果我们继续查看显示轨道 已经很明显存在微型口吃 我们可以看到 每个显示器都有不同的时间 所以在这种情况下 例如 我们有50、33 16 回到50 再回到33 所以当我们在仪器捕获中 看到这种模式时 这意味着存在微型口吃 我们应该纠正它 我们看看做法 回到幻灯片
好了… 我们刚刚看到了这个问题 及它在现实世界中的发生 模式基本相同 那我们该怎么做呢?
最佳做实践 是确定游戏可以达到的帧速率 所以在最小帧持续时间 长于帧生成时间
为此 有很多API可以帮助你 例如 MTLDrawable addPresentedHandler 一旦可绘图出现 会给你一个回调 这样一旦微型口吃发生 你就可以识别
其它两个API会帮你解决这个问题 它们将允许你明确控制帧速 在这种情况下 我们提供了afterMinimumDuration 和atTime 我们这里的目的 是把帧的最小持续时间 设置得比生成时间长
这正是我们要做的 让我们看看
注意 当我们开始生成时 我们从一开始就保持一致 我们的帧显示时间长于生成时间 每一帧都是一致的
用户的观感也会一致 太好了 另外 注意有副作用 帧速率将降低 我们从每秒40帧降到30帧 所以这也给了我们额外的帧时间
我们是怎么做到的?
我们是如何修复帧速的? 嗯… 真的 只需几行代码 我们的模式与以前相同 我们生成场景 得到了下一个可绘图 我们做最后一步生成 唯一的区别 是我们指定帧的最小持续时间 并以最短持续时间呈现 就是这样 这将允许我们设置帧的最小持续时间 它们将保持一致
这样做之后 你可能会想 那么 最长持续时间呢? 我们的工作优先级概念如何体现? 或一帧可以花多长时间? 嗯 这是我们要谈的下一个问题 线程优先级
像之前一样 让我们先看一下它 还是用Fox II演示的修改版本
你可能在想 而你会是对的 有很多事情可能导致这类的口吃 也许你正在作资源加载或着色器编译 今天我们将重点关注更为根本 但也是非常常见的类型的口吃
即由线程停滞引起的口吃
如果工作优先级未很好地传达给系统 你的游戏可能有意想不到的停滞
除了生成游戏之外 iOS还做很多事
线程优先级用于保证 整个系统的服务质量
所以如果一个线程做了很多工作 其优先级将随时间的推移而降低 以让其它线程有机会运行 这就是优先级衰减的概念
另外 在我身后的幻灯片上可看到 优先级倒置 这是另一类 以非常类似的方式表现出来的问题 当生成线程依赖于来自同一引擎的 较低优先级的工作线程以完成工作时 在这种情况下 发生优先级倒置 我们看看它是怎么样的 与我们之前看到的时间表相同
在这种情况下 我们开始以每秒30帧的速度生成 很轻松 但是 有一些背景工作 iOS做很多东西 也许现在正在检查电子邮件
若我们的线程配置不正确 那问题就来了
你可能会被该后台工作抢占 你可能无法完成GPU上 所有工作安排
并且没有帧的最大持续时间的概念 所以帧生成可能会持续数百毫秒 用户会看到口吃
这就是它背后的理论 在实践中 它的发生遵循相同的模式 但以不同的方式显示 我们再作一个演示 我将向你展示另一个仪器捕捉的 《塔洛斯法则》信息 来看看如何识别此问题
在这种情况下 你看到的是 使用游戏性能模板作的信息捕捉 但这一次 我们已经放大了 进入我们感兴趣的帧 这是一个很长的帧
它的持续时间为233毫秒 所以我们应该调查这个很典型的口吃
一眼看去 我们已经知道GPU似乎未做什么 在此期间它处于闲置状态 这意味着我们没有喂饱它 现在 我们可以看一下CPU CPU似乎相当忙 就像它们真的…… 所有这些看起来都很稳固
注意 你在此处看到的是我们的 app的时间分析器视图 看上去没有在运行
为什么我们的游戏没有运行 这怎么会导致口吃?为什么? 嗯… 我们可以切换到我提到的新视图 新的线程状态视图 要做到这个 你进入app的图标 并点击此处的按钮 这将拉出轨道显示
在这种情况下 你可以切换到线程状态
这有望帮助你看到这里有问题 它以橙色突出显示 这已经告诉我们 该线程已被抢占192毫秒 这就是问题所在 生成线程未运行 有些东西先占用了
如果你想了解更多 你可以在底部展开信息 也将包含线程叙述
并且通过单击被抢先的线程 你会看到这里 对正在发生的事情的解释 在这里 你的生成线程优先级为26 非常低 低于后台线程优先级 因为App Store正更新
这是不应该的 我们想告诉系统 对我们的用户来说 在那个特定的时刻 我们的游戏 比App Store更新更重要 让我们回到幻灯片 看看该怎么做
最好的做法是配置你的生成线程 建议将生成线程优先级固定为45
注意 在iOS和macOS中 优先级具有升序值 因此优先级31高于优先级4 此外 我们需要选择退出 调度程序的服务质量 以防止优先级衰减 从而降低我们的优先级
我们看看配置良好的 生成线程是怎样的
现在我们按照刚才所说的来配置 我们开始正常生成
我们还有一些背景工作在运行 否则这不公平 而且 这个后台工作 可能是更新App Store 正如我们在演示中看到的 但请注意一个又一个Vsync后 正常生成 我们正在抢占CPU的 后台工作以运行
用户看不到口吃 你的游戏可以每秒30个固帧运行 即使系统负载很重 这在技术上很棒 这就是我们要做的 让我们看看如何通过 一点代码实现这一点
确实是一点代码 只有几行 在这种情况下 它只是配置pthread属性 在创建pthread之前
我们需要选择退出服务质量 将优先级设置为45
就是这样 现在我们 可以用这些属性创建pthread 它会正常工作 这很简单同时技术上很了不起
不是那么简单的
是我们要讲的下一个问题
处理多个热状态的问题
目的是明确的
为持续性能设计
并处理偶尔出现的热问题 我们看看我们如何来做
iOS设备可提供前所未有的电量 但是在一个非常小的外形中 因此 随着更多的app 在设备上使用更多资源 系统可能会开始采取措施 以保持低温和反应速度
此外 用户可能已启用低功耗模式 会产生相似的效果
好 所以最好的做法 是将工作负载调整为系统状态
你应该监控系统并相应调整工作负载 iOS有许多API可以有帮助 例如 用NSProcessInfo thermalState 来查询或注册通知 设备热状态变化 检查低功耗模式的方式类似 另外 请考虑查询 来自MTLCommandBuffer的 GPU启动/GPU结束时间 以了解系统负载如何影响GPU时间
我们看看如何用一个简单的 代码示例来做到这个
这直接来自我们的最佳实践 它的核心是一个 非常简单的switch语句 每个case对应一个热状态 这里有名义上的
一般
重要 和关键
这一切都很好 现在我们知道我们处于热状态 并且这些指令告诉我们要做些什么 我们怎样才能真正帮助 系统保持低温?
嗯…
我可以给一些建议 但这取决于游戏开发者 来决定要做出什么样的妥协 以帮助系统 你知道什么行动最适合你的游戏 以在压力下保持出色
我的一些建议是针对帧速率 以在整个游戏会话保持 例如 如果你不能以每秒60帧 维持10分钟或更长 那么保持每秒30帧
调整GPU工作也非常有用 例如 考虑降低 中间生成目标的分辨率 或者简化阴影图 加载更简单的资产 甚至完全删除一些后期处理 什么办法最适合你的游戏 你就应该采取什么
而且这把我们带到我们的下一个议题
关于处理不必要的GPU工作 为此 请欢迎我的同事Ohad上台 他会都告诉你 (不必要的GPU工作) 谢谢 Guillem
大家好 我叫Ohad Apple的游戏技术团队成员 在之前的幻灯片中 Guillem展示了 适应系统的重要性
响应低功耗模式或不同的热状态等 将要求你调整GPU工作负载 以保持整个游戏阶段 一致的帧速 然而 对于许多开发人员来说 GPU有点像黑盒子 藏在游戏引擎的窗帘后面 今天 我们来拉开这个窗帘
浪费GPU时间是一个很常见的问题 也经常被忽视 但我希望你记住这一点 技术上出色的游戏 不仅会达到其GPU预算
它们也是系统的好公民 帮助它保持低温并节能 所有流行的游戏引擎 都提供了一系列最佳实践 我们不会介绍这些 相反 我们将专注于 如何判断生成的内容是否昂贵 正如我们今天已经对CPU多次做的 这最佳实践是对GPU进行分析
我们GPU的强大功能可以隐藏许多 内容或算法上的效率 你需要对工作量计时 但也要了解 你启用的每种生成技术 并只保留那些 明显增加游戏视觉质量的技术
但是你怎么发现效率问题? 你如何确定过程的 哪些部分完全不必要? 这当然让我们回到了工具
与往常一样 你的第一站 应该是Instruments 我们在这里看看Metal系统跟踪 它将为你提供精确的时间: 顶点、片段及计算工作
但是测量GPU时间 只是一半的工作 接下来 你想要真正了解 每一步正在做什么
为此我们今年为Metal系统跟踪 添加了一个新工具 这是依赖关系图
依赖关系图是一个单帧的故事
它由节点和边组成 每一个都讲述了故事的不同部分
边表示步与步之间的依赖关系 当你从上到下跟踪它们时 你将看到每一步 在哪里可嵌入你的生成管道 以及它们如何协同工作来创建框架
另一方面 节点是单个步的故事
它们由三个主要组成部分组成 首先 title元素将给出步名称 我想确实强调这一点 命名标记一切 这不仅有助于 Dependency查看器 也有助于我们的整个工具套件
其次 它可以让你快速了解 正在查看的步类型 生成、位块传输还是计算 这里 从图标中 我们可以看到它是一个生成步
接下来 这里有一个统计列表 描述了此步中正在完成的工作
最后 在底部 在此步中写入的 所有资源的列表 并且每个资源都带标签 允许预览的缩略图 以及具体描述每个资源的信息列表
所有这些一起 可让你真正了解每一步 好的 现在我们知道如何读图表了 让我们跳转到一个演示 看看它们是如何组合在一起的
我在我的机器上运行了 Fox II演示 它是在SceneKit中构建的 允许我添加各种很棒的效果
如你所见 我有瀑落式阴影图
高光 景深 所有这些合在一起 创建一个精美生成的场景 我们用依赖关系查看器 来查看它是如何工作的 首先 我们将转到Xcode 然后我们将捕获一个帧 用底部的 Capture GPU框架按钮 我们选择左边的主要步
并切换到自动模式 我们右边出现助手 现在 注意我在调试导航器中 选择的同一步 也是被选的 并且在主视图中心 这是一条双向的街 当我们与图表进行交互时 选择不同的步或纹理甚至缓冲区 左侧的导航器和右侧的助手 都会更新以显示你的选择 这是帧导航的一种非常棒的方式
现在当我缩小时 你会注意到的第一件事 是统计隐藏了 焦点消失了 视图从单独步转到了整个帧 我可以缩小更多 可以看到我整个帧的鸟瞰图
现在注意真正酷的是 由于依赖关系驱动图的互联
每个逻辑工作在空间中组合在一起 让我们放大 看看我是什么意思 这里我有一个工作分支 正在创建我的阴影图
在左边 我可以看到三步
生成阴影 这太好了 因为我不只是了解整个帧的状态 还有这两层之间的联系 每个生成技术是怎么构建起来的 而当你使用游戏引擎打开它们时 这并非总是显而易见的 例如 用我的阴影图 我可能不知道 每个级联都需要自己的步 如果我单独考虑这些中的每一个 这不会很明显 但现在我看到 我必须将它们视为一个群体
我在调整GPU工作负载作妥协时 这个认识帮我做出明智决策
这就是依赖关系查看器 我切换回幻灯片 欢迎Guillem回到台上 谈他最后几点想法 谢谢
谢谢 这是一个出色的演示 谢谢
酷 Ohad刚刚向我们展示过 通过依赖关系查看器查看图帧的方式
而且 你可以检查GPU工作负载 例如 我们通常可能 从一个非常小而简单的过程 像这个
演变到一个非常复杂的过程 经过后期处理 HDR中的多个阴影图
所有这些的实现方法可以是 在你的游戏引擎的相机对象中 添加几个属性
你会发现这些更改的代码复杂性很小 但生成复杂性可能增加了十倍
这就把我们带回到开头 就在我们开篇的地方
分析资料 了解游戏的功能非常重要
你花了数万小时开发游戏 你应该考虑花一些时间进行分析
我们今天看到的一切 可以在几分钟内找到
最好的部分是什么? 你无需知道自己在寻找什么 仅仅记录口吃 查到长帧 并从那里开始一直继续 就这么简单 这个工具将为你提供所需的所有信息 来识别问题 但你需要使用该工具
这就是关键 我们看到了一堆常见的陷阱 和一些最佳的解决办法
所有这些问题 可以通过分析找到 我们就是这样找到问题 我们分析了大量游戏 发现了常见问题 并决定把它们放在一起
如果你有权访问引擎源代码 确保帧速度和线程优先级都配置好 这只是几行代码 但无论如何 你的游戏应该始终适应热量需求 并且不要提交不必要的GPU工作 确保遵循所有这些最佳实践 你也将开发技术上令人敬畏的游戏 这就是我们这个谈话的宗旨
更多信息 中午12点会设一个实验室 我们会在那儿 我会在那儿 如果你在这次会议之后有任何问题 我们非常乐意回答 或者也许你只是想坐下来 让我们分析你的游戏 此次WWDC还有两场精彩的演讲 关于游戏开发者的Metal 和我们的分析工具
非常感谢 祝你们今天愉快 度过愉快的一天
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。