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(
109 adapter_report: Option<&AdapterReport>,
110 params: &TestParameters,
111) -> (Instance, Adapter, Option<SurfaceGuard>) {
112 let backends = adapter_report
113 .map(|report| Backends::from(report.info.backend))
114 .unwrap_or_default();
115
116 let instance = initialize_instance(backends, params);
117 #[allow(unused_variables)]
118 let surface: Option<wgpu::Surface>;
119 let surface_guard: Option<SurfaceGuard>;
120
121 #[allow(unused_assignments)]
122 #[cfg(not(all(
124 target_arch = "wasm32",
125 any(target_os = "emscripten", feature = "webgl")
126 )))]
127 {
128 surface = None;
129 surface_guard = None;
130 }
131 #[cfg(all(
132 target_arch = "wasm32",
133 any(target_os = "emscripten", feature = "webgl")
134 ))]
135 {
136 let canvas = initialize_html_canvas();
138
139 surface = Some(
140 instance
141 .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))
142 .expect("could not create surface from canvas"),
143 );
144
145 surface_guard = Some(SurfaceGuard { canvas });
146 }
147
148 cfg_if::cfg_if! {
149 if #[cfg(not(target_arch = "wasm32"))] {
150 let adapter_iter = instance.enumerate_adapters(backends).await;
151 let adapter = adapter_iter.into_iter()
152 .find(|adapter| if let Some(adapter_report) = adapter_report {
156 adapter.get_info() == adapter_report.info
157 } else {
158 true
159 });
160 let Some(adapter) = adapter else {
161 panic!(
162 "Could not find adapter with info {:#?} in {:#?}",
163 adapter_report.map(|r| &r.info),
164 instance.enumerate_adapters(backends).await.into_iter().map(|a| a.get_info()).collect::<Vec<_>>(),
165 );
166 };
167 } else {
168 let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
169 compatible_surface: surface.as_ref(),
170 ..Default::default()
171 }).await.unwrap();
172 }
173 }
174
175 log::info!("Testing using adapter: {:#?}", adapter.get_info());
176
177 (instance, adapter, surface_guard)
178}
179
180pub async fn initialize_device(
182 adapter: &Adapter,
183 features: Features,
184 limits: Limits,
185) -> (Device, Queue) {
186 let bundle = adapter
187 .request_device(&wgpu::DeviceDescriptor {
188 label: None,
189 required_features: features,
190 required_limits: limits,
191 experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
192 memory_hints: wgpu::MemoryHints::MemoryUsage,
193 trace: wgpu::Trace::Off,
194 })
195 .await;
196
197 let (device, queue) = match bundle {
198 Ok((device, queue)) => (device, queue),
199 Err(e) => panic!("Failed to initialize device: {e}"),
200 };
201
202 device.set_device_lost_callback(default_device_lost_callback);
203
204 (device, queue)
205}
206
207#[cfg(target_arch = "wasm32")]
209pub fn initialize_html_canvas() -> web_sys::HtmlCanvasElement {
210 use wasm_bindgen::JsCast;
211
212 web_sys::window()
213 .and_then(|win| win.document())
214 .and_then(|doc| {
215 let canvas = doc.create_element("Canvas").unwrap();
216 canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()
217 })
218 .expect("couldn't create canvas")
219}
220
221pub struct SurfaceGuard {
222 #[cfg(target_arch = "wasm32")]
223 #[allow(unused)]
224 canvas: web_sys::HtmlCanvasElement,
225}
226
227impl SurfaceGuard {
228 #[cfg(all(
229 target_arch = "wasm32",
230 any(target_os = "emscripten", feature = "webgl")
231 ))]
232 pub(crate) fn check_for_unreported_errors(&self) -> bool {
233 use wasm_bindgen::JsCast;
234
235 self.canvas
236 .get_context("webgl2")
237 .unwrap()
238 .unwrap()
239 .dyn_into::<web_sys::WebGl2RenderingContext>()
240 .unwrap()
241 .get_error()
242 != web_sys::WebGl2RenderingContext::NO_ERROR
243 }
244}
245
246#[cfg(all(
249 target_arch = "wasm32",
250 any(target_os = "emscripten", feature = "webgl")
251))]
252#[derive(Debug)]
253struct WebDisplayHandle;
254
255#[cfg(all(
256 target_arch = "wasm32",
257 any(target_os = "emscripten", feature = "webgl")
258))]
259impl raw_window_handle::HasDisplayHandle for WebDisplayHandle {
260 fn display_handle(
261 &self,
262 ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
263 Ok(raw_window_handle::DisplayHandle::web())
264 }
265}