Files
armorpaint/base/tools/iris.c/iris_vulkan_res_attn.comp
T
2026-06-17 17:44:40 +02:00

94 lines
2.9 KiB
Plaintext

#version 450
/*
* Z-Image full (bidirectional) self-attention, f32, flash-style so it works at
* any sequence length without materializing the [seq, seq] score matrix.
* One workgroup per (query position i, head h); 128 lanes cooperate.
* scores[j] = scale * dot(Q[i,h], K[j,h])
* out[i,h,:] = softmax(scores) @ V[:,h,:]
* Online softmax: keys are processed in tiles of 128; running max/sum and the
* per-dim accumulator are updated per tile. head_dim must be <= 128.
*/
layout(local_size_x = 128) in;
layout(std430, binding = 0) readonly buffer Q { float q[]; };
layout(std430, binding = 1) readonly buffer K { float k[]; };
layout(std430, binding = 2) readonly buffer V { float v[]; };
layout(std430, binding = 3) writeonly buffer O { float o[]; };
layout(push_constant) uniform PC { uint seq, num_heads, head_dim; float scale; } pc;
shared float q_sh[128];
shared float sc[128];
shared float red[128];
shared float s_m;
shared float s_l;
void main() {
uint i = gl_WorkGroupID.x; /* query position */
uint h = gl_WorkGroupID.y; /* head */
uint tid = gl_LocalInvocationID.x;
uint hd = pc.head_dim;
uint dim = pc.num_heads * hd;
uint q_base = i * dim + h * hd;
if (tid < hd) q_sh[tid] = q[q_base + tid];
if (tid == 0u) { s_m = -1e30; s_l = 0.0; }
barrier();
float acc = 0.0; /* lane `tid` accumulates output dim `tid` (tid < hd) */
for (uint tile = 0u; tile < pc.seq; tile += 128u) {
uint j = tile + tid;
float score = -1e30;
if (j < pc.seq) {
uint k_base = j * dim + h * hd;
float dot = 0.0;
for (uint d = 0u; d < hd; d++) dot += q_sh[d] * k[k_base + d];
score = dot * pc.scale;
}
sc[tid] = score;
barrier();
/* tile max */
red[tid] = score;
barrier();
for (uint s = 64u; s > 0u; s >>= 1u) {
if (tid < s) red[tid] = max(red[tid], red[tid + s]);
barrier();
}
float tile_max = red[0];
barrier();
float m_old = s_m;
float m_new = max(m_old, tile_max);
float corr = exp(m_old - m_new);
/* tile sum of exp(score - m_new) */
float e = (j < pc.seq) ? exp(sc[tid] - m_new) : 0.0;
red[tid] = e;
barrier();
for (uint s = 64u; s > 0u; s >>= 1u) {
if (tid < s) red[tid] += red[tid + s];
barrier();
}
float tile_sum = red[0];
barrier();
if (tid < hd) {
float a = acc * corr;
for (uint t = 0u; t < 128u; t++) {
uint jj = tile + t;
if (jj < pc.seq) {
float p = exp(sc[t] - m_new);
a += p * v[jj * dim + h * hd + tid];
}
}
acc = a;
}
if (tid == 0u) { s_l = s_l * corr + tile_sum; s_m = m_new; }
barrier();
}
if (tid < hd) o[q_base + tid] = acc / s_l;
}