// daedalus-fourier — H.264 luma qpel mc20 (8x8, horizontal half-pel), V3D 7.1. // // H.264 spec §8.4.2.2.1 horizontal 6-tap luma interpolation: // // dst[r,c] = clip255( // ( s[r,c-2] // - 5 * s[r,c-1] // + 20 * s[r,c] // + 20 * s[r,c+1] // - 5 * s[r,c+2] // + s[r,c+3] // + 16 // ) >> 5) // // Single-stride: dst and src share `stride` (H264QpelContext // convention). src+src_off already points at the leftmost output // column (col 0); the filter reads cols -2..+3. Caller guarantees // edge-padding context per the public API docstring. // // Workgroup layout: 64 invocations = 1 lane per output pixel. // 1 block per WG; n_blocks WGs total. This is the simplest layout // that avoids any inter-lane communication — each lane independently // reads its 6 src samples and writes its 1 dst sample. V3D's L2 // cache handles the redundant reads from adjacent lanes. // // License: BSD-2-Clause. #version 450 #extension GL_EXT_shader_8bit_storage : require #extension GL_EXT_shader_explicit_arithmetic_types : require layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; layout(binding = 0) readonly buffer Src { uint8_t src[]; } u_src; layout(binding = 1) buffer Dst { uint8_t dst[]; } u_dst; layout(binding = 2) readonly buffer Meta { uvec4 meta[]; // .x = dst_off, .y = src_off } u_meta; layout(push_constant) uniform PC { uint n_blocks; uint stride_u8; uint _pad0, _pad1; } pc; void main() { // 1 block per WG, 64 lanes covering the 8x8 output block. uint wg_id = gl_WorkGroupID.x; uint block_idx = wg_id; if (block_idx >= pc.n_blocks) return; uint lane = gl_LocalInvocationID.x; uint r = lane >> 3; // 0..7 (row) uint c = lane & 7u; // 0..7 (column) uint dst_off = u_meta.meta[block_idx].x; uint src_off = u_meta.meta[block_idx].y; uint stride = pc.stride_u8; // src points at output col 0 of the block; filter reads cols -2..+3 // of the current row. Negative col arithmetic is unsigned-safe // because src_off >= 2 (caller-guaranteed left context). uint row_base = src_off + r * stride + c; int s_m2 = int(u_src.src[row_base - 2u]); int s_m1 = int(u_src.src[row_base - 1u]); int s_0 = int(u_src.src[row_base + 0u]); int s_p1 = int(u_src.src[row_base + 1u]); int s_p2 = int(u_src.src[row_base + 2u]); int s_p3 = int(u_src.src[row_base + 3u]); int v = s_m2 - 5 * s_m1 + 20 * s_0 + 20 * s_p1 - 5 * s_p2 + s_p3 + 16; int p = clamp(v >> 5, 0, 255); u_dst.dst[dst_off + r * stride + c] = uint8_t(p); }