Skip to content

Commit d2c28c8

Browse files
committed
Tests
1 parent 3adc7af commit d2c28c8

File tree

4 files changed

+377
-5
lines changed

4 files changed

+377
-5
lines changed

tests/tests/binding_array/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
mod partially_bounded_arrays;
2-
mod textures;
2+
mod sampled_textures;
3+
mod storage_buffers;
4+
mod storage_textures;

tests/tests/binding_array/textures.rs renamed to tests/tests/binding_array/sampled_textures.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use wgpu_test::{
66
};
77

88
#[gpu_test]
9-
static BINDING_ARRAY_TEXTURES: GpuTestConfiguration = GpuTestConfiguration::new()
9+
static BINDING_ARRAY_SAMPLED_TEXTURES: GpuTestConfiguration = GpuTestConfiguration::new()
1010
.parameters(
1111
TestParameters::default()
1212
.features(
@@ -18,9 +18,14 @@ static BINDING_ARRAY_TEXTURES: GpuTestConfiguration = GpuTestConfiguration::new(
1818
..Limits::default()
1919
}),
2020
)
21-
.run_async(binding_array_textures);
22-
23-
async fn binding_array_textures(ctx: TestingContext) {
21+
.run_async(binding_array_sampled_textures);
22+
23+
/// Test to see how texture bindings array work and additionally making sure
24+
/// that non-uniform indexing is working correctly.
25+
///
26+
/// If non-uniform indexing is not working correctly, AMD will produce the wrong
27+
/// output due to non-native support for non-uniform indexing within a WARP.
28+
async fn binding_array_sampled_textures(ctx: TestingContext) {
2429
let shader = r#"
2530
@group(0) @binding(0)
2631
var textures: binding_array<texture_2d<f32>>;
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
use std::num::{NonZeroU32, NonZeroU64};
2+
3+
use wgpu::{
4+
util::{BufferInitDescriptor, DeviceExt},
5+
*,
6+
};
7+
use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};
8+
9+
#[gpu_test]
10+
static BINDING_ARRAY_STORAGE_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()
11+
.parameters(
12+
TestParameters::default()
13+
.features(
14+
Features::BUFFER_BINDING_ARRAY
15+
| Features::STORAGE_RESOURCE_BINDING_ARRAY
16+
| Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
17+
)
18+
.limits(Limits {
19+
max_storage_buffers_per_shader_stage: 17,
20+
..Limits::default()
21+
}),
22+
)
23+
.run_async(binding_array_storage_buffers);
24+
25+
/// Test to see how texture bindings array work and additionally making sure
26+
/// that non-uniform indexing is working correctly.
27+
///
28+
/// If non-uniform indexing is not working correctly, AMD will produce the wrong
29+
/// output due to non-native support for non-uniform indexing within a WARP.
30+
async fn binding_array_storage_buffers(ctx: TestingContext) {
31+
let shader = r#"
32+
struct ImAU32 {
33+
value: u32,
34+
};
35+
36+
@group(0) @binding(0)
37+
var<storage> buffers: binding_array<ImAU32>;
38+
39+
@group(0) @binding(1)
40+
var<storage, read_write> output_buffer: array<u32>;
41+
42+
@compute
43+
@workgroup_size(16, 1, 1)
44+
fn compMain(@builtin(global_invocation_id) id: vec3u) {
45+
output_buffer[id.x] = buffers[id.x].value;
46+
}
47+
"#;
48+
49+
let module = ctx
50+
.device
51+
.create_shader_module(wgpu::ShaderModuleDescriptor {
52+
label: Some("Binding Array Buffer"),
53+
source: wgpu::ShaderSource::Wgsl(shader.into()),
54+
});
55+
56+
let image = image::load_from_memory(include_bytes!("../3x3_colors.png")).unwrap();
57+
// Resize image to 4x4
58+
let image = image
59+
.resize_exact(4, 4, image::imageops::FilterType::Gaussian)
60+
.into_rgba8();
61+
62+
// Create one buffer for each pixel
63+
let mut buffers = Vec::with_capacity(64);
64+
for data in image.pixels() {
65+
let buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {
66+
label: None,
67+
contents: bytemuck::cast_slice(&data.0),
68+
usage: BufferUsages::STORAGE,
69+
});
70+
buffers.push(buffer);
71+
}
72+
73+
let output_buffer = ctx.device.create_buffer(&BufferDescriptor {
74+
label: None,
75+
size: 4 * 4 * 4,
76+
usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC,
77+
mapped_at_creation: false,
78+
});
79+
80+
let bind_group_layout = ctx
81+
.device
82+
.create_bind_group_layout(&BindGroupLayoutDescriptor {
83+
label: Some("Bind Group Layout"),
84+
entries: &[
85+
BindGroupLayoutEntry {
86+
binding: 0,
87+
visibility: ShaderStages::COMPUTE,
88+
ty: BindingType::Buffer {
89+
ty: BufferBindingType::Storage { read_only: true },
90+
has_dynamic_offset: false,
91+
min_binding_size: Some(NonZeroU64::new(4).unwrap()),
92+
},
93+
count: Some(NonZeroU32::new(16).unwrap()),
94+
},
95+
BindGroupLayoutEntry {
96+
binding: 1,
97+
visibility: ShaderStages::COMPUTE,
98+
ty: BindingType::Buffer {
99+
ty: BufferBindingType::Storage { read_only: false },
100+
has_dynamic_offset: false,
101+
min_binding_size: Some(NonZeroU64::new(4).unwrap()),
102+
},
103+
count: None,
104+
},
105+
],
106+
});
107+
108+
let buffer_references: Vec<_> = buffers
109+
.iter()
110+
.map(|b| b.as_entire_buffer_binding())
111+
.collect();
112+
113+
let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {
114+
label: Some("Bind Group"),
115+
layout: &bind_group_layout,
116+
entries: &[
117+
BindGroupEntry {
118+
binding: 0,
119+
resource: BindingResource::BufferArray(&buffer_references),
120+
},
121+
BindGroupEntry {
122+
binding: 1,
123+
resource: output_buffer.as_entire_binding(),
124+
},
125+
],
126+
});
127+
128+
let pipeline_layout = ctx
129+
.device
130+
.create_pipeline_layout(&PipelineLayoutDescriptor {
131+
label: Some("Pipeline Layout"),
132+
bind_group_layouts: &[&bind_group_layout],
133+
push_constant_ranges: &[],
134+
});
135+
136+
let pipeline = ctx
137+
.device
138+
.create_compute_pipeline(&ComputePipelineDescriptor {
139+
label: Some("Compute Pipeline"),
140+
layout: Some(&pipeline_layout),
141+
module: &module,
142+
entry_point: Some("compMain"),
143+
compilation_options: Default::default(),
144+
cache: None,
145+
});
146+
147+
let mut encoder = ctx
148+
.device
149+
.create_command_encoder(&CommandEncoderDescriptor { label: None });
150+
{
151+
let mut render_pass = encoder.begin_compute_pass(&ComputePassDescriptor {
152+
label: None,
153+
timestamp_writes: None,
154+
});
155+
render_pass.set_pipeline(&pipeline);
156+
render_pass.set_bind_group(0, &bind_group, &[]);
157+
render_pass.dispatch_workgroups(1, 1, 1);
158+
}
159+
160+
let readback_buffer = ctx.device.create_buffer(&BufferDescriptor {
161+
label: None,
162+
size: 4 * 4 * 4,
163+
usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
164+
mapped_at_creation: false,
165+
});
166+
167+
encoder.copy_buffer_to_buffer(&output_buffer, 0, &readback_buffer, 0, 4 * 4 * 4);
168+
169+
ctx.queue.submit(Some(encoder.finish()));
170+
ctx.device.poll(Maintain::Wait);
171+
172+
let slice = readback_buffer.slice(..);
173+
slice.map_async(MapMode::Read, |_| {});
174+
175+
let data = slice.get_mapped_range();
176+
177+
assert_eq!(&data[..], &*image);
178+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
use std::num::NonZeroU32;
2+
3+
use wgpu::*;
4+
use wgpu_test::{
5+
gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, TestParameters,
6+
TestingContext,
7+
};
8+
9+
#[gpu_test]
10+
static BINDING_ARRAY_STORAGE_TEXTURES: GpuTestConfiguration = GpuTestConfiguration::new()
11+
.parameters(
12+
TestParameters::default()
13+
.features(
14+
Features::TEXTURE_BINDING_ARRAY
15+
| Features::STORAGE_RESOURCE_BINDING_ARRAY
16+
| Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING
17+
| Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
18+
)
19+
.limits(Limits {
20+
max_storage_textures_per_shader_stage: 17,
21+
..Limits::default()
22+
})
23+
.expect_fail(FailureCase::backend(Backends::METAL)),
24+
)
25+
.run_async(binding_array_storage_textures);
26+
27+
/// Test to see how texture bindings array work and additionally making sure
28+
/// that non-uniform indexing is working correctly.
29+
///
30+
/// If non-uniform indexing is not working correctly, AMD will produce the wrong
31+
/// output due to non-native support for non-uniform indexing within a WARP.
32+
async fn binding_array_storage_textures(ctx: TestingContext) {
33+
let shader = r#"
34+
@group(0) @binding(0)
35+
var textures: binding_array<texture_storage_2d<rgba8unorm, read_write> >;
36+
37+
@compute
38+
@workgroup_size(4, 4, 1)
39+
fn compMain(@builtin(global_invocation_id) id: vec3u) {
40+
// Read from the 4x4 textures in 0-15, then write to the 4x4 texture in 16
41+
42+
let pixel = vec2u(id.xy);
43+
let index = pixel.y * 4 + pixel.x;
44+
45+
let color = textureLoad(textures[index], vec2u(0));
46+
textureStore(textures[16], pixel, color);
47+
}
48+
"#;
49+
50+
let module = ctx
51+
.device
52+
.create_shader_module(wgpu::ShaderModuleDescriptor {
53+
label: Some("Binding Array Texture"),
54+
source: wgpu::ShaderSource::Wgsl(shader.into()),
55+
});
56+
57+
let image = image::load_from_memory(include_bytes!("../3x3_colors.png")).unwrap();
58+
// Resize image to 4x4
59+
let image = image
60+
.resize_exact(4, 4, image::imageops::FilterType::Gaussian)
61+
.into_rgba8();
62+
63+
// Create one texture for each pixel
64+
let mut input_views = Vec::with_capacity(64);
65+
for data in image.pixels() {
66+
let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
67+
label: None,
68+
size: Extent3d {
69+
width: 1,
70+
height: 1,
71+
depth_or_array_layers: 1,
72+
},
73+
mip_level_count: 1,
74+
sample_count: 1,
75+
dimension: TextureDimension::D2,
76+
format: TextureFormat::Rgba8Unorm,
77+
usage: TextureUsages::STORAGE_BINDING | TextureUsages::COPY_DST,
78+
view_formats: &[],
79+
});
80+
81+
ctx.queue.write_texture(
82+
TexelCopyTextureInfo {
83+
texture: &texture,
84+
mip_level: 0,
85+
origin: Origin3d::ZERO,
86+
aspect: TextureAspect::All,
87+
},
88+
&data.0,
89+
TexelCopyBufferLayout {
90+
offset: 0,
91+
bytes_per_row: Some(4),
92+
rows_per_image: Some(1),
93+
},
94+
Extent3d {
95+
width: 1,
96+
height: 1,
97+
depth_or_array_layers: 1,
98+
},
99+
);
100+
101+
input_views.push(texture.create_view(&TextureViewDescriptor::default()));
102+
}
103+
104+
let output_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
105+
label: Some("Output Texture"),
106+
size: Extent3d {
107+
width: 4,
108+
height: 4,
109+
depth_or_array_layers: 1,
110+
},
111+
mip_level_count: 1,
112+
sample_count: 1,
113+
dimension: TextureDimension::D2,
114+
format: TextureFormat::Rgba8Unorm,
115+
usage: TextureUsages::STORAGE_BINDING | TextureUsages::COPY_SRC,
116+
view_formats: &[],
117+
});
118+
119+
let output_view = output_texture.create_view(&TextureViewDescriptor::default());
120+
121+
let bind_group_layout = ctx
122+
.device
123+
.create_bind_group_layout(&BindGroupLayoutDescriptor {
124+
label: Some("Bind Group Layout"),
125+
entries: &[BindGroupLayoutEntry {
126+
binding: 0,
127+
visibility: ShaderStages::COMPUTE,
128+
ty: BindingType::StorageTexture {
129+
access: StorageTextureAccess::ReadWrite,
130+
format: TextureFormat::Rgba8Unorm,
131+
view_dimension: TextureViewDimension::D2,
132+
},
133+
count: Some(NonZeroU32::new(4 * 4 + 1).unwrap()),
134+
}],
135+
});
136+
137+
let mut input_view_references: Vec<_> = input_views.iter().collect();
138+
input_view_references.push(&output_view);
139+
140+
let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {
141+
label: Some("Bind Group"),
142+
layout: &bind_group_layout,
143+
entries: &[BindGroupEntry {
144+
binding: 0,
145+
resource: BindingResource::TextureViewArray(&input_view_references),
146+
}],
147+
});
148+
149+
let pipeline_layout = ctx
150+
.device
151+
.create_pipeline_layout(&PipelineLayoutDescriptor {
152+
label: Some("Pipeline Layout"),
153+
bind_group_layouts: &[&bind_group_layout],
154+
push_constant_ranges: &[],
155+
});
156+
157+
let pipeline = ctx
158+
.device
159+
.create_compute_pipeline(&ComputePipelineDescriptor {
160+
label: Some("Compute Pipeline"),
161+
layout: Some(&pipeline_layout),
162+
module: &module,
163+
entry_point: Some("compMain"),
164+
compilation_options: Default::default(),
165+
cache: None,
166+
});
167+
168+
let mut encoder = ctx
169+
.device
170+
.create_command_encoder(&CommandEncoderDescriptor { label: None });
171+
{
172+
let mut render_pass = encoder.begin_compute_pass(&ComputePassDescriptor {
173+
label: None,
174+
timestamp_writes: None,
175+
});
176+
render_pass.set_pipeline(&pipeline);
177+
render_pass.set_bind_group(0, &bind_group, &[]);
178+
render_pass.dispatch_workgroups(1, 1, 1);
179+
}
180+
181+
let readback_buffers = ReadbackBuffers::new(&ctx.device, &output_texture);
182+
readback_buffers.copy_from(&ctx.device, &mut encoder, &output_texture);
183+
184+
ctx.queue.submit(Some(encoder.finish()));
185+
186+
readback_buffers.assert_buffer_contents(&ctx, &image).await;
187+
}

0 commit comments

Comments
 (0)