#version 450
// ============================================================================
// Stream compaction scatter -- INDEX-ONLY variant.
//
// Instead of copying data from a source buffer, writes the INDEX of each
// active element into the compacted buffer. This is the common case for
// voxel pipelines: flags[i] marks active edges/voxels, and you want the
// list of active indices.
//
// Given:
// flags[N] - 1 if element is active, 0 otherwise
// scan_result[N] - exclusive prefix sum of flags[]
//
// Produces:
// compacted[scan_result[i]] = i for all i where flags[i] == 1
// count[0] = total number of active elements
//
// Dispatch: ceil(N / 256), 1, 1
// ============================================================================
#define THREADS 256
layout(local_size_x = THREADS, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0, std430) readonly buffer FlagsBuf { uint flags[]; };
layout(set = 0, binding = 1, std430) readonly buffer ScanBuf { uint scan_result[]; };
layout(set = 0, binding = 2, std430) writeonly buffer CompactedBuf { uint compacted[]; };
layout(set = 0, binding = 3, std430) buffer CountBuf { uint count[]; };
// Phase B: Push constant replaces UBO — no buffer_update() needed.
layout(push_constant) uniform CompactPushConstants {
uint N; // total number of elements
uint _pad0;
uint _pad1;
uint _pad2;
};
void main() {
uint gid = gl_GlobalInvocationID.x;
if (gid >= N) return;
if (flags[gid] != 0u) {
uint dest = scan_result[gid];
compacted[dest] = gid;
}
// Last thread writes total count.
if (gid == N - 1) {
count[0] = scan_result[gid] + flags[gid];
}
}