Skip to content

Commit 4d0fbe9

Browse files
committed
test: Limit bucketing tests
1 parent c9e7d14 commit 4d0fbe9

File tree

5 files changed

+398
-24
lines changed

5 files changed

+398
-24
lines changed

tests/src/init.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) ->
7373
// will chose the noop on wasm32 for some reason.
7474
noop: wgpu::NoopBackendOptions {
7575
enable: !cfg!(target_arch = "wasm32"),
76+
..Default::default()
7677
},
7778
}
7879
.with_env(),
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
//! Limit bucketing tests
2+
//!
3+
//! See [`wgpu_core::limits`].
4+
5+
use wgpu_core as wgc;
6+
use wgpu_types as wgt;
7+
8+
fn create_noop_global(options: wgt::NoopBackendOptions) -> wgc::global::Global {
9+
wgc::global::Global::new(
10+
"test",
11+
wgt::instance::InstanceDescriptor {
12+
backends: wgt::Backends::NOOP,
13+
backend_options: wgt::BackendOptions {
14+
noop: options,
15+
..Default::default()
16+
},
17+
..wgt::instance::InstanceDescriptor::new_without_display_handle()
18+
},
19+
None,
20+
)
21+
}
22+
23+
#[test]
24+
fn enabled() {
25+
let global = create_noop_global(wgt::NoopBackendOptions {
26+
enable: true,
27+
device_type: Some(wgt::DeviceType::DiscreteGpu),
28+
features: Some(wgt::Features::empty()),
29+
subgroup_min_size: Some(4),
30+
subgroup_max_size: Some(128),
31+
..Default::default() // noop defaults to max limits
32+
});
33+
34+
let adapter_id = global
35+
.request_adapter(
36+
&wgt::RequestAdapterOptions {
37+
apply_limit_buckets: true,
38+
..Default::default()
39+
},
40+
wgt::Backends::NOOP,
41+
None,
42+
)
43+
.unwrap();
44+
45+
let limits = global.adapter_limits(adapter_id);
46+
let features = global.adapter_features(adapter_id);
47+
let info = global.adapter_get_info(adapter_id);
48+
49+
// Max limits should be replaced with "default"
50+
assert_eq!(limits, wgt::Limits::defaults());
51+
assert_eq!(features, wgt::Features::empty());
52+
assert_eq!(info.subgroup_min_size, 4);
53+
assert_eq!(info.subgroup_max_size, 128);
54+
}
55+
56+
#[test]
57+
fn exempt_features() {
58+
const EXEMPT_FEATURES: wgt::Features = wgt::Features::EXTERNAL_TEXTURE
59+
.union(wgt::Features::TEXTURE_FORMAT_NV12)
60+
.union(wgt::Features::TEXTURE_FORMAT_P010)
61+
.union(wgt::Features::TEXTURE_FORMAT_16BIT_NORM);
62+
63+
let global = create_noop_global(wgt::NoopBackendOptions {
64+
enable: true,
65+
device_type: Some(wgt::DeviceType::DiscreteGpu),
66+
features: Some(EXEMPT_FEATURES.union(wgt::Features::SUBGROUP)),
67+
subgroup_min_size: Some(4),
68+
subgroup_max_size: Some(128),
69+
..Default::default() // noop defaults to max limits
70+
});
71+
72+
let adapter_id = global
73+
.request_adapter(
74+
&wgt::RequestAdapterOptions {
75+
apply_limit_buckets: true,
76+
..Default::default()
77+
},
78+
wgt::Backends::NOOP,
79+
None,
80+
)
81+
.unwrap();
82+
83+
let limits = global.adapter_limits(adapter_id);
84+
let features = global.adapter_features(adapter_id);
85+
let info = global.adapter_get_info(adapter_id);
86+
87+
// Max limits should be replaced with "default" bucket
88+
assert_eq!(limits, wgt::Limits::defaults());
89+
assert_eq!(features, EXEMPT_FEATURES);
90+
assert_eq!(info.subgroup_min_size, 4);
91+
assert_eq!(info.subgroup_max_size, 128);
92+
}
93+
94+
#[test]
95+
fn limits_below_minimums_returns_no_adapter() {
96+
let global = create_noop_global(wgt::NoopBackendOptions {
97+
enable: true,
98+
limits: Some(wgt::Limits {
99+
max_texture_dimension_2d: 1024,
100+
..wgt::Limits::default()
101+
}),
102+
device_type: Some(wgt::DeviceType::DiscreteGpu),
103+
features: Some(wgt::Features::empty()),
104+
..Default::default()
105+
});
106+
107+
let result = global.request_adapter(
108+
&wgt::RequestAdapterOptions {
109+
apply_limit_buckets: true,
110+
..Default::default()
111+
},
112+
wgt::Backends::NOOP,
113+
None,
114+
);
115+
116+
// Device is below WebGPU minimums, so no bucket matches
117+
assert!(result.is_err());
118+
}
119+
120+
#[test]
121+
fn device_creation_exceeding_bucket_fails() {
122+
let global = create_noop_global(wgt::NoopBackendOptions {
123+
enable: true,
124+
device_type: Some(wgt::DeviceType::DiscreteGpu),
125+
features: None,
126+
..Default::default()
127+
});
128+
129+
let adapter_id = global
130+
.request_adapter(
131+
&wgt::RequestAdapterOptions {
132+
apply_limit_buckets: true,
133+
..Default::default()
134+
},
135+
wgt::Backends::NOOP,
136+
None,
137+
)
138+
.unwrap();
139+
140+
// The "default" bucket has max_bind_groups = 4
141+
let result = global.adapter_request_device(
142+
adapter_id,
143+
&wgt::DeviceDescriptor {
144+
required_limits: wgt::Limits {
145+
max_bind_groups: 8,
146+
..wgt::Limits::default()
147+
},
148+
..Default::default()
149+
},
150+
None,
151+
None,
152+
);
153+
154+
assert!(result.is_err());
155+
}
156+
157+
#[test]
158+
fn subgroup_sizes_fixed_when_unsupported() {
159+
let global = create_noop_global(wgt::NoopBackendOptions {
160+
enable: true,
161+
device_type: Some(wgt::DeviceType::DiscreteGpu),
162+
features: Some(wgt::Features::empty()),
163+
subgroup_min_size: Some(64),
164+
subgroup_max_size: Some(64),
165+
..Default::default()
166+
});
167+
168+
let adapter_id = global
169+
.request_adapter(
170+
&wgt::RequestAdapterOptions {
171+
apply_limit_buckets: true,
172+
..Default::default()
173+
},
174+
wgt::Backends::NOOP,
175+
None,
176+
)
177+
.unwrap();
178+
179+
let info = global.adapter_get_info(adapter_id);
180+
let features = global.adapter_features(adapter_id);
181+
182+
// Since the "default" bucket doesn't have the `subgroups` feature:
183+
// - bucket match should succeed despite subgroup size range being narrower than bucket,
184+
// - subgroup sizes should be replaced with WebGPU's fixed defaults.
185+
assert!(!features.contains(wgt::Features::SUBGROUP));
186+
assert_eq!(info.subgroup_min_size, 4);
187+
assert_eq!(info.subgroup_max_size, 128);
188+
}
189+
190+
#[test]
191+
fn fallback_adapter() {
192+
// DeviceType::Cpu with empty features should match "fallback" bucket
193+
let global = create_noop_global(wgt::NoopBackendOptions {
194+
enable: true,
195+
device_type: Some(wgt::DeviceType::Cpu),
196+
features: Some(wgt::Features::empty()),
197+
..Default::default()
198+
});
199+
200+
let adapter_id = global
201+
.request_adapter(
202+
&wgt::RequestAdapterOptions {
203+
apply_limit_buckets: true,
204+
..Default::default()
205+
},
206+
wgt::Backends::NOOP,
207+
None,
208+
)
209+
.unwrap();
210+
211+
let info = global.adapter_get_info(adapter_id);
212+
213+
// Should match fallback bucket which is a CPU/fallback adapter
214+
assert_eq!(info.device_type, wgt::DeviceType::Cpu);
215+
}
216+
217+
// Subgroup limits are not treated like regular limits (where a device qualifies
218+
// if its max limits meet or exceed the bucket). A device qualifies if its max
219+
// subgroup size is the same or less than the bucket's max, and conversely for
220+
// min. i.e. the device's subgroup size range must be a subset of the bucket's
221+
// subgroup size range.
222+
223+
#[test]
224+
fn subgroup_max_above_bucket() {
225+
// The highest subgroup_max_size among non-fallback buckets with subgroups
226+
// support is 64 (A1, NO_F16). A device with a max of 65 exceeds this,
227+
// so will not qualify for a bucket that has SUBGROUPS.
228+
let global = create_noop_global(wgt::NoopBackendOptions {
229+
enable: true,
230+
device_type: Some(wgt::DeviceType::DiscreteGpu),
231+
subgroup_min_size: Some(32),
232+
subgroup_max_size: Some(65),
233+
..Default::default()
234+
});
235+
236+
let adapter_id = global
237+
.request_adapter(
238+
&wgt::RequestAdapterOptions {
239+
apply_limit_buckets: true,
240+
..Default::default()
241+
},
242+
wgt::Backends::NOOP,
243+
None,
244+
)
245+
.unwrap();
246+
247+
let features = global.adapter_features(adapter_id);
248+
249+
assert!(!features.contains(wgt::Features::SUBGROUP));
250+
}
251+
252+
#[test]
253+
fn subgroup_min_below_bucket() {
254+
// The uplevel buckets with smallest subgroup_min_size are M1 (4) and I1/I2 (8). We
255+
// construct a device that has subgroup_min_size = 7 and max_vertex_attributes = 29, so
256+
// that it will not qualify for any UPLEVEL bucket (which means it won't get subgroups).
257+
let global = create_noop_global(wgt::NoopBackendOptions {
258+
enable: true,
259+
device_type: Some(wgt::DeviceType::DiscreteGpu),
260+
subgroup_min_size: Some(7),
261+
subgroup_max_size: Some(32),
262+
limits: Some(wgt::Limits {
263+
max_vertex_attributes: 29, // Disqualify from M1 bucket
264+
..Default::default()
265+
}),
266+
..Default::default()
267+
});
268+
269+
let adapter_id = global
270+
.request_adapter(
271+
&wgt::RequestAdapterOptions {
272+
apply_limit_buckets: true,
273+
..Default::default()
274+
},
275+
wgt::Backends::NOOP,
276+
None,
277+
)
278+
.unwrap();
279+
280+
let features = global.adapter_features(adapter_id);
281+
282+
assert!(!features.contains(wgt::Features::SUBGROUP));
283+
}
284+
285+
#[test]
286+
fn enumerate_adapters_bucketing_enabled() {
287+
let global = create_noop_global(wgt::NoopBackendOptions {
288+
enable: true,
289+
device_type: Some(wgt::DeviceType::DiscreteGpu),
290+
features: Some(wgt::Features::SUBGROUP),
291+
..Default::default()
292+
});
293+
294+
let adapters = global.enumerate_adapters(wgt::Backends::NOOP, true);
295+
assert_eq!(adapters.len(), 1);
296+
297+
let adapter_id = adapters[0];
298+
let limits = global.adapter_limits(adapter_id);
299+
300+
// With bucketing, should have bucketed limits
301+
assert_eq!(limits, wgt::Limits::defaults());
302+
}
303+
304+
#[test]
305+
fn enumerate_adapters_bucketing_disabled() {
306+
let custom_limits = wgt::Limits {
307+
max_bind_groups: 99,
308+
..wgt::Limits::default()
309+
};
310+
311+
let global = create_noop_global(wgt::NoopBackendOptions {
312+
enable: true,
313+
limits: Some(custom_limits),
314+
..Default::default()
315+
});
316+
317+
let adapters = global.enumerate_adapters(wgt::Backends::NOOP, false);
318+
assert_eq!(adapters.len(), 1);
319+
320+
let adapter_id = adapters[0];
321+
let limits = global.adapter_limits(adapter_id);
322+
323+
// Without bucketing, should have raw limits
324+
assert_eq!(limits.max_bind_groups, 99);
325+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Tests of the [`wgpu`] library API that are not run against a particular GPU.
22
33
mod api;
4+
mod limit_buckets;
45
mod noop;
56
mod util;

0 commit comments

Comments
 (0)