#version 450 #extension GL_EXT_control_flow_attributes : require // Collapse the hc residual streams of a token into one, weighted per stream: // // dst[i0, it] = sum_ih x[i0, ih, it] * weights[ih, it] layout(constant_id = 0) const uint BLOCK_SIZE = 256; layout(local_size_x_id = 0, local_size_y = 1, local_size_z = 1) in; layout(push_constant) uniform parameter { uint n_embd; uint n_tokens; uint nbx0; uint nbx1; uint nbx2; // x uint nbw0; uint nbw1; // weights uint nbd0; uint nbd1; // dst uint x_offset; uint w_offset; uint d_offset; }; layout(binding = 0, std430) readonly buffer X { float data_x[]; }; layout(binding = 1, std430) readonly buffer W { float data_w[]; }; layout(binding = 2, std430) writeonly buffer D { float data_d[]; }; const uint hc = 4; shared float w[hc]; void main() { const uint tid = gl_LocalInvocationID.x; const uint it = gl_WorkGroupID.y; if (tid < hc) { w[tid] = data_w[w_offset + tid * nbw0 + it * nbw1]; } barrier(); // After the barrier, so every invocation reaches it. const uint i0 = gl_WorkGroupID.x * BLOCK_SIZE + tid; if (i0 >= n_embd) { return; } const uint xb = x_offset + i0 * nbx0 + it * nbx2; float result = 0.0f; [[unroll]] for (uint ih = 0; ih < hc; ++ih) { result = fma(data_x[xb + ih * nbx1], w[ih], result); } data_d[d_offset + i0 * nbd0 + it * nbd1] = result; }