use wgpu::{Adapter, Backends, Device, Features, Instance, Limits, Queue}; use crate::{report::AdapterReport, TestParameters}; /// Initialize the logger for the test runner. pub fn init_logger() { // We don't actually care if it fails #[cfg(not(target_arch = "wasm32"))] let _ = env_logger::try_init(); #[cfg(target_arch = "wasm32")] let _ = console_log::init_with_level(log::Level::Info); } /// Initialize a wgpu instance with the options from the environment. pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) -> Instance { // We ignore `WGPU_BACKEND` for now, merely using test filtering to only run a single backend's tests. // // We can potentially work support back into the test runner in the future, but as the adapters are matched up // based on adapter index, removing some backends messes up the indexes in annoying ways. // // WORKAROUND for https://github.com/rust-lang/cargo/issues/7160: // `--no-default-features` is not passed through correctly to the test runner. // We use it whenever we want to explicitly run with webgl instead of webgpu. // To "disable" webgpu regardless, we do this by removing the webgpu backend whenever we see // the webgl feature. let backends = if cfg!(feature = "webgl") { backends - wgpu::Backends::BROWSER_WEBGPU } else { backends }; // Some tests need to be able to force demote to FXC, to specifically test workarounds for FXC // behavior. let dx12_shader_compiler = if params.force_fxc { wgpu::Dx12Compiler::Fxc } else { wgpu::Dx12Compiler::from_env().unwrap_or(wgpu::Dx12Compiler::StaticDxc) }; // The defaults for debugging, overridden by the environment, overridden by the test parameters. let flags = wgpu::InstanceFlags::debugging() .with_env() .union(params.required_instance_flags); Instance::new(&wgpu::InstanceDescriptor { backends, flags, backend_options: wgpu::BackendOptions { dx12: wgpu::Dx12BackendOptions { shader_compiler: dx12_shader_compiler, }, gl: wgpu::GlBackendOptions { fence_behavior: if cfg!(target_family = "wasm") { // On WebGL, you cannot call Poll(Wait) with any timeout. This is because the // browser does not things to block. However all of our tests are written to // expect this behavior. This is the workaround to allow this to work. // // However on native you can wait, so we want to ensure that behavior as well. wgpu::GlFenceBehavior::AutoFinish } else { wgpu::GlFenceBehavior::Normal }, ..Default::default() } .with_env(), // TODO(https://github.com/gfx-rs/wgpu/issues/7119): Enable noop backend? noop: wgpu::NoopBackendOptions::default(), }, }) } /// Initialize a wgpu adapter, using the given adapter report to match the adapter. pub async fn initialize_adapter( adapter_report: Option<&AdapterReport>, params: &TestParameters, ) -> (Instance, Adapter, Option) { let backends = adapter_report .map(|report| Backends::from(report.info.backend)) .unwrap_or_default(); let instance = initialize_instance(backends, params); #[allow(unused_variables)] let surface: Option; let surface_guard: Option; #[allow(unused_assignments)] // Create a canvas if we need a WebGL2RenderingContext to have a working device. #[cfg(not(all( target_arch = "wasm32", any(target_os = "emscripten", feature = "webgl") )))] { surface = None; surface_guard = None; } #[cfg(all( target_arch = "wasm32", any(target_os = "emscripten", feature = "webgl") ))] { // On wasm, append a canvas to the document body for initializing the adapter let canvas = initialize_html_canvas(); surface = Some( instance .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone())) .expect("could not create surface from canvas"), ); surface_guard = Some(SurfaceGuard { canvas }); } cfg_if::cfg_if! { if #[cfg(not(target_arch = "wasm32"))] { let adapter_iter = instance.enumerate_adapters(backends); let adapter = adapter_iter.into_iter() // If we have a report, we only want to match the adapter with the same info. // // If we don't have a report, we just take the first adapter. .find(|adapter| if let Some(adapter_report) = adapter_report { adapter.get_info() == adapter_report.info } else { true }); let Some(adapter) = adapter else { panic!( "Could not find adapter with info {:#?} in {:#?}", adapter_report.map(|r| &r.info), instance.enumerate_adapters(backends).into_iter().map(|a| a.get_info()).collect::>(), ); }; } else { let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions { compatible_surface: surface.as_ref(), ..Default::default() }).await.unwrap(); } } log::info!("Testing using adapter: {:#?}", adapter.get_info()); (instance, adapter, surface_guard) } /// Initialize a wgpu device from a given adapter. pub async fn initialize_device( adapter: &Adapter, features: Features, limits: Limits, ) -> (Device, Queue) { let bundle = adapter .request_device(&wgpu::DeviceDescriptor { label: None, required_features: features, required_limits: limits, memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, }) .await; match bundle { Ok(b) => b, Err(e) => panic!("Failed to initialize device: {e}"), } } /// Create a canvas for testing. #[cfg(target_arch = "wasm32")] pub fn initialize_html_canvas() -> web_sys::HtmlCanvasElement { use wasm_bindgen::JsCast; web_sys::window() .and_then(|win| win.document()) .and_then(|doc| { let canvas = doc.create_element("Canvas").unwrap(); canvas.dyn_into::().ok() }) .expect("couldn't create canvas") } pub struct SurfaceGuard { #[cfg(target_arch = "wasm32")] #[allow(unused)] canvas: web_sys::HtmlCanvasElement, } impl SurfaceGuard { #[cfg(all( target_arch = "wasm32", any(target_os = "emscripten", feature = "webgl") ))] pub(crate) fn check_for_unreported_errors(&self) -> bool { use wasm_bindgen::JsCast; self.canvas .get_context("webgl2") .unwrap() .unwrap() .dyn_into::() .unwrap() .get_error() != web_sys::WebGl2RenderingContext::NO_ERROR } }