Files
rustytorch/crates/core/rtx-backend-sycl/src/device.rs
T
2026-03-04 00:08:42 +00:00

375 lines
9.6 KiB
Rust

//! 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<RwLock<HashMap<usize, Arc<SyclDevice>>>> =
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<Self> {
// 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<Self> {
// 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<SyclContext>,
/// Default queue
queue: Arc<SyclQueue>,
}
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<Self> {
// 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<SyclDeviceInfo> {
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<SyclDeviceInfo> {
#[cfg(feature = "sycl-runtime")]
{
// With SYCL runtime, query device properties:
// - device.get_info<sycl::info::device::name>()
// - device.get_info<sycl::info::device::global_mem_size>()
// - device.get_info<sycl::info::device::max_compute_units>()
// 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<SyclBackend> 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<H: std::hash::Hasher>(&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();
}
}