lampshade 0.13.0

Fast, composable GPU primitives for Rust applications using wgpu and WGSL.
Documentation
struct KeyValue {
    key: u32,
    value: u32,
}

struct Config {
    capacity_items: u32,
    count_word: u32,
    _padding_0: u32,
    _padding_1: u32,
}

@group(0) @binding(0) var<storage, read> keys_input: array<u32>;
@group(0) @binding(1) var<storage, read> values_input: array<u32>;
@group(0) @binding(2) var<storage, read_write> packed_output: array<KeyValue>;
@group(0) @binding(3) var<storage, read> count_words: array<u32>;
@group(0) @binding(4) var<uniform> config: Config;
@group(0) @binding(5) var<storage, read_write> clamped_count: array<u32>;

const BLOCK_SIZE: u32 = 256u;

fn item_index(
    group_id: vec3<u32>,
    workgroup_count: vec3<u32>,
    local_id: vec3<u32>,
) -> u32 {
    let groups = config.capacity_items / BLOCK_SIZE
        + select(0u, 1u, config.capacity_items % BLOCK_SIZE != 0u);
    let flat_group = group_id.y * workgroup_count.x + group_id.x;
    if (flat_group >= groups) {
        return config.capacity_items;
    }
    return flat_group * BLOCK_SIZE + local_id.x;
}

@compute @workgroup_size(BLOCK_SIZE)
fn main(
    @builtin(workgroup_id) group_id: vec3<u32>,
    @builtin(local_invocation_id) local_id: vec3<u32>,
    @builtin(num_workgroups) workgroup_count: vec3<u32>,
) {
    let index = item_index(group_id, workgroup_count, local_id);
    let count = min(count_words[config.count_word], config.capacity_items);
    if (index == 0u) {
        clamped_count[0] = count;
    }
    if (index < count) {
        packed_output[index] = KeyValue(keys_input[index], values_input[index]);
    }
}