PR #22864 opened by kjg0724 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22864 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22864.patch
Add AArch64 NEON optimised implementations of lfe_fir0_float and lfe_fir1_float in the DCA decoder. Precompute two LFE float vectors (raw + reversed) before the inner loop to eliminate per-iteration reversal, keeping the hot path clean. Benchmarks on AWS Graviton3 (Neoverse V1, c7g.xlarge): lfe_fir0_float: C 5857.9 cycles -> NEON 2183.4 cycles (2.68x) lfe_fir1_float: C 2749.1 cycles -> NEON 1689.7 cycles (1.63x) Signed-off-by: Jeongkeun Kim <[email protected]> >From faec00479cafd130e6776ace1c79f14a323f69c2 Mon Sep 17 00:00:00 2001 From: Jeongkeun Kim <[email protected]> Date: Mon, 20 Apr 2026 19:18:57 +0900 Subject: [PATCH 1/2] WIP: avcodec/aarch64: add NEON blockdsp functions --- libavcodec/aarch64/Makefile | 2 + libavcodec/aarch64/blockdsp_init_aarch64.c | 47 +++++++++++++++ libavcodec/aarch64/blockdsp_neon.S | 68 ++++++++++++++++++++++ libavcodec/blockdsp.c | 4 +- libavcodec/blockdsp.h | 1 + 5 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 libavcodec/aarch64/blockdsp_init_aarch64.c create mode 100644 libavcodec/aarch64/blockdsp_neon.S diff --git a/libavcodec/aarch64/Makefile b/libavcodec/aarch64/Makefile index 085376ecd6..f9c52699ab 100644 --- a/libavcodec/aarch64/Makefile +++ b/libavcodec/aarch64/Makefile @@ -1,5 +1,6 @@ # subsystems OBJS-$(CONFIG_AC3DSP) += aarch64/ac3dsp_init_aarch64.o +OBJS-$(CONFIG_BLOCKDSP) += aarch64/blockdsp_init_aarch64.o OBJS-$(CONFIG_FDCTDSP) += aarch64/fdctdsp_init_aarch64.o OBJS-$(CONFIG_FMTCONVERT) += aarch64/fmtconvert_init.o OBJS-$(CONFIG_H264CHROMA) += aarch64/h264chroma_init_aarch64.o @@ -43,6 +44,7 @@ ARMV8-OBJS-$(CONFIG_VIDEODSP) += aarch64/videodsp.o NEON-OBJS-$(CONFIG_AAC_DECODER) += aarch64/sbrdsp_neon.o NEON-OBJS-$(CONFIG_AAC_ENCODER) += aarch64/aacencdsp_neon.o NEON-OBJS-$(CONFIG_AC3DSP) += aarch64/ac3dsp_neon.o +NEON-OBJS-$(CONFIG_BLOCKDSP) += aarch64/blockdsp_neon.o NEON-OBJS-$(CONFIG_FDCTDSP) += aarch64/fdctdsp_neon.o NEON-OBJS-$(CONFIG_FMTCONVERT) += aarch64/fmtconvert_neon.o NEON-OBJS-$(CONFIG_H264CHROMA) += aarch64/h264cmc_neon.o diff --git a/libavcodec/aarch64/blockdsp_init_aarch64.c b/libavcodec/aarch64/blockdsp_init_aarch64.c new file mode 100644 index 0000000000..2b1b9324aa --- /dev/null +++ b/libavcodec/aarch64/blockdsp_init_aarch64.c @@ -0,0 +1,47 @@ +/* + * AArch64 NEON optimised block operations + * Copyright (c) 2026 Jeongkeun Kim <[email protected]> + * + * 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 <stdint.h> + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/aarch64/cpu.h" +#include "libavcodec/blockdsp.h" + +void ff_clear_block_neon(int16_t *block); +void ff_clear_blocks_neon(int16_t *blocks); +void ff_fill_block16_neon(uint8_t *block, uint8_t value, + ptrdiff_t line_size, int h); +void ff_fill_block8_neon(uint8_t *block, uint8_t value, + ptrdiff_t line_size, int h); + +av_cold void ff_blockdsp_init_aarch64(BlockDSPContext *c) +{ + int cpu_flags = av_get_cpu_flags(); + + if (have_neon(cpu_flags)) { + c->clear_block = ff_clear_block_neon; + c->clear_blocks = ff_clear_blocks_neon; + + c->fill_block_tab[0] = ff_fill_block16_neon; + c->fill_block_tab[1] = ff_fill_block8_neon; + } +} diff --git a/libavcodec/aarch64/blockdsp_neon.S b/libavcodec/aarch64/blockdsp_neon.S new file mode 100644 index 0000000000..664b8bacd7 --- /dev/null +++ b/libavcodec/aarch64/blockdsp_neon.S @@ -0,0 +1,68 @@ +/* + * AArch64 NEON optimised block functions + * Copyright (c) 2026 Jeongkeun Kim <[email protected]> + * + * 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 "libavutil/aarch64/asm.S" + +// void ff_clear_block_neon(int16_t *block) +// x0: block (aligned 32, 8x8 int16_t = 128 bytes) +function ff_clear_block_neon, export=1 + movi v0.8h, #0 + .rept 4 + stp q0, q0, [x0], #32 + .endr + ret +endfunc + +// void ff_clear_blocks_neon(int16_t *blocks) +// x0: blocks (aligned 32, 6 * 8x8 int16_t = 768 bytes) +function ff_clear_blocks_neon, export=1 + movi v0.8h, #0 + .rept 24 + stp q0, q0, [x0], #32 + .endr + ret +endfunc + +// void ff_fill_block16_neon(uint8_t *block, uint8_t value, +// ptrdiff_t line_size, int h) +// x0: block, w1: value, x2: line_size, w3: h +function ff_fill_block16_neon, export=1 + dup v0.16b, w1 +1: + str q0, [x0] + add x0, x0, x2 + subs w3, w3, #1 + b.gt 1b + ret +endfunc + +// void ff_fill_block8_neon(uint8_t *block, uint8_t value, +// ptrdiff_t line_size, int h) +// x0: block, w1: value, x2: line_size, w3: h +function ff_fill_block8_neon, export=1 + dup v0.8b, w1 +1: + str d0, [x0] + add x0, x0, x2 + subs w3, w3, #1 + b.gt 1b + ret +endfunc diff --git a/libavcodec/blockdsp.c b/libavcodec/blockdsp.c index 793e7664ec..2de4b652d0 100644 --- a/libavcodec/blockdsp.c +++ b/libavcodec/blockdsp.c @@ -63,7 +63,9 @@ av_cold void ff_blockdsp_init(BlockDSPContext *c) c->fill_block_tab[0] = fill_block16_c; c->fill_block_tab[1] = fill_block8_c; -#if ARCH_ARM +#if ARCH_AARCH64 + ff_blockdsp_init_aarch64(c); +#elif ARCH_ARM ff_blockdsp_init_arm(c); #elif ARCH_PPC ff_blockdsp_init_ppc(c); diff --git a/libavcodec/blockdsp.h b/libavcodec/blockdsp.h index f83068ce53..0992238f3e 100644 --- a/libavcodec/blockdsp.h +++ b/libavcodec/blockdsp.h @@ -38,6 +38,7 @@ typedef struct BlockDSPContext { void ff_blockdsp_init(BlockDSPContext *c); +void ff_blockdsp_init_aarch64(BlockDSPContext *c); void ff_blockdsp_init_arm(BlockDSPContext *c); void ff_blockdsp_init_ppc(BlockDSPContext *c); void ff_blockdsp_init_riscv(BlockDSPContext *c); -- 2.52.0 >From d6ef9f4640da3f8cfb602d32432a9dbe5fcd7f7c Mon Sep 17 00:00:00 2001 From: Jeongkeun Kim <[email protected]> Date: Mon, 20 Apr 2026 23:34:45 +0900 Subject: [PATCH 2/2] avcodec/aarch64: add NEON DCA LFE FIR filter functions Port lfe_fir0_float and lfe_fir1_float to AArch64 NEON. These polyphase FIR interpolation filters have an x86 SSE/AVX path but no AArch64 equivalent, falling back to scalar C. The inner loop computes two dot products per output pair. Precomputing a reversed LFE sample vector before the inner loop avoids per-iteration shuffle overhead. Benchmarks on AWS Graviton3 (Neoverse V1, c7g.xlarge): lfe_fir0_float: C 5857.9 cycles -> NEON 2183.4 cycles (2.68x) lfe_fir1_float: C 2749.1 cycles -> NEON 1689.7 cycles (1.63x) Signed-off-by: Jeongkeun Kim <[email protected]> --- libavcodec/aarch64/Makefile | 6 +- libavcodec/aarch64/dcadsp_init_aarch64.c | 42 +++++++ libavcodec/aarch64/dcadsp_neon.S | 134 +++++++++++++++++++++++ libavcodec/dcadsp.c | 4 +- libavcodec/dcadsp.h | 1 + 5 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 libavcodec/aarch64/dcadsp_init_aarch64.c create mode 100644 libavcodec/aarch64/dcadsp_neon.S diff --git a/libavcodec/aarch64/Makefile b/libavcodec/aarch64/Makefile index f9c52699ab..24888ee421 100644 --- a/libavcodec/aarch64/Makefile +++ b/libavcodec/aarch64/Makefile @@ -23,7 +23,8 @@ OBJS-$(CONFIG_VP8DSP) += aarch64/vp8dsp_init_aarch64.o OBJS-$(CONFIG_AAC_DECODER) += aarch64/aacpsdsp_init_aarch64.o \ aarch64/sbrdsp_init_aarch64.o OBJS-$(CONFIG_AAC_ENCODER) += aarch64/aacencdsp_init.o -OBJS-$(CONFIG_DCA_DECODER) += aarch64/synth_filter_init.o +OBJS-$(CONFIG_DCA_DECODER) += aarch64/dcadsp_init_aarch64.o \ + aarch64/synth_filter_init.o OBJS-$(CONFIG_OPUS_DECODER) += aarch64/opusdsp_init.o OBJS-$(CONFIG_RV40_DECODER) += aarch64/rv40dsp_init_aarch64.o OBJS-$(CONFIG_VC1DSP) += aarch64/vc1dsp_init_aarch64.o @@ -67,7 +68,8 @@ NEON-OBJS-$(CONFIG_VP8DSP) += aarch64/vp8dsp_neon.o # decoders/encoders NEON-OBJS-$(CONFIG_AAC_DECODER) += aarch64/aacpsdsp_neon.o -NEON-OBJS-$(CONFIG_DCA_DECODER) += aarch64/synth_filter_neon.o +NEON-OBJS-$(CONFIG_DCA_DECODER) += aarch64/dcadsp_neon.o \ + aarch64/synth_filter_neon.o NEON-OBJS-$(CONFIG_OPUS_DECODER) += aarch64/opusdsp_neon.o NEON-OBJS-$(CONFIG_VORBIS_DECODER) += aarch64/vorbisdsp_neon.o NEON-OBJS-$(CONFIG_VP9_DECODER) += aarch64/vp9itxfm_16bpp_neon.o \ diff --git a/libavcodec/aarch64/dcadsp_init_aarch64.c b/libavcodec/aarch64/dcadsp_init_aarch64.c new file mode 100644 index 0000000000..1fc6ec7a32 --- /dev/null +++ b/libavcodec/aarch64/dcadsp_init_aarch64.c @@ -0,0 +1,42 @@ +/* + * AArch64 NEON optimised DCA DSP functions + * Copyright (c) 2026 Jeongkeun Kim <[email protected]> + * + * 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/aarch64/cpu.h" +#include "libavcodec/dcadsp.h" + +void ff_lfe_fir0_float_neon(float *pcm_samples, const int32_t *lfe_samples, + const float *filter_coeff, ptrdiff_t npcmblocks); +void ff_lfe_fir1_float_neon(float *pcm_samples, const int32_t *lfe_samples, + const float *filter_coeff, ptrdiff_t npcmblocks); + +av_cold void ff_dcadsp_init_aarch64(DCADSPContext *s) +{ + int cpu_flags = av_get_cpu_flags(); + + if (have_neon(cpu_flags)) { + s->lfe_fir_float[0] = ff_lfe_fir0_float_neon; + s->lfe_fir_float[1] = ff_lfe_fir1_float_neon; + } +} diff --git a/libavcodec/aarch64/dcadsp_neon.S b/libavcodec/aarch64/dcadsp_neon.S new file mode 100644 index 0000000000..b735b565ec --- /dev/null +++ b/libavcodec/aarch64/dcadsp_neon.S @@ -0,0 +1,134 @@ +/* + * AArch64 NEON optimised DCA LFE FIR filter functions + * Copyright (c) 2026 Jeongkeun Kim <[email protected]> + * + * 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 "libavutil/aarch64/asm.S" + +// void ff_lfe_fir0_float_neon(float *pcm_samples, const int32_t *lfe_samples, +// const float *filter_coeff, ptrdiff_t npcmblocks) +// +// factor=64, ncoeffs=8, nlfesamples=npcmblocks/2 +// lfe_samples points to lfe[0]; taps are lfe[-7..0]. +// +// a[j] = dot(coeff[j*8..j*8+7], [lfe[0],lfe[-1],...,lfe[-7]]) j=0..31 +// b[j] = dot(coeff[248-j*8..255-j*8], [lfe[-7],...,lfe[0]]) j=0..31 +// +// Precompute two LFE float vectors: +// v6 = [lfe[0], lfe[-1], lfe[-2], lfe[-3]] (a: hi half, rev of raw load) +// v7 = [lfe[-4],lfe[-5], lfe[-6], lfe[-7]] (a: lo half, rev of raw load) +// v4 = [lfe[-7],lfe[-6], lfe[-5], lfe[-4]] (b: lo half, raw load) +// v5 = [lfe[-3],lfe[-2], lfe[-1], lfe[0]] (b: hi half, raw load) +function ff_lfe_fir0_float_neon, export=1 + lsr x3, x3, #1 // nlfesamples = npcmblocks/2 + sub x1, x1, #28 // x1 -> &lfe[-7] +.Louter0: + ld1 {v4.4s, v5.4s}, [x1] // v4=[lfe[-7..-4]], v5=[lfe[-3..0]] (int32) + scvtf v4.4s, v4.4s + scvtf v5.4s, v5.4s + + ext v6.16b, v5.16b, v5.16b, #8 + rev64 v6.4s, v6.4s // v6 = [lfe[0], lfe[-1], lfe[-2], lfe[-3]] + ext v7.16b, v4.16b, v4.16b, #8 + rev64 v7.4s, v7.4s // v7 = [lfe[-4],lfe[-5], lfe[-6], lfe[-7]] + + mov x4, x2 // fwd coeff ptr: coeff[0], +32 per j + add x5, x2, #(248*4) // bwd coeff ptr: coeff[248], -32 per j + mov x6, x0 // output a: samples[0..31] + add x7, x0, #(32*4) // output b: samples[32..63] + mov w8, #32 +.Linner0: + // a[j] = dot(coeff[j*8..j*8+7], [v6, v7]) + ld1 {v0.4s, v1.4s}, [x4], #32 + fmul v2.4s, v0.4s, v6.4s + fmla v2.4s, v1.4s, v7.4s + faddp v2.4s, v2.4s, v2.4s + faddp v2.4s, v2.4s, v2.4s + str s2, [x6], #4 + + // b[j] = dot(coeff[248-j*8..255-j*8], [v4, v5]) + ld1 {v0.4s, v1.4s}, [x5] + fmul v2.4s, v0.4s, v4.4s + fmla v2.4s, v1.4s, v5.4s + faddp v2.4s, v2.4s, v2.4s + faddp v2.4s, v2.4s, v2.4s + str s2, [x7], #4 + sub x5, x5, #32 + + subs w8, w8, #1 + b.gt .Linner0 + + add x1, x1, #4 // lfe++ + add x0, x0, #(64*4) // samples += 64 floats + subs x3, x3, #1 + b.gt .Louter0 + ret +endfunc + +// void ff_lfe_fir1_float_neon(float *pcm_samples, const int32_t *lfe_samples, +// const float *filter_coeff, ptrdiff_t npcmblocks) +// +// factor=128, ncoeffs=4, nlfesamples=npcmblocks/4 +// lfe_samples points to lfe[0]; taps are lfe[-3..0]. +// +// a[j] = dot(coeff[j*4..j*4+3], [lfe[0],lfe[-1],lfe[-2],lfe[-3]]) j=0..63 +// b[j] = dot(coeff[252-j*4..255-j*4], [lfe[-3],lfe[-2],lfe[-1],lfe[0]]) j=0..63 +// +// v4 = [lfe[-3],lfe[-2],lfe[-1],lfe[0]] (b: raw load) +// v5 = [lfe[0], lfe[-1],lfe[-2],lfe[-3]] (a: rev(v4)) +function ff_lfe_fir1_float_neon, export=1 + lsr x3, x3, #2 // nlfesamples = npcmblocks/4 + sub x1, x1, #12 // x1 -> &lfe[-3] +.Louter1: + ld1 {v4.4s}, [x1] // v4 = [lfe[-3],lfe[-2],lfe[-1],lfe[0]] (int32) + scvtf v4.4s, v4.4s + + ext v5.16b, v4.16b, v4.16b, #8 + rev64 v5.4s, v5.4s // v5 = [lfe[0],lfe[-1],lfe[-2],lfe[-3]] + + mov x4, x2 // fwd coeff ptr: coeff[0], +16 per j + add x5, x2, #(252*4) // bwd coeff ptr: coeff[252], -16 per j + mov x6, x0 // output a: samples[0..63] + add x7, x0, #(64*4) // output b: samples[64..127] + mov w8, #64 +.Linner1: + // a[j] = dot(coeff[j*4..j*4+3], v5) + ld1 {v0.4s}, [x4], #16 + fmul v2.4s, v0.4s, v5.4s + faddp v2.4s, v2.4s, v2.4s + faddp v2.4s, v2.4s, v2.4s + str s2, [x6], #4 + + // b[j] = dot(coeff[252-j*4..255-j*4], v4) + ld1 {v0.4s}, [x5] + fmul v2.4s, v0.4s, v4.4s + faddp v2.4s, v2.4s, v2.4s + faddp v2.4s, v2.4s, v2.4s + str s2, [x7], #4 + sub x5, x5, #16 + + subs w8, w8, #1 + b.gt .Linner1 + + add x1, x1, #4 // lfe++ + add x0, x0, #(128*4) // samples += 128 floats + subs x3, x3, #1 + b.gt .Louter1 + ret +endfunc diff --git a/libavcodec/dcadsp.c b/libavcodec/dcadsp.c index a0be676699..553ce620ad 100644 --- a/libavcodec/dcadsp.c +++ b/libavcodec/dcadsp.c @@ -487,7 +487,9 @@ av_cold void ff_dcadsp_init(DCADSPContext *s) s->lbr_bank = lbr_bank_c; s->lfe_iir = lfe_iir_c; -#if ARCH_X86 && HAVE_X86ASM +#if ARCH_AARCH64 + ff_dcadsp_init_aarch64(s); +#elif ARCH_X86 && HAVE_X86ASM ff_dcadsp_init_x86(s); #endif } diff --git a/libavcodec/dcadsp.h b/libavcodec/dcadsp.h index b99ec55619..5d59e5aec1 100644 --- a/libavcodec/dcadsp.h +++ b/libavcodec/dcadsp.h @@ -95,6 +95,7 @@ typedef struct DCADSPContext { } DCADSPContext; av_cold void ff_dcadsp_init(DCADSPContext *s); +av_cold void ff_dcadsp_init_aarch64(DCADSPContext *s); av_cold void ff_dcadsp_init_x86(DCADSPContext *s); #endif -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
