cb3aef3dac
Follows PR #26 (Intra_4x4 luma) with the same promotion pattern for the rest of the intra prediction primitive set: Intra_16x16 luma (4 modes, PR #13) — V/H/DC/Plane Intra_8x8 chroma (4 modes, PR #14) — DC/H/V/Plane (4:2:0) Intra_8x8 luma (9 modes, PRs #21 + #22) — High profile, with 1-2-1 pre-filter 3 file moves via `git mv`, ~17 function renames stripping the `_ref` suffix. Test binaries rewired to link daedalus_core instead of compiling the (now moved) ref files directly. No code change — pure plumbing for substitution-arc consumers. 26 intra prediction modes total now in the public API after this PR. Verified on hertz: test_intra_pred_16x16: 5/5 PASS test_intra_pred_chroma8x8: 5/5 PASS test_intra_pred_8x8_luma: 11/11 PASS All via public symbols (test binaries linked against daedalus_core). Unblocks marfrit-packages substitution arc patch 0014 — wires H264PredContext.pred4x4[], pred16x16[], pred8x8[], pred8x8l[] through daedalus alongside the existing IDCT / deblock / qpel / DC Hadamard substitutions. After 0014 lands, the libavcodec.so built by marfrit-packages will have EVERY hot-path pixel-math kernel of an H.264 8-bit 4:2:0 decode routing through daedalus — the substitution arc is feature- complete for the campaign target (Pi 5 Firefox YouTube playback).
171 lines
7.2 KiB
C
171 lines
7.2 KiB
C
/*
|
||
* Tests the H.264 Intra_8x8 luma prediction modes against spec-derived
|
||
* expectations. Buffer layout is 9 rows × 17 cols (extra cols for the
|
||
* top-right extension that DDL/VL need; not exercised by V/H/DC but
|
||
* already in-place for the eventual directional-modes follow-up):
|
||
*
|
||
* row 0: [tl][t0..t15] — 17 bytes
|
||
* row 1: [l0][output row 0 ..] — 17 bytes
|
||
* ...
|
||
* row 8: [l7][output row 7 ..]
|
||
*/
|
||
#include <stdint.h>
|
||
#include <stddef.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
extern void daedalus_h264_pred_8x8l_vertical(uint8_t *dst, ptrdiff_t stride);
|
||
extern void daedalus_h264_pred_8x8l_horizontal(uint8_t *dst, ptrdiff_t stride);
|
||
extern void daedalus_h264_pred_8x8l_dc(uint8_t *dst, ptrdiff_t stride);
|
||
extern void daedalus_h264_pred_8x8l_ddl(uint8_t *dst, ptrdiff_t stride);
|
||
extern void daedalus_h264_pred_8x8l_ddr(uint8_t *dst, ptrdiff_t stride);
|
||
extern void daedalus_h264_pred_8x8l_vr(uint8_t *dst, ptrdiff_t stride);
|
||
extern void daedalus_h264_pred_8x8l_hd(uint8_t *dst, ptrdiff_t stride);
|
||
extern void daedalus_h264_pred_8x8l_vl(uint8_t *dst, ptrdiff_t stride);
|
||
extern void daedalus_h264_pred_8x8l_hu(uint8_t *dst, ptrdiff_t stride);
|
||
|
||
#define STRIDE 17
|
||
#define ROWS 9
|
||
|
||
static void set_ctx(uint8_t buf[ROWS][STRIDE], int tl,
|
||
const int t[16], const int l[8])
|
||
{
|
||
for (int r = 0; r < ROWS; r++)
|
||
for (int c = 0; c < STRIDE; c++) buf[r][c] = 0xff;
|
||
buf[0][0] = (uint8_t) tl;
|
||
for (int c = 0; c < 16; c++) buf[0][1 + c] = (uint8_t) t[c];
|
||
for (int r = 0; r < 8; r++) buf[1 + r][0] = (uint8_t) l[r];
|
||
}
|
||
|
||
static int check_uniform(const uint8_t buf[ROWS][STRIDE], const char *name,
|
||
uint8_t expect_val)
|
||
{
|
||
int diff = 0;
|
||
for (int r = 0; r < 8; r++)
|
||
for (int c = 0; c < 8; c++)
|
||
if (buf[1+r][1+c] != expect_val) diff++;
|
||
if (diff == 0) printf(" %-30s PASS\n", name);
|
||
else printf(" %-30s FAIL (%d/64 wrong, expected %u)\n", name, diff, expect_val);
|
||
return diff == 0 ? 0 : 1;
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
int fail = 0;
|
||
|
||
/* Mode 0 Vertical with uniform top → uniform output.
|
||
* Filtered top[c] = (a + 2*a + a + 2) >> 2 = a for uniform a. */
|
||
{
|
||
uint8_t buf[ROWS][STRIDE];
|
||
int t[16], l[8];
|
||
for (int i = 0; i < 16; i++) t[i] = 50;
|
||
for (int j = 0; j < 8; j++) l[j] = 0;
|
||
set_ctx(buf, 50, t, l);
|
||
daedalus_h264_pred_8x8l_vertical(&buf[1][1], STRIDE);
|
||
fail |= check_uniform(buf, "Vertical (mode 0, uniform top)", 50);
|
||
}
|
||
|
||
/* Mode 1 Horizontal with uniform left → uniform output. */
|
||
{
|
||
uint8_t buf[ROWS][STRIDE];
|
||
int t[16] = {0}, l[8];
|
||
for (int j = 0; j < 8; j++) l[j] = 70;
|
||
set_ctx(buf, 70, t, l);
|
||
daedalus_h264_pred_8x8l_horizontal(&buf[1][1], STRIDE);
|
||
fail |= check_uniform(buf, "Horizontal (mode 1, uniform left)", 70);
|
||
}
|
||
|
||
/* Mode 2 DC with all-uniform neighbours → uniform output.
|
||
* Filtered top[c] = top for uniform; filtered left[j] = left.
|
||
* sum = 8*a + 8*a + 8 = 16a + 8. >> 4 = a (exact when +8 rounds). */
|
||
{
|
||
uint8_t buf[ROWS][STRIDE];
|
||
int t[16], l[8];
|
||
for (int i = 0; i < 16; i++) t[i] = 33;
|
||
for (int j = 0; j < 8; j++) l[j] = 33;
|
||
set_ctx(buf, 33, t, l);
|
||
daedalus_h264_pred_8x8l_dc(&buf[1][1], STRIDE);
|
||
fail |= check_uniform(buf, "DC (mode 2, uniform)", 33);
|
||
}
|
||
|
||
/* Mode 0 Vertical with NON-uniform top: gradient 0..15. Filtered
|
||
* top[c] for c in 1..14 = (t[c-1] + 2*t[c] + t[c+1] + 2) >> 2
|
||
* = (c-1 + 2c + c+1 + 2) >> 2
|
||
* = (4c + 2) >> 2 = c (since (4c+2)/4 = c with rounding).
|
||
* Wait — (4c + 2) >> 2 = c + 0 (since 4c is divisible by 4 and +2 rounds
|
||
* BELOW 4, doesn't change anything). So filtered = c for c=1..14.
|
||
* filt[0] (top-left) = (t[0] + 2*tl + l[0] + 2) >> 2 (not exercised
|
||
* directly by Vertical mode).
|
||
* filt[top 0] = (tl + 2*t[0] + t[1] + 2) >> 2 = (0 + 0 + 1 + 2) >> 2 = 0
|
||
* (tl=0, t[0]=0, t[1]=1)
|
||
* filt[top 15] = (t[14] + 3*t[15] + 2) >> 2 = (14 + 45 + 2) >> 2
|
||
* = 61 >> 2 = 15
|
||
*
|
||
* So Vertical output col 0 = filt[top 0] = 0, col 1 = filt[top 1] = 1,
|
||
* ..., col 7 = filt[top 7] = 7. Same for all 8 rows. */
|
||
{
|
||
uint8_t buf[ROWS][STRIDE];
|
||
int t[16], l[8] = {0};
|
||
for (int i = 0; i < 16; i++) t[i] = i;
|
||
set_ctx(buf, 0, t, l);
|
||
daedalus_h264_pred_8x8l_vertical(&buf[1][1], STRIDE);
|
||
int diff = 0;
|
||
for (int r = 0; r < 8; r++)
|
||
for (int c = 0; c < 8; c++)
|
||
if (buf[1+r][1+c] != c) diff++;
|
||
if (diff == 0) printf(" %-30s PASS (filtered gradient)\n", "Vertical (mode 0, gradient)");
|
||
else printf(" %-30s FAIL (%d/64 wrong)\n", "Vertical (mode 0, gradient)", diff);
|
||
fail |= (diff == 0) ? 0 : 1;
|
||
}
|
||
|
||
/* Mode 1 Horizontal gradient: left = 0..7. Filtered left:
|
||
* filt[left 0] = (tl + 2*l[0] + l[1] + 2) >> 2 = (0 + 0 + 1 + 2) >> 2 = 0
|
||
* filt[left j] for j=1..6 = (l[j-1] + 2*l[j] + l[j+1] + 2) >> 2 = j
|
||
* (same arithmetic as top)
|
||
* filt[left 7] = (l[6] + 3*l[7] + 2) >> 2 = (6 + 21 + 2) >> 2 = 7
|
||
* So Horizontal output row 0 = 0, row 7 = 7. */
|
||
{
|
||
uint8_t buf[ROWS][STRIDE];
|
||
int t[16] = {0}, l[8];
|
||
for (int j = 0; j < 8; j++) l[j] = j;
|
||
set_ctx(buf, 0, t, l);
|
||
daedalus_h264_pred_8x8l_horizontal(&buf[1][1], STRIDE);
|
||
int diff = 0;
|
||
for (int r = 0; r < 8; r++)
|
||
for (int c = 0; c < 8; c++)
|
||
if (buf[1+r][1+c] != r) diff++;
|
||
if (diff == 0) printf(" %-30s PASS (filtered gradient)\n", "Horizontal (mode 1, gradient)");
|
||
else printf(" %-30s FAIL (%d/64 wrong)\n", "Horizontal (mode 1, gradient)", diff);
|
||
fail |= (diff == 0) ? 0 : 1;
|
||
}
|
||
|
||
/* Directional modes — uniform-context sanity tests. With all
|
||
* neighbours = N, the 1-2-1 filter produces uniform N, and any
|
||
* 3-tap / 2-tap on uniform N produces N. So every directional
|
||
* mode should output uniform N on uniform input. */
|
||
{
|
||
typedef void (*pred_fn_t)(uint8_t *dst, ptrdiff_t stride);
|
||
struct { const char *name; pred_fn_t fn; } modes[] = {
|
||
{ "DDL (mode 3, uniform)", daedalus_h264_pred_8x8l_ddl },
|
||
{ "DDR (mode 4, uniform)", daedalus_h264_pred_8x8l_ddr },
|
||
{ "VR (mode 5, uniform)", daedalus_h264_pred_8x8l_vr },
|
||
{ "HD (mode 6, uniform)", daedalus_h264_pred_8x8l_hd },
|
||
{ "VL (mode 7, uniform)", daedalus_h264_pred_8x8l_vl },
|
||
{ "HU (mode 8, uniform)", daedalus_h264_pred_8x8l_hu },
|
||
};
|
||
for (size_t i = 0; i < sizeof(modes)/sizeof(modes[0]); i++) {
|
||
uint8_t buf[ROWS][STRIDE];
|
||
int t[16], l[8];
|
||
for (int k = 0; k < 16; k++) t[k] = 120;
|
||
for (int k = 0; k < 8; k++) l[k] = 120;
|
||
set_ctx(buf, 120, t, l);
|
||
modes[i].fn(&buf[1][1], STRIDE);
|
||
fail |= check_uniform(buf, modes[i].name, 120);
|
||
}
|
||
}
|
||
|
||
if (fail == 0) printf("\nALL Intra_8x8 luma PASS (9 modes — V, H, DC, DDL, DDR, VR, HD, VL, HU)\n");
|
||
else fprintf(stderr, "\n%d test(s) FAILED\n", fail);
|
||
return fail ? 1 : 0;
|
||
}
|