use burn_dispatch::devices::wgpu as runtime;
pub use runtime::{MemoryConfiguration, WgpuBackend, WgpuInitError, WgpuSetup, wgpu};
use crate::{Device, DeviceKind, device::wgpu_device};
impl Device {
pub fn wgpu_options() -> WgpuOptions {
WgpuOptions::default()
}
}
#[derive(Default)]
struct Options {
memory: Option<MemoryConfiguration>,
tasks_max: Option<usize>,
}
impl Options {
fn resolve(self) -> Result<runtime::RuntimeOptions, WgpuInitError> {
let mut options = runtime::RuntimeOptions::with_tasks_max(self.tasks_max)?;
if let Some(memory) = self.memory {
options.memory_config = memory;
}
Ok(options)
}
}
#[must_use = "wgpu options do nothing until initialized"]
pub struct WgpuOptions {
kind: DeviceKind,
api: WgpuBackend,
selection_explicit: bool,
options: Options,
}
impl Default for WgpuOptions {
fn default() -> Self {
Self {
kind: DeviceKind::DefaultDevice,
api: WgpuBackend::Auto,
selection_explicit: false,
options: Options::default(),
}
}
}
impl WgpuOptions {
pub fn device_kind(mut self, kind: DeviceKind) -> Self {
self.kind = kind;
self.selection_explicit = true;
self
}
pub fn graphics_api(mut self, api: WgpuBackend) -> Self {
self.api = api;
self.selection_explicit = true;
self
}
pub fn memory_config(mut self, config: MemoryConfiguration) -> Self {
self.options.memory = Some(config);
self
}
pub fn tasks_max(mut self, tasks: usize) -> Self {
self.options.tasks_max = Some(tasks);
self
}
pub fn setup(self, setup: WgpuSetup) -> WgpuSetupOptions {
WgpuSetupOptions {
setup,
options: self.options,
selection_explicit: self.selection_explicit,
}
}
fn into_request(self) -> Result<(runtime::WgpuDevice, runtime::RuntimeOptions), WgpuInitError> {
if matches!(self.kind, DeviceKind::Existing(_)) {
return Err(WgpuInitError::InvalidConfiguration {
message: "Existing is a runtime identity, not an adapter selector; supply a setup or reuse its device".into(),
});
}
Ok((wgpu_device(self.kind, self.api), self.options.resolve()?))
}
#[cfg(not(target_family = "wasm"))]
pub fn init(self) -> Result<Device, WgpuInitError> {
self.init_with_setup().map(|(device, _)| device)
}
pub async fn init_async(self) -> Result<Device, WgpuInitError> {
self.init_with_setup_async().await.map(|(device, _)| device)
}
#[cfg(not(target_family = "wasm"))]
pub fn init_with_setup(self) -> Result<(Device, WgpuSetup), WgpuInitError> {
let (device, options) = self.into_request()?;
let setup = runtime::try_init_setup::<runtime::AutoGraphicsApi>(&device, options)?;
Ok((Device::from(device), setup))
}
pub async fn init_with_setup_async(self) -> Result<(Device, WgpuSetup), WgpuInitError> {
let (device, options) = self.into_request()?;
let setup =
runtime::try_init_setup_async::<runtime::AutoGraphicsApi>(&device, options).await?;
Ok((Device::from(device), setup))
}
}
#[must_use = "wgpu setup options do nothing until initialized"]
pub struct WgpuSetupOptions {
setup: WgpuSetup,
options: Options,
selection_explicit: bool,
}
impl WgpuSetupOptions {
pub fn memory_config(mut self, config: MemoryConfiguration) -> Self {
self.options.memory = Some(config);
self
}
pub fn tasks_max(mut self, tasks: usize) -> Self {
self.options.tasks_max = Some(tasks);
self
}
pub fn init(self) -> Result<Device, WgpuInitError> {
if self.selection_explicit {
return Err(WgpuInitError::InvalidConfiguration {
message: "device_kind and graphics_api cannot be combined with an existing setup"
.into(),
});
}
runtime::try_init_device(self.setup, self.options.resolve()?).map(Device::from)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn existing_identity_is_not_an_adapter_to_create() {
assert!(matches!(
Device::wgpu_options()
.device_kind(DeviceKind::Existing(42))
.into_request(),
Err(WgpuInitError::InvalidConfiguration { .. })
));
}
#[test]
fn zero_tasks_are_rejected_before_acquiring_a_gpu() {
assert!(matches!(
WgpuOptions::default().tasks_max(0).into_request(),
Err(WgpuInitError::InvalidConfiguration { .. })
));
}
#[cfg(all(feature = "std", not(target_family = "wasm")))]
#[test]
fn explicit_tasks_override_a_malformed_environment_default() {
const CHILD: &str = "BURN_TEST_WGPU_TASKS_CHILD";
if std::env::var_os(CHILD).is_some() {
assert!(matches!(
Device::wgpu_options().into_request(),
Err(WgpuInitError::InvalidConfiguration { .. })
));
assert_eq!(
Device::wgpu_options()
.tasks_max(7)
.into_request()
.unwrap()
.1
.tasks_max,
7
);
return;
}
let status = std::process::Command::new(std::env::current_exe().unwrap())
.args([
"--exact",
"wgpu::tests::explicit_tasks_override_a_malformed_environment_default",
])
.env(CHILD, "1")
.env("CUBECL_WGPU_MAX_TASKS", "invalid")
.status()
.unwrap();
assert!(status.success());
}
}