mtl-sys 1.0.1

Low-level Objective-C runtime bindings for Metal
docs.rs failed to build mtl-sys-1.0.1
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

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