Files
wgpu/tests/gpu-tests/subgroup_operations/mod.rs
2025-02-23 14:06:34 -05:00

135 lines
5.5 KiB
Rust

use std::num::NonZeroU64;
use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters};
const THREAD_COUNT: u64 = 128;
const TEST_COUNT: u32 = 32;
#[gpu_test]
static SUBGROUP_OPERATIONS: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(
TestParameters::default()
.features(wgpu::Features::SUBGROUP)
.limits(wgpu::Limits::downlevel_defaults())
// Expect metal to fail on tests involving operations in divergent control flow
//
// Newlines are included in the panic message to ensure that _additional_ failures
// are not matched against.
.expect_fail(
wgpu_test::FailureCase::molten_vk()
// 14.3 doesn't fail test 29
.panic("thread 0 failed tests: 27,\nthread 1 failed tests: 27, 28,\n")
// Prior versions do.
.panic("thread 0 failed tests: 27, 29,\nthread 1 failed tests: 27, 28, 29,\n"),
)
.expect_fail(
wgpu_test::FailureCase::backend(wgpu::Backends::METAL)
// 14.3 doesn't fail test 29
.panic("thread 0 failed tests: 27,\nthread 1 failed tests: 27, 28,\n")
// Prior versions do.
.panic("thread 0 failed tests: 27, 29,\nthread 1 failed tests: 27, 28, 29,\n"),
),
)
.run_sync(|ctx| {
let device = &ctx.device;
let storage_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: THREAD_COUNT * size_of::<u32>() as u64,
usage: wgpu::BufferUsages::STORAGE
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("bind group layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: false },
has_dynamic_offset: false,
min_binding_size: NonZeroU64::new(THREAD_COUNT * size_of::<u32>() as u64),
},
count: None,
}],
});
let cs_module = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("main"),
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
});
let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
module: &cs_module,
entry_point: Some("main"),
compilation_options: Default::default(),
cache: None,
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: storage_buffer.as_entire_binding(),
}],
layout: &bind_group_layout,
label: Some("bind group"),
});
let mut encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: None,
timestamp_writes: None,
});
cpass.set_pipeline(&compute_pipeline);
cpass.set_bind_group(0, &bind_group, &[]);
cpass.dispatch_workgroups(1, 1, 1);
}
ctx.queue.submit(Some(encoder.finish()));
wgpu::util::DownloadBuffer::read_buffer(
device,
&ctx.queue,
&storage_buffer.slice(..),
|mapping_buffer_view| {
let mapping_buffer_view = mapping_buffer_view.unwrap();
let result: &[u32; THREAD_COUNT as usize] =
bytemuck::from_bytes(&mapping_buffer_view);
let expected_mask = (1u64 << (TEST_COUNT)) - 1; // generate full mask
let expected_array = [expected_mask as u32; THREAD_COUNT as usize];
if result != &expected_array {
use std::fmt::Write;
let mut msg = String::new();
writeln!(
&mut msg,
"Got from GPU:\n{:x?}\n expected:\n{:x?}",
result, &expected_array,
)
.unwrap();
for (thread, (result, expected)) in result
.iter()
.zip(expected_array)
.enumerate()
.filter(|(_, (r, e))| *r != e)
{
write!(&mut msg, "thread {thread} failed tests:").unwrap();
let difference = result ^ expected;
for i in (0..u32::BITS).filter(|i| (difference & (1 << i)) != 0) {
write!(&mut msg, " {i},").unwrap();
}
writeln!(&mut msg).unwrap();
}
panic!("{}", msg);
}
},
);
});