diff --git a/Cargo.lock b/Cargo.lock index 0127285e3..8f0573de9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1883,6 +1883,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" @@ -3188,9 +3194,14 @@ dependencies = [ name = "wgpu-info" version = "0.16.0" dependencies = [ + "anyhow", "bitflags 2.3.2", "env_logger", + "pico-args", + "serde", + "serde_json", "wgpu", + "wgpu-types", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 79aaa25c5..d43787f80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ rev = "76003dc0035d53a474d366dcdf49d2e4d12e921f" version = "0.12.0" [workspace.dependencies] +anyhow = "1.0" arrayvec = "0.7" async-executor = "1" bitflags = "2" @@ -82,6 +83,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.9" pollster = "0.2" profiling = { version = "1", default-features = false } diff --git a/wgpu-info/Cargo.toml b/wgpu-info/Cargo.toml index bf9fe449d..a729bbc88 100644 --- a/wgpu-info/Cargo.toml +++ b/wgpu-info/Cargo.toml @@ -3,14 +3,18 @@ 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 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 = ["trace", "replay"] } diff --git a/wgpu-info/src/cli.rs b/wgpu-info/src/cli.rs new file mode 100644 index 000000000..1e81e9f0f --- /dev/null +++ b/wgpu-info/src/cli.rs @@ -0,0 +1,90 @@ +use std::{io, process::exit}; + +use anyhow::Context; + +const HELP: &str = "\ +Usage: wgpu-info [--input ] [--output ] [--json] + +Options: + -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. +"; + +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 { + 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(); + + // Generate or load report + let report = match input_path.as_deref() { + // Pull report from stdin or file + Some(path) => { + 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")? + } + // 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 { + 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 new file mode 100644 index 000000000..66b0e506e --- /dev/null +++ b/wgpu-info/src/human.rs @@ -0,0 +1,240 @@ +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 + } + + 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 impl io::Write) -> io::Result<()> { + 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, "─┐")?; + Ok(()) + } + + 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, "─┴─")?; + } + let length = bit.name().len(); + write!(output, "{}", "─".repeat(length))?; + } + writeln!(output, "─┘")?; + Ok(()) + } +} + +impl FlagsExt for T where T: Flags {} + +// Lets keep these print statements on one line +#[rustfmt::skip] +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())?; + + ////////////// + // 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 = texture::max_texture_format_string_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 = 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, " │ ")?; + 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 = 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::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)?; + Ok(()) +} + +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 aa6fd2f26..9a37976f0 100644 --- a/wgpu-info/src/main.rs +++ b/wgpu-info/src/main.rs @@ -1,435 +1,16 @@ #[cfg(not(target_arch = "wasm32"))] -mod inner { - use std::{ - process::{exit, Command}, - time::Instant, - }; +mod cli; +#[cfg(not(target_arch = "wasm32"))] +mod human; +#[cfg(not(target_arch = "wasm32"))] +mod report; +#[cfg(not(target_arch = "wasm32"))] +mod texture; - 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:?}:") - } - } - } -} - -fn main() { +fn main() -> anyhow::Result<()> { #[cfg(not(target_arch = "wasm32"))] - inner::main(); + { + cli::main()?; + } + Ok(()) } diff --git a/wgpu-info/src/report.rs b/wgpu-info/src/report.rs new file mode 100644 index 000000000..656e96ee8 --- /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 000000000..5bafa67a5 --- /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 7ce565238..ebfc9f65a 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 8e6caa023..6ae0291c0 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()