axiom-metal-migration-ref
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMetal Migration Reference
Metal迁移参考文档
Complete reference for converting OpenGL/DirectX code to Metal.
这是一份将OpenGL/DirectX代码转换为Metal的完整参考文档。
When to Use This Reference
何时使用本参考文档
Use this reference when:
- Converting GLSL shaders to Metal Shading Language (MSL)
- Converting HLSL shaders to MSL
- Looking up GL/D3D API equivalents in Metal
- Setting up MTKView or CAMetalLayer
- Building render pipelines
- Using Metal Shader Converter for DirectX
在以下场景中使用本参考文档:
- 将GLSL着色器转换为Metal着色语言(MSL)
- 将HLSL着色器转换为MSL
- 查询GL/D3D API在Metal中的等效实现
- 配置MTKView或CAMetalLayer
- 构建渲染管线
- 使用Metal Shader Converter处理DirectX
Part 1: GLSL to MSL Conversion
第一部分:GLSL转MSL转换
Type Mappings
类型映射
| GLSL | MSL | Notes |
|---|---|---|
| | |
| | |
| | 32-bit signed |
| | 32-bit unsigned |
| | 32-bit |
| N/A | Use |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | Columns x Rows |
| | |
| | Separate in MSL |
| | |
| | |
| | |
| |
| GLSL | MSL | 说明 |
|---|---|---|
| | |
| | |
| | 32位有符号 |
| | 32位无符号 |
| | 32位 |
| N/A | 使用 |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 列数 x 行数 |
| | |
| | 在MSL中是分离的 |
| | |
| | |
| | |
| |
Built-in Variable Mappings
内置变量映射
| GLSL | MSL | Stage |
|---|---|---|
| Return | Vertex |
| Return | Vertex |
| | Vertex |
| | Vertex |
| | Fragment |
| | Fragment |
| | Fragment |
| Return | Fragment |
| | Fragment |
| | Fragment |
| GLSL | MSL | 阶段 |
|---|---|---|
| 返回 | 顶点着色器 |
| 返回 | 顶点着色器 |
| | 顶点着色器 |
| | 顶点着色器 |
| | 片元着色器 |
| | 片元着色器 |
| | 片元着色器 |
| 返回 | 片元着色器 |
| | 片元着色器 |
| | 片元着色器 |
Function Mappings
函数映射
| GLSL | MSL | Notes |
|---|---|---|
| | Method on texture |
| | |
| | |
| | Integer coords |
| | Separate calls |
| | |
| | |
| | Same |
| | Same |
| | Same |
| | Same |
| | Same |
| | Different name |
| | Same |
| | Different name |
| | Different name |
| GLSL | MSL | 说明 |
|---|---|---|
| | MSL中为纹理对象的方法 |
| | |
| | |
| | 整数坐标 |
| | 需分开调用 |
| | |
| | |
| | 名称相同 |
| | 名称相同 |
| | 名称相同 |
| | 名称相同 |
| | 名称相同 |
| | 名称不同 |
| | 名称相同 |
| | 名称不同 |
| | 名称不同 |
Shader Structure Conversion
着色器结构转换
GLSL Vertex Shader:
glsl
#version 300 es
precision highp float;
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec2 aTexCoord;
uniform mat4 uModelViewProjection;
out vec2 vTexCoord;
void main() {
gl_Position = uModelViewProjection * vec4(aPosition, 1.0);
vTexCoord = aTexCoord;
}MSL Vertex Shader:
metal
#include <metal_stdlib>
using namespace metal;
struct VertexIn {
float3 position [[attribute(0)]];
float2 texCoord [[attribute(1)]];
};
struct VertexOut {
float4 position [[position]];
float2 texCoord;
};
struct Uniforms {
float4x4 modelViewProjection;
};
vertex VertexOut vertexShader(
VertexIn in [[stage_in]],
constant Uniforms& uniforms [[buffer(1)]]
) {
VertexOut out;
out.position = uniforms.modelViewProjection * float4(in.position, 1.0);
out.texCoord = in.texCoord;
return out;
}GLSL Fragment Shader:
glsl
#version 300 es
precision highp float;
in vec2 vTexCoord;
uniform sampler2D uTexture;
out vec4 fragColor;
void main() {
fragColor = texture(uTexture, vTexCoord);
}MSL Fragment Shader:
metal
fragment float4 fragmentShader(
VertexOut in [[stage_in]],
texture2d<float> tex [[texture(0)]],
sampler samp [[sampler(0)]]
) {
return tex.sample(samp, in.texCoord);
}GLSL顶点着色器:
glsl
#version 300 es
precision highp float;
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec2 aTexCoord;
uniform mat4 uModelViewProjection;
out vec2 vTexCoord;
void main() {
gl_Position = uModelViewProjection * vec4(aPosition, 1.0);
vTexCoord = aTexCoord;
}MSL顶点着色器:
metal
#include <metal_stdlib>
using namespace metal;
struct VertexIn {
float3 position [[attribute(0)]];
float2 texCoord [[attribute(1)]];
};
struct VertexOut {
float4 position [[position]];
float2 texCoord;
};
struct Uniforms {
float4x4 modelViewProjection;
};
vertex VertexOut vertexShader(
VertexIn in [[stage_in]],
constant Uniforms& uniforms [[buffer(1)]]
) {
VertexOut out;
out.position = uniforms.modelViewProjection * float4(in.position, 1.0);
out.texCoord = in.texCoord;
return out;
}GLSL片元着色器:
glsl
#version 300 es
precision highp float;
in vec2 vTexCoord;
uniform sampler2D uTexture;
out vec4 fragColor;
void main() {
fragColor = texture(uTexture, vTexCoord);
}MSL片元着色器:
metal
fragment float4 fragmentShader(
VertexOut in [[stage_in]],
texture2d<float> tex [[texture(0)]],
sampler samp [[sampler(0)]]
) {
return tex.sample(samp, in.texCoord);
}Precision Qualifiers
精度限定符
GLSL precision qualifiers have no direct MSL equivalent — MSL uses explicit types:
| GLSL | MSL Equivalent |
|---|---|
| |
| |
| |
| |
| |
| |
GLSL的精度限定符在MSL中没有直接等效项——MSL使用显式类型:
| GLSL | MSL等效项 |
|---|---|
| |
| |
| |
| |
| |
| |
Buffer Alignment (Critical)
缓冲区对齐(关键)
GLSL/C assumes:
- : 12 bytes, any alignment
vec3 - : 16 bytes
vec4
MSL requires:
- : 12 bytes storage, 16-byte aligned
float3 - : 16 bytes storage, 16-byte aligned
float4
Solution: Use types in Swift for CPU-GPU shared structs:
simdswift
import simd
struct Uniforms {
var modelViewProjection: simd_float4x4 // Correct alignment
var cameraPosition: simd_float3 // 16-byte aligned
var padding: Float = 0 // Explicit padding if needed
}Or use packed types in MSL (slower):
metal
struct VertexPacked {
packed_float3 position; // 12 bytes, no padding
packed_float2 texCoord; // 8 bytes
};GLSL/C的默认假设:
- :12字节,任意对齐方式
vec3 - :16字节
vec4
MSL的要求:
- :12字节存储,16字节对齐
float3 - :16字节存储,16字节对齐
float4
解决方案:在Swift中使用类型作为CPU-GPU共享结构体:
simdswift
import simd
struct Uniforms {
var modelViewProjection: simd_float4x4 // 正确对齐
var cameraPosition: simd_float3 // 16字节对齐
var padding: Float = 0 // 必要时显式填充
}或者在MSL中使用打包类型(性能较低):
metal
struct VertexPacked {
packed_float3 position; // 12字节,无填充
packed_float2 texCoord; // 8字节
};Part 2: HLSL to MSL Conversion
第二部分:HLSL转MSL转换
Type Mappings
类型映射
| HLSL | MSL | Notes |
|---|---|---|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
| HLSL | MSL | 说明 |
|---|---|---|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
Semantic Mappings
语义映射
| HLSL Semantic | MSL Attribute |
|---|---|
| |
| Return value / |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| HLSL语义 | MSL属性 |
|---|---|
| |
| 返回值 / |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
Function Mappings
函数映射
| HLSL | MSL | Notes |
|---|---|---|
| | Lowercase |
| | |
| | |
| | Split coord |
| | Operator |
| | Same |
| | Different name |
| | Different name |
| | Different name |
| | Different name |
| | Manual |
| | Function call |
| HLSL | MSL | 说明 |
|---|---|---|
| | 小写形式 |
| | |
| | |
| | 拆分坐标 |
| | 运算符形式 |
| | 名称相同 |
| | 名称不同 |
| | 名称不同 |
| | 名称不同 |
| | 名称不同 |
| | 手动实现 |
| | 函数调用形式 |
Metal Shader Converter (DirectX → Metal)
Metal Shader Converter(DirectX → Metal)
Apple's official tool for converting DXIL (compiled HLSL) to Metal libraries.
Requirements:
- macOS 13+ with Xcode 15+
- OR Windows 10+ with VS 2019+
- Target devices: Argument Buffers Tier 2 (macOS 14+, iOS 17+)
Workflow:
bash
undefined这是苹果官方提供的将DXIL(编译后的HLSL)转换为Metal库的工具。
要求:
- macOS 13+ 搭配 Xcode 15+
- 或 Windows 10+ 搭配 VS 2019+
- 目标设备:Argument Buffers Tier 2(macOS 14+、iOS 17+)
工作流程:
bash
undefinedStep 1: Compile HLSL to DXIL using DXC
Step 1: Compile HLSL to DXIL using DXC
dxc -T vs_6_0 -E MainVS -Fo vertex.dxil shader.hlsl
dxc -T ps_6_0 -E MainPS -Fo fragment.dxil shader.hlsl
dxc -T vs_6_0 -E MainVS -Fo vertex.dxil shader.hlsl
dxc -T ps_6_0 -E MainPS -Fo fragment.dxil shader.hlsl
Step 2: Convert DXIL to Metal library
Step 2: Convert DXIL to Metal library
metal-shaderconverter vertex.dxil -o vertex.metallib
metal-shaderconverter fragment.dxil -o fragment.metallib
metal-shaderconverter vertex.dxil -o vertex.metallib
metal-shaderconverter fragment.dxil -o fragment.metallib
Step 3: Load in Swift
Step 3: Load in Swift
let vertexLib = try device.makeLibrary(URL: vertexURL)
let fragmentLib = try device.makeLibrary(URL: fragmentURL)
**Key Options**:
| Option | Purpose |
|--------|---------|
| `-o <file>` | Output metallib path |
| `--minimum-gpu-family` | Target GPU family |
| `--minimum-os-build-version` | Minimum OS version |
| `--vertex-stage-in` | Separate vertex fetch function |
| `-dualSourceBlending` | Enable dual-source blending |
**Supported Shader Models**: SM 6.0 - 6.6 (with limitations on 6.6 features)let vertexLib = try device.makeLibrary(URL: vertexURL)
let fragmentLib = try device.makeLibrary(URL: fragmentURL)
**Key Options**:
| Option | Purpose |
|--------|---------|
| `-o <file>` | 指定输出metallib的路径 |
| `--minimum-gpu-family` | 指定目标GPU系列 |
| `--minimum-os-build-version` | 指定最低OS版本 |
| `--vertex-stage-in` | 分离顶点获取函数 |
| `-dualSourceBlending` | 启用双源混合 |
**Supported Shader Models**: SM 6.0 - 6.6 (with limitations on 6.6 features)Part 3: OpenGL API to Metal API
第三部分:OpenGL API到Metal API
View/Context Setup
视图/上下文配置
| OpenGL | Metal |
|---|---|
| |
| |
| |
| |
| OpenGL | Metal |
|---|---|
| |
| |
| |
| |
Resource Creation
资源创建
| OpenGL | Metal |
|---|---|
| |
| |
| |
| |
| Build-time compilation → |
| |
| OpenGL | Metal |
|---|---|
| |
| |
| |
| |
| 编译期编译 → |
| |
State Management
状态管理
| OpenGL | Metal |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| |
| OpenGL | Metal |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| |
Draw Commands
绘制命令
| OpenGL | Metal |
|---|---|
| |
| |
| |
| |
| OpenGL | Metal |
|---|---|
| |
| |
| |
| |
Primitive Types
图元类型
| OpenGL | Metal |
|---|---|
| |
| |
| |
| |
| |
| N/A (decompose to triangles) |
| OpenGL | Metal |
|---|---|
| |
| |
| |
| |
| |
| 无等效项(需分解为三角形) |
Part 4: Complete Setup Examples
第四部分:完整配置示例
MTKView Setup (Recommended)
MTKView配置(推荐)
swift
import MetalKit
class GameViewController: UIViewController {
var metalView: MTKView!
var renderer: Renderer!
override func viewDidLoad() {
super.viewDidLoad()
// Create Metal view
guard let device = MTLCreateSystemDefaultDevice() else {
fatalError("Metal not supported")
}
metalView = MTKView(frame: view.bounds, device: device)
metalView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
metalView.colorPixelFormat = .bgra8Unorm
metalView.depthStencilPixelFormat = .depth32Float
metalView.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1)
metalView.preferredFramesPerSecond = 60
view.addSubview(metalView)
// Create renderer
renderer = Renderer(metalView: metalView)
metalView.delegate = renderer
}
}
class Renderer: NSObject, MTKViewDelegate {
let device: MTLDevice
let commandQueue: MTLCommandQueue
var pipelineState: MTLRenderPipelineState!
var depthState: MTLDepthStencilState!
var vertexBuffer: MTLBuffer!
init(metalView: MTKView) {
device = metalView.device!
commandQueue = device.makeCommandQueue()!
super.init()
buildPipeline(metalView: metalView)
buildDepthStencil()
buildBuffers()
}
private func buildPipeline(metalView: MTKView) {
let library = device.makeDefaultLibrary()!
let descriptor = MTLRenderPipelineDescriptor()
descriptor.vertexFunction = library.makeFunction(name: "vertexShader")
descriptor.fragmentFunction = library.makeFunction(name: "fragmentShader")
descriptor.colorAttachments[0].pixelFormat = metalView.colorPixelFormat
descriptor.depthAttachmentPixelFormat = metalView.depthStencilPixelFormat
// Vertex descriptor (matches shader's VertexIn struct)
let vertexDescriptor = MTLVertexDescriptor()
vertexDescriptor.attributes[0].format = .float3
vertexDescriptor.attributes[0].offset = 0
vertexDescriptor.attributes[0].bufferIndex = 0
vertexDescriptor.attributes[1].format = .float2
vertexDescriptor.attributes[1].offset = MemoryLayout<SIMD3<Float>>.stride
vertexDescriptor.attributes[1].bufferIndex = 0
vertexDescriptor.layouts[0].stride = MemoryLayout<Vertex>.stride
descriptor.vertexDescriptor = vertexDescriptor
pipelineState = try! device.makeRenderPipelineState(descriptor: descriptor)
}
private func buildDepthStencil() {
let descriptor = MTLDepthStencilDescriptor()
descriptor.depthCompareFunction = .less
descriptor.isDepthWriteEnabled = true
depthState = device.makeDepthStencilState(descriptor: descriptor)
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
// Handle resize
}
func draw(in view: MTKView) {
guard let drawable = view.currentDrawable,
let descriptor = view.currentRenderPassDescriptor,
let commandBuffer = commandQueue.makeCommandBuffer(),
let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else {
return
}
encoder.setRenderPipelineState(pipelineState)
encoder.setDepthStencilState(depthState)
encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vertexCount)
encoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
}swift
import MetalKit
class GameViewController: UIViewController {
var metalView: MTKView!
var renderer: Renderer!
override func viewDidLoad() {
super.viewDidLoad()
// 创建Metal视图
guard let device = MTLCreateSystemDefaultDevice() else {
fatalError("Metal不被支持")
}
metalView = MTKView(frame: view.bounds, device: device)
metalView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
metalView.colorPixelFormat = .bgra8Unorm
metalView.depthStencilPixelFormat = .depth32Float
metalView.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1)
metalView.preferredFramesPerSecond = 60
view.addSubview(metalView)
// 创建渲染器
renderer = Renderer(metalView: metalView)
metalView.delegate = renderer
}
}
class Renderer: NSObject, MTKViewDelegate {
let device: MTLDevice
let commandQueue: MTLCommandQueue
var pipelineState: MTLRenderPipelineState!
var depthState: MTLDepthStencilState!
var vertexBuffer: MTLBuffer!
init(metalView: MTKView) {
device = metalView.device!
commandQueue = device.makeCommandQueue()!
super.init()
buildPipeline(metalView: metalView)
buildDepthStencil()
buildBuffers()
}
private func buildPipeline(metalView: MTKView) {
let library = device.makeDefaultLibrary()!
let descriptor = MTLRenderPipelineDescriptor()
descriptor.vertexFunction = library.makeFunction(name: "vertexShader")
descriptor.fragmentFunction = library.makeFunction(name: "fragmentShader")
descriptor.colorAttachments[0].pixelFormat = metalView.colorPixelFormat
descriptor.depthAttachmentPixelFormat = metalView.depthStencilPixelFormat
// 顶点描述符(与着色器的VertexIn结构体匹配)
let vertexDescriptor = MTLVertexDescriptor()
vertexDescriptor.attributes[0].format = .float3
vertexDescriptor.attributes[0].offset = 0
vertexDescriptor.attributes[0].bufferIndex = 0
vertexDescriptor.attributes[1].format = .float2
vertexDescriptor.attributes[1].offset = MemoryLayout<SIMD3<Float>>.stride
vertexDescriptor.attributes[1].bufferIndex = 0
vertexDescriptor.layouts[0].stride = MemoryLayout<Vertex>.stride
descriptor.vertexDescriptor = vertexDescriptor
pipelineState = try! device.makeRenderPipelineState(descriptor: descriptor)
}
private func buildDepthStencil() {
let descriptor = MTLDepthStencilDescriptor()
descriptor.depthCompareFunction = .less
descriptor.isDepthWriteEnabled = true
depthState = device.makeDepthStencilState(descriptor: descriptor)
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
// 处理视图大小变化
}
func draw(in view: MTKView) {
guard let drawable = view.currentDrawable,
let descriptor = view.currentRenderPassDescriptor,
let commandBuffer = commandQueue.makeCommandBuffer(),
let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else {
return
}
encoder.setRenderPipelineState(pipelineState)
encoder.setDepthStencilState(depthState)
encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vertexCount)
encoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
}CAMetalLayer Setup (Custom Control)
CAMetalLayer配置(自定义控件)
swift
import Metal
import QuartzCore
class MetalLayerView: UIView {
var metalLayer: CAMetalLayer!
var device: MTLDevice!
var commandQueue: MTLCommandQueue!
var displayLink: CADisplayLink?
override class var layerClass: AnyClass { CAMetalLayer.self }
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
private func setup() {
device = MTLCreateSystemDefaultDevice()!
commandQueue = device.makeCommandQueue()!
metalLayer = layer as? CAMetalLayer
metalLayer.device = device
metalLayer.pixelFormat = .bgra8Unorm
metalLayer.framebufferOnly = true
displayLink = CADisplayLink(target: self, selector: #selector(render))
displayLink?.add(to: .main, forMode: .common)
}
override func layoutSubviews() {
super.layoutSubviews()
metalLayer.drawableSize = CGSize(
width: bounds.width * contentScaleFactor,
height: bounds.height * contentScaleFactor
)
}
@objc func render() {
guard let drawable = metalLayer.nextDrawable(),
let commandBuffer = commandQueue.makeCommandBuffer() else {
return
}
let descriptor = MTLRenderPassDescriptor()
descriptor.colorAttachments[0].texture = drawable.texture
descriptor.colorAttachments[0].loadAction = .clear
descriptor.colorAttachments[0].storeAction = .store
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1)
guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else {
return
}
// Draw commands here
encoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
}swift
import Metal
import QuartzCore
class MetalLayerView: UIView {
var metalLayer: CAMetalLayer!
var device: MTLDevice!
var commandQueue: MTLCommandQueue!
var displayLink: CADisplayLink?
override class var layerClass: AnyClass { CAMetalLayer.self }
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
private func setup() {
device = MTLCreateSystemDefaultDevice()!
commandQueue = device.makeCommandQueue()!
metalLayer = layer as? CAMetalLayer
metalLayer.device = device
metalLayer.pixelFormat = .bgra8Unorm
metalLayer.framebufferOnly = true
displayLink = CADisplayLink(target: self, selector: #selector(render))
displayLink?.add(to: .main, forMode: .common)
}
override func layoutSubviews() {
super.layoutSubviews()
metalLayer.drawableSize = CGSize(
width: bounds.width * contentScaleFactor,
height: bounds.height * contentScaleFactor
)
}
@objc func render() {
guard let drawable = metalLayer.nextDrawable(),
let commandBuffer = commandQueue.makeCommandBuffer() else {
return
}
let descriptor = MTLRenderPassDescriptor()
descriptor.colorAttachments[0].texture = drawable.texture
descriptor.colorAttachments[0].loadAction = .clear
descriptor.colorAttachments[0].storeAction = .store
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1)
guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else {
return
}
// 此处添加绘制命令
encoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
}Compute Shader Setup
计算着色器配置
swift
class ComputeProcessor {
let device: MTLDevice
let commandQueue: MTLCommandQueue
var computePipeline: MTLComputePipelineState!
init() {
device = MTLCreateSystemDefaultDevice()!
commandQueue = device.makeCommandQueue()!
let library = device.makeDefaultLibrary()!
let function = library.makeFunction(name: "computeKernel")!
computePipeline = try! device.makeComputePipelineState(function: function)
}
func process(input: MTLBuffer, output: MTLBuffer, count: Int) {
let commandBuffer = commandQueue.makeCommandBuffer()!
let encoder = commandBuffer.makeComputeCommandEncoder()!
encoder.setComputePipelineState(computePipeline)
encoder.setBuffer(input, offset: 0, index: 0)
encoder.setBuffer(output, offset: 0, index: 1)
let threadGroupSize = MTLSize(width: 256, height: 1, depth: 1)
let threadGroups = MTLSize(
width: (count + 255) / 256,
height: 1,
depth: 1
)
encoder.dispatchThreadgroups(threadGroups, threadsPerThreadgroup: threadGroupSize)
encoder.endEncoding()
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
}
}metal
// Compute shader
kernel void computeKernel(
device float* input [[buffer(0)]],
device float* output [[buffer(1)]],
uint id [[thread_position_in_grid]]
) {
output[id] = input[id] * 2.0;
}swift
class ComputeProcessor {
let device: MTLDevice
let commandQueue: MTLCommandQueue
var computePipeline: MTLComputePipelineState!
init() {
device = MTLCreateSystemDefaultDevice()!
commandQueue = device.makeCommandQueue()!
let library = device.makeDefaultLibrary()!
let function = library.makeFunction(name: "computeKernel")!
computePipeline = try! device.makeComputePipelineState(function: function)
}
func process(input: MTLBuffer, output: MTLBuffer, count: Int) {
let commandBuffer = commandQueue.makeCommandBuffer()!
let encoder = commandBuffer.makeComputeCommandEncoder()!
encoder.setComputePipelineState(computePipeline)
encoder.setBuffer(input, offset: 0, index: 0)
encoder.setBuffer(output, offset: 0, index: 1)
let threadGroupSize = MTLSize(width: 256, height: 1, depth: 1)
let threadGroups = MTLSize(
width: (count + 255) / 256,
height: 1,
depth: 1
)
encoder.dispatchThreadgroups(threadGroups, threadsPerThreadgroup: threadGroupSize)
encoder.endEncoding()
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
}
}metal
// 计算着色器
kernel void computeKernel(
device float* input [[buffer(0)]],
device float* output [[buffer(1)]],
uint id [[thread_position_in_grid]]
) {
output[id] = input[id] * 2.0;
}Part 5: Storage Modes & Synchronization
第五部分:存储模式与同步
Buffer Storage Modes
缓冲区存储模式
| Mode | CPU Access | GPU Access | Use Case |
|---|---|---|---|
| Read/Write | Read/Write | Small dynamic data, uniforms |
| None | Read/Write | Static assets, render targets |
| Read/Write | Read/Write | Large buffers with partial updates |
swift
// Shared: CPU and GPU both access (iOS typical)
let uniformBuffer = device.makeBuffer(length: size, options: .storageModeShared)
// Private: GPU only (best for static geometry)
let vertexBuffer = device.makeBuffer(bytes: vertices, length: size, options: .storageModePrivate)
// Managed: Explicit sync (macOS)
#if os(macOS)
let buffer = device.makeBuffer(length: size, options: .storageModeManaged)
// After CPU write:
buffer.didModifyRange(0..<size)
#endif| 模式 | CPU访问权限 | GPU访问权限 | 使用场景 |
|---|---|---|---|
| 可读可写 | 可读可写 | 小型动态数据、 uniforms |
| 无 | 可读可写 | 静态资源、渲染目标 |
| 可读可写 | 可读可写 | 大型缓冲区的部分更新 |
swift
// Shared:CPU和GPU均可访问(iOS常用)
let uniformBuffer = device.makeBuffer(length: size, options: .storageModeShared)
// Private:仅GPU访问(静态几何体的最佳选择)
let vertexBuffer = device.makeBuffer(bytes: vertices, length: size, options: .storageModePrivate)
// Managed:显式同步(macOS)
#if os(macOS)
let buffer = device.makeBuffer(length: size, options: .storageModeManaged)
// CPU写入后:
buffer.didModifyRange(0..<size)
#endifTexture Storage Modes
纹理存储模式
swift
let descriptor = MTLTextureDescriptor.texture2DDescriptor(
pixelFormat: .rgba8Unorm,
width: 1024,
height: 1024,
mipmapped: true
)
// For static textures (loaded once)
descriptor.storageMode = .private
descriptor.usage = [.shaderRead]
// For render targets
descriptor.storageMode = .private
descriptor.usage = [.renderTarget, .shaderRead]
// For CPU-readable (screenshots, readback)
descriptor.storageMode = .shared // iOS
descriptor.storageMode = .managed // macOS
descriptor.usage = [.shaderRead, .shaderWrite]swift
let descriptor = MTLTextureDescriptor.texture2DDescriptor(
pixelFormat: .rgba8Unorm,
width: 1024,
height: 1024,
mipmapped: true
)
// 静态纹理(仅加载一次)
descriptor.storageMode = .private
descriptor.usage = [.shaderRead]
// 渲染目标
descriptor.storageMode = .private
descriptor.usage = [.renderTarget, .shaderRead]
// CPU可读(截图、回读)
descriptor.storageMode = .shared // iOS
descriptor.storageMode = .managed // macOS
descriptor.usage = [.shaderRead, .shaderWrite]Resources
资源
WWDC: 2016-00602, 2018-00604, 2019-00611
Docs: /metal/migrating-opengl-code-to-metal, /metal/shader-converter, /metalkit/mtkview
Skills: axiom-metal-migration, axiom-metal-migration-diag
Last Updated: 2025-12-29
Platforms: iOS 12+, macOS 10.14+, tvOS 12+
Status: Complete shader conversion and API mapping reference
WWDC: 2016-00602, 2018-00604, 2019-00611
文档: /metal/migrating-opengl-code-to-metal, /metal/shader-converter, /metalkit/mtkview
Skills: axiom-metal-migration, axiom-metal-migration-diag
最后更新: 2025-12-29
支持平台: iOS 12+, macOS 10.14+, tvOS 12+
状态: 完整的着色器转换与API映射参考文档