PR #23633 opened by damitha URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23633 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23633.patch
Add neon .S implemention of vf_overlay. # Summary of changes Briefly describe what this PR does and why. NEON: - vf_overlay.overlay_row_44 [OK] - vf_overlay.overlay_row_20 [OK] overlay_row_44_c: 64.8 (1.00x) overlay_row_44_neon: 29.1 (2.22x) overlay_row_20_c: 96.9 (1.00x) overlay_row_20_neon: 47.4 (2.05x) <!-- If this PR requires new FATE test samples, attach them to the PR and list their target paths below (relative to the fate-suite root). Attached filenames must match the sample's filename: ```fate-samples # e.g. vorbis/new-sample.ogg ``` --> >From f952a2697d26190a44194811c531a597867f2f1a Mon Sep 17 00:00:00 2001 From: Damitha Gunawardena <[email protected]> Date: Mon, 29 Jun 2026 12:49:20 +1000 Subject: [PATCH] avfilter/vf_overlay: add aarch64 NEON blend_row (yuv420) Add neon .S implemention of vf_overlay. --- libavfilter/aarch64/Makefile | 2 + libavfilter/aarch64/vf_overlay_init.c | 51 ++++++++ libavfilter/aarch64/vf_overlay_neon.S | 96 ++++++++++++++ libavfilter/vf_overlay.c | 3 + libavfilter/vf_overlay.h | 1 + tests/checkasm/Makefile | 1 + tests/checkasm/checkasm.c | 3 + tests/checkasm/checkasm.h | 1 + tests/checkasm/vf_overlay.c | 172 ++++++++++++++++++++++++++ 9 files changed, 330 insertions(+) create mode 100644 libavfilter/aarch64/vf_overlay_init.c create mode 100644 libavfilter/aarch64/vf_overlay_neon.S create mode 100644 tests/checkasm/vf_overlay.c diff --git a/libavfilter/aarch64/Makefile b/libavfilter/aarch64/Makefile index c7b7e18467..f93c46d242 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_OVERLAY_FILTER) += aarch64/vf_overlay_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_OVERLAY_FILTER) += aarch64/vf_overlay_neon.o diff --git a/libavfilter/aarch64/vf_overlay_init.c b/libavfilter/aarch64/vf_overlay_init.c new file mode 100644 index 0000000000..1e7fb14fcc --- /dev/null +++ b/libavfilter/aarch64/vf_overlay_init.c @@ -0,0 +1,51 @@ +/* + * aarch64 NEON optimizations for the overlay filter (dispatch). + * + * 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, 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 <stddef.h> +#include <stdint.h> +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/aarch64/cpu.h" +#include "libavutil/log.h" +#include "libavfilter/vf_overlay.h" + +int ff_overlay_row_44_neon(uint8_t *d, uint8_t *da, uint8_t *s, uint8_t *a, + int w, ptrdiff_t alinesize); +int ff_overlay_row_20_neon(uint8_t *d, uint8_t *da, uint8_t *s, uint8_t *a, + int w, ptrdiff_t alinesize); + +av_cold void ff_overlay_init_aarch64(AVFilterContext *ctx) +{ + OverlayContext *s = ctx->priv; + const AVFilterLink *main = ctx->inputs[0]; + const AVFilterLink *overlay = ctx->inputs[1]; + int cpu_flags = av_get_cpu_flags(); + + if (have_neon(cpu_flags) && + main->format == AV_PIX_FMT_YUV420P && + s->format == OVERLAY_FORMAT_YUV420 && + overlay->alpha_mode != AVALPHA_MODE_PREMULTIPLIED && + !s->main_has_alpha) { + s->blend_row[0] = ff_overlay_row_44_neon; + s->blend_row[1] = ff_overlay_row_20_neon; + s->blend_row[2] = ff_overlay_row_20_neon; + av_log(ctx, AV_LOG_VERBOSE, "overlay: aarch64 NEON blend_row enabled (yuv420).\n"); + } +} diff --git a/libavfilter/aarch64/vf_overlay_neon.S b/libavfilter/aarch64/vf_overlay_neon.S new file mode 100644 index 0000000000..a488ffa6f6 --- /dev/null +++ b/libavfilter/aarch64/vf_overlay_neon.S @@ -0,0 +1,96 @@ +/* + * aarch64 NEON overlay blend kernels. + * + * 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" + +/* d = FAST_DIV255(d*(255-a) + s*a), FAST_DIV255(x) = ((x+128)*257)>>16 + * uses umull/umlal straight from bytes and uzp2 for the >>16. + * + * int ff_overlay_row_44_neon(uint8_t *d, uint8_t *da, uint8_t *s, + * uint8_t *a, int w, ptrdiff_t alinesize) + * x0=d x1=da(unused) x2=s x3=a w4=w x5=alinesize(unused) + */ +function ff_overlay_row_44_neon, export=1 + mov w6, #0 + cmp w4, #8 + b.lt 2f + movi v6.8b, #0xff // 255 + movi v5.8h, #0x80 // 128 + mov w8, #257 + dup v7.8h, w8 + sub w7, w4, #8 // process while x <= w-8 +1: ldr d0, [x0, x6] // d + ldr d1, [x2, x6] // s + ldr d2, [x3, x6] // a + sub v3.8b, v6.8b, v2.8b // 255 - a + umull v4.8h, v0.8b, v3.8b // d*(255-a) + umlal v4.8h, v1.8b, v2.8b // + s*a + add v4.8h, v4.8h, v5.8h // + 128 + umull v16.4s, v4.4h, v7.h[0] // *257 (lo) + umull2 v17.4s, v4.8h, v7.h[0] // *257 (hi) + uzp2 v4.8h, v16.8h, v17.8h // high 16 of each = >>16 + uqxtn v4.8b, v4.8h + str d4, [x0, x6] + add x6, x6, #8 + cmp w6, w7 + b.le 1b +2: mov w0, w6 + ret +endfunc + +/* chroma 4:2:0: average a 2x2 alpha block per pixel; leave the right-edge + * column to the scalar remainder loop (process while x+8 < w). + * Same args; x5 = alpha linesize (used for the 2nd alpha row). + */ +function ff_overlay_row_20_neon, export=1 + mov w6, #0 + cmp w4, #9 + b.lt 2f + movi v6.8b, #0xff + movi v5.8h, #0x80 + mov w8, #257 + dup v7.8h, w8 + sub w7, w4, #9 +1: add x9, x3, x6, lsl #1 // a + 2*x (row0) + ld1 {v18.16b}, [x9] + add x10, x9, x5 // row1 (+alinesize) + ld1 {v19.16b}, [x10] + uaddlp v18.8h, v18.16b // a[2i]+a[2i+1] + uaddlp v19.8h, v19.16b + add v20.8h, v18.8h, v19.8h // 2x2 sum + ushr v20.8h, v20.8h, #2 // /4 = avg alpha + xtn v2.8b, v20.8h // back to bytes + ldr d0, [x0, x6] // d + ldr d1, [x2, x6] // s + sub v3.8b, v6.8b, v2.8b + umull v4.8h, v0.8b, v3.8b + umlal v4.8h, v1.8b, v2.8b + add v4.8h, v4.8h, v5.8h + umull v16.4s, v4.4h, v7.h[0] + umull2 v17.4s, v4.8h, v7.h[0] + uzp2 v4.8h, v16.8h, v17.8h + uqxtn v4.8b, v4.8h + str d4, [x0, x6] + add x6, x6, #8 + cmp w6, w7 + b.le 1b +2: mov w0, w6 + ret +endfunc diff --git a/libavfilter/vf_overlay.c b/libavfilter/vf_overlay.c index d627db0889..f56e380b82 100644 --- a/libavfilter/vf_overlay.c +++ b/libavfilter/vf_overlay.c @@ -835,6 +835,9 @@ static int init_slice_fn(AVFilterContext *ctx) break; } +#if ARCH_AARCH64 && HAVE_NEON + ff_overlay_init_aarch64(ctx); +#endif #if ARCH_X86 && HAVE_X86ASM ff_overlay_init_x86(ctx); #endif diff --git a/libavfilter/vf_overlay.h b/libavfilter/vf_overlay.h index 22e84362b7..44931b46fd 100644 --- a/libavfilter/vf_overlay.h +++ b/libavfilter/vf_overlay.h @@ -83,5 +83,6 @@ typedef struct OverlayContext { } OverlayContext; void ff_overlay_init_x86(AVFilterContext *ctx); +void ff_overlay_init_aarch64(AVFilterContext *ctx); #endif /* AVFILTER_OVERLAY_H */ diff --git a/tests/checkasm/Makefile b/tests/checkasm/Makefile index bf2d6b7ec2..d7dc00ee32 100644 --- a/tests/checkasm/Makefile +++ b/tests/checkasm/Makefile @@ -77,6 +77,7 @@ AVFILTEROBJS-$(CONFIG_HFLIP_FILTER) += vf_hflip.o AVFILTEROBJS-$(CONFIG_IDET_FILTER) += vf_idet.o AVFILTEROBJS-$(CONFIG_THRESHOLD_FILTER) += vf_threshold.o AVFILTEROBJS-$(CONFIG_NLMEANS_FILTER) += vf_nlmeans.o +AVFILTEROBJS-$(CONFIG_OVERLAY_FILTER) += vf_overlay.o AVFILTEROBJS-$(CONFIG_SOBEL_FILTER) += vf_convolution.o CHECKASMOBJS-$(CONFIG_AVFILTER) += $(AVFILTEROBJS-yes) diff --git a/tests/checkasm/checkasm.c b/tests/checkasm/checkasm.c index 8629f26788..e15e459532 100644 --- a/tests/checkasm/checkasm.c +++ b/tests/checkasm/checkasm.c @@ -339,6 +339,9 @@ static const struct { #if CONFIG_NLMEANS_FILTER { "vf_nlmeans", checkasm_check_nlmeans }, #endif + #if CONFIG_OVERLAY_FILTER + { "vf_overlay", checkasm_check_vf_overlay }, + #endif #if CONFIG_THRESHOLD_FILTER { "vf_threshold", checkasm_check_vf_threshold }, #endif diff --git a/tests/checkasm/checkasm.h b/tests/checkasm/checkasm.h index 43c158e97b..dc770bfa91 100644 --- a/tests/checkasm/checkasm.h +++ b/tests/checkasm/checkasm.h @@ -132,6 +132,7 @@ void checkasm_check_mpegvideo_unquantize(void); void checkasm_check_mpegvideoencdsp(void); void checkasm_check_nlmeans(void); void checkasm_check_opusdsp(void); +void checkasm_check_vf_overlay(void); void checkasm_check_pixblockdsp(void); void checkasm_check_pixelutils(void); void checkasm_check_png(void); diff --git a/tests/checkasm/vf_overlay.c b/tests/checkasm/vf_overlay.c new file mode 100644 index 0000000000..7c6f699c4e --- /dev/null +++ b/tests/checkasm/vf_overlay.c @@ -0,0 +1,172 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 <string.h> +#include "checkasm.h" +#include "libavutil/cpu.h" +#include "libavutil/mem_internal.h" + +#if ARCH_AARCH64 +#include "libavutil/aarch64/cpu.h" +#endif +#if ARCH_X86 +#include "libavutil/x86/cpu.h" +#endif + +/* blend_row signature, see OverlayContext::blend_row in libavfilter/vf_overlay.h */ +typedef int (*blend_row)(uint8_t *d, uint8_t *da, uint8_t *s, uint8_t *a, + int w, ptrdiff_t alinesize); + +#if ARCH_AARCH64 +int ff_overlay_row_44_neon(uint8_t *d, uint8_t *da, uint8_t *s, uint8_t *a, + int w, ptrdiff_t alinesize); +int ff_overlay_row_20_neon(uint8_t *d, uint8_t *da, uint8_t *s, uint8_t *a, + int w, ptrdiff_t alinesize); +#endif +#if ARCH_X86 && HAVE_X86ASM +int ff_overlay_row_44_sse4(uint8_t *d, uint8_t *da, uint8_t *s, uint8_t *a, + int w, ptrdiff_t alinesize); +int ff_overlay_row_20_sse4(uint8_t *d, uint8_t *da, uint8_t *s, uint8_t *a, + int w, ptrdiff_t alinesize); +#endif + +#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16) + +#define WIDTH 359 +#define PAD 32 +/* two alpha samples per output pixel, two rows, plus slack for the SIMD overread */ +#define ASTRIDE (2 * WIDTH + PAD) + +/* straight-alpha blend, one alpha sample per pixel (Y / 4:4:4 plane) */ +static int ref_overlay_row_44(uint8_t *d, uint8_t *da, uint8_t *s, uint8_t *a, + int w, ptrdiff_t alinesize) +{ + for (int x = 0; x < w; x++) + d[x] = FAST_DIV255(d[x] * (255 - a[x]) + s[x] * a[x]); + return w; +} + +/* straight-alpha blend, 2x2 averaged alpha per pixel (4:2:0 chroma plane) */ +static int ref_overlay_row_20(uint8_t *d, uint8_t *da, uint8_t *s, uint8_t *a, + int w, ptrdiff_t alinesize) +{ + for (int x = 0; x < w; x++) { + int alpha = (a[2 * x] + a[2 * x + 1] + + a[2 * x + alinesize] + a[2 * x + 1 + alinesize]) >> 2; + d[x] = FAST_DIV255(d[x] * (255 - alpha) + s[x] * alpha); + } + return w; +} + +static blend_row get_row_44(void) +{ + int flags = av_get_cpu_flags(); +#if ARCH_AARCH64 + if (have_neon(flags)) + return ff_overlay_row_44_neon; +#elif ARCH_X86 && HAVE_X86ASM + if (EXTERNAL_SSE4(flags)) + return ff_overlay_row_44_sse4; +#endif + return ref_overlay_row_44; +} + +static blend_row get_row_20(void) +{ + int flags = av_get_cpu_flags(); +#if ARCH_AARCH64 + if (have_neon(flags)) + return ff_overlay_row_20_neon; +#elif ARCH_X86 && HAVE_X86ASM + if (EXTERNAL_SSE4(flags)) + return ff_overlay_row_20_sse4; +#endif + return ref_overlay_row_20; +} + +static void check_row_44(void) +{ + LOCAL_ALIGNED_32(uint8_t, d0, [WIDTH + PAD]); + LOCAL_ALIGNED_32(uint8_t, src, [WIDTH + PAD]); + LOCAL_ALIGNED_32(uint8_t, alpha, [WIDTH + PAD]); + LOCAL_ALIGNED_32(uint8_t, dst_ref, [WIDTH + PAD]); + LOCAL_ALIGNED_32(uint8_t, dst_new, [WIDTH + PAD]); + + declare_func(int, uint8_t *, uint8_t *, uint8_t *, uint8_t *, int, ptrdiff_t); + + for (int i = 0; i < WIDTH + PAD; i++) { + d0[i] = rnd() & 0xff; + src[i] = rnd() & 0xff; + alpha[i] = rnd() & 0xff; + } + + if (check_func(get_row_44(), "overlay_row_44")) { + for (int w = 1; w <= WIDTH; w++) { + int cref, cnew; + memcpy(dst_ref, d0, WIDTH + PAD); + memcpy(dst_new, d0, WIDTH + PAD); + cref = call_ref(dst_ref, NULL, src, alpha, w, 0); + cnew = call_new(dst_new, NULL, src, alpha, w, 0); + if (cnew > cref || memcmp(dst_ref, dst_new, cnew)) + fail(); + } + memcpy(dst_new, d0, WIDTH + PAD); + bench_new(dst_new, NULL, src, alpha, WIDTH, 0); + } +} + +static void check_row_20(void) +{ + LOCAL_ALIGNED_32(uint8_t, d0, [WIDTH + PAD]); + LOCAL_ALIGNED_32(uint8_t, src, [WIDTH + PAD]); + LOCAL_ALIGNED_32(uint8_t, alpha, [2 * ASTRIDE]); + LOCAL_ALIGNED_32(uint8_t, dst_ref, [WIDTH + PAD]); + LOCAL_ALIGNED_32(uint8_t, dst_new, [WIDTH + PAD]); + + declare_func(int, uint8_t *, uint8_t *, uint8_t *, uint8_t *, int, ptrdiff_t); + + for (int i = 0; i < WIDTH + PAD; i++) { + d0[i] = rnd() & 0xff; + src[i] = rnd() & 0xff; + } + for (int i = 0; i < 2 * ASTRIDE; i++) + alpha[i] = rnd() & 0xff; + + if (check_func(get_row_20(), "overlay_row_20")) { + for (int w = 1; w <= WIDTH; w++) { + int cref, cnew; + memcpy(dst_ref, d0, WIDTH + PAD); + memcpy(dst_new, d0, WIDTH + PAD); + cref = call_ref(dst_ref, NULL, src, alpha, w, ASTRIDE); + cnew = call_new(dst_new, NULL, src, alpha, w, ASTRIDE); + if (cnew > cref || memcmp(dst_ref, dst_new, cnew)) + fail(); + } + memcpy(dst_new, d0, WIDTH + PAD); + bench_new(dst_new, NULL, src, alpha, WIDTH, ASTRIDE); + } +} + +void checkasm_check_vf_overlay(void) +{ + check_row_44(); + report("overlay_row_44"); + + check_row_20(); + report("overlay_row_20"); +} -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
