746533582e
Closes the put_ qpel QPU matrix. Adds mc11/12/13/21/23/31/32/33 — each composes two half-pel anchor outputs via L2 rounded-average: mc11 ¼¼ : avg(mc20[r, c], mc02[r, c]) mc12 ¼½ : avg(mc22[r, c], mc02[r, c]) mc13 ¼¾ : avg(mc20[r+1, c], mc02[r, c]) mc21 ½¼ : avg(mc22[r, c], mc20[r, c]) mc23 ½¾ : avg(mc22[r, c], mc20[r+1, c]) mc31 ¾¼ : avg(mc20[r, c], mc02[r, c+1]) mc32 ¾½ : avg(mc22[r, c], mc02[r, c+1]) mc33 ¾¾ : avg(mc20[r+1, c], mc02[r, c+1]) Per-lane structure: each lane runs the FULL cascade for BOTH anchors at its own (r, c) target, then L2 averages. No shared memory. Shaders inline hpel_h() / hpel_v() / hpel_hv() helpers (the latter does the 13×6 int16 cascade per cell). ~88 lines each. Shaders generated from a python template (POSITIONS table + format string) — the 8 .comp files are 1:1 with the C reference's DEFINE_DIAG_REF macro from fourier PR #18. Dispatch plumbing: shared dispatch_h264_qpel_diag_qpu helper covers all 8 (same src envelope as mc22: src_max = src_off + 10*stride + 11, covering rows -2..+10 and cols -2..+10 for any (r±1, c±1) offset). Recipe table: all 8 DAEDALUS_KERNEL_H264_QPEL_MC{11..33} flipped to QPU. Public dispatchers re-defined via DEFINE_QPEL_DIAG_PUBLIC macro (replaces the old DEFINE_QPEL_DISPATCH which fast-failed QPU). Verified on hertz: $ ./build/test_api_h264 | grep "qpel mc[1-3][1-3]" H.264 qpel mc11: 2048/2048 bytes bit-exact (100.0000%) H.264 qpel mc12: 2048/2048 bytes bit-exact (100.0000%) H.264 qpel mc13: 2048/2048 bytes bit-exact (100.0000%) H.264 qpel mc21: 2048/2048 bytes bit-exact (100.0000%) H.264 qpel mc23: 2048/2048 bytes bit-exact (100.0000%) H.264 qpel mc31: 2048/2048 bytes bit-exact (100.0000%) H.264 qpel mc32: 2048/2048 bytes bit-exact (100.0000%) H.264 qpel mc33: 2048/2048 bytes bit-exact (100.0000%) Meaningful: the (r±1, c±1) offsets are easy to transpose between positions; passing first try on the asymmetric variants (mc13/23/31/33) means the position-specific shifts are correct in all 8 templates. put_ qpel QPU matrix is now COMPLETE: 15 of 15 useful positions (mc00 = integer copy, no shader needed). avg_ qpel positions (15 more) remain on CPU NEON; can land as a follow-up since avg_ is just put_ + one extra L2 against existing dst. put_ mc20 ✓ mc02 ✓ mc22 ✓ (anchors) mc10 ✓ mc30 ✓ mc01 ✓ mc03 ✓ (single-axis ¼-pel) mc11 ✓ mc12 ✓ mc13 ✓ (this PR — row-1 diagonals) mc21 ✓ mc23 ✓ (this PR — row-2 diagonals) mc31 ✓ mc32 ✓ mc33 ✓ (this PR — row-3 diagonals) avg_ all 15 — CPU NEON
89 lines
3.4 KiB
Plaintext
89 lines
3.4 KiB
Plaintext
// daedalus-fourier — H.264 luma qpel mc12 (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:
|
|
//
|
|
// mc12[r,c] = avg(mc22(r, c),
|
|
// mc02(r, c))
|
|
//
|
|
// 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_hv(src_off, stride, r, c);
|
|
int b = hpel_v(src_off, stride, r, c);
|
|
int avg = (a + b + 1) >> 1;
|
|
u_dst.dst[dst_off + r * stride + c] = uint8_t(avg);
|
|
}
|