From c0664c2bb7eae4d897227fd540c2b612bf5cb9c3 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Mon, 21 Jun 2021 22:34:00 -0400 Subject: [PATCH] Begin robust exception system --- Cargo.lock | 1 + wgpu/Cargo.toml | 1 + wgpu/examples/conservative-raster/main.rs | 2 +- wgpu/examples/framework.rs | 1 + wgpu/examples/mipmap/main.rs | 2 +- wgpu/examples/skybox/main.rs | 6 +- wgpu/src/lib.rs | 2 +- wgpu/tests/common/mod.rs | 151 +++++++++++----------- 8 files changed, 88 insertions(+), 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15072ef562..3a3004d343 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1895,6 +1895,7 @@ version = "0.8.0" dependencies = [ "arrayvec", "async-executor", + "bitflags", "bytemuck", "cgmath", "console_error_panic_hook", diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 382d4f46c5..a4c9b26b38 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -55,6 +55,7 @@ serde = { version = "1", features = ["derive"], optional = true } smallvec = "1" [dev-dependencies] +bitflags = "1" bytemuck = { version = "1.4", features = ["derive"] } cgmath = "0.18" ddsfile = "0.4" diff --git a/wgpu/examples/conservative-raster/main.rs b/wgpu/examples/conservative-raster/main.rs index 5a35ff283d..dc8bf1d7fa 100644 --- a/wgpu/examples/conservative-raster/main.rs +++ b/wgpu/examples/conservative-raster/main.rs @@ -323,7 +323,7 @@ fn conservative_raster() { 1024, 768, wgpu::Features::default(), - framework::test_common::TestParameters::default(), + framework::test_common::TestParameters::default().failure(), 0, 0, ); diff --git a/wgpu/examples/framework.rs b/wgpu/examples/framework.rs index 89935cdc2e..7c8bc69420 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -439,6 +439,7 @@ pub fn test( texture: &dst_texture, mip_level: 0, origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, }, wgpu::ImageCopyBuffer { buffer: &dst_buffer, diff --git a/wgpu/examples/mipmap/main.rs b/wgpu/examples/mipmap/main.rs index 29ff698592..7545a0f9ae 100644 --- a/wgpu/examples/mipmap/main.rs +++ b/wgpu/examples/mipmap/main.rs @@ -485,7 +485,7 @@ fn mipmap() { 1024, 768, wgpu::Features::default(), - framework::test_common::TestParameters::default().backend_failures(wgpu::BackendBit::DX11), + framework::test_common::TestParameters::default().backend_failures(wgpu::BackendBit::VULKAN), 25, 3000, // Mipmap sampling is highly variant between impls. This is currently bounded by AMD on mac ); diff --git a/wgpu/examples/skybox/main.rs b/wgpu/examples/skybox/main.rs index a0dfd7928d..4675c489d6 100644 --- a/wgpu/examples/skybox/main.rs +++ b/wgpu/examples/skybox/main.rs @@ -470,7 +470,7 @@ fn skybox() { 1024, 768, wgpu::Features::default(), - framework::test_common::TestParameters::default(), + framework::test_common::TestParameters::default().backend_failures(wgpu::BackendBit::VULKAN), 2, 3, ); @@ -503,7 +503,7 @@ fn skybox_etc2() { 768, wgpu::Features::TEXTURE_COMPRESSION_ETC2, framework::test_common::TestParameters::default(), - 5, // TODO + 5, // TODO 10, // TODO ); } @@ -519,7 +519,7 @@ fn skybox_astc() { 768, wgpu::Features::TEXTURE_COMPRESSION_ASTC_LDR, framework::test_common::TestParameters::default(), - 5, // TODO + 5, // TODO 10, // TODO ); } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 10420e32d5..5429041320 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -1525,7 +1525,7 @@ impl Adapter { } /// Get info about the adapter itself. - pub fn get_downlevel_properties(&self) -> DownlevelProperties { + pub fn get_downlevel_properties(&self) -> DownlevelCapabilities { Context::adapter_downlevel_properties(&*self.context, &self.id) } diff --git a/wgpu/tests/common/mod.rs b/wgpu/tests/common/mod.rs index e202f05a99..b0ef8fef45 100644 --- a/wgpu/tests/common/mod.rs +++ b/wgpu/tests/common/mod.rs @@ -3,7 +3,7 @@ use std::panic::{catch_unwind, AssertUnwindSafe}; -use wgt::{BackendBit, DeviceDescriptor, DownlevelProperties, Features, Limits}; +use wgt::{BackendBit, DeviceDescriptor, DownlevelCapabilities, Features, Limits}; use wgpu::{util, Adapter, Device, Instance, Queue}; @@ -49,7 +49,7 @@ pub fn lowest_reasonable_limits() -> Limits { max_bind_groups: 2, max_dynamic_uniform_buffers_per_pipeline_layout: 2, max_dynamic_storage_buffers_per_pipeline_layout: 2, - max_sampled_textures_per_shader_stage:2, + max_sampled_textures_per_shader_stage: 2, max_samplers_per_shader_stage: 2, max_storage_buffers_per_shader_stage: 2, max_storage_textures_per_shader_stage: 2, @@ -63,8 +63,8 @@ pub fn lowest_reasonable_limits() -> Limits { } } -fn lowest_downlevel_properties() -> DownlevelProperties { - DownlevelProperties { +fn lowest_downlevel_properties() -> DownlevelCapabilities { + DownlevelCapabilities { flags: wgt::DownlevelFlags::empty(), shader_model: wgt::ShaderModel::Sm2, } @@ -74,15 +74,9 @@ fn lowest_downlevel_properties() -> DownlevelProperties { pub struct TestParameters { pub required_features: Features, pub required_limits: Limits, - pub required_downlevel_properties: DownlevelProperties, - // Test should always fail - pub always_failure: bool, + pub required_downlevel_properties: DownlevelCapabilities, // Backends where test should fail. - pub backend_failures: BackendBit, - // Vendors where test should fail. - pub vendor_failures: &'static [usize], - // Device names where test should fail. - pub device_failures: &'static [&'static str], + pub failures: Vec<(Option, Option, Option)>, } impl Default for TestParameters { @@ -91,14 +85,20 @@ impl Default for TestParameters { required_features: Features::empty(), required_limits: lowest_reasonable_limits(), required_downlevel_properties: lowest_downlevel_properties(), - always_failure: false, - backend_failures: BackendBit::empty(), - vendor_failures: &[], - device_failures: &[], + failures: Vec::new(), } } } +bitflags::bitflags! { + pub struct FailureReason: u8 { + const BACKEND = 0x1; + const VENDOR = 0x2; + const ADAPTER = 0x4; + const ALWAYS = 0x8; + } +} + // Builder pattern to make it easier impl TestParameters { /// Set of common features that most tests require. @@ -106,23 +106,46 @@ impl TestParameters { self.features(Features::MAPPABLE_PRIMARY_BUFFERS | Features::VERTEX_WRITABLE_STORAGE) } + /// Set the list of features this test requires. pub fn features(mut self, features: Features) -> Self { self.required_features |= features; self } + /// Set the list pub fn limits(mut self, limits: Limits) -> Self { self.required_limits = limits; self } - pub fn backend_failures(mut self, backends: BackendBit) -> Self { - self.backend_failures |= backends; + /// Mark the test as always failing, equivilant to specific_failure(None, None, None) + pub fn failure(mut self) -> Self { + self.failures.push((None, None, None)); self } - pub fn failure(mut self) -> Self { - self.always_failure = true; + /// Mark the test as always failing on a specific backend, equivilant to specific_failure(backend, None, None) + pub fn backend_failures(mut self, backends: wgpu::BackendBit) -> Self { + self.failures.push((Some(backends), None, None)); + self + } + + /// Determines if a test should fail under a particular set of conditions. If any of these are None, that means that it will match anything in that field. + /// + /// ex. + /// `specific_failure(Some(wgpu::BackendBit::DX11 | wgpu::BackendBit::DX12), None, Some("RTX"))` + /// means that this test will fail on all cards with RTX in their name on either D3D backend, no matter the vendor ID. + pub fn specific_failure( + mut self, + backends: Option, + vendor: Option, + device: Option<&'static str>, + ) -> Self { + self.failures.push(( + backends, + vendor, + device.as_ref().map(AsRef::as_ref).map(str::to_lowercase), + )); self } } @@ -191,66 +214,50 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te let panicked = catch_unwind(AssertUnwindSafe(|| test_function(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 failure_reason = parameters.failures.iter().find_map( + |(backend_failure, vendor_failure, adapter_failure)| { + let always = + backend_failure.is_none() && vendor_failure.is_none() && adapter_failure.is_none(); - let expect_failure = parameters.always_failure - || expect_failure_backend - || expect_failure_vendor - || expect_failure_device.is_some(); + let expect_failure_backend = backend_failure + .map(|f| f.contains(wgpu::BackendBit::from(adapter_info.backend))) + .unwrap_or(true); + let expect_failure_vendor = vendor_failure + .map(|v| v == adapter_info.vendor) + .unwrap_or(true); + let expect_failure_adapter = adapter_failure + .as_deref() + .map(|f| adapter_lowercase_name.contains(f)) + .unwrap_or(true); + + if expect_failure_backend && expect_failure_vendor && expect_failure_adapter { + if always { + Some(FailureReason::ALWAYS) + } else { + let mut reason = FailureReason::empty(); + reason.set(FailureReason::BACKEND, expect_failure_backend); + reason.set(FailureReason::VENDOR, expect_failure_vendor); + reason.set(FailureReason::ADAPTER, expect_failure_adapter); + Some(reason) + } + } else { + None + } + }, + ); + + let expect_failure = failure_reason.is_some(); if panicked == expect_failure { // We got the conditions we expected - if expect_failure { + if let Some(reason) = failure_reason { // 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 - ); - } + println!("GOT EXPECTED TEST FAILURE: {:?}", reason); } } else { - if expect_failure { + if let Some(reason) = failure_reason { // 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!() + panic!("UNEXPECTED TEST PASS: {:?}", reason); } else { panic!("UNEXPECTED TEST FAILURE") }