From 9b69660ec73fa7f67f79e848482dde6d17c1f71f Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Sat, 10 Jun 2023 15:16:05 -0400 Subject: [PATCH 01/41] Begin with a proper CLI --- Cargo.lock | 7 + Cargo.toml | 1 + wgpu-info/Cargo.toml | 1 + wgpu-info/src/cli.rs | 42 ++++ wgpu-info/src/human.rs | 368 ++++++++++++++++++++++++++++++++++ wgpu-info/src/main.rs | 436 +---------------------------------------- 6 files changed, 425 insertions(+), 430 deletions(-) create mode 100644 wgpu-info/src/cli.rs create mode 100644 wgpu-info/src/human.rs diff --git a/Cargo.lock b/Cargo.lock index 9e5a01838c..6041f3e8ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1880,6 +1880,12 @@ dependencies = [ "indexmap", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" version = "1.1.0" @@ -3187,6 +3193,7 @@ version = "0.16.0" dependencies = [ "bitflags 2.3.1", "env_logger", + "pico-args", "wgpu", ] diff --git a/Cargo.toml b/Cargo.toml index 28e4ac9f57..be8b06fd83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,7 @@ noise = { version = "0.7", default-features = false } obj = "0.10" # parking_lot 0.12 switches from `winapi` to `windows`; permit either parking_lot = ">=0.11,<0.13" +pico-args = { version = "0.5.0", features = ["eq-separator", "short-space-opt", "combined-flags"] } png = "0.17.8" pollster = "0.2" profiling = { version = "1", default-features = false } diff --git a/wgpu-info/Cargo.toml b/wgpu-info/Cargo.toml index bf9fe449d5..d6f4293972 100644 --- a/wgpu-info/Cargo.toml +++ b/wgpu-info/Cargo.toml @@ -13,4 +13,5 @@ publish = false [dependencies] bitflags.workspace = true env_logger.workspace = true +pico-args.workspace = true wgpu.workspace = true diff --git a/wgpu-info/src/cli.rs b/wgpu-info/src/cli.rs new file mode 100644 index 0000000000..68349edfb0 --- /dev/null +++ b/wgpu-info/src/cli.rs @@ -0,0 +1,42 @@ +use std::{io, process::exit}; + +const HELP: &str = "\ +Usage: wgpu-info [--output ] [--json] + +Options: + -h, --help Print this help + -o, --output Destination to write output to + -j, --json Output JSON information instead of text +"; + +pub fn main() { + let mut args = pico_args::Arguments::from_env(); + let help = args.contains(["-h", "--help"]); + + if help { + eprintln!("{HELP}"); + exit(101); + } + + let output_path: Option = args.opt_value_from_str(["-o", "--output"]).unwrap(); + let json = args.contains(["-j", "--json"]); + + env_logger::init(); + + let mut file_handle; + let mut std_handle; + let output: &mut dyn io::Write = match output_path { + Some(path) => { + file_handle = std::fs::File::create(path).unwrap(); + &mut file_handle + } + None => { + std_handle = io::stdout(); + &mut std_handle + } + }; + + if !json { + crate::human::print_adapters(output) + } +} diff --git a/wgpu-info/src/human.rs b/wgpu-info/src/human.rs new file mode 100644 index 0000000000..bdf72ceb54 --- /dev/null +++ b/wgpu-info/src/human.rs @@ -0,0 +1,368 @@ +use std::io; + +use bitflags::Flags; + +trait FlagsExt: Flags { + fn name(&self) -> &'static str { + self.iter_names().next().unwrap().0 + } + + fn valid_bits() -> std::iter::Enumerate> { + Self::all().iter().enumerate() + } + + fn max_debug_print_width() -> usize { + let mut width = 0; + for bit in Self::all().iter() { + width = width.max(bit.name().len()); + } + width + } + + fn println_table_header(output: &mut dyn io::Write) { + write!(output, "┌─"); + for (i, bit) in Self::valid_bits() { + if i != 0 { + write!(output, "─┬─"); + } + let length = bit.name().len(); + write!(output, "{}", "─".repeat(length)); + } + writeln!(output, "─┐"); + } + + fn println_table_footer(output: &mut dyn io::Write) { + write!(output, "└─"); + for (i, bit) in Self::valid_bits() { + if i != 0 { + write!(output, "─┴─"); + } + let length = bit.name().len(); + write!(output, "{}", "─".repeat(length)); + } + writeln!(output, "─┘"); + } +} + +impl FlagsExt for T where T: Flags {} + +fn max_texture_format_name_size() -> usize { + TEXTURE_FORMAT_LIST + .into_iter() + .map(|f| texture_format_name(f).len()) + .max() + .unwrap() +} + +fn texture_format_name(format: wgpu::TextureFormat) -> String { + match format { + wgpu::TextureFormat::Astc { block, channel } => { + format!("Astc{block:?}{channel:?}:") + } + _ => { + format!("{format:?}:") + } + } +} + +// Lets keep these on one line +#[rustfmt::skip] +const TEXTURE_FORMAT_LIST: [wgpu::TextureFormat; 114] = [ + wgpu::TextureFormat::R8Unorm, + wgpu::TextureFormat::R8Snorm, + wgpu::TextureFormat::R8Uint, + wgpu::TextureFormat::R8Sint, + wgpu::TextureFormat::R16Uint, + wgpu::TextureFormat::R16Sint, + wgpu::TextureFormat::R16Unorm, + wgpu::TextureFormat::R16Snorm, + wgpu::TextureFormat::R16Float, + wgpu::TextureFormat::Rg8Unorm, + wgpu::TextureFormat::Rg8Snorm, + wgpu::TextureFormat::Rg8Uint, + wgpu::TextureFormat::Rg8Sint, + wgpu::TextureFormat::R32Uint, + wgpu::TextureFormat::R32Sint, + wgpu::TextureFormat::R32Float, + wgpu::TextureFormat::Rg16Uint, + wgpu::TextureFormat::Rg16Sint, + wgpu::TextureFormat::Rg16Unorm, + wgpu::TextureFormat::Rg16Snorm, + wgpu::TextureFormat::Rg16Float, + wgpu::TextureFormat::Rgba8Unorm, + wgpu::TextureFormat::Rgba8UnormSrgb, + wgpu::TextureFormat::Rgba8Snorm, + wgpu::TextureFormat::Rgba8Uint, + wgpu::TextureFormat::Rgba8Sint, + wgpu::TextureFormat::Bgra8Unorm, + wgpu::TextureFormat::Bgra8UnormSrgb, + wgpu::TextureFormat::Rgb10a2Unorm, + wgpu::TextureFormat::Rg11b10Float, + wgpu::TextureFormat::Rg32Uint, + wgpu::TextureFormat::Rg32Sint, + wgpu::TextureFormat::Rg32Float, + wgpu::TextureFormat::Rgba16Uint, + wgpu::TextureFormat::Rgba16Sint, + wgpu::TextureFormat::Rgba16Unorm, + wgpu::TextureFormat::Rgba16Snorm, + wgpu::TextureFormat::Rgba16Float, + wgpu::TextureFormat::Rgba32Uint, + wgpu::TextureFormat::Rgba32Sint, + wgpu::TextureFormat::Rgba32Float, + wgpu::TextureFormat::Stencil8, + wgpu::TextureFormat::Depth16Unorm, + wgpu::TextureFormat::Depth32Float, + wgpu::TextureFormat::Depth32FloatStencil8, + wgpu::TextureFormat::Depth24Plus, + wgpu::TextureFormat::Depth24PlusStencil8, + wgpu::TextureFormat::Rgb9e5Ufloat, + wgpu::TextureFormat::Bc1RgbaUnorm, + wgpu::TextureFormat::Bc1RgbaUnormSrgb, + wgpu::TextureFormat::Bc2RgbaUnorm, + wgpu::TextureFormat::Bc2RgbaUnormSrgb, + wgpu::TextureFormat::Bc3RgbaUnorm, + wgpu::TextureFormat::Bc3RgbaUnormSrgb, + wgpu::TextureFormat::Bc4RUnorm, + wgpu::TextureFormat::Bc4RSnorm, + wgpu::TextureFormat::Bc5RgUnorm, + wgpu::TextureFormat::Bc5RgSnorm, + wgpu::TextureFormat::Bc6hRgbUfloat, + wgpu::TextureFormat::Bc6hRgbFloat, + wgpu::TextureFormat::Bc7RgbaUnorm, + wgpu::TextureFormat::Bc7RgbaUnormSrgb, + wgpu::TextureFormat::Etc2Rgb8Unorm, + wgpu::TextureFormat::Etc2Rgb8UnormSrgb, + wgpu::TextureFormat::Etc2Rgb8A1Unorm, + wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb, + wgpu::TextureFormat::Etc2Rgba8Unorm, + wgpu::TextureFormat::Etc2Rgba8UnormSrgb, + wgpu::TextureFormat::EacR11Unorm, + wgpu::TextureFormat::EacR11Snorm, + wgpu::TextureFormat::EacRg11Unorm, + wgpu::TextureFormat::EacRg11Snorm, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::Hdr }, +]; + +// Lets keep these on one line +#[rustfmt::skip] +fn print_adapter(output: &mut dyn io::Write, adapter: &wgpu::Adapter, idx: usize) { + let info = adapter.get_info(); + let downlevel = adapter.get_downlevel_capabilities(); + let features = adapter.features(); + let limits = adapter.limits(); + + ////////////////// + // Adapter Info // + ////////////////// + + writeln!(output, "Adapter {idx}:"); + writeln!(output, "\t Backend: {:?}", info.backend); + writeln!(output, "\t Name: {:?}", info.name); + writeln!(output, "\t VendorID: {:?}", info.vendor); + writeln!(output, "\t DeviceID: {:?}", info.device); + writeln!(output, "\t Type: {:?}", info.device_type); + writeln!(output, "\t Driver: {:?}", info.driver); + writeln!(output, "\tDriverInfo: {:?}", info.driver_info); + writeln!(output, "\t Compliant: {:?}", downlevel.is_webgpu_compliant()); + + ////////////// + // Features // + ////////////// + + writeln!(output, "\tFeatures:"); + let max_feature_flag_width = wgpu::Features::max_debug_print_width(); + for bit in wgpu::Features::all().iter() { + writeln!(output, "\t\t{:>width$}: {}", bit.name(), features.contains(bit), width = max_feature_flag_width); + } + + //////////// + // Limits // + //////////// + + writeln!(output, "\tLimits:"); + let wgpu::Limits { + max_texture_dimension_1d, + max_texture_dimension_2d, + max_texture_dimension_3d, + max_texture_array_layers, + max_bind_groups, + max_bindings_per_bind_group, + max_dynamic_uniform_buffers_per_pipeline_layout, + max_dynamic_storage_buffers_per_pipeline_layout, + max_sampled_textures_per_shader_stage, + max_samplers_per_shader_stage, + max_storage_buffers_per_shader_stage, + max_storage_textures_per_shader_stage, + max_uniform_buffers_per_shader_stage, + max_uniform_buffer_binding_size, + max_storage_buffer_binding_size, + max_buffer_size, + max_vertex_buffers, + max_vertex_attributes, + max_vertex_buffer_array_stride, + max_push_constant_size, + min_uniform_buffer_offset_alignment, + min_storage_buffer_offset_alignment, + max_inter_stage_shader_components, + max_compute_workgroup_storage_size, + max_compute_invocations_per_workgroup, + max_compute_workgroup_size_x, + max_compute_workgroup_size_y, + max_compute_workgroup_size_z, + max_compute_workgroups_per_dimension, + } = limits; + writeln!(output, "\t\t Max Texture Dimension 1d: {max_texture_dimension_1d}"); + writeln!(output, "\t\t Max Texture Dimension 2d: {max_texture_dimension_2d}"); + writeln!(output, "\t\t Max Texture Dimension 3d: {max_texture_dimension_3d}"); + writeln!(output, "\t\t Max Texture Array Layers: {max_texture_array_layers}"); + writeln!(output, "\t\t Max Bind Groups: {max_bind_groups}"); + writeln!(output, "\t\t Max Bindings Per Bind Group: {max_bindings_per_bind_group}"); + writeln!(output, "\t\t Max Dynamic Uniform Buffers Per Pipeline Layout: {max_dynamic_uniform_buffers_per_pipeline_layout}"); + writeln!(output, "\t\t Max Dynamic Storage Buffers Per Pipeline Layout: {max_dynamic_storage_buffers_per_pipeline_layout}"); + writeln!(output, "\t\t Max Sampled Textures Per Shader Stage: {max_sampled_textures_per_shader_stage}"); + writeln!(output, "\t\t Max Samplers Per Shader Stage: {max_samplers_per_shader_stage}"); + writeln!(output, "\t\t Max Storage Buffers Per Shader Stage: {max_storage_buffers_per_shader_stage}"); + writeln!(output, "\t\t Max Storage Textures Per Shader Stage: {max_storage_textures_per_shader_stage}"); + writeln!(output, "\t\t Max Uniform Buffers Per Shader Stage: {max_uniform_buffers_per_shader_stage}"); + writeln!(output, "\t\t Max Uniform Buffer Binding Size: {max_uniform_buffer_binding_size}"); + writeln!(output, "\t\t Max Storage Buffer Binding Size: {max_storage_buffer_binding_size}"); + writeln!(output, "\t\t Max Buffer Size: {max_buffer_size}"); + writeln!(output, "\t\t Max Vertex Buffers: {max_vertex_buffers}"); + writeln!(output, "\t\t Max Vertex Attributes: {max_vertex_attributes}"); + writeln!(output, "\t\t Max Vertex Buffer Array Stride: {max_vertex_buffer_array_stride}"); + writeln!(output, "\t\t Max Push Constant Size: {max_push_constant_size}"); + writeln!(output, "\t\t Min Uniform Buffer Offset Alignment: {min_uniform_buffer_offset_alignment}"); + writeln!(output, "\t\t Min Storage Buffer Offset Alignment: {min_storage_buffer_offset_alignment}"); + writeln!(output, "\t\t Max Inter-Stage Shader Component: {max_inter_stage_shader_components}"); + writeln!(output, "\t\t Max Compute Workgroup Storage Size: {max_compute_workgroup_storage_size}"); + writeln!(output, "\t\t Max Compute Invocations Per Workgroup: {max_compute_invocations_per_workgroup}"); + writeln!(output, "\t\t Max Compute Workgroup Size X: {max_compute_workgroup_size_x}"); + writeln!(output, "\t\t Max Compute Workgroup Size Y: {max_compute_workgroup_size_y}"); + writeln!(output, "\t\t Max Compute Workgroup Size Z: {max_compute_workgroup_size_z}"); + writeln!(output, "\t\t Max Compute Workgroups Per Dimension: {max_compute_workgroups_per_dimension}"); + + ////////////////////////// + // Downlevel Properties // + ////////////////////////// + + writeln!(output, "\tDownlevel Properties:"); + let wgpu::DownlevelCapabilities { + shader_model, + limits: _, + flags, + } = downlevel; + writeln!(output, "\t\t Shader Model: {shader_model:?}"); + let max_downlevel_flag_width = wgpu::DownlevelFlags::max_debug_print_width(); + for bit in wgpu::DownlevelFlags::all().iter() { + writeln!(output, "\t\t{:>width$}: {}", bit.name(), flags.contains(bit), width = max_downlevel_flag_width); + }; + + //////////////////// + // Texture Usages // + //////////////////// + + let max_format_name_size = max_texture_format_name_size(); + let texture_format_whitespace = " ".repeat(max_format_name_size); + + writeln!(output, "\n\t Texture Format Allowed Usages:"); + + write!(output, "\t\t {texture_format_whitespace}"); + wgpu::TextureUsages::println_table_header(output); + for format in TEXTURE_FORMAT_LIST { + let features = adapter.get_texture_format_features(format); + let format_name = texture_format_name(format); + write!(output, "\t\t{format_name:>0$}", max_format_name_size); + for bit in wgpu::TextureUsages::all().iter() { + write!(output, " │ "); + if features.allowed_usages.contains(bit) { + write!(output, "{}", bit.name()); + } + else { + let length = bit.name().len(); + write!(output, "{}", " ".repeat(length)); + } + }; + writeln!(output, " │"); + } + write!(output, "\t\t {texture_format_whitespace}"); + wgpu::TextureUsages::println_table_footer(output); + + ////////////////////////// + // Texture Format Flags // + ////////////////////////// + + writeln!(output, "\n\t Texture Format Flags:"); + + write!(output, "\t\t {texture_format_whitespace}"); + wgpu::TextureFormatFeatureFlags::println_table_header(output); + + for format in TEXTURE_FORMAT_LIST { + let features = adapter.get_texture_format_features(format); + let format_name = texture_format_name(format); + + write!(output, "\t\t{format_name:>0$}", max_format_name_size); + for bit in wgpu::TextureFormatFeatureFlags::all().iter() { + write!(output, " │ "); + if features.flags.contains(bit) { + write!(output, "{}", bit.name()); + } + else { + let length = bit.name().len(); + write!(output, "{}", " ".repeat(length)); + } + }; + writeln!(output, " │"); + } + write!(output, "\t\t {texture_format_whitespace}"); + wgpu::TextureFormatFeatureFlags::println_table_footer(output); +} + +pub fn print_adapters(output: &mut dyn io::Write) { + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default()); + let adapters = instance.enumerate_adapters(wgpu::Backends::all()); + + for (idx, adapter) in adapters.into_iter().enumerate() { + print_adapter(output, &adapter, idx); + } +} diff --git a/wgpu-info/src/main.rs b/wgpu-info/src/main.rs index aa6fd2f262..e65b2f5ee3 100644 --- a/wgpu-info/src/main.rs +++ b/wgpu-info/src/main.rs @@ -1,435 +1,11 @@ #[cfg(not(target_arch = "wasm32"))] -mod inner { - use std::{ - process::{exit, Command}, - time::Instant, - }; - - use bitflags::Flags; - - // Lets keep these on one line - #[rustfmt::skip] - const TEXTURE_FORMAT_LIST: [wgpu::TextureFormat; 114] = [ - wgpu::TextureFormat::R8Unorm, - wgpu::TextureFormat::R8Snorm, - wgpu::TextureFormat::R8Uint, - wgpu::TextureFormat::R8Sint, - wgpu::TextureFormat::R16Uint, - wgpu::TextureFormat::R16Sint, - wgpu::TextureFormat::R16Unorm, - wgpu::TextureFormat::R16Snorm, - wgpu::TextureFormat::R16Float, - wgpu::TextureFormat::Rg8Unorm, - wgpu::TextureFormat::Rg8Snorm, - wgpu::TextureFormat::Rg8Uint, - wgpu::TextureFormat::Rg8Sint, - wgpu::TextureFormat::R32Uint, - wgpu::TextureFormat::R32Sint, - wgpu::TextureFormat::R32Float, - wgpu::TextureFormat::Rg16Uint, - wgpu::TextureFormat::Rg16Sint, - wgpu::TextureFormat::Rg16Unorm, - wgpu::TextureFormat::Rg16Snorm, - wgpu::TextureFormat::Rg16Float, - wgpu::TextureFormat::Rgba8Unorm, - wgpu::TextureFormat::Rgba8UnormSrgb, - wgpu::TextureFormat::Rgba8Snorm, - wgpu::TextureFormat::Rgba8Uint, - wgpu::TextureFormat::Rgba8Sint, - wgpu::TextureFormat::Bgra8Unorm, - wgpu::TextureFormat::Bgra8UnormSrgb, - wgpu::TextureFormat::Rgb10a2Unorm, - wgpu::TextureFormat::Rg11b10Float, - wgpu::TextureFormat::Rg32Uint, - wgpu::TextureFormat::Rg32Sint, - wgpu::TextureFormat::Rg32Float, - wgpu::TextureFormat::Rgba16Uint, - wgpu::TextureFormat::Rgba16Sint, - wgpu::TextureFormat::Rgba16Unorm, - wgpu::TextureFormat::Rgba16Snorm, - wgpu::TextureFormat::Rgba16Float, - wgpu::TextureFormat::Rgba32Uint, - wgpu::TextureFormat::Rgba32Sint, - wgpu::TextureFormat::Rgba32Float, - wgpu::TextureFormat::Stencil8, - wgpu::TextureFormat::Depth16Unorm, - wgpu::TextureFormat::Depth32Float, - wgpu::TextureFormat::Depth32FloatStencil8, - wgpu::TextureFormat::Depth24Plus, - wgpu::TextureFormat::Depth24PlusStencil8, - wgpu::TextureFormat::Rgb9e5Ufloat, - wgpu::TextureFormat::Bc1RgbaUnorm, - wgpu::TextureFormat::Bc1RgbaUnormSrgb, - wgpu::TextureFormat::Bc2RgbaUnorm, - wgpu::TextureFormat::Bc2RgbaUnormSrgb, - wgpu::TextureFormat::Bc3RgbaUnorm, - wgpu::TextureFormat::Bc3RgbaUnormSrgb, - wgpu::TextureFormat::Bc4RUnorm, - wgpu::TextureFormat::Bc4RSnorm, - wgpu::TextureFormat::Bc5RgUnorm, - wgpu::TextureFormat::Bc5RgSnorm, - wgpu::TextureFormat::Bc6hRgbUfloat, - wgpu::TextureFormat::Bc6hRgbFloat, - wgpu::TextureFormat::Bc7RgbaUnorm, - wgpu::TextureFormat::Bc7RgbaUnormSrgb, - wgpu::TextureFormat::Etc2Rgb8Unorm, - wgpu::TextureFormat::Etc2Rgb8UnormSrgb, - wgpu::TextureFormat::Etc2Rgb8A1Unorm, - wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb, - wgpu::TextureFormat::Etc2Rgba8Unorm, - wgpu::TextureFormat::Etc2Rgba8UnormSrgb, - wgpu::TextureFormat::EacR11Unorm, - wgpu::TextureFormat::EacR11Snorm, - wgpu::TextureFormat::EacRg11Unorm, - wgpu::TextureFormat::EacRg11Snorm, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::Hdr }, - ]; - - // Lets keep these on one line - #[rustfmt::skip] - fn print_info_from_adapter(adapter: &wgpu::Adapter, idx: usize) { - let info = adapter.get_info(); - let downlevel = adapter.get_downlevel_capabilities(); - let features = adapter.features(); - let limits = adapter.limits(); - - ////////////////// - // Adapter Info // - ////////////////// - - println!("Adapter {idx}:"); - println!("\t Backend: {:?}", info.backend); - println!("\t Name: {:?}", info.name); - println!("\t VendorID: {:?}", info.vendor); - println!("\t DeviceID: {:?}", info.device); - println!("\t Type: {:?}", info.device_type); - println!("\t Driver: {:?}", info.driver); - println!("\tDriverInfo: {:?}", info.driver_info); - println!("\t Compliant: {:?}", downlevel.is_webgpu_compliant()); - - ////////////// - // Features // - ////////////// - - println!("\tFeatures:"); - let max_feature_flag_width = wgpu::Features::max_debug_print_width(); - for bit in wgpu::Features::all().iter() { - println!("\t\t{:>width$}: {}", bit.name(), features.contains(bit), width = max_feature_flag_width); - } - - //////////// - // Limits // - //////////// - - println!("\tLimits:"); - let wgpu::Limits { - max_texture_dimension_1d, - max_texture_dimension_2d, - max_texture_dimension_3d, - max_texture_array_layers, - max_bind_groups, - max_bindings_per_bind_group, - max_dynamic_uniform_buffers_per_pipeline_layout, - max_dynamic_storage_buffers_per_pipeline_layout, - max_sampled_textures_per_shader_stage, - max_samplers_per_shader_stage, - max_storage_buffers_per_shader_stage, - max_storage_textures_per_shader_stage, - max_uniform_buffers_per_shader_stage, - max_uniform_buffer_binding_size, - max_storage_buffer_binding_size, - max_buffer_size, - max_vertex_buffers, - max_vertex_attributes, - max_vertex_buffer_array_stride, - max_push_constant_size, - min_uniform_buffer_offset_alignment, - min_storage_buffer_offset_alignment, - max_inter_stage_shader_components, - max_compute_workgroup_storage_size, - max_compute_invocations_per_workgroup, - max_compute_workgroup_size_x, - max_compute_workgroup_size_y, - max_compute_workgroup_size_z, - max_compute_workgroups_per_dimension, - } = limits; - println!("\t\t Max Texture Dimension 1d: {max_texture_dimension_1d}"); - println!("\t\t Max Texture Dimension 2d: {max_texture_dimension_2d}"); - println!("\t\t Max Texture Dimension 3d: {max_texture_dimension_3d}"); - println!("\t\t Max Texture Array Layers: {max_texture_array_layers}"); - println!("\t\t Max Bind Groups: {max_bind_groups}"); - println!("\t\t Max Bindings Per Bind Group: {max_bindings_per_bind_group}"); - println!("\t\t Max Dynamic Uniform Buffers Per Pipeline Layout: {max_dynamic_uniform_buffers_per_pipeline_layout}"); - println!("\t\t Max Dynamic Storage Buffers Per Pipeline Layout: {max_dynamic_storage_buffers_per_pipeline_layout}"); - println!("\t\t Max Sampled Textures Per Shader Stage: {max_sampled_textures_per_shader_stage}"); - println!("\t\t Max Samplers Per Shader Stage: {max_samplers_per_shader_stage}"); - println!("\t\t Max Storage Buffers Per Shader Stage: {max_storage_buffers_per_shader_stage}"); - println!("\t\t Max Storage Textures Per Shader Stage: {max_storage_textures_per_shader_stage}"); - println!("\t\t Max Uniform Buffers Per Shader Stage: {max_uniform_buffers_per_shader_stage}"); - println!("\t\t Max Uniform Buffer Binding Size: {max_uniform_buffer_binding_size}"); - println!("\t\t Max Storage Buffer Binding Size: {max_storage_buffer_binding_size}"); - println!("\t\t Max Buffer Size: {max_buffer_size}"); - println!("\t\t Max Vertex Buffers: {max_vertex_buffers}"); - println!("\t\t Max Vertex Attributes: {max_vertex_attributes}"); - println!("\t\t Max Vertex Buffer Array Stride: {max_vertex_buffer_array_stride}"); - println!("\t\t Max Push Constant Size: {max_push_constant_size}"); - println!("\t\t Min Uniform Buffer Offset Alignment: {min_uniform_buffer_offset_alignment}"); - println!("\t\t Min Storage Buffer Offset Alignment: {min_storage_buffer_offset_alignment}"); - println!("\t\t Max Inter-Stage Shader Component: {max_inter_stage_shader_components}"); - println!("\t\t Max Compute Workgroup Storage Size: {max_compute_workgroup_storage_size}"); - println!("\t\t Max Compute Invocations Per Workgroup: {max_compute_invocations_per_workgroup}"); - println!("\t\t Max Compute Workgroup Size X: {max_compute_workgroup_size_x}"); - println!("\t\t Max Compute Workgroup Size Y: {max_compute_workgroup_size_y}"); - println!("\t\t Max Compute Workgroup Size Z: {max_compute_workgroup_size_z}"); - println!("\t\t Max Compute Workgroups Per Dimension: {max_compute_workgroups_per_dimension}"); - - ////////////////////////// - // Downlevel Properties // - ////////////////////////// - - println!("\tDownlevel Properties:"); - let wgpu::DownlevelCapabilities { - shader_model, - limits: _, - flags, - } = downlevel; - println!("\t\t Shader Model: {shader_model:?}"); - let max_downlevel_flag_width = wgpu::DownlevelFlags::max_debug_print_width(); - for bit in wgpu::DownlevelFlags::all().iter() { - println!("\t\t{:>width$}: {}", bit.name(), flags.contains(bit), width = max_downlevel_flag_width); - }; - - //////////////////// - // Texture Usages // - //////////////////// - - let max_format_name_size = max_texture_format_name_size(); - let texture_format_whitespace = " ".repeat(max_format_name_size); - - println!("\n\t Texture Format Allowed Usages:"); - - print!("\t\t {texture_format_whitespace}"); - wgpu::TextureUsages::println_table_header(); - for format in TEXTURE_FORMAT_LIST { - let features = adapter.get_texture_format_features(format); - let format_name = texture_format_name(format); - print!("\t\t{format_name:>0$}", max_format_name_size); - for bit in wgpu::TextureUsages::all().iter() { - print!(" │ "); - if features.allowed_usages.contains(bit) { - print!("{}", bit.name()); - } - else { - let length = bit.name().len(); - print!("{}", " ".repeat(length)) - } - }; - println!(" │"); - } - print!("\t\t {texture_format_whitespace}"); - wgpu::TextureUsages::println_table_footer(); - - ////////////////////////// - // Texture Format Flags // - ////////////////////////// - - println!("\n\t Texture Format Flags:"); - - print!("\t\t {texture_format_whitespace}"); - wgpu::TextureFormatFeatureFlags::println_table_header(); - - for format in TEXTURE_FORMAT_LIST { - let features = adapter.get_texture_format_features(format); - let format_name = texture_format_name(format); - - print!("\t\t{format_name:>0$}", max_format_name_size); - for bit in wgpu::TextureFormatFeatureFlags::all().iter() { - print!(" │ "); - if features.flags.contains(bit) { - print!("{}", bit.name()); - } - else { - let length = bit.name().len(); - print!("{}", " ".repeat(length)) - } - }; - println!(" │"); - } - print!("\t\t {texture_format_whitespace}"); - wgpu::TextureFormatFeatureFlags::println_table_footer(); - } - - pub fn main() { - env_logger::init(); - let args: Vec<_> = std::env::args().skip(1).collect(); - - let instance = wgpu::Instance::default(); - let adapters: Vec<_> = instance.enumerate_adapters(wgpu::Backends::all()).collect(); - let adapter_count = adapters.len(); - - if args.is_empty() { - for (idx, adapter) in adapters.into_iter().enumerate() { - print_info_from_adapter(&adapter, idx) - } - } else { - let all_start = Instant::now(); - - for (idx, adapter) in adapters.into_iter().enumerate() { - let adapter_start_time = Instant::now(); - let idx = idx + 1; - let info = adapter.get_info(); - println!( - "=========== TESTING {} on {:?} ({} of {}) ===========", - info.name, info.backend, idx, adapter_count - ); - let exit_status = Command::new(&args[0]) - .args(&args[1..]) - .env("WGPU_ADAPTER_NAME", &info.name) - .env( - "WGPU_BACKEND", - match info.backend { - wgpu::Backend::Empty => unreachable!(), - wgpu::Backend::Vulkan => "vulkan", - wgpu::Backend::Metal => "metal", - wgpu::Backend::Dx12 => "dx12", - wgpu::Backend::Dx11 => "dx11", - wgpu::Backend::Gl => "gl", - wgpu::Backend::BrowserWebGpu => "webgpu", - }, - ) - .spawn() - .unwrap() - .wait() - .unwrap(); - - let adapter_time = adapter_start_time.elapsed().as_secs_f32(); - - if exit_status.success() { - println!( - "=========== PASSED! {} on {:?} ({} of {}) in {:.3}s ===========", - info.name, info.backend, idx, adapter_count, adapter_time - ); - } else { - println!( - "=========== FAILED! {} on {:?} ({} of {}) in {:.3}s ===========", - info.name, info.backend, idx, adapter_count, adapter_time - ); - exit(1); - } - } - - let all_time = all_start.elapsed().as_secs_f32(); - - println!("=========== {adapter_count} adapters PASSED in {all_time:.3}s ==========="); - } - } - - trait FlagsExt: Flags { - fn name(&self) -> &'static str { - self.iter_names().next().unwrap().0 - } - - fn valid_bits() -> std::iter::Enumerate> { - Self::all().iter().enumerate() - } - - fn max_debug_print_width() -> usize { - let mut width = 0; - for bit in Self::all().iter() { - width = width.max(bit.name().len()); - } - width - } - - fn println_table_header() { - print!("┌─"); - for (i, bit) in Self::valid_bits() { - if i != 0 { - print!("─┬─"); - } - let length = bit.name().len(); - print!("{}", "─".repeat(length)); - } - println!("─┐"); - } - - fn println_table_footer() { - print!("└─"); - for (i, bit) in Self::valid_bits() { - if i != 0 { - print!("─┴─"); - } - let length = bit.name().len(); - print!("{}", "─".repeat(length)); - } - println!("─┘") - } - } - - impl FlagsExt for T where T: Flags {} - - fn max_texture_format_name_size() -> usize { - TEXTURE_FORMAT_LIST - .into_iter() - .map(|f| texture_format_name(f).len()) - .max() - .unwrap() - } - - fn texture_format_name(format: wgpu::TextureFormat) -> String { - match format { - wgpu::TextureFormat::Astc { block, channel } => { - format!("Astc{block:?}{channel:?}:") - } - _ => { - format!("{format:?}:") - } - } - } -} +mod cli; +#[cfg(not(target_arch = "wasm32"))] +mod human; fn main() { #[cfg(not(target_arch = "wasm32"))] - inner::main(); + { + cli::main(); + } } From 2f8f3c116e4cac341ae87f59393e54094d6ed970 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Sun, 11 Jun 2023 02:27:01 -0400 Subject: [PATCH 02/41] Fill out proper CLI --- Cargo.lock | 4 + Cargo.toml | 1 + wgpu-info/Cargo.toml | 4 + wgpu-info/src/cli.rs | 78 +++++++-- wgpu-info/src/human.rs | 330 ++++++++++++--------------------------- wgpu-info/src/main.rs | 9 +- wgpu-info/src/report.rs | 58 +++++++ wgpu-info/src/texture.rs | 137 ++++++++++++++++ wgpu-types/src/lib.rs | 20 +-- wgpu/src/lib.rs | 2 +- 10 files changed, 386 insertions(+), 257 deletions(-) create mode 100644 wgpu-info/src/report.rs create mode 100644 wgpu-info/src/texture.rs diff --git a/Cargo.lock b/Cargo.lock index 6041f3e8ff..59d9ea60f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3191,10 +3191,14 @@ dependencies = [ name = "wgpu-info" version = "0.16.0" dependencies = [ + "anyhow", "bitflags 2.3.1", "env_logger", "pico-args", + "serde", + "serde_json", "wgpu", + "wgpu-types", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index be8b06fd83..3c7ba7e29e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ rev = "b99d58ea435090e561377949f428bce2c18451bb" version = "0.12.0" [workspace.dependencies] +anyhow = "1.0" arrayvec = "0.7" async-executor = "1" bitflags = "2" diff --git a/wgpu-info/Cargo.toml b/wgpu-info/Cargo.toml index d6f4293972..8d6bb45636 100644 --- a/wgpu-info/Cargo.toml +++ b/wgpu-info/Cargo.toml @@ -11,7 +11,11 @@ license.workspace = true publish = false [dependencies] +anyhow.workspace = true bitflags.workspace = true env_logger.workspace = true pico-args.workspace = true +serde.workspace = true +serde_json.workspace = true wgpu.workspace = true +wgpu-types = { workspace = true, features = ["serde"] } diff --git a/wgpu-info/src/cli.rs b/wgpu-info/src/cli.rs index 68349edfb0..3a8fa82a46 100644 --- a/wgpu-info/src/cli.rs +++ b/wgpu-info/src/cli.rs @@ -1,42 +1,90 @@ use std::{io, process::exit}; +use anyhow::Context; + const HELP: &str = "\ -Usage: wgpu-info [--output ] [--json] +Usage: wgpu-info [--input ] [--output ] [--json] Options: - -h, --help Print this help - -o, --output Destination to write output to - -j, --json Output JSON information instead of text + -h, --help Print this help message. + -i, --input Source to read JSON report from. (\"-\" reads from stdin) + -o, --output Destination to write output to. (\"-\" writes to stdout) + -j, --json Output JSON information instead of human-readable text. "; -pub fn main() { +fn exit_with_help() { + eprintln!("{HELP}"); + exit(101); +} + +pub fn main() -> anyhow::Result<()> { let mut args = pico_args::Arguments::from_env(); + // Check for help flag before parsing arguments let help = args.contains(["-h", "--help"]); if help { - eprintln!("{HELP}"); - exit(101); + exit_with_help(); } + // Argument parsing + let input_path: Option = args.opt_value_from_str(["-i", "--input"]).unwrap(); let output_path: Option = args.opt_value_from_str(["-o", "--output"]).unwrap(); let json = args.contains(["-j", "--json"]); + let remaining = args.finish(); + if !remaining.is_empty() { + eprint!("Unknown argument(s): "); + for arg in remaining { + eprint!("\"{}\" ", arg.to_string_lossy()); + } + eprint!("\n\n"); + exit_with_help(); + } + env_logger::init(); - let mut file_handle; - let mut std_handle; - let output: &mut dyn io::Write = match output_path { + // Generate or load report + let report = match input_path.as_deref() { + // Pull report from stdin or file Some(path) => { - file_handle = std::fs::File::create(path).unwrap(); - &mut file_handle + let json = if "-" == path { + std::io::read_to_string(std::io::stdin()).context("Could not read from stdin")? + } else { + std::fs::read_to_string(path) + .with_context(|| format!("Could not read from file \"{path}\""))? + }; + crate::report::GpuReport::from_json(&json).context("Could not parse JSON")? } - None => { + // Generate the report natively + None => crate::report::GpuReport::generate(), + }; + + // Setup output writer + let mut file_handle; + let mut std_handle; + let output: &mut dyn io::Write = match output_path.as_deref() { + None | Some("-") => { std_handle = io::stdout(); &mut std_handle } + Some(path) => { + file_handle = std::fs::File::create(path) + .with_context(|| format!("Could not create file \"{path}\""))?; + &mut file_handle + } }; + let mut output = io::BufWriter::new(output); + + let output_name = output_path.as_deref().unwrap_or("stdout"); - if !json { - crate::human::print_adapters(output) + if json { + report + .into_json(output) + .with_context(|| format!("Failed to write to output: {output_name}"))?; + } else { + crate::human::print_adapters(&mut output, &report) + .with_context(|| format!("Failed to write to output: {output_name}"))?; } + + Ok(()) } diff --git a/wgpu-info/src/human.rs b/wgpu-info/src/human.rs index bdf72ceb54..66b0e506e2 100644 --- a/wgpu-info/src/human.rs +++ b/wgpu-info/src/human.rs @@ -2,6 +2,11 @@ use std::io; use bitflags::Flags; +use crate::{ + report::{AdapterReport, GpuReport}, + texture::{self, TEXTURE_FORMAT_LIST}, +}; + trait FlagsExt: Flags { fn name(&self) -> &'static str { self.iter_names().next().unwrap().0 @@ -19,208 +24,76 @@ trait FlagsExt: Flags { width } - fn println_table_header(output: &mut dyn io::Write) { - write!(output, "┌─"); + fn println_table_header(output: &mut impl io::Write) -> io::Result<()> { + write!(output, "┌─")?; for (i, bit) in Self::valid_bits() { if i != 0 { - write!(output, "─┬─"); + write!(output, "─┬─")?; } let length = bit.name().len(); - write!(output, "{}", "─".repeat(length)); + write!(output, "{}", "─".repeat(length))?; } - writeln!(output, "─┐"); + writeln!(output, "─┐")?; + Ok(()) } - fn println_table_footer(output: &mut dyn io::Write) { - write!(output, "└─"); + fn println_table_footer(output: &mut impl io::Write) -> io::Result<()> { + write!(output, "└─")?; for (i, bit) in Self::valid_bits() { if i != 0 { - write!(output, "─┴─"); + write!(output, "─┴─")?; } let length = bit.name().len(); - write!(output, "{}", "─".repeat(length)); + write!(output, "{}", "─".repeat(length))?; } - writeln!(output, "─┘"); + writeln!(output, "─┘")?; + Ok(()) } } impl FlagsExt for T where T: Flags {} -fn max_texture_format_name_size() -> usize { - TEXTURE_FORMAT_LIST - .into_iter() - .map(|f| texture_format_name(f).len()) - .max() - .unwrap() -} - -fn texture_format_name(format: wgpu::TextureFormat) -> String { - match format { - wgpu::TextureFormat::Astc { block, channel } => { - format!("Astc{block:?}{channel:?}:") - } - _ => { - format!("{format:?}:") - } - } -} - -// Lets keep these on one line +// Lets keep these print statements on one line #[rustfmt::skip] -const TEXTURE_FORMAT_LIST: [wgpu::TextureFormat; 114] = [ - wgpu::TextureFormat::R8Unorm, - wgpu::TextureFormat::R8Snorm, - wgpu::TextureFormat::R8Uint, - wgpu::TextureFormat::R8Sint, - wgpu::TextureFormat::R16Uint, - wgpu::TextureFormat::R16Sint, - wgpu::TextureFormat::R16Unorm, - wgpu::TextureFormat::R16Snorm, - wgpu::TextureFormat::R16Float, - wgpu::TextureFormat::Rg8Unorm, - wgpu::TextureFormat::Rg8Snorm, - wgpu::TextureFormat::Rg8Uint, - wgpu::TextureFormat::Rg8Sint, - wgpu::TextureFormat::R32Uint, - wgpu::TextureFormat::R32Sint, - wgpu::TextureFormat::R32Float, - wgpu::TextureFormat::Rg16Uint, - wgpu::TextureFormat::Rg16Sint, - wgpu::TextureFormat::Rg16Unorm, - wgpu::TextureFormat::Rg16Snorm, - wgpu::TextureFormat::Rg16Float, - wgpu::TextureFormat::Rgba8Unorm, - wgpu::TextureFormat::Rgba8UnormSrgb, - wgpu::TextureFormat::Rgba8Snorm, - wgpu::TextureFormat::Rgba8Uint, - wgpu::TextureFormat::Rgba8Sint, - wgpu::TextureFormat::Bgra8Unorm, - wgpu::TextureFormat::Bgra8UnormSrgb, - wgpu::TextureFormat::Rgb10a2Unorm, - wgpu::TextureFormat::Rg11b10Float, - wgpu::TextureFormat::Rg32Uint, - wgpu::TextureFormat::Rg32Sint, - wgpu::TextureFormat::Rg32Float, - wgpu::TextureFormat::Rgba16Uint, - wgpu::TextureFormat::Rgba16Sint, - wgpu::TextureFormat::Rgba16Unorm, - wgpu::TextureFormat::Rgba16Snorm, - wgpu::TextureFormat::Rgba16Float, - wgpu::TextureFormat::Rgba32Uint, - wgpu::TextureFormat::Rgba32Sint, - wgpu::TextureFormat::Rgba32Float, - wgpu::TextureFormat::Stencil8, - wgpu::TextureFormat::Depth16Unorm, - wgpu::TextureFormat::Depth32Float, - wgpu::TextureFormat::Depth32FloatStencil8, - wgpu::TextureFormat::Depth24Plus, - wgpu::TextureFormat::Depth24PlusStencil8, - wgpu::TextureFormat::Rgb9e5Ufloat, - wgpu::TextureFormat::Bc1RgbaUnorm, - wgpu::TextureFormat::Bc1RgbaUnormSrgb, - wgpu::TextureFormat::Bc2RgbaUnorm, - wgpu::TextureFormat::Bc2RgbaUnormSrgb, - wgpu::TextureFormat::Bc3RgbaUnorm, - wgpu::TextureFormat::Bc3RgbaUnormSrgb, - wgpu::TextureFormat::Bc4RUnorm, - wgpu::TextureFormat::Bc4RSnorm, - wgpu::TextureFormat::Bc5RgUnorm, - wgpu::TextureFormat::Bc5RgSnorm, - wgpu::TextureFormat::Bc6hRgbUfloat, - wgpu::TextureFormat::Bc6hRgbFloat, - wgpu::TextureFormat::Bc7RgbaUnorm, - wgpu::TextureFormat::Bc7RgbaUnormSrgb, - wgpu::TextureFormat::Etc2Rgb8Unorm, - wgpu::TextureFormat::Etc2Rgb8UnormSrgb, - wgpu::TextureFormat::Etc2Rgb8A1Unorm, - wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb, - wgpu::TextureFormat::Etc2Rgba8Unorm, - wgpu::TextureFormat::Etc2Rgba8UnormSrgb, - wgpu::TextureFormat::EacR11Unorm, - wgpu::TextureFormat::EacR11Snorm, - wgpu::TextureFormat::EacRg11Unorm, - wgpu::TextureFormat::EacRg11Snorm, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::Hdr }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::Unorm }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::UnormSrgb }, - wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::Hdr }, -]; - -// Lets keep these on one line -#[rustfmt::skip] -fn print_adapter(output: &mut dyn io::Write, adapter: &wgpu::Adapter, idx: usize) { - let info = adapter.get_info(); - let downlevel = adapter.get_downlevel_capabilities(); - let features = adapter.features(); - let limits = adapter.limits(); +fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize) -> io::Result<()> { + let AdapterReport { + info, + features, + limits, + downlevel_caps: + downlevel, + texture_format_features + } = &report; ////////////////// // Adapter Info // ////////////////// - writeln!(output, "Adapter {idx}:"); - writeln!(output, "\t Backend: {:?}", info.backend); - writeln!(output, "\t Name: {:?}", info.name); - writeln!(output, "\t VendorID: {:?}", info.vendor); - writeln!(output, "\t DeviceID: {:?}", info.device); - writeln!(output, "\t Type: {:?}", info.device_type); - writeln!(output, "\t Driver: {:?}", info.driver); - writeln!(output, "\tDriverInfo: {:?}", info.driver_info); - writeln!(output, "\t Compliant: {:?}", downlevel.is_webgpu_compliant()); + writeln!(output, "Adapter {idx}:")?; + writeln!(output, "\t Backend: {:?}", info.backend)?; + writeln!(output, "\t Name: {:?}", info.name)?; + writeln!(output, "\t VendorID: {:?}", info.vendor)?; + writeln!(output, "\t DeviceID: {:?}", info.device)?; + writeln!(output, "\t Type: {:?}", info.device_type)?; + writeln!(output, "\t Driver: {:?}", info.driver)?; + writeln!(output, "\tDriverInfo: {:?}", info.driver_info)?; + writeln!(output, "\t Compliant: {:?}", downlevel.is_webgpu_compliant())?; ////////////// // Features // ////////////// - writeln!(output, "\tFeatures:"); + writeln!(output, "\tFeatures:")?; let max_feature_flag_width = wgpu::Features::max_debug_print_width(); for bit in wgpu::Features::all().iter() { - writeln!(output, "\t\t{:>width$}: {}", bit.name(), features.contains(bit), width = max_feature_flag_width); + writeln!(output, "\t\t{:>width$}: {}", bit.name(), features.contains(bit), width = max_feature_flag_width)?; } //////////// // Limits // //////////// - writeln!(output, "\tLimits:"); + writeln!(output, "\tLimits:")?; let wgpu::Limits { max_texture_dimension_1d, max_texture_dimension_2d, @@ -252,117 +125,116 @@ fn print_adapter(output: &mut dyn io::Write, adapter: &wgpu::Adapter, idx: usize max_compute_workgroup_size_z, max_compute_workgroups_per_dimension, } = limits; - writeln!(output, "\t\t Max Texture Dimension 1d: {max_texture_dimension_1d}"); - writeln!(output, "\t\t Max Texture Dimension 2d: {max_texture_dimension_2d}"); - writeln!(output, "\t\t Max Texture Dimension 3d: {max_texture_dimension_3d}"); - writeln!(output, "\t\t Max Texture Array Layers: {max_texture_array_layers}"); - writeln!(output, "\t\t Max Bind Groups: {max_bind_groups}"); - writeln!(output, "\t\t Max Bindings Per Bind Group: {max_bindings_per_bind_group}"); - writeln!(output, "\t\t Max Dynamic Uniform Buffers Per Pipeline Layout: {max_dynamic_uniform_buffers_per_pipeline_layout}"); - writeln!(output, "\t\t Max Dynamic Storage Buffers Per Pipeline Layout: {max_dynamic_storage_buffers_per_pipeline_layout}"); - writeln!(output, "\t\t Max Sampled Textures Per Shader Stage: {max_sampled_textures_per_shader_stage}"); - writeln!(output, "\t\t Max Samplers Per Shader Stage: {max_samplers_per_shader_stage}"); - writeln!(output, "\t\t Max Storage Buffers Per Shader Stage: {max_storage_buffers_per_shader_stage}"); - writeln!(output, "\t\t Max Storage Textures Per Shader Stage: {max_storage_textures_per_shader_stage}"); - writeln!(output, "\t\t Max Uniform Buffers Per Shader Stage: {max_uniform_buffers_per_shader_stage}"); - writeln!(output, "\t\t Max Uniform Buffer Binding Size: {max_uniform_buffer_binding_size}"); - writeln!(output, "\t\t Max Storage Buffer Binding Size: {max_storage_buffer_binding_size}"); - writeln!(output, "\t\t Max Buffer Size: {max_buffer_size}"); - writeln!(output, "\t\t Max Vertex Buffers: {max_vertex_buffers}"); - writeln!(output, "\t\t Max Vertex Attributes: {max_vertex_attributes}"); - writeln!(output, "\t\t Max Vertex Buffer Array Stride: {max_vertex_buffer_array_stride}"); - writeln!(output, "\t\t Max Push Constant Size: {max_push_constant_size}"); - writeln!(output, "\t\t Min Uniform Buffer Offset Alignment: {min_uniform_buffer_offset_alignment}"); - writeln!(output, "\t\t Min Storage Buffer Offset Alignment: {min_storage_buffer_offset_alignment}"); - writeln!(output, "\t\t Max Inter-Stage Shader Component: {max_inter_stage_shader_components}"); - writeln!(output, "\t\t Max Compute Workgroup Storage Size: {max_compute_workgroup_storage_size}"); - writeln!(output, "\t\t Max Compute Invocations Per Workgroup: {max_compute_invocations_per_workgroup}"); - writeln!(output, "\t\t Max Compute Workgroup Size X: {max_compute_workgroup_size_x}"); - writeln!(output, "\t\t Max Compute Workgroup Size Y: {max_compute_workgroup_size_y}"); - writeln!(output, "\t\t Max Compute Workgroup Size Z: {max_compute_workgroup_size_z}"); - writeln!(output, "\t\t Max Compute Workgroups Per Dimension: {max_compute_workgroups_per_dimension}"); + writeln!(output, "\t\t Max Texture Dimension 1d: {max_texture_dimension_1d}")?; + writeln!(output, "\t\t Max Texture Dimension 2d: {max_texture_dimension_2d}")?; + writeln!(output, "\t\t Max Texture Dimension 3d: {max_texture_dimension_3d}")?; + writeln!(output, "\t\t Max Texture Array Layers: {max_texture_array_layers}")?; + writeln!(output, "\t\t Max Bind Groups: {max_bind_groups}")?; + writeln!(output, "\t\t Max Bindings Per Bind Group: {max_bindings_per_bind_group}")?; + writeln!(output, "\t\t Max Dynamic Uniform Buffers Per Pipeline Layout: {max_dynamic_uniform_buffers_per_pipeline_layout}")?; + writeln!(output, "\t\t Max Dynamic Storage Buffers Per Pipeline Layout: {max_dynamic_storage_buffers_per_pipeline_layout}")?; + writeln!(output, "\t\t Max Sampled Textures Per Shader Stage: {max_sampled_textures_per_shader_stage}")?; + writeln!(output, "\t\t Max Samplers Per Shader Stage: {max_samplers_per_shader_stage}")?; + writeln!(output, "\t\t Max Storage Buffers Per Shader Stage: {max_storage_buffers_per_shader_stage}")?; + writeln!(output, "\t\t Max Storage Textures Per Shader Stage: {max_storage_textures_per_shader_stage}")?; + writeln!(output, "\t\t Max Uniform Buffers Per Shader Stage: {max_uniform_buffers_per_shader_stage}")?; + writeln!(output, "\t\t Max Uniform Buffer Binding Size: {max_uniform_buffer_binding_size}")?; + writeln!(output, "\t\t Max Storage Buffer Binding Size: {max_storage_buffer_binding_size}")?; + writeln!(output, "\t\t Max Buffer Size: {max_buffer_size}")?; + writeln!(output, "\t\t Max Vertex Buffers: {max_vertex_buffers}")?; + writeln!(output, "\t\t Max Vertex Attributes: {max_vertex_attributes}")?; + writeln!(output, "\t\t Max Vertex Buffer Array Stride: {max_vertex_buffer_array_stride}")?; + writeln!(output, "\t\t Max Push Constant Size: {max_push_constant_size}")?; + writeln!(output, "\t\t Min Uniform Buffer Offset Alignment: {min_uniform_buffer_offset_alignment}")?; + writeln!(output, "\t\t Min Storage Buffer Offset Alignment: {min_storage_buffer_offset_alignment}")?; + writeln!(output, "\t\t Max Inter-Stage Shader Component: {max_inter_stage_shader_components}")?; + writeln!(output, "\t\t Max Compute Workgroup Storage Size: {max_compute_workgroup_storage_size}")?; + writeln!(output, "\t\t Max Compute Invocations Per Workgroup: {max_compute_invocations_per_workgroup}")?; + writeln!(output, "\t\t Max Compute Workgroup Size X: {max_compute_workgroup_size_x}")?; + writeln!(output, "\t\t Max Compute Workgroup Size Y: {max_compute_workgroup_size_y}")?; + writeln!(output, "\t\t Max Compute Workgroup Size Z: {max_compute_workgroup_size_z}")?; + writeln!(output, "\t\t Max Compute Workgroups Per Dimension: {max_compute_workgroups_per_dimension}")?; ////////////////////////// // Downlevel Properties // ////////////////////////// - writeln!(output, "\tDownlevel Properties:"); + writeln!(output, "\tDownlevel Properties:")?; let wgpu::DownlevelCapabilities { shader_model, limits: _, flags, } = downlevel; - writeln!(output, "\t\t Shader Model: {shader_model:?}"); + writeln!(output, "\t\t Shader Model: {shader_model:?}")?; let max_downlevel_flag_width = wgpu::DownlevelFlags::max_debug_print_width(); for bit in wgpu::DownlevelFlags::all().iter() { - writeln!(output, "\t\t{:>width$}: {}", bit.name(), flags.contains(bit), width = max_downlevel_flag_width); + writeln!(output, "\t\t{:>width$}: {}", bit.name(), flags.contains(bit), width = max_downlevel_flag_width)?; }; //////////////////// // Texture Usages // //////////////////// - let max_format_name_size = max_texture_format_name_size(); + let max_format_name_size = texture::max_texture_format_string_size(); let texture_format_whitespace = " ".repeat(max_format_name_size); - writeln!(output, "\n\t Texture Format Allowed Usages:"); + writeln!(output, "\n\t Texture Format Allowed Usages:")?; - write!(output, "\t\t {texture_format_whitespace}"); - wgpu::TextureUsages::println_table_header(output); + write!(output, "\t\t {texture_format_whitespace}")?; + wgpu::TextureUsages::println_table_header(output)?; for format in TEXTURE_FORMAT_LIST { - let features = adapter.get_texture_format_features(format); - let format_name = texture_format_name(format); - write!(output, "\t\t{format_name:>0$}", max_format_name_size); + let features = texture_format_features[&format]; + let format_name = texture::texture_format_name(format); + write!(output, "\t\t{format_name:>0$}", max_format_name_size)?; for bit in wgpu::TextureUsages::all().iter() { - write!(output, " │ "); + write!(output, " │ ")?; if features.allowed_usages.contains(bit) { - write!(output, "{}", bit.name()); + write!(output, "{}", bit.name())?; } else { let length = bit.name().len(); - write!(output, "{}", " ".repeat(length)); + write!(output, "{}", " ".repeat(length))?; } }; - writeln!(output, " │"); + writeln!(output, " │")?; } - write!(output, "\t\t {texture_format_whitespace}"); - wgpu::TextureUsages::println_table_footer(output); + write!(output, "\t\t {texture_format_whitespace}")?; + wgpu::TextureUsages::println_table_footer(output)?; ////////////////////////// // Texture Format Flags // ////////////////////////// - writeln!(output, "\n\t Texture Format Flags:"); + writeln!(output, "\n\t Texture Format Flags:")?; - write!(output, "\t\t {texture_format_whitespace}"); - wgpu::TextureFormatFeatureFlags::println_table_header(output); + write!(output, "\t\t {texture_format_whitespace}")?; + wgpu::TextureFormatFeatureFlags::println_table_header(output)?; for format in TEXTURE_FORMAT_LIST { - let features = adapter.get_texture_format_features(format); - let format_name = texture_format_name(format); + let features = texture_format_features[&format]; + let format_name = texture::texture_format_name(format); - write!(output, "\t\t{format_name:>0$}", max_format_name_size); + write!(output, "\t\t{format_name:>0$}", max_format_name_size)?; for bit in wgpu::TextureFormatFeatureFlags::all().iter() { - write!(output, " │ "); + write!(output, " │ ")?; if features.flags.contains(bit) { - write!(output, "{}", bit.name()); + write!(output, "{}", bit.name())?; } else { let length = bit.name().len(); - write!(output, "{}", " ".repeat(length)); + write!(output, "{}", " ".repeat(length))?; } }; - writeln!(output, " │"); + writeln!(output, " │")?; } - write!(output, "\t\t {texture_format_whitespace}"); - wgpu::TextureFormatFeatureFlags::println_table_footer(output); + write!(output, "\t\t {texture_format_whitespace}")?; + wgpu::TextureFormatFeatureFlags::println_table_footer(output)?; + Ok(()) } -pub fn print_adapters(output: &mut dyn io::Write) { - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default()); - let adapters = instance.enumerate_adapters(wgpu::Backends::all()); - - for (idx, adapter) in adapters.into_iter().enumerate() { - print_adapter(output, &adapter, idx); +pub fn print_adapters(output: &mut impl io::Write, report: &GpuReport) -> io::Result<()> { + for (idx, adapter) in report.devices.iter().enumerate() { + print_adapter(output, adapter, idx)?; } + Ok(()) } diff --git a/wgpu-info/src/main.rs b/wgpu-info/src/main.rs index e65b2f5ee3..9a37976f0e 100644 --- a/wgpu-info/src/main.rs +++ b/wgpu-info/src/main.rs @@ -2,10 +2,15 @@ mod cli; #[cfg(not(target_arch = "wasm32"))] mod human; +#[cfg(not(target_arch = "wasm32"))] +mod report; +#[cfg(not(target_arch = "wasm32"))] +mod texture; -fn main() { +fn main() -> anyhow::Result<()> { #[cfg(not(target_arch = "wasm32"))] { - cli::main(); + cli::main()?; } + Ok(()) } diff --git a/wgpu-info/src/report.rs b/wgpu-info/src/report.rs new file mode 100644 index 0000000000..656e96ee87 --- /dev/null +++ b/wgpu-info/src/report.rs @@ -0,0 +1,58 @@ +use std::{collections::HashMap, io}; + +use serde::{Deserialize, Serialize}; +use wgpu::{ + AdapterInfo, DownlevelCapabilities, Features, Limits, TextureFormat, TextureFormatFeatures, +}; + +use crate::texture; + +#[derive(Deserialize, Serialize)] +pub struct GpuReport { + pub devices: Vec, +} + +impl GpuReport { + pub fn generate() -> Self { + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default()); + let adapters = instance.enumerate_adapters(wgpu::Backends::all()); + + let mut devices = Vec::with_capacity(adapters.len()); + for adapter in adapters { + let features = adapter.features(); + let limits = adapter.limits(); + let downlevel_caps = adapter.get_downlevel_capabilities(); + let texture_format_features = texture::TEXTURE_FORMAT_LIST + .into_iter() + .map(|format| (format, adapter.get_texture_format_features(format))) + .collect(); + + devices.push(AdapterReport { + info: adapter.get_info(), + features, + limits, + downlevel_caps, + texture_format_features, + }); + } + + Self { devices } + } + + pub fn from_json(file: &str) -> serde_json::Result { + serde_json::from_str(file) + } + + pub fn into_json(self, output: impl io::Write) -> serde_json::Result<()> { + serde_json::to_writer_pretty(output, &self) + } +} + +#[derive(Deserialize, Serialize)] +pub struct AdapterReport { + pub info: AdapterInfo, + pub features: Features, + pub limits: Limits, + pub downlevel_caps: DownlevelCapabilities, + pub texture_format_features: HashMap, +} diff --git a/wgpu-info/src/texture.rs b/wgpu-info/src/texture.rs new file mode 100644 index 0000000000..5bafa67a59 --- /dev/null +++ b/wgpu-info/src/texture.rs @@ -0,0 +1,137 @@ +// Lets keep these on one line +#[rustfmt::skip] +pub const TEXTURE_FORMAT_LIST: [wgpu::TextureFormat; 114] = [ + wgpu::TextureFormat::R8Unorm, + wgpu::TextureFormat::R8Snorm, + wgpu::TextureFormat::R8Uint, + wgpu::TextureFormat::R8Sint, + wgpu::TextureFormat::R16Uint, + wgpu::TextureFormat::R16Sint, + wgpu::TextureFormat::R16Unorm, + wgpu::TextureFormat::R16Snorm, + wgpu::TextureFormat::R16Float, + wgpu::TextureFormat::Rg8Unorm, + wgpu::TextureFormat::Rg8Snorm, + wgpu::TextureFormat::Rg8Uint, + wgpu::TextureFormat::Rg8Sint, + wgpu::TextureFormat::R32Uint, + wgpu::TextureFormat::R32Sint, + wgpu::TextureFormat::R32Float, + wgpu::TextureFormat::Rg16Uint, + wgpu::TextureFormat::Rg16Sint, + wgpu::TextureFormat::Rg16Unorm, + wgpu::TextureFormat::Rg16Snorm, + wgpu::TextureFormat::Rg16Float, + wgpu::TextureFormat::Rgba8Unorm, + wgpu::TextureFormat::Rgba8UnormSrgb, + wgpu::TextureFormat::Rgba8Snorm, + wgpu::TextureFormat::Rgba8Uint, + wgpu::TextureFormat::Rgba8Sint, + wgpu::TextureFormat::Bgra8Unorm, + wgpu::TextureFormat::Bgra8UnormSrgb, + wgpu::TextureFormat::Rgb10a2Unorm, + wgpu::TextureFormat::Rg11b10Float, + wgpu::TextureFormat::Rg32Uint, + wgpu::TextureFormat::Rg32Sint, + wgpu::TextureFormat::Rg32Float, + wgpu::TextureFormat::Rgba16Uint, + wgpu::TextureFormat::Rgba16Sint, + wgpu::TextureFormat::Rgba16Unorm, + wgpu::TextureFormat::Rgba16Snorm, + wgpu::TextureFormat::Rgba16Float, + wgpu::TextureFormat::Rgba32Uint, + wgpu::TextureFormat::Rgba32Sint, + wgpu::TextureFormat::Rgba32Float, + wgpu::TextureFormat::Stencil8, + wgpu::TextureFormat::Depth16Unorm, + wgpu::TextureFormat::Depth32Float, + wgpu::TextureFormat::Depth32FloatStencil8, + wgpu::TextureFormat::Depth24Plus, + wgpu::TextureFormat::Depth24PlusStencil8, + wgpu::TextureFormat::Rgb9e5Ufloat, + wgpu::TextureFormat::Bc1RgbaUnorm, + wgpu::TextureFormat::Bc1RgbaUnormSrgb, + wgpu::TextureFormat::Bc2RgbaUnorm, + wgpu::TextureFormat::Bc2RgbaUnormSrgb, + wgpu::TextureFormat::Bc3RgbaUnorm, + wgpu::TextureFormat::Bc3RgbaUnormSrgb, + wgpu::TextureFormat::Bc4RUnorm, + wgpu::TextureFormat::Bc4RSnorm, + wgpu::TextureFormat::Bc5RgUnorm, + wgpu::TextureFormat::Bc5RgSnorm, + wgpu::TextureFormat::Bc6hRgbUfloat, + wgpu::TextureFormat::Bc6hRgbFloat, + wgpu::TextureFormat::Bc7RgbaUnorm, + wgpu::TextureFormat::Bc7RgbaUnormSrgb, + wgpu::TextureFormat::Etc2Rgb8Unorm, + wgpu::TextureFormat::Etc2Rgb8UnormSrgb, + wgpu::TextureFormat::Etc2Rgb8A1Unorm, + wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb, + wgpu::TextureFormat::Etc2Rgba8Unorm, + wgpu::TextureFormat::Etc2Rgba8UnormSrgb, + wgpu::TextureFormat::EacR11Unorm, + wgpu::TextureFormat::EacR11Snorm, + wgpu::TextureFormat::EacRg11Unorm, + wgpu::TextureFormat::EacRg11Snorm, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B4x4, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x4, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B5x5, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x5, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B6x6, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x5, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x6, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B8x8, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x5, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x6, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x8, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B10x10, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x10, channel: wgpu::AstcChannel::Hdr }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::Unorm }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::UnormSrgb }, + wgpu::TextureFormat::Astc { block: wgpu::AstcBlock::B12x12, channel: wgpu::AstcChannel::Hdr }, +]; + +pub fn max_texture_format_string_size() -> usize { + TEXTURE_FORMAT_LIST + .into_iter() + .map(|f| texture_format_name(f).len()) + .max() + .unwrap() +} + +pub fn texture_format_name(format: wgpu::TextureFormat) -> String { + match format { + wgpu::TextureFormat::Astc { block, channel } => { + format!("Astc{block:?}{channel:?}:") + } + _ => { + format!("{format:?}:") + } + } +} diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 7ce5652380..ebfc9f65aa 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -26,7 +26,7 @@ pub mod math; // behavior to this macro (unspecified bit do not produce an error). macro_rules! impl_bitflags { ($name:ident) => { - #[cfg(feature = "trace")] + #[cfg(feature = "serde")] impl serde::Serialize for $name { fn serialize(&self, serializer: S) -> Result where @@ -36,7 +36,7 @@ macro_rules! impl_bitflags { } } - #[cfg(feature = "replay")] + #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for $name { fn deserialize(deserializer: D) -> Result<$name, D::Error> where @@ -92,8 +92,7 @@ pub const QUERY_SIZE: u32 = 8; /// Backends supported by wgpu. #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "trace", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Backend { /// Dummy backend, used for testing. Empty = 0, @@ -825,8 +824,7 @@ impl Features { /// [`downlevel_defaults()`]: Limits::downlevel_defaults #[repr(C)] #[derive(Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "trace", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase", default))] pub struct Limits { /// Maximum allowed value for the `size.width` of a texture created with `TextureDimension::D1`. @@ -1116,6 +1114,7 @@ impl Limits { /// Represents the sets of additional limits on an adapter, /// which take place when running on downlevel backends. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DownlevelLimits {} #[allow(unknown_lints)] // derivable_impls is nightly only currently @@ -1128,6 +1127,7 @@ impl Default for DownlevelLimits { /// Lists various ways the underlying platform does not conform to the WebGPU standard. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DownlevelCapabilities { /// Combined boolean flags. pub flags: DownlevelFlags, @@ -1282,6 +1282,7 @@ impl DownlevelFlags { /// Collections of shader features a device supports if they support less than WebGPU normally allows. // TODO: Fill out the differences between shader models more completely #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum ShaderModel { /// Extremely limited shaders, including a total instruction limit. Sm2, @@ -1294,8 +1295,7 @@ pub enum ShaderModel { /// Supported physical device types. #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "trace", derive(serde::Serialize))] -#[cfg_attr(feature = "replay", derive(serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum DeviceType { /// Other or Unknown. Other, @@ -1313,8 +1313,7 @@ pub enum DeviceType { /// Information about an adapter. #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "trace", derive(serde::Serialize))] -#[cfg_attr(feature = "replay", derive(serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct AdapterInfo { /// Adapter name pub name: String, @@ -1865,6 +1864,7 @@ impl_bitflags!(TextureFormatFeatureFlags); /// /// Features are defined by WebGPU specification unless `Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES` is enabled. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TextureFormatFeatures { /// Valid bits for `TextureDescriptor::Usage` provided for format creation. pub allowed_usages: TextureUsages, diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 8e6caa0231..6ae0291c03 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -1440,7 +1440,7 @@ impl Instance { target_os = "emscripten", feature = "webgl" ))] - pub fn enumerate_adapters(&self, backends: Backends) -> impl Iterator { + pub fn enumerate_adapters(&self, backends: Backends) -> impl ExactSizeIterator { let context = Arc::clone(&self.context); self.context .as_any() From b9d0680bd899afe14589513395dba80e1f387d02 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Sun, 11 Jun 2023 03:10:06 -0400 Subject: [PATCH 03/41] Make publishable --- wgpu-info/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wgpu-info/Cargo.toml b/wgpu-info/Cargo.toml index 8d6bb45636..59fc074fa8 100644 --- a/wgpu-info/Cargo.toml +++ b/wgpu-info/Cargo.toml @@ -3,12 +3,11 @@ name = "wgpu-info" version.workspace = true authors.workspace = true edition.workspace = true -description = "Adapter information and per-adapter test program" +description = "A tool to print and process information about available wgpu adapters." homepage.workspace = true repository.workspace = true keywords.workspace = true license.workspace = true -publish = false [dependencies] anyhow.workspace = true From e468a5b84874eba44dd2368fd7abbded6a15ef78 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Sun, 11 Jun 2023 03:30:08 -0400 Subject: [PATCH 04/41] fmt --- wgpu-info/src/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-info/src/cli.rs b/wgpu-info/src/cli.rs index 3a8fa82a46..1e81e9f0f8 100644 --- a/wgpu-info/src/cli.rs +++ b/wgpu-info/src/cli.rs @@ -74,7 +74,7 @@ pub fn main() -> anyhow::Result<()> { } }; let mut output = io::BufWriter::new(output); - + let output_name = output_path.as_deref().unwrap_or("stdout"); if json { From 0192b10c7ec296d38b4231d4ceabdc43ecf32c2e Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Sun, 11 Jun 2023 23:52:48 -0400 Subject: [PATCH 05/41] First pass at gpu framework --- .gitignore | 3 + Cargo.lock | 135 +++++++++++++++++ Cargo.toml | 2 + tests/Cargo.toml | 11 +- tests/src/infra/mod.rs | 39 +++++ tests/src/infra/params.rs | 32 ++++ tests/src/infra/report.rs | 26 ++++ tests/src/infra/single.rs | 75 +++++++++ tests/src/lib.rs | 143 +++++++----------- tests/tests/instance.rs | 37 +---- tests/tests/queue_transfer.rs | 14 +- tests/tests/regression/issue_3457.rs | 2 +- tests/tests/resource_descriptor_accessor.rs | 14 +- tests/tests/resource_error.rs | 31 ++-- tests/tests/root.rs | 22 ++- tests/tests/texture_bounds.rs | 130 ++++++++-------- tests/tests/transfer.rs | 12 +- tests/tests/write_texture.rs | 37 ++--- .../tests/zero_init_texture_after_discard.rs | 126 ++++++++------- 19 files changed, 593 insertions(+), 298 deletions(-) create mode 100644 tests/src/infra/mod.rs create mode 100644 tests/src/infra/params.rs create mode 100644 tests/src/infra/report.rs create mode 100644 tests/src/infra/single.rs diff --git a/.gitignore b/.gitignore index 6192f9dede..089a7f2e19 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ cts/ # Readme says to put angle in working directory *.dll + +# Cached GPU config +.gpuconfig diff --git a/Cargo.lock b/Cargo.lock index 59d9ea60f8..ee33ed5845 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,6 +46,55 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.71" @@ -265,6 +314,48 @@ dependencies = [ "libc", ] +[[package]] +name = "clap" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab" +dependencies = [ + "anstream", + "anstyle", + "bitflags 1.3.2", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + [[package]] name = "cmake" version = "0.1.50" @@ -321,6 +412,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "com-rs" version = "0.2.1" @@ -1197,6 +1294,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.2.6" @@ -1399,6 +1502,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "libtest-mimic" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7b603516767d1ab23d0de09d023e62966c3322f7148297c35cf3d97aa8b37fa" +dependencies = [ + "clap", + "termcolor", + "threadpool", +] + [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -2514,6 +2628,15 @@ dependencies = [ "syn 2.0.18", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "tiny-skia" version = "0.7.0" @@ -2706,6 +2829,12 @@ dependencies = [ "url", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.3.3" @@ -3273,19 +3402,25 @@ dependencies = [ name = "wgpu-test" version = "0.16.0" dependencies = [ + "anyhow", + "arrayvec 0.7.2", "bitflags 2.3.1", "bytemuck", "cfg-if", "console_log", "env_logger", + "heck", "image", "js-sys", + "libtest-mimic", "log", "naga", "nv-flip", "png", "pollster", "raw-window-handle 0.5.2", + "serde", + "serde_json", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test", diff --git a/Cargo.toml b/Cargo.toml index 3c7ba7e29e..5af9f84338 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,10 +69,12 @@ env_logger = "0.10" futures-intrusive = "0.4" rustc-hash = "1.1.0" glam = "0.21.3" +heck = "0.4" image = { version = "0.24", default-features = false, features = ["png"] } # libloading 0.8 switches from `winapi` to `windows-sys`; permit either libloading = ">=0.7,<0.9" libc = "0.2" +libtest-mimic = "0.6" log = "0.4" nanorand = { version = "0.7", default-features = false, features = ["wyrand"] } nv-flip = "0.1" diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 5ab713ddd9..3ae7374e65 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -14,19 +14,26 @@ publish = false [[test]] name = "wgpu-tests" path = "tests/root.rs" +harness = false [features] webgl = ["wgpu/webgl"] [dependencies] +anyhow.workspace = true +arrayvec.workspace = true bitflags.workspace = true cfg-if.workspace = true env_logger.workspace = true +heck.workspace = true +libtest-mimic.workspace = true log.workspace = true -pollster.workspace = true png.workspace = true +pollster.workspace = true +serde_json.workspace = true +serde.workspace = true wgpu.workspace = true -wgt.workspace = true +wgt = { workspace = true, features = ["replay"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] nv-flip.workspace = true diff --git a/tests/src/infra/mod.rs b/tests/src/infra/mod.rs new file mode 100644 index 0000000000..e6184c67e5 --- /dev/null +++ b/tests/src/infra/mod.rs @@ -0,0 +1,39 @@ +use std::sync::Arc; + +use anyhow::Context; + +pub use params::GpuTest; + +mod params; +mod report; +mod single; + +pub type MainResult = anyhow::Result<()>; + +pub fn main( + test_list: [Arc; TEST_COUNT], +) -> MainResult { + let args = libtest_mimic::Arguments::from_args(); + + let config_text = + &std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) + .context("failed to read .gpuconfig")?; + let report = + report::GpuReport::from_json(config_text).context("Could not pare .gpuconfig JSON")?; + + libtest_mimic::run( + &args, + test_list + .into_iter() + .flat_map(|test| { + report + .devices + .iter() + .map(move |device| single::run_test(test.clone(), device)) + }) + .collect(), + ) + .exit_if_failed(); + + Ok(()) +} diff --git a/tests/src/infra/params.rs b/tests/src/infra/params.rs new file mode 100644 index 0000000000..47a6081c25 --- /dev/null +++ b/tests/src/infra/params.rs @@ -0,0 +1,32 @@ +use std::sync::Arc; + +use heck::ToSnakeCase; + +use crate::{TestParameters, TestingContext}; + +pub trait GpuTest: Send + Sync + 'static { + fn new() -> Arc + where + Self: Sized + Default, + { + Arc::new(Self::default()) + } + + fn name(&self) -> String { + let name = std::any::type_name::(); + let type_name = name.rsplit_once("::").unwrap().1; + let snake_case = type_name.to_snake_case(); + let snake_case_trimmed = snake_case.trim_end_matches("_test"); + assert_ne!( + &snake_case, snake_case_trimmed, + "Type name of the test must end with \"Test\"" + ); + snake_case_trimmed.to_string() + } + + fn parameters(&self, params: TestParameters) -> TestParameters { + params + } + + fn run(&self, ctx: TestingContext); +} diff --git a/tests/src/infra/report.rs b/tests/src/infra/report.rs new file mode 100644 index 0000000000..089d47d431 --- /dev/null +++ b/tests/src/infra/report.rs @@ -0,0 +1,26 @@ +use std::collections::HashMap; + +use serde::Deserialize; +use wgpu::{ + AdapterInfo, DownlevelCapabilities, Features, Limits, TextureFormat, TextureFormatFeatures, +}; + +#[derive(Deserialize)] +pub struct GpuReport { + pub devices: Vec, +} + +impl GpuReport { + pub fn from_json(file: &str) -> serde_json::Result { + serde_json::from_str(file) + } +} + +#[derive(Deserialize)] +pub struct AdapterReport { + pub info: AdapterInfo, + pub features: Features, + pub limits: Limits, + pub downlevel_caps: DownlevelCapabilities, + pub texture_format_features: HashMap, +} diff --git a/tests/src/infra/single.rs b/tests/src/infra/single.rs new file mode 100644 index 0000000000..87ee726011 --- /dev/null +++ b/tests/src/infra/single.rs @@ -0,0 +1,75 @@ +use std::sync::Arc; + +use arrayvec::ArrayVec; + +use crate::{ + infra::{report::AdapterReport, GpuTest}, + initialize_test, TestParameters, +}; + +pub fn run_test( + test: Arc, + adapter: &AdapterReport, +) -> libtest_mimic::Trial { + let params = TestParameters::default(); + let params = test.parameters(params); + + let base_name = test.name(); + let backend = &adapter.info.backend; + let device_name = &adapter.info.name; + + // Figure out if we should skip the test and if so, why. + let mut skipped_reasons: ArrayVec<_, 4> = ArrayVec::new(); + let missing_features = params.required_features - adapter.features; + if !missing_features.is_empty() { + skipped_reasons.push("Features"); + } + + if !params.required_limits.check_limits(&adapter.limits) { + skipped_reasons.push("Limits"); + } + + let missing_downlevel_flags = + params.required_downlevel_caps.flags - adapter.downlevel_caps.flags; + if !missing_downlevel_flags.is_empty() { + skipped_reasons.push("Downlevel Flags"); + } + + if params.required_downlevel_caps.shader_model > adapter.downlevel_caps.shader_model { + skipped_reasons.push("Shader Model"); + } + + let expected_failure = params.to_failure_reasons(&adapter.info); + + let mut should_skip = false; + let running_msg = if !skipped_reasons.is_empty() { + should_skip = true; + format!("Skipped: {}", skipped_reasons.join(" | ")) + } else if let Some((failure_resasons, skip)) = expected_failure { + should_skip |= skip; + let names: ArrayVec<_, 4> = failure_resasons + .iter_names() + .map(|(name, _)| name) + .collect(); + let names_text = names.join(" | "); + + let skip_text = if skip { "Skipped " } else { "Executed " }; + format!("{skip_text}Failure: {}", names_text) + } else { + String::from("Executed") + }; + + let full_name = format!("[{backend:?}/{device_name}] [{running_msg}] {base_name}"); + + libtest_mimic::Trial::test(full_name, move || { + if should_skip { + return Ok(()); + } + initialize_test( + params, + expected_failure.map(|(reasons, _)| reasons), + |ctx| test.run(ctx), + ); + Ok(()) + }) +} diff --git a/tests/src/lib.rs b/tests/src/lib.rs index b8a0fb2443..7999994cdc 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -4,9 +4,10 @@ use std::panic::{catch_unwind, AssertUnwindSafe}; use wgpu::{Adapter, Device, DownlevelFlags, Instance, Queue, Surface}; -use wgt::{Backends, DeviceDescriptor, DownlevelCapabilities, Features, Limits}; +use wgt::{AdapterInfo, Backends, DeviceDescriptor, DownlevelCapabilities, Features, Limits}; pub mod image; +pub mod infra; mod isolation; pub use self::image::ComparisonType; @@ -63,7 +64,7 @@ pub struct FailureCase { // This information determines if a test should run. pub struct TestParameters { pub required_features: Features, - pub required_downlevel_properties: DownlevelCapabilities, + pub required_downlevel_caps: DownlevelCapabilities, pub required_limits: Limits, // Backends where test should fail. pub failures: Vec, @@ -73,13 +74,58 @@ impl Default for TestParameters { fn default() -> Self { Self { required_features: Features::empty(), - required_downlevel_properties: lowest_downlevel_properties(), + required_downlevel_caps: lowest_downlevel_properties(), required_limits: Limits::downlevel_webgl2_defaults(), failures: Vec::new(), } } } +impl TestParameters { + fn to_failure_reasons(&self, adapter_info: &AdapterInfo) -> Option<(FailureReasons, bool)> { + self.failures.iter().find_map(|failure| { + let adapter_lowercase_name = adapter_info.name.to_lowercase(); + let always = + failure.backends.is_none() && failure.vendor.is_none() && failure.adapter.is_none(); + + let expect_failure_backend = failure + .backends + .map(|f| f.contains(wgpu::Backends::from(adapter_info.backend))); + let expect_failure_vendor = failure.vendor.map(|v| v == adapter_info.vendor); + let expect_failure_adapter = failure + .adapter + .as_deref() + .map(|f| adapter_lowercase_name.contains(f)); + + if expect_failure_backend.unwrap_or(true) + && expect_failure_vendor.unwrap_or(true) + && expect_failure_adapter.unwrap_or(true) + { + if always { + Some((FailureReasons::ALWAYS, failure.skip)) + } else { + let mut reason = FailureReasons::empty(); + reason.set( + FailureReasons::BACKEND, + expect_failure_backend.unwrap_or(false), + ); + reason.set( + FailureReasons::VENDOR, + expect_failure_vendor.unwrap_or(false), + ); + reason.set( + FailureReasons::ADAPTER, + expect_failure_adapter.unwrap_or(false), + ); + Some((reason, failure.skip)) + } + } else { + None + } + }) + } +} + bitflags::bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct FailureReasons: u8 { @@ -105,7 +151,7 @@ impl TestParameters { } pub fn downlevel_flags(mut self, downlevel_flags: DownlevelFlags) -> Self { - self.required_downlevel_properties.flags |= downlevel_flags; + self.required_downlevel_caps.flags |= downlevel_flags; self } @@ -186,7 +232,11 @@ impl TestParameters { } } -pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(TestingContext)) { +pub fn initialize_test( + parameters: TestParameters, + expected_failure_reason: Option, + test_function: impl FnOnce(TestingContext), +) { // We don't actually care if it fails #[cfg(not(target_arch = "wasm32"))] let _ = env_logger::try_init(); @@ -198,42 +248,8 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te let (adapter, _surface_guard) = initialize_adapter(); let adapter_info = adapter.get_info(); - let adapter_lowercase_name = adapter_info.name.to_lowercase(); - let adapter_features = adapter.features(); - let adapter_limits = adapter.limits(); let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities(); - let missing_features = parameters.required_features - adapter_features; - if !missing_features.is_empty() { - log::info!("TEST SKIPPED: MISSING FEATURES {:?}", missing_features); - return; - } - - if !parameters.required_limits.check_limits(&adapter_limits) { - log::info!("TEST SKIPPED: LIMIT TOO LOW"); - return; - } - - let missing_downlevel_flags = - parameters.required_downlevel_properties.flags - adapter_downlevel_capabilities.flags; - if !missing_downlevel_flags.is_empty() { - log::info!( - "TEST SKIPPED: MISSING DOWNLEVEL FLAGS {:?}", - missing_downlevel_flags - ); - return; - } - - if adapter_downlevel_capabilities.shader_model - < parameters.required_downlevel_properties.shader_model - { - log::info!( - "TEST SKIPPED: LOW SHADER MODEL {:?}", - adapter_downlevel_capabilities.shader_model - ); - return; - } - let (device, queue) = pollster::block_on(initialize_device( &adapter, parameters.required_features, @@ -250,51 +266,6 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te queue, }; - let expected_failure_reason = parameters.failures.iter().find_map(|failure| { - let always = - failure.backends.is_none() && failure.vendor.is_none() && failure.adapter.is_none(); - - let expect_failure_backend = failure - .backends - .map(|f| f.contains(wgpu::Backends::from(adapter_info.backend))); - let expect_failure_vendor = failure.vendor.map(|v| v == adapter_info.vendor); - let expect_failure_adapter = failure - .adapter - .as_deref() - .map(|f| adapter_lowercase_name.contains(f)); - - if expect_failure_backend.unwrap_or(true) - && expect_failure_vendor.unwrap_or(true) - && expect_failure_adapter.unwrap_or(true) - { - if always { - Some((FailureReasons::ALWAYS, failure.skip)) - } else { - let mut reason = FailureReasons::empty(); - reason.set( - FailureReasons::BACKEND, - expect_failure_backend.unwrap_or(false), - ); - reason.set( - FailureReasons::VENDOR, - expect_failure_vendor.unwrap_or(false), - ); - reason.set( - FailureReasons::ADAPTER, - expect_failure_adapter.unwrap_or(false), - ); - Some((reason, failure.skip)) - } - } else { - None - } - }); - - if let Some((reason, true)) = expected_failure_reason { - log::info!("EXPECTED TEST FAILURE SKIPPED: {:?}", reason); - return; - } - let panicked = catch_unwind(AssertUnwindSafe(|| test_function(context))).is_err(); cfg_if::cfg_if!( if #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] { @@ -317,7 +288,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te if failed == expect_failure { // We got the conditions we expected - if let Some((expected_reason, _)) = expected_failure_reason { + if let Some(expected_reason) = expected_failure_reason { // Print out reason for the failure log::info!( "GOT EXPECTED TEST FAILURE DUE TO {}: {:?}", @@ -325,7 +296,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te expected_reason ); } - } else if let Some((reason, _)) = expected_failure_reason { + } else if let Some(reason) = expected_failure_reason { // We expected to fail, but things passed panic!("UNEXPECTED TEST PASS: {reason:?}"); } else { diff --git a/tests/tests/instance.rs b/tests/tests/instance.rs index e9ff6afff0..61db72040b 100644 --- a/tests/tests/instance.rs +++ b/tests/tests/instance.rs @@ -1,34 +1,7 @@ -use wasm_bindgen_test::*; +use wgpu_test::{infra::GpuTest, TestingContext}; +#[derive(Clone, Default)] +pub struct InitializeTest; -#[test] -#[wasm_bindgen_test] -fn initialize() { - let _ = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all), - dx12_shader_compiler: wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(), - }); -} - -fn request_adapter_inner(power: wgt::PowerPreference) { - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all), - dx12_shader_compiler: wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(), - }); - - let _adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: power, - force_fallback_adapter: false, - compatible_surface: None, - })) - .unwrap(); -} - -#[test] -fn request_adapter_low_power() { - request_adapter_inner(wgt::PowerPreference::LowPower); -} - -#[test] -fn request_adapter_high_power() { - request_adapter_inner(wgt::PowerPreference::HighPerformance); +impl GpuTest for InitializeTest { + fn run(&self, _ctx: TestingContext) {} } diff --git a/tests/tests/queue_transfer.rs b/tests/tests/queue_transfer.rs index f17030f372..239657c18c 100644 --- a/tests/tests/queue_transfer.rs +++ b/tests/tests/queue_transfer.rs @@ -1,12 +1,12 @@ //! Tests for buffer copy validation. -use wasm_bindgen_test::*; -use wgpu_test::{fail, initialize_test, TestParameters}; +use wgpu_test::{fail, infra::GpuTest}; -#[test] -#[wasm_bindgen_test] -fn queue_write_texture_overflow() { - initialize_test(TestParameters::default(), |ctx| { +#[derive(Default)] +pub struct QueueWriteTextureOverflowTest; + +impl GpuTest for QueueWriteTextureOverflowTest { + fn run(&self, ctx: wgpu_test::TestingContext) { let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, size: wgpu::Extent3d { @@ -46,5 +46,5 @@ fn queue_write_texture_overflow() { }, ); }); - }); + } } diff --git a/tests/tests/regression/issue_3457.rs b/tests/tests/regression/issue_3457.rs index 1582277654..b6a2175cc7 100644 --- a/tests/tests/regression/issue_3457.rs +++ b/tests/tests/regression/issue_3457.rs @@ -15,7 +15,7 @@ use wgpu::*; /// /// We use non-consecutive vertex attribute locations (0 and 5) in order to also test /// that we unset the correct locations (see PR #3706). -#[wasm_bindgen_test] +// #[wasm_bindgen_test] #[test] fn pass_reset_vertex_buffer() { initialize_test(TestParameters::default(), |ctx| { diff --git a/tests/tests/resource_descriptor_accessor.rs b/tests/tests/resource_descriptor_accessor.rs index 5f70258ac3..68bbf9496d 100644 --- a/tests/tests/resource_descriptor_accessor.rs +++ b/tests/tests/resource_descriptor_accessor.rs @@ -1,11 +1,11 @@ -use wasm_bindgen_test::*; -use wgpu_test::{initialize_test, TestParameters}; +use wgpu_test::infra::GpuTest; /// Buffer's size and usage can be read back. -#[test] -#[wasm_bindgen_test] -fn buffer_size_and_usage() { - initialize_test(TestParameters::default(), |ctx| { +#[derive(Default)] +pub struct BufferSizeAndUsageTest; + +impl GpuTest for BufferSizeAndUsageTest { + fn run(&self, ctx: wgpu_test::TestingContext) { let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { label: None, size: 1234, @@ -18,5 +18,5 @@ fn buffer_size_and_usage() { buffer.usage(), wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST ); - }) + } } diff --git a/tests/tests/resource_error.rs b/tests/tests/resource_error.rs index c02033b33c..a7a6c70b65 100644 --- a/tests/tests/resource_error.rs +++ b/tests/tests/resource_error.rs @@ -1,12 +1,12 @@ -use wasm_bindgen_test::*; -use wgpu_test::{fail, initialize_test, valid, TestParameters}; +use wgpu_test::{fail, infra::GpuTest, valid}; -#[test] -#[wasm_bindgen_test] -fn bad_buffer() { - // Create a buffer with bad parameters and call a few methods. - // Validation should fail but there should be not panic. - initialize_test(TestParameters::default(), |ctx| { +#[derive(Default)] +pub struct BadBufferTest; + +impl GpuTest for BadBufferTest { + fn run(&self, ctx: wgpu_test::TestingContext) { + // Create a buffer with bad parameters and call a few methods. + // Validation should fail but there should be not panic. let buffer = fail(&ctx.device, || { ctx.device.create_buffer(&wgpu::BufferDescriptor { label: None, @@ -22,15 +22,14 @@ fn bad_buffer() { fail(&ctx.device, || buffer.unmap()); valid(&ctx.device, || buffer.destroy()); valid(&ctx.device, || buffer.destroy()); - }); + } } -#[test] -#[wasm_bindgen_test] -fn bad_texture() { - // Create a texture with bad parameters and call a few methods. - // Validation should fail but there should be not panic. - initialize_test(TestParameters::default(), |ctx| { +#[derive(Default)] +pub struct BadTextureTest; + +impl GpuTest for BadTextureTest { + fn run(&self, ctx: wgpu_test::TestingContext) { let texture = fail(&ctx.device, || { ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, @@ -53,5 +52,5 @@ fn bad_texture() { }); valid(&ctx.device, || texture.destroy()); valid(&ctx.device, || texture.destroy()); - }); + } } diff --git a/tests/tests/root.rs b/tests/tests/root.rs index 27422ffb99..9941a33999 100644 --- a/tests/tests/root.rs +++ b/tests/tests/root.rs @@ -1,4 +1,4 @@ -use wasm_bindgen_test::wasm_bindgen_test_configure; +use wgpu_test::infra::GpuTest; mod regression { mod issue_3457; @@ -25,4 +25,22 @@ mod vertex_indices; mod write_texture; mod zero_init_texture_after_discard; -wasm_bindgen_test_configure!(run_in_browser); +// wasm_bindgen_test_configure!(run_in_browser); + +fn main() -> wgpu_test::infra::MainResult { + wgpu_test::infra::main([ + instance::InitializeTest::new(), + queue_transfer::QueueWriteTextureOverflowTest::new(), + resource_descriptor_accessor::BufferSizeAndUsageTest::new(), + resource_error::BadBufferTest::new(), + resource_error::BadTextureTest::new(), + texture_bounds::BadCopyOriginTest::new(), + transfer::CopyOverflowZTest::new(), + write_texture::WriteTextureSubset2dTest::new(), + write_texture::WriteTextureSubset3dTest::new(), + zero_init_texture_after_discard::DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyAfterSubmitTest::new(), + zero_init_texture_after_discard::DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest::new(), + zero_init_texture_after_discard::DiscardingDepthTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest::new(), + zero_init_texture_after_discard::DiscardingEitherDepthOrStencilAspectTest::new(), + ]) +} diff --git a/tests/tests/texture_bounds.rs b/tests/tests/texture_bounds.rs index da6cc6b528..3313363a88 100644 --- a/tests/tests/texture_bounds.rs +++ b/tests/tests/texture_bounds.rs @@ -1,15 +1,13 @@ //! Tests for texture copy bounds checks. -use wasm_bindgen_test::*; -use wgpu_test::{fail_if, initialize_test, TestParameters}; +use wgpu_test::{fail_if, infra::GpuTest}; -#[test] -#[wasm_bindgen_test] -fn bad_copy_origin() { - fn try_origin(origin: wgpu::Origin3d, size: wgpu::Extent3d, should_panic: bool) { - let parameters = TestParameters::default(); +#[derive(Default)] +pub struct BadCopyOriginTest; - initialize_test(parameters, |ctx| { +impl GpuTest for BadCopyOriginTest { + fn run(&self, ctx: wgpu_test::TestingContext) { + let try_origin = |origin, size, should_panic| { let texture = ctx.device.create_texture(&TEXTURE_DESCRIPTOR); let data = vec![255; BUFFER_SIZE as usize]; @@ -26,66 +24,66 @@ fn bad_copy_origin() { size, ) }); - }); - } + }; - try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false); - try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, TEXTURE_SIZE, true); - try_origin(wgpu::Origin3d { x: 0, y: 1, z: 0 }, TEXTURE_SIZE, true); - try_origin(wgpu::Origin3d { x: 0, y: 0, z: 1 }, TEXTURE_SIZE, true); + try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false); + try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, TEXTURE_SIZE, true); + try_origin(wgpu::Origin3d { x: 0, y: 1, z: 0 }, TEXTURE_SIZE, true); + try_origin(wgpu::Origin3d { x: 0, y: 0, z: 1 }, TEXTURE_SIZE, true); - try_origin( - wgpu::Origin3d { - x: TEXTURE_SIZE.width - 1, - y: TEXTURE_SIZE.height - 1, - z: TEXTURE_SIZE.depth_or_array_layers - 1, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - false, - ); - try_origin( - wgpu::Origin3d { - x: u32::MAX, - y: 0, - z: 0, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - true, - ); - try_origin( - wgpu::Origin3d { - x: u32::MAX, - y: 0, - z: 0, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - true, - ); - try_origin( - wgpu::Origin3d { - x: u32::MAX, - y: 0, - z: 0, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - true, - ); + try_origin( + wgpu::Origin3d { + x: TEXTURE_SIZE.width - 1, + y: TEXTURE_SIZE.height - 1, + z: TEXTURE_SIZE.depth_or_array_layers - 1, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + false, + ); + try_origin( + wgpu::Origin3d { + x: u32::MAX, + y: 0, + z: 0, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + true, + ); + try_origin( + wgpu::Origin3d { + x: u32::MAX, + y: 0, + z: 0, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + true, + ); + try_origin( + wgpu::Origin3d { + x: u32::MAX, + y: 0, + z: 0, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + true, + ); + } } const TEXTURE_SIZE: wgpu::Extent3d = wgpu::Extent3d { diff --git a/tests/tests/transfer.rs b/tests/tests/transfer.rs index 8263b217be..1c9a00f583 100644 --- a/tests/tests/transfer.rs +++ b/tests/tests/transfer.rs @@ -1,10 +1,12 @@ -use wgpu_test::{fail, initialize_test, TestParameters}; +use wgpu_test::{fail, infra::GpuTest}; -#[test] -fn copy_overflow_z() { +#[derive(Default)] +pub struct CopyOverflowZTest; + +impl GpuTest for CopyOverflowZTest { // A simple crash test exercising validation that used to happen a bit too // late, letting an integer overflow slip through. - initialize_test(TestParameters::default(), |ctx| { + fn run(&self, ctx: wgpu_test::TestingContext) { let mut encoder = ctx .device .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); @@ -65,5 +67,5 @@ fn copy_overflow_z() { ); ctx.queue.submit(Some(encoder.finish())); }); - }) + } } diff --git a/tests/tests/write_texture.rs b/tests/tests/write_texture.rs index 0578c60352..a3571426f1 100644 --- a/tests/tests/write_texture.rs +++ b/tests/tests/write_texture.rs @@ -1,15 +1,18 @@ //! Tests for texture copy -use wgpu_test::{initialize_test, TestParameters}; +use wgpu_test::{infra::GpuTest, TestParameters}; -use wasm_bindgen_test::*; +#[derive(Default)] +pub struct WriteTextureSubset2dTest; + +impl GpuTest for WriteTextureSubset2dTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params.backend_failure(wgpu::Backends::DX12) + } + + fn run(&self, ctx: wgpu_test::TestingContext) { + let size = 256; -#[test] -#[wasm_bindgen_test] -fn write_texture_subset_2d() { - let size = 256; - let parameters = TestParameters::default().backend_failure(wgpu::Backends::DX12); - initialize_test(parameters, |ctx| { let tex = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, dimension: wgpu::TextureDimension::D2, @@ -96,16 +99,16 @@ fn write_texture_subset_2d() { for byte in &data[(size as usize * 2)..] { assert_eq!(*byte, 0); } - }); + } } -#[test] -#[wasm_bindgen_test] -fn write_texture_subset_3d() { - let size = 256; - let depth = 4; - let parameters = TestParameters::default(); - initialize_test(parameters, |ctx| { +#[derive(Default)] +pub struct WriteTextureSubset3dTest; + +impl GpuTest for WriteTextureSubset3dTest { + fn run(&self, ctx: wgpu_test::TestingContext) { + let size = 256; + let depth = 4; let tex = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, dimension: wgpu::TextureDimension::D3, @@ -192,5 +195,5 @@ fn write_texture_subset_3d() { for byte in &data[((size * size) as usize * 2)..] { assert_eq!(*byte, 0); } - }); + } } diff --git a/tests/tests/zero_init_texture_after_discard.rs b/tests/tests/zero_init_texture_after_discard.rs index f83576b1d9..aaa5b8a03e 100644 --- a/tests/tests/zero_init_texture_after_discard.rs +++ b/tests/tests/zero_init_texture_after_discard.rs @@ -1,12 +1,16 @@ -use wasm_bindgen_test::*; use wgpu::*; -use wgpu_test::{image::ReadbackBuffers, initialize_test, TestParameters, TestingContext}; +use wgpu_test::{image::ReadbackBuffers, infra::GpuTest, TestParameters, TestingContext}; // Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in after submit of the encoder. -#[test] -#[wasm_bindgen_test] -fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_after_submit() { - initialize_test(TestParameters::default().webgl2_failure(), |mut ctx| { +#[derive(Default)] +pub struct DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyAfterSubmitTest; + +impl GpuTest for DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyAfterSubmitTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params.webgl2_failure() + } + + fn run(&self, mut ctx: TestingContext) { let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb); case.create_command_encoder(); case.discard(); @@ -17,14 +21,18 @@ fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_after case.submit_command_encoder(); case.assert_buffers_are_zero(); - }); + } } -// Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in the same encoder to a buffer. -#[test] -#[wasm_bindgen_test] -fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_in_same_encoder() { - initialize_test(TestParameters::default().webgl2_failure(), |mut ctx| { +#[derive(Default)] +pub struct DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest; + +impl GpuTest for DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params.webgl2_failure() + } + + fn run(&self, mut ctx: TestingContext) { let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb); case.create_command_encoder(); case.discard(); @@ -32,64 +40,68 @@ fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_in_sa case.submit_command_encoder(); case.assert_buffers_are_zero(); - }); + } } -#[test] -#[wasm_bindgen_test] -fn discarding_depth_target_resets_texture_init_state_check_visible_on_copy_in_same_encoder() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct DiscardingDepthTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest; + +impl GpuTest for DiscardingDepthTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .downlevel_flags( DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | DownlevelFlags::COMPUTE_SHADERS, ) - .limits(Limits::downlevel_defaults()), - |mut ctx| { - for format in [ - TextureFormat::Stencil8, - TextureFormat::Depth16Unorm, - TextureFormat::Depth24Plus, - TextureFormat::Depth24PlusStencil8, - TextureFormat::Depth32Float, - ] { - let mut case = TestCase::new(&mut ctx, format); - case.create_command_encoder(); - case.discard(); - case.copy_texture_to_buffer(); - case.submit_command_encoder(); - - case.assert_buffers_are_zero(); - } - }, - ); + .limits(Limits::downlevel_defaults()) + } + + fn run(&self, mut ctx: TestingContext) { + for format in [ + TextureFormat::Stencil8, + TextureFormat::Depth16Unorm, + TextureFormat::Depth24Plus, + TextureFormat::Depth24PlusStencil8, + TextureFormat::Depth32Float, + ] { + let mut case = TestCase::new(&mut ctx, format); + case.create_command_encoder(); + case.discard(); + case.copy_texture_to_buffer(); + case.submit_command_encoder(); + + case.assert_buffers_are_zero(); + } + } } -#[test] -#[wasm_bindgen_test] -fn discarding_either_depth_or_stencil_aspect() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct DiscardingEitherDepthOrStencilAspectTest; + +impl GpuTest for DiscardingEitherDepthOrStencilAspectTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .downlevel_flags( DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | DownlevelFlags::COMPUTE_SHADERS, ) - .limits(Limits::downlevel_defaults()), - |mut ctx| { - let mut case = TestCase::new(&mut ctx, TextureFormat::Depth24PlusStencil8); - case.create_command_encoder(); - case.discard_depth(); - case.submit_command_encoder(); + .limits(Limits::downlevel_defaults()) + } - case.create_command_encoder(); - case.discard_stencil(); - case.submit_command_encoder(); + fn run(&self, mut ctx: TestingContext) { + let mut case = TestCase::new(&mut ctx, TextureFormat::Depth24PlusStencil8); + case.create_command_encoder(); + case.discard_depth(); + case.submit_command_encoder(); - case.create_command_encoder(); - case.copy_texture_to_buffer(); - case.submit_command_encoder(); + case.create_command_encoder(); + case.discard_stencil(); + case.submit_command_encoder(); - case.assert_buffers_are_zero(); - }, - ); + case.create_command_encoder(); + case.copy_texture_to_buffer(); + case.submit_command_encoder(); + + case.assert_buffers_are_zero(); + } } struct TestCase<'ctx> { From 25b564e929e342a70c76db63ad550aacd92ca420 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Mon, 12 Jun 2023 22:37:29 -0400 Subject: [PATCH 06/41] CPU tests working --- tests/src/infra/mod.rs | 38 ++++---- tests/src/infra/params.rs | 29 +++++- tests/tests/example_wgsl.rs | 3 +- tests/tests/poll.rs | 175 ++++++++++++++++++++---------------- tests/tests/root.rs | 39 ++++---- 5 files changed, 172 insertions(+), 112 deletions(-) diff --git a/tests/src/infra/mod.rs b/tests/src/infra/mod.rs index e6184c67e5..c12d84453d 100644 --- a/tests/src/infra/mod.rs +++ b/tests/src/infra/mod.rs @@ -2,7 +2,9 @@ use std::sync::Arc; use anyhow::Context; -pub use params::GpuTest; +pub use params::{cpu_test, GpuTest}; + +use crate::infra::params::CpuTest; mod params; mod report; @@ -10,8 +12,9 @@ mod single; pub type MainResult = anyhow::Result<()>; -pub fn main( - test_list: [Arc; TEST_COUNT], +pub fn main( + gpu_test_list: [Arc; GPU_TEST_COUNT], + cpu_test_list: [CpuTest; CPU_TEST_COUNT], ) -> MainResult { let args = libtest_mimic::Arguments::from_args(); @@ -21,19 +24,24 @@ pub fn main( let report = report::GpuReport::from_json(config_text).context("Could not pare .gpuconfig JSON")?; - libtest_mimic::run( - &args, - test_list + // Gpu tests + let mut tests: Vec<_> = gpu_test_list + .into_iter() + .flat_map(|test| { + report + .devices + .iter() + .map(move |device| single::run_test(test.clone(), device)) + }) + .collect(); + // Cpu tests + tests.extend( + cpu_test_list .into_iter() - .flat_map(|test| { - report - .devices - .iter() - .map(move |device| single::run_test(test.clone(), device)) - }) - .collect(), - ) - .exit_if_failed(); + .map(|test| libtest_mimic::Trial::test(test.name(), move || Ok(test.call()))), + ); + + libtest_mimic::run(&args, tests).exit_if_failed(); Ok(()) } diff --git a/tests/src/infra/params.rs b/tests/src/infra/params.rs index 47a6081c25..d6cd3ad286 100644 --- a/tests/src/infra/params.rs +++ b/tests/src/infra/params.rs @@ -14,14 +14,14 @@ pub trait GpuTest: Send + Sync + 'static { fn name(&self) -> String { let name = std::any::type_name::(); - let type_name = name.rsplit_once("::").unwrap().1; + let (path, type_name) = name.rsplit_once("::").unwrap(); let snake_case = type_name.to_snake_case(); let snake_case_trimmed = snake_case.trim_end_matches("_test"); assert_ne!( &snake_case, snake_case_trimmed, "Type name of the test must end with \"Test\"" ); - snake_case_trimmed.to_string() + format!("{path}::{snake_case_trimmed}") } fn parameters(&self, params: TestParameters) -> TestParameters { @@ -30,3 +30,28 @@ pub trait GpuTest: Send + Sync + 'static { fn run(&self, ctx: TestingContext); } + +pub struct CpuTest { + name: &'static str, + test: Box, +} + +impl CpuTest { + pub fn name(&self) -> &'static str { + self.name + } + + pub fn call(self) { + (self.test)(); + } +} + +pub fn cpu_test(test: T) -> CpuTest +where + T: FnOnce() + Send + Sync + 'static, +{ + CpuTest { + name: std::any::type_name::(), + test: Box::new(test), + } +} diff --git a/tests/tests/example_wgsl.rs b/tests/tests/example_wgsl.rs index d645dd4ed3..689d291db5 100644 --- a/tests/tests/example_wgsl.rs +++ b/tests/tests/example_wgsl.rs @@ -2,8 +2,7 @@ use naga::{front::wgsl, valid::Validator}; use std::{fs, path::PathBuf}; /// Runs through all example shaders and ensures they are valid wgsl. -#[test] -fn parse_example_wgsl() { +pub fn parse_example_wgsl() { let read_dir = match PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("examples") .read_dir() diff --git a/tests/tests/poll.rs b/tests/tests/poll.rs index 7409dad093..4800bd86bc 100644 --- a/tests/tests/poll.rs +++ b/tests/tests/poll.rs @@ -1,115 +1,134 @@ use std::num::NonZeroU64; use wgpu::{ - BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, - BindingResource, BindingType, BufferBindingType, BufferDescriptor, BufferUsages, CommandBuffer, - CommandEncoderDescriptor, ComputePassDescriptor, Maintain, ShaderStages, + BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, + BindGroupLayoutEntry, BindingResource, BindingType, Buffer, BufferBindingType, + BufferDescriptor, BufferUsages, CommandBuffer, CommandEncoderDescriptor, ComputePassDescriptor, + Maintain, ShaderStages, }; -use wasm_bindgen_test::*; -use wgpu_test::{initialize_test, TestParameters, TestingContext}; +use wgpu_test::{infra::GpuTest, TestingContext}; -fn generate_dummy_work(ctx: &TestingContext) -> CommandBuffer { - let buffer = ctx.device.create_buffer(&BufferDescriptor { - label: None, - size: 16, - usage: BufferUsages::UNIFORM, - mapped_at_creation: false, - }); +struct DummyWorkData { + _buffer: Buffer, + _bgl: BindGroupLayout, + _bg: BindGroup, + cmd_buf: CommandBuffer, +} + +impl DummyWorkData { + fn new(ctx: &TestingContext) -> Self { + let buffer = ctx.device.create_buffer(&BufferDescriptor { + label: None, + size: 16, + usage: BufferUsages::UNIFORM, + mapped_at_creation: false, + }); - let bind_group_layout = ctx - .device - .create_bind_group_layout(&BindGroupLayoutDescriptor { + let bind_group_layout = ctx + .device + .create_bind_group_layout(&BindGroupLayoutDescriptor { + label: None, + entries: &[BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::COMPUTE, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: Some(NonZeroU64::new(16).unwrap()), + }, + count: None, + }], + }); + + let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor { label: None, - entries: &[BindGroupLayoutEntry { + layout: &bind_group_layout, + entries: &[BindGroupEntry { binding: 0, - visibility: ShaderStages::COMPUTE, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: Some(NonZeroU64::new(16).unwrap()), - }, - count: None, + resource: BindingResource::Buffer(buffer.as_entire_buffer_binding()), }], }); - let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor { - label: None, - layout: &bind_group_layout, - entries: &[BindGroupEntry { - binding: 0, - resource: BindingResource::Buffer(buffer.as_entire_buffer_binding()), - }], - }); - - let mut cmd_buf = ctx - .device - .create_command_encoder(&CommandEncoderDescriptor::default()); - - let mut cpass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::default()); - cpass.set_bind_group(0, &bind_group, &[]); - drop(cpass); - - cmd_buf.finish() + let mut cmd_buf = ctx + .device + .create_command_encoder(&CommandEncoderDescriptor::default()); + + let mut cpass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::default()); + cpass.set_bind_group(0, &bind_group, &[]); + drop(cpass); + + Self { + _buffer: buffer, + _bgl: bind_group_layout, + _bg: bind_group, + cmd_buf: cmd_buf.finish(), + } + } } -#[test] -#[wasm_bindgen_test] -fn wait() { - initialize_test(TestParameters::default().skip(), |ctx| { - let cmd_buf = generate_dummy_work(&ctx); +#[derive(Default)] +pub struct WaitTest; + +impl GpuTest for WaitTest { + fn run(&self, ctx: TestingContext) { + let data = DummyWorkData::new(&ctx); - ctx.queue.submit(Some(cmd_buf)); + ctx.queue.submit(Some(data.cmd_buf)); ctx.device.poll(Maintain::Wait); - }) + } } -#[test] -#[wasm_bindgen_test] -fn double_wait() { - initialize_test(TestParameters::default().skip(), |ctx| { - let cmd_buf = generate_dummy_work(&ctx); +#[derive(Default)] +pub struct DoubleWaitTest; + +impl GpuTest for DoubleWaitTest { + fn run(&self, ctx: TestingContext) { + let data = DummyWorkData::new(&ctx); - ctx.queue.submit(Some(cmd_buf)); + ctx.queue.submit(Some(data.cmd_buf)); ctx.device.poll(Maintain::Wait); ctx.device.poll(Maintain::Wait); - }) + } } -#[test] -#[wasm_bindgen_test] -fn wait_on_submission() { - initialize_test(TestParameters::default().skip(), |ctx| { - let cmd_buf = generate_dummy_work(&ctx); +#[derive(Default)] +pub struct WaitOnSubmissionTest; - let index = ctx.queue.submit(Some(cmd_buf)); +impl GpuTest for WaitOnSubmissionTest { + fn run(&self, ctx: TestingContext) { + let data = DummyWorkData::new(&ctx); + + let index = ctx.queue.submit(Some(data.cmd_buf)); ctx.device.poll(Maintain::WaitForSubmissionIndex(index)); - }) + } } -#[test] -#[wasm_bindgen_test] -fn double_wait_on_submission() { - initialize_test(TestParameters::default().skip(), |ctx| { - let cmd_buf = generate_dummy_work(&ctx); +#[derive(Default)] +pub struct DoubleWaitOnSubmissionTest; + +impl GpuTest for DoubleWaitOnSubmissionTest { + fn run(&self, ctx: TestingContext) { + let data = DummyWorkData::new(&ctx); - let index = ctx.queue.submit(Some(cmd_buf)); + let index = ctx.queue.submit(Some(data.cmd_buf)); ctx.device .poll(Maintain::WaitForSubmissionIndex(index.clone())); ctx.device.poll(Maintain::WaitForSubmissionIndex(index)); - }) + } } -#[test] -#[wasm_bindgen_test] -fn wait_out_of_order() { - initialize_test(TestParameters::default().skip(), |ctx| { - let cmd_buf1 = generate_dummy_work(&ctx); - let cmd_buf2 = generate_dummy_work(&ctx); +#[derive(Default)] +pub struct WaitOutOfOrderTest; + +impl GpuTest for WaitOutOfOrderTest { + fn run(&self, ctx: TestingContext) { + let data1 = DummyWorkData::new(&ctx); + let data2 = DummyWorkData::new(&ctx); - let index1 = ctx.queue.submit(Some(cmd_buf1)); - let index2 = ctx.queue.submit(Some(cmd_buf2)); + let index1 = ctx.queue.submit(Some(data1.cmd_buf)); + let index2 = ctx.queue.submit(Some(data2.cmd_buf)); ctx.device.poll(Maintain::WaitForSubmissionIndex(index2)); ctx.device.poll(Maintain::WaitForSubmissionIndex(index1)); - }) + } } diff --git a/tests/tests/root.rs b/tests/tests/root.rs index 9941a33999..f1a7758946 100644 --- a/tests/tests/root.rs +++ b/tests/tests/root.rs @@ -28,19 +28,28 @@ mod zero_init_texture_after_discard; // wasm_bindgen_test_configure!(run_in_browser); fn main() -> wgpu_test::infra::MainResult { - wgpu_test::infra::main([ - instance::InitializeTest::new(), - queue_transfer::QueueWriteTextureOverflowTest::new(), - resource_descriptor_accessor::BufferSizeAndUsageTest::new(), - resource_error::BadBufferTest::new(), - resource_error::BadTextureTest::new(), - texture_bounds::BadCopyOriginTest::new(), - transfer::CopyOverflowZTest::new(), - write_texture::WriteTextureSubset2dTest::new(), - write_texture::WriteTextureSubset3dTest::new(), - zero_init_texture_after_discard::DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyAfterSubmitTest::new(), - zero_init_texture_after_discard::DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest::new(), - zero_init_texture_after_discard::DiscardingDepthTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest::new(), - zero_init_texture_after_discard::DiscardingEitherDepthOrStencilAspectTest::new(), - ]) + wgpu_test::infra::main( + [ + instance::InitializeTest::new(), + poll::WaitTest::new(), + poll::DoubleWaitTest::new(), + poll::WaitOnSubmissionTest::new(), + poll::DoubleWaitOnSubmissionTest::new(), + poll::WaitOutOfOrderTest::new(), + queue_transfer::QueueWriteTextureOverflowTest::new(), + resource_descriptor_accessor::BufferSizeAndUsageTest::new(), + resource_error::BadBufferTest::new(), + resource_error::BadTextureTest::new(), + texture_bounds::BadCopyOriginTest::new(), + transfer::CopyOverflowZTest::new(), + write_texture::WriteTextureSubset2dTest::new(), + write_texture::WriteTextureSubset3dTest::new(), + zero_init_texture_after_discard::DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyAfterSubmitTest::new(), + zero_init_texture_after_discard::DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest::new(), + zero_init_texture_after_discard::DiscardingDepthTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest::new(), + zero_init_texture_after_discard::DiscardingEitherDepthOrStencilAspectTest::new(), + ], [ + wgpu_test::infra::cpu_test(example_wgsl::parse_example_wgsl) + ] + ) } From f7710886fa310f259af628f81a2627481fac077c Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Mon, 12 Jun 2023 23:01:23 -0400 Subject: [PATCH 07/41] Implement adapter selection --- tests/src/infra/mod.rs | 5 ++++- tests/src/infra/single.rs | 4 +++- tests/src/lib.rs | 23 ++++++++++------------- wgpu-info/Cargo.toml | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/tests/src/infra/mod.rs b/tests/src/infra/mod.rs index c12d84453d..2a0d0844cd 100644 --- a/tests/src/infra/mod.rs +++ b/tests/src/infra/mod.rs @@ -31,7 +31,10 @@ pub fn main( report .devices .iter() - .map(move |device| single::run_test(test.clone(), device)) + .enumerate() + .map(move |(adapter_index, adapter)| { + single::run_test(test.clone(), adapter, adapter_index) + }) }) .collect(); // Cpu tests diff --git a/tests/src/infra/single.rs b/tests/src/infra/single.rs index 87ee726011..0f8cec235f 100644 --- a/tests/src/infra/single.rs +++ b/tests/src/infra/single.rs @@ -10,6 +10,7 @@ use crate::{ pub fn run_test( test: Arc, adapter: &AdapterReport, + adapter_index: usize, ) -> libtest_mimic::Trial { let params = TestParameters::default(); let params = test.parameters(params); @@ -59,7 +60,7 @@ pub fn run_test( String::from("Executed") }; - let full_name = format!("[{backend:?}/{device_name}] [{running_msg}] {base_name}"); + let full_name = format!("[{running_msg}] [{backend:?}/{device_name}] {base_name}"); libtest_mimic::Trial::test(full_name, move || { if should_skip { @@ -68,6 +69,7 @@ pub fn run_test( initialize_test( params, expected_failure.map(|(reasons, _)| reasons), + adapter_index, |ctx| test.run(ctx), ); Ok(()) diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 7999994cdc..90b1f579ad 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -235,6 +235,7 @@ impl TestParameters { pub fn initialize_test( parameters: TestParameters, expected_failure_reason: Option, + adapter_index: usize, test_function: impl FnOnce(TestingContext), ) { // We don't actually care if it fails @@ -245,7 +246,7 @@ pub fn initialize_test( let _test_guard = isolation::OneTestPerProcessGuard::new(); - let (adapter, _surface_guard) = initialize_adapter(); + let (adapter, _surface_guard) = initialize_adapter(adapter_index); let adapter_info = adapter.get_info(); let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities(); @@ -304,7 +305,7 @@ pub fn initialize_test( } } -fn initialize_adapter() -> (Adapter, SurfaceGuard) { +fn initialize_adapter(adapter_index: usize) -> (Adapter, SurfaceGuard) { let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(Backends::all); let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(); let instance = Instance::new(wgpu::InstanceDescriptor { @@ -312,7 +313,6 @@ fn initialize_adapter() -> (Adapter, SurfaceGuard) { dx12_shader_compiler, }); let surface_guard; - let compatible_surface; #[cfg(not(all( target_arch = "wasm32", @@ -320,7 +320,6 @@ fn initialize_adapter() -> (Adapter, SurfaceGuard) { )))] { surface_guard = SurfaceGuard {}; - compatible_surface = None; } #[cfg(all( target_arch = "wasm32", @@ -356,17 +355,15 @@ fn initialize_adapter() -> (Adapter, SurfaceGuard) { }; surface_guard = SurfaceGuard { canvas }; - - compatible_surface = Some(surface); } - let compatible_surface: Option<&Surface> = compatible_surface.as_ref(); - let adapter = pollster::block_on(wgpu::util::initialize_adapter_from_env_or_default( - &instance, - backends, - compatible_surface, - )) - .expect("could not find suitable adapter on the system"); + let adapter_iter = instance.enumerate_adapters(wgpu::Backends::all()); + let adapter_count = adapter_iter.len(); + let adapter = adapter_iter.into_iter() + .nth(adapter_index) + .unwrap_or_else(|| panic!("Tried to get index {adapter_index} adapter, but adapter list was only {adapter_count} long. Is .gpuconfig out of date?")); + + log::info!("Testing using adapter: {:#?}", adapter.get_info()); (adapter, surface_guard) } diff --git a/wgpu-info/Cargo.toml b/wgpu-info/Cargo.toml index 59fc074fa8..91cdbf64eb 100644 --- a/wgpu-info/Cargo.toml +++ b/wgpu-info/Cargo.toml @@ -17,4 +17,4 @@ pico-args.workspace = true serde.workspace = true serde_json.workspace = true wgpu.workspace = true -wgpu-types = { workspace = true, features = ["serde"] } +wgpu-types = { workspace = true, features = ["replay", "trace"] } From 3b82b5fa22b08aaed57ceaa4213dd20b4ea9f846 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 15 Jun 2023 20:04:47 -0400 Subject: [PATCH 08/41] Finish gpu-test conversion --- .github/workflows/ci.yml | 8 +- Cargo.lock | 28 ++ Cargo.toml | 1 + examples/boids/Cargo.toml | 1 + examples/boids/src/main.rs | 42 +-- examples/bunnymark/Cargo.toml | 1 + examples/bunnymark/src/main.rs | 46 +-- examples/common/src/framework.rs | 227 +++++++-------- examples/conservative-raster/Cargo.toml | 1 + examples/conservative-raster/src/main.rs | 34 ++- examples/cube/Cargo.toml | 1 + examples/cube/src/main.rs | 75 ++--- examples/hello-compute/Cargo.toml | 1 + examples/hello-compute/src/main.rs | 20 +- examples/hello-compute/src/tests.rs | 180 ++++++------ examples/mipmap/Cargo.toml | 1 + examples/mipmap/src/main.rs | 38 ++- examples/msaa-line/Cargo.toml | 1 + examples/msaa-line/src/main.rs | 54 ++-- examples/shadow/Cargo.toml | 1 + examples/shadow/src/main.rs | 64 +++-- examples/skybox/Cargo.toml | 1 + examples/skybox/src/main.rs | 125 ++++----- examples/stencil-triangles/Cargo.toml | 1 + examples/stencil-triangles/src/main.rs | 44 +-- examples/texture-arrays/Cargo.toml | 1 + examples/texture-arrays/src/main.rs | 67 +++-- examples/water/Cargo.toml | 1 + examples/water/src/main.rs | 38 ++- tests/src/infra/mod.rs | 11 +- tests/src/infra/params.rs | 11 +- tests/src/lib.rs | 6 +- tests/tests/buffer.rs | 30 +- tests/tests/buffer_copy.rs | 57 ++-- tests/tests/buffer_usages.rs | 127 +++++---- tests/tests/clear_texture.rs | 153 +++++----- tests/tests/device.rs | 11 - tests/tests/encoder.rs | 14 +- tests/tests/regression/issue_3457.rs | 14 +- tests/tests/root.rs | 33 ++- tests/tests/shader/mod.rs | 6 +- tests/tests/shader/numeric_builtins.rs | 33 +-- tests/tests/shader/struct_layout.rs | 93 ++++--- tests/tests/shader/zero_init_workgroup_mem.rs | 262 +++++++++--------- tests/tests/shader_primitive_index/mod.rs | 94 ++++--- tests/tests/shader_view_format/mod.rs | 22 +- tests/tests/vertex_indices/mod.rs | 89 +++--- wgpu/Cargo.toml | 3 +- wgpu/src/util/belt.rs | 8 +- 49 files changed, 1212 insertions(+), 968 deletions(-) delete mode 100644 tests/tests/device.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50c3038615..0856af1669 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -204,17 +204,14 @@ jobs: # Windows - name: Windows x86_64 os: windows-2022 - backends: dx12 # Mac - name: Mac aarch64 os: [self-hosted, macOS] - backends: metal # Linux - name: Linux x86_64 os: ubuntu-22.04 - backends: vulkan gl name: Test ${{ matrix.name }} @@ -281,10 +278,7 @@ jobs: run: | set -e - for backend in ${{ matrix.backends }}; do - echo "======= NATIVE TESTS $backend ======"; - WGPU_BACKEND=$backend cargo llvm-cov --no-cfg-coverage nextest --no-fail-fast --no-report - done + WGPU_BACKEND=$backend cargo llvm-cov --no-cfg-coverage nextest --no-fail-fast --no-report - uses: actions/upload-artifact@v3 with: diff --git a/Cargo.lock b/Cargo.lock index ee33ed5845..872344fcc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -892,6 +892,19 @@ dependencies = [ "miniz_oxide 0.5.4", ] +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1114,8 +1127,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1659,6 +1674,9 @@ name = "nanorand" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.10", +] [[package]] name = "ndk" @@ -2555,6 +2573,15 @@ dependencies = [ "url", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "spirv" version = "0.2.0+1.5.4" @@ -3082,6 +3109,7 @@ version = "0.16.0" dependencies = [ "arrayvec 0.7.2", "cfg-if", + "flume", "js-sys", "log", "naga", diff --git a/Cargo.toml b/Cargo.toml index 5af9f84338..26af055079 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,7 @@ cfg-if = "1" codespan-reporting = "0.11" ddsfile = "0.5" env_logger = "0.10" +flume = "0.10" futures-intrusive = "0.4" rustc-hash = "1.1.0" glam = "0.21.3" diff --git a/examples/boids/Cargo.toml b/examples/boids/Cargo.toml index 933acd64eb..45e2cd3e7e 100644 --- a/examples/boids/Cargo.toml +++ b/examples/boids/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "boids" path = "src/main.rs" +harness = false [dependencies] bytemuck.workspace = true diff --git a/examples/boids/src/main.rs b/examples/boids/src/main.rs index 9fca8e7e22..2ddaaacb24 100644 --- a/examples/boids/src/main.rs +++ b/examples/boids/src/main.rs @@ -322,24 +322,34 @@ impl wgpu_example::framework::Example for Example { } /// run example +#[cfg(not(test))] fn main() { wgpu_example::framework::run::("boids"); } -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn boids() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - // Generated on 1080ti on Vk/Windows - image_path: "examples/boids/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default() - .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) - .limits(wgpu::Limits::downlevel_defaults()), - comparisons: &[wgpu_test::ComparisonType::Mean(0.005)], - }); +// Test example +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use std::marker::PhantomData; + + use wgpu_test::infra::GpuTest; + + wgpu_test::infra::main( + [GpuTest::from_value( + wgpu_example::framework::ExampleTestParams { + name: "boids", + // Generated on 1080ti on Vk/Windows + image_path: "examples/boids/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default() + .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) + .limits(wgpu::Limits::downlevel_defaults()), + comparisons: &[wgpu_test::ComparisonType::Mean(0.005)], + _phantom: PhantomData::, + }, + )], + [], + ) } diff --git a/examples/bunnymark/Cargo.toml b/examples/bunnymark/Cargo.toml index 43e0be0d66..4bd3e77344 100644 --- a/examples/bunnymark/Cargo.toml +++ b/examples/bunnymark/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "bunnymark" path = "src/main.rs" +harness = false [dependencies] bytemuck.workspace = true diff --git a/examples/bunnymark/src/main.rs b/examples/bunnymark/src/main.rs index 9322131f09..b2b40f5462 100644 --- a/examples/bunnymark/src/main.rs +++ b/examples/bunnymark/src/main.rs @@ -351,28 +351,38 @@ impl wgpu_example::framework::Example for Example { } } +#[cfg(not(test))] fn main() { wgpu_example::framework::run::("bunnymark"); } -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); +// Test example +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use std::marker::PhantomData; -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn bunnymark() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/bunnymark/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default(), - // We're looking for very small differences, so look in the high percentiles. - comparisons: &[ - wgpu_test::ComparisonType::Mean(0.05), - wgpu_test::ComparisonType::Percentile { - percentile: 0.95, - threshold: 0.05, + use wgpu_test::infra::GpuTest; + + wgpu_test::infra::main( + [GpuTest::from_value( + wgpu_example::framework::ExampleTestParams { + name: "bunnymark", + image_path: "/examples/bunnymark/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default(), + // We're looking for very small differences, so look in the high percentiles. + comparisons: &[ + wgpu_test::ComparisonType::Mean(0.05), + wgpu_test::ComparisonType::Percentile { + percentile: 0.95, + threshold: 0.05, + }, + ], + _phantom: PhantomData::, }, - ], - }); + )], + [], + ) } diff --git a/examples/common/src/framework.rs b/examples/common/src/framework.rs index 27b76bf86b..fbbb0ac243 100644 --- a/examples/common/src/framework.rs +++ b/examples/common/src/framework.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use std::time::Instant; #[cfg(target_arch = "wasm32")] use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas}; +use wgpu_test::infra::GpuTest; use winit::{ event::{self, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -492,7 +493,9 @@ pub fn parse_url_query_string<'a>(query: &'a str, search_key: &str) -> Option<&' pub use wgpu_test::image::ComparisonType; -pub struct FrameworkRefTest { +#[derive(Clone)] +pub struct ExampleTestParams { + pub name: &'static str, // Path to the reference image, relative to the root of the repo. pub image_path: &'static str, pub width: u32, @@ -501,129 +504,127 @@ pub struct FrameworkRefTest { pub base_test_parameters: wgpu_test::TestParameters, /// Comparisons against FLIP statistics that determine if the test passes or fails. pub comparisons: &'static [ComparisonType], + pub _phantom: std::marker::PhantomData, } -#[allow(dead_code)] -pub fn test(mut params: FrameworkRefTest) { - use std::mem; - - assert_eq!(params.width % 64, 0, "width needs to be aligned 64"); - - let features = E::required_features() | params.optional_features; +impl GpuTest for ExampleTestParams { + fn name(&self) -> String { + self.name.into() + } - wgpu_test::initialize_test( - mem::take(&mut params.base_test_parameters).features(features), - |ctx| { - let spawner = Spawner::new(); + fn parameters(&self, _params: wgpu_test::TestParameters) -> wgpu_test::TestParameters { + assert_eq!(self.width % 64, 0, "width needs to be aligned 64"); - let dst_texture = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: Some("destination"), - size: wgpu::Extent3d { - width: params.width, - height: params.height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, - view_formats: &[], - }); + let features = E::required_features() | self.optional_features; - let dst_view = dst_texture.create_view(&wgpu::TextureViewDescriptor::default()); + self.base_test_parameters.clone().features(features) + } - let dst_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: Some("image map buffer"), - size: params.width as u64 * params.height as u64 * 4, - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, - mapped_at_creation: false, - }); + fn run(&self, ctx: wgpu_test::TestingContext) { + let spawner = Spawner::new(); - let mut example = E::init( - &wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - width: params.width, - height: params.height, - present_mode: wgpu::PresentMode::Fifo, - alpha_mode: wgpu::CompositeAlphaMode::Auto, - view_formats: vec![wgpu::TextureFormat::Rgba8UnormSrgb], + let dst_texture = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: Some("destination"), + size: wgpu::Extent3d { + width: self.width, + height: self.height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + }); + + let dst_view = dst_texture.create_view(&wgpu::TextureViewDescriptor::default()); + + let dst_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: Some("image map buffer"), + size: self.width as u64 * self.height as u64 * 4, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + + let mut example = E::init( + &wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + width: self.width, + height: self.height, + present_mode: wgpu::PresentMode::Fifo, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![wgpu::TextureFormat::Rgba8UnormSrgb], + }, + &ctx.adapter, + &ctx.device, + &ctx.queue, + ); + + example.render(&dst_view, &ctx.device, &ctx.queue, &spawner); + + // Handle specific case for bunnymark + #[allow(deprecated)] + if self.image_path == "/examples/bunnymark/screenshot.png" { + // Press spacebar to spawn bunnies + example.update(winit::event::WindowEvent::KeyboardInput { + input: winit::event::KeyboardInput { + scancode: 0, + state: winit::event::ElementState::Pressed, + virtual_keycode: Some(winit::event::VirtualKeyCode::Space), + modifiers: winit::event::ModifiersState::empty(), }, - &ctx.adapter, - &ctx.device, - &ctx.queue, - ); - - example.render(&dst_view, &ctx.device, &ctx.queue, &spawner); - - // Handle specific case for bunnymark - #[allow(deprecated)] - if params.image_path == "/examples/bunnymark/screenshot.png" { - // Press spacebar to spawn bunnies - example.update(winit::event::WindowEvent::KeyboardInput { - input: winit::event::KeyboardInput { - scancode: 0, - state: winit::event::ElementState::Pressed, - virtual_keycode: Some(winit::event::VirtualKeyCode::Space), - modifiers: winit::event::ModifiersState::empty(), - }, - device_id: unsafe { winit::event::DeviceId::dummy() }, - is_synthetic: false, - }); + device_id: unsafe { winit::event::DeviceId::dummy() }, + is_synthetic: false, + }); - // Step 3 extra frames - for _ in 0..3 { - example.render(&dst_view, &ctx.device, &ctx.queue, &spawner); - } + // Step 3 extra frames + for _ in 0..3 { + example.render(&dst_view, &ctx.device, &ctx.queue, &spawner); } + } - let mut cmd_buf = ctx - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + let mut cmd_buf = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); - cmd_buf.copy_texture_to_buffer( - wgpu::ImageCopyTexture { - texture: &dst_texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - wgpu::ImageCopyBuffer { - buffer: &dst_buffer, - layout: wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: Some(params.width * 4), - rows_per_image: None, - }, - }, - wgpu::Extent3d { - width: params.width, - height: params.height, - depth_or_array_layers: 1, + cmd_buf.copy_texture_to_buffer( + wgpu::ImageCopyTexture { + texture: &dst_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::ImageCopyBuffer { + buffer: &dst_buffer, + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(self.width * 4), + rows_per_image: None, }, - ); - - ctx.queue.submit(Some(cmd_buf.finish())); - - let dst_buffer_slice = dst_buffer.slice(..); - dst_buffer_slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.device.poll(wgpu::Maintain::Wait); - let bytes = dst_buffer_slice.get_mapped_range().to_vec(); - - wgpu_test::image::compare_image_output( - env!("CARGO_MANIFEST_DIR").to_string() + "/../../" + params.image_path, - ctx.adapter_info.backend, - params.width, - params.height, - &bytes, - params.comparisons, - ); - }, - ); + }, + wgpu::Extent3d { + width: self.width, + height: self.height, + depth_or_array_layers: 1, + }, + ); + + ctx.queue.submit(Some(cmd_buf.finish())); + + let dst_buffer_slice = dst_buffer.slice(..); + dst_buffer_slice.map_async(wgpu::MapMode::Read, |_| ()); + ctx.device.poll(wgpu::Maintain::Wait); + let bytes = dst_buffer_slice.get_mapped_range().to_vec(); + + wgpu_test::image::compare_image_output( + env!("CARGO_MANIFEST_DIR").to_string() + "/../../" + self.image_path, + ctx.adapter_info.backend, + self.width, + self.height, + &bytes, + self.comparisons, + ); + } } - -// This allows treating the framework as a standalone example, -// thus avoiding listing the example names in `Cargo.toml`. -#[allow(dead_code)] -fn main() {} diff --git a/examples/conservative-raster/Cargo.toml b/examples/conservative-raster/Cargo.toml index 1b4de48395..4d9435e796 100644 --- a/examples/conservative-raster/Cargo.toml +++ b/examples/conservative-raster/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "conservative-raster" path = "src/main.rs" +harness = false [dependencies] wasm-bindgen-test.workspace = true diff --git a/examples/conservative-raster/src/main.rs b/examples/conservative-raster/src/main.rs index 1dba599591..31db12434a 100644 --- a/examples/conservative-raster/src/main.rs +++ b/examples/conservative-raster/src/main.rs @@ -308,21 +308,31 @@ impl wgpu_example::framework::Example for Example { } } +#[cfg(not(test))] fn main() { wgpu_example::framework::run::("conservative-raster"); } -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); +// Test example +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use std::marker::PhantomData; -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn conservative_raster() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/conservative-raster/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], - }); + use wgpu_test::infra::GpuTest; + + wgpu_test::infra::main( + [GpuTest::from_value( + wgpu_example::framework::ExampleTestParams { + name: "conservative-raster", + image_path: "/examples/conservative-raster/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], + _phantom: PhantomData::, + }, + )], + [], + ) } diff --git a/examples/cube/Cargo.toml b/examples/cube/Cargo.toml index 697aa240e6..51afe89478 100644 --- a/examples/cube/Cargo.toml +++ b/examples/cube/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "cube" path = "src/main.rs" +harness = false [dependencies] bytemuck.workspace = true diff --git a/examples/cube/src/main.rs b/examples/cube/src/main.rs index 271bc17ce4..e4c43eca68 100644 --- a/examples/cube/src/main.rs +++ b/examples/cube/src/main.rs @@ -403,45 +403,52 @@ impl wgpu_example::framework::Example for Example { } } +#[cfg(not(test))] fn main() { wgpu_example::framework::run::("cube"); } -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); +// Test example +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use std::marker::PhantomData; -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn cube() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - // Generated on 1080ti on Vk/Windows - image_path: "/examples/cube/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[ - wgpu_test::ComparisonType::Mean(0.04), // Bounded by Intel 630 on Vk/Windows - ], - }); -} + use wgpu_test::infra::GpuTest; -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn cube_lines() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - // Generated on 1080ti on Vk/Windows - image_path: "/examples/cube/screenshot-lines.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::POLYGON_MODE_LINE, - base_test_parameters: wgpu_test::TestParameters::default(), - // We're looking for tiny changes here, so we focus on a spike in the 95th percentile. - comparisons: &[ - wgpu_test::ComparisonType::Mean(0.05), // Bounded by Intel 630 on Vk/Windows - wgpu_test::ComparisonType::Percentile { - percentile: 0.95, - threshold: 0.36, - }, // Bounded by 1080ti on DX12 + wgpu_test::infra::main( + [ + GpuTest::from_value(wgpu_example::framework::ExampleTestParams { + name: "cube", + // Generated on 1080ti on Vk/Windows + image_path: "/examples/cube/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[ + wgpu_test::ComparisonType::Mean(0.04), // Bounded by Intel 630 on Vk/Windows + ], + _phantom: PhantomData::, + }), + GpuTest::from_value(wgpu_example::framework::ExampleTestParams { + name: "cube-lines", + // Generated on 1080ti on Vk/Windows + image_path: "/examples/cube/screenshot-lines.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::POLYGON_MODE_LINE, + base_test_parameters: wgpu_test::TestParameters::default(), + // We're looking for tiny changes here, so we focus on a spike in the 95th percentile. + comparisons: &[ + wgpu_test::ComparisonType::Mean(0.05), // Bounded by Intel 630 on Vk/Windows + wgpu_test::ComparisonType::Percentile { + percentile: 0.95, + threshold: 0.36, + }, // Bounded by 1080ti on DX12 + ], + _phantom: PhantomData::, + }), ], - }); + [], + ) } diff --git a/examples/hello-compute/Cargo.toml b/examples/hello-compute/Cargo.toml index af5b29d735..cbc8fb661d 100644 --- a/examples/hello-compute/Cargo.toml +++ b/examples/hello-compute/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "hello-compute" path = "src/main.rs" +harness = false [dependencies] bytemuck.workspace = true diff --git a/examples/hello-compute/src/main.rs b/examples/hello-compute/src/main.rs index afdf7744c9..7c91c7f484 100644 --- a/examples/hello-compute/src/main.rs +++ b/examples/hello-compute/src/main.rs @@ -4,6 +4,7 @@ use wgpu::util::DeviceExt; // Indicates a u32 overflow in an intermediate Collatz value const OVERFLOW: u32 = 0xffffffff; +#[cfg_attr(test, allow(dead_code))] async fn run() { let numbers = if std::env::args().len() <= 1 { let default = vec![1, 2, 3, 4]; @@ -31,6 +32,7 @@ async fn run() { log::info!("Steps: [{}]", disp_steps.join(", ")); } +#[cfg_attr(test, allow(dead_code))] async fn execute_gpu(numbers: &[u32]) -> Option> { // Instantiates instance of WebGPU let instance = wgpu::Instance::default(); @@ -179,6 +181,7 @@ async fn execute_gpu_inner( } } +#[cfg(not(test))] fn main() { #[cfg(not(target_arch = "wasm32"))] { @@ -193,5 +196,20 @@ fn main() { } } -#[cfg(all(test, not(target_arch = "wasm32")))] +#[cfg(test)] mod tests; + +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use wgpu_test::infra::GpuTest; + + wgpu_test::infra::main( + [ + tests::Compute1Test::new(), + tests::Compute2Test::new(), + tests::ComputeOverflowTest::new(), + tests::MultithreadedComputeTest::new(), + ], + [], + ) +} diff --git a/examples/hello-compute/src/tests.rs b/examples/hello-compute/src/tests.rs index c67f2ca8f4..335384f7f9 100644 --- a/examples/hello-compute/src/tests.rs +++ b/examples/hello-compute/src/tests.rs @@ -1,111 +1,117 @@ use std::sync::Arc; use super::*; -use wgpu_test::{initialize_test, TestParameters}; +use wgpu_test::{infra::GpuTest, TestParameters}; -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); +#[derive(Default)] +pub struct Compute1Test; -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn test_compute_1() { - initialize_test( - TestParameters::default() +impl GpuTest for Compute1Test { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) .limits(wgpu::Limits::downlevel_defaults()) - .specific_failure(None, None, Some("V3D"), true), - |ctx| { - let input = &[1, 2, 3, 4]; - - pollster::block_on(assert_execute_gpu( - &ctx.device, - &ctx.queue, - input, - &[0, 1, 7, 2], - )); - }, - ); + .specific_failure(None, None, Some("V3D"), true) + } + + fn run(&self, ctx: wgpu_test::TestingContext) { + let input = &[1, 2, 3, 4]; + + pollster::block_on(assert_execute_gpu( + &ctx.device, + &ctx.queue, + input, + &[0, 1, 7, 2], + )); + } } -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn test_compute_2() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct Compute2Test; + +impl GpuTest for Compute2Test { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) .limits(wgpu::Limits::downlevel_defaults()) - .specific_failure(None, None, Some("V3D"), true), - |ctx| { - let input = &[5, 23, 10, 9]; - - pollster::block_on(assert_execute_gpu( - &ctx.device, - &ctx.queue, - input, - &[5, 15, 6, 19], - )); - }, - ); + .specific_failure(None, None, Some("V3D"), true) + } + + fn run(&self, ctx: wgpu_test::TestingContext) { + let input = &[5, 23, 10, 9]; + + pollster::block_on(assert_execute_gpu( + &ctx.device, + &ctx.queue, + input, + &[5, 15, 6, 19], + )); + } } -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn test_compute_overflow() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct ComputeOverflowTest; + +impl GpuTest for ComputeOverflowTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) .limits(wgpu::Limits::downlevel_defaults()) - .specific_failure(None, None, Some("V3D"), true), - |ctx| { - let input = &[77031, 837799, 8400511, 63728127]; - pollster::block_on(assert_execute_gpu( - &ctx.device, - &ctx.queue, - input, - &[350, 524, OVERFLOW, OVERFLOW], - )); - }, - ); + .specific_failure(None, None, Some("V3D"), true) + } + + fn run(&self, ctx: wgpu_test::TestingContext) { + let input = &[77031, 837799, 8400511, 63728127]; + pollster::block_on(assert_execute_gpu( + &ctx.device, + &ctx.queue, + input, + &[350, 524, OVERFLOW, OVERFLOW], + )); + } } -#[test] -// Wasm doesn't support threads -fn test_multithreaded_compute() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct MultithreadedComputeTest; + +impl GpuTest for MultithreadedComputeTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) .limits(wgpu::Limits::downlevel_defaults()) .specific_failure(None, None, Some("V3D"), true) // https://github.com/gfx-rs/wgpu/issues/3250 - .specific_failure(Some(wgpu::Backends::GL), None, Some("llvmpipe"), true), - |ctx| { - use std::{sync::mpsc, thread, time::Duration}; - - let ctx = Arc::new(ctx); - - let thread_count = 8; - - let (tx, rx) = mpsc::channel(); - for _ in 0..thread_count { - let tx = tx.clone(); - let ctx = Arc::clone(&ctx); - thread::spawn(move || { - let input = &[100, 100, 100]; - pollster::block_on(assert_execute_gpu( - &ctx.device, - &ctx.queue, - input, - &[25, 25, 25], - )); - tx.send(true).unwrap(); - }); - } - - for _ in 0..thread_count { - rx.recv_timeout(Duration::from_secs(10)) - .expect("A thread never completed."); - } - }, - ); + .specific_failure(Some(wgpu::Backends::GL), None, Some("llvmpipe"), true) + } + + fn run(&self, ctx: wgpu_test::TestingContext) { + use std::{sync::mpsc, thread, time::Duration}; + + let ctx = Arc::new(ctx); + + let thread_count = 8; + + let (tx, rx) = mpsc::channel(); + for _ in 0..thread_count { + let tx = tx.clone(); + let ctx = Arc::clone(&ctx); + thread::spawn(move || { + let input = &[100, 100, 100]; + pollster::block_on(assert_execute_gpu( + &ctx.device, + &ctx.queue, + input, + &[25, 25, 25], + )); + tx.send(true).unwrap(); + }); + } + + for _ in 0..thread_count { + rx.recv_timeout(Duration::from_secs(10)) + .expect("A thread never completed."); + } + } } async fn assert_execute_gpu( diff --git a/examples/mipmap/Cargo.toml b/examples/mipmap/Cargo.toml index ab117e409b..dff04a4f2b 100644 --- a/examples/mipmap/Cargo.toml +++ b/examples/mipmap/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "mipmap" path = "src/main.rs" +harness = false [dependencies] bytemuck.workspace = true diff --git a/examples/mipmap/src/main.rs b/examples/mipmap/src/main.rs index 25e1890d95..2c098650fb 100644 --- a/examples/mipmap/src/main.rs +++ b/examples/mipmap/src/main.rs @@ -481,22 +481,32 @@ impl wgpu_example::framework::Example for Example { } } +#[cfg(not(test))] fn main() { wgpu_example::framework::run::("mipmap"); } -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn mipmap() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/mipmap/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default() - .backend_failure(wgpu::Backends::GL), - comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], - }); +// Test example +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use std::marker::PhantomData; + + use wgpu_test::infra::GpuTest; + + wgpu_test::infra::main( + [GpuTest::from_value( + wgpu_example::framework::ExampleTestParams { + name: "mipmap", + image_path: "/examples/mipmap/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default() + .backend_failure(wgpu::Backends::GL), + comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + _phantom: PhantomData::, + }, + )], + [], + ) } diff --git a/examples/msaa-line/Cargo.toml b/examples/msaa-line/Cargo.toml index c84d2676ce..ae2ca5b05e 100644 --- a/examples/msaa-line/Cargo.toml +++ b/examples/msaa-line/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "msaa-line" path = "src/main.rs" +harness = false [dependencies] bytemuck.workspace = true diff --git a/examples/msaa-line/src/main.rs b/examples/msaa-line/src/main.rs index c7f69b6339..d10f080050 100644 --- a/examples/msaa-line/src/main.rs +++ b/examples/msaa-line/src/main.rs @@ -308,31 +308,47 @@ impl wgpu_example::framework::Example for Example { } } +#[cfg(not(test))] fn main() { wgpu_example::framework::run::("msaa-line"); } -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); +// Test example +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use std::marker::PhantomData; + + use wgpu_test::infra::GpuTest; + + wgpu_test::infra::main( + [GpuTest::from_value( + wgpu_example::framework::ExampleTestParams { + name: "msaa-line", + image_path: "/examples/msaa-line/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES, + base_test_parameters: wgpu_test::TestParameters::default() + // AMD seems to render nothing on DX12 https://github.com/gfx-rs/wgpu/issues/3838 + .specific_failure(Some(wgpu::Backends::DX12), Some(0x1002), None, false), + // There's a lot of natural variance so we check the weighted median too to differentiate + // real failures from variance. + comparisons: &[ + wgpu_test::ComparisonType::Mean(0.065), + wgpu_test::ComparisonType::Percentile { + percentile: 0.5, + threshold: 0.29, + }, + ], + _phantom: PhantomData::, + }, + )], + [], + ) +} #[test] #[wasm_bindgen_test::wasm_bindgen_test] fn msaa_line() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/msaa-line/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES, - base_test_parameters: wgpu_test::TestParameters::default() - // AMD seems to render nothing on DX12 https://github.com/gfx-rs/wgpu/issues/3838 - .specific_failure(Some(wgpu::Backends::DX12), Some(0x1002), None, false), - // There's a lot of natural variance so we check the weighted median too to differentiate - // real failures from variance. - comparisons: &[ - wgpu_test::ComparisonType::Mean(0.065), - wgpu_test::ComparisonType::Percentile { - percentile: 0.5, - threshold: 0.29, - }, - ], - }); + wgpu_example::framework::test::(wgpu_example::framework::ExampleTestParams {}); } diff --git a/examples/shadow/Cargo.toml b/examples/shadow/Cargo.toml index 0f7847a888..d536eb0e48 100644 --- a/examples/shadow/Cargo.toml +++ b/examples/shadow/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "shadow" path = "src/main.rs" +harness = false [dependencies] bytemuck.workspace = true diff --git a/examples/shadow/src/main.rs b/examples/shadow/src/main.rs index 461b04d17a..2dcfaa3d6b 100644 --- a/examples/shadow/src/main.rs +++ b/examples/shadow/src/main.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, f32::consts, iter, mem, ops::Range, rc::Rc}; +use std::{borrow::Cow, f32::consts, iter, mem, ops::Range, sync::Arc}; use bytemuck::{Pod, Zeroable}; use wgpu::util::{align_to, DeviceExt}; @@ -80,8 +80,8 @@ struct Entity { mx_world: glam::Mat4, rotation_speed: f32, color: wgpu::Color, - vertex_buf: Rc, - index_buf: Rc, + vertex_buf: Arc, + index_buf: Arc, index_format: wgpu::IndexFormat, index_count: usize, uniform_offset: wgpu::DynamicOffset, @@ -221,7 +221,7 @@ impl wgpu_example::framework::Example for Example { // Create the vertex and index buffers let vertex_size = mem::size_of::(); let (cube_vertex_data, cube_index_data) = create_cube(); - let cube_vertex_buf = Rc::new(device.create_buffer_init( + let cube_vertex_buf = Arc::new(device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Cubes Vertex Buffer"), contents: bytemuck::cast_slice(&cube_vertex_data), @@ -229,7 +229,7 @@ impl wgpu_example::framework::Example for Example { }, )); - let cube_index_buf = Rc::new(device.create_buffer_init( + let cube_index_buf = Arc::new(device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Cubes Index Buffer"), contents: bytemuck::cast_slice(&cube_index_data), @@ -306,8 +306,8 @@ impl wgpu_example::framework::Example for Example { mx_world: glam::Mat4::IDENTITY, rotation_speed: 0.0, color: wgpu::Color::WHITE, - vertex_buf: Rc::new(plane_vertex_buf), - index_buf: Rc::new(plane_index_buf), + vertex_buf: Arc::new(plane_vertex_buf), + index_buf: Arc::new(plane_index_buf), index_format, index_count: plane_index_data.len(), uniform_offset: 0, @@ -327,8 +327,8 @@ impl wgpu_example::framework::Example for Example { mx_world, rotation_speed: cube.rotation, color: wgpu::Color::GREEN, - vertex_buf: Rc::clone(&cube_vertex_buf), - index_buf: Rc::clone(&cube_index_buf), + vertex_buf: Arc::clone(&cube_vertex_buf), + index_buf: Arc::clone(&cube_index_buf), index_format, index_count: cube_index_data.len(), uniform_offset: ((i + 1) * uniform_alignment as usize) as _, @@ -836,26 +836,36 @@ impl wgpu_example::framework::Example for Example { } } +#[cfg(not(test))] fn main() { wgpu_example::framework::run::("shadow"); } -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn shadow() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/shadow/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default() - .downlevel_flags(wgpu::DownlevelFlags::COMPARISON_SAMPLERS) - // rpi4 on VK doesn't work: https://gitlab.freedesktop.org/mesa/mesa/-/issues/3916 - .specific_failure(Some(wgpu::Backends::VULKAN), None, Some("V3D"), false) - // llvmpipe versions in CI are flaky: https://github.com/gfx-rs/wgpu/issues/2594 - .specific_failure(Some(wgpu::Backends::VULKAN), None, Some("llvmpipe"), true), - comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], - }); +// Test example +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use std::marker::PhantomData; + + use wgpu_test::infra::GpuTest; + + wgpu_test::infra::main( + [GpuTest::from_value( + wgpu_example::framework::ExampleTestParams { + name: "shadow", + image_path: "/examples/shadow/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default() + .downlevel_flags(wgpu::DownlevelFlags::COMPARISON_SAMPLERS) + // rpi4 on VK doesn't work: https://gitlab.freedesktop.org/mesa/mesa/-/issues/3916 + .specific_failure(Some(wgpu::Backends::VULKAN), None, Some("V3D"), false) + // llvmpipe versions in CI are flaky: https://github.com/gfx-rs/wgpu/issues/2594 + .specific_failure(Some(wgpu::Backends::VULKAN), None, Some("llvmpipe"), true), + comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + _phantom: PhantomData::, + }, + )], + [], + ) } diff --git a/examples/skybox/Cargo.toml b/examples/skybox/Cargo.toml index f70e78e9b1..c96b75004e 100644 --- a/examples/skybox/Cargo.toml +++ b/examples/skybox/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "skybox" path = "src/main.rs" +harness = false [dependencies] bytemuck.workspace = true diff --git a/examples/skybox/src/main.rs b/examples/skybox/src/main.rs index bfb5c10521..7addca0ce4 100644 --- a/examples/skybox/src/main.rs +++ b/examples/skybox/src/main.rs @@ -52,7 +52,7 @@ impl Camera { } } -pub struct Skybox { +pub struct Example { camera: Camera, sky_pipeline: wgpu::RenderPipeline, entity_pipeline: wgpu::RenderPipeline, @@ -63,7 +63,7 @@ pub struct Skybox { staging_belt: wgpu::util::StagingBelt, } -impl Skybox { +impl Example { const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth24Plus; fn create_depth_texture( @@ -89,7 +89,7 @@ impl Skybox { } } -impl wgpu_example::framework::Example for Skybox { +impl wgpu_example::framework::Example for Example { fn optional_features() -> wgpu::Features { wgpu::Features::TEXTURE_COMPRESSION_ASTC | wgpu::Features::TEXTURE_COMPRESSION_ETC2 @@ -356,7 +356,7 @@ impl wgpu_example::framework::Example for Skybox { let depth_view = Self::create_depth_texture(config, device); - Skybox { + Example { camera, sky_pipeline, entity_pipeline, @@ -459,65 +459,66 @@ impl wgpu_example::framework::Example for Skybox { } } +#[cfg(not(test))] fn main() { - wgpu_example::framework::run::("skybox"); + wgpu_example::framework::run::("skybox"); } -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn skybox() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/skybox/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default().specific_failure( - Some(wgpu::Backends::GL), - None, - Some("ANGLE"), - false, - ), - comparisons: &[wgpu_test::ComparisonType::Mean(0.015)], - }); -} - -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn skybox_bc1() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/skybox/screenshot-bc1.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::TEXTURE_COMPRESSION_BC, - base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], - }); -} - -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn skybox_etc2() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/skybox/screenshot-etc2.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::TEXTURE_COMPRESSION_ETC2, - base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - comparisons: &[wgpu_test::ComparisonType::Mean(0.015)], - }); -} - -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn skybox_astc() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/skybox/screenshot-astc.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::TEXTURE_COMPRESSION_ASTC, - base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - comparisons: &[wgpu_test::ComparisonType::Mean(0.016)], - }); +// Test example +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use std::marker::PhantomData; + + use wgpu_test::infra::GpuTest; + + wgpu_test::infra::main( + [ + GpuTest::from_value(wgpu_example::framework::ExampleTestParams { + name: "skybox", + image_path: "/examples/skybox/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default().specific_failure( + Some(wgpu::Backends::GL), + None, + Some("ANGLE"), + false, + ), + comparisons: &[wgpu_test::ComparisonType::Mean(0.015)], + _phantom: PhantomData::, + }), + GpuTest::from_value(wgpu_example::framework::ExampleTestParams { + name: "skybox-bc1", + image_path: "/examples/skybox/screenshot-bc1.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::TEXTURE_COMPRESSION_BC, + base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 + comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + _phantom: PhantomData::, + }), + GpuTest::from_value(wgpu_example::framework::ExampleTestParams { + name: "skybox-etc2", + image_path: "/examples/skybox/screenshot-etc2.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::TEXTURE_COMPRESSION_ETC2, + base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 + comparisons: &[wgpu_test::ComparisonType::Mean(0.015)], + _phantom: PhantomData::, + }), + GpuTest::from_value(wgpu_example::framework::ExampleTestParams { + name: "skybox-astc", + image_path: "/examples/skybox/screenshot-astc.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::TEXTURE_COMPRESSION_ASTC, + base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 + comparisons: &[wgpu_test::ComparisonType::Mean(0.016)], + _phantom: PhantomData::, + }), + ], + [], + ) } diff --git a/examples/stencil-triangles/Cargo.toml b/examples/stencil-triangles/Cargo.toml index cd8e42676c..729b3d9ff1 100644 --- a/examples/stencil-triangles/Cargo.toml +++ b/examples/stencil-triangles/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "stencil-triangles" path = "src/main.rs" +harness = false [dependencies] bytemuck.workspace = true diff --git a/examples/stencil-triangles/src/main.rs b/examples/stencil-triangles/src/main.rs index 1499f9ab74..30b8c72503 100644 --- a/examples/stencil-triangles/src/main.rs +++ b/examples/stencil-triangles/src/main.rs @@ -15,7 +15,7 @@ fn vertex(x: f32, y: f32) -> Vertex { } } -struct Triangles { +struct Example { outer_vertex_buffer: wgpu::Buffer, mask_vertex_buffer: wgpu::Buffer, outer_pipeline: wgpu::RenderPipeline, @@ -23,7 +23,7 @@ struct Triangles { stencil_buffer: wgpu::Texture, } -impl wgpu_example::framework::Example for Triangles { +impl wgpu_example::framework::Example for Example { fn init( config: &wgpu::SurfaceConfiguration, _adapter: &wgpu::Adapter, @@ -155,7 +155,7 @@ impl wgpu_example::framework::Example for Triangles { }); // Done - Triangles { + Example { outer_vertex_buffer, mask_vertex_buffer, outer_pipeline, @@ -228,21 +228,31 @@ impl wgpu_example::framework::Example for Triangles { } } +#[cfg(not(test))] fn main() { - wgpu_example::framework::run::("stencil-triangles"); + wgpu_example::framework::run::("stencil-triangles"); } -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn stencil_triangles() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/stencil-triangles/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[wgpu_test::ComparisonType::Mean(0.03)], - }); +// Test example +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use std::marker::PhantomData; + + use wgpu_test::infra::GpuTest; + + wgpu_test::infra::main( + [GpuTest::from_value( + wgpu_example::framework::ExampleTestParams { + name: "stencil-triangles", + image_path: "/examples/stencil-triangles/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[wgpu_test::ComparisonType::Mean(0.03)], + _phantom: PhantomData::, + }, + )], + [], + ) } diff --git a/examples/texture-arrays/Cargo.toml b/examples/texture-arrays/Cargo.toml index af30e93054..b78821930e 100644 --- a/examples/texture-arrays/Cargo.toml +++ b/examples/texture-arrays/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "texture-arrays" path = "src/main.rs" +harness = false [dependencies] bytemuck.workspace = true diff --git a/examples/texture-arrays/src/main.rs b/examples/texture-arrays/src/main.rs index b2683c8471..b55ae5bb0c 100644 --- a/examples/texture-arrays/src/main.rs +++ b/examples/texture-arrays/src/main.rs @@ -404,35 +404,52 @@ impl wgpu_example::framework::Example for Example { } } +#[cfg(not(test))] fn main() { wgpu_example::framework::run::("texture-arrays"); } -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); +// Test example +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use std::marker::PhantomData; -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn texture_arrays_uniform() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/texture-arrays/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::empty(), - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], - }); -} + use wgpu_test::infra::GpuTest; -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn texture_arrays_non_uniform() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/texture-arrays/screenshot.png", - width: 1024, - height: 768, - optional_features: - wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], - }); + wgpu_test::infra::main( + [ + GpuTest::from_value(wgpu_example::framework::ExampleTestParams { + name: "texture-arrays", + image_path: "/examples/texture-arrays/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::empty(), + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], + _phantom: PhantomData::, + }), + GpuTest::from_value(wgpu_example::framework::ExampleTestParams { + name: "texture-arrays-uniform", + image_path: "/examples/texture-arrays/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::empty(), + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], + _phantom: PhantomData::, + }), + GpuTest::from_value(wgpu_example::framework::ExampleTestParams { + name: "texture-arrays-non-uniform", + image_path: "/examples/texture-arrays/screenshot.png", + width: 1024, + height: 768, + optional_features: + wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], + _phantom: PhantomData::, + }), + ], + [], + ) } diff --git a/examples/water/Cargo.toml b/examples/water/Cargo.toml index 6310f83f8f..de25142033 100644 --- a/examples/water/Cargo.toml +++ b/examples/water/Cargo.toml @@ -9,6 +9,7 @@ publish = false [[bin]] name = "water" path = "src/main.rs" +harness = false [dependencies] bytemuck.workspace = true diff --git a/examples/water/src/main.rs b/examples/water/src/main.rs index 9d1c1e1559..42ce1ea00c 100644 --- a/examples/water/src/main.rs +++ b/examples/water/src/main.rs @@ -813,22 +813,32 @@ impl wgpu_example::framework::Example for Example { } } +#[cfg(not(test))] fn main() { wgpu_example::framework::run::("water"); } -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn water() { - wgpu_example::framework::test::(wgpu_example::framework::FrameworkRefTest { - image_path: "/examples/water/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default() - .downlevel_flags(wgpu::DownlevelFlags::READ_ONLY_DEPTH_STENCIL), - comparisons: &[wgpu_test::ComparisonType::Mean(0.01)], - }); +// Test example +#[cfg(test)] +fn main() -> wgpu_test::infra::MainResult { + use std::marker::PhantomData; + + use wgpu_test::infra::GpuTest; + + wgpu_test::infra::main( + [GpuTest::from_value( + wgpu_example::framework::ExampleTestParams { + name: "water", + image_path: "/examples/water/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default() + .downlevel_flags(wgpu::DownlevelFlags::READ_ONLY_DEPTH_STENCIL), + comparisons: &[wgpu_test::ComparisonType::Mean(0.01)], + _phantom: PhantomData::, + }, + )], + [], + ) } diff --git a/tests/src/infra/mod.rs b/tests/src/infra/mod.rs index 2a0d0844cd..8577baa2ca 100644 --- a/tests/src/infra/mod.rs +++ b/tests/src/infra/mod.rs @@ -38,11 +38,12 @@ pub fn main( }) .collect(); // Cpu tests - tests.extend( - cpu_test_list - .into_iter() - .map(|test| libtest_mimic::Trial::test(test.name(), move || Ok(test.call()))), - ); + tests.extend(cpu_test_list.into_iter().map(|test| { + libtest_mimic::Trial::test(test.name(), move || { + test.call(); + Ok(()) + }) + })); libtest_mimic::run(&args, tests).exit_if_failed(); diff --git a/tests/src/infra/params.rs b/tests/src/infra/params.rs index d6cd3ad286..5cfb7b7e49 100644 --- a/tests/src/infra/params.rs +++ b/tests/src/infra/params.rs @@ -5,11 +5,19 @@ use heck::ToSnakeCase; use crate::{TestParameters, TestingContext}; pub trait GpuTest: Send + Sync + 'static { + #[allow(clippy::new_ret_no_self)] fn new() -> Arc where Self: Sized + Default, { - Arc::new(Self::default()) + Self::from_value(Self::default()) + } + + fn from_value(value: Self) -> Arc + where + Self: Sized, + { + Arc::new(value) } fn name(&self) -> String { @@ -46,6 +54,7 @@ impl CpuTest { } } +// This needs to be generic, otherwise we will get the generic type name of `fn() -> ()`. pub fn cpu_test(test: T) -> CpuTest where T: FnOnce() + Send + Sync + 'static, diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 90b1f579ad..21bdaad358 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -3,7 +3,7 @@ use std::panic::{catch_unwind, AssertUnwindSafe}; -use wgpu::{Adapter, Device, DownlevelFlags, Instance, Queue, Surface}; +use wgpu::{Adapter, Device, DownlevelFlags, Instance, Queue}; use wgt::{AdapterInfo, Backends, DeviceDescriptor, DownlevelCapabilities, Features, Limits}; pub mod image; @@ -54,6 +54,7 @@ fn lowest_downlevel_properties() -> DownlevelCapabilities { } } +#[derive(Clone)] pub struct FailureCase { backends: Option, vendor: Option, @@ -62,6 +63,7 @@ pub struct FailureCase { } // This information determines if a test should run. +#[derive(Clone)] pub struct TestParameters { pub required_features: Features, pub required_downlevel_caps: DownlevelCapabilities, @@ -259,7 +261,7 @@ pub fn initialize_test( let context = TestingContext { adapter, - adapter_info: adapter_info.clone(), + adapter_info, adapter_downlevel_capabilities, device, device_features: parameters.required_features, diff --git a/tests/tests/buffer.rs b/tests/tests/buffer.rs index 1e7445d89e..030430cb30 100644 --- a/tests/tests/buffer.rs +++ b/tests/tests/buffer.rs @@ -1,4 +1,4 @@ -use wgpu_test::{initialize_test, TestParameters, TestingContext}; +use wgpu_test::{infra::GpuTest, TestingContext}; fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str) { let r = wgpu::BufferUsages::MAP_READ; @@ -80,20 +80,26 @@ fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str) ctx.device.poll(wgpu::MaintainBase::Wait); } -#[test] -#[ignore] -fn empty_buffer() { - // TODO: Currently wgpu does not accept empty buffer slices, which - // is what test is about. - initialize_test(TestParameters::default(), |ctx| { +#[derive(Default)] +pub struct EmptyBufferTest; + +impl GpuTest for EmptyBufferTest { + fn parameters(&self, params: wgpu_test::TestParameters) -> wgpu_test::TestParameters { + // wgpu doesn't support zero sized buffers + params.failure() + } + + fn run(&self, ctx: TestingContext) { test_empty_buffer_range(&ctx, 2048, "regular buffer"); test_empty_buffer_range(&ctx, 0, "zero-sized buffer"); - }) + } } -#[test] -fn test_map_offset() { - initialize_test(TestParameters::default(), |ctx| { +#[derive(Default)] +pub struct MapOffsetTest; + +impl GpuTest for MapOffsetTest { + fn run(&self, ctx: TestingContext) { // This test writes 16 bytes at the beginning of buffer mapped mapped with // an offset of 32 bytes. Then the buffer is copied into another buffer that // is read back and we check that the written bytes are correctly placed at @@ -157,5 +163,5 @@ fn test_map_offset() { for byte in &view[48..] { assert_eq!(*byte, 0); } - }); + } } diff --git a/tests/tests/buffer_copy.rs b/tests/tests/buffer_copy.rs index 5fcafe68f0..a7975d4b87 100644 --- a/tests/tests/buffer_copy.rs +++ b/tests/tests/buffer_copy.rs @@ -1,34 +1,39 @@ //! Tests for buffer copy validation. -use wasm_bindgen_test::wasm_bindgen_test; use wgt::BufferAddress; -use wgpu_test::{fail_if, initialize_test, TestParameters}; - -#[test] -#[wasm_bindgen_test] -fn copy_alignment() { - fn try_copy(offset: BufferAddress, size: BufferAddress, should_fail: bool) { - initialize_test(TestParameters::default(), |ctx| { - let buffer = ctx.device.create_buffer(&BUFFER_DESCRIPTOR); - let data = vec![255; size as usize]; - fail_if(&ctx.device, should_fail, || { - ctx.queue.write_buffer(&buffer, offset, &data) - }); - }); - } +use wgpu_test::{fail_if, infra::GpuTest}; + +fn try_copy( + ctx: &wgpu_test::TestingContext, + offset: BufferAddress, + size: BufferAddress, + should_fail: bool, +) { + let buffer = ctx.device.create_buffer(&BUFFER_DESCRIPTOR); + let data = vec![255; size as usize]; + fail_if(&ctx.device, should_fail, || { + ctx.queue.write_buffer(&buffer, offset, &data) + }); +} - try_copy(0, 0, false); - try_copy(4, 16 + 1, true); - try_copy(64, 20 + 2, true); - try_copy(256, 44 + 3, true); - try_copy(1024, 8 + 4, false); - - try_copy(0, 4, false); - try_copy(4 + 1, 8, true); - try_copy(64 + 2, 12, true); - try_copy(256 + 3, 16, true); - try_copy(1024 + 4, 4, false); +#[derive(Default)] +pub struct CopyAlignmentTest; + +impl GpuTest for CopyAlignmentTest { + fn run(&self, ctx: wgpu_test::TestingContext) { + try_copy(&ctx, 0, 0, false); + try_copy(&ctx, 4, 16 + 1, true); + try_copy(&ctx, 64, 20 + 2, true); + try_copy(&ctx, 256, 44 + 3, true); + try_copy(&ctx, 1024, 8 + 4, false); + + try_copy(&ctx, 0, 4, false); + try_copy(&ctx, 4 + 1, 8, true); + try_copy(&ctx, 64 + 2, 12, true); + try_copy(&ctx, 256 + 3, 16, true); + try_copy(&ctx, 1024 + 4, 4, false); + } } const BUFFER_SIZE: BufferAddress = 1234; diff --git a/tests/tests/buffer_usages.rs b/tests/tests/buffer_usages.rs index e0f5164f6e..0cde12331e 100644 --- a/tests/tests/buffer_usages.rs +++ b/tests/tests/buffer_usages.rs @@ -1,74 +1,79 @@ //! Tests for buffer usages validation. -use wasm_bindgen_test::*; -use wgpu_test::{fail_if, initialize_test, TestParameters}; +use wgpu::BufferUsages as Bu; +use wgpu_test::{fail_if, infra::GpuTest, TestParameters}; use wgt::BufferAddress; const BUFFER_SIZE: BufferAddress = 1234; -#[test] -#[wasm_bindgen_test] -fn buffer_usage() { - fn try_create(enable_mappable_primary_buffers: bool, usages: &[(bool, &[wgpu::BufferUsages])]) { - let mut parameters = TestParameters::default(); - if enable_mappable_primary_buffers { - parameters = parameters.features(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS); - } +const ALWAYS_VALID: &[Bu; 4] = &[ + Bu::MAP_READ, + Bu::MAP_WRITE, + Bu::MAP_READ.union(Bu::COPY_DST), + Bu::MAP_WRITE.union(Bu::COPY_SRC), +]; +// MAP_READ can only be paired with COPY_DST and MAP_WRITE can only be paired with COPY_SRC +// (unless Features::MAPPABlE_PRIMARY_BUFFERS is enabled). +const NEEDS_MAPPABLE_PRIMARY_BUFFERS: &[Bu; 7] = &[ + Bu::MAP_READ.union(Bu::COPY_DST.union(Bu::COPY_SRC)), + Bu::MAP_WRITE.union(Bu::COPY_SRC.union(Bu::COPY_DST)), + Bu::MAP_READ.union(Bu::MAP_WRITE), + Bu::MAP_WRITE.union(Bu::MAP_READ), + Bu::MAP_READ.union(Bu::COPY_DST.union(Bu::STORAGE)), + Bu::MAP_WRITE.union(Bu::COPY_SRC.union(Bu::STORAGE)), + Bu::all(), +]; +const INVALID_BITS: Bu = Bu::from_bits_retain(0b1111111111111); +const ALWAYS_FAIL: &[Bu; 2] = &[Bu::empty(), INVALID_BITS]; - initialize_test(parameters, |ctx| { - for (expect_validation_error, usage) in - usages.iter().flat_map(|&(expect_error, usages)| { - usages.iter().copied().map(move |u| (expect_error, u)) - }) - { - fail_if(&ctx.device, expect_validation_error, || { - let _buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: BUFFER_SIZE, - usage, - mapped_at_creation: false, - }); - }); - } +fn try_create(ctx: wgpu_test::TestingContext, usages: &[(bool, &[wgpu::BufferUsages])]) { + for (expect_validation_error, usage) in usages + .iter() + .flat_map(|&(expect_error, usages)| usages.iter().copied().map(move |u| (expect_error, u))) + { + fail_if(&ctx.device, expect_validation_error, || { + let _buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: BUFFER_SIZE, + usage, + mapped_at_creation: false, + }); }); } +} + +#[derive(Default)] +pub struct BufferUsageTest; - use wgpu::BufferUsages as Bu; +impl GpuTest for BufferUsageTest { + fn run(&self, ctx: wgpu_test::TestingContext) { + try_create( + ctx, + &[ + (false, ALWAYS_VALID), + (true, NEEDS_MAPPABLE_PRIMARY_BUFFERS), + (true, ALWAYS_FAIL), + ], + ); + } +} - let always_valid = &[ - Bu::MAP_READ, - Bu::MAP_WRITE, - Bu::MAP_READ | Bu::COPY_DST, - Bu::MAP_WRITE | Bu::COPY_SRC, - ]; - // MAP_READ can only be paired with COPY_DST and MAP_WRITE can only be paired with COPY_SRC - // (unless Features::MAPPABlE_PRIMARY_BUFFERS is enabled). - let needs_mappable_primary_buffers = &[ - Bu::MAP_READ | Bu::COPY_DST | Bu::COPY_SRC, - Bu::MAP_WRITE | Bu::COPY_SRC | Bu::COPY_DST, - Bu::MAP_READ | Bu::MAP_WRITE, - Bu::MAP_WRITE | Bu::MAP_READ, - Bu::MAP_READ | Bu::COPY_DST | Bu::STORAGE, - Bu::MAP_WRITE | Bu::COPY_SRC | Bu::STORAGE, - Bu::all(), - ]; - let invalid_bits = Bu::from_bits_retain(0b1111111111111); - let always_fail = &[Bu::empty(), invalid_bits]; +#[derive(Default)] +pub struct BufferUsageMappablePrimaryTest; - try_create( - false, - &[ - (false, always_valid), - (true, needs_mappable_primary_buffers), - (true, always_fail), - ], - ); - try_create( - true, // enable Features::MAPPABLE_PRIMARY_BUFFERS - &[ - (false, always_valid), - (false, needs_mappable_primary_buffers), - (true, always_fail), - ], - ); +impl GpuTest for BufferUsageMappablePrimaryTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params.features(wgt::Features::MAPPABLE_PRIMARY_BUFFERS) + } + + fn run(&self, ctx: wgpu_test::TestingContext) { + try_create( + ctx, + &[ + (false, ALWAYS_VALID), + (false, NEEDS_MAPPABLE_PRIMARY_BUFFERS), + (true, ALWAYS_FAIL), + ], + ); + } } diff --git a/tests/tests/clear_texture.rs b/tests/tests/clear_texture.rs index 7b2024c64c..92edbe9f97 100644 --- a/tests/tests/clear_texture.rs +++ b/tests/tests/clear_texture.rs @@ -1,5 +1,4 @@ -use wasm_bindgen_test::*; -use wgpu_test::{image::ReadbackBuffers, initialize_test, TestParameters, TestingContext}; +use wgpu_test::{image::ReadbackBuffers, infra::GpuTest, TestParameters, TestingContext}; static TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT: &[wgpu::TextureFormat] = &[ wgpu::TextureFormat::R8Unorm, @@ -323,102 +322,118 @@ fn clear_texture_tests(ctx: &TestingContext, formats: &[wgpu::TextureFormat]) { } } -#[test] -#[wasm_bindgen_test] -fn clear_texture_uncompressed_gles_compat() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct ClearTextureUncompressedGlesCompatTest; + +impl GpuTest for ClearTextureUncompressedGlesCompatTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .webgl2_failure() - .features(wgpu::Features::CLEAR_TEXTURE), - |ctx| { - clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT); - }, - ) + .features(wgpu::Features::CLEAR_TEXTURE) + } + + fn run(&self, ctx: TestingContext) { + clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT); + } } -#[test] -#[wasm_bindgen_test] -fn clear_texture_uncompressed() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct ClearTextureUncompressedCompatTest; + +impl GpuTest for ClearTextureUncompressedCompatTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .webgl2_failure() .backend_failure(wgpu::Backends::GL) - .features(wgpu::Features::CLEAR_TEXTURE), - |ctx| { - clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED); - }, - ) + .features(wgpu::Features::CLEAR_TEXTURE) + } + + fn run(&self, ctx: TestingContext) { + clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED); + } } -#[test] -#[wasm_bindgen_test] -fn clear_texture_depth() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct ClearTextureDepthCompatTest; + +impl GpuTest for ClearTextureDepthCompatTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .webgl2_failure() .downlevel_flags( wgpu::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | wgpu::DownlevelFlags::COMPUTE_SHADERS, ) .limits(wgpu::Limits::downlevel_defaults()) - .features(wgpu::Features::CLEAR_TEXTURE), - |ctx| { - clear_texture_tests(&ctx, TEXTURE_FORMATS_DEPTH); - }, - ) + .features(wgpu::Features::CLEAR_TEXTURE) + } + + fn run(&self, ctx: TestingContext) { + clear_texture_tests(&ctx, TEXTURE_FORMATS_DEPTH); + } } -#[test] -#[wasm_bindgen_test] -fn clear_texture_d32_s8() { - initialize_test( - TestParameters::default() - .features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::DEPTH32FLOAT_STENCIL8), - |ctx| { - clear_texture_tests(&ctx, &[wgpu::TextureFormat::Depth32FloatStencil8]); - }, - ) +#[derive(Default)] +pub struct ClearTextureD32S8Test; + +impl GpuTest for ClearTextureD32S8Test { + fn parameters(&self, params: TestParameters) -> TestParameters { + params.features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::DEPTH32FLOAT_STENCIL8) + } + + fn run(&self, ctx: TestingContext) { + clear_texture_tests(&ctx, &[wgpu::TextureFormat::Depth32FloatStencil8]); + } } -#[test] -fn clear_texture_bc() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct ClearTextureBcTest; + +impl GpuTest for ClearTextureBcTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_BC) .specific_failure(Some(wgpu::Backends::GL), None, Some("ANGLE"), false) // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - .backend_failure(wgpu::Backends::GL), // compressed texture copy to buffer not yet implemented - |ctx| { - clear_texture_tests(&ctx, TEXTURE_FORMATS_BC); - }, - ) + .backend_failure(wgpu::Backends::GL) // compressed texture copy to buffer not yet implemented + } + + fn run(&self, ctx: TestingContext) { + clear_texture_tests(&ctx, TEXTURE_FORMATS_BC); + } } -#[test] -fn clear_texture_astc() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct ClearTextureAstcTest; + +impl GpuTest for ClearTextureAstcTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_ASTC) .limits(wgpu::Limits { max_texture_dimension_2d: wgpu::COPY_BYTES_PER_ROW_ALIGNMENT * 12, ..wgpu::Limits::downlevel_defaults() }) .specific_failure(Some(wgpu::Backends::GL), None, Some("ANGLE"), false) // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - .backend_failure(wgpu::Backends::GL), // compressed texture copy to buffer not yet implemented - |ctx| { - clear_texture_tests(&ctx, TEXTURE_FORMATS_ASTC); - }, - ) + .backend_failure(wgpu::Backends::GL) // compressed texture copy to buffer not yet implemented + } + + fn run(&self, ctx: TestingContext) { + clear_texture_tests(&ctx, TEXTURE_FORMATS_ASTC); + } } -#[test] -fn clear_texture_etc2() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct ClearTextureEtc2Test; + +impl GpuTest for ClearTextureEtc2Test { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_ETC2) .specific_failure(Some(wgpu::Backends::GL), None, Some("ANGLE"), false) // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - .backend_failure(wgpu::Backends::GL), // compressed texture copy to buffer not yet implemented - |ctx| { - clear_texture_tests(&ctx, TEXTURE_FORMATS_ETC2); - }, - ) + .backend_failure(wgpu::Backends::GL) // compressed texture copy to buffer not yet implemented + } + + fn run(&self, ctx: TestingContext) { + clear_texture_tests(&ctx, TEXTURE_FORMATS_ETC2); + } } diff --git a/tests/tests/device.rs b/tests/tests/device.rs deleted file mode 100644 index cfd9ac9606..0000000000 --- a/tests/tests/device.rs +++ /dev/null @@ -1,11 +0,0 @@ -use wasm_bindgen_test::*; - -use wgpu_test::{initialize_test, TestParameters}; - -#[test] -#[wasm_bindgen_test] -fn device_initialization() { - initialize_test(TestParameters::default(), |_ctx| { - // intentionally empty - }) -} diff --git a/tests/tests/encoder.rs b/tests/tests/encoder.rs index 9e541c16a8..1fdcfbb09b 100644 --- a/tests/tests/encoder.rs +++ b/tests/tests/encoder.rs @@ -1,13 +1,13 @@ -use wasm_bindgen_test::*; -use wgpu_test::{initialize_test, TestParameters}; +use wgpu_test::{infra::GpuTest, TestingContext}; -#[test] -#[wasm_bindgen_test] -fn drop_encoder() { - initialize_test(TestParameters::default(), |ctx| { +#[derive(Default)] +pub struct DropEncoderTest; + +impl GpuTest for DropEncoderTest { + fn run(&self, ctx: TestingContext) { let encoder = ctx .device .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); drop(encoder); - }) + } } diff --git a/tests/tests/regression/issue_3457.rs b/tests/tests/regression/issue_3457.rs index b6a2175cc7..d5bfde1ef5 100644 --- a/tests/tests/regression/issue_3457.rs +++ b/tests/tests/regression/issue_3457.rs @@ -1,6 +1,5 @@ -use wgpu_test::{initialize_test, TestParameters}; +use wgpu_test::infra::GpuTest; -use wasm_bindgen_test::wasm_bindgen_test; use wgpu::*; /// The core issue here was that we weren't properly disabling vertex attributes on GL @@ -15,10 +14,11 @@ use wgpu::*; /// /// We use non-consecutive vertex attribute locations (0 and 5) in order to also test /// that we unset the correct locations (see PR #3706). -// #[wasm_bindgen_test] -#[test] -fn pass_reset_vertex_buffer() { - initialize_test(TestParameters::default(), |ctx| { +#[derive(Default)] +pub struct PassResetVertexBufferTest; + +impl GpuTest for PassResetVertexBufferTest { + fn run(&self, ctx: wgpu_test::TestingContext) { let module = ctx .device .create_shader_module(include_wgsl!("issue_3457.wgsl")); @@ -186,5 +186,5 @@ fn pass_reset_vertex_buffer() { drop(single_rpass); ctx.queue.submit(Some(encoder2.finish())); - }) + } } diff --git a/tests/tests/root.rs b/tests/tests/root.rs index f1a7758946..b7b60c605d 100644 --- a/tests/tests/root.rs +++ b/tests/tests/root.rs @@ -1,13 +1,12 @@ use wgpu_test::infra::GpuTest; mod regression { - mod issue_3457; + pub mod issue_3457; } mod buffer; mod buffer_copy; mod buffer_usages; mod clear_texture; -mod device; mod encoder; mod example_wgsl; mod external_texture; @@ -30,18 +29,44 @@ mod zero_init_texture_after_discard; fn main() -> wgpu_test::infra::MainResult { wgpu_test::infra::main( [ + buffer_copy::CopyAlignmentTest::new(), + buffer_usages::BufferUsageMappablePrimaryTest::new(), + buffer_usages::BufferUsageTest::new(), + buffer::EmptyBufferTest::new(), + buffer::MapOffsetTest::new(), + clear_texture::ClearTextureAstcTest::new(), + clear_texture::ClearTextureBcTest::new(), + clear_texture::ClearTextureD32S8Test::new(), + clear_texture::ClearTextureDepthCompatTest::new(), + clear_texture::ClearTextureEtc2Test::new(), + clear_texture::ClearTextureUncompressedCompatTest::new(), + clear_texture::ClearTextureUncompressedGlesCompatTest::new(), + encoder::DropEncoderTest::new(), instance::InitializeTest::new(), - poll::WaitTest::new(), + poll::DoubleWaitOnSubmissionTest::new(), poll::DoubleWaitTest::new(), poll::WaitOnSubmissionTest::new(), - poll::DoubleWaitOnSubmissionTest::new(), poll::WaitOutOfOrderTest::new(), + poll::WaitTest::new(), queue_transfer::QueueWriteTextureOverflowTest::new(), + regression::issue_3457::PassResetVertexBufferTest::new(), resource_descriptor_accessor::BufferSizeAndUsageTest::new(), resource_error::BadBufferTest::new(), resource_error::BadTextureTest::new(), + shader_primitive_index::DrawIndexedTest::new(), + shader_primitive_index::DrawTest::new(), + shader_view_format::ReinterpretSrgbTest::new(), + shader::numeric_builtins::NumericBuiltinsTest::new(), + shader::struct_layout::PushConstantInputTest::new(), + shader::struct_layout::StorageInputTest::new(), + shader::struct_layout::UniformInputTest::new(), + shader::zero_init_workgroup_mem::ZeroInitWorkgroupMemTest::new(), texture_bounds::BadCopyOriginTest::new(), transfer::CopyOverflowZTest::new(), + vertex_indices::DrawInstancedOffsetTest::new(), + vertex_indices::DrawInstancedTest::new(), + vertex_indices::DrawTest::new(), + vertex_indices::DrawVertexTest::new(), write_texture::WriteTextureSubset2dTest::new(), write_texture::WriteTextureSubset3dTest::new(), zero_init_texture_after_discard::DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyAfterSubmitTest::new(), diff --git a/tests/tests/shader/mod.rs b/tests/tests/shader/mod.rs index 4508033068..95a9d858bb 100644 --- a/tests/tests/shader/mod.rs +++ b/tests/tests/shader/mod.rs @@ -15,9 +15,9 @@ use wgpu::{ use wgpu_test::TestingContext; -mod numeric_builtins; -mod struct_layout; -mod zero_init_workgroup_mem; +pub mod numeric_builtins; +pub mod struct_layout; +pub mod zero_init_workgroup_mem; #[derive(Clone, Copy, PartialEq)] enum InputStorageType { diff --git a/tests/tests/shader/numeric_builtins.rs b/tests/tests/shader/numeric_builtins.rs index 2f6981ef7b..d8e6bd22b7 100644 --- a/tests/tests/shader/numeric_builtins.rs +++ b/tests/tests/shader/numeric_builtins.rs @@ -1,8 +1,7 @@ -use wasm_bindgen_test::*; use wgpu::{DownlevelFlags, Limits}; use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest}; -use wgpu_test::{initialize_test, TestParameters}; +use wgpu_test::{infra::GpuTest, TestParameters}; fn create_numeric_builtin_test() -> Vec { let mut tests = Vec::new(); @@ -38,19 +37,21 @@ fn create_numeric_builtin_test() -> Vec { tests } -#[test] -#[wasm_bindgen_test] -fn numeric_builtins() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct NumericBuiltinsTest; + +impl GpuTest for NumericBuiltinsTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) - .limits(Limits::downlevel_defaults()), - |ctx| { - shader_input_output_test( - ctx, - InputStorageType::Storage, - create_numeric_builtin_test(), - ); - }, - ); + .limits(Limits::downlevel_defaults()) + } + + fn run(&self, ctx: wgpu_test::TestingContext) { + shader_input_output_test( + ctx, + InputStorageType::Storage, + create_numeric_builtin_test(), + ); + } } diff --git a/tests/tests/shader/struct_layout.rs b/tests/tests/shader/struct_layout.rs index bc433b5820..dcceb9438d 100644 --- a/tests/tests/shader/struct_layout.rs +++ b/tests/tests/shader/struct_layout.rs @@ -1,10 +1,9 @@ use std::fmt::Write; -use wasm_bindgen_test::*; use wgpu::{Backends, DownlevelFlags, Features, Limits}; use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest, MAX_BUFFER_SIZE}; -use wgpu_test::{initialize_test, TestParameters}; +use wgpu_test::{infra::GpuTest, TestParameters}; fn create_struct_layout_tests(storage_type: InputStorageType) -> Vec { let input_values: Vec<_> = (0..(MAX_BUFFER_SIZE as u32 / 4)).collect(); @@ -175,60 +174,66 @@ fn create_struct_layout_tests(storage_type: InputStorageType) -> Vec tests } -#[test] -#[wasm_bindgen_test] -fn uniform_input() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct UniformInputTest; + +impl GpuTest for UniformInputTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) // Validation errors thrown by the SPIR-V validator https://github.com/gfx-rs/naga/issues/2034 .specific_failure(Some(wgpu::Backends::VULKAN), None, None, false) - .limits(Limits::downlevel_defaults()), - |ctx| { - shader_input_output_test( - ctx, - InputStorageType::Uniform, - create_struct_layout_tests(InputStorageType::Uniform), - ); - }, - ); + .limits(Limits::downlevel_defaults()) + } + + fn run(&self, ctx: wgpu_test::TestingContext) { + shader_input_output_test( + ctx, + InputStorageType::Uniform, + create_struct_layout_tests(InputStorageType::Uniform), + ); + } } -#[test] -#[wasm_bindgen_test] -fn storage_input() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct StorageInputTest; + +impl GpuTest for StorageInputTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) - .limits(Limits::downlevel_defaults()), - |ctx| { - shader_input_output_test( - ctx, - InputStorageType::Storage, - create_struct_layout_tests(InputStorageType::Storage), - ); - }, - ); + .limits(Limits::downlevel_defaults()) + } + + fn run(&self, ctx: wgpu_test::TestingContext) { + shader_input_output_test( + ctx, + InputStorageType::Storage, + create_struct_layout_tests(InputStorageType::Storage), + ); + } } -#[test] -#[wasm_bindgen_test] -fn push_constant_input() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct PushConstantInputTest; + +impl GpuTest for PushConstantInputTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .features(Features::PUSH_CONSTANTS) .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) .limits(Limits { max_push_constant_size: MAX_BUFFER_SIZE as u32, ..Limits::downlevel_defaults() }) - .backend_failure(Backends::GL), - |ctx| { - shader_input_output_test( - ctx, - InputStorageType::PushConstant, - create_struct_layout_tests(InputStorageType::PushConstant), - ); - }, - ); + .backend_failure(Backends::GL) + } + + fn run(&self, ctx: wgpu_test::TestingContext) { + shader_input_output_test( + ctx, + InputStorageType::PushConstant, + create_struct_layout_tests(InputStorageType::PushConstant), + ); + } } diff --git a/tests/tests/shader/zero_init_workgroup_mem.rs b/tests/tests/shader/zero_init_workgroup_mem.rs index a666d2aa28..6e7a68d86d 100644 --- a/tests/tests/shader/zero_init_workgroup_mem.rs +++ b/tests/tests/shader/zero_init_workgroup_mem.rs @@ -1,174 +1,168 @@ use std::num::NonZeroU64; use wgpu::{ - include_wgsl, Backends, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, + include_wgsl, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BufferBinding, BufferBindingType, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, ComputePassDescriptor, ComputePipelineDescriptor, DownlevelFlags, Limits, Maintain, MapMode, PipelineLayoutDescriptor, ShaderStages, }; -use wgpu_test::{initialize_test, TestParameters, TestingContext}; +use wgpu_test::{infra::GpuTest, TestParameters}; -#[test] -fn zero_init_workgroup_mem() { - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct ZeroInitWorkgroupMemTest; + +impl GpuTest for ZeroInitWorkgroupMemTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) + // Validation errors thrown by the SPIR-V validator https://github.com/gfx-rs/naga/issues/2034 + .specific_failure(Some(wgpu::Backends::VULKAN), None, None, false) .limits(Limits::downlevel_defaults()) - // remove both of these once we get to https://github.com/gfx-rs/wgpu/issues/3193 or - // https://github.com/gfx-rs/wgpu/issues/3160 - .specific_failure( - Some(Backends::DX12), - Some(5140), - Some("Microsoft Basic Render Driver"), - true, - ) - .specific_failure(Some(Backends::VULKAN), None, Some("swiftshader"), true), - zero_init_workgroup_mem_impl, - ); -} - -const DISPATCH_SIZE: (u32, u32, u32) = (64, 64, 64); -const TOTAL_WORK_GROUPS: u32 = DISPATCH_SIZE.0 * DISPATCH_SIZE.1 * DISPATCH_SIZE.2; + } -/// nr of bytes we use in the shader -const SHADER_WORKGROUP_MEMORY: u32 = 512 * 4 + 4; -// assume we have this much workgroup memory (2GB) -const MAX_DEVICE_WORKGROUP_MEMORY: u32 = i32::MAX as u32; -const NR_OF_DISPATCHES: u32 = - MAX_DEVICE_WORKGROUP_MEMORY / (SHADER_WORKGROUP_MEMORY * TOTAL_WORK_GROUPS) + 1; // TODO: use div_ceil once stabilized + fn run(&self, ctx: wgpu_test::TestingContext) { + let bgl = ctx + .device + .create_bind_group_layout(&BindGroupLayoutDescriptor { + label: None, + entries: &[BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::COMPUTE, + ty: BindingType::Buffer { + ty: BufferBindingType::Storage { read_only: false }, + has_dynamic_offset: true, + min_binding_size: None, + }, + count: None, + }], + }); + + let output_buffer = ctx.device.create_buffer(&BufferDescriptor { + label: Some("output buffer"), + size: BUFFER_SIZE, + usage: BufferUsages::COPY_DST | BufferUsages::COPY_SRC | BufferUsages::STORAGE, + mapped_at_creation: false, + }); -const OUTPUT_ARRAY_SIZE: u32 = TOTAL_WORK_GROUPS * NR_OF_DISPATCHES; -const BUFFER_SIZE: u64 = OUTPUT_ARRAY_SIZE as u64 * 4; -const BUFFER_BINDING_SIZE: u32 = TOTAL_WORK_GROUPS * 4; + let mapping_buffer = ctx.device.create_buffer(&BufferDescriptor { + label: Some("mapping buffer"), + size: BUFFER_SIZE, + usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, + mapped_at_creation: false, + }); -fn zero_init_workgroup_mem_impl(ctx: TestingContext) { - let bgl = ctx - .device - .create_bind_group_layout(&BindGroupLayoutDescriptor { + let bg = ctx.device.create_bind_group(&BindGroupDescriptor { label: None, - entries: &[BindGroupLayoutEntry { + layout: &bgl, + entries: &[BindGroupEntry { binding: 0, - visibility: ShaderStages::COMPUTE, - ty: BindingType::Buffer { - ty: BufferBindingType::Storage { read_only: false }, - has_dynamic_offset: true, - min_binding_size: None, - }, - count: None, + resource: BindingResource::Buffer(BufferBinding { + buffer: &output_buffer, + offset: 0, + size: Some(NonZeroU64::new(BUFFER_BINDING_SIZE as u64).unwrap()), + }), }], }); - let output_buffer = ctx.device.create_buffer(&BufferDescriptor { - label: Some("output buffer"), - size: BUFFER_SIZE, - usage: BufferUsages::COPY_DST | BufferUsages::COPY_SRC | BufferUsages::STORAGE, - mapped_at_creation: false, - }); - - let mapping_buffer = ctx.device.create_buffer(&BufferDescriptor { - label: Some("mapping buffer"), - size: BUFFER_SIZE, - usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ, - mapped_at_creation: false, - }); - - let bg = ctx.device.create_bind_group(&BindGroupDescriptor { - label: None, - layout: &bgl, - entries: &[BindGroupEntry { - binding: 0, - resource: BindingResource::Buffer(BufferBinding { - buffer: &output_buffer, - offset: 0, - size: Some(NonZeroU64::new(BUFFER_BINDING_SIZE as u64).unwrap()), - }), - }], - }); - - let pll = ctx - .device - .create_pipeline_layout(&PipelineLayoutDescriptor { - label: None, - bind_group_layouts: &[&bgl], - push_constant_ranges: &[], - }); + let pll = ctx + .device + .create_pipeline_layout(&PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[&bgl], + push_constant_ranges: &[], + }); - let sm = ctx - .device - .create_shader_module(include_wgsl!("zero_init_workgroup_mem.wgsl")); - - let pipeline_read = ctx - .device - .create_compute_pipeline(&ComputePipelineDescriptor { - label: Some("pipeline read"), - layout: Some(&pll), - module: &sm, - entry_point: "read", - }); + let sm = ctx + .device + .create_shader_module(include_wgsl!("zero_init_workgroup_mem.wgsl")); - let pipeline_write = ctx - .device - .create_compute_pipeline(&ComputePipelineDescriptor { - label: Some("pipeline write"), - layout: None, - module: &sm, - entry_point: "write", - }); + let pipeline_read = ctx + .device + .create_compute_pipeline(&ComputePipelineDescriptor { + label: Some("pipeline read"), + layout: Some(&pll), + module: &sm, + entry_point: "read", + }); - // -- Initializing data -- + let pipeline_write = ctx + .device + .create_compute_pipeline(&ComputePipelineDescriptor { + label: Some("pipeline write"), + layout: None, + module: &sm, + entry_point: "write", + }); - let output_pre_init_data = vec![1; OUTPUT_ARRAY_SIZE as usize]; - ctx.queue.write_buffer( - &output_buffer, - 0, - bytemuck::cast_slice(&output_pre_init_data), - ); + // -- Initializing data -- - // -- Run test -- + let output_pre_init_data = vec![1; OUTPUT_ARRAY_SIZE as usize]; + ctx.queue.write_buffer( + &output_buffer, + 0, + bytemuck::cast_slice(&output_pre_init_data), + ); - let mut encoder = ctx - .device - .create_command_encoder(&CommandEncoderDescriptor::default()); + // -- Run test -- - let mut cpass = encoder.begin_compute_pass(&ComputePassDescriptor::default()); + let mut encoder = ctx + .device + .create_command_encoder(&CommandEncoderDescriptor::default()); - cpass.set_pipeline(&pipeline_write); - for _ in 0..NR_OF_DISPATCHES { - cpass.dispatch_workgroups(DISPATCH_SIZE.0, DISPATCH_SIZE.1, DISPATCH_SIZE.2); - } + let mut cpass = encoder.begin_compute_pass(&ComputePassDescriptor::default()); - cpass.set_pipeline(&pipeline_read); - for i in 0..NR_OF_DISPATCHES { - cpass.set_bind_group(0, &bg, &[i * BUFFER_BINDING_SIZE]); - cpass.dispatch_workgroups(DISPATCH_SIZE.0, DISPATCH_SIZE.1, DISPATCH_SIZE.2); - } - drop(cpass); + cpass.set_pipeline(&pipeline_write); + for _ in 0..NR_OF_DISPATCHES { + cpass.dispatch_workgroups(DISPATCH_SIZE.0, DISPATCH_SIZE.1, DISPATCH_SIZE.2); + } - // -- Pulldown data -- + cpass.set_pipeline(&pipeline_read); + for i in 0..NR_OF_DISPATCHES { + cpass.set_bind_group(0, &bg, &[i * BUFFER_BINDING_SIZE]); + cpass.dispatch_workgroups(DISPATCH_SIZE.0, DISPATCH_SIZE.1, DISPATCH_SIZE.2); + } + drop(cpass); - encoder.copy_buffer_to_buffer(&output_buffer, 0, &mapping_buffer, 0, BUFFER_SIZE); + // -- Pulldown data -- - ctx.queue.submit(Some(encoder.finish())); + encoder.copy_buffer_to_buffer(&output_buffer, 0, &mapping_buffer, 0, BUFFER_SIZE); - mapping_buffer.slice(..).map_async(MapMode::Read, |_| ()); - ctx.device.poll(Maintain::Wait); + ctx.queue.submit(Some(encoder.finish())); - let mapped = mapping_buffer.slice(..).get_mapped_range(); + mapping_buffer.slice(..).map_async(MapMode::Read, |_| ()); + ctx.device.poll(Maintain::Wait); - let typed: &[u32] = bytemuck::cast_slice(&mapped); + let mapped = mapping_buffer.slice(..).get_mapped_range(); - // -- Check results -- + let typed: &[u32] = bytemuck::cast_slice(&mapped); - let num_disptaches_failed = typed.iter().filter(|&&res| res != 0).count(); - let ratio = (num_disptaches_failed as f32 / OUTPUT_ARRAY_SIZE as f32) * 100.; + // -- Check results -- - assert!( - num_disptaches_failed == 0, - "Zero-initialization of workgroup memory failed ({ratio:.0}% of disptaches failed)." - ); + let num_disptaches_failed = typed.iter().filter(|&&res| res != 0).count(); + let ratio = (num_disptaches_failed as f32 / OUTPUT_ARRAY_SIZE as f32) * 100.; - drop(mapped); - mapping_buffer.unmap(); + assert!( + num_disptaches_failed == 0, + "Zero-initialization of workgroup memory failed ({ratio:.0}% of disptaches failed)." + ); + + drop(mapped); + mapping_buffer.unmap(); + } } + +const DISPATCH_SIZE: (u32, u32, u32) = (64, 64, 64); +const TOTAL_WORK_GROUPS: u32 = DISPATCH_SIZE.0 * DISPATCH_SIZE.1 * DISPATCH_SIZE.2; + +/// nr of bytes we use in the shader +const SHADER_WORKGROUP_MEMORY: u32 = 512 * 4 + 4; +// assume we have this much workgroup memory (2GB) +const MAX_DEVICE_WORKGROUP_MEMORY: u32 = i32::MAX as u32; +const NR_OF_DISPATCHES: u32 = + MAX_DEVICE_WORKGROUP_MEMORY / (SHADER_WORKGROUP_MEMORY * TOTAL_WORK_GROUPS) + 1; // TODO: use div_ceil once stabilized + +const OUTPUT_ARRAY_SIZE: u32 = TOTAL_WORK_GROUPS * NR_OF_DISPATCHES; +const BUFFER_SIZE: u64 = OUTPUT_ARRAY_SIZE as u64 * 4; +const BUFFER_BINDING_SIZE: u32 = TOTAL_WORK_GROUPS * 4; diff --git a/tests/tests/shader_primitive_index/mod.rs b/tests/tests/shader_primitive_index/mod.rs index fafd4b6824..fc83dbb1cc 100644 --- a/tests/tests/shader_primitive_index/mod.rs +++ b/tests/tests/shader_primitive_index/mod.rs @@ -1,6 +1,5 @@ -use wasm_bindgen_test::*; use wgpu::util::{align_to, DeviceExt}; -use wgpu_test::{initialize_test, TestParameters, TestingContext}; +use wgpu_test::{infra::GpuTest, TestParameters, TestingContext}; // // These tests render two triangles to a 2x2 render target. The first triangle @@ -36,54 +35,57 @@ use wgpu_test::{initialize_test, TestParameters, TestingContext}; // draw_indexed() draws the triangles in the opposite order, using index // buffer [3, 4, 5, 0, 1, 2]. This also swaps the resulting pixel colors. // -#[test] -#[wasm_bindgen_test] -fn draw() { - // - // +-----+-----+ - // |white|blue | - // +-----+-----+ - // | red |white| - // +-----+-----+ - // - let expected = [ - 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, - ]; - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct DrawTest; + +impl GpuTest for DrawTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .test_features_limits() - .features(wgpu::Features::SHADER_PRIMITIVE_INDEX), - |ctx| { - pulling_common(ctx, &expected, |rpass| { - rpass.draw(0..6, 0..1); - }) - }, - ); + .features(wgpu::Features::SHADER_PRIMITIVE_INDEX) + } + fn run(&self, ctx: TestingContext) { + // + // +-----+-----+ + // |white|blue | + // +-----+-----+ + // | red |white| + // +-----+-----+ + // + let expected = [ + 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, + ]; + pulling_common(ctx, &expected, |rpass| { + rpass.draw(0..6, 0..1); + }) + } } -#[test] -#[wasm_bindgen_test] -fn draw_indexed() { - // - // +-----+-----+ - // |white| red | - // +-----+-----+ - // |blue |white| - // +-----+-----+ - // - let expected = [ - 255, 255, 255, 255, 255, 0, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255, - ]; - initialize_test( - TestParameters::default() +#[derive(Default)] +pub struct DrawIndexedTest; + +impl GpuTest for DrawIndexedTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params .test_features_limits() - .features(wgpu::Features::SHADER_PRIMITIVE_INDEX), - |ctx| { - pulling_common(ctx, &expected, |rpass| { - rpass.draw_indexed(0..6, 0, 0..1); - }) - }, - ); + .features(wgpu::Features::SHADER_PRIMITIVE_INDEX) + } + + fn run(&self, ctx: TestingContext) { + // + // +-----+-----+ + // |white| red | + // +-----+-----+ + // |blue |white| + // +-----+-----+ + // + let expected = [ + 255, 255, 255, 255, 255, 0, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255, + ]; + pulling_common(ctx, &expected, |rpass| { + rpass.draw_indexed(0..6, 0, 0..1); + }) + } } fn pulling_common( diff --git a/tests/tests/shader_view_format/mod.rs b/tests/tests/shader_view_format/mod.rs index 04d7467d29..4cdd4f470b 100644 --- a/tests/tests/shader_view_format/mod.rs +++ b/tests/tests/shader_view_format/mod.rs @@ -1,13 +1,17 @@ use wgpu::{util::DeviceExt, DownlevelFlags, Limits, TextureFormat}; -use wgpu_test::{image::calc_difference, initialize_test, TestParameters, TestingContext}; +use wgpu_test::{image::calc_difference, infra::GpuTest, TestParameters, TestingContext}; -#[test] -fn reinterpret_srgb_ness() { - let parameters = TestParameters::default() - .downlevel_flags(DownlevelFlags::VIEW_FORMATS) - .limits(Limits::downlevel_defaults()) - .specific_failure(Some(wgpu::Backends::GL), None, None, true); - initialize_test(parameters, |ctx| { +#[derive(Default)] +pub struct ReinterpretSrgbTest; + +impl GpuTest for ReinterpretSrgbTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params + .downlevel_flags(DownlevelFlags::VIEW_FORMATS) + .limits(Limits::downlevel_defaults()) + } + + fn run(&self, ctx: TestingContext) { let unorm_data: [[u8; 4]; 4] = [ [180, 0, 0, 255], [0, 84, 0, 127], @@ -52,7 +56,7 @@ fn reinterpret_srgb_ness() { &srgb_data, &unorm_data, ); - }); + } } fn reinterpret( diff --git a/tests/tests/vertex_indices/mod.rs b/tests/tests/vertex_indices/mod.rs index 707a16a903..57ab6e1f2b 100644 --- a/tests/tests/vertex_indices/mod.rs +++ b/tests/tests/vertex_indices/mod.rs @@ -1,9 +1,8 @@ use std::num::NonZeroU64; -use wasm_bindgen_test::*; use wgpu::util::DeviceExt; -use wgpu_test::{initialize_test, TestParameters, TestingContext}; +use wgpu_test::{infra::GpuTest, TestParameters, TestingContext}; fn pulling_common( ctx: TestingContext, @@ -132,54 +131,64 @@ fn pulling_common( assert_eq!(data, expected); } -#[test] -#[wasm_bindgen_test] -fn draw() { - initialize_test(TestParameters::default().test_features_limits(), |ctx| { +#[derive(Default)] +pub struct DrawTest; + +impl GpuTest for DrawTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params.test_features_limits() + } + + fn run(&self, ctx: TestingContext) { pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { cmb.draw(0..6, 0..1); }) - }) + } } -#[test] -#[wasm_bindgen_test] -fn draw_vertex_offset() { - initialize_test( - TestParameters::default() - .test_features_limits() - .backend_failure(wgpu::Backends::DX11), - |ctx| { - pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { - cmb.draw(0..3, 0..1); - cmb.draw(3..6, 0..1); - }) - }, - ) +#[derive(Default)] +pub struct DrawVertexTest; + +impl GpuTest for DrawVertexTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params.test_features_limits() + } + + fn run(&self, ctx: TestingContext) { + pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { + cmb.draw(0..3, 0..1); + cmb.draw(3..6, 0..1); + }) + } } -#[test] -#[wasm_bindgen_test] -fn draw_instanced() { - initialize_test(TestParameters::default().test_features_limits(), |ctx| { +#[derive(Default)] +pub struct DrawInstancedTest; + +impl GpuTest for DrawInstancedTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params.test_features_limits() + } + + fn run(&self, ctx: TestingContext) { pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { cmb.draw(0..3, 0..2); }) - }) + } } -#[test] -#[wasm_bindgen_test] -fn draw_instanced_offset() { - initialize_test( - TestParameters::default() - .test_features_limits() - .backend_failure(wgpu::Backends::DX11), - |ctx| { - pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { - cmb.draw(0..3, 0..1); - cmb.draw(0..3, 1..2); - }) - }, - ) +#[derive(Default)] +pub struct DrawInstancedOffsetTest; + +impl GpuTest for DrawInstancedOffsetTest { + fn parameters(&self, params: TestParameters) -> TestParameters { + params.test_features_limits() + } + + fn run(&self, ctx: TestingContext) { + pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { + cmb.draw(0..3, 0..1); + cmb.draw(0..3, 1..2); + }) + } } diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 1047e7bf47..eae5217ae1 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -89,6 +89,8 @@ optional = true [dependencies] arrayvec.workspace = true +cfg-if.workspace = true +flume.workspace = true log.workspace = true parking_lot.workspace = true profiling.workspace = true @@ -96,7 +98,6 @@ raw-window-handle.workspace = true serde = { workspace = true, features = ["derive"], optional = true } smallvec.workspace = true static_assertions.workspace = true -cfg-if.workspace = true [dependencies.naga] workspace = true diff --git a/wgpu/src/util/belt.rs b/wgpu/src/util/belt.rs index 9800718e82..0c019fa239 100644 --- a/wgpu/src/util/belt.rs +++ b/wgpu/src/util/belt.rs @@ -3,7 +3,7 @@ use crate::{ BufferViewMut, CommandEncoder, Device, MapMode, }; use std::fmt; -use std::sync::{mpsc, Arc}; +use std::sync::Arc; struct Chunk { buffer: Arc, @@ -36,9 +36,9 @@ pub struct StagingBelt { /// into `active_chunks`. free_chunks: Vec, /// When closed chunks are mapped again, the map callback sends them here. - sender: mpsc::Sender, + sender: flume::Sender, /// Free chunks are received here to be put on `self.free_chunks`. - receiver: mpsc::Receiver, + receiver: flume::Receiver, } impl StagingBelt { @@ -53,7 +53,7 @@ impl StagingBelt { /// (per [`StagingBelt::finish()`]); and /// * bigger is better, within these bounds. pub fn new(chunk_size: BufferAddress) -> Self { - let (sender, receiver) = mpsc::channel(); + let (sender, receiver) = flume::unbounded(); StagingBelt { chunk_size, active_chunks: Vec::new(), From 27d8b6bbfaa1bd231127b51379df9d9519826a13 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 15 Jun 2023 21:36:27 -0400 Subject: [PATCH 09/41] Build out simple xtask for testing --- .github/workflows/ci.yml | 10 ++-------- tests/src/infra/mod.rs | 2 +- xtask/Cargo.lock | 16 ++++++++++++++++ xtask/Cargo.toml | 1 + xtask/src/cli.rs | 3 +++ xtask/src/main.rs | 2 ++ xtask/src/test.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 xtask/src/test.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0856af1669..38add5c03a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -266,19 +266,13 @@ jobs: with: key: test-${{ matrix.os }}-${{ env.CACHE_SUFFIX }} - - name: run wgpu-info - shell: bash - run: | - set -e - - cargo llvm-cov --no-cfg-coverage run --bin wgpu-info --no-report - - name: run tests shell: bash run: | set -e - WGPU_BACKEND=$backend cargo llvm-cov --no-cfg-coverage nextest --no-fail-fast --no-report + cargo xtask test + # WGPU_BACKEND=$backend cargo llvm-cov --no-cfg-coverage nextest --no-fail-fast --no-report - uses: actions/upload-artifact@v3 with: diff --git a/tests/src/infra/mod.rs b/tests/src/infra/mod.rs index 8577baa2ca..cffd9139ac 100644 --- a/tests/src/infra/mod.rs +++ b/tests/src/infra/mod.rs @@ -20,7 +20,7 @@ pub fn main( let config_text = &std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) - .context("failed to read .gpuconfig")?; + .context("Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?")?; let report = report::GpuReport::from_json(config_text).context("Could not pare .gpuconfig JSON")?; diff --git a/xtask/Cargo.lock b/xtask/Cargo.lock index f252d2b0b5..5d8df45a50 100644 --- a/xtask/Cargo.lock +++ b/xtask/Cargo.lock @@ -626,6 +626,21 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "xshell" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "962c039b3a7b16cf4e9a4248397c6585c07547412e7d6a6e035389a802dcfe90" +dependencies = [ + "xshell-macros", +] + +[[package]] +name = "xshell-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dbabb1cbd15a1d6d12d9ed6b35cc6777d4af87ab3ba155ea37215f20beab80c" + [[package]] name = "xtask" version = "0.1.0" @@ -635,4 +650,5 @@ dependencies = [ "env_logger", "log", "pico-args", + "xshell", ] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 343a2166a8..44f61320c6 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -12,5 +12,6 @@ cargo-run-wasm = { version = "0.3.2", git = "https://github.com/ErichDonGubler/c env_logger = "0.10.0" log = "0.4.18" pico-args = { version = "0.5.0", features = ["eq-separator", "short-space-opt", "combined-flags"] } +xshell = "0.2.3" [workspace] diff --git a/xtask/src/cli.rs b/xtask/src/cli.rs index 924e429b48..da774a32f1 100644 --- a/xtask/src/cli.rs +++ b/xtask/src/cli.rs @@ -8,6 +8,7 @@ Usage: xtask Commands: run-wasm + test Options: -h, --help Print help @@ -40,6 +41,7 @@ impl Args { pub(crate) enum Subcommand { RunWasm { args: Arguments }, + Test { args: Arguments }, } impl Subcommand { @@ -50,6 +52,7 @@ impl Subcommand { .context("no subcommand specified; see `--help` for more details")?; match &*subcmd { "run-wasm" => Ok(Self::RunWasm { args }), + "test" => Ok(Self::Test { args }), other => { bail!("unrecognized subcommand {other:?}; see `--help` for more details") } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index ae00c4e2d6..685df94185 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -4,6 +4,7 @@ use anyhow::Context; use cli::{Args, Subcommand}; mod cli; +mod test; fn main() -> ExitCode { env_logger::builder() @@ -35,5 +36,6 @@ fn run(args: Args) -> anyhow::Result<()> { ); Ok(()) } + Subcommand::Test { args } => test::run_tests(args), } } diff --git a/xtask/src/test.rs b/xtask/src/test.rs new file mode 100644 index 0000000000..e87a5c49f2 --- /dev/null +++ b/xtask/src/test.rs @@ -0,0 +1,40 @@ +use anyhow::Context; +use pico_args::Arguments; + +pub fn run_tests(args: Arguments) -> anyhow::Result<()> { + let shell = xshell::Shell::new().context("Couldn't create xshell shell")?; + + shell.change_dir(String::from(env!("CARGO_MANIFEST_DIR")) + "/.."); + + log::info!("Generating .gpuconfig file based on gpus on the system"); + + xshell::cmd!(shell, "cargo run --bin wgpu-info -- --json -o .gpuconfig") + .quiet() + .run() + .context("Failed to run wgpu-info to generate .gpuconfig")?; + + let gpu_count = shell + .read_file(".gpuconfig") + .unwrap() + .lines() + .filter(|line| line.contains("name")) + .count(); + + log::info!( + "Found {} gpu{}", + gpu_count, + if gpu_count == 1 { "" } else { "s" } + ); + + log::info!("Running cargo tests"); + + xshell::cmd!(shell, "cargo nextest run") + .args(args.finish()) + .quiet() + .run() + .context("Tests failed")?; + + log::info!("Finished tests"); + + Ok(()) +} From 80ccf26ed488ccf18e3989fa320db3ed0fad0e3d Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 15 Jun 2023 21:42:40 -0400 Subject: [PATCH 10/41] No fail fast --- xtask/src/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/src/test.rs b/xtask/src/test.rs index e87a5c49f2..8a7d2117ee 100644 --- a/xtask/src/test.rs +++ b/xtask/src/test.rs @@ -28,7 +28,7 @@ pub fn run_tests(args: Arguments) -> anyhow::Result<()> { log::info!("Running cargo tests"); - xshell::cmd!(shell, "cargo nextest run") + xshell::cmd!(shell, "cargo nextest run --no-fail-fast") .args(args.finish()) .quiet() .run() From ce615e9174677e4b8f75c370e3896d95ea188bc4 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 15 Jun 2023 23:07:04 -0400 Subject: [PATCH 11/41] Use adapter index in test name --- tests/src/infra/single.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/infra/single.rs b/tests/src/infra/single.rs index 0f8cec235f..9fb466bfcb 100644 --- a/tests/src/infra/single.rs +++ b/tests/src/infra/single.rs @@ -60,7 +60,7 @@ pub fn run_test( String::from("Executed") }; - let full_name = format!("[{running_msg}] [{backend:?}/{device_name}] {base_name}"); + let full_name = format!("[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}"); libtest_mimic::Trial::test(full_name, move || { if should_skip { From 824c0ecee99c18cd4cb5e3f930bcf365ba87caf9 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 22 Jun 2023 18:12:19 -0400 Subject: [PATCH 12/41] Various adjustments --- .github/workflows/ci.yml | 4 +-- tests/src/infra/single.rs | 3 ++- tests/tests/shader/zero_init_workgroup_mem.rs | 11 ++++++-- xtask/src/cli.rs | 1 + xtask/src/main.rs | 3 ++- xtask/src/test.rs | 27 ++++++++++++++----- 6 files changed, 37 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38add5c03a..77c6ec1f0d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -271,8 +271,7 @@ jobs: run: | set -e - cargo xtask test - # WGPU_BACKEND=$backend cargo llvm-cov --no-cfg-coverage nextest --no-fail-fast --no-report + cargo xtask test --llvm-cov - uses: actions/upload-artifact@v3 with: @@ -331,6 +330,7 @@ jobs: - name: run rustfmt run: | cargo fmt -- --check + cargo fmt --manifest-path xtask/ -- --check check-msrv-cts_runner: name: Clippy cts_runner diff --git a/tests/src/infra/single.rs b/tests/src/infra/single.rs index 9fb466bfcb..b91dbf71fc 100644 --- a/tests/src/infra/single.rs +++ b/tests/src/infra/single.rs @@ -60,7 +60,8 @@ pub fn run_test( String::from("Executed") }; - let full_name = format!("[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}"); + let full_name = + format!("[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}"); libtest_mimic::Trial::test(full_name, move || { if should_skip { diff --git a/tests/tests/shader/zero_init_workgroup_mem.rs b/tests/tests/shader/zero_init_workgroup_mem.rs index 6e7a68d86d..82e31c7560 100644 --- a/tests/tests/shader/zero_init_workgroup_mem.rs +++ b/tests/tests/shader/zero_init_workgroup_mem.rs @@ -17,8 +17,15 @@ impl GpuTest for ZeroInitWorkgroupMemTest { fn parameters(&self, params: TestParameters) -> TestParameters { params .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) - // Validation errors thrown by the SPIR-V validator https://github.com/gfx-rs/naga/issues/2034 - .specific_failure(Some(wgpu::Backends::VULKAN), None, None, false) + // remove both of these once we get to https://github.com/gfx-rs/wgpu/issues/3193 or + // https://github.com/gfx-rs/wgpu/issues/3160 + .specific_failure( + Some(wgpu::Backends::DX12), + Some(5140), + Some("Microsoft Basic Render Driver"), + true, + ) + .specific_failure(Some(wgpu::Backends::VULKAN), None, Some("swiftshader"), true) .limits(Limits::downlevel_defaults()) } diff --git a/xtask/src/cli.rs b/xtask/src/cli.rs index da774a32f1..26831f769d 100644 --- a/xtask/src/cli.rs +++ b/xtask/src/cli.rs @@ -9,6 +9,7 @@ Usage: xtask Commands: run-wasm test + --llvm-cov Run tests with LLVM code coverage using the llvm-cov tool Options: -h, --help Print help diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 8941302822..cad9aa5419 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -30,7 +30,8 @@ fn run(args: Args) -> anyhow::Result<()> { match subcommand { Subcommand::RunWasm { mut args } => { // Use top-level Cargo.toml instead of xtask/Cargo.toml by default - let manifest_path = args.value_from_str("--manifest-path") + let manifest_path = args + .value_from_str("--manifest-path") .unwrap_or_else(|_| "../Cargo.toml".to_string()); let mut arg_vec = args.finish(); arg_vec.push("--manifest-path".into()); diff --git a/xtask/src/test.rs b/xtask/src/test.rs index 8a7d2117ee..8fe36483bb 100644 --- a/xtask/src/test.rs +++ b/xtask/src/test.rs @@ -1,17 +1,32 @@ use anyhow::Context; use pico_args::Arguments; -pub fn run_tests(args: Arguments) -> anyhow::Result<()> { +pub fn run_tests(mut args: Arguments) -> anyhow::Result<()> { + let llvm_cov = args.contains("--llvm-cov"); + let llvm_cov_flags: &[_] = if llvm_cov { + &["llvm-cov", "--no-cfg-coverage", "--no-report"] + } else { + &[""] + }; + let llvm_cov_nextest_flags: &[_] = if llvm_cov { + &["llvm-cov", "--no-cfg-coverage", "--no-report", "nextest"] + } else { + &["nextest", "run"] + }; + let shell = xshell::Shell::new().context("Couldn't create xshell shell")?; shell.change_dir(String::from(env!("CARGO_MANIFEST_DIR")) + "/.."); log::info!("Generating .gpuconfig file based on gpus on the system"); - xshell::cmd!(shell, "cargo run --bin wgpu-info -- --json -o .gpuconfig") - .quiet() - .run() - .context("Failed to run wgpu-info to generate .gpuconfig")?; + xshell::cmd!( + shell, + "cargo {llvm_cov_flags...} run --bin wgpu-info -- --json -o .gpuconfig" + ) + .quiet() + .run() + .context("Failed to run wgpu-info to generate .gpuconfig")?; let gpu_count = shell .read_file(".gpuconfig") @@ -28,7 +43,7 @@ pub fn run_tests(args: Arguments) -> anyhow::Result<()> { log::info!("Running cargo tests"); - xshell::cmd!(shell, "cargo nextest run --no-fail-fast") + xshell::cmd!(shell, "cargo {llvm_cov_nextest_flags...} --no-fail-fast") .args(args.finish()) .quiet() .run() From 3ac189a4922e19433b71a7151190421fbae1fc54 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 22 Jun 2023 19:44:36 -0400 Subject: [PATCH 13/41] Asyncify infrastructure --- Cargo.lock | 1 + Cargo.toml | 1 + examples/common/src/framework.rs | 207 ++++++++++++++++--------------- tests/Cargo.toml | 1 + tests/src/image.rs | 173 +++++++++++++++----------- tests/src/infra/mod.rs | 27 ++-- tests/src/infra/params.rs | 9 +- tests/src/infra/single.rs | 153 +++++++++++++---------- tests/src/lib.rs | 14 ++- xtask/src/test.rs | 2 +- 10 files changed, 332 insertions(+), 256 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4299f3502d..fbb1a79d75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3440,6 +3440,7 @@ dependencies = [ "cfg-if", "console_log", "env_logger", + "futures-lite", "heck", "image", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 4f4759cc4d..48c5bfe641 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ codespan-reporting = "0.11" ddsfile = "0.5" env_logger = "0.10" flume = "0.10" +futures-lite = "1" futures-intrusive = "0.4" rustc-hash = "1.1.0" glam = "0.21.3" diff --git a/examples/common/src/framework.rs b/examples/common/src/framework.rs index fbbb0ac243..946f3e18ad 100644 --- a/examples/common/src/framework.rs +++ b/examples/common/src/framework.rs @@ -1,8 +1,8 @@ -use std::future::Future; #[cfg(target_arch = "wasm32")] use std::str::FromStr; #[cfg(not(target_arch = "wasm32"))] use std::time::Instant; +use std::{future::Future, pin::Pin}; #[cfg(target_arch = "wasm32")] use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas}; use wgpu_test::infra::GpuTest; @@ -520,111 +520,122 @@ impl GpuTest for ExampleTestParams { self.base_test_parameters.clone().features(features) } - fn run(&self, ctx: wgpu_test::TestingContext) { - let spawner = Spawner::new(); + fn run(&self, _ctx: wgpu_test::TestingContext) { + unimplemented!() + } - let dst_texture = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: Some("destination"), - size: wgpu::Extent3d { - width: self.width, - height: self.height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, - view_formats: &[], - }); - - let dst_view = dst_texture.create_view(&wgpu::TextureViewDescriptor::default()); - - let dst_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: Some("image map buffer"), - size: self.width as u64 * self.height as u64 * 4, - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, - mapped_at_creation: false, - }); - - let mut example = E::init( - &wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - width: self.width, - height: self.height, - present_mode: wgpu::PresentMode::Fifo, - alpha_mode: wgpu::CompositeAlphaMode::Auto, - view_formats: vec![wgpu::TextureFormat::Rgba8UnormSrgb], - }, - &ctx.adapter, - &ctx.device, - &ctx.queue, - ); - - example.render(&dst_view, &ctx.device, &ctx.queue, &spawner); - - // Handle specific case for bunnymark - #[allow(deprecated)] - if self.image_path == "/examples/bunnymark/screenshot.png" { - // Press spacebar to spawn bunnies - example.update(winit::event::WindowEvent::KeyboardInput { - input: winit::event::KeyboardInput { - scancode: 0, - state: winit::event::ElementState::Pressed, - virtual_keycode: Some(winit::event::VirtualKeyCode::Space), - modifiers: winit::event::ModifiersState::empty(), + fn run_async<'a>( + &'a self, + ctx: wgpu_test::TestingContext, + ) -> Pin + Send + Sync + 'a>> { + Box::pin(async move { + let dst_texture = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: Some("destination"), + size: wgpu::Extent3d { + width: self.width, + height: self.height, + depth_or_array_layers: 1, }, - device_id: unsafe { winit::event::DeviceId::dummy() }, - is_synthetic: false, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], }); - // Step 3 extra frames - for _ in 0..3 { + let dst_view = dst_texture.create_view(&wgpu::TextureViewDescriptor::default()); + + let dst_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: Some("image map buffer"), + size: self.width as u64 * self.height as u64 * 4, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + + let mut example = E::init( + &wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + width: self.width, + height: self.height, + present_mode: wgpu::PresentMode::Fifo, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![wgpu::TextureFormat::Rgba8UnormSrgb], + }, + &ctx.adapter, + &ctx.device, + &ctx.queue, + ); + + { + let spawner = Spawner::new(); example.render(&dst_view, &ctx.device, &ctx.queue, &spawner); + + // Handle specific case for bunnymark + #[allow(deprecated)] + if self.image_path == "/examples/bunnymark/screenshot.png" { + // Press spacebar to spawn bunnies + example.update(winit::event::WindowEvent::KeyboardInput { + input: winit::event::KeyboardInput { + scancode: 0, + state: winit::event::ElementState::Pressed, + virtual_keycode: Some(winit::event::VirtualKeyCode::Space), + modifiers: winit::event::ModifiersState::empty(), + }, + device_id: unsafe { winit::event::DeviceId::dummy() }, + is_synthetic: false, + }); + + // Step 3 extra frames + for _ in 0..3 { + example.render(&dst_view, &ctx.device, &ctx.queue, &spawner); + } + } } - } - let mut cmd_buf = ctx - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + let mut cmd_buf = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); - cmd_buf.copy_texture_to_buffer( - wgpu::ImageCopyTexture { - texture: &dst_texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - wgpu::ImageCopyBuffer { - buffer: &dst_buffer, - layout: wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: Some(self.width * 4), - rows_per_image: None, + cmd_buf.copy_texture_to_buffer( + wgpu::ImageCopyTexture { + texture: &dst_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, }, - }, - wgpu::Extent3d { - width: self.width, - height: self.height, - depth_or_array_layers: 1, - }, - ); - - ctx.queue.submit(Some(cmd_buf.finish())); - - let dst_buffer_slice = dst_buffer.slice(..); - dst_buffer_slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.device.poll(wgpu::Maintain::Wait); - let bytes = dst_buffer_slice.get_mapped_range().to_vec(); - - wgpu_test::image::compare_image_output( - env!("CARGO_MANIFEST_DIR").to_string() + "/../../" + self.image_path, - ctx.adapter_info.backend, - self.width, - self.height, - &bytes, - self.comparisons, - ); + wgpu::ImageCopyBuffer { + buffer: &dst_buffer, + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(self.width * 4), + rows_per_image: None, + }, + }, + wgpu::Extent3d { + width: self.width, + height: self.height, + depth_or_array_layers: 1, + }, + ); + + ctx.queue.submit(Some(cmd_buf.finish())); + + let dst_buffer_slice = dst_buffer.slice(..); + dst_buffer_slice.map_async(wgpu::MapMode::Read, |_| ()); + ctx.device.poll(wgpu::Maintain::Wait); + let bytes = dst_buffer_slice.get_mapped_range().to_vec(); + + wgpu_test::image::compare_image_output( + env!("CARGO_MANIFEST_DIR").to_string() + "/../../" + self.image_path, + ctx.adapter_info.backend, + self.width, + self.height, + &bytes, + self.comparisons, + ) + .await; + }) } } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 3ae7374e65..dc804e9eda 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -25,6 +25,7 @@ arrayvec.workspace = true bitflags.workspace = true cfg-if.workspace = true env_logger.workspace = true +futures-lite.workspace = true heck.workspace = true libtest-mimic.workspace = true log.workspace = true diff --git a/tests/src/image.rs b/tests/src/image.rs index 9009909d68..278cc59f5b 100644 --- a/tests/src/image.rs +++ b/tests/src/image.rs @@ -2,7 +2,8 @@ use std::{borrow::Cow, ffi::OsStr, io, path::Path}; use wgpu::util::DeviceExt; use wgpu::*; -fn read_png(path: impl AsRef, width: u32, height: u32) -> Option> { +#[cfg(not(target_arch = "wasm32"))] +async fn read_png(path: impl AsRef, width: u32, height: u32) -> Option> { let data = match std::fs::read(&path) { Ok(f) => f, Err(e) => { @@ -39,26 +40,38 @@ fn read_png(path: impl AsRef, width: u32, height: u32) -> Option> Some(buffer) } -#[allow(unused_variables)] -fn write_png( +#[cfg(target_arch = "wasm32")] +async fn read_png(path: impl AsRef, width: u32, height: u32) -> Option> { + unimplemented!("") +} + +#[cfg(not(target_arch = "wasm32"))] +async fn write_png( path: impl AsRef, width: u32, height: u32, data: &[u8], compression: png::Compression, ) { - #[cfg(not(target_arch = "wasm32"))] - { - let file = io::BufWriter::new(std::fs::File::create(path).unwrap()); + let file = io::BufWriter::new(std::fs::File::create(path).unwrap()); - let mut encoder = png::Encoder::new(file, width, height); - encoder.set_color(png::ColorType::Rgba); - encoder.set_depth(png::BitDepth::Eight); - encoder.set_compression(compression); - let mut writer = encoder.write_header().unwrap(); + let mut encoder = png::Encoder::new(file, width, height); + encoder.set_color(png::ColorType::Rgba); + encoder.set_depth(png::BitDepth::Eight); + encoder.set_compression(compression); + let mut writer = encoder.write_header().unwrap(); - writer.write_image_data(data).unwrap(); - } + writer.write_image_data(data).unwrap(); +} + +#[cfg(target_arch = "wasm32")] +async fn write_png( + path: impl AsRef, + width: u32, + height: u32, + data: &[u8], + compression: png::Compression, +) { } pub fn calc_difference(lhs: u8, rhs: u8) -> u8 { @@ -147,7 +160,8 @@ impl ComparisonType { } } -pub fn compare_image_output( +#[cfg(not(target_arch = "wasm32"))] +pub async fn compare_image_output( path: impl AsRef + AsRef, backend: wgpu::Backend, width: u32, @@ -155,29 +169,32 @@ pub fn compare_image_output( test_with_alpha: &[u8], checks: &[ComparisonType], ) { - #[cfg(not(target_arch = "wasm32"))] - { - use std::{ffi::OsString, str::FromStr}; - - let reference_with_alpha = read_png(&path, width, height); - - let reference = match reference_with_alpha { - Some(v) => remove_alpha(&v), - None => { - write_png( - &path, - width, - height, - test_with_alpha, - png::Compression::Best, - ); - return; - } - }; - let test = remove_alpha(test_with_alpha); + use std::{ffi::OsString, str::FromStr}; + + let reference_path = Path::new(&path); + let reference_with_alpha = read_png(&path, width, height).await; + + let reference = match reference_with_alpha { + Some(v) => remove_alpha(&v), + None => { + write_png( + &path, + width, + height, + test_with_alpha, + png::Compression::Best, + ) + .await; + return; + } + }; + let test = remove_alpha(test_with_alpha); - assert_eq!(reference.len(), test.len()); + assert_eq!(reference.len(), test.len()); + let mut all_passed; + let magma_image_with_alpha; + { let reference_flip = nv_flip::FlipImageRgb8::with_data(width, height, &reference); let test_flip = nv_flip::FlipImageRgb8::with_data(width, height, &test); @@ -188,7 +205,6 @@ pub fn compare_image_output( ); let mut pool = nv_flip::FlipPool::from_image(&error_map_flip); - let reference_path = Path::new(&path); println!( "Starting image comparison test with reference image \"{}\"", reference_path.display() @@ -197,58 +213,67 @@ pub fn compare_image_output( print_flip(&mut pool); // If there are no checks, we want to fail the test. - let mut all_passed = !checks.is_empty(); + all_passed = !checks.is_empty(); // We always iterate all of these, as the call to check prints for check in checks { all_passed &= check.check(&mut pool); } - let file_stem = reference_path.file_stem().unwrap().to_string_lossy(); - // Determine the paths to write out the various intermediate files - let actual_path = Path::new(&path).with_file_name( - OsString::from_str(&format!("{}-{}-actual.png", file_stem, backend.to_str(),)).unwrap(), - ); - let difference_path = Path::new(&path).with_file_name( - OsString::from_str(&format!( - "{}-{}-difference.png", - file_stem, - backend.to_str(), - )) - .unwrap(), - ); - // Convert the error values to a false color reprensentation let magma_image = error_map_flip .apply_color_lut(&nv_flip::magma_lut()) .to_vec(); - let magma_image_with_alpha = add_alpha(&magma_image); - - write_png( - actual_path, - width, - height, - test_with_alpha, - png::Compression::Fast, - ); - write_png( - difference_path, - width, - height, - &magma_image_with_alpha, - png::Compression::Fast, - ); - - if !all_passed { - panic!("Image data mismatch!") - } + magma_image_with_alpha = add_alpha(&magma_image); } - #[cfg(target_arch = "wasm32")] - { - let _ = (path, backend, width, height, test_with_alpha, checks); + let file_stem = reference_path.file_stem().unwrap().to_string_lossy(); + + // Determine the paths to write out the various intermediate files + let actual_path = Path::new(&path).with_file_name( + OsString::from_str(&format!("{}-{}-actual.png", file_stem, backend.to_str(),)).unwrap(), + ); + let difference_path = Path::new(&path).with_file_name( + OsString::from_str(&format!( + "{}-{}-difference.png", + file_stem, + backend.to_str(), + )) + .unwrap(), + ); + + write_png( + actual_path, + width, + height, + test_with_alpha, + png::Compression::Fast, + ) + .await; + write_png( + difference_path, + width, + height, + &magma_image_with_alpha, + png::Compression::Fast, + ) + .await; + + if !all_passed { + panic!("Image data mismatch!") } } +#[cfg(target_arch = "wasm32")] +pub async fn compare_image_output( + path: impl AsRef + AsRef, + backend: wgpu::Backend, + width: u32, + height: u32, + test_with_alpha: &[u8], + checks: &[ComparisonType], +) { +} + fn copy_via_compute( device: &Device, encoder: &mut CommandEncoder, diff --git a/tests/src/infra/mod.rs b/tests/src/infra/mod.rs index cffd9139ac..e0524ceb84 100644 --- a/tests/src/infra/mod.rs +++ b/tests/src/infra/mod.rs @@ -4,7 +4,7 @@ use anyhow::Context; pub use params::{cpu_test, GpuTest}; -use crate::infra::params::CpuTest; +use crate::infra::{params::CpuTest, single::SingleTest}; mod params; mod report; @@ -16,8 +16,6 @@ pub fn main( gpu_test_list: [Arc; GPU_TEST_COUNT], cpu_test_list: [CpuTest; CPU_TEST_COUNT], ) -> MainResult { - let args = libtest_mimic::Arguments::from_args(); - let config_text = &std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) .context("Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?")?; @@ -25,7 +23,7 @@ pub fn main( report::GpuReport::from_json(config_text).context("Could not pare .gpuconfig JSON")?; // Gpu tests - let mut tests: Vec<_> = gpu_test_list + let tests = gpu_test_list .into_iter() .flat_map(|test| { report @@ -33,19 +31,20 @@ pub fn main( .iter() .enumerate() .map(move |(adapter_index, adapter)| { - single::run_test(test.clone(), adapter, adapter_index) + SingleTest::from_gpu_test(test.clone(), adapter, adapter_index) }) - }) - .collect(); + }); // Cpu tests - tests.extend(cpu_test_list.into_iter().map(|test| { - libtest_mimic::Trial::test(test.name(), move || { - test.call(); - Ok(()) - }) - })); + let tests = tests.chain(cpu_test_list.into_iter().map(SingleTest::from_cpu_test)); - libtest_mimic::run(&args, tests).exit_if_failed(); + execute_native(tests); Ok(()) } + +fn execute_native(tests: impl IntoIterator) { + let args = libtest_mimic::Arguments::from_args(); + let trials = tests.into_iter().map(SingleTest::into_trial).collect(); + + libtest_mimic::run(&args, trials).exit_if_failed(); +} diff --git a/tests/src/infra/params.rs b/tests/src/infra/params.rs index 5cfb7b7e49..48413f4002 100644 --- a/tests/src/infra/params.rs +++ b/tests/src/infra/params.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{future::Future, pin::Pin, sync::Arc}; use heck::ToSnakeCase; @@ -37,6 +37,13 @@ pub trait GpuTest: Send + Sync + 'static { } fn run(&self, ctx: TestingContext); + + fn run_async<'a>( + &'a self, + ctx: TestingContext, + ) -> Pin + Send + Sync + 'a>> { + Box::pin(async move { self.run(ctx) }) + } } pub struct CpuTest { diff --git a/tests/src/infra/single.rs b/tests/src/infra/single.rs index b91dbf71fc..bc3282c54a 100644 --- a/tests/src/infra/single.rs +++ b/tests/src/infra/single.rs @@ -1,78 +1,103 @@ -use std::sync::Arc; +use std::{future::Future, pin::Pin, sync::Arc}; use arrayvec::ArrayVec; use crate::{ - infra::{report::AdapterReport, GpuTest}, + infra::{params::CpuTest, report::AdapterReport, GpuTest}, initialize_test, TestParameters, }; -pub fn run_test( - test: Arc, - adapter: &AdapterReport, - adapter_index: usize, -) -> libtest_mimic::Trial { - let params = TestParameters::default(); - let params = test.parameters(params); - - let base_name = test.name(); - let backend = &adapter.info.backend; - let device_name = &adapter.info.name; - - // Figure out if we should skip the test and if so, why. - let mut skipped_reasons: ArrayVec<_, 4> = ArrayVec::new(); - let missing_features = params.required_features - adapter.features; - if !missing_features.is_empty() { - skipped_reasons.push("Features"); - } +pub(super) struct SingleTest { + name: String, + future: Pin> + Send + Sync>>, +} - if !params.required_limits.check_limits(&adapter.limits) { - skipped_reasons.push("Limits"); +impl SingleTest { + pub fn from_cpu_test(cpu_test: CpuTest) -> Self { + Self { + name: cpu_test.name().to_owned(), + future: Box::pin(async move { + cpu_test.call(); + Ok(()) + }), + } } - let missing_downlevel_flags = - params.required_downlevel_caps.flags - adapter.downlevel_caps.flags; - if !missing_downlevel_flags.is_empty() { - skipped_reasons.push("Downlevel Flags"); - } + pub fn from_gpu_test( + test: Arc, + adapter: &AdapterReport, + adapter_index: usize, + ) -> Self { + let params = TestParameters::default(); + let params = test.parameters(params); - if params.required_downlevel_caps.shader_model > adapter.downlevel_caps.shader_model { - skipped_reasons.push("Shader Model"); - } + let base_name = test.name(); + let backend = &adapter.info.backend; + let device_name = &adapter.info.name; + + // Figure out if we should skip the test and if so, why. + let mut skipped_reasons: ArrayVec<_, 4> = ArrayVec::new(); + let missing_features = params.required_features - adapter.features; + if !missing_features.is_empty() { + skipped_reasons.push("Features"); + } + + if !params.required_limits.check_limits(&adapter.limits) { + skipped_reasons.push("Limits"); + } + + let missing_downlevel_flags = + params.required_downlevel_caps.flags - adapter.downlevel_caps.flags; + if !missing_downlevel_flags.is_empty() { + skipped_reasons.push("Downlevel Flags"); + } + + if params.required_downlevel_caps.shader_model > adapter.downlevel_caps.shader_model { + skipped_reasons.push("Shader Model"); + } + + let expected_failure = params.to_failure_reasons(&adapter.info); + + let mut should_skip = false; + let running_msg = if !skipped_reasons.is_empty() { + should_skip = true; + format!("Skipped: {}", skipped_reasons.join(" | ")) + } else if let Some((failure_resasons, skip)) = expected_failure { + should_skip |= skip; + let names: ArrayVec<_, 4> = failure_resasons + .iter_names() + .map(|(name, _)| name) + .collect(); + let names_text = names.join(" | "); - let expected_failure = params.to_failure_reasons(&adapter.info); - - let mut should_skip = false; - let running_msg = if !skipped_reasons.is_empty() { - should_skip = true; - format!("Skipped: {}", skipped_reasons.join(" | ")) - } else if let Some((failure_resasons, skip)) = expected_failure { - should_skip |= skip; - let names: ArrayVec<_, 4> = failure_resasons - .iter_names() - .map(|(name, _)| name) - .collect(); - let names_text = names.join(" | "); - - let skip_text = if skip { "Skipped " } else { "Executed " }; - format!("{skip_text}Failure: {}", names_text) - } else { - String::from("Executed") - }; - - let full_name = - format!("[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}"); - - libtest_mimic::Trial::test(full_name, move || { - if should_skip { - return Ok(()); + let skip_text = if skip { "Skipped " } else { "Executed " }; + format!("{skip_text}Failure: {}", names_text) + } else { + String::from("Executed") + }; + + let full_name = + format!("[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}"); + + Self { + name: full_name, + future: Box::pin(async move { + if should_skip { + return Ok(()); + } + initialize_test( + params, + expected_failure.map(|(reasons, _)| reasons), + adapter_index, + &*test, + ) + .await; + Ok(()) + }), } - initialize_test( - params, - expected_failure.map(|(reasons, _)| reasons), - adapter_index, - |ctx| test.run(ctx), - ); - Ok(()) - }) + } + + pub fn into_trial(self) -> libtest_mimic::Trial { + libtest_mimic::Trial::test(self.name, || pollster::block_on(self.future)) + } } diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 21bdaad358..8774eb910a 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,8 +1,9 @@ //! This module contains common test-only code that needs to be shared between the examples and the tests. #![allow(dead_code)] // This module is used in a lot of contexts and only parts of it will be used -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::AssertUnwindSafe; +use futures_lite::FutureExt; use wgpu::{Adapter, Device, DownlevelFlags, Instance, Queue}; use wgt::{AdapterInfo, Backends, DeviceDescriptor, DownlevelCapabilities, Features, Limits}; @@ -10,6 +11,8 @@ pub mod image; pub mod infra; mod isolation; +use crate::infra::GpuTest; + pub use self::image::ComparisonType; const CANVAS_ID: &str = "test-canvas"; @@ -234,11 +237,11 @@ impl TestParameters { } } -pub fn initialize_test( +pub async fn initialize_test( parameters: TestParameters, expected_failure_reason: Option, adapter_index: usize, - test_function: impl FnOnce(TestingContext), + test: &dyn GpuTest, ) { // We don't actually care if it fails #[cfg(not(target_arch = "wasm32"))] @@ -269,7 +272,10 @@ pub fn initialize_test( queue, }; - let panicked = catch_unwind(AssertUnwindSafe(|| test_function(context))).is_err(); + let panicked = AssertUnwindSafe(test.run_async(context)) + .catch_unwind() + .await + .is_err(); cfg_if::cfg_if!( if #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] { let canary_set = wgpu::hal::VALIDATION_CANARY.get_and_reset(); diff --git a/xtask/src/test.rs b/xtask/src/test.rs index 8fe36483bb..96afdd0ab4 100644 --- a/xtask/src/test.rs +++ b/xtask/src/test.rs @@ -6,7 +6,7 @@ pub fn run_tests(mut args: Arguments) -> anyhow::Result<()> { let llvm_cov_flags: &[_] = if llvm_cov { &["llvm-cov", "--no-cfg-coverage", "--no-report"] } else { - &[""] + &[] }; let llvm_cov_nextest_flags: &[_] = if llvm_cov { &["llvm-cov", "--no-cfg-coverage", "--no-report", "nextest"] From 6e54c4df3b20b0289cdcefe9e7bb1a0f3ab849e0 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Wed, 27 Sep 2023 22:59:56 -0400 Subject: [PATCH 14/41] Convert to macro based tests --- Cargo.lock | 24 +- Cargo.toml | 5 + examples/boids/src/main.rs | 43 ++-- examples/bunnymark/src/main.rs | 49 ++-- examples/common/src/framework.rs | 234 +++++++++--------- examples/conservative-raster/src/main.rs | 35 ++- examples/cube/src/main.rs | 79 +++--- examples/hello-compute/Cargo.toml | 3 +- examples/hello-compute/src/main.rs | 19 +- examples/hello-compute/src/tests.rs | 110 ++++---- examples/mipmap/src/main.rs | 39 ++- examples/msaa-line/src/main.rs | 59 ++--- examples/shadow/src/main.rs | 47 ++-- examples/skybox/src/main.rs | 119 ++++----- examples/stencil-triangles/src/main.rs | 35 ++- examples/texture-arrays/src/main.rs | 87 +++---- examples/water/src/main.rs | 39 ++- tests/Cargo.toml | 11 +- tests/src/infra/mod.rs | 42 ++-- tests/src/infra/params.rs | 96 +++---- tests/src/infra/single.rs | 25 +- tests/src/lib.rs | 19 +- tests/tests/buffer.rs | 131 +++++----- tests/tests/buffer_copy.rs | 34 ++- tests/tests/buffer_usages.rs | 44 ++-- tests/tests/clear_texture.rs | 143 +++++------ tests/tests/cpu.rs | 1 + tests/tests/encoder.rs | 20 +- tests/tests/example_wgsl.rs | 1 + tests/tests/external_texture.rs | 231 +++++++++-------- tests/tests/gpu.rs | 26 ++ tests/tests/instance.rs | 9 +- tests/tests/poll.rs | 89 +++---- tests/tests/queue_transfer.rs | 13 +- tests/tests/regression/issue_3457.rs | 13 +- tests/tests/resource_descriptor_accessor.rs | 35 ++- tests/tests/resource_error.rs | 96 ++++--- tests/tests/root.rs | 80 ------ tests/tests/shader/numeric_builtins.rs | 22 +- tests/tests/shader/struct_layout.rs | 62 ++--- tests/tests/shader/zero_init_workgroup_mem.rs | 29 +-- tests/tests/shader_primitive_index/mod.rs | 40 ++- tests/tests/shader_view_format/mod.rs | 24 +- tests/tests/texture_bounds.rs | 158 ++++++------ tests/tests/transfer.rs | 126 +++++----- tests/tests/vertex_indices/mod.rs | 66 ++--- tests/tests/write_texture.rs | 29 +-- .../tests/zero_init_texture_after_discard.rs | 125 +++++----- wgpu-macros/Cargo.toml | 20 ++ wgpu-macros/src/lib.rs | 33 +++ 50 files changed, 1325 insertions(+), 1594 deletions(-) create mode 100644 tests/tests/cpu.rs create mode 100644 tests/tests/gpu.rs delete mode 100644 tests/tests/root.rs create mode 100644 wgpu-macros/Cargo.toml create mode 100644 wgpu-macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index fbb1a79d75..a0ac33514d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -556,6 +556,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "ctor" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583" +dependencies = [ + "quote", + "syn 2.0.18", +] + [[package]] name = "cts_runner" version = "0.1.0" @@ -3295,7 +3305,7 @@ dependencies = [ "console_error_panic_hook", "console_log", "env_logger", - "futures-intrusive", + "flume", "log", "pollster", "wasm-bindgen-futures", @@ -3361,6 +3371,15 @@ dependencies = [ "wgpu-types", ] +[[package]] +name = "wgpu-macros" +version = "0.16.0" +dependencies = [ + "heck", + "quote", + "syn 2.0.18", +] + [[package]] name = "wgpu-mipmap-example" version = "0.16.0" @@ -3439,6 +3458,7 @@ dependencies = [ "bytemuck", "cfg-if", "console_log", + "ctor", "env_logger", "futures-lite", "heck", @@ -3448,6 +3468,7 @@ dependencies = [ "log", "naga", "nv-flip", + "parking_lot 0.12.1", "png", "pollster", "raw-window-handle 0.5.2", @@ -3458,6 +3479,7 @@ dependencies = [ "wasm-bindgen-test", "web-sys", "wgpu", + "wgpu-macros", "wgpu-types", ] diff --git a/Cargo.toml b/Cargo.toml index 48c5bfe641..5bfb280f62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "wgpu-core", "wgpu-hal", "wgpu-info", + "wgpu-macros", "wgpu-types", "tests", ] @@ -20,6 +21,7 @@ default-members = [ "wgpu-core", "wgpu-hal", "wgpu-info", + "wgpu-macros", "wgpu-types", "tests" ] @@ -64,6 +66,7 @@ bytemuck = { version = "1.13", features = ["derive"] } cfg_aliases = "0.1" cfg-if = "1" codespan-reporting = "0.11" +ctor = "0.2" ddsfile = "0.5" env_logger = "0.10" flume = "0.10" @@ -85,6 +88,7 @@ num-traits = { version = "0.2" } # This will not be required in the next release since it has been removed from the default feature in https://github.com/Razaekel/noise-rs/commit/1af9e1522236b2c584fb9a02150c9c67a5e6bb04#diff-2e9d962a08321605940b5a657135052fbcef87b5e360662bb527c96d9a615542 noise = { version = "0.7", default-features = false } obj = "0.10" +once_cell = "1" # parking_lot 0.12 switches from `winapi` to `windows`; permit either parking_lot = ">=0.11,<0.13" pico-args = { version = "0.5.0", features = ["eq-separator", "short-space-opt", "combined-flags"] } @@ -101,6 +105,7 @@ static_assertions = "1.1.0" thiserror = "1" wgpu = { version = "0.16", path = "./wgpu" } wgpu-example = { version = "0.16", path = "./examples/common" } +wgpu-macros = { version = "0.16", path = "./wgpu-macros" } wgpu-test = { version = "0.16", path = "./tests"} winit = "0.27.1" diff --git a/examples/boids/src/main.rs b/examples/boids/src/main.rs index 2ddaaacb24..663acdefd2 100644 --- a/examples/boids/src/main.rs +++ b/examples/boids/src/main.rs @@ -327,29 +327,22 @@ fn main() { wgpu_example::framework::run::("boids"); } -// Test example #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use std::marker::PhantomData; - - use wgpu_test::infra::GpuTest; - - wgpu_test::infra::main( - [GpuTest::from_value( - wgpu_example::framework::ExampleTestParams { - name: "boids", - // Generated on 1080ti on Vk/Windows - image_path: "examples/boids/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default() - .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) - .limits(wgpu::Limits::downlevel_defaults()), - comparisons: &[wgpu_test::ComparisonType::Mean(0.005)], - _phantom: PhantomData::, - }, - )], - [], - ) -} +#[wgpu_test::gpu_test] +static TEST: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "boids", + // Generated on 1080ti on Vk/Windows + image_path: "examples/boids/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default() + .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) + .limits(wgpu::Limits::downlevel_defaults()), + comparisons: &[wgpu_test::ComparisonType::Mean(0.005)], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +wgpu_test::gpu_test_main!(); diff --git a/examples/bunnymark/src/main.rs b/examples/bunnymark/src/main.rs index b2b40f5462..1763869bb9 100644 --- a/examples/bunnymark/src/main.rs +++ b/examples/bunnymark/src/main.rs @@ -356,33 +356,26 @@ fn main() { wgpu_example::framework::run::("bunnymark"); } -// Test example #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use std::marker::PhantomData; - - use wgpu_test::infra::GpuTest; - - wgpu_test::infra::main( - [GpuTest::from_value( - wgpu_example::framework::ExampleTestParams { - name: "bunnymark", - image_path: "/examples/bunnymark/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default(), - // We're looking for very small differences, so look in the high percentiles. - comparisons: &[ - wgpu_test::ComparisonType::Mean(0.05), - wgpu_test::ComparisonType::Percentile { - percentile: 0.95, - threshold: 0.05, - }, - ], - _phantom: PhantomData::, +#[wgpu_test::gpu_test] +static TEST: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "bunnymark", + image_path: "/examples/bunnymark/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default(), + // We're looking for very small differences, so look in the high percentiles. + comparisons: &[ + wgpu_test::ComparisonType::Mean(0.05), + wgpu_test::ComparisonType::Percentile { + percentile: 0.95, + threshold: 0.05, }, - )], - [], - ) -} + ], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +wgpu_test::gpu_test_main!(); diff --git a/examples/common/src/framework.rs b/examples/common/src/framework.rs index 946f3e18ad..f77799bb29 100644 --- a/examples/common/src/framework.rs +++ b/examples/common/src/framework.rs @@ -1,11 +1,11 @@ +use std::future::Future; #[cfg(target_arch = "wasm32")] use std::str::FromStr; #[cfg(not(target_arch = "wasm32"))] use std::time::Instant; -use std::{future::Future, pin::Pin}; #[cfg(target_arch = "wasm32")] use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas}; -use wgpu_test::infra::GpuTest; +use wgpu_test::infra::GpuTestConfiguration; use winit::{ event::{self, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -507,135 +507,125 @@ pub struct ExampleTestParams { pub _phantom: std::marker::PhantomData, } -impl GpuTest for ExampleTestParams { - fn name(&self) -> String { - self.name.into() - } - - fn parameters(&self, _params: wgpu_test::TestParameters) -> wgpu_test::TestParameters { - assert_eq!(self.width % 64, 0, "width needs to be aligned 64"); - - let features = E::required_features() | self.optional_features; +impl From> for GpuTestConfiguration { + fn from(params: ExampleTestParams) -> Self { + GpuTestConfiguration::new() + .name(params.name) + .parameters({ + assert_eq!(params.width % 64, 0, "width needs to be aligned 64"); - self.base_test_parameters.clone().features(features) - } - - fn run(&self, _ctx: wgpu_test::TestingContext) { - unimplemented!() - } + let features = E::required_features() | params.optional_features; - fn run_async<'a>( - &'a self, - ctx: wgpu_test::TestingContext, - ) -> Pin + Send + Sync + 'a>> { - Box::pin(async move { - let dst_texture = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: Some("destination"), - size: wgpu::Extent3d { - width: self.width, - height: self.height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, - view_formats: &[], - }); + params.base_test_parameters.clone().features(features) + }) + .run_async(move |ctx| async move { + let dst_texture = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: Some("destination"), + size: wgpu::Extent3d { + width: params.width, + height: params.height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + }); - let dst_view = dst_texture.create_view(&wgpu::TextureViewDescriptor::default()); + let dst_view = dst_texture.create_view(&wgpu::TextureViewDescriptor::default()); - let dst_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: Some("image map buffer"), - size: self.width as u64 * self.height as u64 * 4, - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, - mapped_at_creation: false, - }); + let dst_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: Some("image map buffer"), + size: params.width as u64 * params.height as u64 * 4, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); - let mut example = E::init( - &wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - width: self.width, - height: self.height, - present_mode: wgpu::PresentMode::Fifo, - alpha_mode: wgpu::CompositeAlphaMode::Auto, - view_formats: vec![wgpu::TextureFormat::Rgba8UnormSrgb], - }, - &ctx.adapter, - &ctx.device, - &ctx.queue, - ); - - { - let spawner = Spawner::new(); - example.render(&dst_view, &ctx.device, &ctx.queue, &spawner); - - // Handle specific case for bunnymark - #[allow(deprecated)] - if self.image_path == "/examples/bunnymark/screenshot.png" { - // Press spacebar to spawn bunnies - example.update(winit::event::WindowEvent::KeyboardInput { - input: winit::event::KeyboardInput { - scancode: 0, - state: winit::event::ElementState::Pressed, - virtual_keycode: Some(winit::event::VirtualKeyCode::Space), - modifiers: winit::event::ModifiersState::empty(), - }, - device_id: unsafe { winit::event::DeviceId::dummy() }, - is_synthetic: false, - }); + let mut example = E::init( + &wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + width: params.width, + height: params.height, + present_mode: wgpu::PresentMode::Fifo, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![wgpu::TextureFormat::Rgba8UnormSrgb], + }, + &ctx.adapter, + &ctx.device, + &ctx.queue, + ); - // Step 3 extra frames - for _ in 0..3 { - example.render(&dst_view, &ctx.device, &ctx.queue, &spawner); + { + let spawner = Spawner::new(); + example.render(&dst_view, &ctx.device, &ctx.queue, &spawner); + + // Handle specific case for bunnymark + #[allow(deprecated)] + if params.image_path == "/examples/bunnymark/screenshot.png" { + // Press spacebar to spawn bunnies + example.update(winit::event::WindowEvent::KeyboardInput { + input: winit::event::KeyboardInput { + scancode: 0, + state: winit::event::ElementState::Pressed, + virtual_keycode: Some(winit::event::VirtualKeyCode::Space), + modifiers: winit::event::ModifiersState::empty(), + }, + device_id: unsafe { winit::event::DeviceId::dummy() }, + is_synthetic: false, + }); + + // Step 3 extra frames + for _ in 0..3 { + example.render(&dst_view, &ctx.device, &ctx.queue, &spawner); + } } } - } - let mut cmd_buf = ctx - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); - - cmd_buf.copy_texture_to_buffer( - wgpu::ImageCopyTexture { - texture: &dst_texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - wgpu::ImageCopyBuffer { - buffer: &dst_buffer, - layout: wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: Some(self.width * 4), - rows_per_image: None, + let mut cmd_buf = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + + cmd_buf.copy_texture_to_buffer( + wgpu::ImageCopyTexture { + texture: &dst_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::ImageCopyBuffer { + buffer: &dst_buffer, + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(params.width * 4), + rows_per_image: None, + }, }, - }, - wgpu::Extent3d { - width: self.width, - height: self.height, - depth_or_array_layers: 1, - }, - ); - - ctx.queue.submit(Some(cmd_buf.finish())); - - let dst_buffer_slice = dst_buffer.slice(..); - dst_buffer_slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.device.poll(wgpu::Maintain::Wait); - let bytes = dst_buffer_slice.get_mapped_range().to_vec(); - - wgpu_test::image::compare_image_output( - env!("CARGO_MANIFEST_DIR").to_string() + "/../../" + self.image_path, - ctx.adapter_info.backend, - self.width, - self.height, - &bytes, - self.comparisons, - ) - .await; - }) + wgpu::Extent3d { + width: params.width, + height: params.height, + depth_or_array_layers: 1, + }, + ); + + ctx.queue.submit(Some(cmd_buf.finish())); + + let dst_buffer_slice = dst_buffer.slice(..); + dst_buffer_slice.map_async(wgpu::MapMode::Read, |_| ()); + ctx.device.poll(wgpu::Maintain::Wait); + let bytes = dst_buffer_slice.get_mapped_range().to_vec(); + + wgpu_test::image::compare_image_output( + env!("CARGO_MANIFEST_DIR").to_string() + "/../../" + params.image_path, + ctx.adapter_info.backend, + params.width, + params.height, + &bytes, + params.comparisons, + ) + .await; + }) } } diff --git a/examples/conservative-raster/src/main.rs b/examples/conservative-raster/src/main.rs index 31db12434a..8fc83fe303 100644 --- a/examples/conservative-raster/src/main.rs +++ b/examples/conservative-raster/src/main.rs @@ -313,26 +313,19 @@ fn main() { wgpu_example::framework::run::("conservative-raster"); } -// Test example #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use std::marker::PhantomData; +#[wgpu_test::gpu_test] +static TEST: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "conservative-raster", + image_path: "/examples/conservative-raster/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], + _phantom: std::marker::PhantomData::, + }; - use wgpu_test::infra::GpuTest; - - wgpu_test::infra::main( - [GpuTest::from_value( - wgpu_example::framework::ExampleTestParams { - name: "conservative-raster", - image_path: "/examples/conservative-raster/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], - _phantom: PhantomData::, - }, - )], - [], - ) -} +#[cfg(test)] +wgpu_test::gpu_test_main!(); diff --git a/examples/cube/src/main.rs b/examples/cube/src/main.rs index e4c43eca68..8d72b3adc1 100644 --- a/examples/cube/src/main.rs +++ b/examples/cube/src/main.rs @@ -408,47 +408,44 @@ fn main() { wgpu_example::framework::run::("cube"); } -// Test example #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use std::marker::PhantomData; - - use wgpu_test::infra::GpuTest; +#[wgpu_test::gpu_test] +static TEST: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "cube", + // Generated on 1080ti on Vk/Windows + image_path: "/examples/cube/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[ + wgpu_test::ComparisonType::Mean(0.04), // Bounded by Intel 630 on Vk/Windows + ], + _phantom: std::marker::PhantomData::, + }; - wgpu_test::infra::main( - [ - GpuTest::from_value(wgpu_example::framework::ExampleTestParams { - name: "cube", - // Generated on 1080ti on Vk/Windows - image_path: "/examples/cube/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[ - wgpu_test::ComparisonType::Mean(0.04), // Bounded by Intel 630 on Vk/Windows - ], - _phantom: PhantomData::, - }), - GpuTest::from_value(wgpu_example::framework::ExampleTestParams { - name: "cube-lines", - // Generated on 1080ti on Vk/Windows - image_path: "/examples/cube/screenshot-lines.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::POLYGON_MODE_LINE, - base_test_parameters: wgpu_test::TestParameters::default(), - // We're looking for tiny changes here, so we focus on a spike in the 95th percentile. - comparisons: &[ - wgpu_test::ComparisonType::Mean(0.05), // Bounded by Intel 630 on Vk/Windows - wgpu_test::ComparisonType::Percentile { - percentile: 0.95, - threshold: 0.36, - }, // Bounded by 1080ti on DX12 - ], - _phantom: PhantomData::, - }), +#[cfg(test)] +#[wgpu_test::gpu_test] +static TEST_LINES: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "cube-lines", + // Generated on 1080ti on Vk/Windows + image_path: "/examples/cube/screenshot-lines.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::POLYGON_MODE_LINE, + base_test_parameters: wgpu_test::TestParameters::default(), + // We're looking for tiny changes here, so we focus on a spike in the 95th percentile. + comparisons: &[ + wgpu_test::ComparisonType::Mean(0.05), // Bounded by Intel 630 on Vk/Windows + wgpu_test::ComparisonType::Percentile { + percentile: 0.95, + threshold: 0.36, + }, // Bounded by 1080ti on DX12 ], - [], - ) -} + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +wgpu_test::gpu_test_main!(); diff --git a/examples/hello-compute/Cargo.toml b/examples/hello-compute/Cargo.toml index cbc8fb661d..b9a4857754 100644 --- a/examples/hello-compute/Cargo.toml +++ b/examples/hello-compute/Cargo.toml @@ -14,7 +14,7 @@ harness = false [dependencies] bytemuck.workspace = true env_logger.workspace = true -futures-intrusive.workspace = true +flume.workspace = true pollster.workspace = true wgpu.workspace = true winit.workspace = true @@ -28,4 +28,3 @@ wasm-bindgen-futures.workspace = true [dev-dependencies] wasm-bindgen-test.workspace = true wgpu-test.workspace = true - diff --git a/examples/hello-compute/src/main.rs b/examples/hello-compute/src/main.rs index 7c91c7f484..3db9d02c33 100644 --- a/examples/hello-compute/src/main.rs +++ b/examples/hello-compute/src/main.rs @@ -150,7 +150,7 @@ async fn execute_gpu_inner( // Note that we're not calling `.await` here. let buffer_slice = staging_buffer.slice(..); // Sets the buffer up for mapping, sending over the result of the mapping back to us when it is finished. - let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel(); + let (sender, receiver) = flume::bounded(1); buffer_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap()); // Poll the device in a blocking manner so that our future resolves. @@ -159,7 +159,7 @@ async fn execute_gpu_inner( device.poll(wgpu::Maintain::Wait); // Awaits until `buffer_future` can be read from - if let Some(Ok(())) = receiver.receive().await { + if let Ok(Ok(())) = receiver.recv_async().await { // Gets contents of buffer let data = buffer_slice.get_mapped_range(); // Since contents are got in bytes, this converts these bytes back to u32 @@ -200,16 +200,5 @@ fn main() { mod tests; #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use wgpu_test::infra::GpuTest; - - wgpu_test::infra::main( - [ - tests::Compute1Test::new(), - tests::Compute2Test::new(), - tests::ComputeOverflowTest::new(), - tests::MultithreadedComputeTest::new(), - ], - [], - ) -} +wgpu_test::gpu_test_main!(); + diff --git a/examples/hello-compute/src/tests.rs b/examples/hello-compute/src/tests.rs index 335384f7f9..acd9e7b67a 100644 --- a/examples/hello-compute/src/tests.rs +++ b/examples/hello-compute/src/tests.rs @@ -1,90 +1,67 @@ use std::sync::Arc; use super::*; -use wgpu_test::{infra::GpuTest, TestParameters}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters}; -#[derive(Default)] -pub struct Compute1Test; - -impl GpuTest for Compute1Test { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static COMPUTE_1: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) .limits(wgpu::Limits::downlevel_defaults()) - .specific_failure(None, None, Some("V3D"), true) - } - - fn run(&self, ctx: wgpu_test::TestingContext) { + .specific_failure(None, None, Some("V3D"), true), + ) + .run_async(|ctx| { let input = &[1, 2, 3, 4]; - pollster::block_on(assert_execute_gpu( - &ctx.device, - &ctx.queue, - input, - &[0, 1, 7, 2], - )); - } -} - -#[derive(Default)] -pub struct Compute2Test; + async move { assert_execute_gpu(&ctx.device, &ctx.queue, input, &[0, 1, 7, 2]).await } + }); -impl GpuTest for Compute2Test { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static COMPUTE_2: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) .limits(wgpu::Limits::downlevel_defaults()) - .specific_failure(None, None, Some("V3D"), true) - } - - fn run(&self, ctx: wgpu_test::TestingContext) { + .specific_failure(None, None, Some("V3D"), true), + ) + .run_async(|ctx| { let input = &[5, 23, 10, 9]; - pollster::block_on(assert_execute_gpu( - &ctx.device, - &ctx.queue, - input, - &[5, 15, 6, 19], - )); - } -} - -#[derive(Default)] -pub struct ComputeOverflowTest; + async move { assert_execute_gpu(&ctx.device, &ctx.queue, input, &[5, 15, 6, 19]).await } + }); -impl GpuTest for ComputeOverflowTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static COMPUTE_OVERFLOW: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) .limits(wgpu::Limits::downlevel_defaults()) - .specific_failure(None, None, Some("V3D"), true) - } - - fn run(&self, ctx: wgpu_test::TestingContext) { + .specific_failure(None, None, Some("V3D"), true), + ) + .run_async(|ctx| { let input = &[77031, 837799, 8400511, 63728127]; - pollster::block_on(assert_execute_gpu( - &ctx.device, - &ctx.queue, - input, - &[350, 524, OVERFLOW, OVERFLOW], - )); - } -} - -#[derive(Default)] -pub struct MultithreadedComputeTest; + async move { + assert_execute_gpu( + &ctx.device, + &ctx.queue, + input, + &[350, 524, OVERFLOW, OVERFLOW], + ).await + } + }); -impl GpuTest for MultithreadedComputeTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static MULTITHREADED_COMPUTE: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) .limits(wgpu::Limits::downlevel_defaults()) .specific_failure(None, None, Some("V3D"), true) // https://github.com/gfx-rs/wgpu/issues/3250 - .specific_failure(Some(wgpu::Backends::GL), None, Some("llvmpipe"), true) - } - - fn run(&self, ctx: wgpu_test::TestingContext) { + .specific_failure(Some(wgpu::Backends::GL), None, Some("llvmpipe"), true), + ) + .run_sync(|ctx| { use std::{sync::mpsc, thread, time::Duration}; let ctx = Arc::new(ctx); @@ -111,8 +88,7 @@ impl GpuTest for MultithreadedComputeTest { rx.recv_timeout(Duration::from_secs(10)) .expect("A thread never completed."); } - } -} + }); async fn assert_execute_gpu( device: &wgpu::Device, diff --git a/examples/mipmap/src/main.rs b/examples/mipmap/src/main.rs index 2c098650fb..327d8ba720 100644 --- a/examples/mipmap/src/main.rs +++ b/examples/mipmap/src/main.rs @@ -486,27 +486,20 @@ fn main() { wgpu_example::framework::run::("mipmap"); } -// Test example #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use std::marker::PhantomData; - - use wgpu_test::infra::GpuTest; - - wgpu_test::infra::main( - [GpuTest::from_value( - wgpu_example::framework::ExampleTestParams { - name: "mipmap", - image_path: "/examples/mipmap/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default() - .backend_failure(wgpu::Backends::GL), - comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], - _phantom: PhantomData::, - }, - )], - [], - ) -} +#[wgpu_test::gpu_test] +static TEST: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "mipmap", + image_path: "/examples/mipmap/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default() + .backend_failure(wgpu::Backends::GL), + comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +wgpu_test::gpu_test_main!(); diff --git a/examples/msaa-line/src/main.rs b/examples/msaa-line/src/main.rs index d10f080050..c410b92394 100644 --- a/examples/msaa-line/src/main.rs +++ b/examples/msaa-line/src/main.rs @@ -313,42 +313,29 @@ fn main() { wgpu_example::framework::run::("msaa-line"); } -// Test example #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use std::marker::PhantomData; - - use wgpu_test::infra::GpuTest; - - wgpu_test::infra::main( - [GpuTest::from_value( - wgpu_example::framework::ExampleTestParams { - name: "msaa-line", - image_path: "/examples/msaa-line/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES, - base_test_parameters: wgpu_test::TestParameters::default() - // AMD seems to render nothing on DX12 https://github.com/gfx-rs/wgpu/issues/3838 - .specific_failure(Some(wgpu::Backends::DX12), Some(0x1002), None, false), - // There's a lot of natural variance so we check the weighted median too to differentiate - // real failures from variance. - comparisons: &[ - wgpu_test::ComparisonType::Mean(0.065), - wgpu_test::ComparisonType::Percentile { - percentile: 0.5, - threshold: 0.29, - }, - ], - _phantom: PhantomData::, +#[wgpu_test::gpu_test] +static TEST: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "msaa-line", + image_path: "/examples/msaa-line/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES, + base_test_parameters: wgpu_test::TestParameters::default() + // AMD seems to render nothing on DX12 https://github.com/gfx-rs/wgpu/issues/3838 + .specific_failure(Some(wgpu::Backends::DX12), Some(0x1002), None, false), + // There's a lot of natural variance so we check the weighted median too to differentiate + // real failures from variance. + comparisons: &[ + wgpu_test::ComparisonType::Mean(0.065), + wgpu_test::ComparisonType::Percentile { + percentile: 0.5, + threshold: 0.29, }, - )], - [], - ) -} + ], + _phantom: std::marker::PhantomData::, + }; -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn msaa_line() { - wgpu_example::framework::test::(wgpu_example::framework::ExampleTestParams {}); -} +#[cfg(test)] +wgpu_test::gpu_test_main!(); diff --git a/examples/shadow/src/main.rs b/examples/shadow/src/main.rs index 2dcfaa3d6b..d7a54d0828 100644 --- a/examples/shadow/src/main.rs +++ b/examples/shadow/src/main.rs @@ -841,31 +841,24 @@ fn main() { wgpu_example::framework::run::("shadow"); } -// Test example #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use std::marker::PhantomData; - - use wgpu_test::infra::GpuTest; - - wgpu_test::infra::main( - [GpuTest::from_value( - wgpu_example::framework::ExampleTestParams { - name: "shadow", - image_path: "/examples/shadow/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default() - .downlevel_flags(wgpu::DownlevelFlags::COMPARISON_SAMPLERS) - // rpi4 on VK doesn't work: https://gitlab.freedesktop.org/mesa/mesa/-/issues/3916 - .specific_failure(Some(wgpu::Backends::VULKAN), None, Some("V3D"), false) - // llvmpipe versions in CI are flaky: https://github.com/gfx-rs/wgpu/issues/2594 - .specific_failure(Some(wgpu::Backends::VULKAN), None, Some("llvmpipe"), true), - comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], - _phantom: PhantomData::, - }, - )], - [], - ) -} +#[wgpu_test::gpu_test] +static TEST: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "shadow", + image_path: "/examples/shadow/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default() + .downlevel_flags(wgpu::DownlevelFlags::COMPARISON_SAMPLERS) + // rpi4 on VK doesn't work: https://gitlab.freedesktop.org/mesa/mesa/-/issues/3916 + .specific_failure(Some(wgpu::Backends::VULKAN), None, Some("V3D"), false) + // llvmpipe versions in CI are flaky: https://github.com/gfx-rs/wgpu/issues/2594 + .specific_failure(Some(wgpu::Backends::VULKAN), None, Some("llvmpipe"), true), + comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +wgpu_test::gpu_test_main!(); diff --git a/examples/skybox/src/main.rs b/examples/skybox/src/main.rs index 7addca0ce4..fc7c07ab3d 100644 --- a/examples/skybox/src/main.rs +++ b/examples/skybox/src/main.rs @@ -464,61 +464,66 @@ fn main() { wgpu_example::framework::run::("skybox"); } -// Test example #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use std::marker::PhantomData; - - use wgpu_test::infra::GpuTest; - - wgpu_test::infra::main( - [ - GpuTest::from_value(wgpu_example::framework::ExampleTestParams { - name: "skybox", - image_path: "/examples/skybox/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default().specific_failure( - Some(wgpu::Backends::GL), - None, - Some("ANGLE"), - false, - ), - comparisons: &[wgpu_test::ComparisonType::Mean(0.015)], - _phantom: PhantomData::, - }), - GpuTest::from_value(wgpu_example::framework::ExampleTestParams { - name: "skybox-bc1", - image_path: "/examples/skybox/screenshot-bc1.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::TEXTURE_COMPRESSION_BC, - base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], - _phantom: PhantomData::, - }), - GpuTest::from_value(wgpu_example::framework::ExampleTestParams { - name: "skybox-etc2", - image_path: "/examples/skybox/screenshot-etc2.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::TEXTURE_COMPRESSION_ETC2, - base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - comparisons: &[wgpu_test::ComparisonType::Mean(0.015)], - _phantom: PhantomData::, - }), - GpuTest::from_value(wgpu_example::framework::ExampleTestParams { - name: "skybox-astc", - image_path: "/examples/skybox/screenshot-astc.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::TEXTURE_COMPRESSION_ASTC, - base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - comparisons: &[wgpu_test::ComparisonType::Mean(0.016)], - _phantom: PhantomData::, - }), - ], - [], - ) -} +#[wgpu_test::gpu_test] +static TEST: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "skybox", + image_path: "/examples/skybox/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default().specific_failure( + Some(wgpu::Backends::GL), + None, + Some("ANGLE"), + false, + ), + comparisons: &[wgpu_test::ComparisonType::Mean(0.015)], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +#[wgpu_test::gpu_test] +static TEST_BCN: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "skybox-bc1", + image_path: "/examples/skybox/screenshot-bc1.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::TEXTURE_COMPRESSION_BC, + base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 + comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +#[wgpu_test::gpu_test] +static TEST_ETC2: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "skybox-etc2", + image_path: "/examples/skybox/screenshot-etc2.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::TEXTURE_COMPRESSION_ETC2, + base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 + comparisons: &[wgpu_test::ComparisonType::Mean(0.015)], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +#[wgpu_test::gpu_test] +static TEST_ASTC: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "skybox-astc", + image_path: "/examples/skybox/screenshot-astc.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::TEXTURE_COMPRESSION_ASTC, + base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 + comparisons: &[wgpu_test::ComparisonType::Mean(0.016)], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +wgpu_test::gpu_test_main!(); diff --git a/examples/stencil-triangles/src/main.rs b/examples/stencil-triangles/src/main.rs index 30b8c72503..2b44aa92af 100644 --- a/examples/stencil-triangles/src/main.rs +++ b/examples/stencil-triangles/src/main.rs @@ -233,26 +233,19 @@ fn main() { wgpu_example::framework::run::("stencil-triangles"); } -// Test example #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use std::marker::PhantomData; +#[wgpu_test::gpu_test] +static TEST: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "stencil-triangles", + image_path: "/examples/stencil-triangles/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[wgpu_test::ComparisonType::Mean(0.03)], + _phantom: std::marker::PhantomData::, + }; - use wgpu_test::infra::GpuTest; - - wgpu_test::infra::main( - [GpuTest::from_value( - wgpu_example::framework::ExampleTestParams { - name: "stencil-triangles", - image_path: "/examples/stencil-triangles/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[wgpu_test::ComparisonType::Mean(0.03)], - _phantom: PhantomData::, - }, - )], - [], - ) -} +#[cfg(test)] +wgpu_test::gpu_test_main!(); diff --git a/examples/texture-arrays/src/main.rs b/examples/texture-arrays/src/main.rs index b55ae5bb0c..a8c539bc0e 100644 --- a/examples/texture-arrays/src/main.rs +++ b/examples/texture-arrays/src/main.rs @@ -409,47 +409,48 @@ fn main() { wgpu_example::framework::run::("texture-arrays"); } -// Test example #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use std::marker::PhantomData; - - use wgpu_test::infra::GpuTest; - - wgpu_test::infra::main( - [ - GpuTest::from_value(wgpu_example::framework::ExampleTestParams { - name: "texture-arrays", - image_path: "/examples/texture-arrays/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::empty(), - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], - _phantom: PhantomData::, - }), - GpuTest::from_value(wgpu_example::framework::ExampleTestParams { - name: "texture-arrays-uniform", - image_path: "/examples/texture-arrays/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::empty(), - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], - _phantom: PhantomData::, - }), - GpuTest::from_value(wgpu_example::framework::ExampleTestParams { - name: "texture-arrays-non-uniform", - image_path: "/examples/texture-arrays/screenshot.png", - width: 1024, - height: 768, - optional_features: - wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], - _phantom: PhantomData::, - }), - ], - [], - ) -} +#[wgpu_test::gpu_test] +static TEST: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "texture-arrays", + image_path: "/examples/texture-arrays/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::empty(), + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +#[wgpu_test::gpu_test] +static TEST_UNIFORM: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "texture-arrays-uniform", + image_path: "/examples/texture-arrays/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::empty(), + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +#[wgpu_test::gpu_test] +static TEST_NON_UNIFORM: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "texture-arrays-non-uniform", + image_path: "/examples/texture-arrays/screenshot.png", + width: 1024, + height: 768, + optional_features: + wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +wgpu_test::gpu_test_main!(); diff --git a/examples/water/src/main.rs b/examples/water/src/main.rs index 42ce1ea00c..cbdb43b215 100644 --- a/examples/water/src/main.rs +++ b/examples/water/src/main.rs @@ -818,27 +818,20 @@ fn main() { wgpu_example::framework::run::("water"); } -// Test example #[cfg(test)] -fn main() -> wgpu_test::infra::MainResult { - use std::marker::PhantomData; - - use wgpu_test::infra::GpuTest; - - wgpu_test::infra::main( - [GpuTest::from_value( - wgpu_example::framework::ExampleTestParams { - name: "water", - image_path: "/examples/water/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: wgpu_test::TestParameters::default() - .downlevel_flags(wgpu::DownlevelFlags::READ_ONLY_DEPTH_STENCIL), - comparisons: &[wgpu_test::ComparisonType::Mean(0.01)], - _phantom: PhantomData::, - }, - )], - [], - ) -} +#[wgpu_test::gpu_test] +static TEST: wgpu_example::framework::ExampleTestParams = + wgpu_example::framework::ExampleTestParams { + name: "water", + image_path: "/examples/water/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default() + .downlevel_flags(wgpu::DownlevelFlags::READ_ONLY_DEPTH_STENCIL), + comparisons: &[wgpu_test::ComparisonType::Mean(0.01)], + _phantom: std::marker::PhantomData::, + }; + +#[cfg(test)] +wgpu_test::gpu_test_main!(); diff --git a/tests/Cargo.toml b/tests/Cargo.toml index dc804e9eda..97a81c8f9b 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -12,10 +12,14 @@ autotests = false publish = false [[test]] -name = "wgpu-tests" -path = "tests/root.rs" +name = "gpu-tests" +path = "tests/gpu.rs" harness = false +[[test]] +name = "cpu-tests" +path = "tests/cpu.rs" + [features] webgl = ["wgpu/webgl"] @@ -24,16 +28,19 @@ anyhow.workspace = true arrayvec.workspace = true bitflags.workspace = true cfg-if.workspace = true +ctor.workspace = true env_logger.workspace = true futures-lite.workspace = true heck.workspace = true libtest-mimic.workspace = true log.workspace = true +parking_lot.workspace = true png.workspace = true pollster.workspace = true serde_json.workspace = true serde.workspace = true wgpu.workspace = true +wgpu-macros.workspace = true wgt = { workspace = true, features = ["replay"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/tests/src/infra/mod.rs b/tests/src/infra/mod.rs index e0524ceb84..843e10f7cc 100644 --- a/tests/src/infra/mod.rs +++ b/tests/src/infra/mod.rs @@ -1,48 +1,40 @@ -use std::sync::Arc; - use anyhow::Context; -pub use params::{cpu_test, GpuTest}; +pub use params::{GpuTestConfiguration, RunTestAsync}; +use parking_lot::Mutex; -use crate::infra::{params::CpuTest, single::SingleTest}; +use crate::infra::single::SingleTest; mod params; mod report; mod single; +pub static TEST_LIST: Mutex> = Mutex::new(Vec::new()); + pub type MainResult = anyhow::Result<()>; -pub fn main( - gpu_test_list: [Arc; GPU_TEST_COUNT], - cpu_test_list: [CpuTest; CPU_TEST_COUNT], -) -> MainResult { +pub fn main() -> MainResult { let config_text = &std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) .context("Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?")?; let report = report::GpuReport::from_json(config_text).context("Could not pare .gpuconfig JSON")?; - // Gpu tests - let tests = gpu_test_list - .into_iter() - .flat_map(|test| { - report - .devices - .iter() - .enumerate() - .map(move |(adapter_index, adapter)| { - SingleTest::from_gpu_test(test.clone(), adapter, adapter_index) - }) - }); - // Cpu tests - let tests = tests.chain(cpu_test_list.into_iter().map(SingleTest::from_cpu_test)); - - execute_native(tests); + let mut test_guard = TEST_LIST.lock(); + execute_native(test_guard.drain(..).flat_map(|test| { + report + .devices + .iter() + .enumerate() + .map(move |(adapter_index, adapter)| { + SingleTest::from_gpu_test(test.clone(), adapter, adapter_index) + }) + })); Ok(()) } -fn execute_native(tests: impl IntoIterator) { +fn execute_native<'a>(tests: impl IntoIterator) { let args = libtest_mimic::Arguments::from_args(); let trials = tests.into_iter().map(SingleTest::into_trial).collect(); diff --git a/tests/src/infra/params.rs b/tests/src/infra/params.rs index 48413f4002..a9313fa27e 100644 --- a/tests/src/infra/params.rs +++ b/tests/src/infra/params.rs @@ -1,73 +1,59 @@ use std::{future::Future, pin::Pin, sync::Arc}; -use heck::ToSnakeCase; - use crate::{TestParameters, TestingContext}; -pub trait GpuTest: Send + Sync + 'static { - #[allow(clippy::new_ret_no_self)] - fn new() -> Arc - where - Self: Sized + Default, - { - Self::from_value(Self::default()) - } +pub type RunTestAsync = + Arc Pin + Send + Sync>> + Send + Sync>; - fn from_value(value: Self) -> Arc - where - Self: Sized, - { - Arc::new(value) - } +#[derive(Clone)] +pub struct GpuTestConfiguration { + pub name: &'static str, + pub params: TestParameters, + pub test: Option, +} - fn name(&self) -> String { - let name = std::any::type_name::(); - let (path, type_name) = name.rsplit_once("::").unwrap(); - let snake_case = type_name.to_snake_case(); - let snake_case_trimmed = snake_case.trim_end_matches("_test"); - assert_ne!( - &snake_case, snake_case_trimmed, - "Type name of the test must end with \"Test\"" - ); - format!("{path}::{snake_case_trimmed}") +impl GpuTestConfiguration { + pub fn new() -> Self { + Self { + name: "", + params: TestParameters::default(), + test: None, + } } - fn parameters(&self, params: TestParameters) -> TestParameters { - params + pub fn name(self, name: &'static str) -> Self { + Self { name, ..self } } - fn run(&self, ctx: TestingContext); - - fn run_async<'a>( - &'a self, - ctx: TestingContext, - ) -> Pin + Send + Sync + 'a>> { - Box::pin(async move { self.run(ctx) }) + pub fn name_if_not_set(self, name: &'static str) -> Self { + if self.name != "" { + return self; + } + Self { name, ..self } } -} - -pub struct CpuTest { - name: &'static str, - test: Box, -} -impl CpuTest { - pub fn name(&self) -> &'static str { - self.name + pub fn parameters(self, parameters: TestParameters) -> Self { + Self { + params: parameters, + ..self + } } - pub fn call(self) { - (self.test)(); + pub fn run_sync(self, test: impl Fn(TestingContext) + Copy + Send + Sync + 'static) -> Self { + Self { + test: Some(Arc::new(move |ctx| Box::pin(async move { test(ctx) }))), + ..self + } } -} -// This needs to be generic, otherwise we will get the generic type name of `fn() -> ()`. -pub fn cpu_test(test: T) -> CpuTest -where - T: FnOnce() + Send + Sync + 'static, -{ - CpuTest { - name: std::any::type_name::(), - test: Box::new(test), + pub fn run_async(self, test: F) -> Self + where + F: Fn(TestingContext) -> R + Send + Sync + 'static, + R: Future + Send + Sync + 'static, + { + Self { + test: Some(Arc::new(move |ctx| Box::pin(test(ctx)))), + ..self + } } } diff --git a/tests/src/infra/single.rs b/tests/src/infra/single.rs index bc3282c54a..5d7c554812 100644 --- a/tests/src/infra/single.rs +++ b/tests/src/infra/single.rs @@ -1,10 +1,10 @@ -use std::{future::Future, pin::Pin, sync::Arc}; +use std::{future::Future, pin::Pin}; use arrayvec::ArrayVec; use crate::{ - infra::{params::CpuTest, report::AdapterReport, GpuTest}, - initialize_test, TestParameters, + infra::{report::AdapterReport, GpuTestConfiguration}, + initialize_test, }; pub(super) struct SingleTest { @@ -13,25 +13,14 @@ pub(super) struct SingleTest { } impl SingleTest { - pub fn from_cpu_test(cpu_test: CpuTest) -> Self { - Self { - name: cpu_test.name().to_owned(), - future: Box::pin(async move { - cpu_test.call(); - Ok(()) - }), - } - } - pub fn from_gpu_test( - test: Arc, + test: GpuTestConfiguration, adapter: &AdapterReport, adapter_index: usize, ) -> Self { - let params = TestParameters::default(); - let params = test.parameters(params); + let params = test.params; - let base_name = test.name(); + let base_name = test.name; let backend = &adapter.info.backend; let device_name = &adapter.info.name; @@ -89,7 +78,7 @@ impl SingleTest { params, expected_failure.map(|(reasons, _)| reasons), adapter_index, - &*test, + test.test.expect("Test must be specified"), ) .await; Ok(()) diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 8774eb910a..609981d776 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -11,9 +11,11 @@ pub mod image; pub mod infra; mod isolation; -use crate::infra::GpuTest; +use crate::infra::RunTestAsync; pub use self::image::ComparisonType; +pub use ctor::ctor; +pub use wgpu_macros::gpu_test; const CANVAS_ID: &str = "test-canvas"; @@ -241,7 +243,7 @@ pub async fn initialize_test( parameters: TestParameters, expected_failure_reason: Option, adapter_index: usize, - test: &dyn GpuTest, + test: RunTestAsync, ) { // We don't actually care if it fails #[cfg(not(target_arch = "wasm32"))] @@ -268,11 +270,11 @@ pub async fn initialize_test( adapter_downlevel_capabilities, device, device_features: parameters.required_features, - device_limits: parameters.required_limits, + device_limits: parameters.required_limits.clone(), queue, }; - let panicked = AssertUnwindSafe(test.run_async(context)) + let panicked = AssertUnwindSafe(test(context)) .catch_unwind() .await .is_err(); @@ -474,3 +476,12 @@ pub fn fail_if(device: &wgpu::Device, should_fail: bool, callback: impl FnOnc valid(device, callback) } } + +#[macro_export] +macro_rules! gpu_test_main { + () => { + fn main() -> $crate::infra::MainResult { + $crate::infra::main() + } + }; +} diff --git a/tests/tests/buffer.rs b/tests/tests/buffer.rs index 030430cb30..2634bb16a9 100644 --- a/tests/tests/buffer.rs +++ b/tests/tests/buffer.rs @@ -1,4 +1,4 @@ -use wgpu_test::{infra::GpuTest, TestingContext}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters, TestingContext}; fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str) { let r = wgpu::BufferUsages::MAP_READ; @@ -80,88 +80,77 @@ fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str) ctx.device.poll(wgpu::MaintainBase::Wait); } -#[derive(Default)] -pub struct EmptyBufferTest; - -impl GpuTest for EmptyBufferTest { - fn parameters(&self, params: wgpu_test::TestParameters) -> wgpu_test::TestParameters { - // wgpu doesn't support zero sized buffers - params.failure() - } - - fn run(&self, ctx: TestingContext) { +#[gpu_test] +static EMPTY_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().failure()) + .run_sync(|ctx| { test_empty_buffer_range(&ctx, 2048, "regular buffer"); test_empty_buffer_range(&ctx, 0, "zero-sized buffer"); - } -} + }); -#[derive(Default)] -pub struct MapOffsetTest; - -impl GpuTest for MapOffsetTest { - fn run(&self, ctx: TestingContext) { - // This test writes 16 bytes at the beginning of buffer mapped mapped with - // an offset of 32 bytes. Then the buffer is copied into another buffer that - // is read back and we check that the written bytes are correctly placed at - // offset 32..48. - // The goal is to check that get_mapped_range did not accidentally double-count - // the mapped offset. - - let write_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: 256, - usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC, - mapped_at_creation: false, - }); - let read_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: 256, - usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); +#[gpu_test] +static MAP_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + // This test writes 16 bytes at the beginning of buffer mapped mapped with + // an offset of 32 bytes. Then the buffer is copied into another buffer that + // is read back and we check that the written bytes are correctly placed at + // offset 32..48. + // The goal is to check that get_mapped_range did not accidentally double-count + // the mapped offset. + + let write_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 256, + usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: false, + }); + let read_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 256, + usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); - write_buf - .slice(32..) - .map_async(wgpu::MapMode::Write, move |result| { - result.unwrap(); - }); + write_buf + .slice(32..) + .map_async(wgpu::MapMode::Write, move |result| { + result.unwrap(); + }); - ctx.device.poll(wgpu::MaintainBase::Wait); + ctx.device.poll(wgpu::MaintainBase::Wait); - { - let slice = write_buf.slice(32..48); - let mut view = slice.get_mapped_range_mut(); - for byte in &mut view[..] { - *byte = 2; - } + { + let slice = write_buf.slice(32..48); + let mut view = slice.get_mapped_range_mut(); + for byte in &mut view[..] { + *byte = 2; } + } - write_buf.unmap(); + write_buf.unmap(); - let mut encoder = ctx - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - encoder.copy_buffer_to_buffer(&write_buf, 0, &read_buf, 0, 256); + encoder.copy_buffer_to_buffer(&write_buf, 0, &read_buf, 0, 256); - ctx.queue.submit(Some(encoder.finish())); + ctx.queue.submit(Some(encoder.finish())); - read_buf - .slice(..) - .map_async(wgpu::MapMode::Read, Result::unwrap); + read_buf + .slice(..) + .map_async(wgpu::MapMode::Read, Result::unwrap); - ctx.device.poll(wgpu::MaintainBase::Wait); + ctx.device.poll(wgpu::MaintainBase::Wait); - let slice = read_buf.slice(..); - let view = slice.get_mapped_range(); - for byte in &view[0..32] { - assert_eq!(*byte, 0); - } - for byte in &view[32..48] { - assert_eq!(*byte, 2); - } - for byte in &view[48..] { - assert_eq!(*byte, 0); - } + let slice = read_buf.slice(..); + let view = slice.get_mapped_range(); + for byte in &view[0..32] { + assert_eq!(*byte, 0); } -} + for byte in &view[32..48] { + assert_eq!(*byte, 2); + } + for byte in &view[48..] { + assert_eq!(*byte, 0); + } +}); diff --git a/tests/tests/buffer_copy.rs b/tests/tests/buffer_copy.rs index a7975d4b87..d8fd490336 100644 --- a/tests/tests/buffer_copy.rs +++ b/tests/tests/buffer_copy.rs @@ -2,7 +2,7 @@ use wgt::BufferAddress; -use wgpu_test::{fail_if, infra::GpuTest}; +use wgpu_test::{fail_if, gpu_test, infra::GpuTestConfiguration}; fn try_copy( ctx: &wgpu_test::TestingContext, @@ -17,24 +17,20 @@ fn try_copy( }); } -#[derive(Default)] -pub struct CopyAlignmentTest; - -impl GpuTest for CopyAlignmentTest { - fn run(&self, ctx: wgpu_test::TestingContext) { - try_copy(&ctx, 0, 0, false); - try_copy(&ctx, 4, 16 + 1, true); - try_copy(&ctx, 64, 20 + 2, true); - try_copy(&ctx, 256, 44 + 3, true); - try_copy(&ctx, 1024, 8 + 4, false); - - try_copy(&ctx, 0, 4, false); - try_copy(&ctx, 4 + 1, 8, true); - try_copy(&ctx, 64 + 2, 12, true); - try_copy(&ctx, 256 + 3, 16, true); - try_copy(&ctx, 1024 + 4, 4, false); - } -} +#[gpu_test] +static COPY_ALIGNMENT: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + try_copy(&ctx, 0, 0, false); + try_copy(&ctx, 4, 16 + 1, true); + try_copy(&ctx, 64, 20 + 2, true); + try_copy(&ctx, 256, 44 + 3, true); + try_copy(&ctx, 1024, 8 + 4, false); + + try_copy(&ctx, 0, 4, false); + try_copy(&ctx, 4 + 1, 8, true); + try_copy(&ctx, 64 + 2, 12, true); + try_copy(&ctx, 256 + 3, 16, true); + try_copy(&ctx, 1024 + 4, 4, false); +}); const BUFFER_SIZE: BufferAddress = 1234; diff --git a/tests/tests/buffer_usages.rs b/tests/tests/buffer_usages.rs index 0cde12331e..2f58c1f108 100644 --- a/tests/tests/buffer_usages.rs +++ b/tests/tests/buffer_usages.rs @@ -1,7 +1,7 @@ //! Tests for buffer usages validation. use wgpu::BufferUsages as Bu; -use wgpu_test::{fail_if, infra::GpuTest, TestParameters}; +use wgpu_test::{fail_if, gpu_test, infra::GpuTestConfiguration, TestParameters}; use wgt::BufferAddress; const BUFFER_SIZE: BufferAddress = 1234; @@ -42,31 +42,22 @@ fn try_create(ctx: wgpu_test::TestingContext, usages: &[(bool, &[wgpu::BufferUsa } } -#[derive(Default)] -pub struct BufferUsageTest; +#[gpu_test] +static BUFFER_USAGE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + try_create( + ctx, + &[ + (false, ALWAYS_VALID), + (true, NEEDS_MAPPABLE_PRIMARY_BUFFERS), + (true, ALWAYS_FAIL), + ], + ); +}); -impl GpuTest for BufferUsageTest { - fn run(&self, ctx: wgpu_test::TestingContext) { - try_create( - ctx, - &[ - (false, ALWAYS_VALID), - (true, NEEDS_MAPPABLE_PRIMARY_BUFFERS), - (true, ALWAYS_FAIL), - ], - ); - } -} - -#[derive(Default)] -pub struct BufferUsageMappablePrimaryTest; - -impl GpuTest for BufferUsageMappablePrimaryTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params.features(wgt::Features::MAPPABLE_PRIMARY_BUFFERS) - } - - fn run(&self, ctx: wgpu_test::TestingContext) { +#[gpu_test] +static BUFFER_USAGE_MAPPABLE_PRIMARY_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().features(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS)) + .run_sync(|ctx| { try_create( ctx, &[ @@ -75,5 +66,4 @@ impl GpuTest for BufferUsageMappablePrimaryTest { (true, ALWAYS_FAIL), ], ); - } -} + }); diff --git a/tests/tests/clear_texture.rs b/tests/tests/clear_texture.rs index 92edbe9f97..8a4b15bee9 100644 --- a/tests/tests/clear_texture.rs +++ b/tests/tests/clear_texture.rs @@ -1,4 +1,6 @@ -use wgpu_test::{image::ReadbackBuffers, infra::GpuTest, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, image::ReadbackBuffers, infra::GpuTestConfiguration, TestParameters, TestingContext, +}; static TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT: &[wgpu::TextureFormat] = &[ wgpu::TextureFormat::R8Unorm, @@ -322,118 +324,91 @@ fn clear_texture_tests(ctx: &TestingContext, formats: &[wgpu::TextureFormat]) { } } -#[derive(Default)] -pub struct ClearTextureUncompressedGlesCompatTest; - -impl GpuTest for ClearTextureUncompressedGlesCompatTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static CLEAR_TEXTURE_UNCOMPRESSED_GLES: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .webgl2_failure() - .features(wgpu::Features::CLEAR_TEXTURE) - } - - fn run(&self, ctx: TestingContext) { + .features(wgpu::Features::CLEAR_TEXTURE), + ) + .run_sync(|ctx| { clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT); - } -} - -#[derive(Default)] -pub struct ClearTextureUncompressedCompatTest; + }); -impl GpuTest for ClearTextureUncompressedCompatTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static CLEAR_TEXTURE_UNCOMPRESSED: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .webgl2_failure() .backend_failure(wgpu::Backends::GL) - .features(wgpu::Features::CLEAR_TEXTURE) - } - - fn run(&self, ctx: TestingContext) { + .features(wgpu::Features::CLEAR_TEXTURE), + ) + .run_sync(|ctx| { clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED); - } -} - -#[derive(Default)] -pub struct ClearTextureDepthCompatTest; + }); -impl GpuTest for ClearTextureDepthCompatTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static CLEAR_TEXTURE_DEPTH: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .webgl2_failure() .downlevel_flags( wgpu::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | wgpu::DownlevelFlags::COMPUTE_SHADERS, ) .limits(wgpu::Limits::downlevel_defaults()) - .features(wgpu::Features::CLEAR_TEXTURE) - } - - fn run(&self, ctx: TestingContext) { + .features(wgpu::Features::CLEAR_TEXTURE), + ) + .run_sync(|ctx| { clear_texture_tests(&ctx, TEXTURE_FORMATS_DEPTH); - } -} - -#[derive(Default)] -pub struct ClearTextureD32S8Test; - -impl GpuTest for ClearTextureD32S8Test { - fn parameters(&self, params: TestParameters) -> TestParameters { - params.features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::DEPTH32FLOAT_STENCIL8) - } + }); - fn run(&self, ctx: TestingContext) { +#[gpu_test] +static CLEAR_TEXTURE_DEPTH32_STENCIL8: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::DEPTH32FLOAT_STENCIL8), + ) + .run_sync(|ctx| { clear_texture_tests(&ctx, &[wgpu::TextureFormat::Depth32FloatStencil8]); - } -} - -#[derive(Default)] -pub struct ClearTextureBcTest; + }); -impl GpuTest for ClearTextureBcTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static CLEAR_TEXTURE_COMPRESSED_BCN: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_BC) .specific_failure(Some(wgpu::Backends::GL), None, Some("ANGLE"), false) // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - .backend_failure(wgpu::Backends::GL) // compressed texture copy to buffer not yet implemented - } - - fn run(&self, ctx: TestingContext) { + .backend_failure(wgpu::Backends::GL), // compressed texture copy to buffer not yet implemented + ) + .run_sync(|ctx| { clear_texture_tests(&ctx, TEXTURE_FORMATS_BC); - } -} - -#[derive(Default)] -pub struct ClearTextureAstcTest; + }); -impl GpuTest for ClearTextureAstcTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static CLEAR_TEXTURE_COMPRESSED_ASTC: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_ASTC) .limits(wgpu::Limits { max_texture_dimension_2d: wgpu::COPY_BYTES_PER_ROW_ALIGNMENT * 12, ..wgpu::Limits::downlevel_defaults() }) .specific_failure(Some(wgpu::Backends::GL), None, Some("ANGLE"), false) // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - .backend_failure(wgpu::Backends::GL) // compressed texture copy to buffer not yet implemented - } - - fn run(&self, ctx: TestingContext) { + .backend_failure(wgpu::Backends::GL), // compressed texture copy to buffer not yet implemented + ) + .run_sync(|ctx| { clear_texture_tests(&ctx, TEXTURE_FORMATS_ASTC); - } -} - -#[derive(Default)] -pub struct ClearTextureEtc2Test; + }); -impl GpuTest for ClearTextureEtc2Test { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static CLEAR_TEXTURE_COMPRESSED_ETC2: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_ETC2) .specific_failure(Some(wgpu::Backends::GL), None, Some("ANGLE"), false) // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056 - .backend_failure(wgpu::Backends::GL) // compressed texture copy to buffer not yet implemented - } - - fn run(&self, ctx: TestingContext) { + .backend_failure(wgpu::Backends::GL), // compressed texture copy to buffer not yet implemented + ) + .run_sync(|ctx| { clear_texture_tests(&ctx, TEXTURE_FORMATS_ETC2); - } -} + }); diff --git a/tests/tests/cpu.rs b/tests/tests/cpu.rs new file mode 100644 index 0000000000..e7858ddb07 --- /dev/null +++ b/tests/tests/cpu.rs @@ -0,0 +1 @@ +mod example_wgsl; diff --git a/tests/tests/encoder.rs b/tests/tests/encoder.rs index 1fdcfbb09b..c91b1a6fec 100644 --- a/tests/tests/encoder.rs +++ b/tests/tests/encoder.rs @@ -1,13 +1,9 @@ -use wgpu_test::{infra::GpuTest, TestingContext}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration}; -#[derive(Default)] -pub struct DropEncoderTest; - -impl GpuTest for DropEncoderTest { - fn run(&self, ctx: TestingContext) { - let encoder = ctx - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); - drop(encoder); - } -} +#[gpu_test] +static DROP_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + let encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + drop(encoder); +}); diff --git a/tests/tests/example_wgsl.rs b/tests/tests/example_wgsl.rs index 689d291db5..10ebd4ad32 100644 --- a/tests/tests/example_wgsl.rs +++ b/tests/tests/example_wgsl.rs @@ -2,6 +2,7 @@ use naga::{front::wgsl, valid::Validator}; use std::{fs, path::PathBuf}; /// Runs through all example shaders and ensures they are valid wgsl. +#[test] pub fn parse_example_wgsl() { let read_dir = match PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("examples") diff --git a/tests/tests/external_texture.rs b/tests/tests/external_texture.rs index 2c3a8b987d..af55427619 100644 --- a/tests/tests/external_texture.rs +++ b/tests/tests/external_texture.rs @@ -3,136 +3,136 @@ use wasm_bindgen::JsCast; use wasm_bindgen_test::*; use wgpu::ExternalImageSource; -use wgpu_test::{fail_if, initialize_test, TestParameters}; +use wgpu_test::{fail_if, gpu_test, infra::GpuTestConfiguration, initialize_test}; -#[wasm_bindgen_test] -async fn image_bitmap_import() { - let image_encoded = include_bytes!("3x3_colors.png"); +#[gpu_test] +static IMAGE_BITMAP_IMPORT: GpuTestConfiguration = + GpuTestConfiguration::new().run_async(|ctx| async move { + let image_encoded = include_bytes!("3x3_colors.png"); - // Create an array-of-arrays for Blob's constructor - let array = js_sys::Array::new(); - array.push(&js_sys::Uint8Array::from(&image_encoded[..])); + // Create an array-of-arrays for Blob's constructor + let array = js_sys::Array::new(); + array.push(&js_sys::Uint8Array::from(&image_encoded[..])); - // We're passing an array of Uint8Arrays - let blob = web_sys::Blob::new_with_u8_array_sequence(&array).unwrap(); + // We're passing an array of Uint8Arrays + let blob = web_sys::Blob::new_with_u8_array_sequence(&array).unwrap(); - // Parse the image from the blob + // Parse the image from the blob - // Because we need to call the function in a way that isn't bound by - // web_sys, we need to manually construct the options struct and call - // the function. - let image_bitmap_function: js_sys::Function = web_sys::window() - .unwrap() - .get("createImageBitmap") - .unwrap() - .dyn_into() - .unwrap(); + // Because we need to call the function in a way that isn't bound by + // web_sys, we need to manually construct the options struct and call + // the function. + let image_bitmap_function: js_sys::Function = web_sys::window() + .unwrap() + .get("createImageBitmap") + .unwrap() + .dyn_into() + .unwrap(); - let options_arg = js_sys::Object::new(); - js_sys::Reflect::set( - &options_arg, - &wasm_bindgen::JsValue::from_str("premultiplyAlpha"), - &wasm_bindgen::JsValue::from_str("none"), - ) - .unwrap(); - let image_bitmap_promise: js_sys::Promise = image_bitmap_function - .call2(&wasm_bindgen::JsValue::UNDEFINED, &blob, &options_arg) - .unwrap() - .dyn_into() + let options_arg = js_sys::Object::new(); + js_sys::Reflect::set( + &options_arg, + &wasm_bindgen::JsValue::from_str("premultiplyAlpha"), + &wasm_bindgen::JsValue::from_str("none"), + ) .unwrap(); - - // Wait for the parsing to be done - let image_bitmap: web_sys::ImageBitmap = - wasm_bindgen_futures::JsFuture::from(image_bitmap_promise) - .await + let image_bitmap_promise: js_sys::Promise = image_bitmap_function + .call2(&wasm_bindgen::JsValue::UNDEFINED, &blob, &options_arg) .unwrap() .dyn_into() .unwrap(); - // Sanity checks - assert_eq!(image_bitmap.width(), 3); - assert_eq!(image_bitmap.height(), 3); + // Wait for the parsing to be done + let image_bitmap: web_sys::ImageBitmap = + wasm_bindgen_futures::JsFuture::from(image_bitmap_promise) + .await + .unwrap() + .dyn_into() + .unwrap(); - // Due to restrictions with premultiplication with ImageBitmaps, we also create an HtmlCanvasElement - // by drawing the image bitmap onto the canvas. - let canvas: web_sys::HtmlCanvasElement = web_sys::window() - .unwrap() - .document() - .unwrap() - .create_element("canvas") - .unwrap() - .dyn_into() - .unwrap(); - canvas.set_width(3); - canvas.set_height(3); + // Sanity checks + assert_eq!(image_bitmap.width(), 3); + assert_eq!(image_bitmap.height(), 3); - let d2_context: web_sys::CanvasRenderingContext2d = canvas - .get_context("2d") - .unwrap() - .unwrap() - .dyn_into() - .unwrap(); - d2_context - .draw_image_with_image_bitmap(&image_bitmap, 0.0, 0.0) - .unwrap(); + // Due to restrictions with premultiplication with ImageBitmaps, we also create an HtmlCanvasElement + // by drawing the image bitmap onto the canvas. + let canvas: web_sys::HtmlCanvasElement = web_sys::window() + .unwrap() + .document() + .unwrap() + .create_element("canvas") + .unwrap() + .dyn_into() + .unwrap(); + canvas.set_width(3); + canvas.set_height(3); - // Decode it cpu side - let raw_image = image::load_from_memory_with_format(image_encoded, image::ImageFormat::Png) - .unwrap() - .into_rgba8(); + let d2_context: web_sys::CanvasRenderingContext2d = canvas + .get_context("2d") + .unwrap() + .unwrap() + .dyn_into() + .unwrap(); + d2_context + .draw_image_with_image_bitmap(&image_bitmap, 0.0, 0.0) + .unwrap(); - // Set of test cases to test with image import - #[derive(Debug, Copy, Clone)] - enum TestCase { - // Import the image as normal - Normal, - // Sets the FlipY flag. Deals with global state on GLES, so run before other tests to ensure it's reset. - // - // Only works on canvases. - FlipY, - // Sets the premultiplied alpha flag. Deals with global state on GLES, so run before other tests to ensure it's reset. - // - // Only works on canvases. - Premultiplied, - // Sets the color space to P3. - // - // Only works on canvases. - ColorSpace, - // Sets the premultiplied alpha flag. Deals with global state on GLES, so run before other tests to ensure it's reset. - // Set both the input offset and output offset to 1 in x, so the first column is omitted. - TrimLeft, - // Set the size to 2 in x, so the last column is omitted - TrimRight, - // Set only the output offset to 1, so the second column gets the first column's data. - SlideRight, - // Try to copy from out of bounds of the source image - SourceOutOfBounds, - // Try to copy from out of bounds of the destination image - DestOutOfBounds, - // Try to copy more than one slice from the source - MultiSliceCopy, - // Copy into the second slice of a 2D array texture, - SecondSliceCopy, - } - let sources = [ - ExternalImageSource::ImageBitmap(image_bitmap), - ExternalImageSource::HTMLCanvasElement(canvas), - ]; - let cases = [ - TestCase::Normal, - TestCase::FlipY, - TestCase::Premultiplied, - TestCase::ColorSpace, - TestCase::TrimLeft, - TestCase::TrimRight, - TestCase::SlideRight, - TestCase::SourceOutOfBounds, - TestCase::DestOutOfBounds, - TestCase::MultiSliceCopy, - TestCase::SecondSliceCopy, - ]; + // Decode it cpu side + let raw_image = image::load_from_memory_with_format(image_encoded, image::ImageFormat::Png) + .unwrap() + .into_rgba8(); + + // Set of test cases to test with image import + #[derive(Debug, Copy, Clone)] + enum TestCase { + // Import the image as normal + Normal, + // Sets the FlipY flag. Deals with global state on GLES, so run before other tests to ensure it's reset. + // + // Only works on canvases. + FlipY, + // Sets the premultiplied alpha flag. Deals with global state on GLES, so run before other tests to ensure it's reset. + // + // Only works on canvases. + Premultiplied, + // Sets the color space to P3. + // + // Only works on canvases. + ColorSpace, + // Sets the premultiplied alpha flag. Deals with global state on GLES, so run before other tests to ensure it's reset. + // Set both the input offset and output offset to 1 in x, so the first column is omitted. + TrimLeft, + // Set the size to 2 in x, so the last column is omitted + TrimRight, + // Set only the output offset to 1, so the second column gets the first column's data. + SlideRight, + // Try to copy from out of bounds of the source image + SourceOutOfBounds, + // Try to copy from out of bounds of the destination image + DestOutOfBounds, + // Try to copy more than one slice from the source + MultiSliceCopy, + // Copy into the second slice of a 2D array texture, + SecondSliceCopy, + } + let sources = [ + ExternalImageSource::ImageBitmap(image_bitmap), + ExternalImageSource::HTMLCanvasElement(canvas), + ]; + let cases = [ + TestCase::Normal, + TestCase::FlipY, + TestCase::Premultiplied, + TestCase::ColorSpace, + TestCase::TrimLeft, + TestCase::TrimRight, + TestCase::SlideRight, + TestCase::SourceOutOfBounds, + TestCase::DestOutOfBounds, + TestCase::MultiSliceCopy, + TestCase::SecondSliceCopy, + ]; - initialize_test(TestParameters::default(), |ctx| { for source in sources { for case in cases { // Copy the data, so we can modify it for tests @@ -346,5 +346,4 @@ async fn image_bitmap_import() { } } } - }) -} + }); diff --git a/tests/tests/gpu.rs b/tests/tests/gpu.rs new file mode 100644 index 0000000000..2a381c1389 --- /dev/null +++ b/tests/tests/gpu.rs @@ -0,0 +1,26 @@ +mod regression { + pub mod issue_3457; +} +mod buffer; +mod buffer_copy; +mod buffer_usages; +mod clear_texture; +mod encoder; +mod external_texture; +mod instance; +mod poll; +mod queue_transfer; +mod resource_descriptor_accessor; +mod resource_error; +mod shader; +mod shader_primitive_index; +mod shader_view_format; +mod texture_bounds; +mod transfer; +mod vertex_indices; +mod write_texture; +mod zero_init_texture_after_discard; + +// wasm_bindgen_test_configure!(run_in_browser); + +wgpu_test::gpu_test_main!(); diff --git a/tests/tests/instance.rs b/tests/tests/instance.rs index 61db72040b..0594804413 100644 --- a/tests/tests/instance.rs +++ b/tests/tests/instance.rs @@ -1,7 +1,4 @@ -use wgpu_test::{infra::GpuTest, TestingContext}; -#[derive(Clone, Default)] -pub struct InitializeTest; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration}; -impl GpuTest for InitializeTest { - fn run(&self, _ctx: TestingContext) {} -} +#[gpu_test] +static INITIALIZE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|_ctx| {}); diff --git a/tests/tests/poll.rs b/tests/tests/poll.rs index 4800bd86bc..2e7be9aed7 100644 --- a/tests/tests/poll.rs +++ b/tests/tests/poll.rs @@ -7,7 +7,7 @@ use wgpu::{ Maintain, ShaderStages, }; -use wgpu_test::{infra::GpuTest, TestingContext}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestingContext}; struct DummyWorkData { _buffer: Buffer, @@ -67,68 +67,49 @@ impl DummyWorkData { } } -#[derive(Default)] -pub struct WaitTest; +#[gpu_test] +static WAIT: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + let data = DummyWorkData::new(&ctx); -impl GpuTest for WaitTest { - fn run(&self, ctx: TestingContext) { - let data = DummyWorkData::new(&ctx); - - ctx.queue.submit(Some(data.cmd_buf)); - ctx.device.poll(Maintain::Wait); - } -} - -#[derive(Default)] -pub struct DoubleWaitTest; + ctx.queue.submit(Some(data.cmd_buf)); + ctx.device.poll(Maintain::Wait); +}); -impl GpuTest for DoubleWaitTest { - fn run(&self, ctx: TestingContext) { - let data = DummyWorkData::new(&ctx); +#[gpu_test] +static DOUBLE_WAIT: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + let data = DummyWorkData::new(&ctx); - ctx.queue.submit(Some(data.cmd_buf)); - ctx.device.poll(Maintain::Wait); - ctx.device.poll(Maintain::Wait); - } -} - -#[derive(Default)] -pub struct WaitOnSubmissionTest; - -impl GpuTest for WaitOnSubmissionTest { - fn run(&self, ctx: TestingContext) { - let data = DummyWorkData::new(&ctx); + ctx.queue.submit(Some(data.cmd_buf)); + ctx.device.poll(Maintain::Wait); + ctx.device.poll(Maintain::Wait); +}); - let index = ctx.queue.submit(Some(data.cmd_buf)); - ctx.device.poll(Maintain::WaitForSubmissionIndex(index)); - } -} +#[gpu_test] +static WAIT_ON_SUBMISSION: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + let data = DummyWorkData::new(&ctx); -#[derive(Default)] -pub struct DoubleWaitOnSubmissionTest; + let index = ctx.queue.submit(Some(data.cmd_buf)); + ctx.device.poll(Maintain::WaitForSubmissionIndex(index)); +}); -impl GpuTest for DoubleWaitOnSubmissionTest { - fn run(&self, ctx: TestingContext) { +#[gpu_test] +static DOUBLE_WAIT_ON_SUBMISSION: GpuTestConfiguration = + GpuTestConfiguration::new().run_sync(|ctx| { let data = DummyWorkData::new(&ctx); let index = ctx.queue.submit(Some(data.cmd_buf)); ctx.device .poll(Maintain::WaitForSubmissionIndex(index.clone())); ctx.device.poll(Maintain::WaitForSubmissionIndex(index)); - } -} - -#[derive(Default)] -pub struct WaitOutOfOrderTest; - -impl GpuTest for WaitOutOfOrderTest { - fn run(&self, ctx: TestingContext) { - let data1 = DummyWorkData::new(&ctx); - let data2 = DummyWorkData::new(&ctx); - - let index1 = ctx.queue.submit(Some(data1.cmd_buf)); - let index2 = ctx.queue.submit(Some(data2.cmd_buf)); - ctx.device.poll(Maintain::WaitForSubmissionIndex(index2)); - ctx.device.poll(Maintain::WaitForSubmissionIndex(index1)); - } -} + }); + +#[gpu_test] +static WAIT_OUT_OF_ORDER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + let data1 = DummyWorkData::new(&ctx); + let data2 = DummyWorkData::new(&ctx); + + let index1 = ctx.queue.submit(Some(data1.cmd_buf)); + let index2 = ctx.queue.submit(Some(data2.cmd_buf)); + ctx.device.poll(Maintain::WaitForSubmissionIndex(index2)); + ctx.device.poll(Maintain::WaitForSubmissionIndex(index1)); +}); diff --git a/tests/tests/queue_transfer.rs b/tests/tests/queue_transfer.rs index 239657c18c..68d78bdd22 100644 --- a/tests/tests/queue_transfer.rs +++ b/tests/tests/queue_transfer.rs @@ -1,12 +1,10 @@ //! Tests for buffer copy validation. -use wgpu_test::{fail, infra::GpuTest}; +use wgpu_test::{fail, gpu_test, infra::GpuTestConfiguration}; -#[derive(Default)] -pub struct QueueWriteTextureOverflowTest; - -impl GpuTest for QueueWriteTextureOverflowTest { - fn run(&self, ctx: wgpu_test::TestingContext) { +#[gpu_test] +static QUEUE_WRITE_TEXTURE_OVERFLOW: GpuTestConfiguration = + GpuTestConfiguration::new().run_sync(|ctx| { let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, size: wgpu::Extent3d { @@ -46,5 +44,4 @@ impl GpuTest for QueueWriteTextureOverflowTest { }, ); }); - } -} + }); diff --git a/tests/tests/regression/issue_3457.rs b/tests/tests/regression/issue_3457.rs index d5bfde1ef5..8e7d6fea15 100644 --- a/tests/tests/regression/issue_3457.rs +++ b/tests/tests/regression/issue_3457.rs @@ -1,4 +1,4 @@ -use wgpu_test::infra::GpuTest; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration}; use wgpu::*; @@ -14,11 +14,9 @@ use wgpu::*; /// /// We use non-consecutive vertex attribute locations (0 and 5) in order to also test /// that we unset the correct locations (see PR #3706). -#[derive(Default)] -pub struct PassResetVertexBufferTest; - -impl GpuTest for PassResetVertexBufferTest { - fn run(&self, ctx: wgpu_test::TestingContext) { +#[gpu_test] +static PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration = + GpuTestConfiguration::new().run_sync(|ctx| { let module = ctx .device .create_shader_module(include_wgsl!("issue_3457.wgsl")); @@ -186,5 +184,4 @@ impl GpuTest for PassResetVertexBufferTest { drop(single_rpass); ctx.queue.submit(Some(encoder2.finish())); - } -} + }); diff --git a/tests/tests/resource_descriptor_accessor.rs b/tests/tests/resource_descriptor_accessor.rs index 68bbf9496d..d0851ed621 100644 --- a/tests/tests/resource_descriptor_accessor.rs +++ b/tests/tests/resource_descriptor_accessor.rs @@ -1,22 +1,17 @@ -use wgpu_test::infra::GpuTest; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration}; -/// Buffer's size and usage can be read back. -#[derive(Default)] -pub struct BufferSizeAndUsageTest; +#[gpu_test] +static BUFFER_SIZE_AND_USAGE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1234, + usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); -impl GpuTest for BufferSizeAndUsageTest { - fn run(&self, ctx: wgpu_test::TestingContext) { - let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: 1234, - usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - - assert_eq!(buffer.size(), 1234); - assert_eq!( - buffer.usage(), - wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST - ); - } -} + assert_eq!(buffer.size(), 1234); + assert_eq!( + buffer.usage(), + wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST + ); +}); diff --git a/tests/tests/resource_error.rs b/tests/tests/resource_error.rs index a7a6c70b65..3a4e53a3f2 100644 --- a/tests/tests/resource_error.rs +++ b/tests/tests/resource_error.rs @@ -1,56 +1,48 @@ -use wgpu_test::{fail, infra::GpuTest, valid}; +use wgpu_test::{fail, gpu_test, infra::GpuTestConfiguration, valid}; -#[derive(Default)] -pub struct BadBufferTest; +#[gpu_test] +static BAD_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + // Create a buffer with bad parameters and call a few methods. + // Validation should fail but there should be not panic. + let buffer = fail(&ctx.device, || { + ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 99999999, + usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::STORAGE, + mapped_at_creation: false, + }) + }); -impl GpuTest for BadBufferTest { - fn run(&self, ctx: wgpu_test::TestingContext) { - // Create a buffer with bad parameters and call a few methods. - // Validation should fail but there should be not panic. - let buffer = fail(&ctx.device, || { - ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: 99999999, - usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::STORAGE, - mapped_at_creation: false, - }) - }); + fail(&ctx.device, || { + buffer.slice(..).map_async(wgpu::MapMode::Write, |_| {}) + }); + fail(&ctx.device, || buffer.unmap()); + valid(&ctx.device, || buffer.destroy()); + valid(&ctx.device, || buffer.destroy()); +}); - fail(&ctx.device, || { - buffer.slice(..).map_async(wgpu::MapMode::Write, |_| {}) - }); - fail(&ctx.device, || buffer.unmap()); - valid(&ctx.device, || buffer.destroy()); - valid(&ctx.device, || buffer.destroy()); - } -} +#[gpu_test] +static BAD_TEXTURE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + let texture = fail(&ctx.device, || { + ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: 0, + height: 12345678, + depth_or_array_layers: 9001, + }, + mip_level_count: 2000, + sample_count: 27, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::all(), + view_formats: &[], + }) + }); -#[derive(Default)] -pub struct BadTextureTest; - -impl GpuTest for BadTextureTest { - fn run(&self, ctx: wgpu_test::TestingContext) { - let texture = fail(&ctx.device, || { - ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width: 0, - height: 12345678, - depth_or_array_layers: 9001, - }, - mip_level_count: 2000, - sample_count: 27, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::all(), - view_formats: &[], - }) - }); - - fail(&ctx.device, || { - let _ = texture.create_view(&wgpu::TextureViewDescriptor::default()); - }); - valid(&ctx.device, || texture.destroy()); - valid(&ctx.device, || texture.destroy()); - } -} + fail(&ctx.device, || { + let _ = texture.create_view(&wgpu::TextureViewDescriptor::default()); + }); + valid(&ctx.device, || texture.destroy()); + valid(&ctx.device, || texture.destroy()); +}); diff --git a/tests/tests/root.rs b/tests/tests/root.rs deleted file mode 100644 index b7b60c605d..0000000000 --- a/tests/tests/root.rs +++ /dev/null @@ -1,80 +0,0 @@ -use wgpu_test::infra::GpuTest; - -mod regression { - pub mod issue_3457; -} -mod buffer; -mod buffer_copy; -mod buffer_usages; -mod clear_texture; -mod encoder; -mod example_wgsl; -mod external_texture; -mod instance; -mod poll; -mod queue_transfer; -mod resource_descriptor_accessor; -mod resource_error; -mod shader; -mod shader_primitive_index; -mod shader_view_format; -mod texture_bounds; -mod transfer; -mod vertex_indices; -mod write_texture; -mod zero_init_texture_after_discard; - -// wasm_bindgen_test_configure!(run_in_browser); - -fn main() -> wgpu_test::infra::MainResult { - wgpu_test::infra::main( - [ - buffer_copy::CopyAlignmentTest::new(), - buffer_usages::BufferUsageMappablePrimaryTest::new(), - buffer_usages::BufferUsageTest::new(), - buffer::EmptyBufferTest::new(), - buffer::MapOffsetTest::new(), - clear_texture::ClearTextureAstcTest::new(), - clear_texture::ClearTextureBcTest::new(), - clear_texture::ClearTextureD32S8Test::new(), - clear_texture::ClearTextureDepthCompatTest::new(), - clear_texture::ClearTextureEtc2Test::new(), - clear_texture::ClearTextureUncompressedCompatTest::new(), - clear_texture::ClearTextureUncompressedGlesCompatTest::new(), - encoder::DropEncoderTest::new(), - instance::InitializeTest::new(), - poll::DoubleWaitOnSubmissionTest::new(), - poll::DoubleWaitTest::new(), - poll::WaitOnSubmissionTest::new(), - poll::WaitOutOfOrderTest::new(), - poll::WaitTest::new(), - queue_transfer::QueueWriteTextureOverflowTest::new(), - regression::issue_3457::PassResetVertexBufferTest::new(), - resource_descriptor_accessor::BufferSizeAndUsageTest::new(), - resource_error::BadBufferTest::new(), - resource_error::BadTextureTest::new(), - shader_primitive_index::DrawIndexedTest::new(), - shader_primitive_index::DrawTest::new(), - shader_view_format::ReinterpretSrgbTest::new(), - shader::numeric_builtins::NumericBuiltinsTest::new(), - shader::struct_layout::PushConstantInputTest::new(), - shader::struct_layout::StorageInputTest::new(), - shader::struct_layout::UniformInputTest::new(), - shader::zero_init_workgroup_mem::ZeroInitWorkgroupMemTest::new(), - texture_bounds::BadCopyOriginTest::new(), - transfer::CopyOverflowZTest::new(), - vertex_indices::DrawInstancedOffsetTest::new(), - vertex_indices::DrawInstancedTest::new(), - vertex_indices::DrawTest::new(), - vertex_indices::DrawVertexTest::new(), - write_texture::WriteTextureSubset2dTest::new(), - write_texture::WriteTextureSubset3dTest::new(), - zero_init_texture_after_discard::DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyAfterSubmitTest::new(), - zero_init_texture_after_discard::DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest::new(), - zero_init_texture_after_discard::DiscardingDepthTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest::new(), - zero_init_texture_after_discard::DiscardingEitherDepthOrStencilAspectTest::new(), - ], [ - wgpu_test::infra::cpu_test(example_wgsl::parse_example_wgsl) - ] - ) -} diff --git a/tests/tests/shader/numeric_builtins.rs b/tests/tests/shader/numeric_builtins.rs index d8e6bd22b7..5e46e1c887 100644 --- a/tests/tests/shader/numeric_builtins.rs +++ b/tests/tests/shader/numeric_builtins.rs @@ -1,7 +1,7 @@ use wgpu::{DownlevelFlags, Limits}; use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest}; -use wgpu_test::{infra::GpuTest, TestParameters}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters}; fn create_numeric_builtin_test() -> Vec { let mut tests = Vec::new(); @@ -37,21 +37,17 @@ fn create_numeric_builtin_test() -> Vec { tests } -#[derive(Default)] -pub struct NumericBuiltinsTest; - -impl GpuTest for NumericBuiltinsTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static NUMERIC_BUILTINS: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) - .limits(Limits::downlevel_defaults()) - } - - fn run(&self, ctx: wgpu_test::TestingContext) { + .limits(Limits::downlevel_defaults()), + ) + .run_sync(|ctx| { shader_input_output_test( ctx, InputStorageType::Storage, create_numeric_builtin_test(), ); - } -} + }); diff --git a/tests/tests/shader/struct_layout.rs b/tests/tests/shader/struct_layout.rs index dcceb9438d..039590c493 100644 --- a/tests/tests/shader/struct_layout.rs +++ b/tests/tests/shader/struct_layout.rs @@ -3,7 +3,7 @@ use std::fmt::Write; use wgpu::{Backends, DownlevelFlags, Features, Limits}; use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest, MAX_BUFFER_SIZE}; -use wgpu_test::{infra::GpuTest, TestParameters}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters}; fn create_struct_layout_tests(storage_type: InputStorageType) -> Vec { let input_values: Vec<_> = (0..(MAX_BUFFER_SIZE as u32 / 4)).collect(); @@ -174,66 +174,54 @@ fn create_struct_layout_tests(storage_type: InputStorageType) -> Vec tests } -#[derive(Default)] -pub struct UniformInputTest; - -impl GpuTest for UniformInputTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static UNIFORM_INPUT: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) // Validation errors thrown by the SPIR-V validator https://github.com/gfx-rs/naga/issues/2034 .specific_failure(Some(wgpu::Backends::VULKAN), None, None, false) - .limits(Limits::downlevel_defaults()) - } - - fn run(&self, ctx: wgpu_test::TestingContext) { + .limits(Limits::downlevel_defaults()), + ) + .run_sync(|ctx| { shader_input_output_test( ctx, InputStorageType::Uniform, create_struct_layout_tests(InputStorageType::Uniform), ); - } -} - -#[derive(Default)] -pub struct StorageInputTest; + }); -impl GpuTest for StorageInputTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static STORAGE_INPUT: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) - .limits(Limits::downlevel_defaults()) - } - - fn run(&self, ctx: wgpu_test::TestingContext) { + .limits(Limits::downlevel_defaults()), + ) + .run_sync(|ctx| { shader_input_output_test( ctx, InputStorageType::Storage, create_struct_layout_tests(InputStorageType::Storage), ); - } -} + }); -#[derive(Default)] -pub struct PushConstantInputTest; - -impl GpuTest for PushConstantInputTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static PUSH_CONSTANT_INPUT: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .features(Features::PUSH_CONSTANTS) .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) .limits(Limits { max_push_constant_size: MAX_BUFFER_SIZE as u32, ..Limits::downlevel_defaults() }) - .backend_failure(Backends::GL) - } - - fn run(&self, ctx: wgpu_test::TestingContext) { + .backend_failure(Backends::GL), + ) + .run_sync(|ctx| { shader_input_output_test( ctx, InputStorageType::PushConstant, create_struct_layout_tests(InputStorageType::PushConstant), ); - } -} + }); diff --git a/tests/tests/shader/zero_init_workgroup_mem.rs b/tests/tests/shader/zero_init_workgroup_mem.rs index 82e31c7560..f1defa674c 100644 --- a/tests/tests/shader/zero_init_workgroup_mem.rs +++ b/tests/tests/shader/zero_init_workgroup_mem.rs @@ -8,14 +8,12 @@ use wgpu::{ ShaderStages, }; -use wgpu_test::{infra::GpuTest, TestParameters}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters}; -#[derive(Default)] -pub struct ZeroInitWorkgroupMemTest; - -impl GpuTest for ZeroInitWorkgroupMemTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) // remove both of these once we get to https://github.com/gfx-rs/wgpu/issues/3193 or // https://github.com/gfx-rs/wgpu/issues/3160 @@ -25,11 +23,15 @@ impl GpuTest for ZeroInitWorkgroupMemTest { Some("Microsoft Basic Render Driver"), true, ) - .specific_failure(Some(wgpu::Backends::VULKAN), None, Some("swiftshader"), true) - .limits(Limits::downlevel_defaults()) - } - - fn run(&self, ctx: wgpu_test::TestingContext) { + .specific_failure( + Some(wgpu::Backends::VULKAN), + None, + Some("swiftshader"), + true, + ) + .limits(Limits::downlevel_defaults()), + ) + .run_sync(|ctx| { let bgl = ctx .device .create_bind_group_layout(&BindGroupLayoutDescriptor { @@ -157,8 +159,7 @@ impl GpuTest for ZeroInitWorkgroupMemTest { drop(mapped); mapping_buffer.unmap(); - } -} + }); const DISPATCH_SIZE: (u32, u32, u32) = (64, 64, 64); const TOTAL_WORK_GROUPS: u32 = DISPATCH_SIZE.0 * DISPATCH_SIZE.1 * DISPATCH_SIZE.2; diff --git a/tests/tests/shader_primitive_index/mod.rs b/tests/tests/shader_primitive_index/mod.rs index fc83dbb1cc..e56a8f5f75 100644 --- a/tests/tests/shader_primitive_index/mod.rs +++ b/tests/tests/shader_primitive_index/mod.rs @@ -1,5 +1,5 @@ use wgpu::util::{align_to, DeviceExt}; -use wgpu_test::{infra::GpuTest, TestParameters, TestingContext}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters, TestingContext}; // // These tests render two triangles to a 2x2 render target. The first triangle @@ -35,16 +35,15 @@ use wgpu_test::{infra::GpuTest, TestParameters, TestingContext}; // draw_indexed() draws the triangles in the opposite order, using index // buffer [3, 4, 5, 0, 1, 2]. This also swaps the resulting pixel colors. // -#[derive(Default)] -pub struct DrawTest; -impl GpuTest for DrawTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static DRAW: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .test_features_limits() - .features(wgpu::Features::SHADER_PRIMITIVE_INDEX) - } - fn run(&self, ctx: TestingContext) { + .features(wgpu::Features::SHADER_PRIMITIVE_INDEX), + ) + .run_sync(|ctx| { // // +-----+-----+ // |white|blue | @@ -58,20 +57,16 @@ impl GpuTest for DrawTest { pulling_common(ctx, &expected, |rpass| { rpass.draw(0..6, 0..1); }) - } -} - -#[derive(Default)] -pub struct DrawIndexedTest; + }); -impl GpuTest for DrawIndexedTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static DRAW_INDEXED: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .test_features_limits() - .features(wgpu::Features::SHADER_PRIMITIVE_INDEX) - } - - fn run(&self, ctx: TestingContext) { + .features(wgpu::Features::SHADER_PRIMITIVE_INDEX), + ) + .run_sync(|ctx| { // // +-----+-----+ // |white| red | @@ -85,8 +80,7 @@ impl GpuTest for DrawIndexedTest { pulling_common(ctx, &expected, |rpass| { rpass.draw_indexed(0..6, 0, 0..1); }) - } -} + }); fn pulling_common( ctx: TestingContext, diff --git a/tests/tests/shader_view_format/mod.rs b/tests/tests/shader_view_format/mod.rs index 4cdd4f470b..b1e2e25494 100644 --- a/tests/tests/shader_view_format/mod.rs +++ b/tests/tests/shader_view_format/mod.rs @@ -1,17 +1,16 @@ use wgpu::{util::DeviceExt, DownlevelFlags, Limits, TextureFormat}; -use wgpu_test::{image::calc_difference, infra::GpuTest, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, image::calc_difference, infra::GpuTestConfiguration, TestParameters, TestingContext, +}; -#[derive(Default)] -pub struct ReinterpretSrgbTest; - -impl GpuTest for ReinterpretSrgbTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static REINTERPRET_SRGB: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .downlevel_flags(DownlevelFlags::VIEW_FORMATS) - .limits(Limits::downlevel_defaults()) - } - - fn run(&self, ctx: TestingContext) { + .limits(Limits::downlevel_defaults()), + ) + .run_sync(|ctx| { let unorm_data: [[u8; 4]; 4] = [ [180, 0, 0, 255], [0, 84, 0, 127], @@ -56,8 +55,7 @@ impl GpuTest for ReinterpretSrgbTest { &srgb_data, &unorm_data, ); - } -} + }); fn reinterpret( ctx: &TestingContext, diff --git a/tests/tests/texture_bounds.rs b/tests/tests/texture_bounds.rs index 3313363a88..5dbf8bbdcb 100644 --- a/tests/tests/texture_bounds.rs +++ b/tests/tests/texture_bounds.rs @@ -1,90 +1,86 @@ //! Tests for texture copy bounds checks. -use wgpu_test::{fail_if, infra::GpuTest}; +use wgpu_test::{fail_if, gpu_test, infra::GpuTestConfiguration}; -#[derive(Default)] -pub struct BadCopyOriginTest; +#[gpu_test] +static BAD_COPY_ORIGIN_TEST: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + let try_origin = |origin, size, should_panic| { + let texture = ctx.device.create_texture(&TEXTURE_DESCRIPTOR); + let data = vec![255; BUFFER_SIZE as usize]; -impl GpuTest for BadCopyOriginTest { - fn run(&self, ctx: wgpu_test::TestingContext) { - let try_origin = |origin, size, should_panic| { - let texture = ctx.device.create_texture(&TEXTURE_DESCRIPTOR); - let data = vec![255; BUFFER_SIZE as usize]; + fail_if(&ctx.device, should_panic, || { + ctx.queue.write_texture( + wgpu::ImageCopyTexture { + texture: &texture, + mip_level: 0, + origin, + aspect: wgpu::TextureAspect::All, + }, + &data, + BUFFER_COPY_LAYOUT, + size, + ) + }); + }; - fail_if(&ctx.device, should_panic, || { - ctx.queue.write_texture( - wgpu::ImageCopyTexture { - texture: &texture, - mip_level: 0, - origin, - aspect: wgpu::TextureAspect::All, - }, - &data, - BUFFER_COPY_LAYOUT, - size, - ) - }); - }; + try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false); + try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, TEXTURE_SIZE, true); + try_origin(wgpu::Origin3d { x: 0, y: 1, z: 0 }, TEXTURE_SIZE, true); + try_origin(wgpu::Origin3d { x: 0, y: 0, z: 1 }, TEXTURE_SIZE, true); - try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false); - try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, TEXTURE_SIZE, true); - try_origin(wgpu::Origin3d { x: 0, y: 1, z: 0 }, TEXTURE_SIZE, true); - try_origin(wgpu::Origin3d { x: 0, y: 0, z: 1 }, TEXTURE_SIZE, true); - - try_origin( - wgpu::Origin3d { - x: TEXTURE_SIZE.width - 1, - y: TEXTURE_SIZE.height - 1, - z: TEXTURE_SIZE.depth_or_array_layers - 1, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - false, - ); - try_origin( - wgpu::Origin3d { - x: u32::MAX, - y: 0, - z: 0, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - true, - ); - try_origin( - wgpu::Origin3d { - x: u32::MAX, - y: 0, - z: 0, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - true, - ); - try_origin( - wgpu::Origin3d { - x: u32::MAX, - y: 0, - z: 0, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - true, - ); - } -} + try_origin( + wgpu::Origin3d { + x: TEXTURE_SIZE.width - 1, + y: TEXTURE_SIZE.height - 1, + z: TEXTURE_SIZE.depth_or_array_layers - 1, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + false, + ); + try_origin( + wgpu::Origin3d { + x: u32::MAX, + y: 0, + z: 0, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + true, + ); + try_origin( + wgpu::Origin3d { + x: u32::MAX, + y: 0, + z: 0, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + true, + ); + try_origin( + wgpu::Origin3d { + x: u32::MAX, + y: 0, + z: 0, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + true, + ); +}); const TEXTURE_SIZE: wgpu::Extent3d = wgpu::Extent3d { width: 64, diff --git a/tests/tests/transfer.rs b/tests/tests/transfer.rs index 1c9a00f583..143d510e68 100644 --- a/tests/tests/transfer.rs +++ b/tests/tests/transfer.rs @@ -1,71 +1,65 @@ -use wgpu_test::{fail, infra::GpuTest}; +use wgpu_test::{fail, gpu_test, infra::GpuTestConfiguration}; -#[derive(Default)] -pub struct CopyOverflowZTest; +#[gpu_test] +static COPY_OVERFLOW_Z: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); -impl GpuTest for CopyOverflowZTest { - // A simple crash test exercising validation that used to happen a bit too - // late, letting an integer overflow slip through. - fn run(&self, ctx: wgpu_test::TestingContext) { - let mut encoder = ctx - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + let t1 = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size: wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }, + format: wgpu::TextureFormat::Rgba8Uint, + usage: wgpu::TextureUsages::COPY_DST, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); + let t2 = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size: wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }, + format: wgpu::TextureFormat::Rgba8Uint, + usage: wgpu::TextureUsages::COPY_DST, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); - let t1 = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - dimension: wgpu::TextureDimension::D2, - size: wgpu::Extent3d { - width: 256, - height: 256, - depth_or_array_layers: 1, + fail(&ctx.device, || { + // Validation should catch the silly selected z layer range without panicking. + encoder.copy_texture_to_texture( + wgpu::ImageCopyTexture { + texture: &t1, + mip_level: 1, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, }, - format: wgpu::TextureFormat::Rgba8Uint, - usage: wgpu::TextureUsages::COPY_DST, - mip_level_count: 1, - sample_count: 1, - view_formats: &[], - }); - let t2 = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - dimension: wgpu::TextureDimension::D2, - size: wgpu::Extent3d { - width: 256, - height: 256, - depth_or_array_layers: 1, - }, - format: wgpu::TextureFormat::Rgba8Uint, - usage: wgpu::TextureUsages::COPY_DST, - mip_level_count: 1, - sample_count: 1, - view_formats: &[], - }); - - fail(&ctx.device, || { - // Validation should catch the silly selected z layer range without panicking. - encoder.copy_texture_to_texture( - wgpu::ImageCopyTexture { - texture: &t1, - mip_level: 1, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - wgpu::ImageCopyTexture { - texture: &t2, - mip_level: 1, - origin: wgpu::Origin3d { - x: 0, - y: 0, - z: 3824276442, - }, - aspect: wgpu::TextureAspect::All, + wgpu::ImageCopyTexture { + texture: &t2, + mip_level: 1, + origin: wgpu::Origin3d { + x: 0, + y: 0, + z: 3824276442, }, - wgpu::Extent3d { - width: 100, - height: 3, - depth_or_array_layers: 613286111, - }, - ); - ctx.queue.submit(Some(encoder.finish())); - }); - } -} + aspect: wgpu::TextureAspect::All, + }, + wgpu::Extent3d { + width: 100, + height: 3, + depth_or_array_layers: 613286111, + }, + ); + ctx.queue.submit(Some(encoder.finish())); + }); +}); diff --git a/tests/tests/vertex_indices/mod.rs b/tests/tests/vertex_indices/mod.rs index 57ab6e1f2b..d2d5f76bcd 100644 --- a/tests/tests/vertex_indices/mod.rs +++ b/tests/tests/vertex_indices/mod.rs @@ -2,7 +2,7 @@ use std::num::NonZeroU64; use wgpu::util::DeviceExt; -use wgpu_test::{infra::GpuTest, TestParameters, TestingContext}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters, TestingContext}; fn pulling_common( ctx: TestingContext, @@ -131,64 +131,30 @@ fn pulling_common( assert_eq!(data, expected); } -#[derive(Default)] -pub struct DrawTest; - -impl GpuTest for DrawTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params.test_features_limits() - } - - fn run(&self, ctx: TestingContext) { +#[gpu_test] +static DRAW_VERTEX: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().test_features_limits()) + .run_sync(|ctx| { pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { cmb.draw(0..6, 0..1); }) - } -} - -#[derive(Default)] -pub struct DrawVertexTest; - -impl GpuTest for DrawVertexTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params.test_features_limits() - } - - fn run(&self, ctx: TestingContext) { - pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { - cmb.draw(0..3, 0..1); - cmb.draw(3..6, 0..1); - }) - } -} - -#[derive(Default)] -pub struct DrawInstancedTest; - -impl GpuTest for DrawInstancedTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params.test_features_limits() - } + }); - fn run(&self, ctx: TestingContext) { +#[gpu_test] +static DRAW_INSTANCED: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().test_features_limits()) + .run_sync(|ctx| { pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { cmb.draw(0..3, 0..2); }) - } -} - -#[derive(Default)] -pub struct DrawInstancedOffsetTest; - -impl GpuTest for DrawInstancedOffsetTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params.test_features_limits() - } + }); - fn run(&self, ctx: TestingContext) { +#[gpu_test] +static DRAW_INSTANCED_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().test_features_limits()) + .run_sync(|ctx| { pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { cmb.draw(0..3, 0..1); cmb.draw(0..3, 1..2); }) - } -} + }); diff --git a/tests/tests/write_texture.rs b/tests/tests/write_texture.rs index a3571426f1..1ab488ea1a 100644 --- a/tests/tests/write_texture.rs +++ b/tests/tests/write_texture.rs @@ -1,16 +1,11 @@ //! Tests for texture copy -use wgpu_test::{infra::GpuTest, TestParameters}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters}; -#[derive(Default)] -pub struct WriteTextureSubset2dTest; - -impl GpuTest for WriteTextureSubset2dTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params.backend_failure(wgpu::Backends::DX12) - } - - fn run(&self, ctx: wgpu_test::TestingContext) { +#[gpu_test] +static WRITE_TEXTURE_SUBSET_2D: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().backend_failure(wgpu::Backends::DX12)) + .run_sync(|ctx| { let size = 256; let tex = ctx.device.create_texture(&wgpu::TextureDescriptor { @@ -99,14 +94,11 @@ impl GpuTest for WriteTextureSubset2dTest { for byte in &data[(size as usize * 2)..] { assert_eq!(*byte, 0); } - } -} - -#[derive(Default)] -pub struct WriteTextureSubset3dTest; + }); -impl GpuTest for WriteTextureSubset3dTest { - fn run(&self, ctx: wgpu_test::TestingContext) { +#[gpu_test] +static WRITE_TEXTURE_SUBSET_3D: GpuTestConfiguration = + GpuTestConfiguration::new().run_sync(|ctx| { let size = 256; let depth = 4; let tex = ctx.device.create_texture(&wgpu::TextureDescriptor { @@ -195,5 +187,4 @@ impl GpuTest for WriteTextureSubset3dTest { for byte in &data[((size * size) as usize * 2)..] { assert_eq!(*byte, 0); } - } -} + }); diff --git a/tests/tests/zero_init_texture_after_discard.rs b/tests/tests/zero_init_texture_after_discard.rs index aaa5b8a03e..940f20c64f 100644 --- a/tests/tests/zero_init_texture_after_discard.rs +++ b/tests/tests/zero_init_texture_after_discard.rs @@ -1,16 +1,14 @@ use wgpu::*; -use wgpu_test::{image::ReadbackBuffers, infra::GpuTest, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, image::ReadbackBuffers, infra::GpuTestConfiguration, TestParameters, TestingContext, +}; // Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in after submit of the encoder. -#[derive(Default)] -pub struct DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyAfterSubmitTest; - -impl GpuTest for DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyAfterSubmitTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params.webgl2_failure() - } - - fn run(&self, mut ctx: TestingContext) { +#[gpu_test] +static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_AFTER_SUBMIT: + GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().webgl2_failure()) + .run_sync(|mut ctx| { let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb); case.create_command_encoder(); case.discard(); @@ -21,18 +19,13 @@ impl GpuTest for DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyAf case.submit_command_encoder(); case.assert_buffers_are_zero(); - } -} + }); -#[derive(Default)] -pub struct DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest; - -impl GpuTest for DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params.webgl2_failure() - } - - fn run(&self, mut ctx: TestingContext) { +#[gpu_test] +static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER: + GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().webgl2_failure()) + .run_sync(|mut ctx| { let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb); case.create_command_encoder(); case.discard(); @@ -40,22 +33,19 @@ impl GpuTest for DiscardingColorTargetResetsTextureInitStateCheckVisibleOnCopyIn case.submit_command_encoder(); case.assert_buffers_are_zero(); - } -} + }); -#[derive(Default)] -pub struct DiscardingDepthTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest; - -impl GpuTest for DiscardingDepthTargetResetsTextureInitStateCheckVisibleOnCopyInSameEncoderTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params +#[gpu_test] +static DISCARDING_DEPTH_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER: + GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() .downlevel_flags( DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | DownlevelFlags::COMPUTE_SHADERS, ) - .limits(Limits::downlevel_defaults()) - } - - fn run(&self, mut ctx: TestingContext) { + .limits(Limits::downlevel_defaults()), + ) + .run_sync(|mut ctx| { for format in [ TextureFormat::Stencil8, TextureFormat::Depth16Unorm, @@ -71,38 +61,43 @@ impl GpuTest for DiscardingDepthTargetResetsTextureInitStateCheckVisibleOnCopyIn case.assert_buffers_are_zero(); } - } -} - -#[derive(Default)] -pub struct DiscardingEitherDepthOrStencilAspectTest; - -impl GpuTest for DiscardingEitherDepthOrStencilAspectTest { - fn parameters(&self, params: TestParameters) -> TestParameters { - params - .downlevel_flags( - DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | DownlevelFlags::COMPUTE_SHADERS, - ) - .limits(Limits::downlevel_defaults()) - } - - fn run(&self, mut ctx: TestingContext) { - let mut case = TestCase::new(&mut ctx, TextureFormat::Depth24PlusStencil8); - case.create_command_encoder(); - case.discard_depth(); - case.submit_command_encoder(); - - case.create_command_encoder(); - case.discard_stencil(); - case.submit_command_encoder(); - - case.create_command_encoder(); - case.copy_texture_to_buffer(); - case.submit_command_encoder(); - - case.assert_buffers_are_zero(); - } -} + }); + +#[gpu_test] +static DISCARDING_EITHER_DEPTH_OR_STENCIL_ASPECT_TEST: GpuTestConfiguration = + GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .downlevel_flags( + DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES + | DownlevelFlags::COMPUTE_SHADERS, + ) + .limits(Limits::downlevel_defaults()), + ) + .run_sync(|mut ctx| { + for format in [ + TextureFormat::Stencil8, + TextureFormat::Depth16Unorm, + TextureFormat::Depth24Plus, + TextureFormat::Depth24PlusStencil8, + TextureFormat::Depth32Float, + ] { + let mut case = TestCase::new(&mut ctx, format); + case.create_command_encoder(); + case.discard_depth(); + case.submit_command_encoder(); + + case.create_command_encoder(); + case.discard_stencil(); + case.submit_command_encoder(); + + case.create_command_encoder(); + case.copy_texture_to_buffer(); + case.submit_command_encoder(); + + case.assert_buffers_are_zero(); + } + }); struct TestCase<'ctx> { ctx: &'ctx mut TestingContext, diff --git a/wgpu-macros/Cargo.toml b/wgpu-macros/Cargo.toml new file mode 100644 index 0000000000..b06df02cce --- /dev/null +++ b/wgpu-macros/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "wgpu-macros" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "Macros for wgpu" +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +license.workspace = true +exclude = ["Cargo.lock"] +publish = false + +[lib] +proc-macro = true + +[dependencies] +heck = "0.4" +quote = "1" +syn = { version = "2", features = ["full"] } diff --git a/wgpu-macros/src/lib.rs b/wgpu-macros/src/lib.rs new file mode 100644 index 0000000000..adeab23b90 --- /dev/null +++ b/wgpu-macros/src/lib.rs @@ -0,0 +1,33 @@ +use heck::ToSnakeCase; +use proc_macro::TokenStream; +use quote::quote; +use syn::Ident; + +#[proc_macro_attribute] +pub fn gpu_test(_attr: TokenStream, item: TokenStream) -> TokenStream { + let input_static = syn::parse_macro_input!(item as syn::ItemStatic); + let expr = &input_static.expr; + let ident = &input_static.ident; + let ident_str = ident.to_string(); + let ident_lower = ident_str.to_snake_case(); + + let register_test_name = Ident::new(&format!("{}_initializer", ident_lower), ident.span()); + let test_name_webgl = Ident::new(&format!("{}_webgl", ident_lower), ident.span()); + + quote! { + #[cfg(not(target_arch = "wasm32"))] + #[::wgpu_test::ctor] + fn #register_test_name() { + ::wgpu_test::infra::TEST_LIST.lock().push( + // Allow any type that can be converted to a GpuTestConfiguration + ::wgpu_test::infra::GpuTestConfiguration::from(#expr).name_if_not_set(#ident_lower) + ) + } + + #[cfg(target_arch = "wasm32")] + fn #test_name_webgl() { + // todo + } + } + .into() +} From 2fd5b41b955ff16a5691c59e16043252a4c8fedc Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Wed, 27 Sep 2023 23:38:46 -0400 Subject: [PATCH 15/41] Fix type name --- tests/src/infra/params.rs | 39 ++++++++++++++++++++++++++----- tests/tests/vertex_indices/mod.rs | 12 +++++++++- wgpu-macros/src/lib.rs | 4 +++- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/tests/src/infra/params.rs b/tests/src/infra/params.rs index a9313fa27e..efdbcfc02c 100644 --- a/tests/src/infra/params.rs +++ b/tests/src/infra/params.rs @@ -7,7 +7,7 @@ pub type RunTestAsync = #[derive(Clone)] pub struct GpuTestConfiguration { - pub name: &'static str, + pub name: String, pub params: TestParameters, pub test: Option, } @@ -15,21 +15,48 @@ pub struct GpuTestConfiguration { impl GpuTestConfiguration { pub fn new() -> Self { Self { - name: "", + name: String::new(), params: TestParameters::default(), test: None, } } - pub fn name(self, name: &'static str) -> Self { - Self { name, ..self } + pub fn name(self, name: &str) -> Self { + Self { + name: String::from(name), + ..self + } } - pub fn name_if_not_set(self, name: &'static str) -> Self { + #[doc(hidden)] + /// Derives the name from a `struct S` in the function initializing the test. + /// + /// Does not overwrite a given name if a name has already been set + pub fn name_from_init_function_typename(self, name: &'static str) -> Self { if self.name != "" { return self; } - Self { name, ..self } + let type_name = std::any::type_name::(); + + // We end up with a string like: + // + // module::path::we::want::test_name_initializer::S + // + // So we reverse search for the 4th colon from the end, and take everything before that. + let mut colons = 0; + let mut colon_4_index = type_name.len(); + for i in (0..type_name.len()).rev() { + if type_name.as_bytes()[i] == b':' { + colons += 1; + } + if colons == 4 { + colon_4_index = i; + break; + } + } + + let full = format!("{}::{}", &type_name[..colon_4_index], name); + Self { name: full, ..self } } pub fn parameters(self, parameters: TestParameters) -> Self { diff --git a/tests/tests/vertex_indices/mod.rs b/tests/tests/vertex_indices/mod.rs index d2d5f76bcd..92ec1a6d67 100644 --- a/tests/tests/vertex_indices/mod.rs +++ b/tests/tests/vertex_indices/mod.rs @@ -132,13 +132,23 @@ fn pulling_common( } #[gpu_test] -static DRAW_VERTEX: GpuTestConfiguration = GpuTestConfiguration::new() +static DRAW: GpuTestConfiguration = GpuTestConfiguration::new() .parameters(TestParameters::default().test_features_limits()) .run_sync(|ctx| { pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { cmb.draw(0..6, 0..1); }) }); + +#[gpu_test] +static DRAW_VERTEX: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().test_features_limits()) + .run_sync(|ctx| { + pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { + cmb.draw(0..3, 0..1); + cmb.draw(3..6, 0..1); + }) + }); #[gpu_test] static DRAW_INSTANCED: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/wgpu-macros/src/lib.rs b/wgpu-macros/src/lib.rs index adeab23b90..dd3bb71095 100644 --- a/wgpu-macros/src/lib.rs +++ b/wgpu-macros/src/lib.rs @@ -18,9 +18,11 @@ pub fn gpu_test(_attr: TokenStream, item: TokenStream) -> TokenStream { #[cfg(not(target_arch = "wasm32"))] #[::wgpu_test::ctor] fn #register_test_name() { + struct S; + ::wgpu_test::infra::TEST_LIST.lock().push( // Allow any type that can be converted to a GpuTestConfiguration - ::wgpu_test::infra::GpuTestConfiguration::from(#expr).name_if_not_set(#ident_lower) + ::wgpu_test::infra::GpuTestConfiguration::from(#expr).name_from_init_function_typename::(#ident_lower) ) } From e0596e1b2022f55087444c0630f008e67ef7175d Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 28 Sep 2023 02:39:23 -0400 Subject: [PATCH 16/41] Simple stuff --- .github/workflows/ci.yml | 2 +- tests/src/infra/single.rs | 5 +---- tests/tests/bind_group_layout_dedup.rs | 5 ----- tests/tests/device.rs | 6 ++---- tests/tests/shader/zero_init_workgroup_mem.rs | 5 +---- 5 files changed, 5 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d2177161b..625d071cda 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ env: CARGO_TERM_COLOR: always RUST_LOG: info RUST_BACKTRACE: full - MSRV: 1.70 + MSRV: "1.70" PKG_CONFIG_ALLOW_CROSS: 1 # allow android to work RUSTFLAGS: --cfg=web_sys_unstable_apis -D warnings RUSTDOCFLAGS: -Dwarnings diff --git a/tests/src/infra/single.rs b/tests/src/infra/single.rs index c1123c7e87..a8842ef0ff 100644 --- a/tests/src/infra/single.rs +++ b/tests/src/infra/single.rs @@ -68,10 +68,7 @@ impl SingleTest { let running_msg = if let Some(reasons) = skip_reason { skip = true; - let names: ArrayVec<_, 4> = reasons - .iter_names() - .map(|(name, _)| name) - .collect(); + let names: ArrayVec<_, 4> = reasons.iter_names().map(|(name, _)| name).collect(); let names_text = names.join(" | "); format!("Skipped Failure: {}", names_text) diff --git a/tests/tests/bind_group_layout_dedup.rs b/tests/tests/bind_group_layout_dedup.rs index 06848bd31d..af8e88b8e5 100644 --- a/tests/tests/bind_group_layout_dedup.rs +++ b/tests/tests/bind_group_layout_dedup.rs @@ -137,11 +137,6 @@ static BIND_GROUP_LAYOUT_DEDUPLICATION: GpuTestConfiguration = GpuTestConfigurat ctx.queue.submit(Some(encoder.finish())); }); -#[test] -fn bind_group_layout_deduplication() { - initialize_test(TestParameters::default(), |ctx| {}) -} - const SHADER_SRC: &str = " @vertex fn vs_main() -> @builtin(position) vec4 { return vec4(1.0); } @fragment fn fs_main() -> @location(0) vec4 { return vec4(1.0); } diff --git a/tests/tests/device.rs b/tests/tests/device.rs index 8f7ae3b6af..4300847ce6 100644 --- a/tests/tests/device.rs +++ b/tests/tests/device.rs @@ -1,9 +1,7 @@ -use wgpu_test::{ - fail, gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters, -}; +use wgpu_test::{fail, gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; #[gpu_test] -static BIND_GROUP_LAYOUT_DEDUPLICATION: GpuTestConfiguration = GpuTestConfiguration::new() +static CROSS_DEVICE_BIND_GROUP_USAGE: GpuTestConfiguration = GpuTestConfiguration::new() .parameters(TestParameters::default().skip(FailureCase::always())) .run_sync(|ctx| { // Create a bind group uisng a layout from another device. This should be a validation diff --git a/tests/tests/shader/zero_init_workgroup_mem.rs b/tests/tests/shader/zero_init_workgroup_mem.rs index 1195e63a09..93897c299b 100644 --- a/tests/tests/shader/zero_init_workgroup_mem.rs +++ b/tests/tests/shader/zero_init_workgroup_mem.rs @@ -27,10 +27,7 @@ static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration:: Backends::VULKAN, "swiftshader", )) - .skip(FailureCase::backend_adapter( - Backends::VULKAN, - "llvmpipe", - )) + .skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe")) .limits(Limits::downlevel_defaults()), ) .run_sync(|ctx| { From b15d74a49f7313c05767ea0146ecb38e58031131 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 28 Sep 2023 02:48:13 -0400 Subject: [PATCH 17/41] CI stuff --- .deny.toml | 3 ++- .github/workflows/ci.yml | 2 +- tests/tests/regression/issue_4122.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.deny.toml b/.deny.toml index f7c233c5d4..2d0c13d919 100644 --- a/.deny.toml +++ b/.deny.toml @@ -6,7 +6,8 @@ skip-tree = [ { name = "wgpu-info" }, ] skip = [ - { name = "wgpu" } + { name = "wgpu" }, + { name = "fastrand" } ] wildcards = "deny" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 625d071cda..e8f6400903 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -328,7 +328,7 @@ jobs: - name: run rustfmt run: | cargo fmt -- --check - cargo fmt --manifest-path xtask/ -- --check + cargo fmt --manifest-path xtask/Cargo.toml -- --check check-msrv-cts_runner: name: Clippy cts_runner diff --git a/tests/tests/regression/issue_4122.rs b/tests/tests/regression/issue_4122.rs index d43590e6c7..b5945c5813 100644 --- a/tests/tests/regression/issue_4122.rs +++ b/tests/tests/regression/issue_4122.rs @@ -88,7 +88,7 @@ fn fill_test(ctx: &TestingContext, range: Range, size: u64) -> bool { /// /// This test will fail on nvidia if the bug is not properly worked around. #[gpu_test] -static QUEUE_SUBMITTED_CALLBACK_ORDERING: GpuTestConfiguration = GpuTestConfiguration::new() +static CLEAR_BUFFER_RANGE_RESPECTED: GpuTestConfiguration = GpuTestConfiguration::new() .run_sync(|ctx| { // This hits most of the cases in nvidia's clear buffer bug let mut succeeded = true; From 0d82a2ae6e02d6d685a5447c0b1a6459a0451b64 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 28 Sep 2023 02:52:16 -0400 Subject: [PATCH 18/41] Format --- tests/tests/regression/issue_4122.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tests/regression/issue_4122.rs b/tests/tests/regression/issue_4122.rs index b5945c5813..815323991e 100644 --- a/tests/tests/regression/issue_4122.rs +++ b/tests/tests/regression/issue_4122.rs @@ -88,8 +88,8 @@ fn fill_test(ctx: &TestingContext, range: Range, size: u64) -> bool { /// /// This test will fail on nvidia if the bug is not properly worked around. #[gpu_test] -static CLEAR_BUFFER_RANGE_RESPECTED: GpuTestConfiguration = GpuTestConfiguration::new() - .run_sync(|ctx| { +static CLEAR_BUFFER_RANGE_RESPECTED: GpuTestConfiguration = + GpuTestConfiguration::new().run_sync(|ctx| { // This hits most of the cases in nvidia's clear buffer bug let mut succeeded = true; for power in 4..14 { From 16d27d22a8549fd85d59625aaf52fc8486f0874b Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Tue, 3 Oct 2023 21:35:50 -0400 Subject: [PATCH 19/41] Update --- Cargo.lock | 76 +++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93cf4edb8c..e55e087c8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ab_glyph" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5110f1c78cf582855d895ecd0746b653db010cec6d9f5575293f27934d980a39" +checksum = "b1061f3ff92c2f65800df1f12fc7b4ff44ee14783104187dd04dfee6f11b0fd2" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.5.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", @@ -108,15 +108,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -132,9 +132,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "2.1.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -393,9 +393,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.5" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824956d0dca8334758a5b7f7e50518d66ea319330cbceedcf76905c2f6ab30e3" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", "clap_derive", @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.5" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122ec64120a49b4563ccaedcbea7818d069ed8e9aa6d829b82d8a4128936b2ab" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", @@ -663,7 +663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16e44ab292b1dddfdaf7be62cfd8877df52f2f3fde5858d95bab606be259f20" dependencies = [ "bitflags 2.4.0", - "libloading 0.7.4", + "libloading 0.8.1", "winapi", ] @@ -855,7 +855,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.7.4", + "libloading 0.8.1", ] [[package]] @@ -923,9 +923,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" dependencies = [ "errno-dragonfly", "libc", @@ -1379,7 +1379,7 @@ checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ "bitflags 2.4.0", "gpu-descriptor-types", - "hashbrown 0.14.0", + "hashbrown 0.14.1", ] [[package]] @@ -1399,9 +1399,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" dependencies = [ "ahash", "allocator-api2", @@ -1503,12 +1503,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.1", "serde", ] @@ -1650,9 +1650,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" [[package]] name = "lock_api" @@ -1684,9 +1684,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memmap2" @@ -1766,7 +1766,7 @@ dependencies = [ "bitflags 2.4.0", "codespan-reporting", "hexf-parse", - "indexmap 2.0.1", + "indexmap 2.0.2", "log", "num-traits 0.2.16", "petgraph", @@ -2160,7 +2160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.0.1", + "indexmap 2.0.2", ] [[package]] @@ -2403,9 +2403,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.5" +version = "1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ "aho-corasick", "memchr", @@ -2415,9 +2415,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", @@ -2480,9 +2480,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.14" +version = "0.38.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531" dependencies = [ "bitflags 2.4.0", "errno", @@ -2599,7 +2599,7 @@ version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ - "indexmap 2.0.1", + "indexmap 2.0.2", "itoa", "ryu", "serde", @@ -2951,7 +2951,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.1", + "indexmap 2.0.2", "toml_datetime", "winnow", ] From f2df0842fef4c70f7c83819894796fe952080037 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 5 Oct 2023 00:29:57 -0400 Subject: [PATCH 20/41] Get wasm working --- tests/src/image.rs | 8 ++-- tests/src/infra/mod.rs | 9 ++-- tests/src/infra/params.rs | 14 ++++-- tests/src/infra/report.rs | 17 +++++++ tests/src/infra/single.rs | 83 +++++++++++++++++++++------------ tests/src/lib.rs | 50 ++++++++++++++------ tests/tests/external_texture.rs | 1 - tests/tests/gpu.rs | 2 - wgpu-macros/src/lib.rs | 10 +++- 9 files changed, 136 insertions(+), 58 deletions(-) diff --git a/tests/src/image.rs b/tests/src/image.rs index d380458e70..f76d5d70df 100644 --- a/tests/src/image.rs +++ b/tests/src/image.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, ffi::OsStr, io, path::Path}; +use std::{borrow::Cow, ffi::OsStr, path::Path}; use wgpu::util::{align_to, DeviceExt}; use wgpu::*; @@ -16,7 +16,7 @@ async fn read_png(path: impl AsRef, width: u32, height: u32) -> Option> = Mutex::new(Vec::new()); pub type MainResult = anyhow::Result<()>; +#[cfg(not(target_arch = "wasm32"))] pub fn main() -> MainResult { let config_text = &std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) @@ -27,13 +29,14 @@ pub fn main() -> MainResult { .iter() .enumerate() .map(move |(adapter_index, adapter)| { - SingleTest::from_gpu_test(test.clone(), adapter, adapter_index) + SingleTest::from_configuration(test.clone(), adapter, adapter_index) }) })); Ok(()) } +#[cfg(not(target_arch = "wasm32"))] fn execute_native(tests: impl IntoIterator) { let args = libtest_mimic::Arguments::from_args(); let trials = tests.into_iter().map(SingleTest::into_trial).collect(); diff --git a/tests/src/infra/params.rs b/tests/src/infra/params.rs index 643ba350cf..c2feb79495 100644 --- a/tests/src/infra/params.rs +++ b/tests/src/infra/params.rs @@ -1,9 +1,14 @@ use std::{future::Future, pin::Pin, sync::Arc}; +use wgt::{WasmNotSend, WasmNotSync}; + use crate::{TestParameters, TestingContext}; +#[cfg(not(target_arch = "wasm32"))] pub type RunTestAsync = Arc Pin + Send + Sync>> + Send + Sync>; +#[cfg(target_arch = "wasm32")] +pub type RunTestAsync = Arc Pin>>>; #[derive(Clone)] pub struct GpuTestConfiguration { @@ -66,7 +71,10 @@ impl GpuTestConfiguration { } } - pub fn run_sync(self, test: impl Fn(TestingContext) + Copy + Send + Sync + 'static) -> Self { + pub fn run_sync( + self, + test: impl Fn(TestingContext) + Copy + WasmNotSend + WasmNotSync + 'static, + ) -> Self { Self { test: Some(Arc::new(move |ctx| Box::pin(async move { test(ctx) }))), ..self @@ -75,8 +83,8 @@ impl GpuTestConfiguration { pub fn run_async(self, test: F) -> Self where - F: Fn(TestingContext) -> R + Send + Sync + 'static, - R: Future + Send + Sync + 'static, + F: Fn(TestingContext) -> R + WasmNotSend + WasmNotSync + 'static, + R: Future + WasmNotSend + WasmNotSync + 'static, { Self { test: Some(Arc::new(move |ctx| Box::pin(test(ctx)))), diff --git a/tests/src/infra/report.rs b/tests/src/infra/report.rs index 089d47d431..e79f813b7f 100644 --- a/tests/src/infra/report.rs +++ b/tests/src/infra/report.rs @@ -24,3 +24,20 @@ pub struct AdapterReport { pub downlevel_caps: DownlevelCapabilities, pub texture_format_features: HashMap, } + +impl AdapterReport { + pub fn from_adapter(adapter: &wgpu::Adapter) -> Self { + let info = adapter.get_info(); + let features = adapter.features(); + let limits = adapter.limits(); + let downlevel_caps = adapter.get_downlevel_capabilities(); + + Self { + info, + features, + limits, + downlevel_caps, + texture_format_features: HashMap::new(), // todo + } + } +} diff --git a/tests/src/infra/single.rs b/tests/src/infra/single.rs index a8842ef0ff..ea710a0829 100644 --- a/tests/src/infra/single.rs +++ b/tests/src/infra/single.rs @@ -4,24 +4,22 @@ use arrayvec::ArrayVec; use crate::{ infra::{report::AdapterReport, GpuTestConfiguration}, - initialize_test, + initialize_test, FailureReasons, }; -pub(super) struct SingleTest { - name: String, - future: Pin> + Send + Sync>>, -} +#[cfg(target_arch = "wasm32")] +type SingleTestFuture = Pin>>; +#[cfg(not(target_arch = "wasm32"))] +type SingleTestFuture = Pin + Send + Sync>>; -impl SingleTest { - pub fn from_gpu_test( - test: GpuTestConfiguration, - adapter: &AdapterReport, - adapter_index: usize, - ) -> Self { - let base_name = test.name; - let backend = &adapter.info.backend; - let device_name = &adapter.info.name; +pub struct TestInfo { + pub skip: bool, + pub expected_failure_reason: Option, + pub running_msg: String, +} +impl TestInfo { + pub fn from_configuration(test: &GpuTestConfiguration, adapter: &AdapterReport) -> Self { // Figure out if we should skip the test and if so, why. let mut skipped_reasons: ArrayVec<_, 4> = ArrayVec::new(); let missing_features = test.params.required_features - adapter.features; @@ -58,7 +56,7 @@ impl SingleTest { .iter() .find_map(|case| case.applies_to(&adapter_lowercase_info)); - let expected_failure = test + let expected_failure_reason = test .params .failures .iter() @@ -75,7 +73,11 @@ impl SingleTest { } else if !skipped_reasons.is_empty() { skip = true; format!("Skipped: {}", skipped_reasons.join(" | ")) - } else if let Some(failure_resasons) = expected_failure { + } else if let Some(failure_resasons) = expected_failure_reason { + if cfg!(target_arch = "wasm32") { + skip = true; + } + let names: ArrayVec<_, 4> = failure_resasons .iter_names() .map(|(name, _)| name) @@ -87,28 +89,51 @@ impl SingleTest { String::from("Executed") }; - let full_name = - format!("[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}"); + Self { + skip, + expected_failure_reason, + running_msg, + } + } +} + +pub struct SingleTest { + name: String, + future: SingleTestFuture, +} + +impl SingleTest { + pub fn from_configuration( + config: GpuTestConfiguration, + adapter: &AdapterReport, + adapter_index: usize, + ) -> Self { + let backend = &adapter.info.backend; + let device_name = &adapter.info.name; + + let test_info = TestInfo::from_configuration(&config, adapter); + let full_name = format!( + "[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}", + running_msg = test_info.running_msg, + base_name = config.name, + ); Self { name: full_name, future: Box::pin(async move { - if skip { - return Ok(()); + if test_info.skip { + return; } - initialize_test( - test.params, - expected_failure, - adapter_index, - test.test.expect("Test must be specified"), - ) - .await; - Ok(()) + initialize_test(config, Some(test_info), adapter_index).await; }), } } + #[cfg(not(target_arch = "wasm32"))] pub fn into_trial(self) -> libtest_mimic::Trial { - libtest_mimic::Trial::test(self.name, || pollster::block_on(self.future)) + libtest_mimic::Trial::test(self.name, || { + pollster::block_on(self.future); + Ok(()) + }) } } diff --git a/tests/src/lib.rs b/tests/src/lib.rs index dfa7fb029c..91df74c7a1 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -10,7 +10,7 @@ pub mod image; pub mod infra; mod isolation; -use crate::infra::RunTestAsync; +use crate::infra::{AdapterReport, GpuTestConfiguration, TestInfo}; pub use self::image::ComparisonType; pub use ctor::ctor; @@ -329,11 +329,15 @@ impl TestParameters { } pub async fn initialize_test( - parameters: TestParameters, - expected_failure_reason: Option, + config: GpuTestConfiguration, + test_info: Option, adapter_index: usize, - test: RunTestAsync, ) { + // If we get information externally, skip based on that information before we do anything. + if let Some(TestInfo { skip: true, .. }) = test_info { + return; + } + // We don't actually care if it fails #[cfg(not(target_arch = "wasm32"))] let _ = env_logger::try_init(); @@ -345,13 +349,23 @@ pub async fn initialize_test( let (adapter, _surface_guard) = initialize_adapter(adapter_index); let adapter_info = adapter.get_info(); - let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities(); + let test_info = test_info.unwrap_or_else(|| { + let adapter_report = AdapterReport::from_adapter(&adapter); + TestInfo::from_configuration(&config, &adapter_report) + }); + + // We are now guaranteed to have information about this test, so skip if we need to. + if test_info.skip { + log::info!("TEST RESULT: SKIPPED"); + return; + } + let (device, queue) = pollster::block_on(initialize_device( &adapter, - parameters.required_features, - parameters.required_limits.clone(), + config.params.required_features, + config.params.required_limits.clone(), )); let context = TestingContext { @@ -359,13 +373,13 @@ pub async fn initialize_test( adapter_info, adapter_downlevel_capabilities, device, - device_features: parameters.required_features, - device_limits: parameters.required_limits.clone(), + device_features: config.params.required_features, + device_limits: config.params.required_limits.clone(), queue, }; // Run the test, and catch panics (possibly due to failed assertions). - let panicked = AssertUnwindSafe(test(context)) + let panicked = AssertUnwindSafe((config.test.as_ref().unwrap())(context)) .catch_unwind() .await .is_err(); @@ -388,9 +402,9 @@ pub async fn initialize_test( }; // Compare actual results against expectations. - match (failure_cause, expected_failure_reason) { + match (failure_cause, test_info.expected_failure_reason) { // The test passed, as expected. - (None, None) => {} + (None, None) => log::info!("TEST RESULT: PASSED"), // The test failed unexpectedly. (Some(cause), None) => { panic!("UNEXPECTED TEST FAILURE DUE TO {cause}") @@ -402,7 +416,7 @@ pub async fn initialize_test( // The test failed, as expected. (Some(cause), Some(reason_expected)) => { log::info!( - "EXPECTED FAILURE DUE TO {} (expected because of {:?})", + "TEST RESULT: EXPECTED FAILURE DUE TO {} (expected because of {:?})", cause, reason_expected ); @@ -412,6 +426,8 @@ pub async fn initialize_test( pub fn initialize_adapter(adapter_index: usize) -> (Adapter, Option) { let instance = initialize_instance(); + #[allow(unused_variables)] + let surface: wgpu::Surface; let surface_guard: Option; // Create a canvas iff we need a WebGL2RenderingContext to have a working device. @@ -449,7 +465,7 @@ pub fn initialize_adapter(adapter_index: usize) -> (Adapter, Option(device: &wgpu::Device, should_fail: bool, callback: impl FnOnc #[macro_export] macro_rules! gpu_test_main { () => { + #[cfg(target_arch = "wasm32")] + wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + + #[cfg(not(target_arch = "wasm32"))] fn main() -> $crate::infra::MainResult { $crate::infra::main() } + #[cfg(target_arch = "wasm32")] + fn main() {} }; } diff --git a/tests/tests/external_texture.rs b/tests/tests/external_texture.rs index af55427619..7b17b95efd 100644 --- a/tests/tests/external_texture.rs +++ b/tests/tests/external_texture.rs @@ -1,7 +1,6 @@ #![cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] use wasm_bindgen::JsCast; -use wasm_bindgen_test::*; use wgpu::ExternalImageSource; use wgpu_test::{fail_if, gpu_test, infra::GpuTestConfiguration, initialize_test}; diff --git a/tests/tests/gpu.rs b/tests/tests/gpu.rs index 8ee5a6fdea..9906cc6876 100644 --- a/tests/tests/gpu.rs +++ b/tests/tests/gpu.rs @@ -32,6 +32,4 @@ mod vertex_indices; mod write_texture; mod zero_init_texture_after_discard; -// wasm_bindgen_test_configure!(run_in_browser); - wgpu_test::gpu_test_main!(); diff --git a/wgpu-macros/src/lib.rs b/wgpu-macros/src/lib.rs index dd3bb71095..0f84bd5393 100644 --- a/wgpu-macros/src/lib.rs +++ b/wgpu-macros/src/lib.rs @@ -27,8 +27,14 @@ pub fn gpu_test(_attr: TokenStream, item: TokenStream) -> TokenStream { } #[cfg(target_arch = "wasm32")] - fn #test_name_webgl() { - // todo + #[wasm_bindgen_test::wasm_bindgen_test] + async fn #test_name_webgl() { + struct S; + + // Allow any type that can be converted to a GpuTestConfiguration + let test_config = ::wgpu_test::infra::GpuTestConfiguration::from(#expr).name_from_init_function_typename::(#ident_lower); + + ::wgpu_test::initialize_test(test_config, None, 0).await; } } .into() From 2c0c6ca590f89a2378a9536c02d87d60c6d382a3 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 5 Oct 2023 00:49:02 -0400 Subject: [PATCH 21/41] Fix wasm builds --- examples/common/src/framework.rs | 3 ++- examples/hello-compute/src/tests.rs | 5 ++--- tests/src/image.rs | 22 +++++----------------- tests/src/infra/mod.rs | 6 ++++-- tests/src/infra/report.rs | 1 + tests/src/infra/single.rs | 1 + tests/src/lib.rs | 4 ++-- tests/tests/external_texture.rs | 2 +- 8 files changed, 18 insertions(+), 26 deletions(-) diff --git a/examples/common/src/framework.rs b/examples/common/src/framework.rs index e612dade98..87ff9cdb47 100644 --- a/examples/common/src/framework.rs +++ b/examples/common/src/framework.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use std::time::Instant; #[cfg(target_arch = "wasm32")] use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas}; +use wgpu::{WasmNotSend, WasmNotSync}; use wgpu_test::infra::GpuTestConfiguration; use winit::{ event::{self, WindowEvent}, @@ -508,7 +509,7 @@ pub struct ExampleTestParams { pub _phantom: std::marker::PhantomData, } -impl From> for GpuTestConfiguration { +impl From> for GpuTestConfiguration { fn from(params: ExampleTestParams) -> Self { GpuTestConfiguration::new() .name(params.name) diff --git a/examples/hello-compute/src/tests.rs b/examples/hello-compute/src/tests.rs index 861ad4c32c..134670fbfb 100644 --- a/examples/hello-compute/src/tests.rs +++ b/examples/hello-compute/src/tests.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use super::*; use wgpu_test::{gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; @@ -55,6 +53,7 @@ static COMPUTE_OVERFLOW: GpuTestConfiguration = GpuTestConfiguration::new() } }); +#[cfg(not(target_arch = "wasm32"))] #[gpu_test] static MULTITHREADED_COMPUTE: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( @@ -73,7 +72,7 @@ static MULTITHREADED_COMPUTE: GpuTestConfiguration = GpuTestConfiguration::new() .skip(FailureCase::molten_vk()), ) .run_sync(|ctx| { - use std::{sync::mpsc, thread, time::Duration}; + use std::{sync::mpsc, sync::Arc, thread, time::Duration}; let ctx = Arc::new(ctx); diff --git a/tests/src/image.rs b/tests/src/image.rs index f76d5d70df..f23680b229 100644 --- a/tests/src/image.rs +++ b/tests/src/image.rs @@ -41,11 +41,6 @@ async fn read_png(path: impl AsRef, width: u32, height: u32) -> Option, width: u32, height: u32) -> Option> { - unimplemented!("") -} - #[cfg(not(target_arch = "wasm32"))] async fn write_png( path: impl AsRef, @@ -65,20 +60,11 @@ async fn write_png( writer.write_image_data(data).unwrap(); } -#[cfg(target_arch = "wasm32")] -async fn write_png( - path: impl AsRef, - width: u32, - height: u32, - data: &[u8], - compression: png::Compression, -) { -} - pub fn calc_difference(lhs: u8, rhs: u8) -> u8 { (lhs as i16 - rhs as i16).unsigned_abs() as u8 } +#[cfg_attr(target_arch = "wasm32", allow(unused))] fn add_alpha(input: &[u8]) -> Vec { input .chunks_exact(3) @@ -86,6 +72,7 @@ fn add_alpha(input: &[u8]) -> Vec { .collect() } +#[cfg_attr(target_arch = "wasm32", allow(unused))] fn remove_alpha(input: &[u8]) -> Vec { input .chunks_exact(4) @@ -267,7 +254,7 @@ pub async fn compare_image_output( #[cfg(target_arch = "wasm32")] pub async fn compare_image_output( path: impl AsRef + AsRef, - backend: wgpu::Backend, + adapter_info: &wgt::AdapterInfo, width: u32, height: u32, test_with_alpha: &[u8], @@ -275,10 +262,11 @@ pub async fn compare_image_output( ) { #[cfg(target_arch = "wasm32")] { - let _ = (path, width, height, test_with_alpha, checks); + let _ = (path, adapter_info, width, height, test_with_alpha, checks); } } +#[cfg_attr(target_arch = "wasm32", allow(unused))] fn sanitize_for_path(s: &str) -> String { s.chars() .map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '_' }) diff --git a/tests/src/infra/mod.rs b/tests/src/infra/mod.rs index 4ad3f520c5..0f14c46226 100644 --- a/tests/src/infra/mod.rs +++ b/tests/src/infra/mod.rs @@ -1,7 +1,7 @@ -use anyhow::Context; +#[cfg(not(target_arch = "wasm32"))] +use parking_lot::Mutex; pub use params::{GpuTestConfiguration, RunTestAsync}; -use parking_lot::Mutex; pub use report::AdapterReport; pub use single::{SingleTest, TestInfo}; @@ -16,6 +16,8 @@ pub type MainResult = anyhow::Result<()>; #[cfg(not(target_arch = "wasm32"))] pub fn main() -> MainResult { + use anyhow::Context; + let config_text = &std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) .context("Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?")?; diff --git a/tests/src/infra/report.rs b/tests/src/infra/report.rs index e79f813b7f..38c54fee57 100644 --- a/tests/src/infra/report.rs +++ b/tests/src/infra/report.rs @@ -11,6 +11,7 @@ pub struct GpuReport { } impl GpuReport { + #[cfg_attr(target_arch = "wasm32", allow(unused))] pub fn from_json(file: &str) -> serde_json::Result { serde_json::from_str(file) } diff --git a/tests/src/infra/single.rs b/tests/src/infra/single.rs index ea710a0829..ddd384f4cd 100644 --- a/tests/src/infra/single.rs +++ b/tests/src/infra/single.rs @@ -97,6 +97,7 @@ impl TestInfo { } } +#[cfg_attr(target_arch = "wasm32", allow(unused))] pub struct SingleTest { name: String, future: SingleTestFuture, diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 91df74c7a1..be14fb958a 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -427,7 +427,7 @@ pub async fn initialize_test( pub fn initialize_adapter(adapter_index: usize) -> (Adapter, Option) { let instance = initialize_instance(); #[allow(unused_variables)] - let surface: wgpu::Surface; + let _surface: wgpu::Surface; let surface_guard: Option; // Create a canvas iff we need a WebGL2RenderingContext to have a working device. @@ -465,7 +465,7 @@ pub fn initialize_adapter(adapter_index: usize) -> (Adapter, Option Date: Thu, 5 Oct 2023 01:04:34 -0400 Subject: [PATCH 22/41] Fix more wasm builds --- tests/src/infra/params.rs | 28 ++++++++++++++++++---------- tests/src/lib.rs | 31 +++++++++++++++++++------------ tests/tests/device.rs | 2 +- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/tests/src/infra/params.rs b/tests/src/infra/params.rs index c2feb79495..c014d0430f 100644 --- a/tests/src/infra/params.rs +++ b/tests/src/infra/params.rs @@ -1,14 +1,22 @@ use std::{future::Future, pin::Pin, sync::Arc}; -use wgt::{WasmNotSend, WasmNotSync}; - use crate::{TestParameters, TestingContext}; -#[cfg(not(target_arch = "wasm32"))] -pub type RunTestAsync = - Arc Pin + Send + Sync>> + Send + Sync>; -#[cfg(target_arch = "wasm32")] -pub type RunTestAsync = Arc Pin>>>; +cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + pub type RunTestAsync = Arc Pin>>>; + + // We can't use WasmNonSend and WasmNonSync here, as we need these to not require Send/Sync + // even with the `fragile-send-sync-non-atomic-wasm` enabled. + pub trait RunTestSendSync {} + impl RunTestSendSync for T {} + } else { + pub type RunTestAsync = Arc Pin + Send + Sync>> + Send + Sync>; + + pub trait RunTestSendSync: Send + Sync {} + impl RunTestSendSync for T where T: Send + Sync {} + } +} #[derive(Clone)] pub struct GpuTestConfiguration { @@ -73,7 +81,7 @@ impl GpuTestConfiguration { pub fn run_sync( self, - test: impl Fn(TestingContext) + Copy + WasmNotSend + WasmNotSync + 'static, + test: impl Fn(TestingContext) + Copy + RunTestSendSync + 'static, ) -> Self { Self { test: Some(Arc::new(move |ctx| Box::pin(async move { test(ctx) }))), @@ -83,8 +91,8 @@ impl GpuTestConfiguration { pub fn run_async(self, test: F) -> Self where - F: Fn(TestingContext) -> R + WasmNotSend + WasmNotSync + 'static, - R: Future + WasmNotSend + WasmNotSync + 'static, + F: Fn(TestingContext) -> R + RunTestSendSync + 'static, + R: Future + RunTestSendSync + 'static, { Self { test: Some(Arc::new(move |ctx| Box::pin(test(ctx)))), diff --git a/tests/src/lib.rs b/tests/src/lib.rs index be14fb958a..38e0329827 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -16,10 +16,7 @@ pub use self::image::ComparisonType; pub use ctor::ctor; pub use wgpu_macros::gpu_test; -#[cfg(all( - target_arch = "wasm32", - any(target_os = "emscripten", feature = "webgl") -))] +#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] const CANVAS_ID: &str = "test-canvas"; async fn initialize_device( @@ -346,7 +343,7 @@ pub async fn initialize_test( let _test_guard = isolation::OneTestPerProcessGuard::new(); - let (adapter, _surface_guard) = initialize_adapter(adapter_index); + let (adapter, _surface_guard) = initialize_adapter(adapter_index).await; let adapter_info = adapter.get_info(); let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities(); @@ -388,8 +385,11 @@ pub async fn initialize_test( cfg_if::cfg_if!( if #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] { let canary_set = wgpu::hal::VALIDATION_CANARY.get_and_reset(); - } else { + } else if #[cfg(all(target_arch = "wasm32", feature = "webgl"))] { let canary_set = _surface_guard.unwrap().check_for_unreported_errors(); + } else { + // TODO: WebGPU + let canary_set = false; } ); @@ -424,7 +424,7 @@ pub async fn initialize_test( } } -pub fn initialize_adapter(adapter_index: usize) -> (Adapter, Option) { +pub async fn initialize_adapter(adapter_index: usize) -> (Adapter, Option) { let instance = initialize_instance(); #[allow(unused_variables)] let _surface: wgpu::Surface; @@ -474,11 +474,18 @@ pub fn initialize_adapter(adapter_index: usize) -> (Adapter, Option Date: Wed, 11 Oct 2023 16:30:56 -0400 Subject: [PATCH 23/41] Clean up canvas creation --- Cargo.lock | 133 ++++++++++++++++++++++------------------------- tests/src/lib.rs | 42 +-------------- 2 files changed, 63 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6a15a1695..c4a9322450 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,9 +46,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -213,7 +213,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -331,14 +331,14 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -422,7 +422,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -633,7 +633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583" dependencies = [ "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -737,7 +737,7 @@ checksum = "3c65c2ffdafc1564565200967edc4851c7b55422d3913466688907efd05ea26f" dependencies = [ "deno-proc-macro-rules-macros", "proc-macro2", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -749,7 +749,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -804,7 +804,7 @@ dependencies = [ "regex", "strum", "strum_macros", - "syn 2.0.37", + "syn 2.0.38", "thiserror", ] @@ -962,25 +962,14 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "event-listener" version = "2.5.3" @@ -1083,7 +1072,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1221,7 +1210,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1521,7 +1510,7 @@ dependencies = [ "byteorder", "color_quant", "num-rational", - "num-traits 0.2.16", + "num-traits 0.2.17", "png", ] @@ -1573,9 +1562,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] @@ -1626,7 +1615,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1637,9 +1626,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libloading" @@ -1684,9 +1673,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" @@ -1790,7 +1779,7 @@ dependencies = [ "hexf-parse", "indexmap", "log", - "num-traits 0.2.16", + "num-traits 0.2.17", "petgraph", "pp-rs", "rustc-hash", @@ -1899,7 +1888,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ba869e17168793186c10ca82c7079a4ffdeac4f1a7d9e755b9491c028180e40" dependencies = [ - "num-traits 0.2.16", + "num-traits 0.2.17", "rand 0.7.3", "rand_xorshift", ] @@ -1922,7 +1911,7 @@ checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", - "num-traits 0.2.16", + "num-traits 0.2.17", "rand 0.8.5", ] @@ -1933,7 +1922,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] @@ -1944,7 +1933,7 @@ checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", "num-integer", - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] @@ -1953,14 +1942,14 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" dependencies = [ - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -2014,7 +2003,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2208,7 +2197,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2251,7 +2240,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2306,9 +2295,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -2425,9 +2414,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.6" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" +checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87" dependencies = [ "aho-corasick", "memchr", @@ -2437,9 +2426,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.9" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" dependencies = [ "aho-corasick", "memchr", @@ -2448,9 +2437,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "56d84fdd47036b038fc80dd333d10b6aab10d5d31f4a366e20014def75328d33" [[package]] name = "renderdoc-sys" @@ -2497,14 +2486,14 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.19", + "semver 1.0.20", ] [[package]] name = "rustix" -version = "0.38.17" +version = "0.38.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" +checksum = "5a74ee2d7c2581cd139b42447d7d9389b889bdaad3a73f1ebb16f2a3237bb19c" dependencies = [ "bitflags 2.4.0", "errno", @@ -2582,9 +2571,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "semver-parser" @@ -2618,7 +2607,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2780,7 +2769,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" dependencies = [ "bitflags 1.3.2", - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] @@ -2820,7 +2809,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2836,9 +2825,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -2871,7 +2860,7 @@ checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2950,9 +2939,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", "bytes", @@ -2975,7 +2964,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3191,7 +3180,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-shared", ] @@ -3225,7 +3214,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3614,7 +3603,7 @@ version = "0.17.0" dependencies = [ "heck", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -4089,9 +4078,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "037711d82167854aff2018dfd193aa0fef5370f456732f0d5a0c59b0f1b4b907" dependencies = [ "memchr", ] diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 38e0329827..79a6527a65 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -16,9 +16,6 @@ pub use self::image::ComparisonType; pub use ctor::ctor; pub use wgpu_macros::gpu_test; -#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] -const CANVAS_ID: &str = "test-canvas"; - async fn initialize_device( adapter: &Adapter, features: Features, @@ -446,28 +443,9 @@ pub async fn initialize_adapter(adapter_index: usize) -> (Adapter, Option raw_window_handle::RawWindowHandle { - raw_window_handle::RawWindowHandle::Web({ - let mut handle = raw_window_handle::WebWindowHandle::empty(); - handle.id = 1; - handle - }) - } - } - unsafe impl raw_window_handle::HasRawDisplayHandle for WindowHandle { - fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle { - raw_window_handle::RawDisplayHandle::Web( - raw_window_handle::WebDisplayHandle::empty(), - ) - } - } - _surface = unsafe { instance - .create_surface(&WindowHandle) + .create_surface_from_canvas(&canvas) .expect("could not create surface from canvas") }; @@ -545,26 +523,10 @@ pub fn create_html_canvas() -> web_sys::HtmlCanvasElement { web_sys::window() .and_then(|win| win.document()) .and_then(|doc| { - let body = doc.body().unwrap(); let canvas = doc.create_element("Canvas").unwrap(); - canvas.set_attribute("data-raw-handle", "1").unwrap(); - canvas.set_id(CANVAS_ID); - body.append_child(&canvas).unwrap(); canvas.dyn_into::().ok() }) - .expect("couldn't append canvas to document body") -} - -#[cfg(all( - target_arch = "wasm32", - any(target_os = "emscripten", feature = "webgl") -))] -fn delete_html_canvas() { - if let Some(document) = web_sys::window().and_then(|win| win.document()) { - if let Some(element) = document.get_element_by_id(CANVAS_ID) { - element.remove(); - } - }; + .expect("couldn't create canvas") } // Run some code in an error scope and assert that validation fails. From 7901320065ec973d814898f650ed214325599646 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Wed, 11 Oct 2023 16:47:46 -0400 Subject: [PATCH 24/41] Fix compilation --- Cargo.lock | 4 +-- examples/hello-synchronization/Cargo.toml | 2 +- examples/hello-synchronization/src/main.rs | 4 +-- examples/hello-synchronization/src/tests.rs | 29 +++++++++------------ examples/mipmap/src/main.rs | 2 +- 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca9ecbca66..6e94cfc957 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -958,7 +958,7 @@ checksum = "3fe2568f851fd6144a45fa91cfed8fe5ca8fc0b56ba6797bfc1ed2771b90e37c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -3584,7 +3584,7 @@ dependencies = [ "console_error_panic_hook", "console_log", "env_logger", - "futures-intrusive", + "flume", "log", "pollster", "wasm-bindgen-futures", diff --git a/examples/hello-synchronization/Cargo.toml b/examples/hello-synchronization/Cargo.toml index fb31c55cad..ce3ed56e7c 100644 --- a/examples/hello-synchronization/Cargo.toml +++ b/examples/hello-synchronization/Cargo.toml @@ -13,7 +13,7 @@ path = "src/main.rs" [dependencies] bytemuck.workspace = true env_logger.workspace = true -futures-intrusive.workspace = true +flume.workspace = true log.workspace = true pollster.workspace = true wgpu.workspace = true diff --git a/examples/hello-synchronization/src/main.rs b/examples/hello-synchronization/src/main.rs index 2a8ecac914..36ce83f00a 100644 --- a/examples/hello-synchronization/src/main.rs +++ b/examples/hello-synchronization/src/main.rs @@ -179,10 +179,10 @@ async fn get_data( ); queue.submit(Some(command_encoder.finish())); let buffer_slice = staging_buffer.slice(..); - let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel(); + let (sender, receiver) = flume::bounded(1); buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap()); device.poll(wgpu::Maintain::Wait); - receiver.receive().await.unwrap().unwrap(); + receiver.recv_async().await.unwrap().unwrap(); output.copy_from_slice(bytemuck::cast_slice(&buffer_slice.get_mapped_range()[..])); staging_buffer.unmap(); } diff --git a/examples/hello-synchronization/src/tests.rs b/examples/hello-synchronization/src/tests.rs index c626938233..72cd6a49c8 100644 --- a/examples/hello-synchronization/src/tests.rs +++ b/examples/hello-synchronization/src/tests.rs @@ -1,23 +1,18 @@ use super::*; -use pollster::FutureExt; -use wgpu_test::{initialize_test, TestParameters}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters}; -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - -#[test] -#[wasm_bindgen_test::wasm_bindgen_test] -fn hello_synchronization_test_results() { - initialize_test( +#[gpu_test] +static SYNC: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( // Taken from hello-compute tests. TestParameters::default() .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) .limits(wgpu::Limits::downlevel_defaults()), - |ctx| { - let ExecuteResults { - patient_workgroup_results, - hasty_workgroup_results: _, - } = execute(&ctx.device, &ctx.queue, ARR_SIZE).block_on(); - assert_eq!(patient_workgroup_results, [16_u32; ARR_SIZE]); - }, - ); -} + ) + .run_async(|ctx| async move { + let ExecuteResults { + patient_workgroup_results, + hasty_workgroup_results: _, + } = execute(&ctx.device, &ctx.queue, ARR_SIZE).await; + assert_eq!(patient_workgroup_results, [16_u32; ARR_SIZE]); + }); diff --git a/examples/mipmap/src/main.rs b/examples/mipmap/src/main.rs index 2db30fa475..f6c1c66edd 100644 --- a/examples/mipmap/src/main.rs +++ b/examples/mipmap/src/main.rs @@ -537,7 +537,7 @@ static TEST_QUERY: wgpu_example::framework::ExampleTestParams = optional_features: QUERY_FEATURES, base_test_parameters: wgpu_test::TestParameters::default() .expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::GL)), - comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + comparisons: &[wgpu_test::ComparisonType::Mean(0.025)], _phantom: std::marker::PhantomData::, }; From 3c119923c5fdb72875dcdee3fbf72ce19508768d Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Wed, 11 Oct 2023 16:52:09 -0400 Subject: [PATCH 25/41] Fix wasm-webgl compilation --- tests/src/lib.rs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 20066ad1ce..baf44812b2 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -443,11 +443,9 @@ pub async fn initialize_adapter(adapter_index: usize) -> (Adapter, Option web_sys::HtmlCanvasElement { use wasm_bindgen::JsCast; From 3e7f28a0b571f56eef5505023442b1cd4ef091dc Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Wed, 11 Oct 2023 17:09:21 -0400 Subject: [PATCH 26/41] Fix webgl failure --- tests/tests/occlusion_query/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tests/occlusion_query/mod.rs b/tests/tests/occlusion_query/mod.rs index 6c987342e8..79e6d23184 100644 --- a/tests/tests/occlusion_query/mod.rs +++ b/tests/tests/occlusion_query/mod.rs @@ -1,9 +1,9 @@ use std::borrow::Cow; -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; #[gpu_test] static OCCLUSION_QUERY: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().expect_fail(FailureCase::webgl2())) .run_sync(|ctx| { // Create depth texture let depth_texture = ctx.device.create_texture(&wgpu::TextureDescriptor { From 6100ba5e3acb884027af43ffb83377abbdda49d8 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Wed, 11 Oct 2023 17:52:49 -0400 Subject: [PATCH 27/41] Fix CI? --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7067271a03..e202ef0e41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -291,6 +291,11 @@ jobs: with: tool: cargo-nextest,cargo-llvm-cov + - name: Install Repo MSRV toolchain + run: | + rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal + cargo -V + - name: install swiftshader if: matrix.os == 'ubuntu-22.04' shell: bash From 5cd6330c3194ee9a75ea660449bbd705490d4f42 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Wed, 11 Oct 2023 19:12:14 -0400 Subject: [PATCH 28/41] Reorg --- examples/common/src/framework.rs | 2 +- examples/hello-compute/src/tests.rs | 2 +- examples/hello-synchronization/src/tests.rs | 2 +- examples/timestamp-queries/src/main.rs | 2 +- tests/src/{infra/params.rs => config.rs} | 6 +- tests/src/infra/mod.rs | 47 -- tests/src/infra/single.rs | 140 ----- tests/src/init.rs | 121 ++++ tests/src/lib.rs | 531 +----------------- tests/src/native.rs | 83 +++ tests/src/params.rs | 361 ++++++++++++ tests/src/{infra => }/report.rs | 10 +- tests/src/run.rs | 121 ++++ tests/tests/bind_group_layout_dedup.rs | 2 +- tests/tests/buffer.rs | 4 +- tests/tests/buffer_copy.rs | 2 +- tests/tests/buffer_usages.rs | 2 +- tests/tests/clear_texture.rs | 2 +- tests/tests/create_surface_error.rs | 13 +- tests/tests/device.rs | 2 +- tests/tests/encoder.rs | 2 +- tests/tests/external_texture.rs | 2 +- tests/tests/instance.rs | 2 +- tests/tests/occlusion_query/mod.rs | 2 +- tests/tests/partially_bounded_arrays/mod.rs | 2 +- tests/tests/pipeline.rs | 2 +- tests/tests/poll.rs | 2 +- tests/tests/query_set.rs | 2 +- tests/tests/queue_transfer.rs | 2 +- tests/tests/regression/issue_3457.rs | 2 +- tests/tests/regression/issue_4024.rs | 2 +- tests/tests/regression/issue_4122.rs | 2 +- tests/tests/resource_descriptor_accessor.rs | 2 +- tests/tests/resource_error.rs | 2 +- tests/tests/scissor_tests/mod.rs | 2 +- tests/tests/shader/numeric_builtins.rs | 2 +- tests/tests/shader/struct_layout.rs | 2 +- tests/tests/shader/zero_init_workgroup_mem.rs | 2 +- tests/tests/shader_primitive_index/mod.rs | 2 +- tests/tests/shader_view_format/mod.rs | 2 +- tests/tests/texture_bounds.rs | 2 +- tests/tests/transfer.rs | 2 +- tests/tests/vertex_indices/mod.rs | 4 +- tests/tests/write_texture.rs | 2 +- .../tests/zero_init_texture_after_discard.rs | 2 +- wgpu-macros/src/lib.rs | 8 +- 46 files changed, 757 insertions(+), 758 deletions(-) rename tests/src/{infra/params.rs => config.rs} (96%) delete mode 100644 tests/src/infra/mod.rs delete mode 100644 tests/src/infra/single.rs create mode 100644 tests/src/init.rs create mode 100644 tests/src/native.rs create mode 100644 tests/src/params.rs rename tests/src/{infra => }/report.rs (77%) create mode 100644 tests/src/run.rs diff --git a/examples/common/src/framework.rs b/examples/common/src/framework.rs index ecc574291c..9e9e949ef4 100644 --- a/examples/common/src/framework.rs +++ b/examples/common/src/framework.rs @@ -8,7 +8,7 @@ use wasm_bindgen::prelude::*; #[cfg(target_arch = "wasm32")] use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas}; use wgpu::{WasmNotSend, WasmNotSync}; -use wgpu_test::infra::GpuTestConfiguration; +use wgpu_test::GpuTestConfiguration; use winit::{ event::{self, WindowEvent}, event_loop::{ControlFlow, EventLoop}, diff --git a/examples/hello-compute/src/tests.rs b/examples/hello-compute/src/tests.rs index 134670fbfb..e93fafbe77 100644 --- a/examples/hello-compute/src/tests.rs +++ b/examples/hello-compute/src/tests.rs @@ -1,5 +1,5 @@ use super::*; -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; #[gpu_test] static COMPUTE_1: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/examples/hello-synchronization/src/tests.rs b/examples/hello-synchronization/src/tests.rs index 72cd6a49c8..756289a363 100644 --- a/examples/hello-synchronization/src/tests.rs +++ b/examples/hello-synchronization/src/tests.rs @@ -1,5 +1,5 @@ use super::*; -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; #[gpu_test] static SYNC: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/examples/timestamp-queries/src/main.rs b/examples/timestamp-queries/src/main.rs index 466082627c..d3e2676ad8 100644 --- a/examples/timestamp-queries/src/main.rs +++ b/examples/timestamp-queries/src/main.rs @@ -419,7 +419,7 @@ fn main() { #[cfg(test)] mod tests { - use wgpu_test::{gpu_test, infra::GpuTestConfiguration}; + use wgpu_test::{gpu_test, GpuTestConfiguration}; use crate::{submit_render_and_compute_pass_with_queries, QueryResults}; diff --git a/tests/src/infra/params.rs b/tests/src/config.rs similarity index 96% rename from tests/src/infra/params.rs rename to tests/src/config.rs index c014d0430f..8a99efc7d1 100644 --- a/tests/src/infra/params.rs +++ b/tests/src/config.rs @@ -20,9 +20,9 @@ cfg_if::cfg_if! { #[derive(Clone)] pub struct GpuTestConfiguration { - pub name: String, - pub params: TestParameters, - pub test: Option, + pub(crate) name: String, + pub(crate) params: TestParameters, + pub(crate) test: Option, } impl GpuTestConfiguration { diff --git a/tests/src/infra/mod.rs b/tests/src/infra/mod.rs deleted file mode 100644 index 0f14c46226..0000000000 --- a/tests/src/infra/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -#[cfg(not(target_arch = "wasm32"))] -use parking_lot::Mutex; - -pub use params::{GpuTestConfiguration, RunTestAsync}; -pub use report::AdapterReport; -pub use single::{SingleTest, TestInfo}; - -mod params; -mod report; -mod single; - -#[cfg(not(target_arch = "wasm32"))] -pub static TEST_LIST: Mutex> = Mutex::new(Vec::new()); - -pub type MainResult = anyhow::Result<()>; - -#[cfg(not(target_arch = "wasm32"))] -pub fn main() -> MainResult { - use anyhow::Context; - - let config_text = - &std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) - .context("Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?")?; - let report = - report::GpuReport::from_json(config_text).context("Could not pare .gpuconfig JSON")?; - - let mut test_guard = TEST_LIST.lock(); - execute_native(test_guard.drain(..).flat_map(|test| { - report - .devices - .iter() - .enumerate() - .map(move |(adapter_index, adapter)| { - SingleTest::from_configuration(test.clone(), adapter, adapter_index) - }) - })); - - Ok(()) -} - -#[cfg(not(target_arch = "wasm32"))] -fn execute_native(tests: impl IntoIterator) { - let args = libtest_mimic::Arguments::from_args(); - let trials = tests.into_iter().map(SingleTest::into_trial).collect(); - - libtest_mimic::run(&args, trials).exit_if_failed(); -} diff --git a/tests/src/infra/single.rs b/tests/src/infra/single.rs deleted file mode 100644 index ddd384f4cd..0000000000 --- a/tests/src/infra/single.rs +++ /dev/null @@ -1,140 +0,0 @@ -use std::{future::Future, pin::Pin}; - -use arrayvec::ArrayVec; - -use crate::{ - infra::{report::AdapterReport, GpuTestConfiguration}, - initialize_test, FailureReasons, -}; - -#[cfg(target_arch = "wasm32")] -type SingleTestFuture = Pin>>; -#[cfg(not(target_arch = "wasm32"))] -type SingleTestFuture = Pin + Send + Sync>>; - -pub struct TestInfo { - pub skip: bool, - pub expected_failure_reason: Option, - pub running_msg: String, -} - -impl TestInfo { - pub fn from_configuration(test: &GpuTestConfiguration, adapter: &AdapterReport) -> Self { - // Figure out if we should skip the test and if so, why. - let mut skipped_reasons: ArrayVec<_, 4> = ArrayVec::new(); - let missing_features = test.params.required_features - adapter.features; - if !missing_features.is_empty() { - skipped_reasons.push("Features"); - } - - if !test.params.required_limits.check_limits(&adapter.limits) { - skipped_reasons.push("Limits"); - } - - let missing_downlevel_flags = - test.params.required_downlevel_caps.flags - adapter.downlevel_caps.flags; - if !missing_downlevel_flags.is_empty() { - skipped_reasons.push("Downlevel Flags"); - } - - if test.params.required_downlevel_caps.shader_model > adapter.downlevel_caps.shader_model { - skipped_reasons.push("Shader Model"); - } - - // Produce a lower-case version of the adapter info, for comparison against - // `parameters.skips` and `parameters.failures`. - let adapter_lowercase_info = wgt::AdapterInfo { - name: adapter.info.name.to_lowercase(), - driver: adapter.info.driver.to_lowercase(), - ..adapter.info.clone() - }; - - // Check if we should skip the test altogether. - let skip_reason = test - .params - .skips - .iter() - .find_map(|case| case.applies_to(&adapter_lowercase_info)); - - let expected_failure_reason = test - .params - .failures - .iter() - .find_map(|case| case.applies_to(&adapter_lowercase_info)); - - let mut skip = false; - let running_msg = if let Some(reasons) = skip_reason { - skip = true; - - let names: ArrayVec<_, 4> = reasons.iter_names().map(|(name, _)| name).collect(); - let names_text = names.join(" | "); - - format!("Skipped Failure: {}", names_text) - } else if !skipped_reasons.is_empty() { - skip = true; - format!("Skipped: {}", skipped_reasons.join(" | ")) - } else if let Some(failure_resasons) = expected_failure_reason { - if cfg!(target_arch = "wasm32") { - skip = true; - } - - let names: ArrayVec<_, 4> = failure_resasons - .iter_names() - .map(|(name, _)| name) - .collect(); - let names_text = names.join(" | "); - - format!("Executed Failure: {}", names_text) - } else { - String::from("Executed") - }; - - Self { - skip, - expected_failure_reason, - running_msg, - } - } -} - -#[cfg_attr(target_arch = "wasm32", allow(unused))] -pub struct SingleTest { - name: String, - future: SingleTestFuture, -} - -impl SingleTest { - pub fn from_configuration( - config: GpuTestConfiguration, - adapter: &AdapterReport, - adapter_index: usize, - ) -> Self { - let backend = &adapter.info.backend; - let device_name = &adapter.info.name; - - let test_info = TestInfo::from_configuration(&config, adapter); - - let full_name = format!( - "[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}", - running_msg = test_info.running_msg, - base_name = config.name, - ); - Self { - name: full_name, - future: Box::pin(async move { - if test_info.skip { - return; - } - initialize_test(config, Some(test_info), adapter_index).await; - }), - } - } - - #[cfg(not(target_arch = "wasm32"))] - pub fn into_trial(self) -> libtest_mimic::Trial { - libtest_mimic::Trial::test(self.name, || { - pollster::block_on(self.future); - Ok(()) - }) - } -} diff --git a/tests/src/init.rs b/tests/src/init.rs new file mode 100644 index 0000000000..d257547997 --- /dev/null +++ b/tests/src/init.rs @@ -0,0 +1,121 @@ +use wgpu::{Adapter, Device, Instance, Queue}; +use wgt::{Backends, Features, Limits}; + +pub fn initialize_instance() -> Instance { + let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(Backends::all); + let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(); + let gles_minor_version = wgpu::util::gles_minor_version_from_env().unwrap_or_default(); + Instance::new(wgpu::InstanceDescriptor { + backends, + flags: wgpu::InstanceFlags::debugging().with_env(), + dx12_shader_compiler, + gles_minor_version, + }) +} + +pub async fn initialize_adapter(adapter_index: usize) -> (Adapter, Option) { + let instance = initialize_instance(); + #[allow(unused_variables)] + let _surface: wgpu::Surface; + let surface_guard: Option; + + // Create a canvas iff we need a WebGL2RenderingContext to have a working device. + #[cfg(not(all( + target_arch = "wasm32", + any(target_os = "emscripten", feature = "webgl") + )))] + { + 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 = instance + .create_surface_from_canvas(canvas.clone()) + .expect("could not create surface from canvas"); + + surface_guard = Some(SurfaceGuard { canvas }); + } + + cfg_if::cfg_if! { + if #[cfg(any(not(target_arch = "wasm32"), feature = "webgl"))] { + let adapter_iter = instance.enumerate_adapters(wgpu::Backends::all()); + let adapter_count = adapter_iter.len(); + let adapter = adapter_iter.into_iter() + .nth(adapter_index) + .unwrap_or_else(|| panic!("Tried to get index {adapter_index} adapter, but adapter list was only {adapter_count} long. Is .gpuconfig out of date?")); + } else { + assert_eq!(adapter_index, 0); + let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions::default()).await.unwrap(); + } + } + + log::info!("Testing using adapter: {:#?}", adapter.get_info()); + + (adapter, surface_guard) +} + +pub async fn initialize_device( + adapter: &Adapter, + features: Features, + limits: Limits, +) -> (Device, Queue) { + let bundle = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features, + limits, + }, + None, + ) + .await; + + match bundle { + Ok(b) => b, + Err(e) => panic!("Failed to initialize device: {e}"), + } +} + +#[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 + } +} diff --git a/tests/src/lib.rs b/tests/src/lib.rs index baf44812b2..d8fdaee858 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,523 +1,26 @@ //! This module contains common test-only code that needs to be shared between the examples and the tests. -use std::panic::AssertUnwindSafe; - -use futures_lite::FutureExt; -use wgpu::{Adapter, Device, DownlevelFlags, Instance, Queue}; -use wgt::{Backends, DeviceDescriptor, DownlevelCapabilities, Features, Limits}; - +mod config; pub mod image; -pub mod infra; +mod init; mod isolation; +pub mod native; +mod params; +mod report; +mod run; -use crate::infra::{AdapterReport, GpuTestConfiguration, TestInfo}; +#[cfg(target_arch = "wasm32")] +pub use init::initialize_html_canvas; pub use self::image::ComparisonType; +pub use config::GpuTestConfiguration; +#[doc(hidden)] pub use ctor::ctor; +pub use init::{initialize_instance, initialize_adapter, initialize_device}; +pub use params::{FailureCase, FailureReasons, TestParameters}; +pub use run::{execute_test, TestingContext}; pub use wgpu_macros::gpu_test; -async fn initialize_device( - adapter: &Adapter, - features: Features, - limits: Limits, -) -> (Device, Queue) { - let bundle = adapter - .request_device( - &DeviceDescriptor { - label: None, - features, - limits, - }, - None, - ) - .await; - - match bundle { - Ok(b) => b, - Err(e) => panic!("Failed to initialize device: {e}"), - } -} - -pub struct TestingContext { - pub adapter: Adapter, - pub adapter_info: wgt::AdapterInfo, - pub adapter_downlevel_capabilities: wgt::DownlevelCapabilities, - pub device: Device, - pub device_features: wgt::Features, - pub device_limits: wgt::Limits, - pub queue: Queue, -} - -fn lowest_downlevel_properties() -> DownlevelCapabilities { - DownlevelCapabilities { - flags: wgt::DownlevelFlags::empty(), - limits: wgt::DownlevelLimits {}, - shader_model: wgt::ShaderModel::Sm2, - } -} - -/// Conditions under which a test should fail or be skipped. -/// -/// By passing a `FailureCase` to [`TestParameters::expect_fail`], you can -/// mark a test as expected to fail under the indicated conditions. By -/// passing it to [`TestParameters::skip`], you can request that the -/// test be skipped altogether. -/// -/// If a field is `None`, then that field does not restrict matches. For -/// example: -/// -/// ``` -/// # use wgpu_test::FailureCase; -/// FailureCase { -/// backends: Some(wgpu::Backends::DX11 | wgpu::Backends::DX12), -/// vendor: None, -/// adapter: Some("RTX"), -/// driver: None, -/// } -/// # ; -/// ``` -/// -/// This applies to all cards with `"RTX'` in their name on either -/// Direct3D backend, no matter the vendor ID or driver name. -/// -/// The strings given here need only appear as a substring in the -/// corresponding [`AdapterInfo`] fields. The comparison is -/// case-insensitive. -/// -/// The default value of `FailureCase` applies to any test case. That -/// is, there are no criteria to constrain the match. -/// -/// [`AdapterInfo`]: wgt::AdapterInfo -#[derive(Default, Clone)] -pub struct FailureCase { - /// Backends expected to fail, or `None` for any backend. - /// - /// If this is `None`, or if the test is using one of the backends - /// in `backends`, then this `FailureCase` applies. - pub backends: Option, - - /// Vendor expected to fail, or `None` for any vendor. - /// - /// If `Some`, this must match [`AdapterInfo::device`], which is - /// usually the PCI device id. Otherwise, this `FailureCase` - /// applies regardless of vendor. - /// - /// [`AdapterInfo::device`]: wgt::AdapterInfo::device - pub vendor: Option, - - /// Name of adaper expected to fail, or `None` for any adapter name. - /// - /// If this is `Some(s)` and `s` is a substring of - /// [`AdapterInfo::name`], then this `FailureCase` applies. If - /// this is `None`, the adapter name isn't considered. - /// - /// [`AdapterInfo::name`]: wgt::AdapterInfo::name - pub adapter: Option<&'static str>, - - /// Name of driver expected to fail, or `None` for any driver name. - /// - /// If this is `Some(s)` and `s` is a substring of - /// [`AdapterInfo::driver`], then this `FailureCase` applies. If - /// this is `None`, the driver name isn't considered. - /// - /// [`AdapterInfo::driver`]: wgt::AdapterInfo::driver - pub driver: Option<&'static str>, -} - -impl FailureCase { - /// This case applies to all tests. - pub fn always() -> Self { - FailureCase::default() - } - - /// This case applies to no tests. - pub fn never() -> Self { - FailureCase { - backends: Some(wgpu::Backends::empty()), - ..FailureCase::default() - } - } - - /// Tests running on any of the given backends. - pub fn backend(backends: wgpu::Backends) -> Self { - FailureCase { - backends: Some(backends), - ..FailureCase::default() - } - } - - /// Tests running on `adapter`. - /// - /// For this case to apply, the `adapter` string must appear as a substring - /// of the adapter's [`AdapterInfo::name`]. The comparison is - /// case-insensitive. - /// - /// [`AdapterInfo::name`]: wgt::AdapterInfo::name - pub fn adapter(adapter: &'static str) -> Self { - FailureCase { - adapter: Some(adapter), - ..FailureCase::default() - } - } - - /// Tests running on `backend` and `adapter`. - /// - /// For this case to apply, the test must be using an adapter for one of the - /// given `backend` bits, and `adapter` string must appear as a substring of - /// the adapter's [`AdapterInfo::name`]. The string comparison is - /// case-insensitive. - /// - /// [`AdapterInfo::name`]: wgt::AdapterInfo::name - pub fn backend_adapter(backends: wgpu::Backends, adapter: &'static str) -> Self { - FailureCase { - backends: Some(backends), - adapter: Some(adapter), - ..FailureCase::default() - } - } - - /// Tests running under WebGL. - /// - /// Because of wasm's limited ability to recover from errors, we - /// usually need to skip the test altogether if it's not - /// supported, so this should be usually used with - /// [`TestParameters::skip`]. - pub fn webgl2() -> Self { - #[cfg(target_arch = "wasm32")] - let case = FailureCase::backend(wgpu::Backends::GL); - #[cfg(not(target_arch = "wasm32"))] - let case = FailureCase::never(); - case - } - - /// Tests running on the MoltenVK Vulkan driver on macOS. - pub fn molten_vk() -> Self { - FailureCase { - backends: Some(wgpu::Backends::VULKAN), - driver: Some("MoltenVK"), - ..FailureCase::default() - } - } - - /// Test whether `self` applies to `info`. - /// - /// If it does, return a `FailureReasons` whose set bits indicate - /// why. If it doesn't, return `None`. - /// - /// The caller is responsible for converting the string-valued - /// fields of `info` to lower case, to ensure case-insensitive - /// matching. - fn applies_to(&self, info: &wgt::AdapterInfo) -> Option { - let mut reasons = FailureReasons::empty(); - - if let Some(backends) = self.backends { - if !backends.contains(wgpu::Backends::from(info.backend)) { - return None; - } - reasons.set(FailureReasons::BACKEND, true); - } - if let Some(vendor) = self.vendor { - if vendor != info.vendor { - return None; - } - reasons.set(FailureReasons::VENDOR, true); - } - if let Some(adapter) = self.adapter { - let adapter = adapter.to_lowercase(); - if !info.name.contains(&adapter) { - return None; - } - reasons.set(FailureReasons::ADAPTER, true); - } - if let Some(driver) = self.driver { - let driver = driver.to_lowercase(); - if !info.driver.contains(&driver) { - return None; - } - reasons.set(FailureReasons::DRIVER, true); - } - - // If we got this far but no specific reasons were triggered, then this - // must be a wildcard. - if reasons.is_empty() { - Some(FailureReasons::ALWAYS) - } else { - Some(reasons) - } - } -} - -// This information determines if a test should run. -#[derive(Clone)] -pub struct TestParameters { - pub required_features: Features, - pub required_downlevel_caps: DownlevelCapabilities, - pub required_limits: Limits, - - /// Conditions under which this test should be skipped. - pub skips: Vec, - - /// Conditions under which this test should be run, but is expected to fail. - pub failures: Vec, -} - -impl Default for TestParameters { - fn default() -> Self { - Self { - required_features: Features::empty(), - required_downlevel_caps: lowest_downlevel_properties(), - required_limits: Limits::downlevel_webgl2_defaults(), - skips: Vec::new(), - failures: Vec::new(), - } - } -} - -bitflags::bitflags! { - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub struct FailureReasons: u8 { - const BACKEND = 1 << 0; - const VENDOR = 1 << 1; - const ADAPTER = 1 << 2; - const DRIVER = 1 << 3; - const ALWAYS = 1 << 4; - } -} - -// Builder pattern to make it easier -impl TestParameters { - /// Set of common features that most internal tests require for readback. - pub fn test_features_limits(self) -> Self { - self.features(Features::MAPPABLE_PRIMARY_BUFFERS | Features::VERTEX_WRITABLE_STORAGE) - .limits(wgpu::Limits::downlevel_defaults()) - } - - /// Set the list of features this test requires. - pub fn features(mut self, features: Features) -> Self { - self.required_features |= features; - self - } - - pub fn downlevel_flags(mut self, downlevel_flags: DownlevelFlags) -> Self { - self.required_downlevel_caps.flags |= downlevel_flags; - self - } - - /// Set the limits needed for the test. - pub fn limits(mut self, limits: Limits) -> Self { - self.required_limits = limits; - self - } - - /// Mark the test as always failing, but not to be skipped. - pub fn expect_fail(mut self, when: FailureCase) -> Self { - self.failures.push(when); - self - } - - /// Mark the test as always failing, and needing to be skipped. - pub fn skip(mut self, when: FailureCase) -> Self { - self.skips.push(when); - self - } -} - -pub async fn initialize_test( - config: GpuTestConfiguration, - test_info: Option, - adapter_index: usize, -) { - // If we get information externally, skip based on that information before we do anything. - if let Some(TestInfo { skip: true, .. }) = test_info { - return; - } - - // 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); - - let _test_guard = isolation::OneTestPerProcessGuard::new(); - - let (adapter, _surface_guard) = initialize_adapter(adapter_index).await; - - let adapter_info = adapter.get_info(); - let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities(); - - let test_info = test_info.unwrap_or_else(|| { - let adapter_report = AdapterReport::from_adapter(&adapter); - TestInfo::from_configuration(&config, &adapter_report) - }); - - // We are now guaranteed to have information about this test, so skip if we need to. - if test_info.skip { - log::info!("TEST RESULT: SKIPPED"); - return; - } - - let (device, queue) = pollster::block_on(initialize_device( - &adapter, - config.params.required_features, - config.params.required_limits.clone(), - )); - - let context = TestingContext { - adapter, - adapter_info, - adapter_downlevel_capabilities, - device, - device_features: config.params.required_features, - device_limits: config.params.required_limits.clone(), - queue, - }; - - // Run the test, and catch panics (possibly due to failed assertions). - let panicked = AssertUnwindSafe((config.test.as_ref().unwrap())(context)) - .catch_unwind() - .await - .is_err(); - - // Check whether any validation errors were reported during the test run. - cfg_if::cfg_if!( - if #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] { - let canary_set = wgpu::hal::VALIDATION_CANARY.get_and_reset(); - } else if #[cfg(all(target_arch = "wasm32", feature = "webgl"))] { - let canary_set = _surface_guard.unwrap().check_for_unreported_errors(); - } else { - // TODO: WebGPU - let canary_set = false; - } - ); - - // Summarize reasons for actual failure, if any. - let failure_cause = match (panicked, canary_set) { - (true, true) => Some("PANIC AND VALIDATION ERROR"), - (true, false) => Some("PANIC"), - (false, true) => Some("VALIDATION ERROR"), - (false, false) => None, - }; - - // Compare actual results against expectations. - match (failure_cause, test_info.expected_failure_reason) { - // The test passed, as expected. - (None, None) => log::info!("TEST RESULT: PASSED"), - // The test failed unexpectedly. - (Some(cause), None) => { - panic!("UNEXPECTED TEST FAILURE DUE TO {cause}") - } - // The test passed unexpectedly. - (None, Some(reason)) => { - panic!("UNEXPECTED TEST PASS: {reason:?}"); - } - // The test failed, as expected. - (Some(cause), Some(reason_expected)) => { - log::info!( - "TEST RESULT: EXPECTED FAILURE DUE TO {} (expected because of {:?})", - cause, - reason_expected - ); - } - } -} - -pub async fn initialize_adapter(adapter_index: usize) -> (Adapter, Option) { - let instance = initialize_instance(); - #[allow(unused_variables)] - let _surface: wgpu::Surface; - let surface_guard: Option; - - // Create a canvas iff we need a WebGL2RenderingContext to have a working device. - #[cfg(not(all( - target_arch = "wasm32", - any(target_os = "emscripten", feature = "webgl") - )))] - { - 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 = create_html_canvas(); - - _surface = instance - .create_surface_from_canvas(canvas.clone()) - .expect("could not create surface from canvas"); - - surface_guard = Some(SurfaceGuard { canvas }); - } - - cfg_if::cfg_if! { - if #[cfg(any(not(target_arch = "wasm32"), feature = "webgl"))] { - let adapter_iter = instance.enumerate_adapters(wgpu::Backends::all()); - let adapter_count = adapter_iter.len(); - let adapter = adapter_iter.into_iter() - .nth(adapter_index) - .unwrap_or_else(|| panic!("Tried to get index {adapter_index} adapter, but adapter list was only {adapter_count} long. Is .gpuconfig out of date?")); - } else { - assert_eq!(adapter_index, 0); - let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions::default()).await.unwrap(); - } - } - - log::info!("Testing using adapter: {:#?}", adapter.get_info()); - - (adapter, surface_guard) -} - -pub fn initialize_instance() -> Instance { - let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(Backends::all); - let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(); - let gles_minor_version = wgpu::util::gles_minor_version_from_env().unwrap_or_default(); - Instance::new(wgpu::InstanceDescriptor { - backends, - flags: wgpu::InstanceFlags::debugging().with_env(), - dx12_shader_compiler, - gles_minor_version, - }) -} - -// Public because it is used by tests of interacting with canvas -pub struct SurfaceGuard { - #[cfg(target_arch = "wasm32")] - pub canvas: web_sys::HtmlCanvasElement, -} - -impl SurfaceGuard { - #[cfg(all( - target_arch = "wasm32", - any(target_os = "emscripten", feature = "webgl") - ))] - 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 - } -} - -#[cfg(target_arch = "wasm32")] -pub fn create_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") -} - // Run some code in an error scope and assert that validation fails. pub fn fail(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T { device.push_error_scope(wgpu::ErrorFilter::Validation); @@ -551,12 +54,12 @@ macro_rules! gpu_test_main { () => { #[cfg(target_arch = "wasm32")] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[cfg(target_arch = "wasm32")] + fn main() {} #[cfg(not(target_arch = "wasm32"))] - fn main() -> $crate::infra::MainResult { - $crate::infra::main() + fn main() -> $crate::native::MainResult { + $crate::native::main() } - #[cfg(target_arch = "wasm32")] - fn main() {} }; } diff --git a/tests/src/native.rs b/tests/src/native.rs new file mode 100644 index 0000000000..5be65e0525 --- /dev/null +++ b/tests/src/native.rs @@ -0,0 +1,83 @@ +#![cfg(not(target_arch = "wasm32"))] + +use std::{future::Future, pin::Pin}; + +use parking_lot::Mutex; + +use crate::{ + config::GpuTestConfiguration, params::TestInfo, report::AdapterReport, run::execute_test, +}; + +type NativeTestFuture = Pin + Send + Sync>>; + +struct NativeTest { + name: String, + future: NativeTestFuture, +} + +impl NativeTest { + fn from_configuration( + config: GpuTestConfiguration, + adapter: &AdapterReport, + adapter_index: usize, + ) -> Self { + let backend = &adapter.info.backend; + let device_name = &adapter.info.name; + + let test_info = TestInfo::from_configuration(&config, adapter); + + let full_name = format!( + "[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}", + running_msg = test_info.running_msg, + base_name = config.name, + ); + Self { + name: full_name, + future: Box::pin(async move { + execute_test(config, Some(test_info), adapter_index).await; + }), + } + } + + pub fn into_trial(self) -> libtest_mimic::Trial { + libtest_mimic::Trial::test(self.name, || { + pollster::block_on(self.future); + Ok(()) + }) + } +} + +pub static TEST_LIST: Mutex> = Mutex::new(Vec::new()); + +pub type MainResult = anyhow::Result<()>; + +pub fn main() -> MainResult { + use anyhow::Context; + + use crate::report::GpuReport; + + let config_text = + &std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) + .context("Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?")?; + let report = GpuReport::from_json(config_text).context("Could not pare .gpuconfig JSON")?; + + let mut test_guard = TEST_LIST.lock(); + execute_native(test_guard.drain(..).flat_map(|test| { + report + .devices + .iter() + .enumerate() + .map(move |(adapter_index, adapter)| { + NativeTest::from_configuration(test.clone(), adapter, adapter_index) + }) + })); + + Ok(()) +} + +fn execute_native(tests: impl IntoIterator) { + let args = libtest_mimic::Arguments::from_args(); + let trials = tests.into_iter().map(NativeTest::into_trial).collect(); + + libtest_mimic::run(&args, trials).exit_if_failed(); +} diff --git a/tests/src/params.rs b/tests/src/params.rs new file mode 100644 index 0000000000..8a659cc8f2 --- /dev/null +++ b/tests/src/params.rs @@ -0,0 +1,361 @@ +use arrayvec::ArrayVec; +use wgt::{DownlevelCapabilities, Features, Limits, DownlevelFlags}; + +use crate::{GpuTestConfiguration, report::AdapterReport}; + +/// Conditions under which a test should fail or be skipped. +/// +/// By passing a `FailureCase` to [`TestParameters::expect_fail`], you can +/// mark a test as expected to fail under the indicated conditions. By +/// passing it to [`TestParameters::skip`], you can request that the +/// test be skipped altogether. +/// +/// If a field is `None`, then that field does not restrict matches. For +/// example: +/// +/// ``` +/// # use wgpu_test::FailureCase; +/// FailureCase { +/// backends: Some(wgpu::Backends::DX11 | wgpu::Backends::DX12), +/// vendor: None, +/// adapter: Some("RTX"), +/// driver: None, +/// } +/// # ; +/// ``` +/// +/// This applies to all cards with `"RTX'` in their name on either +/// Direct3D backend, no matter the vendor ID or driver name. +/// +/// The strings given here need only appear as a substring in the +/// corresponding [`AdapterInfo`] fields. The comparison is +/// case-insensitive. +/// +/// The default value of `FailureCase` applies to any test case. That +/// is, there are no criteria to constrain the match. +/// +/// [`AdapterInfo`]: wgt::AdapterInfo +#[derive(Default, Clone)] +pub struct FailureCase { + /// Backends expected to fail, or `None` for any backend. + /// + /// If this is `None`, or if the test is using one of the backends + /// in `backends`, then this `FailureCase` applies. + pub backends: Option, + + /// Vendor expected to fail, or `None` for any vendor. + /// + /// If `Some`, this must match [`AdapterInfo::device`], which is + /// usually the PCI device id. Otherwise, this `FailureCase` + /// applies regardless of vendor. + /// + /// [`AdapterInfo::device`]: wgt::AdapterInfo::device + pub vendor: Option, + + /// Name of adaper expected to fail, or `None` for any adapter name. + /// + /// If this is `Some(s)` and `s` is a substring of + /// [`AdapterInfo::name`], then this `FailureCase` applies. If + /// this is `None`, the adapter name isn't considered. + /// + /// [`AdapterInfo::name`]: wgt::AdapterInfo::name + pub adapter: Option<&'static str>, + + /// Name of driver expected to fail, or `None` for any driver name. + /// + /// If this is `Some(s)` and `s` is a substring of + /// [`AdapterInfo::driver`], then this `FailureCase` applies. If + /// this is `None`, the driver name isn't considered. + /// + /// [`AdapterInfo::driver`]: wgt::AdapterInfo::driver + pub driver: Option<&'static str>, +} + +impl FailureCase { + /// This case applies to all tests. + pub fn always() -> Self { + FailureCase::default() + } + + /// This case applies to no tests. + pub fn never() -> Self { + FailureCase { + backends: Some(wgpu::Backends::empty()), + ..FailureCase::default() + } + } + + /// Tests running on any of the given backends. + pub fn backend(backends: wgpu::Backends) -> Self { + FailureCase { + backends: Some(backends), + ..FailureCase::default() + } + } + + /// Tests running on `adapter`. + /// + /// For this case to apply, the `adapter` string must appear as a substring + /// of the adapter's [`AdapterInfo::name`]. The comparison is + /// case-insensitive. + /// + /// [`AdapterInfo::name`]: wgt::AdapterInfo::name + pub fn adapter(adapter: &'static str) -> Self { + FailureCase { + adapter: Some(adapter), + ..FailureCase::default() + } + } + + /// Tests running on `backend` and `adapter`. + /// + /// For this case to apply, the test must be using an adapter for one of the + /// given `backend` bits, and `adapter` string must appear as a substring of + /// the adapter's [`AdapterInfo::name`]. The string comparison is + /// case-insensitive. + /// + /// [`AdapterInfo::name`]: wgt::AdapterInfo::name + pub fn backend_adapter(backends: wgpu::Backends, adapter: &'static str) -> Self { + FailureCase { + backends: Some(backends), + adapter: Some(adapter), + ..FailureCase::default() + } + } + + /// Tests running under WebGL. + /// + /// Because of wasm's limited ability to recover from errors, we + /// usually need to skip the test altogether if it's not + /// supported, so this should be usually used with + /// [`TestParameters::skip`]. + pub fn webgl2() -> Self { + #[cfg(target_arch = "wasm32")] + let case = FailureCase::backend(wgpu::Backends::GL); + #[cfg(not(target_arch = "wasm32"))] + let case = FailureCase::never(); + case + } + + /// Tests running on the MoltenVK Vulkan driver on macOS. + pub fn molten_vk() -> Self { + FailureCase { + backends: Some(wgpu::Backends::VULKAN), + driver: Some("MoltenVK"), + ..FailureCase::default() + } + } + + /// Test whether `self` applies to `info`. + /// + /// If it does, return a `FailureReasons` whose set bits indicate + /// why. If it doesn't, return `None`. + /// + /// The caller is responsible for converting the string-valued + /// fields of `info` to lower case, to ensure case-insensitive + /// matching. + pub(crate) fn applies_to(&self, info: &wgt::AdapterInfo) -> Option { + let mut reasons = FailureReasons::empty(); + + if let Some(backends) = self.backends { + if !backends.contains(wgpu::Backends::from(info.backend)) { + return None; + } + reasons.set(FailureReasons::BACKEND, true); + } + if let Some(vendor) = self.vendor { + if vendor != info.vendor { + return None; + } + reasons.set(FailureReasons::VENDOR, true); + } + if let Some(adapter) = self.adapter { + let adapter = adapter.to_lowercase(); + if !info.name.contains(&adapter) { + return None; + } + reasons.set(FailureReasons::ADAPTER, true); + } + if let Some(driver) = self.driver { + let driver = driver.to_lowercase(); + if !info.driver.contains(&driver) { + return None; + } + reasons.set(FailureReasons::DRIVER, true); + } + + // If we got this far but no specific reasons were triggered, then this + // must be a wildcard. + if reasons.is_empty() { + Some(FailureReasons::ALWAYS) + } else { + Some(reasons) + } + } +} + +const LOWEST_DOWNLEVEL_PROPERTIES: wgpu::DownlevelCapabilities = DownlevelCapabilities { + flags: wgt::DownlevelFlags::empty(), + limits: wgt::DownlevelLimits {}, + shader_model: wgt::ShaderModel::Sm2, +}; + +// This information determines if a test should run. +#[derive(Clone)] +pub struct TestParameters { + pub required_features: Features, + pub required_downlevel_caps: DownlevelCapabilities, + pub required_limits: Limits, + + /// Conditions under which this test should be skipped. + pub skips: Vec, + + /// Conditions under which this test should be run, but is expected to fail. + pub failures: Vec, +} + +impl Default for TestParameters { + fn default() -> Self { + Self { + required_features: Features::empty(), + required_downlevel_caps: LOWEST_DOWNLEVEL_PROPERTIES, + required_limits: Limits::downlevel_webgl2_defaults(), + skips: Vec::new(), + failures: Vec::new(), + } + } +} + +bitflags::bitflags! { + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub struct FailureReasons: u8 { + const BACKEND = 1 << 0; + const VENDOR = 1 << 1; + const ADAPTER = 1 << 2; + const DRIVER = 1 << 3; + const ALWAYS = 1 << 4; + } +} + +// Builder pattern to make it easier +impl TestParameters { + /// Set of common features that most internal tests require for readback. + pub fn test_features_limits(self) -> Self { + self.features(Features::MAPPABLE_PRIMARY_BUFFERS | Features::VERTEX_WRITABLE_STORAGE) + .limits(wgpu::Limits::downlevel_defaults()) + } + + /// Set the list of features this test requires. + pub fn features(mut self, features: Features) -> Self { + self.required_features |= features; + self + } + + pub fn downlevel_flags(mut self, downlevel_flags: DownlevelFlags) -> Self { + self.required_downlevel_caps.flags |= downlevel_flags; + self + } + + /// Set the limits needed for the test. + pub fn limits(mut self, limits: Limits) -> Self { + self.required_limits = limits; + self + } + + /// Mark the test as always failing, but not to be skipped. + pub fn expect_fail(mut self, when: FailureCase) -> Self { + self.failures.push(when); + self + } + + /// Mark the test as always failing, and needing to be skipped. + pub fn skip(mut self, when: FailureCase) -> Self { + self.skips.push(when); + self + } +} + +pub struct TestInfo { + pub skip: bool, + pub expected_failure_reason: Option, + pub running_msg: String, +} + +impl TestInfo { + pub(crate) fn from_configuration(test: &GpuTestConfiguration, adapter: &AdapterReport) -> Self { + // Figure out if we should skip the test and if so, why. + let mut skipped_reasons: ArrayVec<_, 4> = ArrayVec::new(); + let missing_features = test.params.required_features - adapter.features; + if !missing_features.is_empty() { + skipped_reasons.push("Features"); + } + + if !test.params.required_limits.check_limits(&adapter.limits) { + skipped_reasons.push("Limits"); + } + + let missing_downlevel_flags = + test.params.required_downlevel_caps.flags - adapter.downlevel_caps.flags; + if !missing_downlevel_flags.is_empty() { + skipped_reasons.push("Downlevel Flags"); + } + + if test.params.required_downlevel_caps.shader_model > adapter.downlevel_caps.shader_model { + skipped_reasons.push("Shader Model"); + } + + // Produce a lower-case version of the adapter info, for comparison against + // `parameters.skips` and `parameters.failures`. + let adapter_lowercase_info = wgt::AdapterInfo { + name: adapter.info.name.to_lowercase(), + driver: adapter.info.driver.to_lowercase(), + ..adapter.info.clone() + }; + + // Check if we should skip the test altogether. + let skip_reason = test + .params + .skips + .iter() + .find_map(|case| case.applies_to(&adapter_lowercase_info)); + + let expected_failure_reason = test + .params + .failures + .iter() + .find_map(|case| case.applies_to(&adapter_lowercase_info)); + + let mut skip = false; + let running_msg = if let Some(reasons) = skip_reason { + skip = true; + + let names: ArrayVec<_, 4> = reasons.iter_names().map(|(name, _)| name).collect(); + let names_text = names.join(" | "); + + format!("Skipped Failure: {}", names_text) + } else if !skipped_reasons.is_empty() { + skip = true; + format!("Skipped: {}", skipped_reasons.join(" | ")) + } else if let Some(failure_resasons) = expected_failure_reason { + if cfg!(target_arch = "wasm32") { + skip = true; + } + + let names: ArrayVec<_, 4> = failure_resasons + .iter_names() + .map(|(name, _)| name) + .collect(); + let names_text = names.join(" | "); + + format!("Executed Failure: {}", names_text) + } else { + String::from("Executed") + }; + + Self { + skip, + expected_failure_reason, + running_msg, + } + } +} diff --git a/tests/src/infra/report.rs b/tests/src/report.rs similarity index 77% rename from tests/src/infra/report.rs rename to tests/src/report.rs index 38c54fee57..cb7729dcb0 100644 --- a/tests/src/infra/report.rs +++ b/tests/src/report.rs @@ -6,28 +6,30 @@ use wgpu::{ }; #[derive(Deserialize)] -pub struct GpuReport { +pub(crate) struct GpuReport { + #[cfg_attr(target_arch = "wasm32", allow(unused))] pub devices: Vec, } impl GpuReport { #[cfg_attr(target_arch = "wasm32", allow(unused))] - pub fn from_json(file: &str) -> serde_json::Result { + pub(crate) fn from_json(file: &str) -> serde_json::Result { serde_json::from_str(file) } } #[derive(Deserialize)] -pub struct AdapterReport { +pub(crate) struct AdapterReport { pub info: AdapterInfo, pub features: Features, pub limits: Limits, pub downlevel_caps: DownlevelCapabilities, + #[allow(unused)] pub texture_format_features: HashMap, } impl AdapterReport { - pub fn from_adapter(adapter: &wgpu::Adapter) -> Self { + pub(crate) fn from_adapter(adapter: &wgpu::Adapter) -> Self { let info = adapter.get_info(); let features = adapter.features(); let limits = adapter.limits(); diff --git a/tests/src/run.rs b/tests/src/run.rs new file mode 100644 index 0000000000..c0ca44e3ec --- /dev/null +++ b/tests/src/run.rs @@ -0,0 +1,121 @@ +use std::panic::AssertUnwindSafe; + +use futures_lite::FutureExt; +use wgpu::{Adapter, Device, Queue}; + +use crate::{ + init::{initialize_adapter, initialize_device}, + isolation, + params::TestInfo, + report::AdapterReport, + GpuTestConfiguration, +}; + +pub struct TestingContext { + pub adapter: Adapter, + pub adapter_info: wgt::AdapterInfo, + pub adapter_downlevel_capabilities: wgt::DownlevelCapabilities, + pub device: Device, + pub device_features: wgt::Features, + pub device_limits: wgt::Limits, + pub queue: Queue, +} + +pub async fn execute_test( + config: GpuTestConfiguration, + test_info: Option, + adapter_index: usize, +) { + // If we get information externally, skip based on that information before we do anything. + if let Some(TestInfo { skip: true, .. }) = test_info { + return; + } + + // 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); + + let _test_guard = isolation::OneTestPerProcessGuard::new(); + + let (adapter, _surface_guard) = initialize_adapter(adapter_index).await; + + let adapter_info = adapter.get_info(); + let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities(); + + let test_info = test_info.unwrap_or_else(|| { + let adapter_report = AdapterReport::from_adapter(&adapter); + TestInfo::from_configuration(&config, &adapter_report) + }); + + // We are now guaranteed to have information about this test, so skip if we need to. + if test_info.skip { + log::info!("TEST RESULT: SKIPPED"); + return; + } + + let (device, queue) = pollster::block_on(initialize_device( + &adapter, + config.params.required_features, + config.params.required_limits.clone(), + )); + + let context = TestingContext { + adapter, + adapter_info, + adapter_downlevel_capabilities, + device, + device_features: config.params.required_features, + device_limits: config.params.required_limits.clone(), + queue, + }; + + // Run the test, and catch panics (possibly due to failed assertions). + let panicked = AssertUnwindSafe((config.test.as_ref().unwrap())(context)) + .catch_unwind() + .await + .is_err(); + + // Check whether any validation errors were reported during the test run. + cfg_if::cfg_if!( + if #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] { + let canary_set = wgpu::hal::VALIDATION_CANARY.get_and_reset(); + } else if #[cfg(all(target_arch = "wasm32", feature = "webgl"))] { + let canary_set = _surface_guard.unwrap().check_for_unreported_errors(); + } else { + // TODO: WebGPU + let canary_set = false; + } + ); + + // Summarize reasons for actual failure, if any. + let failure_cause = match (panicked, canary_set) { + (true, true) => Some("PANIC AND VALIDATION ERROR"), + (true, false) => Some("PANIC"), + (false, true) => Some("VALIDATION ERROR"), + (false, false) => None, + }; + + // Compare actual results against expectations. + match (failure_cause, test_info.expected_failure_reason) { + // The test passed, as expected. + (None, None) => log::info!("TEST RESULT: PASSED"), + // The test failed unexpectedly. + (Some(cause), None) => { + panic!("UNEXPECTED TEST FAILURE DUE TO {cause}") + } + // The test passed unexpectedly. + (None, Some(reason)) => { + panic!("UNEXPECTED TEST PASS: {reason:?}"); + } + // The test failed, as expected. + (Some(cause), Some(reason_expected)) => { + log::info!( + "TEST RESULT: EXPECTED FAILURE DUE TO {} (expected because of {:?})", + cause, + reason_expected + ); + } + } +} diff --git a/tests/tests/bind_group_layout_dedup.rs b/tests/tests/bind_group_layout_dedup.rs index af8e88b8e5..3f2e6fe17a 100644 --- a/tests/tests/bind_group_layout_dedup.rs +++ b/tests/tests/bind_group_layout_dedup.rs @@ -1,4 +1,4 @@ -use wgpu_test::{gpu_test, infra::GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration}; #[gpu_test] static BIND_GROUP_LAYOUT_DEDUPLICATION: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/tests/tests/buffer.rs b/tests/tests/buffer.rs index bc33b73a57..9913a0d6b8 100644 --- a/tests/tests/buffer.rs +++ b/tests/tests/buffer.rs @@ -1,6 +1,4 @@ -use wgpu_test::{ - gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters, TestingContext, -}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str) { let r = wgpu::BufferUsages::MAP_READ; diff --git a/tests/tests/buffer_copy.rs b/tests/tests/buffer_copy.rs index d8fd490336..9733255ba6 100644 --- a/tests/tests/buffer_copy.rs +++ b/tests/tests/buffer_copy.rs @@ -2,7 +2,7 @@ use wgt::BufferAddress; -use wgpu_test::{fail_if, gpu_test, infra::GpuTestConfiguration}; +use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration}; fn try_copy( ctx: &wgpu_test::TestingContext, diff --git a/tests/tests/buffer_usages.rs b/tests/tests/buffer_usages.rs index 2f58c1f108..657b8a41b4 100644 --- a/tests/tests/buffer_usages.rs +++ b/tests/tests/buffer_usages.rs @@ -1,7 +1,7 @@ //! Tests for buffer usages validation. use wgpu::BufferUsages as Bu; -use wgpu_test::{fail_if, gpu_test, infra::GpuTestConfiguration, TestParameters}; +use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, TestParameters}; use wgt::BufferAddress; const BUFFER_SIZE: BufferAddress = 1234; diff --git a/tests/tests/clear_texture.rs b/tests/tests/clear_texture.rs index c9e93c9621..6676449840 100644 --- a/tests/tests/clear_texture.rs +++ b/tests/tests/clear_texture.rs @@ -1,5 +1,5 @@ use wgpu_test::{ - gpu_test, image::ReadbackBuffers, infra::GpuTestConfiguration, FailureCase, TestParameters, + gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, TestParameters, TestingContext, }; diff --git a/tests/tests/create_surface_error.rs b/tests/tests/create_surface_error.rs index f8962697ce..9749642f2f 100644 --- a/tests/tests/create_surface_error.rs +++ b/tests/tests/create_surface_error.rs @@ -5,18 +5,17 @@ #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] #[wasm_bindgen_test::wasm_bindgen_test] fn canvas_get_context_returned_null() { - // Not using initialize_test() because that goes straight to creating the canvas for us. + // Not using the normal testing infrastructure because that goes straight to creating the canvas for us. let instance = wgpu_test::initialize_instance(); - // Create canvas and cleanup on drop - let canvas_g = wgpu_test::SurfaceGuard { - canvas: wgpu_test::create_html_canvas(), - }; + // Create canvas + let canvas = wgpu_test::initialize_html_canvas(); + // Using a context id that is not "webgl2" or "webgpu" will render the canvas unusable by wgpu. - canvas_g.canvas.get_context("2d").unwrap(); + canvas.get_context("2d").unwrap(); #[allow(clippy::redundant_clone)] // false positive — can't and shouldn't move out. let error = instance - .create_surface_from_canvas(canvas_g.canvas.clone()) + .create_surface_from_canvas(canvas.clone()) .unwrap_err(); assert!( diff --git a/tests/tests/device.rs b/tests/tests/device.rs index 37ced56f51..4629475190 100644 --- a/tests/tests/device.rs +++ b/tests/tests/device.rs @@ -1,4 +1,4 @@ -use wgpu_test::{fail, gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; +use wgpu_test::{fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; #[gpu_test] static CROSS_DEVICE_BIND_GROUP_USAGE: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/tests/tests/encoder.rs b/tests/tests/encoder.rs index 6ac892b5cf..3487eb05d1 100644 --- a/tests/tests/encoder.rs +++ b/tests/tests/encoder.rs @@ -1,4 +1,4 @@ -use wgpu_test::{fail, gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; +use wgpu_test::{fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; #[gpu_test] static DROP_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { diff --git a/tests/tests/external_texture.rs b/tests/tests/external_texture.rs index f143e66ca2..18a5d193af 100644 --- a/tests/tests/external_texture.rs +++ b/tests/tests/external_texture.rs @@ -2,7 +2,7 @@ use wasm_bindgen::JsCast; use wgpu::ExternalImageSource; -use wgpu_test::{fail_if, gpu_test, infra::GpuTestConfiguration}; +use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration}; #[gpu_test] static IMAGE_BITMAP_IMPORT: GpuTestConfiguration = diff --git a/tests/tests/instance.rs b/tests/tests/instance.rs index 0594804413..c74b7aedd2 100644 --- a/tests/tests/instance.rs +++ b/tests/tests/instance.rs @@ -1,4 +1,4 @@ -use wgpu_test::{gpu_test, infra::GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration}; #[gpu_test] static INITIALIZE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|_ctx| {}); diff --git a/tests/tests/occlusion_query/mod.rs b/tests/tests/occlusion_query/mod.rs index 79e6d23184..7a7f3fa9a2 100644 --- a/tests/tests/occlusion_query/mod.rs +++ b/tests/tests/occlusion_query/mod.rs @@ -1,5 +1,5 @@ use std::borrow::Cow; -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; #[gpu_test] static OCCLUSION_QUERY: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/tests/tests/partially_bounded_arrays/mod.rs b/tests/tests/partially_bounded_arrays/mod.rs index 1e6f82e4c5..acadaad67b 100644 --- a/tests/tests/partially_bounded_arrays/mod.rs +++ b/tests/tests/partially_bounded_arrays/mod.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, num::NonZeroU32}; -use wgpu_test::{gpu_test, image::ReadbackBuffers, infra::GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, image::ReadbackBuffers, GpuTestConfiguration, TestParameters}; #[gpu_test] static PARTIALLY_BOUNDED_ARRAY: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/tests/tests/pipeline.rs b/tests/tests/pipeline.rs index fda892f6a2..2a564c9302 100644 --- a/tests/tests/pipeline.rs +++ b/tests/tests/pipeline.rs @@ -1,4 +1,4 @@ -use wgpu_test::{fail, gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; +use wgpu_test::{fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; // Create an invalid shader and a compute pipeline that uses it // with a default bindgroup layout, and then ask for that layout. diff --git a/tests/tests/poll.rs b/tests/tests/poll.rs index 2e7be9aed7..b832237501 100644 --- a/tests/tests/poll.rs +++ b/tests/tests/poll.rs @@ -7,7 +7,7 @@ use wgpu::{ Maintain, ShaderStages, }; -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestingContext}; +use wgpu_test::{gpu_test, GpuTestConfiguration, TestingContext}; struct DummyWorkData { _buffer: Buffer, diff --git a/tests/tests/query_set.rs b/tests/tests/query_set.rs index 2b77286c72..1030518aa8 100644 --- a/tests/tests/query_set.rs +++ b/tests/tests/query_set.rs @@ -1,4 +1,4 @@ -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; #[gpu_test] static DROP_FAILED_TIMESTAMP_QUERY_SET: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/tests/tests/queue_transfer.rs b/tests/tests/queue_transfer.rs index 68d78bdd22..a1ae41572b 100644 --- a/tests/tests/queue_transfer.rs +++ b/tests/tests/queue_transfer.rs @@ -1,6 +1,6 @@ //! Tests for buffer copy validation. -use wgpu_test::{fail, gpu_test, infra::GpuTestConfiguration}; +use wgpu_test::{fail, gpu_test, GpuTestConfiguration}; #[gpu_test] static QUEUE_WRITE_TEXTURE_OVERFLOW: GpuTestConfiguration = diff --git a/tests/tests/regression/issue_3457.rs b/tests/tests/regression/issue_3457.rs index a87cbfa063..d7ea0e5bb9 100644 --- a/tests/tests/regression/issue_3457.rs +++ b/tests/tests/regression/issue_3457.rs @@ -1,4 +1,4 @@ -use wgpu_test::{gpu_test, infra::GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration}; use wgpu::*; diff --git a/tests/tests/regression/issue_4024.rs b/tests/tests/regression/issue_4024.rs index 103e22edd1..0577b223f6 100644 --- a/tests/tests/regression/issue_4024.rs +++ b/tests/tests/regression/issue_4024.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use parking_lot::Mutex; -use wgpu_test::{gpu_test, infra::GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration}; use wgpu::*; diff --git a/tests/tests/regression/issue_4122.rs b/tests/tests/regression/issue_4122.rs index 815323991e..c6f7afbdde 100644 --- a/tests/tests/regression/issue_4122.rs +++ b/tests/tests/regression/issue_4122.rs @@ -1,6 +1,6 @@ use std::{num::NonZeroU64, ops::Range}; -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestingContext}; +use wgpu_test::{gpu_test, GpuTestConfiguration, TestingContext}; fn fill_test(ctx: &TestingContext, range: Range, size: u64) -> bool { let gpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { diff --git a/tests/tests/resource_descriptor_accessor.rs b/tests/tests/resource_descriptor_accessor.rs index d0851ed621..ee984da60e 100644 --- a/tests/tests/resource_descriptor_accessor.rs +++ b/tests/tests/resource_descriptor_accessor.rs @@ -1,4 +1,4 @@ -use wgpu_test::{gpu_test, infra::GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration}; #[gpu_test] static BUFFER_SIZE_AND_USAGE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { diff --git a/tests/tests/resource_error.rs b/tests/tests/resource_error.rs index 3a4e53a3f2..0a81801a9a 100644 --- a/tests/tests/resource_error.rs +++ b/tests/tests/resource_error.rs @@ -1,4 +1,4 @@ -use wgpu_test::{fail, gpu_test, infra::GpuTestConfiguration, valid}; +use wgpu_test::{fail, gpu_test, valid, GpuTestConfiguration}; #[gpu_test] static BAD_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { diff --git a/tests/tests/scissor_tests/mod.rs b/tests/tests/scissor_tests/mod.rs index 0d9eb0fdb1..d53d31cdac 100644 --- a/tests/tests/scissor_tests/mod.rs +++ b/tests/tests/scissor_tests/mod.rs @@ -1,4 +1,4 @@ -use wgpu_test::{gpu_test, image, infra::GpuTestConfiguration, TestingContext}; +use wgpu_test::{gpu_test, image, GpuTestConfiguration, TestingContext}; struct Rect { x: u32, diff --git a/tests/tests/shader/numeric_builtins.rs b/tests/tests/shader/numeric_builtins.rs index 5e46e1c887..7463e7a505 100644 --- a/tests/tests/shader/numeric_builtins.rs +++ b/tests/tests/shader/numeric_builtins.rs @@ -1,7 +1,7 @@ use wgpu::{DownlevelFlags, Limits}; use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest}; -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; fn create_numeric_builtin_test() -> Vec { let mut tests = Vec::new(); diff --git a/tests/tests/shader/struct_layout.rs b/tests/tests/shader/struct_layout.rs index ebe9f3ca1f..f17dceac08 100644 --- a/tests/tests/shader/struct_layout.rs +++ b/tests/tests/shader/struct_layout.rs @@ -3,7 +3,7 @@ use std::fmt::Write; use wgpu::{Backends, DownlevelFlags, Features, Limits}; use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest, MAX_BUFFER_SIZE}; -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; fn create_struct_layout_tests(storage_type: InputStorageType) -> Vec { let input_values: Vec<_> = (0..(MAX_BUFFER_SIZE as u32 / 4)).collect(); diff --git a/tests/tests/shader/zero_init_workgroup_mem.rs b/tests/tests/shader/zero_init_workgroup_mem.rs index 93897c299b..8e03985991 100644 --- a/tests/tests/shader/zero_init_workgroup_mem.rs +++ b/tests/tests/shader/zero_init_workgroup_mem.rs @@ -8,7 +8,7 @@ use wgpu::{ ShaderStages, }; -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; #[gpu_test] static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/tests/tests/shader_primitive_index/mod.rs b/tests/tests/shader_primitive_index/mod.rs index b7c7ffe7ce..e5157a7c93 100644 --- a/tests/tests/shader_primitive_index/mod.rs +++ b/tests/tests/shader_primitive_index/mod.rs @@ -1,5 +1,5 @@ use wgpu::util::DeviceExt; -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; // // These tests render two triangles to a 2x2 render target. The first triangle diff --git a/tests/tests/shader_view_format/mod.rs b/tests/tests/shader_view_format/mod.rs index 73eeddb694..77b89b8405 100644 --- a/tests/tests/shader_view_format/mod.rs +++ b/tests/tests/shader_view_format/mod.rs @@ -1,6 +1,6 @@ use wgpu::{util::DeviceExt, DownlevelFlags, Limits, TextureFormat}; use wgpu_test::{ - gpu_test, image::calc_difference, infra::GpuTestConfiguration, FailureCase, TestParameters, + gpu_test, image::calc_difference, FailureCase, GpuTestConfiguration, TestParameters, TestingContext, }; diff --git a/tests/tests/texture_bounds.rs b/tests/tests/texture_bounds.rs index 5dbf8bbdcb..48b933109d 100644 --- a/tests/tests/texture_bounds.rs +++ b/tests/tests/texture_bounds.rs @@ -1,6 +1,6 @@ //! Tests for texture copy bounds checks. -use wgpu_test::{fail_if, gpu_test, infra::GpuTestConfiguration}; +use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration}; #[gpu_test] static BAD_COPY_ORIGIN_TEST: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { diff --git a/tests/tests/transfer.rs b/tests/tests/transfer.rs index 143d510e68..f8bd43530b 100644 --- a/tests/tests/transfer.rs +++ b/tests/tests/transfer.rs @@ -1,4 +1,4 @@ -use wgpu_test::{fail, gpu_test, infra::GpuTestConfiguration}; +use wgpu_test::{fail, gpu_test, GpuTestConfiguration}; #[gpu_test] static COPY_OVERFLOW_Z: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { diff --git a/tests/tests/vertex_indices/mod.rs b/tests/tests/vertex_indices/mod.rs index 6119f92758..fbfb181797 100644 --- a/tests/tests/vertex_indices/mod.rs +++ b/tests/tests/vertex_indices/mod.rs @@ -2,9 +2,7 @@ use std::num::NonZeroU64; use wgpu::util::DeviceExt; -use wgpu_test::{ - gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters, TestingContext, -}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; fn pulling_common( ctx: TestingContext, diff --git a/tests/tests/write_texture.rs b/tests/tests/write_texture.rs index 220404954b..9b6771c68c 100644 --- a/tests/tests/write_texture.rs +++ b/tests/tests/write_texture.rs @@ -1,6 +1,6 @@ //! Tests for texture copy -use wgpu_test::{gpu_test, infra::GpuTestConfiguration, FailureCase, TestParameters}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; #[gpu_test] static WRITE_TEXTURE_SUBSET_2D: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/tests/tests/zero_init_texture_after_discard.rs b/tests/tests/zero_init_texture_after_discard.rs index 30b527d236..1798c817be 100644 --- a/tests/tests/zero_init_texture_after_discard.rs +++ b/tests/tests/zero_init_texture_after_discard.rs @@ -1,6 +1,6 @@ use wgpu::*; use wgpu_test::{ - gpu_test, image::ReadbackBuffers, infra::GpuTestConfiguration, FailureCase, TestParameters, + gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, TestParameters, TestingContext, }; diff --git a/wgpu-macros/src/lib.rs b/wgpu-macros/src/lib.rs index 0f84bd5393..bfb2c63509 100644 --- a/wgpu-macros/src/lib.rs +++ b/wgpu-macros/src/lib.rs @@ -20,9 +20,9 @@ pub fn gpu_test(_attr: TokenStream, item: TokenStream) -> TokenStream { fn #register_test_name() { struct S; - ::wgpu_test::infra::TEST_LIST.lock().push( + ::wgpu_test::native::TEST_LIST.lock().push( // Allow any type that can be converted to a GpuTestConfiguration - ::wgpu_test::infra::GpuTestConfiguration::from(#expr).name_from_init_function_typename::(#ident_lower) + ::wgpu_test::GpuTestConfiguration::from(#expr).name_from_init_function_typename::(#ident_lower) ) } @@ -32,9 +32,9 @@ pub fn gpu_test(_attr: TokenStream, item: TokenStream) -> TokenStream { struct S; // Allow any type that can be converted to a GpuTestConfiguration - let test_config = ::wgpu_test::infra::GpuTestConfiguration::from(#expr).name_from_init_function_typename::(#ident_lower); + let test_config = ::wgpu_test::GpuTestConfiguration::from(#expr).name_from_init_function_typename::(#ident_lower); - ::wgpu_test::initialize_test(test_config, None, 0).await; + ::wgpu_test::execute_test(test_config, None, 0).await; } } .into() From 3eab61baaee5cb4b5bc0b9c8feab74e424be676b Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Wed, 11 Oct 2023 19:17:45 -0400 Subject: [PATCH 29/41] Format --- tests/src/lib.rs | 2 +- tests/src/params.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/src/lib.rs b/tests/src/lib.rs index d8fdaee858..54d4463746 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -16,7 +16,7 @@ pub use self::image::ComparisonType; pub use config::GpuTestConfiguration; #[doc(hidden)] pub use ctor::ctor; -pub use init::{initialize_instance, initialize_adapter, initialize_device}; +pub use init::{initialize_adapter, initialize_device, initialize_instance}; pub use params::{FailureCase, FailureReasons, TestParameters}; pub use run::{execute_test, TestingContext}; pub use wgpu_macros::gpu_test; diff --git a/tests/src/params.rs b/tests/src/params.rs index 8a659cc8f2..73701d003d 100644 --- a/tests/src/params.rs +++ b/tests/src/params.rs @@ -1,7 +1,7 @@ use arrayvec::ArrayVec; -use wgt::{DownlevelCapabilities, Features, Limits, DownlevelFlags}; +use wgt::{DownlevelCapabilities, DownlevelFlags, Features, Limits}; -use crate::{GpuTestConfiguration, report::AdapterReport}; +use crate::{report::AdapterReport, GpuTestConfiguration}; /// Conditions under which a test should fail or be skipped. /// From a72c2c4729ea78caa1d8b3b59020c7a8b1299d8b Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 12 Oct 2023 00:07:54 -0400 Subject: [PATCH 30/41] Add documentation --- tests/src/config.rs | 5 +++++ tests/src/image.rs | 6 ++---- tests/src/init.rs | 4 ++++ tests/src/lib.rs | 11 ++++++----- tests/src/native.rs | 6 ++++++ tests/src/params.rs | 4 +++- tests/src/report.rs | 6 ++++++ tests/src/run.rs | 13 +++++++++---- tests/tests/shader_view_format/mod.rs | 13 +++++-------- wgpu-info/src/report.rs | 6 ++++++ wgpu-macros/src/lib.rs | 3 +++ 11 files changed, 55 insertions(+), 22 deletions(-) diff --git a/tests/src/config.rs b/tests/src/config.rs index 8a99efc7d1..c6fe02b051 100644 --- a/tests/src/config.rs +++ b/tests/src/config.rs @@ -18,6 +18,7 @@ cfg_if::cfg_if! { } } +/// Configuration for a GPU test. #[derive(Clone)] pub struct GpuTestConfiguration { pub(crate) name: String, @@ -34,6 +35,7 @@ impl GpuTestConfiguration { } } + /// Set the name of the test. Must be unique across all tests in the binary. pub fn name(self, name: &str) -> Self { Self { name: String::from(name), @@ -72,6 +74,7 @@ impl GpuTestConfiguration { Self { name: full, ..self } } + /// Set the parameters that the test needs to succeed. pub fn parameters(self, parameters: TestParameters) -> Self { Self { params: parameters, @@ -79,6 +82,7 @@ impl GpuTestConfiguration { } } + /// Make the test function an synchronous function. pub fn run_sync( self, test: impl Fn(TestingContext) + Copy + RunTestSendSync + 'static, @@ -89,6 +93,7 @@ impl GpuTestConfiguration { } } + /// Make the test function an asynchronous function/future. pub fn run_async(self, test: F) -> Self where F: Fn(TestingContext) -> R + RunTestSendSync + 'static, diff --git a/tests/src/image.rs b/tests/src/image.rs index f23680b229..0e3ea9ea8e 100644 --- a/tests/src/image.rs +++ b/tests/src/image.rs @@ -1,3 +1,5 @@ +//! Image comparison utilities + use std::{borrow::Cow, ffi::OsStr, path::Path}; use wgpu::util::{align_to, DeviceExt}; @@ -60,10 +62,6 @@ async fn write_png( writer.write_image_data(data).unwrap(); } -pub fn calc_difference(lhs: u8, rhs: u8) -> u8 { - (lhs as i16 - rhs as i16).unsigned_abs() as u8 -} - #[cfg_attr(target_arch = "wasm32", allow(unused))] fn add_alpha(input: &[u8]) -> Vec { input diff --git a/tests/src/init.rs b/tests/src/init.rs index d257547997..3e7e20fe3b 100644 --- a/tests/src/init.rs +++ b/tests/src/init.rs @@ -1,6 +1,7 @@ use wgpu::{Adapter, Device, Instance, Queue}; use wgt::{Backends, Features, Limits}; +/// Initialize a wgpu instance with the options from the environment. pub fn initialize_instance() -> Instance { let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(Backends::all); let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(); @@ -13,6 +14,7 @@ pub fn initialize_instance() -> Instance { }) } +/// Initialize a wgpu adapter, taking the `n`th adapter from the instance. pub async fn initialize_adapter(adapter_index: usize) -> (Adapter, Option) { let instance = initialize_instance(); #[allow(unused_variables)] @@ -60,6 +62,7 @@ pub async fn initialize_adapter(adapter_index: usize) -> (Adapter, Option web_sys::HtmlCanvasElement { use wasm_bindgen::JsCast; diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 54d4463746..91545f3a22 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,4 +1,4 @@ -//! This module contains common test-only code that needs to be shared between the examples and the tests. +//! Test utilities for the wgpu repository. mod config; pub mod image; @@ -21,7 +21,7 @@ pub use params::{FailureCase, FailureReasons, TestParameters}; pub use run::{execute_test, TestingContext}; pub use wgpu_macros::gpu_test; -// Run some code in an error scope and assert that validation fails. +/// Run some code in an error scope and assert that validation fails. pub fn fail(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T { device.push_error_scope(wgpu::ErrorFilter::Validation); let result = callback(); @@ -30,7 +30,7 @@ pub fn fail(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T { result } -// Run some code in an error scope and assert that validation succeeds. +/// Run some code in an error scope and assert that validation succeeds. pub fn valid(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T { device.push_error_scope(wgpu::ErrorFilter::Validation); let result = callback(); @@ -39,8 +39,8 @@ pub fn valid(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T { result } -// Run some code in an error scope and assert that validation succeeds or fails depending on the -// provided `should_fail` boolean. +/// Run some code in an error scope and assert that validation succeeds or fails depending on the +/// provided `should_fail` boolean. pub fn fail_if(device: &wgpu::Device, should_fail: bool, callback: impl FnOnce() -> T) -> T { if should_fail { fail(device, callback) @@ -49,6 +49,7 @@ pub fn fail_if(device: &wgpu::Device, should_fail: bool, callback: impl FnOnc } } +/// Adds the necissary main function for our gpu test harness. #[macro_export] macro_rules! gpu_test_main { () => { diff --git a/tests/src/native.rs b/tests/src/native.rs index 5be65e0525..e0e4da5179 100644 --- a/tests/src/native.rs +++ b/tests/src/native.rs @@ -1,4 +1,7 @@ #![cfg(not(target_arch = "wasm32"))] +//! Infrastructure for the native, `cargo-nextest` based harness. +//! +//! This is largly used by [`gpu_test_main`](crate::gpu_test_main) and [`gpu_test`](crate::gpu_test). use std::{future::Future, pin::Pin}; @@ -47,10 +50,13 @@ impl NativeTest { } } +#[doc(hidden)] pub static TEST_LIST: Mutex> = Mutex::new(Vec::new()); +/// Return value for the main function. pub type MainResult = anyhow::Result<()>; +/// Main function that runs every gpu function once for every adapter on the system. pub fn main() -> MainResult { use anyhow::Context; diff --git a/tests/src/params.rs b/tests/src/params.rs index 73701d003d..4da9989872 100644 --- a/tests/src/params.rs +++ b/tests/src/params.rs @@ -200,7 +200,7 @@ const LOWEST_DOWNLEVEL_PROPERTIES: wgpu::DownlevelCapabilities = DownlevelCapabi shader_model: wgt::ShaderModel::Sm2, }; -// This information determines if a test should run. +/// This information determines if a test should run. #[derive(Clone)] pub struct TestParameters { pub required_features: Features, @@ -227,6 +227,7 @@ impl Default for TestParameters { } bitflags::bitflags! { + /// Ways that a given test can be expected to fail. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct FailureReasons: u8 { const BACKEND = 1 << 0; @@ -275,6 +276,7 @@ impl TestParameters { } } +/// Information about a test, including if if it should be skipped. pub struct TestInfo { pub skip: bool, pub expected_failure_reason: Option, diff --git a/tests/src/report.rs b/tests/src/report.rs index cb7729dcb0..11ffa245b3 100644 --- a/tests/src/report.rs +++ b/tests/src/report.rs @@ -5,6 +5,9 @@ use wgpu::{ AdapterInfo, DownlevelCapabilities, Features, Limits, TextureFormat, TextureFormatFeatures, }; +/// Report specifying the capabilities of the GPUs on the system. +/// +/// Must be synchronized with the definition on wgpu-info/src/report.rs. #[derive(Deserialize)] pub(crate) struct GpuReport { #[cfg_attr(target_arch = "wasm32", allow(unused))] @@ -18,6 +21,9 @@ impl GpuReport { } } +/// A single report of the capabilities of an Adapter. +/// +/// Must be synchronized with the definition on wgpu-info/src/report.rs. #[derive(Deserialize)] pub(crate) struct AdapterReport { pub info: AdapterInfo, diff --git a/tests/src/run.rs b/tests/src/run.rs index c0ca44e3ec..ebc4c1237c 100644 --- a/tests/src/run.rs +++ b/tests/src/run.rs @@ -11,16 +11,21 @@ use crate::{ GpuTestConfiguration, }; +/// Parameters and resources hadned to the test function. pub struct TestingContext { pub adapter: Adapter, - pub adapter_info: wgt::AdapterInfo, - pub adapter_downlevel_capabilities: wgt::DownlevelCapabilities, + pub adapter_info: wgpu::AdapterInfo, + pub adapter_downlevel_capabilities: wgpu::DownlevelCapabilities, pub device: Device, - pub device_features: wgt::Features, - pub device_limits: wgt::Limits, + pub device_features: wgpu::Features, + pub device_limits: wgpu::Limits, pub queue: Queue, } +/// Execute the given test configuration with the given adapter index. +/// +/// If test_info is specified, will use the information whether to skip the test. +/// If it is not, we'll create the test info from the adapter itself. pub async fn execute_test( config: GpuTestConfiguration, test_info: Option, diff --git a/tests/tests/shader_view_format/mod.rs b/tests/tests/shader_view_format/mod.rs index 77b89b8405..b7f56886be 100644 --- a/tests/tests/shader_view_format/mod.rs +++ b/tests/tests/shader_view_format/mod.rs @@ -1,8 +1,5 @@ use wgpu::{util::DeviceExt, DownlevelFlags, Limits, TextureFormat}; -use wgpu_test::{ - gpu_test, image::calc_difference, FailureCase, GpuTestConfiguration, TestParameters, - TestingContext, -}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; #[gpu_test] static REINTERPRET_SRGB: GpuTestConfiguration = GpuTestConfiguration::new() @@ -195,10 +192,10 @@ fn reinterpret( let expect = expect_data[(h * size.width + w) as usize]; let tolerance = tolerance_data[(h * size.width + w) as usize]; let index = (w * 4 + offset) as usize; - if calc_difference(expect[0], data[index]) > tolerance[0] - || calc_difference(expect[1], data[index + 1]) > tolerance[1] - || calc_difference(expect[2], data[index + 2]) > tolerance[2] - || calc_difference(expect[3], data[index + 3]) > tolerance[3] + if expect[0].abs_diff(data[index]) > tolerance[0] + || expect[1].abs_diff(data[index + 1]) > tolerance[1] + || expect[2].abs_diff(data[index + 2]) > tolerance[2] + || expect[3].abs_diff(data[index + 3]) > tolerance[3] { panic!( "Reinterpret {:?} as {:?} mismatch! expect {:?} get [{}, {}, {}, {}]", diff --git a/wgpu-info/src/report.rs b/wgpu-info/src/report.rs index 656e96ee87..3b50ebcf0b 100644 --- a/wgpu-info/src/report.rs +++ b/wgpu-info/src/report.rs @@ -7,6 +7,9 @@ use wgpu::{ use crate::texture; +/// Report specifying the capabilities of the GPUs on the system. +/// +/// Must be synchronized with the definition on tests/src/report.rs. #[derive(Deserialize, Serialize)] pub struct GpuReport { pub devices: Vec, @@ -48,6 +51,9 @@ impl GpuReport { } } +/// A single report of the capabilities of an Adapter. +/// +/// Must be synchronized with the definition on tests/src/report.rs. #[derive(Deserialize, Serialize)] pub struct AdapterReport { pub info: AdapterInfo, diff --git a/wgpu-macros/src/lib.rs b/wgpu-macros/src/lib.rs index bfb2c63509..0b0812507f 100644 --- a/wgpu-macros/src/lib.rs +++ b/wgpu-macros/src/lib.rs @@ -3,6 +3,9 @@ use proc_macro::TokenStream; use quote::quote; use syn::Ident; +/// Creates a test that will run on all gpus on a given system. +/// +/// Apply this macro to a static variable with a type that can be converted to a `GpuTestConfiguration`. #[proc_macro_attribute] pub fn gpu_test(_attr: TokenStream, item: TokenStream) -> TokenStream { let input_static = syn::parse_macro_input!(item as syn::ItemStatic); From 928c840eeb0a17fd9c272604c8a6bf0434683dda Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 12 Oct 2023 00:35:10 -0400 Subject: [PATCH 31/41] Fix readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b721db2d47..98ce237281 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ To install it, run `cargo install cargo-nextest`. To run the test suite: ``` -cargo nextest run --no-fail-fast +cargo xtask test ``` To run the test suite on WebGL (currently incomplete): From 4cc4883326f642f89c32f00f9d1cbbb5369b6abb Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 12 Oct 2023 21:34:21 -0400 Subject: [PATCH 32/41] Disable swiftshader --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e202ef0e41..412f1f2c4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -302,10 +302,10 @@ jobs: run: | set -e - mkdir -p swiftshader - curl -LsSf https://github.com/gfx-rs/ci-build/releases/latest/download/swiftshader-linux-x86_64.tar.xz | tar -xf - -C swiftshader + # mkdir -p swiftshader + # curl -LsSf https://github.com/gfx-rs/ci-build/releases/latest/download/swiftshader-linux-x86_64.tar.xz | tar -xf - -C swiftshader - echo "VK_ICD_FILENAMES=$PWD/swiftshader/vk_swiftshader_icd.json" >> $GITHUB_ENV + # echo "VK_ICD_FILENAMES=$PWD/swiftshader/vk_swiftshader_icd.json" >> $GITHUB_ENV - name: install llvmpipe, vulkan sdk if: matrix.os == 'ubuntu-22.04' From b2ced48a158ed416fd888fe0cfbadf51139cb198 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 12 Oct 2023 22:24:26 -0400 Subject: [PATCH 33/41] Enable lavapipe --- .github/workflows/ci.yml | 15 +++------------ xtask/src/test.rs | 2 +- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 412f1f2c4b..a20cd0f840 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -296,17 +296,6 @@ jobs: rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal cargo -V - - name: install swiftshader - if: matrix.os == 'ubuntu-22.04' - shell: bash - run: | - set -e - - # mkdir -p swiftshader - # curl -LsSf https://github.com/gfx-rs/ci-build/releases/latest/download/swiftshader-linux-x86_64.tar.xz | tar -xf - -C swiftshader - - # echo "VK_ICD_FILENAMES=$PWD/swiftshader/vk_swiftshader_icd.json" >> $GITHUB_ENV - - name: install llvmpipe, vulkan sdk if: matrix.os == 'ubuntu-22.04' shell: bash @@ -319,8 +308,10 @@ jobs: wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list + sudo add-apt-repository ppa:kisak/kisak-mesa + sudo apt-get update - sudo apt install -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev vulkan-sdk + sudo apt install -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev vulkan-sdk mesa-vulkan-drivers - name: disable debug shell: bash diff --git a/xtask/src/test.rs b/xtask/src/test.rs index 96afdd0ab4..dfd277e265 100644 --- a/xtask/src/test.rs +++ b/xtask/src/test.rs @@ -43,7 +43,7 @@ pub fn run_tests(mut args: Arguments) -> anyhow::Result<()> { log::info!("Running cargo tests"); - xshell::cmd!(shell, "cargo {llvm_cov_nextest_flags...} --no-fail-fast") + xshell::cmd!(shell, "cargo {llvm_cov_nextest_flags...} --no-fail-fast --retries 2") .args(args.finish()) .quiet() .run() From 86c1bdbdad52ec4d4b0dda6920e3020a4860fa6b Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 12 Oct 2023 23:24:34 -0400 Subject: [PATCH 34/41] Significantly reduce the amount of skipped tests --- examples/hello-compute/src/tests.rs | 10 +--------- examples/shadow/src/main.rs | 5 ----- tests/tests/clear_texture.rs | 8 +------- tests/tests/device.rs | 2 +- tests/tests/pipeline.rs | 1 - tests/tests/shader/zero_init_workgroup_mem.rs | 13 ------------- tests/tests/shader_view_format/mod.rs | 6 +----- tests/tests/zero_init_texture_after_discard.rs | 4 ++-- xtask/src/test.rs | 13 ++++++++----- 9 files changed, 14 insertions(+), 48 deletions(-) diff --git a/examples/hello-compute/src/tests.rs b/examples/hello-compute/src/tests.rs index e93fafbe77..d435b011fb 100644 --- a/examples/hello-compute/src/tests.rs +++ b/examples/hello-compute/src/tests.rs @@ -61,15 +61,7 @@ static MULTITHREADED_COMPUTE: GpuTestConfiguration = GpuTestConfiguration::new() .downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS) .limits(wgpu::Limits::downlevel_defaults()) .features(wgpu::Features::TIMESTAMP_QUERY) - .skip(FailureCase::adapter("V3D")) - // https://github.com/gfx-rs/wgpu/issues/3944 - .skip(FailureCase::backend_adapter( - wgpu::Backends::VULKAN, - "swiftshader", - )) - // https://github.com/gfx-rs/wgpu/issues/3250 - .skip(FailureCase::backend_adapter(wgpu::Backends::GL, "llvmpipe")) - .skip(FailureCase::molten_vk()), + .skip(FailureCase::adapter("V3D")), ) .run_sync(|ctx| { use std::{sync::mpsc, sync::Arc, thread, time::Duration}; diff --git a/examples/shadow/src/main.rs b/examples/shadow/src/main.rs index a10bb27559..2492dad9ba 100644 --- a/examples/shadow/src/main.rs +++ b/examples/shadow/src/main.rs @@ -860,11 +860,6 @@ static TEST: wgpu_example::framework::ExampleTestParams = .expect_fail(wgpu_test::FailureCase::backend_adapter( wgpu::Backends::VULKAN, "V3D", - )) - // llvmpipe versions in CI are flaky: https://github.com/gfx-rs/wgpu/issues/2594 - .skip(wgpu_test::FailureCase::backend_adapter( - wgpu::Backends::VULKAN, - "llvmpipe", )), comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], _phantom: std::marker::PhantomData::, diff --git a/tests/tests/clear_texture.rs b/tests/tests/clear_texture.rs index 6676449840..361c4cd290 100644 --- a/tests/tests/clear_texture.rs +++ b/tests/tests/clear_texture.rs @@ -328,11 +328,7 @@ fn clear_texture_tests(ctx: &TestingContext, formats: &[wgpu::TextureFormat]) { #[gpu_test] static CLEAR_TEXTURE_UNCOMPRESSED_GLES: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters( - TestParameters::default() - .skip(FailureCase::webgl2()) - .features(wgpu::Features::CLEAR_TEXTURE), - ) + .parameters(TestParameters::default().features(wgpu::Features::CLEAR_TEXTURE)) .run_sync(|ctx| { clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT); }); @@ -341,7 +337,6 @@ static CLEAR_TEXTURE_UNCOMPRESSED_GLES: GpuTestConfiguration = GpuTestConfigurat static CLEAR_TEXTURE_UNCOMPRESSED: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() - .skip(FailureCase::webgl2()) .expect_fail(FailureCase::backend(wgpu::Backends::GL)) .features(wgpu::Features::CLEAR_TEXTURE), ) @@ -353,7 +348,6 @@ static CLEAR_TEXTURE_UNCOMPRESSED: GpuTestConfiguration = GpuTestConfiguration:: static CLEAR_TEXTURE_DEPTH: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() - .skip(FailureCase::webgl2()) .downlevel_flags( wgpu::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | wgpu::DownlevelFlags::COMPUTE_SHADERS, diff --git a/tests/tests/device.rs b/tests/tests/device.rs index 4629475190..3135587fcf 100644 --- a/tests/tests/device.rs +++ b/tests/tests/device.rs @@ -92,7 +92,7 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne .parameters( TestParameters::default() .features(wgpu::Features::CLEAR_TEXTURE) - .skip(FailureCase::backend(wgpu::Backends::DX12)), + .expect_fail(FailureCase::backend(wgpu::Backends::DX12)), ) .run_sync(|ctx| { // Create some resources on the device that we will attempt to use *after* losing diff --git a/tests/tests/pipeline.rs b/tests/tests/pipeline.rs index 2a564c9302..2733bf2f81 100644 --- a/tests/tests/pipeline.rs +++ b/tests/tests/pipeline.rs @@ -7,7 +7,6 @@ use wgpu_test::{fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameter static PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() - .skip(FailureCase::webgl2()) // https://github.com/gfx-rs/wgpu/issues/4167 .expect_fail(FailureCase::always()), ) diff --git a/tests/tests/shader/zero_init_workgroup_mem.rs b/tests/tests/shader/zero_init_workgroup_mem.rs index 8e03985991..fe052451ed 100644 --- a/tests/tests/shader/zero_init_workgroup_mem.rs +++ b/tests/tests/shader/zero_init_workgroup_mem.rs @@ -15,19 +15,6 @@ static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration:: .parameters( TestParameters::default() .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) - // remove both of these once we get to https://github.com/gfx-rs/wgpu/issues/3193 or - // https://github.com/gfx-rs/wgpu/issues/3160 - .skip(FailureCase { - backends: Some(Backends::DX12), - vendor: Some(5140), - adapter: Some("Microsoft Basic Render Driver"), - ..FailureCase::default() - }) - .skip(FailureCase::backend_adapter( - Backends::VULKAN, - "swiftshader", - )) - .skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe")) .limits(Limits::downlevel_defaults()), ) .run_sync(|ctx| { diff --git a/tests/tests/shader_view_format/mod.rs b/tests/tests/shader_view_format/mod.rs index b7f56886be..bca8330909 100644 --- a/tests/tests/shader_view_format/mod.rs +++ b/tests/tests/shader_view_format/mod.rs @@ -6,11 +6,7 @@ static REINTERPRET_SRGB: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() .downlevel_flags(DownlevelFlags::VIEW_FORMATS) - .limits(Limits::downlevel_defaults()) - .skip(FailureCase { - backends: Some(wgpu::Backends::GL), - ..FailureCase::default() - }), + .limits(Limits::downlevel_defaults()), ) .run_sync(|ctx| { let unorm_data: [[u8; 4]; 4] = [ diff --git a/tests/tests/zero_init_texture_after_discard.rs b/tests/tests/zero_init_texture_after_discard.rs index 1798c817be..584cfdb7b8 100644 --- a/tests/tests/zero_init_texture_after_discard.rs +++ b/tests/tests/zero_init_texture_after_discard.rs @@ -8,7 +8,7 @@ use wgpu_test::{ #[gpu_test] static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_AFTER_SUBMIT: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().skip(FailureCase::webgl2())) + .parameters(TestParameters::default().expect_fail(FailureCase::webgl2())) .run_sync(|mut ctx| { let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb); case.create_command_encoder(); @@ -25,7 +25,7 @@ static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_A #[gpu_test] static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().skip(FailureCase::webgl2())) + .parameters(TestParameters::default().expect_fail(FailureCase::webgl2())) .run_sync(|mut ctx| { let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb); case.create_command_encoder(); diff --git a/xtask/src/test.rs b/xtask/src/test.rs index dfd277e265..6d003c7a9f 100644 --- a/xtask/src/test.rs +++ b/xtask/src/test.rs @@ -43,11 +43,14 @@ pub fn run_tests(mut args: Arguments) -> anyhow::Result<()> { log::info!("Running cargo tests"); - xshell::cmd!(shell, "cargo {llvm_cov_nextest_flags...} --no-fail-fast --retries 2") - .args(args.finish()) - .quiet() - .run() - .context("Tests failed")?; + xshell::cmd!( + shell, + "cargo {llvm_cov_nextest_flags...} --no-fail-fast --retries 2" + ) + .args(args.finish()) + .quiet() + .run() + .context("Tests failed")?; log::info!("Finished tests"); From 01b06a578502ebcd60ee16a9dbec80c9fe13d6e4 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 12 Oct 2023 23:29:04 -0400 Subject: [PATCH 35/41] Clippy --- tests/tests/shader/zero_init_workgroup_mem.rs | 4 ++-- tests/tests/shader_view_format/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/tests/shader/zero_init_workgroup_mem.rs b/tests/tests/shader/zero_init_workgroup_mem.rs index fe052451ed..b8985b29ca 100644 --- a/tests/tests/shader/zero_init_workgroup_mem.rs +++ b/tests/tests/shader/zero_init_workgroup_mem.rs @@ -1,14 +1,14 @@ use std::num::NonZeroU64; use wgpu::{ - include_wgsl, Backends, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, + include_wgsl, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BufferBinding, BufferBindingType, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, ComputePassDescriptor, ComputePipelineDescriptor, DownlevelFlags, Limits, Maintain, MapMode, PipelineLayoutDescriptor, ShaderStages, }; -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; #[gpu_test] static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/tests/tests/shader_view_format/mod.rs b/tests/tests/shader_view_format/mod.rs index bca8330909..5e7bed9b08 100644 --- a/tests/tests/shader_view_format/mod.rs +++ b/tests/tests/shader_view_format/mod.rs @@ -1,5 +1,5 @@ use wgpu::{util::DeviceExt, DownlevelFlags, Limits, TextureFormat}; -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; #[gpu_test] static REINTERPRET_SRGB: GpuTestConfiguration = GpuTestConfiguration::new() From 1ba4ee9ba0d9c99381cb15eea6cb2001b6e6daa9 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 12 Oct 2023 23:43:20 -0400 Subject: [PATCH 36/41] Too optimistic about zero_init_workgroup_memory --- tests/tests/shader/zero_init_workgroup_mem.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/tests/shader/zero_init_workgroup_mem.rs b/tests/tests/shader/zero_init_workgroup_mem.rs index b8985b29ca..86462f1d74 100644 --- a/tests/tests/shader/zero_init_workgroup_mem.rs +++ b/tests/tests/shader/zero_init_workgroup_mem.rs @@ -1,21 +1,28 @@ use std::num::NonZeroU64; use wgpu::{ - include_wgsl, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, + include_wgsl, Backends, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BufferBinding, BufferBindingType, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, ComputePassDescriptor, ComputePipelineDescriptor, DownlevelFlags, Limits, Maintain, MapMode, PipelineLayoutDescriptor, ShaderStages, }; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; #[gpu_test] static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) - .limits(Limits::downlevel_defaults()), + .limits(Limits::downlevel_defaults()) + // remove once we get to https://github.com/gfx-rs/wgpu/issues/3193 + .skip(FailureCase { + backends: Some(Backends::DX12), + vendor: Some(5140), + adapter: Some("Microsoft Basic Render Driver"), + ..FailureCase::default() + }), ) .run_sync(|ctx| { let bgl = ctx From 52d5c555fcbfc8442bc03da67782d7620ee18957 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Fri, 13 Oct 2023 00:47:22 -0400 Subject: [PATCH 37/41] Fix webgl2 --- tests/tests/clear_texture.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/tests/clear_texture.rs b/tests/tests/clear_texture.rs index 361c4cd290..ad3a55d711 100644 --- a/tests/tests/clear_texture.rs +++ b/tests/tests/clear_texture.rs @@ -328,7 +328,11 @@ fn clear_texture_tests(ctx: &TestingContext, formats: &[wgpu::TextureFormat]) { #[gpu_test] static CLEAR_TEXTURE_UNCOMPRESSED_GLES: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::CLEAR_TEXTURE)) + .parameters( + TestParameters::default() + .features(wgpu::Features::CLEAR_TEXTURE) + .skip(FailureCase::webgl2()), + ) .run_sync(|ctx| { clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT); }); @@ -352,6 +356,7 @@ static CLEAR_TEXTURE_DEPTH: GpuTestConfiguration = GpuTestConfiguration::new() wgpu::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | wgpu::DownlevelFlags::COMPUTE_SHADERS, ) + .skip(FailureCase::webgl2()) .limits(wgpu::Limits::downlevel_defaults()) .features(wgpu::Features::CLEAR_TEXTURE), ) From cb62a2c67418be621e6db8d9b4fc0280c2af1bb2 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Fri, 13 Oct 2023 20:37:53 -0400 Subject: [PATCH 38/41] ZIWM --- tests/tests/shader/zero_init_workgroup_mem.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tests/shader/zero_init_workgroup_mem.rs b/tests/tests/shader/zero_init_workgroup_mem.rs index 86462f1d74..e5a30c3b14 100644 --- a/tests/tests/shader/zero_init_workgroup_mem.rs +++ b/tests/tests/shader/zero_init_workgroup_mem.rs @@ -22,7 +22,8 @@ static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration:: vendor: Some(5140), adapter: Some("Microsoft Basic Render Driver"), ..FailureCase::default() - }), + }) + .skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe")), ) .run_sync(|ctx| { let bgl = ctx From 2aebfbc22f244ada7b24db7d7239874fea48e22a Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Fri, 13 Oct 2023 20:44:07 -0400 Subject: [PATCH 39/41] Disable wgpu_backend --- tests/src/init.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/src/init.rs b/tests/src/init.rs index 3e7e20fe3b..51a129eaf4 100644 --- a/tests/src/init.rs +++ b/tests/src/init.rs @@ -3,7 +3,11 @@ use wgt::{Backends, Features, Limits}; /// Initialize a wgpu instance with the options from the environment. pub fn initialize_instance() -> Instance { - let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(Backends::all); + // 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. + let backends = Backends::all(); let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(); let gles_minor_version = wgpu::util::gles_minor_version_from_env().unwrap_or_default(); Instance::new(wgpu::InstanceDescriptor { From d76b9a659c8235aa865a659921c40e9e6bbde936 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Fri, 13 Oct 2023 21:53:32 -0400 Subject: [PATCH 40/41] Enable metal validation --- tests/src/native.rs | 17 ++++++++++++++++- wgpu-hal/src/metal/mod.rs | 3 ++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/src/native.rs b/tests/src/native.rs index e0e4da5179..e7aaa5b1e7 100644 --- a/tests/src/native.rs +++ b/tests/src/native.rs @@ -24,7 +24,7 @@ impl NativeTest { adapter: &AdapterReport, adapter_index: usize, ) -> Self { - let backend = &adapter.info.backend; + let backend = adapter.info.backend; let device_name = &adapter.info.name; let test_info = TestInfo::from_configuration(&config, adapter); @@ -37,6 +37,21 @@ impl NativeTest { Self { name: full_name, future: Box::pin(async move { + // Enable metal validation layers if we're running on metal. + // + // This is a process-wide setting as it's via environment variable, but all + // tests are run in separate processes. + // + // We don't do this in the instance initializer as we don't want to enable + // validation layers for the entire process, or other instances. + // + // We do not enable metal validation when running on moltenvk. + let metal_validation = backend == wgpu::Backend::Metal; + + let env_value = if metal_validation { "1" } else { "0" }; + std::env::set_var("MTL_DEBUG_LAYER", env_value); + std::env::set_var("MTL_SHADER_VALIDATION", env_value); + execute_test(config, Some(test_info), adapter_index).await; }), } diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index 38ee2ebd46..a75439096a 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -80,7 +80,8 @@ impl Instance { impl crate::Instance for Instance { unsafe fn init(_desc: &crate::InstanceDescriptor) -> Result { - //TODO: enable `METAL_DEVICE_WRAPPER_TYPE` environment based on the flags? + // We do not enable metal validation based on the validation flags as it affects the entire + // process. Instead, we enable the validation inside the test harness itself in tests/src/native.rs. Ok(Instance { managed_metal_layer_delegate: surface::HalManagedMetalLayerDelegate::new(), }) From c7c7155da586951599cfa8c9fcb277a25b131fc9 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Sat, 14 Oct 2023 00:41:31 -0400 Subject: [PATCH 41/41] Remove unnecessary skip on lavapipe --- examples/hello-compute/src/main.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/hello-compute/src/main.rs b/examples/hello-compute/src/main.rs index a645aa662d..c7d09c6a4a 100644 --- a/examples/hello-compute/src/main.rs +++ b/examples/hello-compute/src/main.rs @@ -56,12 +56,6 @@ async fn execute_gpu(numbers: &[u32]) -> Option> { .await .unwrap(); - let info = adapter.get_info(); - // skip this on LavaPipe temporarily - if info.vendor == 0x10005 { - return None; - } - execute_gpu_inner(&device, &queue, numbers).await }