[PR] avcodec/aarch64: add NEON lfe_x96_fixed for DCA DSP (PR #23652)

kjg0724 via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178278733190.59.4155700800766045679@29965ddac10e>
PR #23652 opened by kjg0724
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23652
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23652.patch

Implement `ff_lfe_x96_fixed_neon`, an AArch64 NEON fixed-point LFE x96 interpolation filter.

Each input sample generates two output samples:

```
a = coeff_a * src[i] + coeff_b * prev
b = coeff_b * src[i] + coeff_a * prev
```

where `coeff_a = 2097471` and `coeff_b = 6291137` (approximately 0.25 and 0.75 in 2^23 fixed-point).

Two input samples are processed per iteration using SMULL/SMLAL for int32×int32 to int64 widening multiply-accumulate, SQRSHRN #23 for `norm23`, and SQSHL/SSHR #8 for `clip23`. ZIP1 interleaves the paired output values before storing.

Benchmarked with `make fate` checkasm on Apple M1:

```
lfe_x96_fixed_c:    ~190 cycles
lfe_x96_fixed_neon:  ~68 cycles  (~2.8x)
```

Also adds a checkasm test for `lfe_x96_fixed`.


From 9aac704b44058999747c6523caa7395dfa97845e Mon Sep 17 00:00:00 2001
From: Jeongkeun Kim <[email protected]>
Date: Tue, 28 Apr 2026 13:51:25 +0900
Subject: [PATCH 1/2] tests/checkasm/llviddsp: fix add_left_pred_int16 buffer
 compare size
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

dst0/dst1 are uint16_t* allocated as width * sizeof(uint16_t), but
the memcmp at the end of check_add_left_pred_16 only compared `width`
bytes — missing the second half of each buffer. Same pattern used
correctly in tests/checkasm/huffyuvdsp.c (memcmp with width * sizeof()).

While at it, fix missing whitespace around & and || on the same line.

Fixes: fbe91487797c ("checkasm/llviddsp : add test for other dsp func")
Signed-off-by: Jeongkeun Kim <[email protected]>
---
 tests/checkasm/llviddsp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/checkasm/llviddsp.c b/tests/checkasm/llviddsp.c
index a8245b0d94..1094638229 100644
--- a/tests/checkasm/llviddsp.c
+++ b/tests/checkasm/llviddsp.c
@@ -145,7 +145,7 @@ static void check_add_left_pred_16(LLVidDSPContext *c, unsigned mask, int width,
 
     res0 = call_ref(dst0, src0, mask, width, acc);
     res1 = call_new(dst1, src1, mask, width, acc);
-    if ((res0 &0xFFFF) != (res1 &0xFFFF)|| memcmp(dst0, dst1, width))
+    if ((res0 & 0xFFFF) != (res1 & 0xFFFF) || memcmp(dst0, dst1, width * sizeof(*dst0)))
         fail();
     bench_new(dst1, src1, mask, width, acc);
 
-- 
2.52.0


From b777d35ba7fbf87b39add21b90621a1bb13bdadc Mon Sep 17 00:00:00 2001
From: Jeongkeun Kim <[email protected]>
Date: Tue, 30 Jun 2026 11:31:05 +0900
Subject: [PATCH 2/2] avcodec/aarch64: add NEON lfe_x96_fixed for DCA DSP

Implement ff_lfe_x96_fixed_neon, an AArch64 NEON fixed-point LFE x96
interpolation filter. Each input sample generates two output samples:

    a = coeff_a * src[i] + coeff_b * prev
    b = coeff_b * src[i] + coeff_a * prev

where coeff_a = 2097471 and coeff_b = 6291137 (approximately 0.25
and 0.75 in 2^23 fixed-point).

Two input samples are processed per iteration using SMULL/SMLAL for
int32x32 to int64 widening multiply-accumulate, SQRSHRN #23 for
norm23, and SQSHL/SSHR #8 for clip23. ZIP1 interleaves the paired
output values before storing.

Measured on Apple M1:
    lfe_x96_fixed_c:    ~190 cycles
    lfe_x96_fixed_neon:  ~68 cycles  (~2.8x)

Also adds a checkasm test for lfe_x96_fixed.
---
 libavcodec/aarch64/dcadsp_init_aarch64.c |  3 +++
 libavcodec/aarch64/dcadsp_neon.S         | 31 ++++++++++++++++++++++++
 tests/checkasm/dcadsp.c                  | 23 ++++++++++++++++++
 3 files changed, 57 insertions(+)

diff --git a/libavcodec/aarch64/dcadsp_init_aarch64.c b/libavcodec/aarch64/dcadsp_init_aarch64.c
index 1fc6ec7a32..acb4194e9d 100644
--- a/libavcodec/aarch64/dcadsp_init_aarch64.c
+++ b/libavcodec/aarch64/dcadsp_init_aarch64.c
@@ -30,6 +30,8 @@ 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);
+void ff_lfe_x96_fixed_neon(int32_t *dst, const int32_t *src,
+                           int32_t *hist, ptrdiff_t len);
 
 av_cold void ff_dcadsp_init_aarch64(DCADSPContext *s)
 {
@@ -38,5 +40,6 @@ av_cold void ff_dcadsp_init_aarch64(DCADSPContext *s)
     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;
+        s->lfe_x96_fixed    = ff_lfe_x96_fixed_neon;
     }
 }
diff --git a/libavcodec/aarch64/dcadsp_neon.S b/libavcodec/aarch64/dcadsp_neon.S
index af149767fd..ae7ce7f906 100644
--- a/libavcodec/aarch64/dcadsp_neon.S
+++ b/libavcodec/aarch64/dcadsp_neon.S
@@ -99,3 +99,34 @@ function ff_lfe_fir1_float_neon, export=1
         b.gt            .Louter1
         ret
 endfunc
+
+function ff_lfe_x96_fixed_neon, export=1
+        ldr             w4, [x2]
+        mov             w9, #0x013F
+        movk            w9, #0x0020, lsl #16
+        dup             v0.2s, w9
+        mov             w9, #0xFEC1
+        movk            w9, #0x005F, lsl #16
+        dup             v1.2s, w9
+.Lloopx96d:
+        ld1             {v2.2s}, [x1], #8
+        dup             v3.2s, w4
+        ins             v3.s[1], v2.s[0]
+        smull           v4.2d, v2.2s, v0.2s
+        smlal           v4.2d, v3.2s, v1.2s
+        smull           v5.2d, v2.2s, v1.2s
+        smlal           v5.2d, v3.2s, v0.2s
+        sqrshrn         v16.2s, v4.2d, #23
+        sqrshrn         v17.2s, v5.2d, #23
+        sqshl           v16.2s, v16.2s, #8
+        sshr            v16.2s, v16.2s, #8
+        sqshl           v17.2s, v17.2s, #8
+        sshr            v17.2s, v17.2s, #8
+        zip1            v18.4s, v16.4s, v17.4s
+        st1             {v18.4s}, [x0], #16
+        umov            w4, v2.s[1]
+        subs            x3, x3, #2
+        b.gt            .Lloopx96d
+        str             w4, [x2]
+        ret
+endfunc
diff --git a/tests/checkasm/dcadsp.c b/tests/checkasm/dcadsp.c
index 809a0a0974..0879977208 100644
--- a/tests/checkasm/dcadsp.c
+++ b/tests/checkasm/dcadsp.c
@@ -86,6 +86,26 @@ static void test_lfe_fir_fixed(void)
     bench_new(dst1, lfe + LFE_HISTORY, coeffs, N);
 }
 
+static void test_lfe_x96_fixed(const DCADSPContext *dca)
+{
+    LOCAL_ALIGNED_16(int32_t, dst0, [2 * BUF_SIZE]);
+    LOCAL_ALIGNED_16(int32_t, dst1, [2 * BUF_SIZE]);
+    LOCAL_ALIGNED_16(int32_t, src,  [BUF_SIZE]);
+    int32_t hist0, hist1;
+
+    declare_func(void, int32_t *dst, const int32_t *src, int32_t *hist, ptrdiff_t len);
+
+    if (check_func(dca->lfe_x96_fixed, "lfe_x96_fixed")) {
+        randomize(src, BUF_SIZE);
+        hist0 = hist1 = (int16_t)rnd();
+        call_ref(dst0, src, &hist0, BUF_SIZE);
+        call_new(dst1, src, &hist1, BUF_SIZE);
+        if (memcmp(dst0, dst1, 2 * BUF_SIZE * sizeof(int32_t)))
+            fail();
+        bench_new(dst1, src, &hist1, BUF_SIZE);
+    }
+}
+
 void checkasm_check_dcadsp(void)
 {
     DCADSPContext dca;
@@ -98,4 +118,7 @@ void checkasm_check_dcadsp(void)
     if (check_func(dca.lfe_fir_fixed, "lfe_fir_fixed"))
         test_lfe_fir_fixed();
     report("lfe_fir_fixed");
+
+    test_lfe_x96_fixed(&dca);
+    report("lfe_x96_fixed");
 }
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.