axiom-metal-migration-ref
Complete reference for converting OpenGL/DirectX code to Metal.
Works with
1
total installs
1
this week
767
GitHub stars
0
upvotes
Install Skill
Run in your terminal
1
installs
1
this week
767
stars
Installation Guide
How to use axiom-metal-migration-ref on Cursor
AI-first code editor with Composer
Prerequisites
Before installing skills in Cursor, ensure your development environment meets these requirements:
- ›Cursor installed and configured on your machine
- ›Node.js 16+ with npm — verify with
node --version - ›Active project directory where you want to add
axiom-metal-migration-ref
Run the install command
Execute the skills CLI command in your project's root directory to begin installation:
Fetches axiom-metal-migration-ref from charleswiltgen/axiom and configures it for Cursor.
Select Cursor when prompted
The CLI shows a list of agents. Use arrow keys and space to select Cursor:
Verify installation
Confirm successful installation by checking the skill directory location:
Restart Cursor to activate axiom-metal-migration-ref. Access via /axiom-metal-migration-ref in your agent's command palette.
Security Notice
We perform automated surface-level scans (Gen AI Scanner, Socket, Snyk) during installation. These checks detect common vulnerabilities but do not guarantee complete security. Always review skill source code and verify the publisher's reputation before production use.
Skills execute code in your environment. Always review source, verify the publisher, and test in isolation before production.
Documentation
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 |