Make wgpu-info into a proper CLI Tool (#3856)

This commit is contained in:
Connor Fitzgerald 2023-06-15 15:56:15 -04:00 committed by GitHub
parent acb7712c5a
commit e6be20f72e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 567 additions and 444 deletions

11
Cargo.lock generated
View File

@ -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]]

View File

@ -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 }

View File

@ -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"] }

90
wgpu-info/src/cli.rs Normal file
View File

@ -0,0 +1,90 @@
use std::{io, process::exit};
use anyhow::Context;
const HELP: &str = "\
Usage: wgpu-info [--input <PATH>] [--output <PATH>] [--json]
Options:
-h, --help Print this help message.
-i, --input <PATH> Source to read JSON report from. (\"-\" reads from stdin)
-o, --output <PATH> 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<String> = args.opt_value_from_str(["-i", "--input"]).unwrap();
let output_path: Option<String> = 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(())
}

240
wgpu-info/src/human.rs Normal file
View File

@ -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<bitflags::iter::Iter<Self>> {
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<T> 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(())
}

View File

@ -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<bitflags::iter::Iter<Self>> {
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<T> 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(())
}

58
wgpu-info/src/report.rs Normal file
View File

@ -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<AdapterReport>,
}
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<Self> {
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<TextureFormat, TextureFormatFeatures>,
}

137
wgpu-info/src/texture.rs Normal file
View File

@ -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:?}:")
}
}
}

View File

@ -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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@ -36,7 +36,7 @@ macro_rules! impl_bitflags {
}
}
#[cfg(feature = "replay")]
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D>(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,

View File

@ -1440,7 +1440,7 @@ impl Instance {
target_os = "emscripten",
feature = "webgl"
))]
pub fn enumerate_adapters(&self, backends: Backends) -> impl Iterator<Item = Adapter> {
pub fn enumerate_adapters(&self, backends: Backends) -> impl ExactSizeIterator<Item = Adapter> {
let context = Arc::clone(&self.context);
self.context
.as_any()