mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
Fully convert testing infrastructure to wgpu-rs
This commit is contained in:
@@ -1524,6 +1524,11 @@ impl Adapter {
|
||||
Context::adapter_get_info(&*self.context, &self.id)
|
||||
}
|
||||
|
||||
/// Get info about the adapter itself.
|
||||
pub fn get_downlevel_properties(&self) -> DownlevelProperties {
|
||||
Context::adapter_downlevel_properties(&*self.context, &self.id)
|
||||
}
|
||||
|
||||
/// Returns the features supported for a given texture format by this adapter.
|
||||
///
|
||||
/// Note that the WebGPU spec further restricts the available usages/features.
|
||||
|
||||
11
wgpu/tests/core-tests.rs
Normal file
11
wgpu/tests/core-tests.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
mod core_tests {
|
||||
// Contains all the helper routines which facilitate this testing framework.
|
||||
mod common;
|
||||
|
||||
// All files containing tests
|
||||
mod instance;
|
||||
mod device;
|
||||
mod vertex_indexes;
|
||||
}
|
||||
|
||||
pub use core_tests::*;
|
||||
282
wgpu/tests/core_tests/common/init.rs
Normal file
282
wgpu/tests/core_tests/common/init.rs
Normal file
@@ -0,0 +1,282 @@
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
|
||||
// Initialize the instance, obeying the WGPU_BACKEND environment variables.
|
||||
pub fn get_backend_bits() -> wgpu::BackendBit {
|
||||
match std::env::var("WGPU_BACKEND")
|
||||
.as_deref()
|
||||
.map(str::to_lowercase)
|
||||
.as_deref()
|
||||
{
|
||||
Ok("vulkan") => wgpu::BackendBit::VULKAN,
|
||||
Ok("dx12") => wgpu::BackendBit::DX12,
|
||||
Ok("dx11") => wgpu::BackendBit::DX11,
|
||||
Ok("metal") => wgpu::BackendBit::METAL,
|
||||
Ok("gl") => wgpu::BackendBit::GL,
|
||||
Ok("webgpu") => wgpu::BackendBit::BROWSER_WEBGPU,
|
||||
Ok(_) => panic!("unknown wgpu backend"),
|
||||
Err(_) => wgpu::BackendBit::all(),
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the adapter, obeying the WGPU_ADAPTER_NAME environment variable.
|
||||
pub fn initialize_adapter(
|
||||
instance: &wgpu::Instance,
|
||||
backend_bits: wgpu::BackendBit,
|
||||
) -> wgpu::Adapter {
|
||||
let adapters = instance.enumerate_adapters(backend_bits);
|
||||
|
||||
let desired_adapter_name = std::env::var("WGPU_ADAPTER_NAME")
|
||||
.as_deref()
|
||||
.map(str::to_lowercase)
|
||||
.ok();
|
||||
|
||||
let mut chosen_adapter = None;
|
||||
for adapter in adapters {
|
||||
let info = adapter.get_info();
|
||||
|
||||
if chosen_adapter.is_none() {
|
||||
if let Some(ref adapter_name) = desired_adapter_name {
|
||||
if info.name.to_lowercase().contains(adapter_name) {
|
||||
chosen_adapter = Some(adapter);
|
||||
continue; // Skip adapter drop
|
||||
}
|
||||
} else {
|
||||
// Just choose first adapter if there's no preference
|
||||
chosen_adapter = Some(adapter);
|
||||
continue; // Skip adapter drop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chosen_adapter.expect("Could not find an adapter")
|
||||
}
|
||||
|
||||
pub fn initialize_device(
|
||||
adapter: &wgpu::Adapter,
|
||||
features: wgpu::Features,
|
||||
limits: wgpu::Limits,
|
||||
) -> (wgpu::Device, wgpu::Queue) {
|
||||
let bundle = pollster::block_on(adapter.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
features,
|
||||
limits,
|
||||
},
|
||||
None,
|
||||
));
|
||||
|
||||
match bundle {
|
||||
Ok(b) => b,
|
||||
Err(e) => panic!("Failed to initialize device: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestingContext<'a> {
|
||||
pub adapter: &'a wgpu::Adapter,
|
||||
pub adapter_info: wgt::AdapterInfo,
|
||||
pub device: &'a wgpu::Device,
|
||||
pub queue: &'a wgpu::Queue,
|
||||
}
|
||||
|
||||
// A rather arbitrary set of limits which should be lower than all devices wgpu reasonably expects to run on and provides enough resources for most tests to run.
|
||||
// Adjust as needed if they are too low/high.
|
||||
fn lowest_reasonable_limits() -> wgpu::Limits {
|
||||
wgpu::Limits {
|
||||
max_texture_dimension_1d: 512,
|
||||
max_texture_dimension_2d: 512,
|
||||
max_texture_dimension_3d: 32,
|
||||
max_texture_array_layers: 32,
|
||||
max_bind_groups: 1,
|
||||
max_dynamic_uniform_buffers_per_pipeline_layout: 1,
|
||||
max_dynamic_storage_buffers_per_pipeline_layout: 1,
|
||||
max_sampled_textures_per_shader_stage: 1,
|
||||
max_samplers_per_shader_stage: 1,
|
||||
max_storage_buffers_per_shader_stage: 1,
|
||||
max_storage_textures_per_shader_stage: 1,
|
||||
max_uniform_buffers_per_shader_stage: 1,
|
||||
max_uniform_buffer_binding_size: 256,
|
||||
max_storage_buffer_binding_size: 256,
|
||||
max_vertex_buffers: 1,
|
||||
max_vertex_attributes: 2,
|
||||
max_vertex_buffer_array_stride: 32,
|
||||
max_push_constant_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn lowest_downlevel_properties() -> wgpu::DownlevelProperties {
|
||||
wgpu::DownlevelProperties {
|
||||
flags: wgt::DownlevelFlags::empty(),
|
||||
shader_model: wgt::ShaderModel::Sm2,
|
||||
}
|
||||
}
|
||||
|
||||
// This information determines if a test should run.
|
||||
pub struct TestParameters {
|
||||
pub required_features: wgpu::Features,
|
||||
pub required_limits: wgpu::Limits,
|
||||
pub required_downlevel_properties: wgpu::DownlevelProperties,
|
||||
// Test should always fail
|
||||
pub always_failure: bool,
|
||||
// Backends where test should fail.
|
||||
pub backend_failures: wgpu::BackendBit,
|
||||
// Vendors where test should fail.
|
||||
pub vendor_failures: &'static [usize],
|
||||
// Device names where test should fail.
|
||||
pub device_failures: &'static [&'static str],
|
||||
}
|
||||
|
||||
impl Default for TestParameters {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: lowest_reasonable_limits(),
|
||||
required_downlevel_properties: lowest_downlevel_properties(),
|
||||
always_failure: false,
|
||||
backend_failures: wgpu::BackendBit::empty(),
|
||||
vendor_failures: &[],
|
||||
device_failures: &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Builder pattern to make it easier
|
||||
impl TestParameters {
|
||||
pub fn features(mut self, features: wgpu::Features) -> Self {
|
||||
self.required_features &= features;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn failure(mut self) -> Self {
|
||||
self.always_failure = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialize_test(
|
||||
parameters: TestParameters,
|
||||
test_function: impl FnOnce(&mut TestingContext<'_>),
|
||||
) {
|
||||
// We don't actually care if it fails
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let backend_bits = get_backend_bits();
|
||||
let instance = wgpu::Instance::new(backend_bits);
|
||||
let adapter = initialize_adapter(&instance, backend_bits);
|
||||
|
||||
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_properties = adapter.get_downlevel_properties();
|
||||
|
||||
let missing_features = parameters.required_features - adapter_features;
|
||||
if !missing_features.is_empty() {
|
||||
println!("TEST SKIPPED: MISSING FEATURES {:?}", missing_features);
|
||||
return;
|
||||
}
|
||||
|
||||
if adapter_limits < parameters.required_limits {
|
||||
println!("TEST SKIPPED: LIMIT TOO LOW");
|
||||
return;
|
||||
}
|
||||
|
||||
let missing_downlevel_flags =
|
||||
parameters.required_downlevel_properties.flags - adapter_downlevel_properties.flags;
|
||||
if !missing_downlevel_flags.is_empty() {
|
||||
println!(
|
||||
"TEST SKIPPED: MISSING DOWNLEVEL FLAGS {:?}",
|
||||
missing_downlevel_flags
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if adapter_downlevel_properties.shader_model
|
||||
< parameters.required_downlevel_properties.shader_model
|
||||
{
|
||||
println!(
|
||||
"TEST SKIPPED: LOW SHADER MODEL {:?}",
|
||||
adapter_downlevel_properties.shader_model
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let (device, queue) = initialize_device(
|
||||
&adapter,
|
||||
parameters.required_features,
|
||||
parameters.required_limits,
|
||||
);
|
||||
|
||||
let mut context = TestingContext {
|
||||
adapter: &adapter,
|
||||
adapter_info: adapter_info.clone(),
|
||||
device: &device,
|
||||
queue: &queue,
|
||||
};
|
||||
|
||||
let panicked = catch_unwind(AssertUnwindSafe(|| test_function(&mut context))).is_err();
|
||||
|
||||
let expect_failure_backend = parameters
|
||||
.backend_failures
|
||||
.contains(wgt::BackendBit::from(adapter_info.backend));
|
||||
let expect_failure_vendor = parameters
|
||||
.vendor_failures
|
||||
.iter()
|
||||
.find(|&&v| v == adapter_info.vendor)
|
||||
.is_some();
|
||||
let expect_failure_device = parameters
|
||||
.device_failures
|
||||
.iter()
|
||||
.find(|&&v| adapter_lowercase_name.contains(&v.to_lowercase()));
|
||||
|
||||
let expect_failure = parameters.always_failure
|
||||
|| expect_failure_backend
|
||||
|| expect_failure_vendor
|
||||
|| expect_failure_device.is_some();
|
||||
|
||||
if panicked == expect_failure {
|
||||
// We got the conditions we expected
|
||||
if expect_failure {
|
||||
// Print out reason for the failure
|
||||
if parameters.always_failure {
|
||||
println!("GOT EXPECTED TEST FAILURE: ALWAYS");
|
||||
}
|
||||
if expect_failure_backend {
|
||||
println!(
|
||||
"GOT EXPECTED TEST FAILURE: BACKEND {:?}",
|
||||
adapter_info.backend
|
||||
);
|
||||
}
|
||||
if expect_failure_vendor {
|
||||
println!("GOT EXPECTED TEST FAILURE: VENDOR {}", adapter_info.vendor);
|
||||
}
|
||||
if let Some(device_match) = expect_failure_device {
|
||||
println!(
|
||||
"GOT EXPECTED TEST FAILURE: DEVICE {} MATCHED NAME {}",
|
||||
adapter_info.name, device_match
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if expect_failure {
|
||||
// We expected to fail, but things passed
|
||||
if parameters.always_failure {
|
||||
panic!("UNEXPECTED TEST PASS: ALWAYS");
|
||||
}
|
||||
if expect_failure_backend {
|
||||
panic!("UNEXPECTED TEST PASS: BACKEND {:?}", adapter_info.backend);
|
||||
}
|
||||
if expect_failure_vendor {
|
||||
panic!("UNEXPECTED TEST PASS: VENDOR {}", adapter_info.vendor);
|
||||
}
|
||||
if let Some(device_match) = expect_failure_device {
|
||||
panic!(
|
||||
"UNEXPECTED TEST PASS: DEVICE {} MATCHED NAME {}",
|
||||
adapter_info.name, device_match
|
||||
);
|
||||
}
|
||||
unreachable!()
|
||||
} else {
|
||||
panic!("UNEXPECTED TEST FAILURE")
|
||||
}
|
||||
}
|
||||
}
|
||||
1
wgpu/tests/core_tests/common/mod.rs
Normal file
1
wgpu/tests/core_tests/common/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod init;
|
||||
8
wgpu/tests/core_tests/device.rs
Normal file
8
wgpu/tests/core_tests/device.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use crate::core_tests::common::init::{initialize_test, TestParameters};
|
||||
|
||||
#[test]
|
||||
fn device_initialization() {
|
||||
initialize_test(TestParameters::default(), |_ctx| {
|
||||
// intentionally empty
|
||||
})
|
||||
}
|
||||
26
wgpu/tests/core_tests/instance.rs
Normal file
26
wgpu/tests/core_tests/instance.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use crate::core_tests::common;
|
||||
|
||||
#[test]
|
||||
fn initialize() {
|
||||
let _ = wgpu::Instance::new(common::init::get_backend_bits());
|
||||
}
|
||||
|
||||
fn request_adapter_inner(power: wgt::PowerPreference) {
|
||||
let instance = wgpu::Instance::new(common::init::get_backend_bits());
|
||||
|
||||
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: power,
|
||||
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);
|
||||
}
|
||||
13
wgpu/tests/core_tests/vertex_indexes/draw.vert.wgsl
Normal file
13
wgpu/tests/core_tests/vertex_indexes/draw.vert.wgsl
Normal file
@@ -0,0 +1,13 @@
|
||||
[[block]]
|
||||
struct Indices {
|
||||
arr: array<u32>;
|
||||
}; // this is used as both input and output for convenience
|
||||
|
||||
[[group(0), binding(0)]]
|
||||
var<storage> indices: [[access(read_write)]] Indices;
|
||||
|
||||
[[stage(vertex)]]
|
||||
fn vs_main([[builtin(vertex_index)]] instance: u32, [[builtin(vertex_index)]] index: u32) -> [[builtin(position)]] vec4<f32> {
|
||||
indices.arr[index] = instance;
|
||||
return vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
13
wgpu/tests/core_tests/vertex_indexes/mod.rs
Normal file
13
wgpu/tests/core_tests/vertex_indexes/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use crate::core_tests::common::init::{initialize_test, TestParameters};
|
||||
|
||||
#[test]
|
||||
fn draw() {
|
||||
initialize_test(
|
||||
TestParameters::default().features(wgpu::Features::VERTEX_WRITABLE_STORAGE),
|
||||
|ctx| {
|
||||
let shader_module = ctx
|
||||
.device
|
||||
.create_shader_module(&wgpu::include_wgsl!("draw.vert.wgsl"));
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,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() {
|
||||
let read_dir = match PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
|
||||
Reference in New Issue
Block a user