axiom-metal-migration-ref▌
charleswiltgen/axiom · updated May 1, 2026
MDX-style export adds YAML metadata + attribution linking explainx.ai and this canonical listing URL.
Complete reference for converting OpenGL/DirectX code to Metal.
Metal Migration Reference
Complete reference for converting OpenGL/DirectX code to 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
Part 1: GLSL to MSL Conversion
Type Mappings
| GLSL | MSL | Notes |
|---|---|---|
void |
void |
|
bool |
bool |
|
int |
int |
32-bit signed |
uint |
uint |
32-bit unsigned |
float |
float |
32-bit |
double |
N/A | Use float (no 64-bit float in MSL) |
vec2 |
float2 |
|
vec3 |
float3 |
|
vec4 |
float4 |
|
ivec2 |
int2 |
|
ivec3 |
int3 |
|
ivec4 |
int4 |
|
uvec2 |
uint2 |
|
uvec3 |
uint3 |
|
uvec4 |
uint4 |
|
bvec2 |
bool2 |
|
bvec3 |
bool3 |
|
bvec4 |
bool4 |
|
mat2 |
float2x2 |
|
mat3 |
float3x3 |
|
mat4 |
float4x4 |
|
mat2x3 |
float2x3 |
Columns x Rows |
mat3x4 |
float3x4 |
|
sampler2D |
texture2d<float> + sampler |
Separate in MSL |
sampler3D |
texture3d<float> + sampler |
|
samplerCube |
texturecube<float> + sampler |
|
sampler2DArray |
texture2d_array<float> + sampler |
|
sampler2DShadow |
depth2d<float> + sampler |
Built-in Variable Mappings
| GLSL | MSL | Stage |
|---|---|---|
gl_Position |
Return [[position]] |
Vertex |
gl_PointSize |
Return [[point_size]] |
Vertex |
gl_VertexID |
[[vertex_id]] parameter |
Vertex |
gl_InstanceID |
[[instance_id]] parameter |
Vertex |
gl_FragCoord |
[[position]] parameter |
Fragment |
gl_FrontFacing |
[[front_facing]] parameter |
Fragment |
gl_PointCoord |
[[point_coord]] parameter |
Fragment |
gl_FragDepth |
Return [[depth(any)]] |
Fragment |
gl_SampleID |
[[sample_id]] parameter |
Fragment |
gl_SamplePosition |
[[sample_position]] parameter |
Fragment |
Function Mappings
| GLSL | MSL | Notes |
|---|---|---|
texture(sampler, uv) |
tex.sample(sampler, uv) |
Method on texture |
textureLod(sampler, uv, lod) |
tex.sample(sampler, uv, level(lod)) |
|
textureGrad(sampler, uv, ddx, ddy) |
tex.sample(sampler, uv, gradient2d(ddx, ddy)) |
|
texelFetch(sampler, coord, lod) |
tex.read(coord, lod) |
Integer coords |
textureSize(sampler, lod) |
tex.get_width(lod), tex.get_height(lod) |
Separate calls |
dFdx(v) |
dfdx(v) |
|
dFdy(v) |
dfdy(v) |
|
fwidth(v) |
fwidth(v) |
Same |
mix(a, b, t) |
mix(a, b, t) |
Same |
clamp(v, lo, hi) |
clamp(v, lo, hi) |
Same |
smoothstep(e0, e1, x) |
smoothstep(e0, e1, x) |
Same |
step(edge, x) |
step(edge, x) |
Same |
mod(x, y) |
fmod(x, y) |
Different name |
fract(x) |
fract(x) |
Same |
inversesqrt(x) |
rsqrt(x) |
Different name |
atan(y, x) |
atan2(y, x) |
Different name |
Shader Structure Conversion
GLSL Vertex Shader:
#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:
#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:
#version 300 es
precision highp float;
in vec2 vTexCoord;
uniform sampler2D uTexture;
out vec4 fragColor;
void main() {
fragColor = texture(uTexture, vTexCoord);
}
MSL Fragment Shader:
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 |
|---|---|
lowp float |
half (16-bit) |
mediump float |
half (16-bit) |
highp float |
float (32-bit) |
lowp int |
short (16-bit) |
mediump int |
short (16-bit) |
highp int |
int (32-bit) |
Buffer Alignment (Critical)
GLSL/C assumes:
vec3: 12 bytes, any alignmentvec4: 16 bytes
MSL requires:
float3: 12 bytes storage, 16-byte alignedfloat4: 16 bytes storage, 16-byte aligned
Solution: Use simd types in Swift for CPU-GPU shared structs:
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):
struct VertexPacked {
packed_float3 position; // 12 bytes, no padding
packed_float2 texCoord; // 8 bytes
};
Part 2: HLSL to MSL Conversion
Type Mappings
| HLSL | MSL | Notes |
|---|---|---|
float |
float |
|
float2 |
float2 |
|
float3 |
float3 |
|
float4 |
float4 |
|
half |
half |
|
int |
int |
|
uint |
uint |
|
bool |
bool |
|
float2x2 |
float2x2 |
|
float3x3 |
float3x3 |
|
float4x4 |
float4x4 |
|
Texture2D |
texture2d<float> |
|
Texture3D |
texture3d<float> |
|
TextureCube |
texturecube<float> |
|
SamplerState |
sampler |
|
RWTexture2D |
texture2d<float, access::read_write> |
|
RWBuffer |
device float* [[buffer(n)]] |
|
StructuredBuffer |
constant T* [[buffer(n)]] |
|
RWStructuredBuffer |
device T* [[buffer(n)]] |
Semantic Mappings
| HLSL Semantic | MSL Attribute |
|---|---|
SV_Position |
[[position]] |
SV_Target0 |
Return value / [[color(0)]] |
SV_Target1 |
[[color(1)]] |
SV_Depth |
[[depth(any)]] |
SV_VertexID |
[[vertex_id]] |
SV_InstanceID |
[[instance_id]] |
SV_IsFrontFace |
[[front_facing]] |
SV_SampleIndex |
[[sample_id]] |
SV_PrimitiveID |
[[primitive_id]] |
SV_DispatchThreadID |
[[thread_position_in_grid]] |
SV_GroupThreadID |
[[thread_position_in_threadgroup]] |
SV_GroupID |
[[threadgroup_position_in_grid]] |
SV_GroupIndex |
[[thread_index_in_threadgroup]] |
Function Mappings
| HLSL | MSL | Notes |
|---|---|---|
tex.Sample(samp, uv) |
tex.sample(samp, uv) |
Lowercase |
tex.SampleLevel(samp, uv, lod) |
tex.sample(samp, uv, level(lod)) |
|
tex.SampleGrad(samp, uv, ddx, ddy) |
tex.sample(samp, uv, gradient2d(ddx, ddy)) |
|
tex.Load(coord) |
tex.read(coord.xy, coord.z) |
Split coord |
mul(a, b) |
a * b |
Operator |
saturate(x) |
saturate(x) |
Same |
lerp(a, b, t) |
mix(a, b, t) |
Different name |
frac(x) |
fract(x) |
Different name |
ddx(v) |
dfdx(v) |
Different name |
ddy(v) |
dfdy(v) |
Different name |
clip(x) |
if (x < 0) discard_fragment() |
Manual |
discard |
discard_fragment() |
Function call |
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:
# 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
# Step 2: Convert DXIL to Metal library
metal-shaderconverter vertex.dxil -o vertex.metallib
metal-shaderconverter fragment.dxil -o fragment.metallib
# 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)
Part 3: OpenGL API to Metal API
View/Context Setup
| OpenGL | Metal |
|---|---|
NSOpenGLView |
MTKView |
GLKView |
MTKView |
EAGLContext |
MTLDevice + MTLCommandQueue |
CGLContextObj |
MTLDevice |
Resource Creation
| OpenGL | Metal |
|---|---|
glGenBuffers + glBufferData |
device.makeBuffer(bytes:length:options:) |
glGenTextures + glTexImage2D |
device.makeTexture(descriptor:) + texture.replace(region:...) |
glGenFramebuffers |
MTLRenderPassDescriptor |
glGenVertexArrays |
MTLVertexDescriptor |
glCreateShader + glCompileShader |
Build-time compilation → MTLLibrary |
glCreateProgram + glLinkProgram |
MTLRenderPipelineDescriptor → MTLRenderPipelineState |