375 lines
9.6 KiB
Rust
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();
|
|
}
|
|
}
|