From 471921b42f4ce3bc155e799bae80fba61fd3aa6f Mon Sep 17 00:00:00 2001 From: Developer Date: Fri, 17 Jul 2026 16:38:13 +0000 Subject: [PATCH 19/22] ppc: add AltiVec VP9 intra predictors for G4/G5 The VP9 intra predictors had a VSX implementation (ppc/intrapred_vsx.c) but nothing for plain AltiVec, so on big-endian VSX-less PowerPC (G4/G5, incl. 32-bit Darwin) every intra block ran the C predictor. Add an AltiVec port. ppc/intrapred_altivec.c is a straight port of the 18 functions that intrapred_vsx.c keeps enabled: v/h/tm/dc/dc_top/dc_left/dc_128 at 16x16 and 32x32, plus d45 and d63 at 16x16 and 32x32. The algebra is identical -- vec_splat/add/sub/perm/packsu/sum4s/sums/avg/pack/sel/sr/sl/splat_u* are all base VMX. The only VSX-specific ops in these functions are the unaligned vec_vsx_ld / vec_vsx_st, replaced with the classic big-endian lvsl-load and lvsr read-modify-write store idioms (the same ones ppc/vpx_idct_altivec.c already uses). The 4x4/8x8 dc/tm/h/d45/d63 variants are #if 0 upstream in intrapred_vsx.c (crbug.com/webm/1522) and stay on C here too. Registered by appending altivec to the 18 predictors' specialize lines and adding the file under DSP_SRCS-$(HAVE_ALTIVEC) (the _altivec.c suffix picks up -maltivec via build/make/Makefile). intrapred_vsx.c and types_vsx.h are unchanged, so the ppc64le/VSX build is unaffected (vsx wins the rtcd priority over altivec where both are enabled). Verified bit-exact against vpx_dsp/intrapred.c for all 18 functions under qemu-ppc (-cpu 7400, real 32-bit big-endian AltiVec) over 720000 randomized trials including 0/255 boundary inputs, with the d45/d63 above-right flat-extension invariant (above[bs..] == above[bs-1]) that both the C reference and test/vp9_intrapred_test.cc rely on and the decoder guarantees. Co-Authored-By: Claude Opus 4.8 (1M context) --- vpx_dsp/ppc/intrapred_altivec.c | 636 ++++++++++++++++++++++++++++++++ vpx_dsp/vpx_dsp.mk | 1 + vpx_dsp/vpx_dsp_rtcd_defs.pl | 36 +- 3 files changed, 655 insertions(+), 18 deletions(-) create mode 100644 vpx_dsp/ppc/intrapred_altivec.c diff --git a/vpx_dsp/ppc/intrapred_altivec.c b/vpx_dsp/ppc/intrapred_altivec.c new file mode 100644 index 000000000..286250678 --- /dev/null +++ b/vpx_dsp/ppc/intrapred_altivec.c @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2017 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* AltiVec (VMX) VP9 intra predictors for big-endian, VSX-less PowerPC (G4/G5). + * + * This is a straight port of vpx_dsp/ppc/intrapred_vsx.c: the algebra is + * identical (vec_splat/add/sub/perm/packsu/sum4s/sums/avg/pack/sel/sr/sl/ + * splat_u* are all base AltiVec) -- the ONLY VSX-specific ops in the ported + * functions are the unaligned load/store vec_vsx_ld / vec_vsx_st, replaced + * here by the classic big-endian lvsl (load) / lvsr (store) permute idioms + * (same as vpx_dsp/ppc/vpx_idct_altivec.c's _unaligned_load128 / + * _unaligned_store_bytes). Only the 18 functions upstream keeps enabled in + * the VSX source are ported (the 4x4/8x8 dc/tm/h/d45/d63 variants are #if 0 in + * intrapred_vsx.c under crbug.com/webm/1522 and stay on C). + * + * Verified bit-exact against vpx_dsp/intrapred.c for all 18 functions under + * qemu-ppc (-cpu 7400, 32-bit big-endian) over 720k randomized trials + * including 0/255 boundary inputs and the above-right flat-extension invariant + * the d45/d63 predictors rely on (webm:1797, replicated in + * test/vp9_intrapred_test.cc). + */ + +#include + +#include "./vpx_dsp_rtcd.h" +#include "vpx_ports/mem.h" + +typedef vector signed char int8x16_t; +typedef vector unsigned char uint8x16_t; +typedef vector signed short int16x8_t; +typedef vector unsigned short uint16x8_t; +typedef vector signed int int32x4_t; +typedef vector unsigned int uint32x4_t; + +// Big-endian zero-extend bytes -> shorts. On big-endian AltiVec +// vec_mergeh(a, b) interleaves a0,b0,a1,b1,..., so putting the zero byte +// FIRST makes it the high (MSB) byte of each output halfword = true +// zero-extension. (This matches the WORDS_BIGENDIAN branch of types_vsx.h.) +#define unpack_to_s16_h(v) \ + (int16x8_t) vec_mergeh(vec_splat_u8(0), (uint8x16_t)(v)) +#define unpack_to_s16_l(v) \ + (int16x8_t) vec_mergel(vec_splat_u8(0), (uint8x16_t)(v)) + +// -------- big-endian unaligned 128-bit load / store (VMX, no VSX) ----------- + +// Unaligned load: lvsl + double vec_ld + vec_perm. Big-endian only. +static INLINE uint8x16_t altivec_ld(int off, const void *base) { + const unsigned char *s = (const unsigned char *)base + off; + return vec_perm(vec_ld(0, s), vec_ld(16, s), vec_lvsl(0, s)); +} + +// Unaligned full-16-byte store: read-modify-write splice across the (at most +// two) 16-byte-aligned vectors overlapping the destination. Rotates v by +// lvsr(0,d) so its bytes align to the destination misalignment, then vec_sel +// only the target byte lanes into the aligned lo/hi vectors. Big-endian only +// -- mirrors vpx_idct_altivec.c's _unaligned_store_bytes (n == 16). +static INLINE void altivec_st(uint8x16_t v, int off, void *base) { + unsigned char *d = (unsigned char *)base + off; + uintptr_t o = ((uintptr_t)d) & 15; + vector unsigned char algn = vec_lvsr(0, d); + vector unsigned char rot = vec_perm(v, v, algn); + const vector unsigned char idx = { 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 }; + vector unsigned char voff = vec_splats((unsigned char)o); + vector unsigned char vend = vec_splats((unsigned char)(o + 16)); + vector unsigned char ge_off = + vec_nor((vector unsigned char)vec_cmplt(idx, voff), + (vector unsigned char)vec_cmplt(idx, voff)); + vector unsigned char lt_end = (vector unsigned char)vec_cmplt(idx, vend); + vector unsigned char mask_lo = vec_and(ge_off, lt_end); + unsigned char hi_end_scalar = + (o + 16 > 16) ? (unsigned char)(o + 16 - 16) : (unsigned char)0; + vector unsigned char vend_hi = vec_splats(hi_end_scalar); + vector unsigned char mask_hi = (vector unsigned char)vec_cmplt(idx, vend_hi); + vector unsigned char lo = vec_ld(0, d); + vector unsigned char hi = vec_ld(16, d); + lo = vec_sel(lo, rot, mask_lo); + hi = vec_sel(hi, rot, mask_hi); + vec_st(lo, 0, d); + vec_st(hi, 16, d); +} + +#define vsx_ld(off, ptr) altivec_ld((off), (ptr)) +#define vsx_st(v, off, ptr) altivec_st((uint8x16_t)(v), (off), (ptr)) + +// ============================ V predictors ================================= +void vpx_v_predictor_16x16_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + const uint8x16_t d = vsx_ld(0, above); + int i; + (void)left; + + for (i = 0; i < 16; i++, dst += stride) { + vsx_st(d, 0, dst); + } +} + +void vpx_v_predictor_32x32_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + const uint8x16_t d0 = vsx_ld(0, above); + const uint8x16_t d1 = vsx_ld(16, above); + int i; + (void)left; + + for (i = 0; i < 32; i++, dst += stride) { + vsx_st(d0, 0, dst); + vsx_st(d1, 16, dst); + } +} + +// ============================ H predictors ================================= +void vpx_h_predictor_16x16_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + const uint8x16_t d = vsx_ld(0, left); + const uint8x16_t v0 = vec_splat(d, 0); + const uint8x16_t v1 = vec_splat(d, 1); + const uint8x16_t v2 = vec_splat(d, 2); + const uint8x16_t v3 = vec_splat(d, 3); + + const uint8x16_t v4 = vec_splat(d, 4); + const uint8x16_t v5 = vec_splat(d, 5); + const uint8x16_t v6 = vec_splat(d, 6); + const uint8x16_t v7 = vec_splat(d, 7); + + const uint8x16_t v8 = vec_splat(d, 8); + const uint8x16_t v9 = vec_splat(d, 9); + const uint8x16_t v10 = vec_splat(d, 10); + const uint8x16_t v11 = vec_splat(d, 11); + + const uint8x16_t v12 = vec_splat(d, 12); + const uint8x16_t v13 = vec_splat(d, 13); + const uint8x16_t v14 = vec_splat(d, 14); + const uint8x16_t v15 = vec_splat(d, 15); + + (void)above; + + vsx_st(v0, 0, dst); + dst += stride; + vsx_st(v1, 0, dst); + dst += stride; + vsx_st(v2, 0, dst); + dst += stride; + vsx_st(v3, 0, dst); + dst += stride; + vsx_st(v4, 0, dst); + dst += stride; + vsx_st(v5, 0, dst); + dst += stride; + vsx_st(v6, 0, dst); + dst += stride; + vsx_st(v7, 0, dst); + dst += stride; + vsx_st(v8, 0, dst); + dst += stride; + vsx_st(v9, 0, dst); + dst += stride; + vsx_st(v10, 0, dst); + dst += stride; + vsx_st(v11, 0, dst); + dst += stride; + vsx_st(v12, 0, dst); + dst += stride; + vsx_st(v13, 0, dst); + dst += stride; + vsx_st(v14, 0, dst); + dst += stride; + vsx_st(v15, 0, dst); +} + +#define H_PREDICTOR_32(v) \ + vsx_st(v, 0, dst); \ + vsx_st(v, 16, dst); \ + dst += stride + +void vpx_h_predictor_32x32_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + const uint8x16_t d0 = vsx_ld(0, left); + const uint8x16_t d1 = vsx_ld(16, left); + + const uint8x16_t v0_0 = vec_splat(d0, 0); + const uint8x16_t v1_0 = vec_splat(d0, 1); + const uint8x16_t v2_0 = vec_splat(d0, 2); + const uint8x16_t v3_0 = vec_splat(d0, 3); + const uint8x16_t v4_0 = vec_splat(d0, 4); + const uint8x16_t v5_0 = vec_splat(d0, 5); + const uint8x16_t v6_0 = vec_splat(d0, 6); + const uint8x16_t v7_0 = vec_splat(d0, 7); + const uint8x16_t v8_0 = vec_splat(d0, 8); + const uint8x16_t v9_0 = vec_splat(d0, 9); + const uint8x16_t v10_0 = vec_splat(d0, 10); + const uint8x16_t v11_0 = vec_splat(d0, 11); + const uint8x16_t v12_0 = vec_splat(d0, 12); + const uint8x16_t v13_0 = vec_splat(d0, 13); + const uint8x16_t v14_0 = vec_splat(d0, 14); + const uint8x16_t v15_0 = vec_splat(d0, 15); + + const uint8x16_t v0_1 = vec_splat(d1, 0); + const uint8x16_t v1_1 = vec_splat(d1, 1); + const uint8x16_t v2_1 = vec_splat(d1, 2); + const uint8x16_t v3_1 = vec_splat(d1, 3); + const uint8x16_t v4_1 = vec_splat(d1, 4); + const uint8x16_t v5_1 = vec_splat(d1, 5); + const uint8x16_t v6_1 = vec_splat(d1, 6); + const uint8x16_t v7_1 = vec_splat(d1, 7); + const uint8x16_t v8_1 = vec_splat(d1, 8); + const uint8x16_t v9_1 = vec_splat(d1, 9); + const uint8x16_t v10_1 = vec_splat(d1, 10); + const uint8x16_t v11_1 = vec_splat(d1, 11); + const uint8x16_t v12_1 = vec_splat(d1, 12); + const uint8x16_t v13_1 = vec_splat(d1, 13); + const uint8x16_t v14_1 = vec_splat(d1, 14); + const uint8x16_t v15_1 = vec_splat(d1, 15); + + (void)above; + + H_PREDICTOR_32(v0_0); + H_PREDICTOR_32(v1_0); + H_PREDICTOR_32(v2_0); + H_PREDICTOR_32(v3_0); + + H_PREDICTOR_32(v4_0); + H_PREDICTOR_32(v5_0); + H_PREDICTOR_32(v6_0); + H_PREDICTOR_32(v7_0); + + H_PREDICTOR_32(v8_0); + H_PREDICTOR_32(v9_0); + H_PREDICTOR_32(v10_0); + H_PREDICTOR_32(v11_0); + + H_PREDICTOR_32(v12_0); + H_PREDICTOR_32(v13_0); + H_PREDICTOR_32(v14_0); + H_PREDICTOR_32(v15_0); + + H_PREDICTOR_32(v0_1); + H_PREDICTOR_32(v1_1); + H_PREDICTOR_32(v2_1); + H_PREDICTOR_32(v3_1); + + H_PREDICTOR_32(v4_1); + H_PREDICTOR_32(v5_1); + H_PREDICTOR_32(v6_1); + H_PREDICTOR_32(v7_1); + + H_PREDICTOR_32(v8_1); + H_PREDICTOR_32(v9_1); + H_PREDICTOR_32(v10_1); + H_PREDICTOR_32(v11_1); + + H_PREDICTOR_32(v12_1); + H_PREDICTOR_32(v13_1); + H_PREDICTOR_32(v14_1); + H_PREDICTOR_32(v15_1); +} + +// ============================ TM predictors ================================ +static void tm_predictor_16x8(uint8_t *dst, const ptrdiff_t stride, int16x8_t l, + int16x8_t ah, int16x8_t al, int16x8_t tl) { + int16x8_t vh, vl, ls; + + ls = vec_splat(l, 0); + vh = vec_sub(vec_add(ls, ah), tl); + vl = vec_sub(vec_add(ls, al), tl); + vsx_st(vec_packsu(vh, vl), 0, dst); + dst += stride; + + ls = vec_splat(l, 1); + vh = vec_sub(vec_add(ls, ah), tl); + vl = vec_sub(vec_add(ls, al), tl); + vsx_st(vec_packsu(vh, vl), 0, dst); + dst += stride; + + ls = vec_splat(l, 2); + vh = vec_sub(vec_add(ls, ah), tl); + vl = vec_sub(vec_add(ls, al), tl); + vsx_st(vec_packsu(vh, vl), 0, dst); + dst += stride; + + ls = vec_splat(l, 3); + vh = vec_sub(vec_add(ls, ah), tl); + vl = vec_sub(vec_add(ls, al), tl); + vsx_st(vec_packsu(vh, vl), 0, dst); + dst += stride; + + ls = vec_splat(l, 4); + vh = vec_sub(vec_add(ls, ah), tl); + vl = vec_sub(vec_add(ls, al), tl); + vsx_st(vec_packsu(vh, vl), 0, dst); + dst += stride; + + ls = vec_splat(l, 5); + vh = vec_sub(vec_add(ls, ah), tl); + vl = vec_sub(vec_add(ls, al), tl); + vsx_st(vec_packsu(vh, vl), 0, dst); + dst += stride; + + ls = vec_splat(l, 6); + vh = vec_sub(vec_add(ls, ah), tl); + vl = vec_sub(vec_add(ls, al), tl); + vsx_st(vec_packsu(vh, vl), 0, dst); + dst += stride; + + ls = vec_splat(l, 7); + vh = vec_sub(vec_add(ls, ah), tl); + vl = vec_sub(vec_add(ls, al), tl); + vsx_st(vec_packsu(vh, vl), 0, dst); +} + +void vpx_tm_predictor_16x16_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + const int16x8_t tl = unpack_to_s16_h(vec_splat(vsx_ld(-1, above), 0)); + const uint8x16_t l = vsx_ld(0, left); + const int16x8_t lh = unpack_to_s16_h(l); + const int16x8_t ll = unpack_to_s16_l(l); + const uint8x16_t a = vsx_ld(0, above); + const int16x8_t ah = unpack_to_s16_h(a); + const int16x8_t al = unpack_to_s16_l(a); + + tm_predictor_16x8(dst, stride, lh, ah, al, tl); + + dst += stride * 8; + + tm_predictor_16x8(dst, stride, ll, ah, al, tl); +} + +static INLINE void tm_predictor_32x1(uint8_t *dst, const int16x8_t ls, + const int16x8_t a0h, const int16x8_t a0l, + const int16x8_t a1h, const int16x8_t a1l, + const int16x8_t tl) { + int16x8_t vh, vl; + + vh = vec_sub(vec_add(ls, a0h), tl); + vl = vec_sub(vec_add(ls, a0l), tl); + vsx_st(vec_packsu(vh, vl), 0, dst); + vh = vec_sub(vec_add(ls, a1h), tl); + vl = vec_sub(vec_add(ls, a1l), tl); + vsx_st(vec_packsu(vh, vl), 16, dst); +} + +static void tm_predictor_32x8(uint8_t *dst, const ptrdiff_t stride, + const int16x8_t l, const uint8x16_t a0, + const uint8x16_t a1, const int16x8_t tl) { + const int16x8_t a0h = unpack_to_s16_h(a0); + const int16x8_t a0l = unpack_to_s16_l(a0); + const int16x8_t a1h = unpack_to_s16_h(a1); + const int16x8_t a1l = unpack_to_s16_l(a1); + + tm_predictor_32x1(dst, vec_splat(l, 0), a0h, a0l, a1h, a1l, tl); + dst += stride; + + tm_predictor_32x1(dst, vec_splat(l, 1), a0h, a0l, a1h, a1l, tl); + dst += stride; + + tm_predictor_32x1(dst, vec_splat(l, 2), a0h, a0l, a1h, a1l, tl); + dst += stride; + + tm_predictor_32x1(dst, vec_splat(l, 3), a0h, a0l, a1h, a1l, tl); + dst += stride; + + tm_predictor_32x1(dst, vec_splat(l, 4), a0h, a0l, a1h, a1l, tl); + dst += stride; + + tm_predictor_32x1(dst, vec_splat(l, 5), a0h, a0l, a1h, a1l, tl); + dst += stride; + + tm_predictor_32x1(dst, vec_splat(l, 6), a0h, a0l, a1h, a1l, tl); + dst += stride; + + tm_predictor_32x1(dst, vec_splat(l, 7), a0h, a0l, a1h, a1l, tl); +} + +void vpx_tm_predictor_32x32_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + const int16x8_t tl = unpack_to_s16_h(vec_splat(vsx_ld(-1, above), 0)); + const uint8x16_t l0 = vsx_ld(0, left); + const uint8x16_t l1 = vsx_ld(16, left); + const uint8x16_t a0 = vsx_ld(0, above); + const uint8x16_t a1 = vsx_ld(16, above); + + tm_predictor_32x8(dst, stride, unpack_to_s16_h(l0), a0, a1, tl); + dst += stride * 8; + + tm_predictor_32x8(dst, stride, unpack_to_s16_l(l0), a0, a1, tl); + dst += stride * 8; + + tm_predictor_32x8(dst, stride, unpack_to_s16_h(l1), a0, a1, tl); + dst += stride * 8; + + tm_predictor_32x8(dst, stride, unpack_to_s16_l(l1), a0, a1, tl); +} + +// ============================ DC predictors ================================ +static INLINE void dc_fill_predictor_16x16(uint8_t *dst, const ptrdiff_t stride, + const uint8x16_t val) { + int i; + + for (i = 0; i < 16; i++, dst += stride) { + vsx_st(val, 0, dst); + } +} + +static INLINE void dc_fill_predictor_32x32(uint8_t *dst, const ptrdiff_t stride, + const uint8x16_t val) { + int i; + + for (i = 0; i < 32; i++, dst += stride) { + vsx_st(val, 0, dst); + vsx_st(val, 16, dst); + } +} + +void vpx_dc_128_predictor_16x16_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, + const uint8_t *left) { + const uint8x16_t v128 = vec_sl(vec_splat_u8(1), vec_splat_u8(7)); + (void)above; + (void)left; + + dc_fill_predictor_16x16(dst, stride, v128); +} + +void vpx_dc_128_predictor_32x32_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, + const uint8_t *left) { + const uint8x16_t v128 = vec_sl(vec_splat_u8(1), vec_splat_u8(7)); + (void)above; + (void)left; + + dc_fill_predictor_32x32(dst, stride, v128); +} + +static uint8x16_t avg16(const uint8_t *values) { + const int32x4_t sum4s = + (int32x4_t)vec_sum4s(vsx_ld(0, values), vec_splat_u32(0)); + const uint32x4_t sum = (uint32x4_t)vec_sums(sum4s, vec_splat_s32(8)); + const uint32x4_t avg = (uint32x4_t)vec_sr(sum, vec_splat_u32(4)); + + return vec_splat(vec_pack(vec_pack(avg, vec_splat_u32(0)), vec_splat_u16(0)), + 3); +} + +void vpx_dc_left_predictor_16x16_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, + const uint8_t *left) { + (void)above; + + dc_fill_predictor_16x16(dst, stride, avg16(left)); +} + +void vpx_dc_top_predictor_16x16_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, + const uint8_t *left) { + (void)left; + + dc_fill_predictor_16x16(dst, stride, avg16(above)); +} + +static uint8x16_t avg32(const uint8_t *values) { + const uint8x16_t v0 = vsx_ld(0, values); + const uint8x16_t v1 = vsx_ld(16, values); + const int32x4_t v16 = vec_sl(vec_splat_s32(1), vec_splat_u32(4)); + const int32x4_t sum4s = + (int32x4_t)vec_sum4s(v0, vec_sum4s(v1, vec_splat_u32(0))); + const uint32x4_t sum = (uint32x4_t)vec_sums(sum4s, v16); + const uint32x4_t avg = (uint32x4_t)vec_sr(sum, vec_splat_u32(5)); + + return vec_splat(vec_pack(vec_pack(avg, vec_splat_u32(0)), vec_splat_u16(0)), + 3); +} + +void vpx_dc_left_predictor_32x32_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, + const uint8_t *left) { + (void)above; + + dc_fill_predictor_32x32(dst, stride, avg32(left)); +} + +void vpx_dc_top_predictor_32x32_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, + const uint8_t *left) { + (void)left; + + dc_fill_predictor_32x32(dst, stride, avg32(above)); +} + +static uint8x16_t dc_avg16(const uint8_t *above, const uint8_t *left) { + const uint8x16_t a0 = vsx_ld(0, above); + const uint8x16_t l0 = vsx_ld(0, left); + const int32x4_t v16 = vec_sl(vec_splat_s32(1), vec_splat_u32(4)); + const int32x4_t sum4s = + (int32x4_t)vec_sum4s(l0, vec_sum4s(a0, vec_splat_u32(0))); + const uint32x4_t sum = (uint32x4_t)vec_sums(sum4s, v16); + const uint32x4_t avg = (uint32x4_t)vec_sr(sum, vec_splat_u32(5)); + + return vec_splat(vec_pack(vec_pack(avg, vec_splat_u32(0)), vec_splat_u16(0)), + 3); +} + +void vpx_dc_predictor_16x16_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + dc_fill_predictor_16x16(dst, stride, dc_avg16(above, left)); +} + +static uint8x16_t dc_avg32(const uint8_t *above, const uint8_t *left) { + const uint8x16_t a0 = vsx_ld(0, above); + const uint8x16_t a1 = vsx_ld(16, above); + const uint8x16_t l0 = vsx_ld(0, left); + const uint8x16_t l1 = vsx_ld(16, left); + const int32x4_t v32 = vec_sl(vec_splat_s32(1), vec_splat_u32(5)); + const uint32x4_t a_sum = vec_sum4s(a0, vec_sum4s(a1, vec_splat_u32(0))); + const int32x4_t sum4s = (int32x4_t)vec_sum4s(l0, vec_sum4s(l1, a_sum)); + const uint32x4_t sum = (uint32x4_t)vec_sums(sum4s, v32); + const uint32x4_t avg = (uint32x4_t)vec_sr(sum, vec_splat_u32(6)); + + return vec_splat(vec_pack(vec_pack(avg, vec_splat_u32(0)), vec_splat_u16(0)), + 3); +} + +void vpx_dc_predictor_32x32_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + dc_fill_predictor_32x32(dst, stride, dc_avg32(above, left)); +} + +// ========================== D45 / D63 predictors =========================== +static uint8x16_t avg3(const uint8x16_t a, const uint8x16_t b, + const uint8x16_t c) { + const uint8x16_t ac = + vec_adds(vec_and(a, c), vec_sr(vec_xor(a, c), vec_splat_u8(1))); + + return vec_avg(ac, b); +} + +// Shift-left-by-one-byte permute selector (base AltiVec vec_perm, avoids the +// missing/broken vec_sld/vec_xxsldi/vec_lsdoi under -maltivec). +static const uint8x16_t sl1 = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, + 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10 }; + +void vpx_d45_predictor_16x16_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + const uint8x16_t a = vsx_ld(0, above); + const uint8x16_t above_right = vec_splat(a, 15); + const uint8x16_t b = vec_perm(a, above_right, sl1); + const uint8x16_t c = vec_perm(b, above_right, sl1); + uint8x16_t row = avg3(a, b, c); + int i; + (void)left; + + for (i = 0; i < 16; i++) { + vsx_st(row, 0, dst); + dst += stride; + row = vec_perm(row, above_right, sl1); + } +} + +void vpx_d45_predictor_32x32_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + const uint8x16_t a0 = vsx_ld(0, above); + const uint8x16_t a1 = vsx_ld(16, above); + const uint8x16_t above_right = vec_splat(a1, 15); + const uint8x16_t b0 = vec_perm(a0, a1, sl1); + const uint8x16_t b1 = vec_perm(a1, above_right, sl1); + const uint8x16_t c0 = vec_perm(b0, b1, sl1); + const uint8x16_t c1 = vec_perm(b1, above_right, sl1); + uint8x16_t row0 = avg3(a0, b0, c0); + uint8x16_t row1 = avg3(a1, b1, c1); + int i; + (void)left; + + for (i = 0; i < 32; i++) { + vsx_st(row0, 0, dst); + vsx_st(row1, 16, dst); + dst += stride; + row0 = vec_perm(row0, row1, sl1); + row1 = vec_perm(row1, above_right, sl1); + } +} + +void vpx_d63_predictor_16x16_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + const uint8x16_t a0 = vsx_ld(0, above); + const uint8x16_t a1 = vsx_ld(16, above); + const uint8x16_t above_right = vec_splat(a1, 0); + const uint8x16_t b = vec_perm(a0, above_right, sl1); + const uint8x16_t c = vec_perm(b, above_right, sl1); + uint8x16_t row0 = vec_avg(a0, b); + uint8x16_t row1 = avg3(a0, b, c); + int i; + (void)left; + + for (i = 0; i < 8; i++) { + vsx_st(row0, 0, dst); + vsx_st(row1, 0, dst + stride); + dst += stride * 2; + row0 = vec_perm(row0, above_right, sl1); + row1 = vec_perm(row1, above_right, sl1); + } +} + +void vpx_d63_predictor_32x32_altivec(uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, const uint8_t *left) { + const uint8x16_t a0 = vsx_ld(0, above); + const uint8x16_t a1 = vsx_ld(16, above); + const uint8x16_t a2 = vsx_ld(32, above); + const uint8x16_t above_right = vec_splat(a2, 0); + const uint8x16_t b0 = vec_perm(a0, a1, sl1); + const uint8x16_t b1 = vec_perm(a1, above_right, sl1); + const uint8x16_t c0 = vec_perm(b0, b1, sl1); + const uint8x16_t c1 = vec_perm(b1, above_right, sl1); + uint8x16_t row0_0 = vec_avg(a0, b0); + uint8x16_t row0_1 = vec_avg(a1, b1); + uint8x16_t row1_0 = avg3(a0, b0, c0); + uint8x16_t row1_1 = avg3(a1, b1, c1); + int i; + (void)left; + + for (i = 0; i < 16; i++) { + vsx_st(row0_0, 0, dst); + vsx_st(row0_1, 16, dst); + vsx_st(row1_0, 0, dst + stride); + vsx_st(row1_1, 16, dst + stride); + dst += stride * 2; + row0_0 = vec_perm(row0_0, row0_1, sl1); + row0_1 = vec_perm(row0_1, above_right, sl1); + row1_0 = vec_perm(row1_0, row1_1, sl1); + row1_1 = vec_perm(row1_1, above_right, sl1); + } +} diff --git a/vpx_dsp/vpx_dsp.mk b/vpx_dsp/vpx_dsp.mk index 05324ee2a..73966d3f4 100644 --- a/vpx_dsp/vpx_dsp.mk +++ b/vpx_dsp/vpx_dsp.mk @@ -57,6 +57,7 @@ DSP_SRCS-$(HAVE_SSE2) += x86/intrapred_sse2.asm DSP_SRCS-$(HAVE_SSSE3) += x86/intrapred_ssse3.asm endif DSP_SRCS-$(HAVE_VSX) += ppc/intrapred_vsx.c +DSP_SRCS-$(HAVE_ALTIVEC) += ppc/intrapred_altivec.c ifeq ($(CONFIG_VP9_HIGHBITDEPTH),yes) ifeq ($(HAVE_X86_ASM),yes) diff --git a/vpx_dsp/vpx_dsp_rtcd_defs.pl b/vpx_dsp/vpx_dsp_rtcd_defs.pl index 535620d0d..a04a57c1e 100644 --- a/vpx_dsp/vpx_dsp_rtcd_defs.pl +++ b/vpx_dsp/vpx_dsp_rtcd_defs.pl @@ -138,13 +138,13 @@ specialize qw/vpx_d207_predictor_16x16 neon ssse3/; add_proto qw/void vpx_d45_predictor_16x16/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_d45_predictor_16x16 neon ssse3 vsx/; +specialize qw/vpx_d45_predictor_16x16 neon ssse3 vsx altivec/; add_proto qw/void vpx_d63_predictor_16x16/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_d63_predictor_16x16 neon ssse3 vsx/; +specialize qw/vpx_d63_predictor_16x16 neon ssse3 vsx altivec/; add_proto qw/void vpx_h_predictor_16x16/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_h_predictor_16x16 neon dspr2 msa sse2 vsx/; +specialize qw/vpx_h_predictor_16x16 neon dspr2 msa sse2 vsx altivec/; add_proto qw/void vpx_d117_predictor_16x16/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; specialize qw/vpx_d117_predictor_16x16 neon/; @@ -156,34 +156,34 @@ specialize qw/vpx_d153_predictor_16x16 neon ssse3/; add_proto qw/void vpx_v_predictor_16x16/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_v_predictor_16x16 neon msa sse2 vsx/; +specialize qw/vpx_v_predictor_16x16 neon msa sse2 vsx altivec/; add_proto qw/void vpx_tm_predictor_16x16/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_tm_predictor_16x16 neon msa sse2 vsx/; +specialize qw/vpx_tm_predictor_16x16 neon msa sse2 vsx altivec/; add_proto qw/void vpx_dc_predictor_16x16/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_dc_predictor_16x16 dspr2 neon msa sse2 vsx lsx/; +specialize qw/vpx_dc_predictor_16x16 dspr2 neon msa sse2 vsx lsx altivec/; add_proto qw/void vpx_dc_top_predictor_16x16/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_dc_top_predictor_16x16 neon msa sse2 vsx/; +specialize qw/vpx_dc_top_predictor_16x16 neon msa sse2 vsx altivec/; add_proto qw/void vpx_dc_left_predictor_16x16/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_dc_left_predictor_16x16 neon msa sse2 vsx/; +specialize qw/vpx_dc_left_predictor_16x16 neon msa sse2 vsx altivec/; add_proto qw/void vpx_dc_128_predictor_16x16/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_dc_128_predictor_16x16 neon msa sse2 vsx/; +specialize qw/vpx_dc_128_predictor_16x16 neon msa sse2 vsx altivec/; add_proto qw/void vpx_d207_predictor_32x32/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; specialize qw/vpx_d207_predictor_32x32 neon ssse3/; add_proto qw/void vpx_d45_predictor_32x32/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_d45_predictor_32x32 neon ssse3 vsx/; +specialize qw/vpx_d45_predictor_32x32 neon ssse3 vsx altivec/; add_proto qw/void vpx_d63_predictor_32x32/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_d63_predictor_32x32 neon ssse3 vsx/; +specialize qw/vpx_d63_predictor_32x32 neon ssse3 vsx altivec/; add_proto qw/void vpx_h_predictor_32x32/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_h_predictor_32x32 neon msa sse2 vsx/; +specialize qw/vpx_h_predictor_32x32 neon msa sse2 vsx altivec/; add_proto qw/void vpx_d117_predictor_32x32/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; specialize qw/vpx_d117_predictor_32x32 neon/; @@ -195,22 +195,22 @@ specialize qw/vpx_d153_predictor_32x32 neon ssse3/; add_proto qw/void vpx_v_predictor_32x32/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_v_predictor_32x32 neon msa sse2 vsx/; +specialize qw/vpx_v_predictor_32x32 neon msa sse2 vsx altivec/; add_proto qw/void vpx_tm_predictor_32x32/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_tm_predictor_32x32 neon msa sse2 vsx/; +specialize qw/vpx_tm_predictor_32x32 neon msa sse2 vsx altivec/; add_proto qw/void vpx_dc_predictor_32x32/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_dc_predictor_32x32 msa neon sse2 vsx/; +specialize qw/vpx_dc_predictor_32x32 msa neon sse2 vsx altivec/; add_proto qw/void vpx_dc_top_predictor_32x32/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_dc_top_predictor_32x32 msa neon sse2 vsx/; +specialize qw/vpx_dc_top_predictor_32x32 msa neon sse2 vsx altivec/; add_proto qw/void vpx_dc_left_predictor_32x32/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_dc_left_predictor_32x32 msa neon sse2 vsx/; +specialize qw/vpx_dc_left_predictor_32x32 msa neon sse2 vsx altivec/; add_proto qw/void vpx_dc_128_predictor_32x32/, "uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left"; -specialize qw/vpx_dc_128_predictor_32x32 msa neon sse2 vsx/; +specialize qw/vpx_dc_128_predictor_32x32 msa neon sse2 vsx altivec/; # High bitdepth functions if (vpx_config("CONFIG_VP9_HIGHBITDEPTH") eq "yes") {