lampshade 0.13.0

Fast, composable GPU primitives for Rust applications using wgpu and WGSL.
Documentation
struct Params {
    num_items: u32,
    bin_count: u32,
    groups_x: u32,
    workgroups: u32,
}

@group(0) @binding(0) var<storage, read> input: array<u32>;
@group(0) @binding(1) var<storage, read_write> output: array<atomic<u32>>;
@group(0) @binding(2) var<uniform> params: Params;

const BLOCK_SIZE: u32 = {{BLOCK_SIZE}}u;
const ITEMS_PER_THREAD: u32 = {{VT}}u;
const ITEMS_PER_WORKGROUP: u32 = BLOCK_SIZE * ITEMS_PER_THREAD;

var<workgroup> local_bins: array<atomic<u32>, {{MAX_BINS}}>;

@compute @workgroup_size(BLOCK_SIZE)
fn main(
    @builtin(workgroup_id) group_id: vec3<u32>,
    @builtin(local_invocation_id) local_id: vec3<u32>,
) {
    let thread = local_id.x;
    let flat_group = group_id.y * params.groups_x + group_id.x;
    if (flat_group >= params.workgroups) {
        return;
    }

    atomicStore(&local_bins[thread], 0u);
    workgroupBarrier();

    let group_base = flat_group * ITEMS_PER_WORKGROUP;
    for (var item = 0u; item < ITEMS_PER_THREAD; item++) {
        let index = group_base + thread + item * BLOCK_SIZE;
        if (index < params.num_items) {
            let bin = input[index];
            if (bin < params.bin_count) {
                atomicAdd(&local_bins[bin], 1u);
            }
        }
    }
    workgroupBarrier();

    if (thread < params.bin_count) {
        let count = atomicLoad(&local_bins[thread]);
        if (count != 0u) {
            atomicAdd(&output[thread], count);
        }
    }
}