Skip to content

Commit 286082a

Browse files
authored
Bindless Tests (#6732)
* Move Partial Binding into Own File * Texture Bindless Test * Make It Work * Tests * Uniform Buffers * BadCode * Bugs! * Exclude llvmpipe * Combine Partial Binding Test * MVK Issue * Sampler Array Tests * Make All Tests Partially Bound As Well
1 parent 2ec87d7 commit 286082a

File tree

9 files changed

+959
-114
lines changed

9 files changed

+959
-114
lines changed

tests/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ ctor.workspace = true
3434
futures-lite.workspace = true
3535
glam.workspace = true
3636
itertools.workspace = true
37+
image.workspace = true
3738
libtest-mimic.workspace = true
3839
log.workspace = true
3940
parking_lot.workspace = true
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
use std::num::{NonZeroU32, NonZeroU64};
2+
3+
use wgpu::*;
4+
use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext};
5+
6+
#[gpu_test]
7+
static BINDING_ARRAY_UNIFORM_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()
8+
.parameters(
9+
TestParameters::default()
10+
.features(
11+
Features::BUFFER_BINDING_ARRAY
12+
| Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
13+
)
14+
.limits(Limits {
15+
max_uniform_buffers_per_shader_stage: 16,
16+
..Limits::default()
17+
})
18+
// Naga bug on vulkan: https://github.com/gfx-rs/wgpu/issues/6733
19+
//
20+
// Causes varying errors on different devices, so we don't match more closely.
21+
.expect_fail(FailureCase::backend(Backends::VULKAN))
22+
// These issues cause a segfault on lavapipe
23+
.skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe")),
24+
)
25+
.run_async(|ctx| async move { binding_array_buffers(ctx, BufferType::Uniform, false).await });
26+
27+
#[gpu_test]
28+
static PARTIAL_BINDING_ARRAY_UNIFORM_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()
29+
.parameters(
30+
TestParameters::default()
31+
.features(
32+
Features::BUFFER_BINDING_ARRAY
33+
| Features::PARTIALLY_BOUND_BINDING_ARRAY
34+
| Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
35+
)
36+
.limits(Limits {
37+
max_uniform_buffers_per_shader_stage: 32,
38+
..Limits::default()
39+
})
40+
// Naga bug on vulkan: https://github.com/gfx-rs/wgpu/issues/6733
41+
//
42+
// Causes varying errors on different devices, so we don't match more closely.
43+
.expect_fail(FailureCase::backend(Backends::VULKAN))
44+
// These issues cause a segfault on lavapipe
45+
.skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe")),
46+
)
47+
.run_async(|ctx| async move { binding_array_buffers(ctx, BufferType::Uniform, true).await });
48+
49+
#[gpu_test]
50+
static BINDING_ARRAY_STORAGE_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()
51+
.parameters(
52+
TestParameters::default()
53+
.features(
54+
Features::BUFFER_BINDING_ARRAY
55+
| Features::STORAGE_RESOURCE_BINDING_ARRAY
56+
| Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
57+
)
58+
.limits(Limits {
59+
max_storage_buffers_per_shader_stage: 17,
60+
..Limits::default()
61+
})
62+
// See https://github.com/gfx-rs/wgpu/issues/6745.
63+
.expect_fail(FailureCase::molten_vk()),
64+
)
65+
.run_async(|ctx| async move { binding_array_buffers(ctx, BufferType::Storage, false).await });
66+
67+
#[gpu_test]
68+
static PARTIAL_BINDING_ARRAY_STORAGE_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()
69+
.parameters(
70+
TestParameters::default()
71+
.features(
72+
Features::BUFFER_BINDING_ARRAY
73+
| Features::PARTIALLY_BOUND_BINDING_ARRAY
74+
| Features::STORAGE_RESOURCE_BINDING_ARRAY
75+
| Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
76+
)
77+
.limits(Limits {
78+
max_storage_buffers_per_shader_stage: 33,
79+
..Limits::default()
80+
})
81+
// See https://github.com/gfx-rs/wgpu/issues/6745.
82+
.expect_fail(FailureCase::molten_vk()),
83+
)
84+
.run_async(|ctx| async move { binding_array_buffers(ctx, BufferType::Storage, true).await });
85+
86+
enum BufferType {
87+
Storage,
88+
Uniform,
89+
}
90+
91+
async fn binding_array_buffers(
92+
ctx: TestingContext,
93+
buffer_type: BufferType,
94+
partial_binding: bool,
95+
) {
96+
let storage_mode = match buffer_type {
97+
BufferType::Storage => "storage",
98+
BufferType::Uniform => "uniform",
99+
};
100+
101+
let shader = r#"
102+
struct ImAU32 {
103+
value: u32,
104+
_padding: u32,
105+
_padding2: u32,
106+
_padding3: u32,
107+
};
108+
109+
@group(0) @binding(0)
110+
var<{storage_mode}> buffers: binding_array<ImAU32>;
111+
112+
@group(0) @binding(1)
113+
var<storage, read_write> output_buffer: array<u32>;
114+
115+
@compute
116+
@workgroup_size(16, 1, 1)
117+
fn compMain(@builtin(global_invocation_id) id: vec3u) {
118+
output_buffer[id.x] = buffers[id.x].value;
119+
}
120+
"#;
121+
let shader = shader.replace("{storage_mode}", storage_mode);
122+
123+
let module = ctx
124+
.device
125+
.create_shader_module(wgpu::ShaderModuleDescriptor {
126+
label: Some("Binding Array Buffer"),
127+
source: wgpu::ShaderSource::Wgsl(shader.into()),
128+
});
129+
130+
let image = image::load_from_memory(include_bytes!("../3x3_colors.png")).unwrap();
131+
// Resize image to 4x4
132+
let image = image
133+
.resize_exact(4, 4, image::imageops::FilterType::Gaussian)
134+
.into_rgba8();
135+
136+
// Create one buffer for each pixel
137+
let mut buffers = Vec::with_capacity(64);
138+
for data in image.pixels() {
139+
let buffer = ctx.device.create_buffer(&BufferDescriptor {
140+
label: None,
141+
usage: match buffer_type {
142+
BufferType::Storage => BufferUsages::STORAGE | BufferUsages::COPY_DST,
143+
BufferType::Uniform => BufferUsages::UNIFORM | BufferUsages::COPY_DST,
144+
},
145+
// 16 to allow padding for uniform buffers
146+
size: 16,
147+
mapped_at_creation: true,
148+
});
149+
buffer.slice(..).get_mapped_range_mut()[0..4].copy_from_slice(&data.0);
150+
buffer.unmap();
151+
buffers.push(buffer);
152+
}
153+
154+
let output_buffer = ctx.device.create_buffer(&BufferDescriptor {
155+
label: None,
156+
size: 4 * 4 * 4,
157+
usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC,
158+
mapped_at_creation: false,
159+
});
160+
161+
let multiplier = if partial_binding { 2 } else { 1 };
162+
163+
let bind_group_layout = ctx
164+
.device
165+
.create_bind_group_layout(&BindGroupLayoutDescriptor {
166+
label: Some("Bind Group Layout"),
167+
entries: &[
168+
BindGroupLayoutEntry {
169+
binding: 0,
170+
visibility: ShaderStages::COMPUTE,
171+
ty: BindingType::Buffer {
172+
ty: match buffer_type {
173+
BufferType::Storage => BufferBindingType::Storage { read_only: true },
174+
BufferType::Uniform => BufferBindingType::Uniform,
175+
},
176+
has_dynamic_offset: false,
177+
min_binding_size: Some(NonZeroU64::new(16).unwrap()),
178+
},
179+
count: Some(NonZeroU32::new(16 * multiplier).unwrap()),
180+
},
181+
BindGroupLayoutEntry {
182+
binding: 1,
183+
visibility: ShaderStages::COMPUTE,
184+
ty: BindingType::Buffer {
185+
ty: BufferBindingType::Storage { read_only: false },
186+
has_dynamic_offset: false,
187+
min_binding_size: Some(NonZeroU64::new(4).unwrap()),
188+
},
189+
count: None,
190+
},
191+
],
192+
});
193+
194+
let buffer_references: Vec<_> = buffers
195+
.iter()
196+
.map(|b| b.as_entire_buffer_binding())
197+
.collect();
198+
199+
let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {
200+
label: Some("Bind Group"),
201+
layout: &bind_group_layout,
202+
entries: &[
203+
BindGroupEntry {
204+
binding: 0,
205+
resource: BindingResource::BufferArray(&buffer_references),
206+
},
207+
BindGroupEntry {
208+
binding: 1,
209+
resource: output_buffer.as_entire_binding(),
210+
},
211+
],
212+
});
213+
214+
let pipeline_layout = ctx
215+
.device
216+
.create_pipeline_layout(&PipelineLayoutDescriptor {
217+
label: Some("Pipeline Layout"),
218+
bind_group_layouts: &[&bind_group_layout],
219+
push_constant_ranges: &[],
220+
});
221+
222+
let pipeline = ctx
223+
.device
224+
.create_compute_pipeline(&ComputePipelineDescriptor {
225+
label: Some("Compute Pipeline"),
226+
layout: Some(&pipeline_layout),
227+
module: &module,
228+
entry_point: Some("compMain"),
229+
compilation_options: Default::default(),
230+
cache: None,
231+
});
232+
233+
let mut encoder = ctx
234+
.device
235+
.create_command_encoder(&CommandEncoderDescriptor { label: None });
236+
{
237+
let mut render_pass = encoder.begin_compute_pass(&ComputePassDescriptor {
238+
label: None,
239+
timestamp_writes: None,
240+
});
241+
render_pass.set_pipeline(&pipeline);
242+
render_pass.set_bind_group(0, &bind_group, &[]);
243+
render_pass.dispatch_workgroups(1, 1, 1);
244+
}
245+
246+
let readback_buffer = ctx.device.create_buffer(&BufferDescriptor {
247+
label: None,
248+
size: 4 * 4 * 4,
249+
usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
250+
mapped_at_creation: false,
251+
});
252+
253+
encoder.copy_buffer_to_buffer(&output_buffer, 0, &readback_buffer, 0, 4 * 4 * 4);
254+
255+
ctx.queue.submit(Some(encoder.finish()));
256+
257+
let slice = readback_buffer.slice(..);
258+
slice.map_async(MapMode::Read, |_| {});
259+
260+
ctx.device.poll(Maintain::Wait);
261+
262+
let data = slice.get_mapped_range();
263+
264+
assert_eq!(&data[..], &*image);
265+
}

tests/tests/binding_array/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
mod buffers;
2+
mod sampled_textures;
3+
mod samplers;
4+
mod storage_textures;

0 commit comments

Comments
 (0)