1#![warn(missing_docs)]
97#![warn(clippy::all)]
98#![allow(hidden_glob_reexports)]
99
100pub use ringkernel_core::*;
102
103pub use ringkernel_derive::*;
105
106pub use ringkernel_cpu::CpuRuntime;
108
109#[cfg(feature = "cuda")]
111pub use ringkernel_cuda::CudaRuntime;
112
113#[cfg(feature = "wgpu")]
114pub use ringkernel_wgpu::WgpuRuntime;
115
116#[cfg(feature = "metal")]
117pub use ringkernel_metal::MetalRuntime;
118
119pub use ringkernel_codegen as codegen;
121
122use ringkernel_core::error::Result;
124use ringkernel_core::error::RingKernelError;
125use ringkernel_core::runtime::Backend;
126use ringkernel_core::runtime::KernelHandle;
127use ringkernel_core::runtime::KernelId;
128use ringkernel_core::runtime::LaunchOptions;
129use ringkernel_core::runtime::RingKernelRuntime;
130use ringkernel_core::runtime::RuntimeBuilder;
131use ringkernel_core::runtime::RuntimeMetrics;
132
133pub mod prelude {
135 pub use crate::RingKernel;
136 pub use ringkernel_core::prelude::*;
137 pub use ringkernel_derive::*;
138}
139
140#[doc(hidden)]
142pub mod __private {
143 use ringkernel_core::types::KernelMode;
144
145 pub struct KernelRegistration {
147 pub id: &'static str,
149 pub mode: KernelMode,
151 pub grid_size: u32,
153 pub block_size: u32,
155 }
156
157 inventory::collect!(KernelRegistration);
159}
160
161pub struct RingKernel {
166 inner: Box<dyn RingKernelRuntime>,
168}
169
170impl RingKernel {
171 pub fn builder() -> RingKernelBuilder {
173 RingKernelBuilder::new()
174 }
175
176 pub async fn new() -> Result<Self> {
178 Self::builder().build().await
179 }
180
181 pub async fn with_backend(backend: Backend) -> Result<Self> {
183 Self::builder().backend(backend).build().await
184 }
185
186 pub fn backend(&self) -> Backend {
188 self.inner.backend()
189 }
190
191 pub fn is_backend_available(&self, backend: Backend) -> bool {
193 self.inner.is_backend_available(backend)
194 }
195
196 pub async fn launch(&self, kernel_id: &str, options: LaunchOptions) -> Result<KernelHandle> {
198 self.inner.launch(kernel_id, options).await
199 }
200
201 pub fn get_kernel(&self, kernel_id: &KernelId) -> Option<KernelHandle> {
203 self.inner.get_kernel(kernel_id)
204 }
205
206 pub fn list_kernels(&self) -> Vec<KernelId> {
208 self.inner.list_kernels()
209 }
210
211 pub fn metrics(&self) -> RuntimeMetrics {
213 self.inner.metrics()
214 }
215
216 pub async fn shutdown(self) -> Result<()> {
218 self.inner.shutdown().await
219 }
220}
221
222pub struct RingKernelBuilder {
224 inner: RuntimeBuilder,
225}
226
227impl RingKernelBuilder {
228 pub fn new() -> Self {
230 Self {
231 inner: RuntimeBuilder::new(),
232 }
233 }
234
235 pub fn backend(mut self, backend: Backend) -> Self {
237 self.inner = self.inner.backend(backend);
238 self
239 }
240
241 pub fn device(mut self, index: usize) -> Self {
243 self.inner = self.inner.device(index);
244 self
245 }
246
247 pub fn debug(mut self, enable: bool) -> Self {
249 self.inner = self.inner.debug(enable);
250 self
251 }
252
253 pub fn profiling(mut self, enable: bool) -> Self {
255 self.inner = self.inner.profiling(enable);
256 self
257 }
258
259 pub async fn build(self) -> Result<RingKernel> {
261 let backend = self.inner.backend;
262
263 let inner: Box<dyn RingKernelRuntime> = match backend {
264 Backend::Auto => self.build_auto().await?,
265 Backend::Cpu => Box::new(CpuRuntime::new().await?),
266 #[cfg(feature = "cuda")]
267 Backend::Cuda => Box::new(ringkernel_cuda::CudaRuntime::new().await?),
268 #[cfg(not(feature = "cuda"))]
269 Backend::Cuda => {
270 return Err(RingKernelError::BackendUnavailable(
271 "CUDA feature not enabled".to_string(),
272 ))
273 }
274 #[cfg(feature = "metal")]
275 Backend::Metal => Box::new(ringkernel_metal::MetalRuntime::new().await?),
276 #[cfg(not(feature = "metal"))]
277 Backend::Metal => {
278 return Err(RingKernelError::BackendUnavailable(
279 "Metal feature not enabled".to_string(),
280 ))
281 }
282 #[cfg(feature = "wgpu")]
283 Backend::Wgpu => Box::new(ringkernel_wgpu::WgpuRuntime::new().await?),
284 #[cfg(not(feature = "wgpu"))]
285 Backend::Wgpu => {
286 return Err(RingKernelError::BackendUnavailable(
287 "WebGPU feature not enabled".to_string(),
288 ))
289 }
290 };
291
292 Ok(RingKernel { inner })
293 }
294
295 async fn build_auto(self) -> Result<Box<dyn RingKernelRuntime>> {
297 #[cfg(feature = "cuda")]
299 if ringkernel_cuda::is_cuda_available() {
300 tracing::info!("Auto-selected CUDA backend");
301 return Ok(Box::new(ringkernel_cuda::CudaRuntime::new().await?));
302 }
303
304 #[cfg(feature = "metal")]
306 if ringkernel_metal::is_metal_available() {
307 tracing::info!("Auto-selected Metal backend");
308 return Ok(Box::new(ringkernel_metal::MetalRuntime::new().await?));
309 }
310
311 #[cfg(feature = "wgpu")]
313 if ringkernel_wgpu::is_wgpu_available() {
314 tracing::info!("Auto-selected WebGPU backend");
315 return Ok(Box::new(ringkernel_wgpu::WgpuRuntime::new().await?));
316 }
317
318 tracing::info!("Auto-selected CPU backend (no GPU available)");
320 Ok(Box::new(CpuRuntime::new().await?))
321 }
322}
323
324impl Default for RingKernelBuilder {
325 fn default() -> Self {
326 Self::new()
327 }
328}
329
330pub fn registered_kernels() -> Vec<&'static str> {
332 inventory::iter::<__private::KernelRegistration>
333 .into_iter()
334 .map(|r| r.id)
335 .collect()
336}
337
338pub mod availability {
340 pub fn cuda() -> bool {
342 #[cfg(feature = "cuda")]
343 {
344 ringkernel_cuda::is_cuda_available()
345 }
346 #[cfg(not(feature = "cuda"))]
347 {
348 false
349 }
350 }
351
352 pub fn metal() -> bool {
354 #[cfg(feature = "metal")]
355 {
356 ringkernel_metal::is_metal_available()
357 }
358 #[cfg(not(feature = "metal"))]
359 {
360 false
361 }
362 }
363
364 pub fn wgpu() -> bool {
366 #[cfg(feature = "wgpu")]
367 {
368 ringkernel_wgpu::is_wgpu_available()
369 }
370 #[cfg(not(feature = "wgpu"))]
371 {
372 false
373 }
374 }
375
376 pub fn available_backends() -> Vec<super::Backend> {
378 let mut backends = vec![super::Backend::Cpu];
379
380 if cuda() {
381 backends.push(super::Backend::Cuda);
382 }
383 if metal() {
384 backends.push(super::Backend::Metal);
385 }
386 if wgpu() {
387 backends.push(super::Backend::Wgpu);
388 }
389
390 backends
391 }
392}
393
394#[cfg(test)]
395mod tests {
396 use super::*;
397
398 #[tokio::test]
399 async fn test_runtime_creation() {
400 let runtime = RingKernel::new().await.unwrap();
401 let backend = runtime.backend();
404 assert!(
405 backend == Backend::Cuda || backend == Backend::Cpu,
406 "Expected Cuda or Cpu backend, got {:?}",
407 backend
408 );
409 }
410
411 #[tokio::test]
412 async fn test_builder() {
413 let runtime = RingKernel::builder()
414 .backend(Backend::Cpu)
415 .debug(true)
416 .build()
417 .await
418 .unwrap();
419
420 assert_eq!(runtime.backend(), Backend::Cpu);
421 }
422
423 #[tokio::test]
424 async fn test_launch_kernel() {
425 let runtime = RingKernel::new().await.unwrap();
426
427 let kernel = runtime
428 .launch("test_kernel", LaunchOptions::default())
429 .await
430 .unwrap();
431
432 assert_eq!(kernel.id().as_str(), "test_kernel");
433 }
434
435 #[test]
436 fn test_availability() {
437 let backends = availability::available_backends();
439 assert!(backends.contains(&Backend::Cpu));
440 }
441}