WWDC Quick Look 💓 By SwiftGGTeam
Discover Metal 4

Discover Metal 4

观看原视频

Highlight

Metal 4 把命令队列、命令缓冲区、编码器全部换成新的 MTL4 系列对象,命令缓冲区与队列解耦,可在多线程并行编码;同时新增 MTL4ArgumentTable、residency sets、placement sparse resources、Barrier API、MTL4Compiler 等机制,并把张量(tensor)与机器学习编码器作为一等资源接入。


核心内容

老 Metal 应用最大的痛点是绑定模型跟不上现代渲染的资源规模。一个场景里成千上万个材质、几何缓冲区、纹理,全得挤进固定数量的 bind point;每个 draw call 都在 CPU 上重新设置绑定,编码线程很容易成为瓶颈。Apple 用 bindless 的方式缓解过一次:把所有资源塞进 argument buffer,每个对象只绑一个。但绑定管理、内存常驻、shader 编译、ML 操作仍然分散在各处,写起来繁琐。

Metal 4 的解法是把整条链路推倒重做。命令编码侧推出 MTL4CommandQueue 和 MTL4CommandBuffer,并把 command buffer 与 queue 解耦——你向 device 申请 buffer,向任意 queue 提交,天然支持多线程并行编码(02:22)。编码器也做了合并:新的 unified compute encoder 同时处理 compute、blit、加速结构构建(02:53);MTL4RenderCommandEncoder 带 attachment map,可以在同一个 encoder 里切换 color attachment,省掉重复分配(03:06)。命令缓冲区的内存由 MTL4CommandAllocator 直接管理,你说了算(03:44)。

资源管理方面,绑定表升级为 MTL4ArgumentTable,按需声明大小,bindless 场景只需要 1 个 buffer binding(05:43)。常驻管理用 residency sets——把资源集合一次性挂到 MTL4CommandQueue 上,所有提交到该队列的命令都自动可见(06:55)。Remedy 的《Control Ultimate Edition》集成 residency sets 后,常驻管理的开销显著下降,关闭光追时内存占用也更低(07:22)。需要超出物理内存的场景,用 placement sparse resources——分配资源时不分页,按需从 placement heap 里申请页(08:32)。并发同步换成 Barrier API,stage 到 stage,对应 DirectX/Vulkan 里的 barrier 概念(09:02)。

最有意思的是 ML 被一等公民化。Metal 4 新增 tensor 资源类型,支持任意维度(14:22),并提供 machine learning command encoder,能把整个 CoreML 网络作为一条命令插进 command buffer,和渲染命令共享 barrier 同步(15:05)。小网络则可以用 Metal performance primitives 在 shader 里就地推理,比如 neural material evaluation——采样 latent 纹理、推理、着色全部融合进一次 dispatch(16:01)。


详细内容

命令编码:新对象与并行编码

Metal 4 的编码模型基于一组新对象:MTL4CommandQueue、MTL4CommandBuffer、MTL4CommandAllocator、MTL4RenderCommandEncoder、新的 unified compute encoder。沿用现有的 MTLDevice,无需替换设备对象(02:08)。

// 伪代码示意:均使用 transcript 中明确提到的 API 名
let queue = device.makeMTL4CommandQueue()
let allocator = device.makeMTL4CommandAllocator()
let cmdBuffer = device.makeMTL4CommandBuffer(allocator: allocator)

let renderEncoder = cmdBuffer.makeMTL4RenderCommandEncoder(descriptor: rpDesc)
// attachment map 允许在同一个 encoder 内切换 color attachment
renderEncoder.setAttachmentMap(mapA)
renderEncoder.drawPrimitives(...)
renderEncoder.setAttachmentMap(mapB)
renderEncoder.drawPrimitives(...)
renderEncoder.endEncoding()

queue.commit([cmdBuffer])

关键点:

  • MTL4CommandQueue:新队列对象,与 command buffer 解耦,可被多个线程同时使用。
  • MTL4CommandAllocator:直接管理 command buffer 的内存使用,避免框架内部隐式分配。
  • makeMTL4CommandBuffer(allocator:):command buffer 由 device 创建而非 queue 创建,所以可以并行编码后再决定提交到哪个 queue(02:29)。
  • MTL4RenderCommandEncoder + attachment map:单个 render encoder 内动态切换 color attachment,省去为不同输出再开一个 encoder(03:16)。

绑定:MTL4ArgumentTable

Argument table 按 stage 声明,可以跨 stage 共享。bindless 场景下一个 buffer binding 就够了(06:00)。

常驻:residency sets

residency set 一次配置、长期使用。常驻内容很少变动时,可以在 app 启动阶段填充;流式加载场景则放到独立线程更新,与编码并行(07:09)。

稀疏资源:placement sparse

placement sparse 资源声明时不带页面,运行时按需从 placement heap 分配页(08:43)。注意:placement sparse 的映射操作需要 MTL4CommandQueue,而旧的渲染队列还是 MTLCommandQueue——用 MTLEvent 在两条队列间同步(21:26)。

同步:Barrier API

Barrier 是 stage 到 stage 的。例:在 compute encoder 里 dispatch 一个灰度转换 shader,紧接着 render encoder 的 fragment 阶段要读它,就需要一个 dispatch-to-fragment barrier(10:01)。具体例子见官方 sample “processing a texture in a compute function”。

编译:MTL4Compiler 与 flexible render pipeline states

MTL4Compiler 从 device 上独立出来,明确控制 CPU 上的编译时机。它继承调用线程的 QoS,多线程并发编译时高优先级线程的请求会被优先处理(11:45)。flexible render pipeline states 让多个仅 color state 不同的 pipeline 共享一份编译好的 Metal IR——先建未特化 pipeline,再按需特化(12:41)。

机器学习:tensor、ML encoder、shader ML

大网络走 ML command encoder:把 CoreML 包用 Metal 工具链转成 Metal package,再喂给 encoder,和渲染命令共享 barrier(15:32)。小网络走 shader ML:用 Metal performance primitives 在 shader 内直接做矩阵/卷积运算,OS 编译器会针对当前设备 inline 优化代码(17:00)。

MetalFX:新增帧插值和去噪

MetalFX 在原有时间放大基础上,今年加了 frame interpolation(合成中间帧、拉高刷新率)和 denoising upscaler(边升采样边对低采样数光追结果去噪)(18:2418:53)。

调试工具与起步

Xcode 26 自带 Metal 4 项目模板,新建 Game 工程时选 Metal 4 即可(22:50)。API/Shader Validation、Metal Debugger、Metal Performance HUD、Metal System Trace 全部支持 Metal 4。


核心启发

  • 做什么:把现有 Metal 应用的 shader 编译路径迁到 MTL4Compiler

    • 为什么值得做:风险最低、收益直接。MTL4Compiler 独立于 device,继承调用线程 QoS,多线程并发编译时高优先级请求优先处理,对启动期 hitches 改善显著。
    • 怎么开始:用 device 申请一个 MTL4Compiler,把原来调 makePipelineState 的地方改走新接口;先不动 shader、不动 pipeline 描述,只换编译入口。
  • 做什么:用 residency sets 替换零散的 useResource / makeResident 调用

    • 为什么值得做:Remedy 在《Control Ultimate Edition》上的实测,关掉光追后内存占用明显下降,常驻管理 CPU 开销同步降低。集成成本低。
    • 怎么开始:按资源生命周期把资源分组(永久、关卡、流式),每组建一个 residency set。永久组在 app 启动时填充并挂到 MTL4CommandQueue 上;流式组放到加载线程更新。
  • 做什么:把超出物理内存的纹理改造成 placement sparse

    • 为什么值得做:让同一份资产覆盖更多设备档位。低端机只常驻低 LOD 的页,高端机常驻全部,无需出多份资产。
    • 怎么开始:从最大的几张纹理(地形 virtual texture、UI atlas)入手。声明为 placement sparse、建 placement heap、按可见性脚本控制页的分配与释放。映射操作走 MTL4CommandQueue,用 MTLEvent 与原有 MTLCommandQueue 同步。
  • 做什么:用 MetalFX 时间放大 + 帧插值组合达成高刷

    • 为什么值得做:渲染 1/4 像素 + 插一帧,理论上把渲染负载砍掉 7/8。对追求 120Hz 的游戏来说,性价比最高的一步。
    • 怎么开始:先接 MetalFX 时间放大(从 2x 倍率起步),确认无明显伪影后再接 frame interpolation;用 Metal Performance HUD 实时观察真实帧率与延迟。
  • 做什么:用 Shader ML + Metal performance primitives 实现 neural material

    • 为什么值得做:latent 纹理体积远小于传统 PBR 纹理集,纹理带宽下降;推理与采样、着色融合进一次 dispatch,不必在 device memory 上反复来回搬数据。
    • 怎么开始:先用 PyTorch / CoreMLtools 训一个 small material decoder,导出 CoreML 包;在 shader 里用 Metal performance primitives 调用,把采样到的 latent 当 tensor 输入,输出 albedo / normal / roughness。

关联 Session

评论

GitHub Issues · utterances