use super::GpuContext;
const SHADER_SOURCE: &str = include_str!("../../shaders/prefix_sum.wgsl");
const BLOCK_SIZE: u32 = 512;
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct Params {
n: u32,
block_offset: u32,
}
pub struct PrefixSumPipelines {
scan_pipeline: wgpu::ComputePipeline,
add_pipeline: wgpu::ComputePipeline,
}
impl PrefixSumPipelines {
pub fn new(ctx: &GpuContext) -> Self {
Self {
scan_pipeline: ctx.create_compute_pipeline(
"prefix_sum_scan",
SHADER_SOURCE,
"scan_blocks",
),
add_pipeline: ctx.create_compute_pipeline(
"prefix_sum_add",
SHADER_SOURCE,
"add_block_sums",
),
}
}
pub fn exclusive_prefix_sum(&self, ctx: &GpuContext, data_buf: &wgpu::Buffer, n: u32) {
if n <= 1 {
if n == 1 {
ctx.upload_to_buffer(data_buf, &[0]);
}
return;
}
let num_blocks = GpuContext::workgroup_count(n, BLOCK_SIZE);
let block_sums_buf = ctx.create_buffer_empty("block_sums", num_blocks.max(1));
let params = Params { n, block_offset: 0 };
let params_buf = ctx.create_uniform_buffer("prefix_sum_params", ¶ms);
let bind_group = ctx.create_bind_group(
&self.scan_pipeline,
0,
&[
wgpu::BindGroupEntry {
binding: 0,
resource: data_buf.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: block_sums_buf.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: params_buf.as_entire_binding(),
},
],
);
ctx.dispatch(&self.scan_pipeline, &bind_group, (num_blocks, 1, 1));
if num_blocks > 1 {
self.exclusive_prefix_sum(ctx, &block_sums_buf, num_blocks);
let add_bind_group = ctx.create_bind_group(
&self.add_pipeline,
0,
&[
wgpu::BindGroupEntry {
binding: 0,
resource: data_buf.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: block_sums_buf.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: params_buf.as_entire_binding(),
},
],
);
ctx.dispatch(&self.add_pipeline, &add_bind_group, (num_blocks, 1, 1));
}
}
}