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