use super::capabilities::{GpuBackend, GpuCapabilities, GpuLimits};
use super::error::{GpuError, GpuResult};
#[derive(Debug, Clone)]
pub struct GpuDeviceConfig {
pub high_performance: bool,
pub required_features: Vec<String>,
pub min_buffer_size: u64,
pub validation: bool,
}
impl Default for GpuDeviceConfig {
fn default() -> Self {
Self {
high_performance: true,
required_features: Vec::new(),
min_buffer_size: 128 * 1024 * 1024, validation: cfg!(debug_assertions),
}
}
}
impl GpuDeviceConfig {
#[must_use]
pub fn for_inference() -> Self {
Self {
high_performance: true,
required_features: Vec::new(),
min_buffer_size: 256 * 1024 * 1024, validation: false,
}
}
#[must_use]
pub fn for_development() -> Self {
Self {
high_performance: false,
required_features: Vec::new(),
min_buffer_size: 64 * 1024 * 1024,
validation: true,
}
}
#[must_use]
pub fn with_min_buffer_size(mut self, size: u64) -> Self {
self.min_buffer_size = size;
self
}
#[must_use]
pub fn with_validation(mut self, enable: bool) -> Self {
self.validation = enable;
self
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct GpuDevice {
capabilities: GpuCapabilities,
config: GpuDeviceConfig,
initialized: bool,
#[cfg(feature = "webgpu")]
device: Option<wgpu::Device>,
#[cfg(feature = "webgpu")]
queue: Option<wgpu::Queue>,
}
impl GpuDevice {
pub fn new() -> GpuResult<Self> {
Self::with_config(GpuDeviceConfig::default())
}
pub fn with_config(config: GpuDeviceConfig) -> GpuResult<Self> {
let capabilities = Self::detect_capabilities()?;
if !capabilities.is_available() {
return Err(GpuError::NotAvailable);
}
if capabilities.limits.max_buffer_size < config.min_buffer_size {
return Err(GpuError::InvalidBufferSize {
requested: config.min_buffer_size,
max: capabilities.limits.max_buffer_size,
});
}
Ok(Self {
capabilities,
config,
initialized: true,
#[cfg(feature = "webgpu")]
device: None,
#[cfg(feature = "webgpu")]
queue: None,
})
}
#[must_use]
pub fn simulated() -> Self {
Self {
capabilities: GpuCapabilities {
name: "Simulated GPU".to_string(),
vendor: "Test".to_string(),
backend: GpuBackend::None,
limits: GpuLimits::default(),
supports_f16: true,
supports_timestamp_query: false,
vram_bytes: 4 * 1024 * 1024 * 1024,
},
config: GpuDeviceConfig::default(),
initialized: false,
#[cfg(feature = "webgpu")]
device: None,
#[cfg(feature = "webgpu")]
queue: None,
}
}
#[allow(clippy::unnecessary_wraps)]
fn detect_capabilities() -> GpuResult<GpuCapabilities> {
#[cfg(not(feature = "webgpu"))]
{
Ok(GpuCapabilities::default())
}
#[cfg(feature = "webgpu")]
{
Ok(GpuCapabilities::default())
}
}
#[must_use]
pub fn is_available() -> bool {
#[cfg(feature = "webgpu")]
{
false
}
#[cfg(not(feature = "webgpu"))]
{
false
}
}
#[must_use]
pub fn capabilities(&self) -> &GpuCapabilities {
&self.capabilities
}
#[must_use]
pub fn config(&self) -> &GpuDeviceConfig {
&self.config
}
#[must_use]
pub fn is_initialized(&self) -> bool {
self.initialized
}
#[must_use]
pub fn backend(&self) -> GpuBackend {
self.capabilities.backend
}
#[must_use]
pub fn supports_compute(&self) -> bool {
self.capabilities.supports_compute()
}
#[must_use]
pub fn supports_f16(&self) -> bool {
self.capabilities.supports_f16
}
#[must_use]
pub fn max_buffer_size(&self) -> u64 {
self.capabilities.limits.max_buffer_size
}
#[must_use]
pub fn optimal_workgroup_size(&self, elements: u32) -> u32 {
self.capabilities.limits.optimal_workgroup_size(elements)
}
#[must_use]
pub fn info(&self) -> String {
format!(
"GpuDevice: {} ({}) - {}",
self.capabilities.name,
self.capabilities.backend,
if self.initialized {
"Ready"
} else {
"Not initialized"
}
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_device_config_default() {
let config = GpuDeviceConfig::default();
assert!(config.high_performance);
assert!(config.required_features.is_empty());
assert_eq!(config.min_buffer_size, 128 * 1024 * 1024);
}
#[test]
fn test_device_config_for_inference() {
let config = GpuDeviceConfig::for_inference();
assert!(config.high_performance);
assert!(!config.validation);
assert_eq!(config.min_buffer_size, 256 * 1024 * 1024);
}
#[test]
fn test_device_config_for_development() {
let config = GpuDeviceConfig::for_development();
assert!(!config.high_performance);
assert!(config.validation);
}
#[test]
fn test_device_config_builder() {
let config = GpuDeviceConfig::default()
.with_min_buffer_size(512 * 1024 * 1024)
.with_validation(true);
assert_eq!(config.min_buffer_size, 512 * 1024 * 1024);
assert!(config.validation);
}
#[test]
fn test_gpu_device_simulated() {
let device = GpuDevice::simulated();
assert!(!device.is_initialized());
assert_eq!(device.capabilities().name, "Simulated GPU");
assert!(device.capabilities().supports_f16);
}
#[test]
fn test_gpu_device_capabilities() {
let device = GpuDevice::simulated();
let caps = device.capabilities();
assert_eq!(caps.vram_bytes, 4 * 1024 * 1024 * 1024);
}
#[test]
fn test_gpu_device_config() {
let device = GpuDevice::simulated();
let config = device.config();
assert!(config.high_performance);
}
#[test]
fn test_gpu_device_backend() {
let device = GpuDevice::simulated();
assert_eq!(device.backend(), GpuBackend::None);
}
#[test]
fn test_gpu_device_supports_compute() {
let device = GpuDevice::simulated();
assert!(!device.supports_compute()); }
#[test]
fn test_gpu_device_supports_f16() {
let device = GpuDevice::simulated();
assert!(device.supports_f16());
}
#[test]
fn test_gpu_device_max_buffer_size() {
let device = GpuDevice::simulated();
assert_eq!(device.max_buffer_size(), 256 * 1024 * 1024);
}
#[test]
fn test_gpu_device_optimal_workgroup_size() {
let device = GpuDevice::simulated();
assert_eq!(device.optimal_workgroup_size(100), 128);
assert_eq!(device.optimal_workgroup_size(256), 256);
}
#[test]
fn test_gpu_device_info() {
let device = GpuDevice::simulated();
let info = device.info();
assert!(info.contains("Simulated GPU"));
assert!(info.contains("Not initialized"));
}
#[test]
fn test_gpu_device_is_available() {
assert!(!GpuDevice::is_available());
}
}