1use wgpu::{Adapter, Backends, Device, Features, Instance, Limits, Queue};
2
3use crate::{report::AdapterReport, TestParameters};
4
5fn default_device_lost_callback(reason: wgpu::DeviceLostReason, message: String) {
12 if reason != wgpu::DeviceLostReason::Destroyed {
13 panic!("Device lost: {message}");
14 }
15}
16
17pub fn init_logger() {
19 #[cfg(not(target_arch = "wasm32"))]
21 let _ = env_logger::try_init();
22 #[cfg(target_arch = "wasm32")]
23 let _ = console_log::init_with_level(log::Level::Info);
24}
25
26pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) -> Instance {
28 let backends = if cfg!(feature = "webgl") {
39 backends - wgpu::Backends::BROWSER_WEBGPU
40 } else {
41 backends
42 };
43 let dx12_shader_compiler = if params.force_fxc {
46 wgpu::Dx12Compiler::Fxc
47 } else {
48 wgpu::Dx12Compiler::from_env().unwrap_or(wgpu::Dx12Compiler::StaticDxc)
49 };
50 let flags = wgpu::InstanceFlags::debugging()
52 .with_env()
53 .union(params.required_instance_flags);
54
55 Instance::new(wgpu::InstanceDescriptor {
56 backends,
57 flags,
58 memory_budget_thresholds: wgpu::MemoryBudgetThresholds {
59 for_resource_creation: Some(99),
60 for_device_loss: None,
61 },
62 backend_options: wgpu::BackendOptions {
63 dx12: wgpu::Dx12BackendOptions {
64 shader_compiler: dx12_shader_compiler,
65 ..Default::default()
66 },
67 gl: wgpu::GlBackendOptions {
68 fence_behavior: if cfg!(target_family = "wasm") {
69 wgpu::GlFenceBehavior::AutoFinish
75 } else {
76 wgpu::GlFenceBehavior::Normal
77 },
78 ..Default::default()
79 },
80 noop: wgpu::NoopBackendOptions {
87 enable: !cfg!(target_arch = "wasm32"),
88 ..Default::default()
89 },
90 }
91 .with_env(),
92 #[cfg(not(all(
93 target_arch = "wasm32",
94 any(target_os = "emscripten", feature = "webgl")
95 )))]
96 display: None,
97 #[cfg(all(
100 target_arch = "wasm32",
101 any(target_os = "emscripten", feature = "webgl")
102 ))]
103 display: Some(Box::new(WebDisplayHandle)),
104 })
105}
106
107pub async fn initialize_adapter(
111 adapter_report: Option<&AdapterReport>,
112 params: &TestParameters,
113) -> Option<(Instance, Adapter, Option<SurfaceGuard>)> {
114 let backends = adapter_report
115 .map(|report| Backends::from(report.info.backend))
116 .unwrap_or_default();
117
118 let instance = initialize_instance(backends, params);
119 #[allow(unused_variables)]
120 let surface: Option<wgpu::Surface>;
121 let surface_guard: Option<SurfaceGuard>;
122
123 #[allow(unused_assignments)]
124 #[cfg(not(all(
126 target_arch = "wasm32",
127 any(target_os = "emscripten", feature = "webgl")
128 )))]
129 {
130 surface = None;
131 surface_guard = None;
132 }
133 #[cfg(all(
134 target_arch = "wasm32",
135 any(target_os = "emscripten", feature = "webgl")
136 ))]
137 {
138 let canvas = initialize_html_canvas();
140
141 surface = Some(
142 instance
143 .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))
144 .expect("could not create surface from canvas"),
145 );
146
147 surface_guard = Some(SurfaceGuard { canvas });
148 }
149
150 cfg_if::cfg_if! {
151 if #[cfg(not(target_arch = "wasm32"))] {
152 let adapter_iter = instance.enumerate_adapters(backends).await;
153 let adapter = adapter_iter.into_iter()
154 .find(|adapter| if let Some(adapter_report) = adapter_report {
158 adapter.get_info() == adapter_report.info
159 } else {
160 true
161 });
162 } else {
163 let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
164 compatible_surface: surface.as_ref(),
165 ..Default::default()
166 }).await.ok();
167 }
168 }
169
170 let Some(adapter) = adapter else {
171 if params
172 .required_instance_flags
173 .contains(wgpu::InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
174 {
175 return None;
176 } else {
177 panic!(
178 "Could not find adapter with info {:#?} in {:#?}",
179 adapter_report.map(|r| &r.info),
180 instance
181 .enumerate_adapters(backends)
182 .await
183 .into_iter()
184 .map(|a| a.get_info())
185 .collect::<Vec<_>>(),
186 );
187 }
188 };
189
190 log::info!("Testing using adapter: {:#?}", adapter.get_info());
191 Some((instance, adapter, surface_guard))
192}
193
194pub async fn initialize_device(
196 adapter: &Adapter,
197 features: Features,
198 limits: Limits,
199) -> (Device, Queue) {
200 let bundle = adapter
201 .request_device(&wgpu::DeviceDescriptor {
202 label: None,
203 required_features: features,
204 required_limits: limits,
205 experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
206 memory_hints: wgpu::MemoryHints::MemoryUsage,
207 trace: wgpu::Trace::Off,
208 })
209 .await;
210
211 let (device, queue) = match bundle {
212 Ok((device, queue)) => (device, queue),
213 Err(e) => panic!("Failed to initialize device: {e}"),
214 };
215
216 device.set_device_lost_callback(default_device_lost_callback);
217
218 (device, queue)
219}
220
221#[cfg(target_arch = "wasm32")]
223pub fn initialize_html_canvas() -> web_sys::HtmlCanvasElement {
224 use wasm_bindgen::JsCast;
225
226 web_sys::window()
227 .and_then(|win| win.document())
228 .and_then(|doc| {
229 let canvas = doc.create_element("Canvas").unwrap();
230 canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()
231 })
232 .expect("couldn't create canvas")
233}
234
235pub struct SurfaceGuard {
236 #[cfg(target_arch = "wasm32")]
237 #[allow(unused)]
238 canvas: web_sys::HtmlCanvasElement,
239}
240
241impl SurfaceGuard {
242 #[cfg(all(
243 target_arch = "wasm32",
244 any(target_os = "emscripten", feature = "webgl")
245 ))]
246 pub(crate) fn check_for_unreported_errors(&self) -> bool {
247 use wasm_bindgen::JsCast;
248
249 self.canvas
250 .get_context("webgl2")
251 .unwrap()
252 .unwrap()
253 .dyn_into::<web_sys::WebGl2RenderingContext>()
254 .unwrap()
255 .get_error()
256 != web_sys::WebGl2RenderingContext::NO_ERROR
257 }
258}
259
260#[cfg(all(
263 target_arch = "wasm32",
264 any(target_os = "emscripten", feature = "webgl")
265))]
266#[derive(Debug)]
267struct WebDisplayHandle;
268
269#[cfg(all(
270 target_arch = "wasm32",
271 any(target_os = "emscripten", feature = "webgl")
272))]
273impl raw_window_handle::HasDisplayHandle for WebDisplayHandle {
274 fn display_handle(
275 &self,
276 ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
277 Ok(raw_window_handle::DisplayHandle::web())
278 }
279}