Commit ff22978529 for ffmpeg

commit ff22978529e6219f65fbc5984cda2f0ee7ce5427
Author: DROOdotFOO <drew@axol.io>
Date:   Thu Jun 11 01:23:38 2026 +0200

    avfilter/aarch64: NEON threshold filter

    depth==8 routes to threshold8_neon (16 bytes/chunk); depth > 8 to
    threshold16_neon (8 shorts/chunk, covering 9/10/12/14/16). C is not
    auto-vectorized: four independent input streams defeat clang's
    vectorizer. Per chunk the NEON kernel is cmhs+bsl.

    checkasm --bench, Apple M1 (clang), speedup vs C:

    threshold8    15.0x
    threshold10    8.1x
    threshold12    8.2x
    threshold16    8.2x

    Signed-off-by: DROOdotFOO <drew@axol.io>

diff --git a/libavfilter/aarch64/Makefile b/libavfilter/aarch64/Makefile
index c7b7e18467..d09f7e4080 100644
--- a/libavfilter/aarch64/Makefile
+++ b/libavfilter/aarch64/Makefile
@@ -1,7 +1,9 @@
 OBJS-$(CONFIG_BWDIF_FILTER)                  += aarch64/vf_bwdif_init_aarch64.o
 OBJS-$(CONFIG_COLORDETECT_FILTER)            += aarch64/vf_colordetect_init.o
 OBJS-$(CONFIG_NLMEANS_FILTER)                += aarch64/vf_nlmeans_init.o
+OBJS-$(CONFIG_THRESHOLD_FILTER)              += aarch64/vf_threshold_init.o

 NEON-OBJS-$(CONFIG_BWDIF_FILTER)             += aarch64/vf_bwdif_neon.o
 NEON-OBJS-$(CONFIG_COLORDETECT_FILTER)       += aarch64/vf_colordetect_neon.o
 NEON-OBJS-$(CONFIG_NLMEANS_FILTER)           += aarch64/vf_nlmeans_neon.o
+NEON-OBJS-$(CONFIG_THRESHOLD_FILTER)         += aarch64/vf_threshold_neon.o
diff --git a/libavfilter/aarch64/vf_threshold_init.c b/libavfilter/aarch64/vf_threshold_init.c
new file mode 100644
index 0000000000..9b14d644a9
--- /dev/null
+++ b/libavfilter/aarch64/vf_threshold_init.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2026 DROOdotFOO <drew@axol.io>
+ *
+ * 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/attributes.h"
+#include "libavutil/aarch64/cpu.h"
+#include "libavfilter/threshold.h"
+
+void ff_threshold8_neon(const uint8_t *in, const uint8_t *threshold,
+                        const uint8_t *min, const uint8_t *max,
+                        uint8_t *out,
+                        ptrdiff_t ilinesize, ptrdiff_t tlinesize,
+                        ptrdiff_t flinesize, ptrdiff_t slinesize,
+                        ptrdiff_t olinesize,
+                        int w, int h);
+
+void ff_threshold16_neon(const uint8_t *in, const uint8_t *threshold,
+                         const uint8_t *min, const uint8_t *max,
+                         uint8_t *out,
+                         ptrdiff_t ilinesize, ptrdiff_t tlinesize,
+                         ptrdiff_t flinesize, ptrdiff_t slinesize,
+                         ptrdiff_t olinesize,
+                         int w, int h);
+
+av_cold void ff_threshold_init_aarch64(ThresholdContext *s)
+{
+    int cpu_flags = av_get_cpu_flags();
+
+    if (have_neon(cpu_flags)) {
+        if (s->depth == 8)
+            s->threshold = ff_threshold8_neon;
+        else
+            s->threshold = ff_threshold16_neon;
+    }
+}
diff --git a/libavfilter/aarch64/vf_threshold_neon.S b/libavfilter/aarch64/vf_threshold_neon.S
new file mode 100644
index 0000000000..415f3974b9
--- /dev/null
+++ b/libavfilter/aarch64/vf_threshold_neon.S
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2026 DROOdotFOO <drew@axol.io>
+ *
+ * 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"
+
+// Apple packs ints to 4 bytes on the stack; AAPCS-64 pads to 8.
+#ifdef __APPLE__
+.set    SP_INT, 4
+#else
+.set    SP_INT, 8
+#endif
+
+.macro THRESHOLD bits, vecsz, shape, ldop, stop, step
+function ff_threshold\bits\()_neon, export=1
+        stp             x19, x20, [sp, #-16]!
+        ldp             x8,  x9,  [sp, #16]         // slinesize, olinesize
+        ldr             w10, [sp, #32]              // w
+        ldr             w11, [sp, #32+SP_INT]       // h
+        cmp             w11, #0
+        b.le            9f
+        cmp             w10, #0
+        b.le            9f
+1:
+        mov             x12, x0
+        mov             x13, x1
+        mov             x14, x2
+        mov             x15, x3
+        mov             x16, x4
+        mov             w17, w10
+2:
+        cmp             w17, #(2*\vecsz)
+        b.lt            3f
+        ld1             {v0.16b, v1.16b}, [x12], #32
+        ld1             {v2.16b, v3.16b}, [x13], #32
+        ld1             {v4.16b, v5.16b}, [x14], #32
+        ld1             {v6.16b, v7.16b}, [x15], #32
+        cmhs            v16.\shape, v2.\shape, v0.\shape  // thr >= in
+        cmhs            v17.\shape, v3.\shape, v1.\shape
+        bsl             v16.16b, v4.16b, v6.16b           // mask ? min : max
+        bsl             v17.16b, v5.16b, v7.16b
+        st1             {v16.16b, v17.16b}, [x16], #32
+        sub             w17, w17, #(2*\vecsz)
+        b               2b
+3:
+        cmp             w17, #\vecsz
+        b.lt            4f
+        ld1             {v0.16b}, [x12], #16
+        ld1             {v1.16b}, [x13], #16
+        ld1             {v2.16b}, [x14], #16
+        ld1             {v3.16b}, [x15], #16
+        cmhs            v4.\shape, v1.\shape, v0.\shape   // thr >= in
+        bsl             v4.16b, v2.16b, v3.16b            // mask ? min : max
+        st1             {v4.16b}, [x16], #16
+        sub             w17, w17, #\vecsz
+4:
+        cbz             w17, 6f
+5:
+        \ldop           w19, [x12], #\step
+        \ldop           w20, [x13], #\step
+        cmp             w19, w20
+        \ldop           w19, [x14], #\step
+        \ldop           w20, [x15], #\step
+        csel            w19, w19, w20, ls
+        \stop           w19, [x16], #\step
+        subs            w17, w17, #1
+        b.gt            5b
+6:
+        add             x0, x0, x5
+        add             x1, x1, x6
+        add             x2, x2, x7
+        add             x3, x3, x8
+        add             x4, x4, x9
+        subs            w11, w11, #1
+        b.gt            1b
+9:
+        ldp             x19, x20, [sp], #16
+        ret
+endfunc
+.endm
+
+THRESHOLD 8,  16, 16b, ldrb, strb, 1
+THRESHOLD 16, 8,  8h,  ldrh, strh, 2
diff --git a/libavfilter/threshold.h b/libavfilter/threshold.h
index 8b55ad6ba1..1efdc9d5e9 100644
--- a/libavfilter/threshold.h
+++ b/libavfilter/threshold.h
@@ -47,5 +47,6 @@ typedef struct ThresholdContext {
 } ThresholdContext;

 void ff_threshold_init_x86(ThresholdContext *s);
+void ff_threshold_init_aarch64(ThresholdContext *s);

 #endif /* AVFILTER_THRESHOLD_H */
diff --git a/libavfilter/vf_threshold_init.h b/libavfilter/vf_threshold_init.h
index fb319c6cf8..87e2ef20f9 100644
--- a/libavfilter/vf_threshold_init.h
+++ b/libavfilter/vf_threshold_init.h
@@ -86,6 +86,8 @@ av_unused static void ff_threshold_init(ThresholdContext *s)

 #if ARCH_X86 && HAVE_X86ASM
     ff_threshold_init_x86(s);
+#elif ARCH_AARCH64
+    ff_threshold_init_aarch64(s);
 #endif
 }