From bc1b8bd1dc3ff0784b90fef7cfc9d068ff2fed59 Mon Sep 17 00:00:00 2001 From: Developer Date: Tue, 14 Jul 2026 21:21:37 +0000 Subject: [PATCH 12/17] ppc: use wraparound (not saturating) add/sub/pack in IDCT4/IDCT8 math Continuation of the AltiVec IDCT correctness work: the plain-C reference this port must match bit-exact (vpx_dsp/inv_txfm.c) computes every transform-stage addition, subtraction, and round-shift-then-narrow step on wide (>=32-bit) intermediates and only narrows to 16 bits at specific points via implicit C truncating conversion -- never via saturation. This file's IDCT4 (idct4_vmx, vpx_idct4x4_16_add_altivec) and the shared IDCT8 macro used saturating vec_adds/vec_subs for stage additions and saturating vec_packs for the round-shift narrowing step, which silently diverges from the reference the moment any intermediate would exceed the int16 range -- exactly the kind of large-residual coefficient that shows up in high-detail/high-motion content, matching the reported symptom ("moving objects look really bad, made of large pixels"). Switched the affected sites to plain (non-saturating) vec_add/vec_sub and vec_pack, which truncate the same way the reference's implicit int16 narrowing does. Left every genuinely saturating operation alone: the final pixel reconstruction (vec_packsu, clamping residual+prediction to 0..255) is correct as-is, and vec_msum's wide 32-bit accumulation was already non-saturating and unaffected. Verified against the QEMU/AltiVec cross-check harness: all four DC-only functions plus the pure-DCT_DCT paths of vpx_idct4x4_16_add and vp9_iht4x4_16_add now match vpx_dsp/inv_txfm.c's C reference exactly (0 failures) at every coefficient magnitude tested, including full int16 range extremes, where they previously diverged. IADST-involving paths (tx_type 1-3) in this same file still fail the cross-check on specific extreme-magnitude coefficient combinations and are not fixed by this commit -- the existing iadst4/8/16_vmx routines use a hand-optimized algebraic rearrangement that doesn't map cleanly onto either the SSE2 or scalar-C reference for verification, and are being reworked separately. 8x8/16x16/32x32 IDCT (non-IADST) paths likely share this same class of fix but are not yet done. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01DLC53yuiz8taQDNndUp4rs --- vpx_dsp/ppc/vpx_idct_altivec.c | 48 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/vpx_dsp/ppc/vpx_idct_altivec.c b/vpx_dsp/ppc/vpx_idct_altivec.c index 3fcb01f08..3268222fd 100644 --- a/vpx_dsp/ppc/vpx_idct_altivec.c +++ b/vpx_dsp/ppc/vpx_idct_altivec.c @@ -214,8 +214,8 @@ void vpx_idct4x4_16_add_altivec(const tran_low_t *input, uint8_t *dest, int stri wnput3 = vec_sra(wnput3, dct_bitshift_vec); // Stage 2. Pack back to short. - input0 = vec_packs(wnput0, wnput1); - input1 = vec_packs(wnput2, wnput3); + input0 = vec_pack(wnput0, wnput1); + input1 = vec_pack(wnput2, wnput3); // Transpose. input2 = vec_mergeh(input0, input1); @@ -253,8 +253,8 @@ void vpx_idct4x4_16_add_altivec(const tran_low_t *input, uint8_t *dest, int stri wnput3 = vec_sra(wnput3, dct_bitshift_vec); // Stage 2 again. Pack back to short. - input0 = vec_packs(wnput0, wnput2); - input1 = vec_packs(wnput1, wnput3); + input0 = vec_pack(wnput0, wnput2); + input1 = vec_pack(wnput1, wnput3); // Transpose again. input2 = vec_mergeh(input0, input1); @@ -535,8 +535,8 @@ static void idct4_vmx(vector signed short *in) { w[2] = vec_sra(w[2], dct_bitshift_vec); w[3] = vec_sra(w[3], dct_bitshift_vec); - u[0] = vec_packs(w[0], w[1]); - u[1] = vec_packs(w[3], w[2]); + u[0] = vec_pack(w[0], w[1]); + u[1] = vec_pack(w[3], w[2]); // Stage 2. in[0] = vec_add(u[0], u[1]); @@ -601,8 +601,8 @@ static void iadst4_vmx(vector signed short *in) { w[2] = vec_sra(v[2], dct_bitshift_vec); w[3] = vec_sra(v[3], dct_bitshift_vec); - in[0] = vec_packs(w[0], w[1]); - in[1] = vec_packs(w[2], w[3]); + in[0] = vec_pack(w[0], w[1]); + in[1] = vec_pack(w[2], w[3]); } void vp9_iht4x4_16_add_altivec(const tran_low_t *input, uint8_t *dest, int stride, int tx_type) { @@ -760,10 +760,10 @@ void vp9_iht4x4_16_add_altivec(const tran_low_t *input, uint8_t *dest, int strid stg2_1, stg2_2, stg2_3, stp2_0, \ stp2_1, stp2_2, stp2_3) \ \ - stp2_4 = vec_adds(stp1_4, stp1_5); \ - stp2_5 = vec_subs(stp1_4, stp1_5); \ - stp2_6 = vec_subs(stp1_7, stp1_6); \ - stp2_7 = vec_adds(stp1_7, stp1_6); \ + stp2_4 = vec_add(stp1_4, stp1_5); \ + stp2_5 = vec_sub(stp1_4, stp1_5); \ + stp2_6 = vec_sub(stp1_7, stp1_6); \ + stp2_7 = vec_add(stp1_7, stp1_6); \ } \ \ /* Stage 3. */ \ @@ -771,10 +771,10 @@ void vp9_iht4x4_16_add_altivec(const tran_low_t *input, uint8_t *dest, int strid vector signed short lo_56 = vec_mergeh(stp2_6, stp2_5); \ vector signed short hi_56 = vec_mergel(stp2_6, stp2_5); \ \ - stp1_0 = vec_adds(stp2_0, stp2_3); \ - stp1_1 = vec_adds(stp2_1, stp2_2); \ - stp1_2 = vec_subs(stp2_1, stp2_2); \ - stp1_3 = vec_subs(stp2_0, stp2_3); \ + stp1_0 = vec_add(stp2_0, stp2_3); \ + stp1_1 = vec_add(stp2_1, stp2_2); \ + stp1_2 = vec_sub(stp2_1, stp2_2); \ + stp1_3 = vec_sub(stp2_0, stp2_3); \ \ tmp0 = vec_msum(lo_56, stg2_1, dct_rounding_vec); \ tmp1 = vec_msum(hi_56, stg2_1, dct_rounding_vec); \ @@ -791,14 +791,14 @@ void vp9_iht4x4_16_add_altivec(const tran_low_t *input, uint8_t *dest, int strid } \ \ /* Stage 4. */ \ - out0 = vec_adds(stp1_0, stp2_7); \ - out1 = vec_adds(stp1_1, stp1_6); \ - out2 = vec_adds(stp1_2, stp1_5); \ - out3 = vec_adds(stp1_3, stp2_4); \ - out4 = vec_subs(stp1_3, stp2_4); \ - out5 = vec_subs(stp1_2, stp1_5); \ - out6 = vec_subs(stp1_1, stp1_6); \ - out7 = vec_subs(stp1_0, stp2_7); \ + out0 = vec_add(stp1_0, stp2_7); \ + out1 = vec_add(stp1_1, stp1_6); \ + out2 = vec_add(stp1_2, stp1_5); \ + out3 = vec_add(stp1_3, stp2_4); \ + out4 = vec_sub(stp1_3, stp2_4); \ + out5 = vec_sub(stp1_2, stp1_5); \ + out6 = vec_sub(stp1_1, stp1_6); \ + out7 = vec_sub(stp1_0, stp2_7); \ } void vpx_idct8x8_64_add_altivec(const tran_low_t *input, uint8_t *dest, int stride) { -- 2.43.0