From 2d0c926d07de6f50a844d52df57da2bd8088efab Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <barracuda@macos-powerpc.org>
Date: Fri, 24 Jul 2026 03:54:29 +0000
Subject: [PATCH 06/38] avcodec/ppc: add AltiVec H.264 intra prediction (16x16,
 8x8 chroma)

New libavcodec/ppc/h264pred.c + h264pred_init.c, dispatched via
ff_h264_pred_init_ppc (ARCH_PPC branch in the shared h264pred.c),
8-bit only:

- 16x16: vertical, horizontal, dc, left_dc, top_dc, 128_dc.
- 8x8 chroma (chroma_format_idc <= 1 only): vertical, horizontal,
  128_dc always; dc/left_dc/top_dc gated off for RV40/VP7/VP8, which
  use different DC predictors at the same H264PredContext slots
  (mirrors the equivalent gate in aarch64/h264pred_init.c).

8x8 rows are 8 bytes wide, so plain 16-byte vec_st would clobber the
next 8 bytes; st8() reuses the aligned-load/vec_perm/store-back idiom
from CHROMA_MC8_ALTIVEC_CORE in h264chroma_template.c to write exactly
8 bytes regardless of dst's 8- or 16-byte alignment.

4x4 (4-byte-wide rows) and pred16x16/8x8_plane are left on the C path:
4x4 gains nothing from vectorizing a single-word store per row, and
plane's H/V gradient fill wasn't included in this pass.

VALIDATED: checkasm h264pred passes under the cross-PPC/qemu harness
(qemu-ppc-static -cpu 7400) across 6+ random seeds, real ALTIVEC
dispatch confirmed for both the pred8x8 and pred16x16 groups, sweeping
checkasm's own H264/VP8/RV40/SVQ3 codec_id x bit_depth x chroma_format
matrix (confirms the RV40/VP7/VP8 DC gate is correct -- no mismatches
against those codecs' own DC predictors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0194bzLUrg6cVCQ7u4NNDpaB
---
 libavcodec/h264pred.c             |   2 +
 libavcodec/h264pred.h             |   2 +
 libavcodec/ppc/Makefile           |   1 +
 libavcodec/ppc/h264pred.c         | 233 ++++++++++++++++++++++++++++++
 libavcodec/ppc/h264pred_altivec.h |  41 ++++++
 libavcodec/ppc/h264pred_init.c    |  62 ++++++++
 6 files changed, 341 insertions(+)
 create mode 100644 libavcodec/ppc/h264pred.c
 create mode 100644 libavcodec/ppc/h264pred_altivec.h
 create mode 100644 libavcodec/ppc/h264pred_init.c

diff --git a/libavcodec/h264pred.c b/libavcodec/h264pred.c
index 5de0d6f..ec067e3 100644
--- a/libavcodec/h264pred.c
+++ b/libavcodec/h264pred.c
@@ -592,6 +592,8 @@ av_cold void ff_h264_pred_init(H264PredContext *h, int codec_id,
     ff_h264_pred_init_aarch64(h, codec_id, bit_depth, chroma_format_idc);
 #elif ARCH_ARM
     ff_h264_pred_init_arm(h, codec_id, bit_depth, chroma_format_idc);
+#elif ARCH_PPC
+    ff_h264_pred_init_ppc(h, codec_id, bit_depth, chroma_format_idc);
 #elif ARCH_X86 && HAVE_X86ASM
     ff_h264_pred_init_x86(h, codec_id, bit_depth, chroma_format_idc);
 #elif ARCH_MIPS
diff --git a/libavcodec/h264pred.h b/libavcodec/h264pred.h
index 8ac5088..a059a2d 100644
--- a/libavcodec/h264pred.h
+++ b/libavcodec/h264pred.h
@@ -120,6 +120,8 @@ void ff_h264_pred_init_aarch64(H264PredContext *h, int codec_id,
                                const int chroma_format_idc);
 void ff_h264_pred_init_arm(H264PredContext *h, int codec_id,
                            const int bit_depth, const int chroma_format_idc);
+void ff_h264_pred_init_ppc(H264PredContext *h, int codec_id,
+                           const int bit_depth, const int chroma_format_idc);
 void ff_h264_pred_init_x86(H264PredContext *h, int codec_id,
                            const int bit_depth, const int chroma_format_idc);
 void ff_h264_pred_init_mips(H264PredContext *h, int codec_id,
diff --git a/libavcodec/ppc/Makefile b/libavcodec/ppc/Makefile
index 3f30e05..2be552f 100644
--- a/libavcodec/ppc/Makefile
+++ b/libavcodec/ppc/Makefile
@@ -5,6 +5,7 @@ OBJS-$(CONFIG_FDCTDSP)                 += ppc/fdctdsp.o
 OBJS-$(CONFIG_FMTCONVERT)              += ppc/fmtconvert_altivec.o
 OBJS-$(CONFIG_H264CHROMA)              += ppc/h264chroma_init.o
 OBJS-$(CONFIG_H264DSP)                 += ppc/h264dsp.o ppc/hpeldsp_altivec.o
+OBJS-$(CONFIG_H264PRED)                += ppc/h264pred_init.o ppc/h264pred.o
 OBJS-$(CONFIG_H264QPEL)                += ppc/h264qpel.o
 OBJS-$(CONFIG_HPELDSP)                 += ppc/hpeldsp_altivec.o
 OBJS-$(CONFIG_IDCTDSP)                 += ppc/idctdsp.o
diff --git a/libavcodec/ppc/h264pred.c b/libavcodec/ppc/h264pred.c
new file mode 100644
index 0000000..4fdccee
--- /dev/null
+++ b/libavcodec/ppc/h264pred.c
@@ -0,0 +1,233 @@
+/*
+ * H.264 intra prediction, AltiVec optimized
+ *
+ * Copyright (c) 2026
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include "libavutil/attributes.h"
+#include "libavutil/cpu.h"
+#include "libavutil/mem_internal.h"
+#include "libavutil/ppc/cpu.h"
+#include "libavutil/ppc/util_altivec.h"
+
+#include "h264pred_altivec.h"
+
+#if HAVE_ALTIVEC && HAVE_BIGENDIAN
+
+/* 16x16 vertical/horizontal/DC variants and 8x8 chroma vertical/horizontal/DC
+ * variants (8-bit, chroma_format_idc <= 1 only -- see ff_h264_pred_init_ppc).
+ * 4x4 rows are only 4 bytes wide, so a vector store buys nothing over the
+ * existing scalar AV_WN4PA word stores; left on the C path. pred*_plane is
+ * not yet vectorized. */
+
+/* Store an 8-byte-wide row without touching the 8 bytes beyond it: load the
+ * 16-byte-aligned vector that overlaps [dst, dst+8), merge in the new bytes
+ * on whichever half `dst`'s alignment puts them, write the full vector back.
+ * Same idiom as CHROMA_MC8_ALTIVEC_CORE's fperm/vec_perm/vec_st in
+ * h264chroma_template.c. `v` must have the 8 wanted bytes replicated in
+ * both halves (low == high), so either fperm selection yields the same
+ * result regardless of alignment.
+ */
+static inline void st8(uint8_t *dst, vec_u8 v)
+{
+    vec_u8 vdst = vec_ld(0, dst);
+    vec_u8 fperm = (((uintptr_t)dst) % 16 == 0) ?
+        (vec_u8){ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                  0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F } :
+        (vec_u8){ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                  0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F };
+
+    vec_st(vec_perm(vdst, v, fperm), 0, dst);
+}
+
+static inline vec_u8 dup8x2(uint8_t v)
+{
+    return vec_splats(v);
+}
+
+/* ------------------------------- 16x16 ---------------------------------- */
+
+void ff_pred16x16_vertical_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i;
+    vec_u8 a = vec_ld(0, src - stride);
+
+    for (i = 0; i < 16; i++)
+        vec_st(a, 0, src + i * stride);
+}
+
+void ff_pred16x16_horizontal_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i;
+
+    for (i = 0; i < 16; i++)
+        vec_st(dup8x2(src[-1 + i * stride]), 0, src + i * stride);
+}
+
+void ff_pred16x16_dc_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i, dc = 0;
+    vec_u8 a;
+
+    for (i = 0; i < 16; i++)
+        dc += src[-1 + i * stride];
+    for (i = 0; i < 16; i++)
+        dc += src[i - stride];
+
+    a = dup8x2((dc + 16) >> 5);
+    for (i = 0; i < 16; i++)
+        vec_st(a, 0, src + i * stride);
+}
+
+void ff_pred16x16_left_dc_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i, dc = 0;
+    vec_u8 a;
+
+    for (i = 0; i < 16; i++)
+        dc += src[-1 + i * stride];
+
+    a = dup8x2((dc + 8) >> 4);
+    for (i = 0; i < 16; i++)
+        vec_st(a, 0, src + i * stride);
+}
+
+void ff_pred16x16_top_dc_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i, dc = 0;
+    vec_u8 a;
+
+    for (i = 0; i < 16; i++)
+        dc += src[i - stride];
+
+    a = dup8x2((dc + 8) >> 4);
+    for (i = 0; i < 16; i++)
+        vec_st(a, 0, src + i * stride);
+}
+
+void ff_pred16x16_128_dc_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i;
+    vec_u8 a = dup8x2(128);
+
+    for (i = 0; i < 16; i++)
+        vec_st(a, 0, src + i * stride);
+}
+
+/* ---------------------------- 8x8 chroma --------------------------------- */
+
+static const vec_u8 dup_low8 = { 0, 1, 2, 3, 4, 5, 6, 7,
+                                 0, 1, 2, 3, 4, 5, 6, 7 };
+
+void ff_pred8x8_vertical_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i;
+    /* Only the low 8 bytes (the actual row) are meaningful; duplicate them
+     * into the high half so st8()'s fperm selection is alignment-agnostic. */
+    vec_u8 row = unaligned_load(0, src - stride);
+    vec_u8 a = vec_perm(row, row, dup_low8);
+
+    for (i = 0; i < 8; i++)
+        st8(src + i * stride, a);
+}
+
+void ff_pred8x8_horizontal_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i;
+
+    for (i = 0; i < 8; i++)
+        st8(src + i * stride, dup8x2(src[-1 + i * stride]));
+}
+
+void ff_pred8x8_128_dc_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i;
+    vec_u8 a = dup8x2(128);
+
+    for (i = 0; i < 8; i++)
+        st8(src + i * stride, a);
+}
+
+void ff_pred8x8_left_dc_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i, dc0 = 0, dc2 = 0;
+    vec_u8 a0, a2;
+
+    for (i = 0; i < 4; i++) {
+        dc0 += src[-1 + i * stride];
+        dc2 += src[-1 + (i + 4) * stride];
+    }
+    a0 = dup8x2((dc0 + 2) >> 2);
+    a2 = dup8x2((dc2 + 2) >> 2);
+
+    for (i = 0; i < 4; i++)
+        st8(src + i * stride, a0);
+    for (i = 4; i < 8; i++)
+        st8(src + i * stride, a2);
+}
+
+/* Build an 8-byte row (replicated into both vector halves, like dup8x2)
+ * whose left 4 bytes are `lo` and right 4 bytes are `hi`. */
+static inline vec_u8 halves8(uint8_t lo, uint8_t hi)
+{
+    vec_u8 vlo = vec_splats(lo), vhi = vec_splats(hi);
+    /* vec_perm byte indices: 0-15 select from the 1st operand (vlo), 16-31
+     * from the 2nd (vhi). Want {lo x4, hi x4} twice. */
+    static const vec_u8 sel = { 0, 0, 0, 0, 16, 16, 16, 16,
+                                0, 0, 0, 0, 16, 16, 16, 16 };
+    return vec_perm(vlo, vhi, sel);
+}
+
+void ff_pred8x8_top_dc_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i, dc0 = 0, dc1 = 0;
+    vec_u8 a;
+
+    for (i = 0; i < 4; i++) {
+        dc0 += src[i - stride];
+        dc1 += src[4 + i - stride];
+    }
+    a = halves8((dc0 + 2) >> 2, (dc1 + 2) >> 2);
+
+    for (i = 0; i < 8; i++)
+        st8(src + i * stride, a);
+}
+
+void ff_pred8x8_dc_altivec(uint8_t *src, ptrdiff_t stride)
+{
+    int i, dc0 = 0, dc1 = 0, dc2 = 0;
+    vec_u8 a0, a1;
+
+    for (i = 0; i < 4; i++) {
+        dc0 += src[-1 + i * stride] + src[i - stride];
+        dc1 += src[4 + i - stride];
+        dc2 += src[-1 + (i + 4) * stride];
+    }
+    a0 = halves8((dc0 + 4) >> 3, (dc1 + 2) >> 2);
+    a1 = halves8((dc2 + 2) >> 2, (dc1 + dc2 + 4) >> 3);
+
+    for (i = 0; i < 4; i++)
+        st8(src + i * stride, a0);
+    for (i = 4; i < 8; i++)
+        st8(src + i * stride, a1);
+}
+
+#endif /* HAVE_ALTIVEC && HAVE_BIGENDIAN */
diff --git a/libavcodec/ppc/h264pred_altivec.h b/libavcodec/ppc/h264pred_altivec.h
new file mode 100644
index 0000000..73563f6
--- /dev/null
+++ b/libavcodec/ppc/h264pred_altivec.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2026
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_PPC_H264PRED_ALTIVEC_H
+#define AVCODEC_PPC_H264PRED_ALTIVEC_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+void ff_pred16x16_vertical_altivec(uint8_t *src, ptrdiff_t stride);
+void ff_pred16x16_horizontal_altivec(uint8_t *src, ptrdiff_t stride);
+void ff_pred16x16_dc_altivec(uint8_t *src, ptrdiff_t stride);
+void ff_pred16x16_left_dc_altivec(uint8_t *src, ptrdiff_t stride);
+void ff_pred16x16_top_dc_altivec(uint8_t *src, ptrdiff_t stride);
+void ff_pred16x16_128_dc_altivec(uint8_t *src, ptrdiff_t stride);
+
+void ff_pred8x8_vertical_altivec(uint8_t *src, ptrdiff_t stride);
+void ff_pred8x8_horizontal_altivec(uint8_t *src, ptrdiff_t stride);
+void ff_pred8x8_128_dc_altivec(uint8_t *src, ptrdiff_t stride);
+void ff_pred8x8_left_dc_altivec(uint8_t *src, ptrdiff_t stride);
+void ff_pred8x8_top_dc_altivec(uint8_t *src, ptrdiff_t stride);
+void ff_pred8x8_dc_altivec(uint8_t *src, ptrdiff_t stride);
+
+#endif /* AVCODEC_PPC_H264PRED_ALTIVEC_H */
diff --git a/libavcodec/ppc/h264pred_init.c b/libavcodec/ppc/h264pred_init.c
new file mode 100644
index 0000000..b7cd012
--- /dev/null
+++ b/libavcodec/ppc/h264pred_init.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2026
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include "libavutil/attributes.h"
+#include "libavutil/cpu.h"
+#include "libavutil/ppc/cpu.h"
+
+#include "libavcodec/codec_id.h"
+#include "libavcodec/h264pred.h"
+
+#if HAVE_ALTIVEC && HAVE_BIGENDIAN
+#include "h264pred_altivec.h"
+#endif
+
+av_cold void ff_h264_pred_init_ppc(H264PredContext *h, int codec_id,
+                                   const int bit_depth,
+                                   const int chroma_format_idc)
+{
+#if HAVE_ALTIVEC && HAVE_BIGENDIAN
+    if (!PPC_ALTIVEC(av_get_cpu_flags()) || bit_depth != 8)
+        return;
+
+    h->pred16x16[VERT_PRED8x8   ] = ff_pred16x16_vertical_altivec;
+    h->pred16x16[HOR_PRED8x8    ] = ff_pred16x16_horizontal_altivec;
+    h->pred16x16[DC_PRED8x8     ] = ff_pred16x16_dc_altivec;
+    h->pred16x16[LEFT_DC_PRED8x8] = ff_pred16x16_left_dc_altivec;
+    h->pred16x16[TOP_DC_PRED8x8 ] = ff_pred16x16_top_dc_altivec;
+    h->pred16x16[DC_128_PRED8x8 ] = ff_pred16x16_128_dc_altivec;
+
+    if (chroma_format_idc <= 1) {
+        h->pred8x8[VERT_PRED8x8  ] = ff_pred8x8_vertical_altivec;
+        h->pred8x8[HOR_PRED8x8   ] = ff_pred8x8_horizontal_altivec;
+        h->pred8x8[DC_128_PRED8x8] = ff_pred8x8_128_dc_altivec;
+
+        if (codec_id != AV_CODEC_ID_RV40 && codec_id != AV_CODEC_ID_VP7 &&
+            codec_id != AV_CODEC_ID_VP8) {
+            h->pred8x8[DC_PRED8x8     ] = ff_pred8x8_dc_altivec;
+            h->pred8x8[LEFT_DC_PRED8x8] = ff_pred8x8_left_dc_altivec;
+            h->pred8x8[TOP_DC_PRED8x8 ] = ff_pred8x8_top_dc_altivec;
+        }
+    }
+#endif
+}
-- 
2.43.0

