关于 metal-gpu
此技能为编写 Metal GPU 代码提供模式、API 提示和性能建议,面向使用 Metal 与 Swift 开发 GPU 渲染或计算的开发者。内容覆盖命令流水线、MSL 着色器及 Apple GPU 优化。
主要功能
- 01Metal 命令流水线模式
- 02编写 MSL 顶点、片元和计算着色器
- 03配置 Swift 渲染与计算管线
- 04管理资源存储模式和缓冲区
- 05提供性能优化与常见问题提示
- 06涵盖 Metal 4 API 变化
适用场景
- 01搭建 Metal 渲染管线
- 02编写 GPU 计算内核
- 03减少渲染循环中的资源分配
- 04排查编码器、缓冲区索引和像素格式问题
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ ios-agent/ iosagent.dev --skill metal-gpu安装后在 Claude Code 中输入/metal-gpu确认已加载。
附带文件references/metal-api-guide.md
详细介绍
Metal GPU 代码技能
编写生产级 Metal 代码,遵循正确模式,实现最佳性能,并提供清晰说明。
何时阅读参考资料
如需了解详细的 API 结构、Metal 4 特性和 Apple Silicon 优化模式,请阅读:
/mnt/skills/user/metal-gpu/references/metal-api-guide.md
核心原则
- 始终从设备开始:
MTLCreateSystemDefaultDevice()—— 每个 Metal 工作流都从这里开始 - 命令模式:Device → Command Queue → Command Buffer → Command Encoder → Commit
- 着色器使用 MSL(Metal Shading Language):基于 C++14,并包含 Metal 专属类型和属性
- 资源管理很重要:使用合适的存储模式,避免不必要的拷贝
- 在渲染循环中使用三重缓冲,让 CPU 和 GPU 并行工作
快速参考:Metal 命令管线
MTLDevice
└─ makeCommandQueue() → MTLCommandQueue
└─ makeCommandBuffer() → MTLCommandBuffer
├─ makeRenderCommandEncoder(descriptor:) → MTLRenderCommandEncoder
├─ makeComputeCommandEncoder() → MTLComputeCommandEncoder
└─ makeBlitCommandEncoder() → MTLBlitCommandEncoder
编写着色器(MSL)
使用 Metal Shading Language。始终包含:
#include <metal_stdlib>和using namespace metal;- 正确的属性限定符:
[[vertex_id]]、[[position]]、[[stage_in]]、[[buffer(n)]]、[[texture(n)]] - 正确的地址空间限定符:
device、constant、threadgroup、thread
顶点着色器模式
#include <metal_stdlib>
using namespace metal;
struct VertexIn {
float3 position [[attribute(0)]];
float3 normal [[attribute(1)]];
float2 texCoord [[attribute(2)]];
};
struct VertexOut {
float4 position [[position]];
float3 normal;
float2 texCoord;
};
vertex VertexOut vertex_main(VertexIn in [[stage_in]],
constant float4x4 &mvp [[buffer(1)]]) {
VertexOut out;
out.position = mvp * float4(in.position, 1.0);
out.normal = in.normal;
out.texCoord = in.texCoord;
return out;
}
片段着色器模式
fragment float4 fragment_main(VertexOut in [[stage_in]],
texture2d<float> albedo [[texture(0)]],
sampler texSampler [[sampler(0)]]) {
float4 color = albedo.sample(texSampler, in.texCoord);
return color;
}
计算内核模式
kernel void compute_main(device float *input [[buffer(0)]],
device float *output [[buffer(1)]],
uint id [[thread_position_in_grid]]) {
output[id] = input[id] * 2.0;
}
Swift 端设置模式
渲染管线设置
let device = MTLCreateSystemDefaultDevice()!
let commandQueue = device.makeCommandQueue()!
// Load shaders
let library = device.makeDefaultLibrary()!
let vertexFunction = library.makeFunction(name: "vertex_main")
let fragmentFunction = library.makeFunction(name: "fragment_main")
// Pipeline descriptor
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexFunction = vertexFunction
pipelineDescriptor.fragmentFunction = fragmentFunction
pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
// Vertex descriptor
let vertexDescriptor = MTLVertexDescriptor()
vertexDescriptor.attributes[0].format = .float3 // position
vertexDescriptor.attributes[0].offset = 0
vertexDescriptor.attributes[0].bufferIndex = 0
vertexDescriptor.layouts[0].stride = MemoryLayout<SIMD3<Float>>.stride
pipelineDescriptor.vertexDescriptor = vertexDescriptor
let pipelineState = try! device.makeRenderPipelineState(descriptor: pipelineDescriptor)
计算管线设置
let computeFunction = library.makeFunction(name: "compute_main")!
let computePipeline = try! device.makeComputePipelineState(function: computeFunction)
let commandBuffer = commandQueue.makeCommandBuffer()!
let encoder = commandBuffer.makeComputeCommandEncoder()!
encoder.setComputePipelineState(computePipeline)
encoder.setBuffer(inputBuffer, offset: 0, index: 0)
encoder.setBuffer(outputBuffer, offset: 0, index: 1)
let gridSize = MTLSize(width: elementCount, height: 1, depth: 1)
let threadGroupSize = MTLSize(
width: min(computePipeline.maxTotalThreadsPerThreadgroup, elementCount),
height: 1, depth: 1
)
encoder.dispatchThreads(gridSize, threadsPerThreadgroup: threadGroupSize)
encoder.endEncoding()
commandBuffer.commit()
MetalKit 视图渲染
import MetalKit
class Renderer: NSObject, MTKViewDelegate {
let device: MTLDevice
let commandQueue: MTLCommandQueue
let pipelineState: MTLRenderPipelineState
func draw(in view: MTKView) {
guard let drawable = view.currentDrawable,
let descriptor = view.currentRenderPassDescriptor else { return }
let commandBuffer = commandQueue.makeCommandBuffer()!
let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor)!
encoder.setRenderPipelineState(pipelineState)
// Set buffers, draw primitives...
encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)
encoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
}
性能最佳实践
- 存储模式:在 Apple Silicon 上使用
.shared(统一内存),GPU 专用数据使用.private,Intel Mac 上使用.managed - 三重缓冲:使用信号量轮换 3 个缓冲区,避免 CPU/GPU 停顿
- 避免逐帧分配:重用缓冲区和命令编码器
- 尽可能使用
dispatchThreads,而不是dispatchThreadgroups(Apple Silicon) - 在 Apple GPU 上优先采用基于瓦片的延迟渲染模式——使用 imageblocks 和瓦片着色器
- 提前编译管线:创建管线的开销很大,应在加载时完成
- 使用 Xcode 中的 Metal GPU 帧捕获进行性能分析和调试
应避免的常见错误
- 提交前忘记调用
encoder.endEncoding() - Swift 和 MSL 之间的缓冲区索引不匹配
- 渲染目标使用了错误的像素格式
- 未处理 Metal API 可选调用返回的
nil - 阻塞主线程等待 GPU 完成——改用
addCompletedHandler - 使用
[[stage_in]]时忘记设置顶点描述符
Metal 4 说明
Metal 4 引入了现代化的核心 API。主要变化:
- 新的编译 API,可更精细地控制着色器编译
- 更新了命令编码模式
- 完整的 Metal 4 API 结构请参阅
references/metal-api-guide.md
框架生态系统
| 框架 | 用途 |
|---|---|
| Metal | 直接访问 GPU、着色器、管线 |
| MetalKit | 视图管理、纹理加载、模型 I/O |
| MetalFX | 性能优化用的超分辨率(时间/空间) |
| Metal Performance Shaders | 优化的计算与图像处理内核 |
| Compositor Services | visionOS 立体渲染 |
| RealityKit | 高级 3D 渲染(底层使用 Metal) |
评论
支持 Markdown