大多数浏览器和
Developer App 均支持流媒体播放。
-
将游戏移植到 Mac,第 2 部分:编译你的着色器
在本讲座中,你将了解 Metal 着色器转换器如何简化将 HLSL 着色器转换为 Metal 的过程。这是我们关于将游戏引入 Mac 平台的三部曲系列之一。你将了解如何从 DXIL 构建一个快速的、端到端的着色器管线,该管线支持所有着色器阶段,并让你可以充分利用 Apple GPU 的高级功能。我们还将向你展示如何使用离线编译器生成 GPU 二进制文件,以减少 App 的启动时间和卡顿现象。为了充分理解本次讲座,我们建议你首先观看“将你的游戏引入 Mac 平台,第 1 部分:制定游戏计划”。当你准备好进一步提升时,可以查看 WWDC23 的“将你的游戏引入Mac平台,第 3 部分:使用 Metal 进行渲染”。
章节
- 0:00 - Intro
- 0:49 - New Metal compiler tools
- 1:54 - Convert your shaders
- 12:27 - Finalize GPU binaries
- 18:20 - Wrap-Up
资源
- Download the game porting toolkit
- Download the Metal shader converter (Mac and Windows)
- Get started with Metal shader converter
- Metal
相关视频
Tech Talks
WWDC23
WWDC22
WWDC21
WWDC20
-
下载
♪ ♪
大家好 欢迎来的本次讲座 我是 Varun Subramanian 是 Apple GPU、 Graphics、Display Software 团队的设计师
本次讲座是将高端游戏引入 Mac 系列讲座的第二部分 第一部分介绍了 如何评估游戏并“制定游戏计划” 本讲座着重介绍着色器 以及如何利用一些新的 Metal 编译器工具 来提高其灵活性和速度 具体包括新的将着色器 转换为 Metal 中间表示形式的方法 以及如何在游戏构建期间 通过完成 GPU 二进制文件 来避免在设备上进行编译 Metal 编译器工具链 帮助你编译着色器 这些着色器 为你的游戏提供动力 而 Metal 现在使这一过程比以往更加简单
在设备上 创建 Metal IR 不理想 因为它增加了编译开销 阻碍了 GPU 执行你所需的工作 Metal 提供了 必要的工具 可以提前从 Metal 着色语言生成 Metal IR Metal IR 存储为 Metal 库的一部分 你应始终使用 Metal 编译器工具链 提前生成 Metal 库
然而 当来自其他 API 和着色语言时 你需要一种 让其适用于 Metal 的方法 如果你是 Mac 的新用户 现在可以使用 Metal 着色器转换器 它简化了你的着色器管线 并允许你直接将 生成的 Metal 库打包到软件包中 避免了在设备上生成 Metal IR 生成的 Metal 库与 通过 Metal 编译器生成的库相同 使得你转换的着色器可以与 Metal API 进行本地集成 使用新工具 将现有着色器转换为 Metal 库 然后与游戏一起发布 Metal 着色器转换器 提供了丰富的功能 以改善 将着色器转换为 Metal 的体验 它使用 DXIL 生成 Metal IR 你可以与开源的 DXC 编译器工具一起使用 构建端到端的着色器管线 从 DXIL 转换到 Metal IR 非常快速 因为 Metal 着色器转换器 在二进制级别上执行转换 因此 你的 着色器资源构建时间将大大减少 它还使你能够 充分利用 Apple GPU 的先进功能 这是因为 Metal 着色器转换器 拥有丰富的功能集合 支持你现有的 DXIL 着色器的 所有传统和现代着色器阶段 使用 Metal 着色器转换器 你可以为传统图形管线转换着色器 包括细分和 几何着色器 生成 Metal 库 它还支持 计算着色器 以及最近引入的 光线追踪阶段和着色器 以及放大和网格着色器 现在 我将为你介绍 如何使用 Metal 着色器转换器
有两种情况你可能 希望通过命令行转换着色器 通过终端使用命令行工具 是逐个转换着色器的好方式 如果有多个着色器 你可以创建一个调用 Metal 着色器转换器的 shell 脚本 自动为你转换多个着色器 使用命令行工具 转换着色器非常简单 在设置好 DXC 和着色器转换器后 首先将你的 HLSL 着色器编译为 DXIL DXC 要求你指定要编译的入口点、 着色器类型和输出文件 接下来 在刚创建的 DXIL 文件上 调用着色器转换器 并指定要创建的输出 Metal 库 默认情况下 着色器转换器会为最新版本的 macOS 生成一个 Metal 库 同时还会生成一个 包含有用反射数据的 JSON 文件 在运行时 你将此 Metal 库传递给 Metal 设备 以加载它并构建管线状态对象 还有两种情况 命令行界面 可能不适合你的工作流程 某些游戏引擎 具有自定义的资产构建程序 可以将着色器编译 和打包为特定于游戏的格式 此外 在一些情况下 在为 Metal 启动游戏时 在你将其完全转换为 Metal 库前 你可能还想查看 着色器在该平台上的工作情况 对于这两种情况 你需要一种更好地将 Metal 着色器转换器 集成到工作流程中的方法 为了实现这一点 使用 Metal 着色器转换器动态库 它提供了 与 CLI 工具相同的所有功能 帮助你生成 Metal 库 该库提供了一个 纯 C 接口 与 CLI 工具一样 它在 macOS 和 Windows 上都可用 因此很容易 集成到你现有的工作流程中 在将着色器转换为 Metal IR 并将其集成到游戏中之后 你需要创建管线状态并绑定资源
在你的着色器中 通常将资源定义为全局变量 并为它们分配“register”声明 从 API 的角度来看 你的游戏 要么直接将资源绑定到这些槽位 要么通过 “root signatures”定义显式内存布局 着色器转换器 可以帮助你实现此模型 因为 Metal 具有非常灵活的绑定模型 该工具 将这些资源布局为参数缓冲区 在这种模型中 你将一个参数缓冲区 绑定到管线 并通过它引用你的资源 对于这个“顶层”参数缓冲区 有两种布局模式可供选择 以契合你的游戏
你可以创建的 最简单布局是自动布局 其中着色器转换器 将按顺序放置你的资源 一旦创建包含着色器的管线状态 你只需绑定一个参数缓冲区 通过它引用所有资源 另外 着色器转换器还支持显式定义 与根签名匹配的布局 当游戏需要将不同的纹理和采样器 指定到各自的资源表中 或者当游戏使用 无绑定资源时 请使用此模式 你还可以 将原始缓冲区和 32 位常量 直接嵌入到顶层参数缓冲区中 如此图中的 0 和 1 所示
现在 顶层参数缓冲区是 CPU 和 GPU 之间共享的资源 因此 当你写入其中时 需要协调对其内存的访问 以避免 可能导致视觉损坏的争用条件 你无需对 CPU 和 GPU 工作 进行序列化以避免这种争用条件 避免这种情况的 一种方法是使用线性分配器 它可以 是一个大型的 Metal 缓冲区 你可以从中为每帧分配不同的资源 然后针对游戏处理的每一个即时帧 进行相应的后台缓冲区着色 若需了解更多关于线性分配器 实现的信息 请查看我们的示例代码 若需了解 最佳的参数缓冲区管理实践 请查看 去年的无绑定讲座和 Metal 文档 绑定模型不是唯一让 Metal 着色器转换器 可以帮助你将着色器 带入 Mac 平台的工具 因为不同的图形 API 之间存在差异 映射特定的 着色器阶段可能会有些困难 举个例子 你可能有一些使用了 传统几何和细分阶段的管线 Metal 是一种现代的 API 它提供了 例如视口 ID 和扩展等功能 这使得其他图形 API 中 旧的、低效的阶段变得不再必要
然而 当你的游戏依赖于 这些管线来实现 增强表面的传统效果 例如在这个图像中渲染的草地 通过手动转换它们会非常耗费资源 Metal 着色器转换器 可以通过将它们映射到网格着色器 帮助你将这些管线 引入到 Metal 上 这是一种现代 且更高效的图形 API 构造
这个工具可以完成繁重的工作 通过将每个阶段映射到 Metal IR 表示 将这些复杂的管线 轻松带到 Metal 上 其中包括传统上 是固定功能操作的镶嵌器 为了支持这个工作流程 今年 Metal 添加了 将可见函数链接到 Mesh 着色器 的“物体和网格”阶段的能力 在编译了你的着色器之后 你使用它们来构建 一个“Metal Mesh RenderPipeline Descriptor” 并将其编译为 一个“MetalRenderPipelineState” 当 Metal 接收到 构建此管线状态的请求时 它会编译和链接 所有的 Metal IR 将所有函数合并成一个单一的管线 完全避免了函数调用开销 从而提高运行时的性能 Metal 可见函数的强大和灵活性 允许你构建这个 包含这些着色阶段 及其附加函数的复杂渲染管线 虽然构建这些网格管线看似简单 但每个管线都必须 按照精确的顺序进行一系列步骤
着色器转换器运行时 帮助你构建这些复杂的管线 它甚至通过 分派网格着色工作模拟绘制调用 有关更多信息 请参阅 Metal 着色器转换器文档 既然你的着色器已经在 Metal 上运行 并且正在运行管线状态 下面是一些提示 有助于 你获得出色的性能和视觉正确性
使用着色器转换器编译的 着色器间接引用 Metal 资源 要将对 Metal 标记资源驻留 可以调用“useResource” 然而 频繁过度调用 useResource 会对性能产生负面影响 可以使用复数形式的 useResources 一次提供多个资源 或者考虑通过 useHeap 使用 Metal 堆在单次调用中 来标记多个资源的驻留 在 Metal 第一次 编译管线对象时 它们会被缓存 从而自动减少 后续游戏运行时基于编译的延迟 二进制存档也可以帮助你 为了更好地利用 GPU 并自定义 Metal IR 以提高性能 着色器转换器为你提供了多个选项 包括自定义兼容性、GPU 系列、 顶点提取行为、入口点命名、反射等等 这里有一个额外的优化机会
我之前提到过 着色器转换器作为另一种机制 与 Metal 编译器一起 从你现有的着色器 IR 中 生成“Metal 库” Metal 使用这些库馈送 各个图形管线阶段 由于一切都是 Metal IR 你可以在单个 App 中甚至单个管线中 混合使用来自 Metal 着色器转换器 和 Metal 编译器的“Metal 库” Metal 着色语言 还允许你访问独特的功能 例如可编程混合 利用这种方法 可充分发挥 Apple GPU 的潜力 你甚至可以利用独特的着色功能 如分块着色 这使得你在将游戏迁移到 Metal 时具有极大的灵活性 性能很重要 但游戏的视觉正确性更为重要 HLSL 允许无缝地 将纹理视为一个元素的数组
为了迁移依赖此行为的着色器 请将纹理创建为纹理数组 或在纹理上 创建“纹理数组视图” 如果你正在使用 “MetalKit 纹理加载器” 它还可以 帮助你将文件加载为纹理数组 为了设置采样器对象 并从这些纹理中读取数据 请确保提前告知 Metal 你打算通过使用 MTLSamplerDescriptor 中的 supportsArgumentBuffers 属性 在参数缓冲区中引用采样器 现在你已经熟悉了如何 将着色器转换器集成到工作流程中 下面是获取它的方法 你可以从 developer.apple.com/cn/ 下载 Metal 着色器转换器 如果你在 Mac 上工作 请获取适用于 Mac 的 Metal 着色器转换器包 如果你在 Windows 上工作 它是 Metal Developer Tools for Windows 包的一部分 该工具的测试版现已可用 这两个包都包含 Metal 着色器转换器 以独立和库的形式提供 并附带运行时的配套头文件 完整的文档以及 Metal C++ 代码示例现已可用 使用示例代码 可探索几何和细分仿真、 实例化绘制以及计算着色器等功能 将你的着色器转换为 与游戏一起提供的 Metal 库 可以避免在游戏运行时生成这些库 你还可以执行另一个额外的优化 即预先编译 GPU 二进制文件 当构建游戏时 你将着色器 编译为 Metal 库 但仍需 将其最终化为 GPU 二进制文件 通常情况下 游戏会在启动时 执行这一步骤 导致加载屏幕时间较长 如果你延迟在运行时 最终化 GPU 二进制文件 可能会导致丢帧 因为游戏会按需编译新的管线 Metal GPU 二进制编译器 可以帮助你解决这个问题 它允许你在游戏构建时 生成着色器二进制文件 游戏进行期间不需要 生成着色器二进制文件 你的玩家可以受益于 减少的 App 加载时间 而不会导致额外的 GPU 中断
要利用这一点 你可以在工作流程中添加另一步骤 在构建时将你的 Metal 库 最终化为 Metal 二进制存档 设备上的 GPU 二进制编译 发生在从描述符创建管线状态时 该描述符 不仅引用 Metal 库中的函数 还为 Metal 提供其他关键信息 例如其渲染附件的颜色格式 和顶点布局描述符 GPU 二进制文件 会在创建 PSO 的过程中即时生成 二进制存档 允许你控制编译发生的时间 为了提前生成 GPU 二进制文件 你需要同时提供现有的 Metal 库 和引用这些库的管线配置脚本 然后将它们都提供给 metal-tt 生成带有 GPU 二进制文件的二进制存档 为了开发管线脚本 你需要生成一个 管线配置类似于 Metal API 的 JSON 脚本 这段 Metal 代码 生成一个渲染管线描述符 并提供其相应的 JSON 表示 对于你的管线脚本 需添加 Metal 库的路径 并指定其片段和顶点函数的名称 同时还可以指定其他管线状态配置 通过这样的方式 你现在已经得到了可用的 Metal 脚本 关于 JSON 模式的更多信息 可以在 Metal 的开发文档中查看 你的着色器预编译工作流 可能不适合生成管线脚本文件 针对这些情况 有一种替代方法 你可以在设备上 运行游戏时记录 Metal 二进制存档 其中包含相应的管线脚本 如果你从设备中提取这些存档 然后使用“metal-source” 提取其中嵌入的管线脚本 随后 你需要更新 提取的脚本中的 Metal 库路径 更多详细信息 请参阅我们关于 “构建 GPU 二进制文件” 和“发现编译工作流程”讲座 由于 GPU 二进制文件 是针对每个 GPU 定制的 “metal-tt”会生成 针对不同设备的二进制文件版本 供你根据 玩家的设备分发给玩家使用 metal-tt 通过将 所有不同的 GPU 二进制文件 整合到 Metal 二进制存档中 来帮助你管理这种复杂性 这样 当你的 App 加载二进制存档时 Metal 将自动选择 适用于玩家的合适的二进制文件 你还可以将多个二进制文件 集合封装到单个二进制存档中
现在 你可以提前 生成二进制存档了 以下是一些最佳实践 当玩家运行带有预编译 GPU 二进制文件的 Metal App 时 Metal 会在打包的二进制存档中 查找所需的 GPU 二进制文件 如果 Metal 在存档中 找不到匹配的文件 它将自动退回到设备上编译 尽管 App 看上去一切正常 但这可能会延迟向 GPU 的提交 你可以使用 “FailOnBinaryArchiveMiss”选项 来测试二进制存档 是否包含你预期的管线 在创建 Metal 管线状态对象时 你可以轻松地指定 FailOnBinaryArchiveMiss 选项 如果发现二进制存档缺失 并且设置了该选项 Metal 将跳过设备上 编译过程 并返回空的管线状态 一旦你准备好 支持所有目标设备的二进制存档 你就可以开始部署了 并非所有玩家 都可能使用最新的操作系统 为了确保所有用户都能受益于 二进制存档 你需要为每个主要的 操作系统版本生成一个存档 并将其存储到你的 App 中 为了实现这一点 你可以 检查玩家设备的操作系统版本 并选择相应的二进制存档 与你的管线描述符关联 当玩家更新其操作系统时 其二进制存档可能需要 重新编译以实现向前兼容性 但 Metal 已为你考虑到了这一点 Metal 会自动 在玩家设备的 App 包中 识别未打包的二进制存档 并在操作系统更新或游戏安装后 在后台自动将它们升级 总结一下 “metal”编译器 和“metal-shaderconverter” 是你提前生成 Metal 库的首选工具 你可以随后将其与游戏一起提供 当编译 MSL 源码时 使用“metal 编译器”; 当你的着色器使用 HLSL 时 使用“metal-shaderconverter” 而 metal-tt 则使你 能够将 Metal 库最终化为 适应 Metal 生态系统中 各种 GPU 的 GPU 二进制文件 最后 metal-source 工具可 帮助你从现有的 macOS 游戏中 提取管线脚本 这些工具中的绝大部分 以及 GPU 二进制编译器工具链的其余部分 现在不仅支持 macOS 也都支持 Windows 将它们集成到你现有的 工作流程中将变得更加容易 总结一下: Metal 着色器转化器是一个新工具 可帮助你将使用其他着色语言开发的 着色器转换为 Metal GPU 二进制编译器及其工具链 现可在 Windows 上使用 它可以将你的 Metal 库 最终化为 GPU 二进制文件 有了这些工具 你现在拥有了 将你的着色器 带入 Metal 的一切所需 我们还有更多内容与你分享 本系列的第三部分将重点介绍 如何优化高端 Metal App 请记得去观看 感谢观看 ♪ ♪
-
-
14:28 - Json Metal Script
{“libraries": { "paths": [ {"path": “ba.metallib”, "label": "myMetalLib"} ] }, "pipelines": { "render_pipelines": [{ "vertex_function": "alias:myMetalLib#v", "fragment_function": "alias:myMetalLib#f", "raster_sample_count": 2, "color_attachments": [{ "pixel_format": "BGRA8Unorm" }], "depth_attachment_pixel_format": "Depth32Float" }] } }
-
16:30 - Testing Binary Archive hit
// Create Pipeline Descriptor MTLComputePipelineDescriptor *computeDesc = [MTLComputePipelineDescriptor new]; computeDesc.binaryArchives = @[existingBinaryArchive]; computeDesc.computeFunction = computeFn; id<MTLComputePipelineState> computePS = [device newComputePipelineStateWithDescriptor:computeDesc options:MTLPipelineOptionFailonBinaryArchiveMiss error:&err]; if(computePS == nil) { // Binary archive is missing compiled shader. }
-
17:03 - Loading appropriate Binary Archive
// Load OS-specific binary archives MTLComputePipelineDescriptor *computeDesc = [MTLComputePipelineDescriptor new]; if (@available(macOS 14, *)) { computeDesc.binaryArchives = @[binaryArchive_macOS14]; } else { computeDesc.binaryArchives = @[binaryArchive_macOS13_3]; } computeDesc.computeFunction = computeFn; id<MTLComputePipelineState> computePS = [device newComputePipelineStateWithDescriptor:computeDesc options:nil error:&err];
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。