use thiserror::Error;
#[derive(Debug, Error)]
pub enum GpuUnavailable {
#[error("no GPU adapter available: {0}")]
NoAdapter(String),
#[error("GPU adapter found but opening a device failed: {0}")]
NoDevice(String),
}
#[derive(Debug)]
pub struct GpuContext {
device: wgpu::Device,
queue: wgpu::Queue,
info: wgpu::AdapterInfo,
}
impl GpuContext {
pub fn new() -> Result<Self, GpuUnavailable> {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::VULKAN | wgpu::Backends::GL,
flags: wgpu::InstanceFlags::default(),
memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(),
backend_options: wgpu::BackendOptions::default(),
display: None,
});
let adapter = pollster::block_on(instance.request_adapter(
&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: None,
force_fallback_adapter: false,
apply_limit_buckets: false,
},
))
.map_err(|e| GpuUnavailable::NoAdapter(e.to_string()))?;
let info = adapter.get_info();
let (device, queue) = pollster::block_on(adapter.request_device(
&wgpu::DeviceDescriptor {
label: Some("kopitiam-gpu device"),
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::downlevel_defaults(),
experimental_features: wgpu::ExperimentalFeatures::default(),
memory_hints: wgpu::MemoryHints::Performance,
trace: wgpu::Trace::Off,
},
))
.map_err(|e| GpuUnavailable::NoDevice(e.to_string()))?;
let ctx = Self {
device,
queue,
info,
};
eprintln!("[kopitiam-gpu] {}", ctx.describe_backend());
Ok(ctx)
}
pub fn device(&self) -> &wgpu::Device {
&self.device
}
pub fn queue(&self) -> &wgpu::Queue {
&self.queue
}
pub fn adapter_info(&self) -> &wgpu::AdapterInfo {
&self.info
}
pub fn describe_backend(&self) -> String {
let i = &self.info;
let driver = if i.driver.is_empty() && i.driver_info.is_empty() {
String::new()
} else {
format!(" (driver: {} {})", i.driver, i.driver_info)
};
format!(
"GPU: {} [{:?}] {:?}{}",
i.name, i.backend, i.device_type, driver
)
}
}