大多数浏览器和
Developer App 均支持流媒体播放。
-
Metal 助力专业 App
Metal 是针对平台优化的图形与计算框架,是 Apple 各平台 GPU 加速的核心所在。了解 Metal 架构中支持现代高性能专业 app 和工作流程所用的各项技术的关键部分。熟悉如何利用各项 Metal 功能来优化性能,并在视频编辑流程中保持稳定的帧率。了解如何利用 CPU 和 GPU 并行计算,并深入了解实现高效数据吞吐的最佳做法。
资源
相关视频
WWDC21
WWDC19
-
下载
欢迎来到 Pro App Metal 会议 我叫 Eugene 与 Dileep 和 Brian 一起 我们将来讨论一下如何利用 Metal 启动新的工作流 并充分发挥 Mac 和 iPad 上 Pro App 的所有功能 但是什么是 Pro App 呢 在 Apple 我们将它定义为 从事创造性工作的专业人士 用来进行内容创作的 App 包括动画设计师 电视直播 摄影 3D 动画 平面媒体和音频制作 另外 它还包括 由你们开发人员创建的 第一方 App 和 第三方 App 例如 Autodesk Maya 它是用于 3D 动画绑定 和视觉效果 或用于音频和音乐制作的 Logic Pro 还有 Serif Labs Affinity Photo 用作专业图像编辑
和 Blackmagic 的 DaVinci Resolve 用作专业视频剪辑
Pro App 一直是 我们生态系统中重要的部分 最近我们发布了 全新的 Mac Pro 全新的 Pro Display XDR 显示器可以强力支持 外部GPU和 全新的搭载 A12X 仿生处理器的 iPad pro 我们的确加倍了我们 在这个领域的关注和承诺 为什么 Pro App 如此与众不同呢
原因之一是 它处理的是大型数据 最高可以处理到 8K 视频 几十亿个多边形模块 数千张照片和几百个音轨 它们也需要许多 CPU 和高性能的 GPU 最后 在实现实时交互 并同时还要保持 原始内容的完全准确的时候 总是会出现挑战 那么我们来看一下 今天的日程
首先 我们将要介绍 我们平台上的视频编辑的流程 讨论一下怎样 优化它 使它能够处理 8K 内容 接下来 我们将要讨论你们如何 在你的 App 中添加 HDR 支持 接下来 我们要向你们展示 跨所有 CPU 端口和 GPU 通道的扩展 最后 我们要讨论如何 实现最有效的数据传输
首先 我们聊一下 视频编辑流程 和 8K 内容
在 Apple 我们将视频编辑 视作要求最高的 和最具创造性的工作之一 所以我们将视频 App 作为一个用例 来展示 Metal 是如何帮助你们的 在此之前 我想要 向来自 Blackmagic Design 的朋友们 表达无比的感激之情 我们与他们一起 紧密地合作来优化 DaVinci Resolve 和我们的平台 释放新的 8K 工作流 我们真的非常自豪 可以一起完成这件事 但是我们来看一下当我们第一次尝试 8K 原始内容的时候是什么样的情况 你们看到内容不是实时的 卡顿的情况非常严重 这个体验不太好
所以这样不行 我们看看专业人士 怎么解决这个问题 他们有大量的 8K 原始素材 首先 他们转码得到 所有的基底 然后他们二次取样 并将他们缩放到 4K 的代理 所以他们可以 实时编辑和修改效果 但是有一个很重要的点 你们不能给等级代理数据调色 原因很简单 因为它不准确 所以你现在必须返回到 你的原始内容 在它上面应用所有的编辑 进行离线渲染工作 这要花费数小时 你需要和导演反复检查 并不断还原和重复 在 Apple 我们想要 为用户节省时间 我们非常希望 专业人士可以直接 处理现成的 8K 内容 那么让我来告诉你 我们是如何实现 实时 8K 视频剪辑的
我们首先要构建一个 高效的视频编辑管道流 我们将会包含所有一般设计的 高效的管道流 应该使用什么样的框架 怎样最大化所有硬件的功能
接下来 我们将讨论如何 管理大型素材 最后 我们会告诉你们一些 当你们尝试保持 可预测的帧频时 可能会遇到的困难 并且告诉你们解决的办法
我们来探讨一下视频编辑的管道流
这些是一般的组成部分 是大部分视频编辑 App 需要具备的 首先我们读取内容 然后我们需要对其解码 然后才能处理它们 最后 呈现或者编码 今天的会议上 我的重点在于解码处理 编码和呈现 我们将讨论 输入和输出块 它们一般使用 AVFoundation 框架 我希望你们看一下 我们在 AVAssetReader 和 AVAssetWriter 上的样例
我们来讨论一下 如何使解码过程与 Metal 联系更紧密
Apple 提供了一个灵活的 低级框架叫做 Video Toolbox 用来实现高效 和高性能的视频处理 它可以在 iOS macOS 和 tvOS 上使用 支持多种格式 并且可以利用我们设备 上的所有硬件
用来解码的构建块 是一个解压缩会话 我可以快速地 给你展示一下怎样创建 首先 我们来指定 我们想使用硬件视频解码
然后为每一个视频流 创建一个会话 我们在这里通过完成处理程序来创建
我们接着往下走 我们调用带有一个解码标志的 的解码帧 这对于代码非租塞 是非常重要的 Apple 帧解码完成 我们的回调将会被调用 最后请不要忘记 在完成后将它清除 所以现在你们知道了如何解压我们的帧 现在我们来聊一下如何确保 我们能够使用最优化的方法 来完成它
你的 Mac 可能已经安装了一些 用来解码的硬件 为了确保我们使用的是 同样的零拷贝的物理内存 我们将会利用一个 叫做 IOSurface 的内部对象
IOSurface 是一个 硬件加速图像缓存 带有 GPU 常驻跟踪 它还会给你提供 进程间和框架间端口 给同样的 GPU 内存 所以这种情况下它表现极佳
Core Video 和 Metal 提供了 一个便捷的方法 可以发挥 IOSurface 的优势 通过使用一个 叫做 CVMetalTextureCache 的对象
让我们来看一下怎样创建它
在这里来创建一个 我们的 CVMetalTextureCache 我们想要确保我们使用 Metal 设备 我们准备用它来进行像素处理 接下来当我们得到一个新的 CVPixelBuffer 我们需要将它变成 Metal 纹理的 如果我们的像素缓存 是由一个 IOSurface 支持的 则无需任何成本就会自动生成 最后不要忘记清除 你的纹理和像素缓存 确保可以再次使用 在纹理缓存上 现在我们有 MTL 纹理的帧 而且我们准备 做一些像素处理 这里我们有几个选项
当然 我们可以编写自己的 处理和分级操作系统内核 这很容易 因为 Metal 的着色语言是用 C++ 写的 同时 我们十分鼓励 你们使用 Metal 高性能着色器 来做像素处理 你甚至还可以创建自己的神经网络 作为你们管道流的一部分
现在我们来看一下如何使用 MPS 运行一个典型的模糊滤镜
首先我们来看一下 Metal 队列 和一般缓冲
我们创建一个高斯模糊操作系统内核 它是 MPS 大量内核中的一个 现在我们可以尝试 进行编码 我们还提供一个回调分配器 以防失败 最后我们提交我们的一般缓存 MPS 是一个非常强大的框架 适用于我们平台上的所有设备 我们鼓励你们多阅读 然后开始使用它 现在我们完成了像素处理 那么我们准备 编码 MTL 纹理来输出格式 当然我们会在这里再次使用 Video Toolbox 让我给你们 展示如何进行最高效的操作 你的新 Mac Pro 可能会有 几个 GPU 每一个可能拥有 不止一个编码引擎
当然 我们可以让 Video Toolbox 成为最可靠的设备 但是在很多情况下 你想要明确指定一个设备 来最小化拷贝成本 我们来看一下 我们是如何使用 CVPixelBufferPool 实现内存再利用的
这是我们如何得到 所有可利用的编码器
首先我们使用 EnableHardware 标志来确保 我们利用硬件编码 下面 我们告诉 Video Toolbox 我们想使用哪个设备 这是关键的一步 可以确保我们编码的设备和 我们进行像素处理的设备是同一个
还有一个非常棒的是 我们可以使用 CVPixelBufferPool 得到与硬件编码器格式一致的操作台
这就是我们如何从缓存池中得到缓存 接下来我们再次使用 MetalTextureCache 现在我们可以转换我们的 Interleaf 过程数据成为 字节格式 这就是 Video Toolbox 所需要的 和之前一样 我们清除所有东西 使缓存可以再次使用 现在我们可以构建 最有效的管道流 它真的很棒 我们将单独讨论呈现部分 现在我们来简单聊一下 8K
8K 意味着非常庞大的素材 我们来看一下如何处理它们
首先 我们来计算一下 一个 8K 帧就很庞大 它是高清帧的 16 倍 未压缩时总共是 270 兆字节 我们每一帧要传输近 300 兆字节 所以每秒 30 帧 我们就需要每秒 9 千兆字节的带宽 这已经十分接近 PCI 总线的极限了 另外 甚至经过 4 乘 4 的加速 一个 10 分钟的 片段就可以达到 1 太字节 这是一个巨大的挑战 对于实时播放来说 我们来看一下它是什么样的 当我们使用 Xcode 中的工具跟踪 这是一个视频回放会话的系统跟踪 我们之前展示过
如果我们放大虚拟内存跟踪 我们可以看到 非常多折叠的页面
并且对应的补零 成本非常高 我们可以在下面看到
它就解释了 为什么所有的解码线程 都会被卡住并且无法处理
我们需要更加细心的管理缓存 现代操作系统 不会配置物理内存页 直到它们被使用 所以一旦我们开始 从许多的解码 FATS 访问所有这些新的内存页 我们必须要 等待系统为我们提供 所有这些内存页的信息
解决的办法非常简单 在使用前预热你的 CPU 缓存 确保所有的内存页 在播放之前是常驻的 现在我给你们一些 管理内存的技巧 每一个配置都有一个系统调用 一个简单的方法就是 尽早配置 避免工作流中间来配置 另一个好的建议就是 一直重复利用你的缓存 这就是为什么 使用像 CVPixelBufferPool 和 CVMetalTextureCache 的对象很重要
现在我们已经提升了 管理帧的方法 但是伴随着 App 的生命周期 总是会有非常非常多 更小的配置需要做 让我来教你们一些管理它们的小技巧 有了 MTLheap 我们可以 高效管理我们所有的过渡配置 来自 MTLheap 的配置 不需要一个内核调用 因为整个堆 都在创建时间被配置了 从系统的角度上看 堆是一个庞大的资源 所以它是整体常驻的
此外 堆的配置 可以允许更紧密的内存打包 使用堆 你可以 在一般缓存中给资源内存命名 这是常规缓存无法是实现的 我来展示一下怎么做
在这个例子中 我们将 重新命名过渡配置 首先 我们使用我们的设备创建一个堆 根据过滤的步骤 我们为堆的模糊内核创建一致性
接下来 我们配置另外一个缓存 给我们同一个堆的颜色等级一致
我们不再需要它们 我们将它们标志为可命名的 并且堆可以 在以后的配置中重复利用这些内存 所以当我们配置一个新的 中间缓冲区时 堆可以 自由的重复使用所有被标记 为可命名的内存 非常高效
这是一个快速的介绍 关于怎样管理我们的资源配置 现在我们来讨论一下 如何给我们的朋友呈现 可预测的帧频 这是一个定时的例子 用来三倍缓冲 Metal 的 App 在这个例子中 我们在 60 赫兹的显示器上播放了一个 30 赫兹的视频 注意帧在 节奏不一致的显示器上是怎样显示的 用户会发现卡顿的情况
为什么会这样 注意 CPU 是如何 在 GPU 上工作的 在没有考虑节奏的情况下
一旦第三个帧 进入 CPU 就卡住了 等到一个可拉的 以便可访问 这不会发生 直到第一帧离开屏幕
所以这个方法最大化了 CPU 的效用 但是却产生了可见的卡顿情况
通常我们希望每一帧 能够在屏幕上停留 同样的时间 为了解决它 Core Video 提供了一个叫做 CVDisplayLink 的界面 00:15:03.646 --> 00:15:05.326 A:middle 这是一个高精度
的低级定时器 它能够给你发送通知 在移位刷新率的 VBLANK 出现之前
不是让 CPU 排入越多的帧越好 我们使用显示器连接 来决定正确的时间 向GPU提交每一帧 这在很大程度上减少了播放的不稳定性 让我来展示一下 代码片段是如何做到的 首先我们创建一个链接 与我们将要使用的显示器连接 接下来 我们创建一个回调处理程序 并且对于每一个调用 我们可以得到当前时间 和下一次 VBLANK 的时间 我们可以使用这些值 确定何时发出一个当前调用 假设我们已经处理了这些帧 我们可以 按照需要调整我们的期望频率 所以这将会在很大程度上 减少卡顿现象 但是我们还可以 再进一步 例如 在 60 赫兹的显示器上 播放 24 赫兹的内容 我们需要三二拆分 这就是说我们要 每三个 VBLANKS 播放偶数个帧 超过两个 VBLANKS 播放奇数个帧 这是我们想到的最佳办法 可以解决错配问题 我刚刚给你们展示的代码片段 可以很好的解决它 但是好消息是我们还可以做得更好 我们全新的 Pro Display XDR 支持 多种刷新频率 包括 48 赫兹 所以你们 24 赫兹的内容 可以播放得相当流畅 没有卡顿
所以我们做了相当多 重要的优化
现在我要给你们看一下最后结果 这是 DaVinci Resolve 现在正在播放同样的 8K 流媒体 你可以看到 播放很流畅 没有任何卡顿 这是一个我们 和 Blackmagic 的完美合作 我们一起取得了出色的成绩 我们鼓励你 开始使用我们的 高性能框架 开始为我们的平台创建 新的和激动人心的 Pro App 我们已经处理了 8K 中的挑战 现在我们邀请 Dileep 来谈一谈我们对于 HDR 的支持 和你们如何在你们的 App 上播放它 Dileep
谢谢 Eugene 近年来 高动态 范围图像和显示器 极大地提高了 我们在屏幕上看到的图像的质量 在 Apple 我们一直在 添加支持来提高 图片技术 如你们所知 我们添加了许多技术 例如 视网膜显示器 4K 和 5K 分辨率 和宽色域颜色空间等 并且已经可以使你们这些开发者 创建出令人惊艳的 高质量的图像 继续刚才的部分 今年我们为 macOS 上的 HDR 添加了巨大的支持 我非常高兴 给你们简单讲一下我们对于 HDR 图像和显示器的支持 为此 我们将来讨论四个话题 第一 我将简单地介绍一下 HDR 图像的一些共同特性 接下来我将介绍我们处理 HDR 的方法 和处理 HDR 的渲染模型 接下来我将详细地谈一谈 如何用 Metal 处理 HDR 图像 最后 会推荐一些最佳操作
首先 来介绍一下 HDR 图像的共同特性 是什么让它们变得与众不同
与标准动态范围图像 或者 SDR 图像相比 HDR 图像有更高的对比度 它们将细节呈现得很好 不论是黑暗区域还是明亮区域的细节
在 SDR 图片上 这些细节是没法辨认的 它们要不就是变形的 要不就是被洗掉了
HDR 图像有更多的色彩 有更广阔的色域 它们看起来非常真实
同时 它们也非常明亮 亮度信息 被编码进了图像本身 拥有如此强的明亮度和对比度 它们添加了 场景中光照效果滤镜 看起来更贴近真实生活 最后 它们需要在 可使用的显示器上观看 可以保留图像 所有细节的显示器上观看 显示器本身 具有底层技术 能够产生高亮度 高颜色逼真度 和出色的对比度
我们全新的 Pro Display XDR 是一个 很好的例子 现在 我们如何渲染 和展现这些 HDR 内容 在我们的设备上呢
在 Apple 我们有一个 独特的方法来解决 我们叫做 EDR 也就是扩展动态范围
这种方法依靠 我们使用的显示器上 亮度的上方空间来展示 HDR 图片的强光部分和阴影部分 我来详细解释一下这个概念 正如你们所知 设置在显示器上的 亮度是独立的 它基于周围条件或环境条件
例如 如果你在一个 灯光昏暗的房间 你的显示器上的亮度设置就是 200 尼特 或者更低 亮度设置对于观看 SDR 内容 是理想的选择 例如 UI 或者 Safari 浏览器或者是 YouTube 视频
但是如果你的显示器可以 产生高达上千尼特的亮度 那么你的显示器就有一个 巨大的亮度的上方空间 同样的 如果你在灯光明亮的条件下 例如办公空间 显示器的亮度设置 会更高 例如 500 尼特 再一次 亮度设置 对于观看 SDR 内容是理想的 但是你的显示器上也会有 亮度上层空间剩下 在这种情况下 在处理 HDR 时 我们利用它 当我们渲染像素时 我们将 SDR 像素映射到 显示器上的亮度设置 并且利用上层空间来映射 HDR 像素 我们在多大程度上 为这两类建立映射取决于 查看条件 就像你 在这两个例子里可以清楚看到的那样
然而 渲染这个模型时 像素需要被 有序地组织起来 并且我们通过缩放 与显示器 SDR 亮度相关的 HDR 像素来实现它
我们使用之前的 灯光昏暗的房间的例子 来看一看 像素值是如何按照 显示器的亮度排列的
在这里 我们用 X 轴代表亮度 用 Y 轴代表标准化像素值 在这个例子里 显示器的亮度被设置为 200 尼特 在我们的渲染模型中 我们经常将像素值 标准化像素值设定在 0 到 14 SDR 内容的范围内 并且我们将它映射在显示器的亮度设置上
所以这里的一个像素值 代表 200 尼特 但是你可以看到 显示器上有一个巨大的上层空间 所以你拥有 一到五的像素值 来呈现 HDR 内容 一个和 SDR 范围有关的 5x 的标度 可以充分利用显示器的亮度
我们把这个标度叫做 EDR 最大值 如果环境条件改变 或者是你在一个灯光明亮的房间里 亮度上升到 500 尼特 这时 EDR 最大值 会下降 但是注意 像素值在 0 到 1 之间 仍然代表 SDR 内容 但是这时它们的映射不同了 这时一个像素值 代表 500 尼特而不是 刚才例子中的 200 尼特
并且由于你现在有用的 上层空间变小了 在这个情况下 所以你可以呈现 HDR 内容 的像素调用范围变小了 所以只有 2x 在这个例子中 与 SDR 范围相关 用来呈现 HDR 像素
就像你看到的 我们用这样一种方法安排像素 HDR 像素与 SDR 范围有关 并且它们根据 SDR 范围缩放 我们还用负像素 来呈现 HDR 图像较暗的面 总之 EDR 或 外部动态范围是一个 与渲染模型有关的显示器 在它里面 我们使用显示器 可利用的上层空间 呈现 HDR 像素 并且我们缩放显示器上与 SDR 亮度 有关的 HDR 像素
使用这个模型 你可以在 非常多的可以设置亮度的显示器上 做 HDR 渲染 但是如果你们有更多可使用的显示器 例如全新的 Pro Display XDR 它有巨大的上层空间 这样内容 看起来会更好 既然我们已经看了 我们处理 HDR 的渲染模型 我们再来看一看 有哪些 API 和你们如何 在你们的 App 上使用它们
在 macOS 和 iOS 上 你有一些选择 一 你可以使用 AVFoundation API 这些 API 对于媒体播放 App 来说是理想的选择 它们可以为你们处理 所有的色调映射和色彩管理 二 你可以直接使用 Metal Metal 提供了多种选择 具有很强的灵活性
使用 CAMetalLayer 和 EDR API 你可以完全控制 你的 App 上的 HDR 渲染 包括色调映射 和色彩管理
这些 API 对于内容创作来说是理想的工具
现在我们使用代码片段 看一看 MetalLayer 和 EDR API 是怎样在你的 App 中 融为一体的 好吗 所以你需要在你的编码基数中做的 最重要的事是 检查显示器上的 EDR 支持 并且这个屏幕是否
接下来通常是 创建并设置你自己的 MetalLayer
当你设置 MetalLayer 时 你需要选择一个宽色域颜色空间 来配合你的 App
Metal 支持渲染 宽色域颜色空间 例如 BT2020 或 B3
接下来 你需要选择 与你的内容匹配的转换函数
再次说明 API 支持 所有行业标准的 转换函数 例如 PQ HLG 或 Gamma
接下来你需要为你的 HDR 渲染 选择像素格式 我们推荐你们使用 float 16 作为首选的像素格式 它可以满足大多数的 HDR 渲染需求 因为 float 16 足够精确可以 展现 HDR 内容的 色彩和亮度信息 最后 在层设置 你需要提示 Metal 你选择加入 EDR 渲染模型 这就是层设置的一般步骤 你们需要这样做 现在 进入到主渲染循环 在主渲染循环中 你需要得到我们刚刚谈到的 亮度上层空间 或是最大 EDR 信息 是吗 所以 NSScreen 为你们提供了 这些性能 但是要注意 这个性能是动态的 它在不停地改变 随着你的环境条件 或是显示器的亮度改变 所以你需要注册一个通知 针对这个性能的每一点改变 随着 EDR 最大值改变 你需要修改你的内容
现在如果你正在 在你的 App 中处理 色调映射和色彩管理 你需要在你的着色器上做另外的一些 像素处理 我们来了解一下 你要做的基本步骤 一般视频进入 YUV 色彩格式 所以在你的 App 或着色器上 第一步你需要做的是 将它转换成 RGB 格式
另外 视频内容 编码了某种 非线性传递函数 例如 PQ 所以在 shader 中下一步 是应用逆传递函数 并且线性化你的像素 接下来将它标准化到 0 到 1 的范围内
现在使用 你从 NSScreen 得到的 EDR 最大值 并缩放像素 按照我们在 EDR 部分讨论的那样 现在进行编辑 分级 或者任何 你的 App 需要的操作 最后来做色调映射
如果亮度改变 你需要用不同的参数集 来做色调映射 如果你的内容在一个色彩空间里 这与播放着的不同 那么你也 使用适当的色彩转换
然而 如果你不想 色调映射或者远程处理 而且你还需要 Metal 来为你处理 你可以很容易地完成它
当你想要创建一个 Metal Layer 时 在它上面添加一个 EDR 元数据对象
通常 EDR 元数据对象 可以提供模板显示信息 上的信息并且 它还能告诉 Metal 你想要 在像素间建立怎样的映射 你也需要使用一个 Metal 中的线性颜色空间 所以一旦你的 像素处理全部完成 你的框架就做好了 使用已经存在的 Metal API 在屏幕上呈现你的框架 正如我刚刚提到的 你需要一个 合适的显示器 例如 Pro Display XDR 来观看这些内容
我们也支持 HDR 10 和 市场上的杜比视界 TV
这是一个简单的介绍 关于你如何使用 Metal layer 和 EDR API 在你的 App 里来做 HDR 渲染 所以在这部分结束之前 我们来回顾一下主要的收获 和一些建议的最佳操作
记住要修改和更新你的内容 当亮度发生改变的时候
使用 Float 16 作为首选的像素格式 来满足你大多数的 HDR 渲染需求
当你创建 Metal Layer 时 选择的颜色空间和转换函数 要适应你的内容 然后任何 额外的处理都可以 根据需要在着色器上完成 最后做 HDR 处理 增加计算的上层空间 并增加内存压力 所以不需要色调映射如果 你的内容色调已经有映射了 或者如果对你来说效果比 颜色准确性更重要
所以 总之 我们已经提供 给你们高性能的 API 一个用作 EDR 渲染模型的显示器 iOS 和 macOS 都支持它 并且支持我们的 Pro Display XDR 显示器
你们可以使用这些工具 创造令人惊艳的 HDR 内容
在下面这个部分 我们将要 讨论你们要如何 使用现有的 我们平台上的计算资源 来达到最完美的表现 并且告诉你们 所有的相关信息 我邀请 Brian Ross 上台
谢谢 Dileep
扩展你的性能 使用 CPU 和 GPU 相似性是最重要的 并且它有时也是 你可以最容易做到的优化 在这个部分 我们将要讲 几个方法来扩展你的性能 在你的硬件的基础上 首先 我们来讨论一下 Metal 是怎样帮助你跨越 任意数量的 CPU 内核
接下来 我将谈一下 利用和使用异步 GPU 通道 最后 我将要谈一下 使用多个 GPU 的一些方法 所以今天的主要内容是添加 越来越多的复杂性
它们需要更多的 CPU 周期来 解码框架 来构建渲染图形 并且编码 Metal 渲染路径
使用今天的设备上的一个 CPU 线程 完成这些是不足够的
最新的 iPhone 是六核的 并且一个 Mac Pro 最高可以有 28 核
因此可扩展的多线程体系结构 对于我们所有设备上的 出色表现是至关重要的 Metal 是为多线程设计的 在这个部分 我们来看两种方法 使你在 CPU 上并行编码 所以我将会 举一个典型的视频框架的例子 使用经典的单线程渲染 你可以串行解码框架 并且在一个命令缓存里 建立命令 来执行 GPU 的命令 你通常还需要 使它适应你们帧像周期 的一小部分 当然 你需要 有最大延迟 因为你 必须要解码整个命令缓存 在 GPU 使用它之前就完成它 显然 还有更好的办法做到这件事 所以我们首先将要做的是 为 CPU 创建相似性 渲染传输和计算路径 是 Metal 上 多线程的基本粒度 你需要做的只是创建 多个命令缓存并且 在它们上开始处理编码路径 在一条单独的线程上 你可以编码任何你想要的命令 执行的最后一个命令是 由你添加在命令列队中的 最后一个命令决定的 所以现在我们来看一下 怎样在你的代码中完成它
编码多个命令缓存 实际上非常简单 第一件我们需要做的事是 从列队中创建任意个 Metal 命令缓存对象
接下来我们使用 队列等待接口 预先执行命令 这样很好 因为你可以 很早完成它 并不需要 等待编码后再完成它 最后 对于每一个命令缓存来说 我们创建一个单独的线程 使用异步调度队列 和编码传递 在框架中 就是这样 它非常迅速也非常简单
正如你们在例子中看到的 我们的并行化做的很好 通过使用多个 Metal 命令缓存对象 但是如果你们只有一个 大型效果和混合过程时应该怎么办 在这种情况下 Metal 提供了一个 专用并行编码器 可以让你在多个线程上编码 不需要明确的 划分渲染路径 或命令缓存 我们来看一下 在你的 App 上做这件事是多么的容易
在这里 第一件事我们需要做的 是创建一个并行编码器 在命令缓存上
接下来我们创建任意数量的 下级编码器 就是在这里 我们定义 GPU 执行命令 通过我们创建的下级命令
接下来 我们创建一个单独的线程 并且为每一个线程 调用我们的编码功能 在这里效果和 混合将会被处理
最后我们创建一个通知 在线程已经完成 并且我们完成 并行编码的时候 就像这样 这就是我们要做的所有事情
所以我已经给你们展示了两个方法 解决 CPU 的并行问题 现在我们看 Metal 怎样帮助 你们利用异步GPU通道
现代的 GPU 有一个常见的性能 它们都有许多的通道 可以帮助你们 异步执行 所以这意味着 你也许可以 在一个通道解码视频 同时在另一个通道执行 3D 效果
Metal 可以通过两种方法 选取这种形式的并行化 第一种方法就是正常的 使用渲染传送和 计算编码器 完成合适的工作 另一种提取 Metal 并行性的方法是 通过分析你的数据依赖 但是这里面最重要的细节 是这全部都是免费的 Metal 在幕后可以为你做很多事情
我们看看是怎样做的 我们来看看 GPU 是怎样 提取一系列的框架 这里我们有另外一个视频框架 在你的 App 里 我们将要 用 Video Toolbox 解码 接下来是用 传送解码器来上传帧 从系统到 VRAM 接下来我们将要应用筛选 通过渲染计算和效果和混合
这个工作可以在 每一个帧上重复 现在我们来看这是如何在 CPU 和 GPU CPU上同时 在一个时间线图中执行的 我们还要看一下我们是否 能做一些优化提升并发性 在一个时间线图中 首先我们要做的是 编码第一帧命令 使用独立线程上的不同编码器 这个工作将会被安排 并最终通过 所有的通道执行 然后我们继续在 第二 三 四帧上完成它 多亏有了 Metal 你可以看到 我们已经有了 一些免费的并行化
但是我们也可以看到存在许多的缺口 这就意味着一些线程 和通道是没用的 而我们的帧像周期是宝贵的 为了保持效率 我们 想要尽可能地使通道饱和
Metal 不可能一直为我们满足所有的要求 所以在这种情况下 我们要做一些优化 你将注意到 CPU 上 存在巨大的缺口 这有许多的原因 但是对于简洁性来说 我要注意 一个共同的错误 有时 App 会 无原因地一直调用或者等待 直至完成 在编码和提交命令缓冲区之后
在这种情况下 它产生了 巨大的缺口和泡沫 对于我们解码工作的时长来说 它也产生了一些小的缺口 在 GPU 的解码通道中 为了修复它 我们可以尝试 用一个 completionHandler 代替它 这将会避免 CPU 的堵塞 并且将所有的后处理放在 GPU 完成工作之后 我们试一下看看它怎么样
你可以看到它好多了 现在我们使 CPU 线程保持工作状态 你还会注意到 解码通道被很好地充分使用 我们也仍然会看到许多缺口 在传送 计算 和 选择通道过程中
仔细来看你可以看到 一个重写依赖项 在这里上载 要等到解码完成才能开始
修复这个问题的一个方法是 提前解码 10 个帧 这将消除依赖性 我们来试一下看会发生什么
所以现在我们更新的资源看起来很不错 我们已经使视频和 传送通道饱和 但是 你会注意到 仍然存在一些缺口 在计算和渲染通道 和上次一样 你可以看到 还存在另外一个依赖 筛选无法开始直到 传送通道上载 所有数据之后
和刚才一样 我们可以修复它 通过预载入位图 它可以消除依赖
有了它 我们现在已经消除了 大部分的缺口并且 我们拥有非常高效的流程 所以现在我们已经了解了 GPU 通道和一些优化的例子 我们来看一下如何 继续扩展性能在 更多的 GPU 上
GPU 很快变成了一个 性能倍增器 支持更多的 GPU 可以 加速图形处理 视频编辑和提高你的整体帧频 在这个部分 首先我会 认真探讨一下 Metal 是如何 使用多个 GPU 配置的 然后我会给你们展示一些 高效的负载平衡策略 它的高效现在已被 Pro App 的开发者证明 最后 我将会谈一下 在你的 GPU 间实现同步操作
所以 Metal 可以为你的 Pro App 做些什么呢 首先 Metal 给你提供所有 你需要的工具来发现 相互关联的 GPU 和它们的作用 使用 Metal 可以很容易地管理 多个 GPU 因为 它们本质上只是独立的 Metal 设备对象 所以使用和今天同样的编程 在单独的 GPU 上
Metal 也支持崭新的 对等组传输 API 这包含远程纹理视图的概念 它可以在 GPU 间拷贝数据 最后 Metal 提供 强大的共享事件 让你能够在 GPU 间 同步你的工作 现在我们迅速看一下 如何发现多个 GPU 配置
Metal 具备的设备性能 可以为每个设备找到位置 位置编号和最大 的传输速率 这些信息可以用作 确定最快的 托管设备和传输设备 它还可以用作决定 一个设备是否是集成的 严谨的或是外部的或者甚至是 低性能的或是无头的 所以一旦我们可以使用它 检测多个 GPU 我们就能够考虑如何 平衡你的负载
有许多许多方法可以 平衡 GPU 的负载 并且有许多不同的设计 当你们在选择策略的时候 你们需要考虑一下 今天的最后我们 我们要寻找的是一个非常简捷的 负载平衡策略 它还具有更高的等级和效率 我们来仔细看一下几个 已被证明有效的策略 一些 Pro App 开发者现在正在使用它们 第一个也是 最直接的方式是支持交替帧 它的意思是在两个 GPU 之间 平衡奇偶帧 它能够很容易地适应 现有框架 还有可能 使你处理帧 的速度加倍 然而有时 App 有不同的负载 例如 UI 更新或图形构建 这可能会使负载不平衡 所以我们看另外一个策略 这个使用小的 32 乘 32 的像素图块 在 GPU 间平均分配 所以如果你有四个 GPU 你可以为每个 GPU 预先选择图块组 在这里我们进行随机分配 为了避免与场景产生过多关联 这意味着图块与 GPU 的映射 是在帧与帧之间固定的 这是一个非常好的解决方法 可以计算大型的负载 但是可能不是最好的选择 对于带宽密集型的 GPU 来说 我们仔细地来看一下 了解一下速率跟踪部分 所以这有一个相似的方法 它也要使用图块队列 在这种情况下 宿主 App 可以添加 所有的图块到队列中 然后它们可以根据需要使用每一个 GPU 从队列中除移 基于一个可以使 GPU 持续工作的算法程序 这个方法可以提供非常好的 负载平衡 但是它 增添了更多的复杂性
所以一旦我们决定了我们的 负载平衡机制 我们需要 考虑怎样在不同的 GPU 之间 同步我们的工作负载 为了完成它 Metal 提供了一些强有力的设备 叫做共享事件 它们可以指定 你们 App 中的同步点 在同步点中你可以等待和 特定的命令补全
这可以跨 GPU 完成 也可以在 CPU 和 GPU 间完成 或者还可以跨进程完成 我们来实际应用一下
这里是一个例子 一个单独的 GPU 负载 对成对的帧进行动作分析 注意这是所有的渲染负载 所以我们不能 使用我们刚刚提到的 并行异步通道 但是如果我们有 两个 GPU 我们就可以将 这个工作分配给它们 来提高性能 所以这个策略是为了将 动作分析转移到第二个GPU 记住每一个动作传送 必须要分析前面一对帧 这就是说我们必须要等到 之前的帧被编写后 才能从动作传中送读取它们 并且这个动作 重复出现在每一对帧上 所以我们可以完成它通过 使用 Metal 的共享事件 首先 我们要提示完成 当帧已经完成渲染时 然后我们要等待提示 才能通过动作传送读取它们 有了它 我们可以安全地 将动作分析转给 第二个 GPU 和所有并行运行的项目 由于 Signal 和 Wait 在 GPU 上编码 你不会妨碍任何的 CPU 执行命令
所有现在我们看一看 我们怎样在代码中实现它 首先我们需要做的是 在渲染帧的设备上 创建一个共享事件 我们还要创建两个命令队列 每个设备一个
现在去渲染帧 我们创建一个命令缓存并且编码 然后立刻编码 一个提示事件来提醒 其他的 GPU 我们已经完成
最后 编码动作 首先我们要 从队列里创建一个命令缓存 然后立刻编码一个等待事件 避免在我们完成前读取帧
然后我们编码动作分析 并提交 就像你们看到的 它是一个相当 直接的过程 并且它也非常强大 在我继续之前 我们看一下 我们工具里的 多个 GPU 和通道是怎样的 所以 Metal System Trace 展示了 每一个你的测试 GPU 上的工作 在这个例子中我们有四个 GPU 一个内部的 三个外部的 GPU 每一个 GPU 都具有 细节可见性 通过展示 跨所有通道的异步工作负载 并且它用相关的符号名 映射了这些工作负载 在活动总结中 你可以看到所有的 执行数据 它们是关于所有的 相关联的 GPU
你甚至可以更进一步 看到更多细节通道信息 关于任意 GPU 的每一帧的信息 这包括上一页和 下一页的信息 在传送 通道或计算和渲染工作负载中 你甚至可以看到 IOSurface 访问 正如 Eugene 提到的 这是 非常有用的 因为许多 Pro App 需要 使用 IOSurfaces IOSurfaces 对于框架互操作性非常重要 最后 我们的工具 提供 Metal 事件和共享事件的可见性 所以你可以看到 提示事件和等待事件在哪里发生 它还展示了使用了多少毫秒 等待和绘制 你需要遵循的依赖线
最后在最底部 甚至还有一个详细的列表 记录你们在活动总结中的事件 所以 Metal system trace 是一个非常棒的 工具 可以满足你的 Pro App 的所有需要 所以现在我们明白了怎样 使用多个 GPU 我们来看一下怎样在它们之间传输数据
在 CPU 和 GPU 间 扩展性能是极其重要的 但是在今天的最后 你的 Pro App 只是 和它最慢的地方一样快 当你传输大量的 8K 帧时 你的数据传输 可以很快地变成瓶颈 在这部分 我们将要 讨论一下带宽的注意事项 和它们与全新的 Mac Pro 的关系
然后我会概述一些 Metal 对等组转移策略 其中一些策略 现在已经被 Pro App 开发者所使用 最后我会给你们讲一个 使用的例子来展示 Infinity Fabric Link 是怎样 帮助解决具有挑战性的工作流的 首先我们看一下 我们关键的连接点的传输速率 在这我们的基点是 PCI Express gen 3 有 16 个链接
所以首先我们有 Thunderbolt 3 实际使用中它的最大输出 大约是 PCI Express 速率的四分之一 它是一个非常棒的可扩展的方法 来计算大规模的工作流 但是它可能不是最佳的选择 对于带宽密集型的来说 接下来我们有两个 GPU 每个都有它们自己的 PCI 通道 这可以使你的带宽增加一倍
这周我们有了 全新的 Infinity Fabric Link 和 对等组传输 API 它可以在 GPU 间传输数据 以五倍于 PCI Express 的速度传输 所以现在我们来看一下常见的 Mac Pro 配置 这个图表展示了许多 带宽密集型的配置 在这我们有一个新的 Apple Afterburner 它有自己的 专门 PCI 通道 我们还有两个内部 GPU 有它们专属的 PCI 通道 最后你可以链接 通过 Infinity Fabric Link 这些 GPU 实现它们之间快速复制数据
Mac Pro 也可以使你 拥有最多四个内部 GPU 它们分享两套 CPI 通道 因为通道是分享的 它将自己借给一个 可能有更多计算的 Pro App 在这种情况下 你也可以 与 Infinity Fabric Link 链接 所以有许多许多的方法 管理这些传输策略 我要来谈一些 已验证的策略 一些 Pro App 开发者现在正在使用它们
最直接的方法是 传输整个帧来播放 前提是处理替换帧 任何在辅助设备上 处理后的帧都可以 快速传输到 显示器附带的 GPU 上 然后发送到显示器上 另一个传输策略是 将图块发送给每一个 GPU 它们是整个帧重要的部分 所有在辅助设备上处理的 图块可以被发送到 显示器 GPU 然后重建为 最终输出图像的一部分
这个结果可以实现很好的负载平衡 所以我们已经快速浏览了两个策略 现在我们看一个很好的例子 关于 Pro App 实际利用 Infinity Fabric Link 所以在过去的六个月 我有机会与 Final Cut Pro 团队合作 他们做出了一些出色的成绩 来优化全新的 Mac Pro 他们可以扩展到 28 个 CPU 内核 和所有的内部 GPU 他们也充分利用了 Infinity Fabric Link 这使他们能够 实时编辑多个 8K ProRes 视频流
现在我们来详细了解一下 它是如何工作的
这有一个简单的 时间线图向你们展示了 视频流从 CPU 移动到显示器的过程 在这里我们重点看 三个带有一些效果的 8k ProRes 原始视频流的回放
这就是它们在一个单独的 GPU 上的样子
首先 我们在 CPU 上 编码第一帧命令
然后上传所有三个视频流 通过 PCI 传到 VRAM
最后我们处理这些视频流 使用 GPU 和显示器上的效果处理 我们继续处理 每一个附加帧
我们可以按照自己的想法使它 适合我们 30 帧每秒的速度 它不能完成 有太多的工作 被塞进这个空间 一个 Pro App 将会丢掉至少 30% 的帧 在这种情况下 现在我们来看一下 在双 GPU 的系统上完成的怎么样
首次 我们编码第一帧 和上面的例子一样
对第二帧也同样这么做 现在这看起来很棒 我们在这使它并行了 但是我们也使帧频也变为两倍了吗 不幸的是 我们没有做到 问题出在任何 我们已经在辅助设备上 处理过的帧必须要 被写回显示器附带的设备上时 这就是事情的难点 为了得到显示器 GPU 我们必须要复制一个 265 兆字节的 输出缓冲区通过 PCI 在宿主 App 上 然后我们从这再一次复制 到显示器附带的 GPU 上
这最多需要 48 毫秒 现在 我们接着说 第三帧和第四帧 给你们一个 更好的画面
并且你们可以很快发现 我们使用相当多的 PCI 带宽 但是我们还是 看到许多缺口和依赖 我们仍然比 单个 GPU 做得好 但是它并不能使帧速率加倍 所有的这个 PCI 流量会导致 丢失一些帧 但是为了改进这个地方 Mac Pro 有了新的 Infinity Fabric Link 特性 它拥有了对等组 API 它完全取代了 GPU 拷贝
你可以在这里看到是 Infinity Fabric Link 传输变得快了很多 它也释放了相当数量的 PCI 带宽 并且使用这些带宽我们可以更早的上传帧
Infinity Fabric Link 也在自己的并行 GPU 通道上运行 这就是说我们可以传输数据 并且同时使用渲染 和计算通道
这帮助我们提高准确性 并隐藏潜在因素 最后 它可以帮助你的 Pro App 处理 具有挑战性的工作负荷 并且支持新的情况 所以这就是它在一个无聊 的时间线图上的样子 但是现在我们来看一下动态的样子 这是 Final Cut Pro 的样片 在刚刚的演示中 你可以看到 它播放了很多 8K 视频流 同时带有效果和转换 这也是实时完成的 利用多个 GPU 和 Infinity Fabric Link
Final Cut Pro 是 利用高效数据传输的 Pro App 的典范 在结束这一小节之前 让我们来看看 如何在代码中使用 Infinity Fabric Link 和对等授权 传输组 API 你需要做的第一件事 是检测这些连接 为方便该操作 Metal 定义了一个全新的对等组 API 在 Metal 设备上定义了 对等组 ID 索引和计数的属性
通过这种方式 你就可以检测到 是否已经连接GPU 是否有共享 PCI 通道 或双 PCI 通道 更重要的是 你可以用它来确定 最佳配置 并根据带宽限制 扩展性能
接下来就是如何在 GPU 之间传输数据 首先 你需要 利用辅助设备 创建共享事件 我们还创建了渲染纹理 和远程纹理视图 远程视图会给你的 显示器附加一个 GPU 通道 访问辅助纹理
接下来 我们要创建一个编码器和渲染
紧接着要马上
现在 显示器上 创建了一个 blit 命令缓冲区
所以 我们先等渲染完成 再进行数据传输 最后 我们用纹理视图 进行复制 就是这样 非常简单
使用 Metal 的对等组 API 来优化 Infinity Fabric Link 可以通过降低 PCI 带宽 极大的扩大工作量 还可以通过并行通道 来提高并行性 在结束之前 让我们再看一个 非常棒的 Pro App
我要非常开心地分享一个 使用 Serif Labs 制作的 Affinity Photo 的样片 Serif 的工程师利用 Metal
他们特别 特别出众地 向我们展示了 如何正规利用平台资源 处理一个传统的文档 使用单核 CPU 的处理速度 比使用八核 CPU
我们来看一下
在这个例子里 Affinity 将会合成一个 大型的文档 名字叫 rabbit trick 如视频中演示的一样 它有几百个巨大的图层 将以 4K 分辨率合成 有些图层具有像素数据
这就需要实时的分层合成 同时也会给 CPU 和 GPU
现在就让我们在 CPU 上 运行这个庞大的文档 这使用的是 18 核系统 你可以看到 我们能做到 实时合成 但是 这个文档非常复杂 所以在用户界面上 有点不稳定
现在我们切换一下 在单个 GPU 上运行 这里 你可以看到 UI 的响应非常好 所有运行都很流畅 帧速率大约为 每秒 18 帧到 20 帧
这是最好的部分 我们可以运行 4 个外部 GPU 你可以看到 它的运行依然很流畅 它还可以一直保持 每秒超过 60 帧的 运行速度 这都归功于 Affinity 先进的基于磁帖的 负载均衡方案 它们可以给任意数量的 GPU 有效地分配工作 并通过这种方式
这个是我想分享的 最后一个例子
这是 Affinity Photo 的 最大内存带宽密集型处理器之一 被称为景深
我们在 CPU 上实时预览 你可以直观地看到 应用效果和实际渲染之间
这同样让人印象深刻 它速度很快 但 GPU 可以 让它速度更快
现在 我们将用 4 个外部 GPU 运行相同的效果 你可以看到 运行超级流畅 很轻松地就可以保持 每秒 60 帧以上的帧速率 这个特殊的过滤器就是 内存带宽密集型 我们获得了很大的改进 因为现在的 GPU 上 可以使用大容量 内存带宽 这也是非常突出的 一个示例 告诉我们 如何在多个 GPU 上正规的 扩展性能 所以 在结束此次会议之前
Apple 提供了丰富的框架 来解决你所有的 Pro App 的需求 你可以利用这个生态系统 以可预测的帧速率 实现 8K 内容的实时编辑
Apple 的 AV Foundation 和 Core Animation Metal Layer 提供的 API 可以无缝支持 高动态范围 你可以将其与 HDR TV 和 Apple 的新 Pro Display XDR 结合使用 以生成精彩的 视频和图像 Mac Pro 最多可以有 28 个 CPU 内核
Metal 为你提供所有的 API 以便扩展这些设备上 你的使用性能
最后 我们介绍了一个 使用 Metal 对等组 API 的 新硬件功能 Affinity Fabric Link 这可以让你优化连接 解锁更多令人激动 的使用案例
想获得更多资讯 请访问我们的网站 参观明天的实验室 同时关注周五的其他 Metal 实验室 谢谢大家
[掌声]
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。