M3 Max 性能灵异事件:为什么 Mesh Shader 在 4K AO 贴图下会“缩水”?
最近在做移动端/桌面端统一渲染管线优化时,我发现了一个非常有意思的现象:在 Apple Silicon(特别是 M2/M3 系列)上,使用 Mesh Shader 替代传统顶点管线时,如果环境光遮蔽(AO)贴图的分辨率保持在 2048x2048 以下,性能提升非常显著;但一旦把 AO 贴图推到 4K,Mesh Shader 的收益会瞬间塌缩,甚至在某些复杂场景下表现不如传统的 Vertex Shader。
这看起来非常不符合常理。按理说 Mesh Shader 擅长的是几何剔除和并行分发,贴图分辨率只影响采样器(Sampler)和带宽。但结合 Apple SoC 的硬件底层逻辑,我怀疑我们撞到了 SLC(System Level Cache) 的边界限制。
1. 理论背景:Mesh Shader 与 Apple 的 TBDR 架构
Apple 的 GPU 是典型的 TBDR(Tile-Based Deferred Rendering)。在传统管线中,顶点处理和像素处理是高度解耦的。但 Mesh Shader(在 Metal 中对应 Object Shader 和 Mesh Shader 阶段)引入了更灵活的几何处理能力。
当我们在 Mesh Shader 阶段进行采样(比如读取 AO 贴图做位移贴图或者基于遮蔽信息的剔除)时,所有的采样负载都会压在 Shader Core 上。
2. 为什么是 2048x2048 这个临界点?
我们可以算一笔账:
- 一张 2048x2048 的单通道 16-bit 浮点(R16F)贴图,占用内存约为 8MB。
- 一张 4096x4096 的同规格贴图,跳到了 32MB。
在 M2/M3 架构中,SLC(系统级缓存) 是 GPU、CPU 和 Neural Engine 共享的池子。虽然 M3 Max 的 SLC 容量有所增加,但在高频采样时,由于 Mesh Shader 需要频繁地在 Threadgroup 之间同步几何数据,如果纹理数据无法命中二级缓存(L2)甚至 SLC,采样延迟(Latency)就会直接击穿 Mesh Shader 的掩盖能力。
当 AO 贴图超过 2048 以后,缓存命中率(Cache Hit Rate)急剧下降。由于 Mesh Shader 往往伴随着复杂的 Meshlet Culling(网格簇剔除) 逻辑,这些逻辑需要等待采样结果来决定是否生成 Meshlet。如果采样被阻塞,整个 GPU 的后端分发器就会处于饥饿状态,导致硬件利用率(Utilization)大幅下滑。
3. 征集 M3 Max “课代表”做对照实验
为了验证这个假设,我写了一个简易的 Metal 测试脚本(基于 Metal Performance Shaders Graph)。逻辑如下:
- 负载 A:传统顶点管线,挂载 4K AO 纹理,对比不同 Overdraw 情况下的 FPS。
- 负载 B:Mesh Shader 管线,在 Object Shader 阶段读取 AO 纹理进行剔除。
- 变量控制:分别循环测试纹理分辨率从 1024 到 8192 的表现。
测试脚本关键逻辑片段(Swift/Metal):
// 关键:观察不同纹理尺寸下的 GPU 采样 stall 比例
let descriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .r16Float,
width: size,
height: size,
mipmapped: true)
// ... 在 Mesh Shader 中通过缓存行对齐方式访问
// 统计 GPU Counter 中的 "Texture Sample Waiting" 和 "SLC Miss Rate"
我需要老哥们帮忙确认的数据:
- 在 M3 Max 上,当纹理从 2048 升至 4096 时,
MTLGPUCounter中的InstructionLag(指令延迟)是否有陡增? - 使用 Xcode GPU Trace 查看,Mesh Shader 的 Threadgroup Occupancy(线程组占用率) 是否因为纹理采样而掉到了 15% 以下?
4. 初步避坑建议
如果你也遇到了类似问题,目前有几个方向可以尝试:
- 分级采样:在 Object Shader 阶段使用更低级别的 Mipmap 进行剔除判断,只在 Fragment Shader 阶段使用全分辨率纹理。
- Tile Optimization:确保你的 Meshlet 布局与纹理采样的空间局部性对齐,减少 SLC 的随机访问开销。
- 重新评估内存对齐:Apple 的内存控制器对 16KB Page 非常敏感,尝试调整纹理的存储模式(Storage Mode)。
如果有哪位手握 M3 Max(特别是 128GB 内存顶配版,SLC 压力可能略有不同)的大神愿意跑一下,评论区见!我会把完整的测试工程发给你,数据跑出来咱们一起写篇深度分析。