/*
* Copyright ( c ) 2025 , Alliance for Open Media . All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1 . 0 . If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file , you can
* obtain it at www . aomedia . org / license / software . If the Alliance for Open
* Media Patent License 1 . 0 was not distributed with this source code in the
* PATENTS file , you can obtain it at www . aomedia . org / license / patent .
*/
#include <arm_neon.h>
#include <assert.h>
#include "config/aom_config.h"
#include "config/av1_rtcd.h"
#include "aom_dsp/arm/mem_neon.h"
#define FILTER_INTRA_SCALE_BITS 4
// The input arrays are reordered compared to the C implementation: the first
// four vectors contain the lower elements of the original filter, the next four
// vectors contain the upper elements. This layout allows all 8 multiplications
// to accumulate into a single element using USDOT instructions, instead of
// having two partial sums in adjacent vector elements and needing to combine
// them with an additional pairwise add.
DECLARE_ALIGNED(16 , static const int8_t,
av1_filter_intra_taps_neon_i8mm[FILTER_INTRA_MODES][8 ][8 ]) = {
{
{ -6 , 10 , 0 , 0 , -5 , 2 , 10 , 0 },
{ -3 , 1 , 1 , 10 , -3 , 1 , 1 , 2 },
{ -4 , 6 , 0 , 0 , -3 , 2 , 6 , 0 },
{ -3 , 2 , 2 , 6 , -3 , 1 , 2 , 2 },
{ 0 , 12 , 0 , 0 , 0 , 9 , 0 , 0 },
{ 0 , 7 , 0 , 0 , 10 , 5 , 0 , 0 },
{ 0 , 2 , 12 , 0 , 0 , 2 , 9 , 0 },
{ 0 , 2 , 7 , 0 , 6 , 3 , 5 , 0 },
},
{
{ -10 , 16 , 0 , 0 , -6 , 0 , 16 , 0 },
{ -4 , 0 , 0 , 16 , -2 , 0 , 0 , 0 },
{ -10 , 16 , 0 , 0 , -6 , 0 , 16 , 0 },
{ -4 , 0 , 0 , 16 , -2 , 0 , 0 , 0 },
{ 0 , 10 , 0 , 0 , 0 , 6 , 0 , 0 },
{ 0 , 4 , 0 , 0 , 16 , 2 , 0 , 0 },
{ 0 , 0 , 10 , 0 , 0 , 0 , 6 , 0 },
{ 0 , 0 , 4 , 0 , 16 , 0 , 2 , 0 },
},
{
{ -8 , 8 , 0 , 0 , -8 , 0 , 8 , 0 },
{ -8 , 0 , 0 , 8 , -8 , 0 , 0 , 0 },
{ -4 , 4 , 0 , 0 , -4 , 0 , 4 , 0 },
{ -4 , 0 , 0 , 4 , -4 , 0 , 0 , 0 },
{ 0 , 16 , 0 , 0 , 0 , 16 , 0 , 0 },
{ 0 , 16 , 0 , 0 , 8 , 16 , 0 , 0 },
{ 0 , 0 , 16 , 0 , 0 , 0 , 16 , 0 },
{ 0 , 0 , 16 , 0 , 4 , 0 , 16 , 0 },
},
{
{ -2 , 8 , 0 , 0 , -1 , 3 , 8 , 0 },
{ -1 , 2 , 3 , 8 , 0 , 1 , 2 , 3 },
{ -1 , 4 , 0 , 0 , -1 , 3 , 4 , 0 },
{ -1 , 2 , 3 , 4 , -1 , 2 , 2 , 3 },
{ 0 , 10 , 0 , 0 , 0 , 6 , 0 , 0 },
{ 0 , 4 , 0 , 0 , 8 , 2 , 0 , 0 },
{ 0 , 3 , 10 , 0 , 0 , 4 , 6 , 0 },
{ 0 , 4 , 4 , 0 , 4 , 3 , 3 , 0 },
},
{
{ -12 , 14 , 0 , 0 , -10 , 0 , 14 , 0 },
{ -9 , 0 , 0 , 14 , -8 , 0 , 0 , 0 },
{ -10 , 12 , 0 , 0 , -9 , 1 , 12 , 0 },
{ -8 , 0 , 0 , 12 , -7 , 0 , 0 , 1 },
{ 0 , 14 , 0 , 0 , 0 , 12 , 0 , 0 },
{ 0 , 11 , 0 , 0 , 14 , 10 , 0 , 0 },
{ 0 , 0 , 14 , 0 , 0 , 0 , 12 , 0 },
{ 0 , 1 , 11 , 0 , 12 , 1 , 9 , 0 },
},
};
static inline uint8x8_t filter_intra_predictor(uint8x16_t p_lo, uint8x16_t p_hi,
const int8x16_t f01,
const int8x16_t f23,
const int8x16_t f45,
const int8x16_t f67) {
int32x4_t acc_0123 = vusdotq_s32(vdupq_n_s32(0 ), p_lo, f01);
acc_0123 = vusdotq_s32(acc_0123, p_hi, f23);
int32x4_t acc_4567 = vusdotq_s32(vdupq_n_s32(0 ), p_lo, f45);
acc_4567 = vusdotq_s32(acc_4567, p_hi, f67);
const int16x8_t acc = vcombine_s16(vmovn_s32(acc_0123), vmovn_s32(acc_4567));
return vqrshrun_n_s16(acc, FILTER_INTRA_SCALE_BITS);
}
void av1_filter_intra_predictor_neon_i8mm(uint8_t *dst, ptrdiff_t stride,
TX_SIZE tx_size, const uint8_t *above,
const uint8_t *left, int mode) {
const int bw = tx_size_wide[tx_size];
const int bh = tx_size_high[tx_size];
if (bw == 4 || (bw == 8 && bh < 16 ) || (bw == 16 && bh <= 4 ) || bw == 32 ) {
av1_filter_intra_predictor_neon(dst, stride, tx_size, above, left, mode);
return ;
}
assert(bw <= 32 && bh <= 32 );
const int8x16_t f01 = vld1q_s8(av1_filter_intra_taps_neon_i8mm[mode][0 ]);
const int8x16_t f45 = vld1q_s8(av1_filter_intra_taps_neon_i8mm[mode][2 ]);
const int8x16_t f23 = vld1q_s8(av1_filter_intra_taps_neon_i8mm[mode][4 ]);
const int8x16_t f67 = vld1q_s8(av1_filter_intra_taps_neon_i8mm[mode][6 ]);
// indexes : 0, 19, 23, -1
uint8x16_t p_hi_idx = vreinterpretq_u8_u32(vdupq_n_u32(0 xFF171300));
uint64_t l01 = ((uint64_t)left[0 ] << 24 ) | ((uint64_t)left[1 ] << 56 );
uint8x16_t l = vreinterpretq_u8_u64(vdupq_n_u64(l01));
int c = 0 ;
do {
const uint8_t *ptr = above + c - 1 ;
uint32_t lo =
ptr[0 ] | (ptr[1 ] << 8 ) | (ptr[2 ] << 16 ) | ((uint32_t)ptr[3 ] << 24 );
uint8x16_t p_lo = vreinterpretq_u8_u32(vdupq_n_u32(lo));
uint8x16x2_t hi;
hi.val[0 ] = vdupq_n_u8(ptr[4 ]);
hi.val[1 ] = l;
uint8x16_t p_hi = vqtbl2q_u8(hi, p_hi_idx);
const uint8x8_t res =
filter_intra_predictor(p_lo, p_hi, f01, f23, f45, f67);
store_u8x4_strided_x2(dst + c, stride, res);
l = vcombine_u8(res, res);
c += 4 ;
} while (c < bw);
dst += 2 * stride;
int r = 2 ;
while (r < bh) {
const uint8_t *ptr = dst - stride;
uint32_t lo =
left[r - 1 ] | (ptr[0 ] << 8 ) | (ptr[1 ] << 16 ) | ((uint32_t)ptr[2 ] << 24 );
uint32_t hi = ptr[3 ] | (left[r] << 8 ) | (left[r + 1 ] << 16 );
uint8x16_t p_lo = vreinterpretq_u8_u32(vdupq_n_u32(lo));
uint8x16_t p_hi = vreinterpretq_u8_u32(vdupq_n_u32(hi));
uint8x8_t res = filter_intra_predictor(p_lo, p_hi, f01, f23, f45, f67);
store_u8x4_strided_x2(dst, stride, res);
l = vcombine_u8(res, res);
c = 4 ;
while (c < bw) {
ptr = dst - stride + c - 1 ;
lo = ptr[0 ] | (ptr[1 ] << 8 ) | (ptr[2 ] << 16 ) | ((uint32_t)ptr[3 ] << 24 );
p_lo = vreinterpretq_u8_u32(vdupq_n_u32(lo));
uint8x16x2_t hi_v;
hi_v.val[0 ] = vdupq_n_u8(ptr[4 ]);
hi_v.val[1 ] = l;
p_hi = vqtbl2q_u8(hi_v, p_hi_idx);
res = filter_intra_predictor(p_lo, p_hi, f01, f23, f45, f67);
store_u8x4_strided_x2(dst + c, stride, res);
l = vcombine_u8(res, res);
c += 4 ;
}
r += 2 ;
dst += 2 * stride;
}
}
Messung V0.5 in Prozent C=96 H=70 G=83
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-08-26)
¤
*© Formatika GbR, Deutschland