// daedalus-fourier โ€” H.264 luma qpel mc33 (8x8, diagonal quarter-pel), // V3D 7.1. Per H.264 ยง8.4.2.2.1 (table 8-4) โ€” composes two half-pel // anchors via L2 rounded-average: // // mc33[r,c] = avg(mc20(r+1, c), // mc02(r, c+1)) // // Per-lane structure: each lane computes BOTH anchor outputs at its // own (r, c) target offset, then L2 averages. No shared memory. // Same WG geometry as the other qpel shaders. // // 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[]; } u_meta; layout(push_constant) uniform PC { uint n_blocks, stride_u8, _p0, _p1; } pc; int hpel_h(uint src_off, uint stride, uint r, uint c) { 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 ]); 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; return clamp(v >> 5, 0, 255); } int hpel_v(uint src_off, uint stride, uint r, uint c) { uint col_base = src_off + c; int s_m2 = int(u_src.src[col_base + (r - 2u) * stride]); int s_m1 = int(u_src.src[col_base + (r - 1u) * stride]); int s_0 = int(u_src.src[col_base + r * stride]); int s_p1 = int(u_src.src[col_base + (r + 1u) * stride]); int s_p2 = int(u_src.src[col_base + (r + 2u) * stride]); int s_p3 = int(u_src.src[col_base + (r + 3u) * stride]); int v = s_m2 - 5*s_m1 + 20*s_0 + 20*s_p1 - 5*s_p2 + s_p3 + 16; return clamp(v >> 5, 0, 255); } int hpel_hv_row(uint src_off, uint stride, uint rr, uint c) { // Single row's int16 horizontal lowpass (NOT clipped โ€” used as // intermediate for the vertical pass of hpel_hv). uint row_base = src_off + rr * 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 ]); 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]); return s_m2 - 5*s_m1 + 20*s_0 + 20*s_p1 - 5*s_p2 + s_p3; } int hpel_hv(uint src_off, uint stride, uint r, uint c) { int t0 = hpel_hv_row(src_off, stride, r - 2u, c); int t1 = hpel_hv_row(src_off, stride, r - 1u, c); int t2 = hpel_hv_row(src_off, stride, r, c); int t3 = hpel_hv_row(src_off, stride, r + 1u, c); int t4 = hpel_hv_row(src_off, stride, r + 2u, c); int t5 = hpel_hv_row(src_off, stride, r + 3u, c); int v = t0 - 5*t1 + 20*t2 + 20*t3 - 5*t4 + t5 + 512; return clamp(v >> 10, 0, 255); } void main() { uint block_idx = gl_WorkGroupID.x; if (block_idx >= pc.n_blocks) return; uint lane = gl_LocalInvocationID.x; uint r = lane >> 3, c = lane & 7u; uint dst_off = u_meta.meta[block_idx].x; uint src_off = u_meta.meta[block_idx].y; uint stride = pc.stride_u8; int a = hpel_h(src_off, stride, r+1u, c); int b = hpel_v(src_off, stride, r, c+1u); int avg = (a + b + 1) >> 1; u_dst.dst[dst_off + r * stride + c] = uint8_t(avg); }