ringkernel_metal/
lib.rs

1//! Metal Backend for RingKernel
2//!
3//! This crate provides Apple Metal GPU support for RingKernel.
4//! Supports macOS, iOS, and Apple Silicon.
5//!
6//! # Features
7//!
8//! - Event-driven kernel execution (Metal compute shaders)
9//! - MSL (Metal Shading Language) support
10//! - Apple Silicon optimization
11//! - Unified memory architecture support
12//!
13//! # Limitations
14//!
15//! - No true persistent kernels (Metal doesn't support cooperative groups)
16//! - macOS/iOS only
17//!
18//! # Example
19//!
20//! ```ignore
21//! use ringkernel_metal::MetalRuntime;
22//!
23//! #[tokio::main]
24//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
25//!     let runtime = MetalRuntime::new().await?;
26//!     let kernel = runtime.launch("compute", Default::default()).await?;
27//!     kernel.activate().await?;
28//!     Ok(())
29//! }
30//! ```
31
32#![warn(missing_docs)]
33
34#[cfg(all(target_os = "macos", feature = "metal"))]
35mod device;
36#[cfg(all(target_os = "macos", feature = "metal"))]
37mod kernel;
38#[cfg(all(target_os = "macos", feature = "metal"))]
39mod memory;
40#[cfg(all(target_os = "macos", feature = "metal"))]
41mod runtime;
42
43#[cfg(all(target_os = "macos", feature = "metal"))]
44pub use device::MetalDevice;
45#[cfg(all(target_os = "macos", feature = "metal"))]
46pub use kernel::MetalKernel;
47#[cfg(all(target_os = "macos", feature = "metal"))]
48pub use memory::MetalBuffer;
49#[cfg(all(target_os = "macos", feature = "metal"))]
50pub use runtime::MetalRuntime;
51
52// Stub implementation when Metal is not available
53#[cfg(not(all(target_os = "macos", feature = "metal")))]
54mod stub {
55    use async_trait::async_trait;
56    use ringkernel_core::error::{Result, RingKernelError};
57    use ringkernel_core::runtime::{
58        Backend, KernelHandle, KernelId, LaunchOptions, RingKernelRuntime, RuntimeMetrics,
59    };
60
61    /// Stub Metal runtime when not on macOS or Metal feature disabled.
62    pub struct MetalRuntime;
63
64    impl MetalRuntime {
65        /// Create fails when Metal is not available.
66        pub async fn new() -> Result<Self> {
67            Err(RingKernelError::BackendUnavailable(
68                "Metal not available (requires macOS with metal feature)".to_string(),
69            ))
70        }
71    }
72
73    #[async_trait]
74    impl RingKernelRuntime for MetalRuntime {
75        fn backend(&self) -> Backend {
76            Backend::Metal
77        }
78
79        fn is_backend_available(&self, _backend: Backend) -> bool {
80            false
81        }
82
83        async fn launch(&self, _kernel_id: &str, _options: LaunchOptions) -> Result<KernelHandle> {
84            Err(RingKernelError::BackendUnavailable("Metal".to_string()))
85        }
86
87        fn get_kernel(&self, _kernel_id: &KernelId) -> Option<KernelHandle> {
88            None
89        }
90
91        fn list_kernels(&self) -> Vec<KernelId> {
92            vec![]
93        }
94
95        fn metrics(&self) -> RuntimeMetrics {
96            RuntimeMetrics::default()
97        }
98
99        async fn shutdown(&self) -> Result<()> {
100            Ok(())
101        }
102    }
103}
104
105#[cfg(not(all(target_os = "macos", feature = "metal")))]
106pub use stub::MetalRuntime;
107
108/// Check if Metal is available at runtime.
109pub fn is_metal_available() -> bool {
110    #[cfg(all(target_os = "macos", feature = "metal"))]
111    {
112        metal::Device::system_default().is_some()
113    }
114    #[cfg(not(all(target_os = "macos", feature = "metal")))]
115    {
116        false
117    }
118}
119
120/// MSL (Metal Shading Language) kernel template.
121pub const RING_KERNEL_MSL_TEMPLATE: &str = r#"
122//
123// RingKernel Metal Shading Language Template
124// Generated by ringkernel-metal
125//
126
127#include <metal_stdlib>
128using namespace metal;
129
130// Control block structure (128 bytes)
131struct ControlBlock {
132    atomic_uint is_active;
133    atomic_uint should_terminate;
134    atomic_uint has_terminated;
135    uint _pad1;
136
137    atomic_ulong messages_processed;
138    atomic_ulong messages_in_flight;
139
140    atomic_ulong input_head;
141    atomic_ulong input_tail;
142    atomic_ulong output_head;
143    atomic_ulong output_tail;
144
145    uint input_capacity;
146    uint output_capacity;
147    uint input_mask;
148    uint output_mask;
149
150    // HLC state
151    atomic_ulong hlc_physical;
152    atomic_ulong hlc_logical;
153
154    atomic_uint last_error;
155    atomic_uint error_count;
156
157    uchar _reserved[16];
158};
159
160// Message header structure (256 bytes)
161struct MessageHeader {
162    ulong magic;
163    uint version;
164    uint flags;
165    ulong message_id;
166    ulong correlation_id;
167    ulong source_kernel;
168    ulong dest_kernel;
169    ulong message_type;
170    uchar priority;
171    uchar _reserved1[7];
172    ulong payload_size;
173    uint checksum;
174    uint _reserved2;
175    // HLC timestamp (24 bytes)
176    ulong ts_physical;
177    ulong ts_logical;
178    ulong ts_node_id;
179    // Deadline
180    ulong deadline_physical;
181    ulong deadline_logical;
182    ulong deadline_node_id;
183    uchar _reserved3[104];
184};
185
186// Kernel entry point
187kernel void ring_kernel_main(
188    device ControlBlock* control [[buffer(0)]],
189    device uchar* input_queue [[buffer(1)]],
190    device uchar* output_queue [[buffer(2)]],
191    device uchar* shared_state [[buffer(3)]],
192    uint thread_id [[thread_position_in_threadgroup]],
193    uint threadgroup_id [[threadgroup_position_in_grid]],
194    uint threads_per_group [[threads_per_threadgroup]]
195) {
196    // Check if kernel should process
197    uint is_active = atomic_load_explicit(&control->is_active, memory_order_acquire);
198    if (is_active == 0) {
199        return;
200    }
201
202    // Check termination
203    uint should_term = atomic_load_explicit(&control->should_terminate, memory_order_acquire);
204    if (should_term != 0) {
205        if (thread_id == 0 && threadgroup_id == 0) {
206            atomic_store_explicit(&control->has_terminated, 1, memory_order_release);
207        }
208        return;
209    }
210
211    // User kernel code will be inserted here
212    // USER_KERNEL_CODE
213
214    // Update message counter
215    if (thread_id == 0 && threadgroup_id == 0) {
216        atomic_fetch_add_explicit(&control->messages_processed, 1, memory_order_relaxed);
217    }
218}
219"#;