//! SYCL Device Management //! //! This module provides device abstraction for Intel GPUs using SYCL/oneAPI. use crate::error::{Result, SyclError}; use parking_lot::RwLock; use rtx_backend::{DeviceId, DeviceOps}; use std::collections::HashMap; use std::sync::Arc; // Forward declaration for DeviceOps implementation use crate::SyclBackend; /// Global device registry static DEVICE_REGISTRY: std::sync::LazyLock>>> = std::sync::LazyLock::new(|| RwLock::new(HashMap::new())); /// Information about an Intel GPU #[derive(Debug, Clone)] pub struct SyclDeviceInfo { /// Device index pub index: usize, /// Device name (e.g., "Intel Arc A770") pub name: String, /// Driver version string pub driver_version: String, /// Total memory in bytes pub total_memory: usize, /// Available memory in bytes pub available_memory: usize, /// Number of execution units (EUs) pub execution_units: usize, /// Maximum work group size pub max_work_group_size: usize, /// Whether device supports FP16 pub supports_fp16: bool, /// Whether device supports FP64 pub supports_fp64: bool, /// Intel GPU architecture pub architecture: IntelArchitecture, } impl Default for SyclDeviceInfo { fn default() -> Self { Self { index: 0, name: "Unknown Intel GPU".to_string(), driver_version: "Unknown".to_string(), total_memory: 0, available_memory: 0, execution_units: 0, max_work_group_size: 256, supports_fp16: true, supports_fp64: false, architecture: IntelArchitecture::Unknown, } } } /// Intel GPU architecture generations #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum IntelArchitecture { /// Unknown architecture Unknown, /// Gen 9 (Skylake/Kaby Lake integrated) Gen9, /// Gen 11 (Ice Lake integrated) Gen11, /// Gen 12 (Tiger Lake/Rocket Lake integrated) Gen12, /// Xe-LP (low power, integrated) XeLP, /// Xe-HPG (high performance graphics, Arc Alchemist) XeHPG, /// Xe-HPC (high performance compute, Ponte Vecchio) XeHPC, /// Xe2 (Battlemage, future) Xe2, } impl IntelArchitecture { /// Get compute capability equivalent (for compatibility) pub fn compute_capability(&self) -> (u32, u32) { match self { Self::Unknown => (0, 0), Self::Gen9 => (9, 0), Self::Gen11 => (11, 0), Self::Gen12 => (12, 0), Self::XeLP => (12, 1), Self::XeHPG => (12, 7), Self::XeHPC => (12, 10), Self::Xe2 => (20, 0), } } } /// SYCL context wrapper #[derive(Debug, Clone)] pub struct SyclContext { /// Context handle (placeholder) _handle: u64, } impl SyclContext { /// Create a new context for a device pub fn new(_device_index: usize) -> Result { // When SYCL runtime is available, this would: // 1. Get SYCL platform (Intel) // 2. Get device from platform // 3. Create context for device Ok(Self { _handle: 0 }) } } /// SYCL queue wrapper (command queue for a device) #[derive(Debug, Clone)] pub struct SyclQueue { /// Queue handle (placeholder) _handle: u64, /// Associated device index device_index: usize, } impl SyclQueue { /// Create a new queue for a device context pub fn new(device_index: usize, _context: &SyclContext) -> Result { // When SYCL runtime is available, this would create // an in-order or out-of-order queue for the device Ok(Self { _handle: 0, device_index, }) } /// Wait for all operations on this queue to complete pub fn wait(&self) { // When SYCL runtime is available: queue.wait() } /// Get device index pub fn device_index(&self) -> usize { self.device_index } } /// Intel GPU device using SYCL #[derive(Debug, Clone)] pub struct SyclDevice { /// Device index index: usize, /// Device information info: SyclDeviceInfo, /// SYCL context context: Arc, /// Default queue queue: Arc, } impl Default for SyclDevice { fn default() -> Self { // Try to create device 0, fall back to stub Self::new(0).unwrap_or_else(|_| Self::stub()) } } impl SyclDevice { /// Create a new SYCL device pub fn new(index: usize) -> Result { // Check registry first { let registry = DEVICE_REGISTRY.read(); if let Some(device) = registry.get(&index) { return Ok((**device).clone()); } } // Create new device let info = Self::query_device_info(index)?; let context = Arc::new(SyclContext::new(index)?); let queue = Arc::new(SyclQueue::new(index, &context)?); let device = Self { index, info, context, queue, }; // Store in registry { let mut registry = DEVICE_REGISTRY.write(); registry.insert(index, Arc::new(device.clone())); } Ok(device) } /// Create a stub device (for when SYCL is not available) fn stub() -> Self { Self { index: 0, info: SyclDeviceInfo::default(), context: Arc::new(SyclContext { _handle: 0 }), queue: Arc::new(SyclQueue { _handle: 0, device_index: 0, }), } } /// Check if SYCL runtime is available pub fn is_available() -> bool { #[cfg(feature = "sycl-runtime")] { // When SYCL runtime feature is enabled, check for Intel GPUs Self::device_count() > 0 } #[cfg(not(feature = "sycl-runtime"))] { false } } /// Get number of available Intel GPUs pub fn device_count() -> usize { #[cfg(feature = "sycl-runtime")] { // Query Intel GPU count via SYCL // This would iterate over platforms and find Intel GPU devices 0 } #[cfg(not(feature = "sycl-runtime"))] { 0 } } /// Get information about all available devices pub fn available_devices() -> Vec { let count = Self::device_count(); (0..count) .filter_map(|i| Self::query_device_info(i).ok()) .collect() } /// Query device information fn query_device_info(_index: usize) -> Result { #[cfg(feature = "sycl-runtime")] { // With SYCL runtime, query device properties: // - device.get_info() // - device.get_info() // - device.get_info() // etc. Err(SyclError::NotImplemented( "SYCL device query not yet implemented".to_string(), )) } #[cfg(not(feature = "sycl-runtime"))] { Err(SyclError::RuntimeNotAvailable) } } /// Get device index pub fn index(&self) -> usize { self.index } /// Get device name pub fn name(&self) -> &str { &self.info.name } /// Get device information pub fn info(&self) -> &SyclDeviceInfo { &self.info } /// Get the default queue for this device pub fn queue(&self) -> &SyclQueue { &self.queue } /// Get the context for this device pub fn context(&self) -> &SyclContext { &self.context } /// Wait for all operations to complete pub fn synchronize(&self) { self.queue.wait(); } /// Get architecture pub fn architecture(&self) -> IntelArchitecture { self.info.architecture } } impl DeviceOps for SyclDevice { fn id(&self) -> DeviceId { DeviceId::Sycl(self.index) } fn memory_capacity(&self) -> usize { self.info.total_memory } fn memory_available(&self) -> usize { self.info .available_memory .max(self.info.total_memory * 8 / 10) } fn compute_capability(&self) -> Option<(u32, u32)> { Some(self.info.architecture.compute_capability()) } fn synchronize(&self) { self.queue.wait(); } fn is_available(&self) -> bool { Self::is_available() } } impl std::hash::Hash for SyclDevice { fn hash(&self, state: &mut H) { self.index.hash(state); } } impl PartialEq for SyclDevice { fn eq(&self, other: &Self) -> bool { self.index == other.index } } impl Eq for SyclDevice {} #[cfg(test)] mod tests { use super::*; #[test] fn test_device_stub() { let device = SyclDevice::stub(); assert_eq!(device.index(), 0); assert_eq!(device.info().architecture, IntelArchitecture::Unknown); } #[test] fn test_architecture_compute_capability() { assert_eq!(IntelArchitecture::XeHPG.compute_capability(), (12, 7)); assert_eq!(IntelArchitecture::XeHPC.compute_capability(), (12, 10)); assert_eq!(IntelArchitecture::Gen12.compute_capability(), (12, 0)); } #[test] fn test_device_info_default() { let info = SyclDeviceInfo::default(); assert!(info.supports_fp16); assert!(!info.supports_fp64); } #[test] fn test_is_available() { // Should not panic let _ = SyclDevice::is_available(); let _ = SyclDevice::device_count(); } }