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]

Reply via email to