2 stable releases

Uses new Rust 2024

1.0.1 Feb 3, 2026

#65 in Graphics APIs


Used in 2 crates

MIT/Apache

2MB
46K SLoC

metal.rs

Documentation Version

Rust bindings to Apple's Metal framework. This is a direct conversion of metal-cpp to Rust, preserving the same API structure and naming conventions where possible.

94% of this codebase was converted from C++ to Rust by Claude, Anthropic's AI assistant.

Requirements

  • macOS or iOS with Metal-capable hardware
  • Rust 1.85+

Crates

Crate Description
mtl-gpu Safe Metal bindings - devices, resources, commands, pipelines, encoders
mtl-sys Low-level Objective-C FFI (zero external dependencies)
mtl-foundation Foundation framework bindings (NSObject, NSString, NSArray, etc.)
mtl-fx MetalFX bindings (SpatialScaler, TemporalScaler, FrameInterpolator)
mtl-quartz-core CAMetalLayer/CAMetalDrawable for display integration

Usage

Add to your Cargo.toml:

[dependencies]
mtl-gpu = { git = "https://github.com/jasonherald/metal.rs" }

Example: Query device info

use mtl_gpu::device;

fn main() {
    let device = device::system_default().expect("No Metal device");

    println!("Device: {}", device.name());
    println!("Unified memory: {}", device.has_unified_memory());
    println!("Max buffer length: {} bytes", device.max_buffer_length());
}

Example: Run a compute shader

use mtl_gpu::{device, ComputeCommandEncoder, ResourceOptions, Size};

const SHADER: &str = r#"
#include <metal_stdlib>
using namespace metal;

kernel void double_values(device float* data [[buffer(0)]], uint id [[thread_position_in_grid]]) {
    data[id] = data[id] * 2.0;
}
"#;

fn main() {
    let device = device::system_default().expect("No Metal device");

    // Create buffer with data
    let data: Vec<f32> = (0..16).map(|i| i as f32).collect();
    let bytes: &[u8] = unsafe {
        std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4)
    };
    let buffer = device.new_buffer_with_bytes(bytes, ResourceOptions::STORAGE_MODE_SHARED).unwrap();

    // Compile shader and create pipeline
    let library = device.new_library_with_source(SHADER, None).unwrap();
    let function = library.new_function_with_name("double_values").unwrap();
    let pipeline = device.new_compute_pipeline_state_with_function(&function).unwrap();

    // Encode and dispatch
    let queue = device.new_command_queue().unwrap();
    let cmd_buffer = queue.command_buffer().unwrap();
    let encoder = unsafe { ComputeCommandEncoder::from_raw(cmd_buffer.compute_command_encoder()) }.unwrap();

    encoder.set_compute_pipeline_state(&pipeline);
    encoder.set_buffer(&buffer, 0, 0);
    encoder.dispatch_threadgroups(Size::new(1, 1, 1), Size::new(16, 1, 1));
    encoder.end_encoding();

    cmd_buffer.commit();
    cmd_buffer.wait_until_completed();

    // Read results
    let result = unsafe { std::slice::from_raw_parts(buffer.contents().unwrap() as *const f32, 16) };
    println!("{:?}", result); // [0.0, 2.0, 4.0, 6.0, ...]
}

Running Examples

cargo run --example 01_device_info
cargo run --example 02_buffer_compute
cargo run --example 03_render_triangle
# ... examples 04-10 cover blit operations, async completion, and Metal 4 features

API Coverage

  • 253/253 classes (100%)
  • 2963/3175 methods (93%)
  • 125/125 enums (100%)

See docs/API_COVERAGE.md for details.

License

MIT OR Apache-2.0

Dependencies