use std::fmt;
#[derive(Debug, Clone)]
pub enum GpuAcquireError {
NoAdapter(String),
RequestDevice(String),
}
impl fmt::Display for GpuAcquireError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NoAdapter(msg) => write!(
f,
"no wgpu adapter is available on this system (no compatible GPU/driver \
found via any backend): {msg}"
),
Self::RequestDevice(msg) => write!(f, "failed to acquire a wgpu device: {msg}"),
}
}
}
impl std::error::Error for GpuAcquireError {}
pub const MEGAKERNEL_STORAGE_BUFFERS: u32 = 11;
pub const WAVEFRONT_STORAGE_BUFFERS: u32 = 18;
const MAX_REQUESTED_STORAGE_BUFFERS_PER_STAGE: u32 = 32;
pub struct GpuContext {
pub instance: wgpu::Instance,
pub adapter: wgpu::Adapter,
pub device: wgpu::Device,
pub queue: wgpu::Queue,
}
impl GpuContext {
pub fn acquire() -> Result<Self, GpuAcquireError> {
pollster::block_on(Self::acquire_async())
}
#[cfg_attr(
target_arch = "wasm32",
allow(
clippy::future_not_send,
reason = "wgpu's web backend futures are inherently !Send (browser-side Rc \
handles); wasm32-unknown-unknown has no second thread regardless"
)
)]
pub(crate) async fn acquire_async() -> Result<Self, GpuAcquireError> {
let instance =
wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
force_fallback_adapter: false,
compatible_surface: None,
apply_limit_buckets: false,
})
.await
.map_err(|e| GpuAcquireError::NoAdapter(format!("{e:?}")))?;
let adapter_limits = adapter.limits();
let required_limits = wgpu::Limits {
max_storage_buffers_per_shader_stage: adapter_limits
.max_storage_buffers_per_shader_stage
.clamp(
wgpu::Limits::default().max_storage_buffers_per_shader_stage,
MAX_REQUESTED_STORAGE_BUFFERS_PER_STAGE,
),
..Default::default()
};
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor {
label: Some("indicatrix gpu-feature compute device"),
required_limits,
..Default::default()
})
.await
.map_err(|e| GpuAcquireError::RequestDevice(format!("{e:?}")))?;
device.set_device_lost_callback(|reason, message| {
tracing::warn!(?reason, %message, "wgpu device lost");
});
Ok(Self {
instance,
adapter,
device,
queue,
})
}
}