1use wgpu::{Adapter, Backends, Device, Features, Instance, Limits, Queue};
23use crate::{report::AdapterReport, TestParameters};
45/// Initialize the logger for the test runner.
6pub fn init_logger() {
7// We don't actually care if it fails
8#[cfg(not(target_arch = "wasm32"))]
9let _ = env_logger::try_init();
10#[cfg(target_arch = "wasm32")]
11let _ = console_log::init_with_level(log::Level::Info);
12}
1314/// Initialize a wgpu instance with the options from the environment.
15pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) -> Instance {
16// We ignore `WGPU_BACKEND` for now, merely using test filtering to only run a single backend's tests.
17 //
18 // We can potentially work support back into the test runner in the future, but as the adapters are matched up
19 // based on adapter index, removing some backends messes up the indexes in annoying ways.
20 //
21 // WORKAROUND for https://github.com/rust-lang/cargo/issues/7160:
22 // `--no-default-features` is not passed through correctly to the test runner.
23 // We use it whenever we want to explicitly run with webgl instead of webgpu.
24 // To "disable" webgpu regardless, we do this by removing the webgpu backend whenever we see
25 // the webgl feature.
26let backends = if cfg!(feature = "webgl") {
27 backends - wgpu::Backends::BROWSER_WEBGPU
28 } else {
29 backends
30 };
31// Some tests need to be able to force demote to FXC, to specifically test workarounds for FXC
32 // behavior.
33let 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// The defaults for debugging, overridden by the environment, overridden by the test parameters.
39let flags = wgpu::InstanceFlags::debugging()
40 .with_env()
41 .union(params.required_instance_flags);
4243 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 },
54 gl: wgpu::GlBackendOptions {
55 fence_behavior: if cfg!(target_family = "wasm") {
56// On WebGL, you cannot call Poll(Wait) with any timeout. This is because the
57 // browser does not things to block. However all of our tests are written to
58 // expect this behavior. This is the workaround to allow this to work.
59 //
60 // However on native you can wait, so we want to ensure that behavior as well.
61wgpu::GlFenceBehavior::AutoFinish
62 } else {
63 wgpu::GlFenceBehavior::Normal
64 },
65 ..Default::default()
66 }
67 .with_env(),
68// TODO(https://github.com/gfx-rs/wgpu/issues/7119): Enable noop backend?
69noop: wgpu::NoopBackendOptions::default(),
70 },
71 })
72}
7374/// Initialize a wgpu adapter, using the given adapter report to match the adapter.
75pub async fn initialize_adapter(
76 adapter_report: Option<&AdapterReport>,
77 params: &TestParameters,
78) -> (Instance, Adapter, Option<SurfaceGuard>) {
79let backends = adapter_report
80 .map(|report| Backends::from(report.info.backend))
81 .unwrap_or_default();
8283let instance = initialize_instance(backends, params);
84#[allow(unused_variables)]
85let surface: Option<wgpu::Surface>;
86let surface_guard: Option<SurfaceGuard>;
8788#[allow(unused_assignments)]
89// Create a canvas if we need a WebGL2RenderingContext to have a working device.
90#[cfg(not(all(
91 target_arch = "wasm32",
92 any(target_os = "emscripten", feature = "webgl")
93 )))]
94{
95 surface = None;
96 surface_guard = None;
97 }
98#[cfg(all(
99 target_arch = "wasm32",
100 any(target_os = "emscripten", feature = "webgl")
101 ))]
102{
103// On wasm, append a canvas to the document body for initializing the adapter
104let canvas = initialize_html_canvas();
105106 surface = Some(
107 instance
108 .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))
109 .expect("could not create surface from canvas"),
110 );
111112 surface_guard = Some(SurfaceGuard { canvas });
113 }
114115cfg_if::cfg_if! {
116if #[cfg(not(target_arch = "wasm32"))] {
117let adapter_iter = instance.enumerate_adapters(backends);
118let adapter = adapter_iter.into_iter()
119// If we have a report, we only want to match the adapter with the same info.
120 //
121 // If we don't have a report, we just take the first adapter.
122.find(|adapter| if let Some(adapter_report) = adapter_report {
123 adapter.get_info() == adapter_report.info
124 } else {
125true
126});
127let Some(adapter) = adapter else {
128panic!(
129"Could not find adapter with info {:#?} in {:#?}",
130 adapter_report.map(|r| &r.info),
131 instance.enumerate_adapters(backends).into_iter().map(|a| a.get_info()).collect::<Vec<_>>(),
132 );
133 };
134 } else {
135let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
136 compatible_surface: surface.as_ref(),
137 ..Default::default()
138 }).await.unwrap();
139 }
140 }
141142log::info!("Testing using adapter: {:#?}", adapter.get_info());
143144 (instance, adapter, surface_guard)
145}
146147/// Initialize a wgpu device from a given adapter.
148pub async fn initialize_device(
149 adapter: &Adapter,
150 features: Features,
151 limits: Limits,
152) -> (Device, Queue) {
153let bundle = adapter
154 .request_device(&wgpu::DeviceDescriptor {
155 label: None,
156 required_features: features,
157 required_limits: limits,
158 memory_hints: wgpu::MemoryHints::MemoryUsage,
159 trace: wgpu::Trace::Off,
160 })
161 .await;
162163match bundle {
164Ok(b) => b,
165Err(e) => panic!("Failed to initialize device: {e}"),
166 }
167}
168169/// Create a canvas for testing.
170#[cfg(target_arch = "wasm32")]
171pub fn initialize_html_canvas() -> web_sys::HtmlCanvasElement {
172use wasm_bindgen::JsCast;
173174 web_sys::window()
175 .and_then(|win| win.document())
176 .and_then(|doc| {
177let canvas = doc.create_element("Canvas").unwrap();
178 canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()
179 })
180 .expect("couldn't create canvas")
181}
182183pub struct SurfaceGuard {
184#[cfg(target_arch = "wasm32")]
185 #[allow(unused)]
186canvas: web_sys::HtmlCanvasElement,
187}
188189impl SurfaceGuard {
190#[cfg(all(
191 target_arch = "wasm32",
192 any(target_os = "emscripten", feature = "webgl")
193 ))]
194pub(crate) fn check_for_unreported_errors(&self) -> bool {
195use wasm_bindgen::JsCast;
196197self.canvas
198 .get_context("webgl2")
199 .unwrap()
200 .unwrap()
201 .dyn_into::<web_sys::WebGl2RenderingContext>()
202 .unwrap()
203 .get_error()
204 != web_sys::WebGl2RenderingContext::NO_ERROR
205 }
206}