1use std::sync::OnceLock;
10use std::sync::atomic::{AtomicU8, Ordering};
11
12const PREF_DEFAULT: u8 = 0;
13const PREF_VULKAN: u8 = 1;
14
15static BACKEND_PREF: AtomicU8 = AtomicU8::new(PREF_DEFAULT);
16
17pub struct WgpuDevice {
21 pub instance: wgpu::Instance,
22 pub adapter: wgpu::Adapter,
23 pub device: wgpu::Device,
24 pub queue: wgpu::Queue,
25 pub name: String,
26 pub backend: wgpu::Backend,
27}
28
29impl WgpuDevice {
30 fn new_with_backends(backends: wgpu::Backends) -> Option<Self> {
31 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
32 backends,
33 flags: wgpu::InstanceFlags::default(),
34 backend_options: wgpu::BackendOptions::default(),
35 memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(),
36 display: None,
37 });
38 let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
39 power_preference: wgpu::PowerPreference::HighPerformance,
40 compatible_surface: None,
41 force_fallback_adapter: false,
42 }))
43 .ok()?;
44
45 let info = adapter.get_info();
46 let limits = adapter.limits();
47 let adapter_feats = adapter.features();
48 let mut required_features = wgpu::Features::empty();
49 if adapter_feats.contains(wgpu::Features::SHADER_F16) {
50 required_features |= wgpu::Features::SHADER_F16;
51 }
52 if adapter_feats.contains(wgpu::Features::EXPERIMENTAL_COOPERATIVE_MATRIX) {
53 required_features |= wgpu::Features::EXPERIMENTAL_COOPERATIVE_MATRIX;
54 }
55 if adapter_feats.contains(wgpu::Features::SUBGROUP) {
56 required_features |= wgpu::Features::SUBGROUP;
57 }
58
59 let (device, queue) =
60 match pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
61 label: Some("rlx-wgpu device"),
62 required_features,
63 required_limits: limits,
64 memory_hints: wgpu::MemoryHints::Performance,
65 experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
66 trace: wgpu::Trace::Off,
67 })) {
68 Ok(p) => p,
69 Err(e) => {
70 eprintln!("rlx-wgpu request_device failed: {e}");
71 return None;
72 }
73 };
74
75 Some(Self {
76 instance,
77 adapter,
78 device,
79 queue,
80 name: info.name,
81 backend: info.backend,
82 })
83 }
84
85 fn new_default() -> Option<Self> {
86 Self::new_with_backends(wgpu::Backends::from_env().unwrap_or(wgpu::Backends::all()))
87 }
88}
89
90unsafe impl Send for WgpuDevice {}
92unsafe impl Sync for WgpuDevice {}
93
94fn default_device() -> Option<&'static WgpuDevice> {
95 static DEVICE: OnceLock<Option<WgpuDevice>> = OnceLock::new();
96 DEVICE.get_or_init(WgpuDevice::new_default).as_ref()
97}
98
99fn vulkan_device() -> Option<&'static WgpuDevice> {
100 static DEVICE: OnceLock<Option<WgpuDevice>> = OnceLock::new();
101 DEVICE
102 .get_or_init(|| WgpuDevice::new_with_backends(wgpu::Backends::VULKAN))
103 .as_ref()
104}
105
106pub fn select_vulkan_backend() {
110 BACKEND_PREF.store(PREF_VULKAN, Ordering::SeqCst);
111}
112
113pub fn is_vulkan_available() -> bool {
115 vulkan_device().is_some()
116}
117
118pub fn wgpu_device() -> Option<&'static WgpuDevice> {
121 if BACKEND_PREF.load(Ordering::SeqCst) == PREF_VULKAN {
122 vulkan_device()
123 } else {
124 default_device()
125 }
126}