1use arrayvec::ArrayVec;
2use wgpu::{DownlevelCapabilities, DownlevelFlags, Features, InstanceFlags, Limits};
3
4use crate::{
5 report::AdapterReport, FailureApplicationReasons, FailureBehavior, FailureCase,
6 GpuTestConfiguration,
7};
8
9const LOWEST_DOWNLEVEL_PROPERTIES: wgpu::DownlevelCapabilities = DownlevelCapabilities {
10 flags: wgpu::DownlevelFlags::empty(),
11 limits: wgpu::DownlevelLimits {},
12 shader_model: wgpu::ShaderModel::Sm2,
13};
14
15#[derive(Clone)]
17pub struct TestParameters {
18 pub required_features: Features,
19 pub required_downlevel_caps: DownlevelCapabilities,
20 pub required_limits: Limits,
21
22 pub required_instance_flags: InstanceFlags,
23
24 pub force_fxc: bool,
28
29 pub skips: Vec<FailureCase>,
31
32 pub failures: Vec<FailureCase>,
34
35 pub disable_mtl_shader_validation: bool,
38}
39
40impl Default for TestParameters {
41 fn default() -> Self {
42 Self {
43 required_features: Features::empty(),
44 required_downlevel_caps: LOWEST_DOWNLEVEL_PROPERTIES,
45 required_limits: Limits::downlevel_webgl2_defaults(),
46 required_instance_flags: InstanceFlags::empty(),
47 force_fxc: false,
48 skips: vec![FailureCase::backend(wgpu::Backends::NOOP)],
51 failures: Vec::new(),
52 disable_mtl_shader_validation: false,
53 }
54 }
55}
56
57impl TestParameters {
59 pub fn test_features_limits(self) -> Self {
61 self.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
62 .limits(wgpu::Limits::downlevel_defaults())
63 }
64
65 pub fn features(mut self, features: Features) -> Self {
67 self.required_features |= features;
68 self
69 }
70
71 pub fn downlevel_flags(mut self, downlevel_flags: DownlevelFlags) -> Self {
72 self.required_downlevel_caps.flags |= downlevel_flags;
73 self
74 }
75
76 pub fn limits(mut self, limits: Limits) -> Self {
78 self.required_limits = limits;
79 self
80 }
81
82 pub fn instance_flags(mut self, instance_flags: InstanceFlags) -> Self {
84 self.required_instance_flags |= instance_flags;
85 self
86 }
87
88 pub fn force_fxc(mut self, force_fxc: bool) -> Self {
89 self.force_fxc = force_fxc;
90 self
91 }
92
93 pub fn expect_fail(mut self, when: FailureCase) -> Self {
95 self.failures.push(when);
96 self
97 }
98
99 pub fn skip(mut self, when: FailureCase) -> Self {
101 self.skips.push(when);
102 self
103 }
104
105 pub fn enable_noop(mut self) -> Self {
110 self.skips
111 .retain(|case| *case != FailureCase::backend(wgpu::Backends::NOOP));
112 self
113 }
114
115 pub fn disable_mtl_shader_validation(mut self) -> Self {
120 self.disable_mtl_shader_validation = true;
121 self
122 }
123}
124
125pub struct TestInfo {
127 pub skip: bool,
128 pub failure_application_reasons: FailureApplicationReasons,
129 pub failures: Vec<FailureCase>,
130 pub running_msg: String,
131}
132
133impl TestInfo {
134 pub(crate) fn from_configuration(test: &GpuTestConfiguration, adapter: &AdapterReport) -> Self {
135 let mut unsupported_reasons: ArrayVec<_, 4> = ArrayVec::new();
137 let missing_features = test.params.required_features - adapter.features;
138 if !missing_features.is_empty() {
139 unsupported_reasons.push("Features");
140 }
141
142 if !test.params.required_limits.check_limits(&adapter.limits) {
143 unsupported_reasons.push("Limits");
144 }
145
146 let missing_downlevel_flags =
147 test.params.required_downlevel_caps.flags - adapter.downlevel_caps.flags;
148 if !missing_downlevel_flags.is_empty() {
149 unsupported_reasons.push("Downlevel Flags");
150 }
151
152 if test.params.required_downlevel_caps.shader_model > adapter.downlevel_caps.shader_model {
153 unsupported_reasons.push("Shader Model");
154 }
155
156 let adapter_lowercase_info = wgpu::AdapterInfo {
159 name: adapter.info.name.to_lowercase(),
160 driver: adapter.info.driver.to_lowercase(),
161 ..adapter.info.clone()
162 };
163
164 let skip_application_reason = test
166 .params
167 .skips
168 .iter()
169 .find_map(|case| case.applies_to_adapter(&adapter_lowercase_info));
170
171 let mut applicable_cases = Vec::with_capacity(test.params.failures.len());
172 let mut failure_application_reasons = FailureApplicationReasons::empty();
173 let mut flaky = false;
174 for failure in &test.params.failures {
175 if let Some(reasons) = failure.applies_to_adapter(&adapter_lowercase_info) {
176 failure_application_reasons.insert(reasons);
177 applicable_cases.push(failure.clone());
178 flaky |= matches!(failure.behavior, FailureBehavior::Ignore);
179 }
180 }
181
182 let mut skip = false;
183 let running_msg = if let Some(reasons) = skip_application_reason {
184 skip = true;
185
186 let names: ArrayVec<_, 4> = reasons.iter_names().map(|(name, _)| name).collect();
187 let names_text = names.join(" | ");
188
189 format!("Skipped Failure: {names_text}")
190 } else if !unsupported_reasons.is_empty() {
191 skip = true;
192 format!("Unsupported: {}", unsupported_reasons.join(" | "))
193 } else if !failure_application_reasons.is_empty() {
194 if cfg!(target_arch = "wasm32") {
195 skip = true;
196 }
197
198 let names: ArrayVec<_, 4> = failure_application_reasons
199 .iter_names()
200 .map(|(name, _)| name)
201 .collect();
202 let names_text = names.join(" & ");
203 let flaky_text = if flaky { " Flaky " } else { " " };
204
205 format!("Executed{flaky_text}Failure: {names_text}")
206 } else {
207 String::from("Executed")
208 };
209
210 Self {
211 skip,
212 failure_application_reasons,
213 failures: applicable_cases,
214 running_msg,
215 }
216 }
217}