From 2d0c926d07de6f50a844d52df57da2bd8088efab Mon Sep 17 00:00:00 2001 From: Sergey Fedorov 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 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 +#include + +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