PR #24501 opened by Zhao Zhili (quink)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24501
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24501.patch

# Summary of changes

1. avcodec/sao: fix swapped 45 and 135 degree EO class comments

2. add hevc and vvc sao edge aarch64 asm

```
                              Cortex-A510       Cortex-A715       Cortex-A725   
      Cortex-X3       Cortex-X925
hevc_sao_edge_8_10_neon:    332.5 (4.07x)      56.9 (5.50x)      57.4 (4.64x)   
   37.0 (7.38x)      24.2 (7.96x)
hevc_sao_edge_8_12_neon:    321.9 (4.22x)      57.1 (5.58x)      57.7 (4.70x)   
   37.0 (7.27x)      23.7 (8.12x)
hevc_sao_edge_16_10_neon:  1280.4 (4.11x)     227.3 (5.30x)     227.9 (4.61x)   
  146.4 (7.27x)      94.9 (8.38x)
hevc_sao_edge_16_12_neon:  1353.7 (3.96x)     227.9 (5.40x)     227.1 (4.63x)   
  146.4 (7.22x)      94.7 (8.40x)
hevc_sao_edge_32_10_neon:  5249.9 (3.84x)     912.9 (5.15x)     911.5 (4.48x)   
  592.8 (7.21x)     372.7 (8.95x)
hevc_sao_edge_32_12_neon:  5483.8 (3.67x)     912.6 (5.29x)     912.3 (4.47x)   
  579.8 (7.21x)     365.2 (9.11x)
hevc_sao_edge_48_10_neon: 11147.5 (4.07x)    2035.5 (5.14x)    2033.9 (4.48x)   
 1324.3 (7.27x)     815.7 (9.29x)
hevc_sao_edge_48_12_neon: 11179.6 (4.06x)    2035.5 (5.29x)    2035.9 (4.47x)   
 1324.9 (7.05x)     816.8 (9.25x)
hevc_sao_edge_64_10_neon: 19965.6 (4.03x)    3606.1 (5.15x)    3603.6 (4.49x)   
 2340.0 (7.27x)    1440.0 (9.42x)
hevc_sao_edge_64_12_neon: 20015.7 (4.02x)    3604.2 (5.30x)    3603.3 (4.49x)   
 2342.3 (7.07x)    1439.2 (9.40x)
```

```
                              Cortex-A510       Cortex-A715       Cortex-A725   
      Cortex-X3       Cortex-X925
vvc_sao_edge_8_10_neon:     308.9 (4.29x)      57.1 (5.47x)      57.4 (4.66x)   
   36.4 (7.35x)      23.7 (8.13x)
vvc_sao_edge_8_12_neon:     314.6 (4.19x)      57.3 (5.58x)      58.3 (4.59x)   
   36.4 (7.34x)      23.9 (8.08x)
vvc_sao_edge_16_10_neon:   1202.1 (4.28x)     228.1 (5.26x)     228.6 (4.54x)   
  144.6 (7.28x)      95.0 (8.39x)
vvc_sao_edge_16_12_neon:   1203.3 (4.28x)     229.4 (5.37x)     239.4 (4.33x)   
  144.6 (7.22x)      95.0 (8.38x)
vvc_sao_edge_32_10_neon:   4718.8 (4.28x)     913.1 (5.12x)     911.5 (4.42x)   
  571.5 (7.30x)     366.9 (9.08x)
vvc_sao_edge_32_12_neon:   4737.9 (4.26x)     913.3 (5.28x)     913.0 (4.41x)   
  571.8 (7.31x)     365.6 (9.15x)
vvc_sao_edge_48_10_neon:  10970.3 (4.16x)    2035.0 (5.13x)    2036.2 (4.43x)   
 1307.6 (7.14x)     823.6 (9.19x)
vvc_sao_edge_48_12_neon:  10980.7 (4.15x)    2037.3 (5.29x)    2035.0 (4.43x)   
 1311.2 (7.14x)     824.1 (9.20x)
vvc_sao_edge_64_10_neon:  19127.9 (4.23x)    3605.3 (5.13x)    3628.5 (4.41x)   
 2315.9 (7.17x)    1452.7 (9.33x)
vvc_sao_edge_64_12_neon:  19136.2 (4.23x)    3606.3 (5.29x)    3629.0 (4.40x)   
 2320.9 (7.17x)    1448.9 (9.36x)
vvc_sao_edge_80_10_neon:  29560.9 (4.29x)    5624.0 (5.30x)    5657.5 (4.64x)   
 3610.8 (7.17x)    2252.1 (9.44x)
vvc_sao_edge_80_12_neon:  29644.8 (4.28x)    5626.8 (5.48x)    5661.2 (4.64x)   
 3614.4 (7.18x)    2250.3 (9.46x)
vvc_sao_edge_96_10_neon:  42316.0 (4.29x)    8088.8 (5.28x)    8135.3 (4.61x)   
 5188.5 (7.20x)    3236.3 (9.49x)
vvc_sao_edge_96_12_neon:  42863.0 (4.23x)    8091.9 (5.45x)    8136.9 (4.61x)   
 5190.6 (7.21x)    3236.1 (9.48x)
vvc_sao_edge_112_10_neon: 57410.2 (4.30x)   11001.8 (5.27x)   11056.2 (4.59x)   
 7066.3 (7.19x)    4407.8 (9.48x)
vvc_sao_edge_112_12_neon: 57372.5 (4.30x)   11006.7 (5.44x)   11054.2 (4.59x)   
 7074.3 (7.19x)    4400.6 (9.52x)
vvc_sao_edge_128_10_neon: 75737.0 (4.26x)   14401.4 (5.26x)   14468.5 (4.57x)   
 9227.8 (7.22x)    5729.6 (9.54x)
vvc_sao_edge_128_12_neon: 75824.1 (4.25x)   14404.0 (5.42x)   14472.1 (4.57x)   
 9222.1 (7.20x)    5730.8 (9.55x)
```


>From cacbdad1ae8c1e85af640c9ee98a93b6fa1cc2cd Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Fri, 11 Sep 2026 21:57:11 +0800
Subject: [PATCH 1/3] avcodec/sao: fix swapped 45 and 135 degree EO class
 comments

The position tables are indexed by sao_eo_class: index 2 is SAO_EO_135D,
with neighbours at (-1,-1) and (1,1), and index 3 is SAO_EO_45D.

Signed-off-by: Zhao Zhili <[email protected]>
---
 libavcodec/aarch64/h26x/sao_neon.S   | 8 ++++----
 libavcodec/arm/hevcdsp_init_neon.c   | 4 ++--
 libavcodec/h26x/h2656_sao_template.c | 4 ++--
 libavcodec/wasm/hevc/sao.c           | 8 ++++----
 4 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libavcodec/aarch64/h26x/sao_neon.S 
b/libavcodec/aarch64/h26x/sao_neon.S
index 354614ecab..90022fcfc7 100644
--- a/libavcodec/aarch64/h26x/sao_neon.S
+++ b/libavcodec/aarch64/h26x/sao_neon.S
@@ -103,14 +103,14 @@ endfunc
 .Lhevc_sao_edge_pos:
 .word 1 // horizontal
 .word HEVC_SAO_STRIDE // vertical
-.word HEVC_SAO_STRIDE + 1 // 45 degree
-.word HEVC_SAO_STRIDE - 1 // 135 degree
+.word HEVC_SAO_STRIDE + 1 // 135 degree
+.word HEVC_SAO_STRIDE - 1 // 45 degree
 
 .Lvvc_sao_edge_pos:
 .word 1 // horizontal
 .word VVC_SAO_STRIDE // vertical
-.word VVC_SAO_STRIDE + 1 // 45 degree
-.word VVC_SAO_STRIDE - 1 // 135 degree
+.word VVC_SAO_STRIDE + 1 // 135 degree
+.word VVC_SAO_STRIDE - 1 // 45 degree
 
 function ff_vvc_sao_edge_filter_16x16_8_neon, export=1
         adr             x7, .Lvvc_sao_edge_pos
diff --git a/libavcodec/arm/hevcdsp_init_neon.c 
b/libavcodec/arm/hevcdsp_init_neon.c
index 6f113618c0..1bda997ceb 100644
--- a/libavcodec/arm/hevcdsp_init_neon.c
+++ b/libavcodec/arm/hevcdsp_init_neon.c
@@ -177,8 +177,8 @@ void ff_hevc_sao_edge_filter_neon_8_wrapper(uint8_t *_dst, 
const uint8_t *_src,
     static const int8_t pos[4][2][2] = {
         { { -1,  0 }, {  1, 0 } }, // horizontal
         { {  0, -1 }, {  0, 1 } }, // vertical
-        { { -1, -1 }, {  1, 1 } }, // 45 degree
-        { {  1, -1 }, { -1, 1 } }, // 135 degree
+        { { -1, -1 }, {  1, 1 } }, // 135 degree
+        { {  1, -1 }, { -1, 1 } }, // 45 degree
     };
     uint8_t *dst = _dst;
     const uint8_t *src = _src;
diff --git a/libavcodec/h26x/h2656_sao_template.c 
b/libavcodec/h26x/h2656_sao_template.c
index b3eb8a3deb..a5028315a7 100644
--- a/libavcodec/h26x/h2656_sao_template.c
+++ b/libavcodec/h26x/h2656_sao_template.c
@@ -54,8 +54,8 @@ static void FUNC(sao_edge_filter)(uint8_t *_dst, const 
uint8_t *_src, ptrdiff_t
     static const int8_t pos[4][2][2] = {
         { { -1,  0 }, {  1, 0 } }, // horizontal
         { {  0, -1 }, {  0, 1 } }, // vertical
-        { { -1, -1 }, {  1, 1 } }, // 45 degree
-        { {  1, -1 }, { -1, 1 } }, // 135 degree
+        { { -1, -1 }, {  1, 1 } }, // 135 degree
+        { {  1, -1 }, { -1, 1 } }, // 45 degree
     };
     pixel *dst = (pixel *)_dst;
     const pixel *src = (const pixel *)_src;
diff --git a/libavcodec/wasm/hevc/sao.c b/libavcodec/wasm/hevc/sao.c
index a863b8e720..89e6b2cf07 100644
--- a/libavcodec/wasm/hevc/sao.c
+++ b/libavcodec/wasm/hevc/sao.c
@@ -124,8 +124,8 @@ void ff_hevc_sao_edge_filter_8x8_8_simd128(uint8_t *dst, 
const uint8_t *src,
     static const int8_t pos[4][2][2] = {
             { { -1,  0 }, {  1, 0 } }, // horizontal
             { {  0, -1 }, {  0, 1 } }, // vertical
-            { { -1, -1 }, {  1, 1 } }, // 45 degree
-            { {  1, -1 }, { -1, 1 } }, // 135 degree
+            { { -1, -1 }, {  1, 1 } }, // 135 degree
+            { {  1, -1 }, { -1, 1 } }, // 45 degree
     };
     int a_stride, b_stride;
     ptrdiff_t stride_src = (2 * HEVC_MAX_PB_SIZE + 
AV_INPUT_BUFFER_PADDING_SIZE);
@@ -194,8 +194,8 @@ void ff_hevc_sao_edge_filter_16x16_8_simd128(uint8_t *dst, 
const uint8_t *src,
     static const int8_t pos[4][2][2] = {
             { { -1,  0 }, {  1, 0 } }, // horizontal
             { {  0, -1 }, {  0, 1 } }, // vertical
-            { { -1, -1 }, {  1, 1 } }, // 45 degree
-            { {  1, -1 }, { -1, 1 } }, // 135 degree
+            { { -1, -1 }, {  1, 1 } }, // 135 degree
+            { {  1, -1 }, { -1, 1 } }, // 45 degree
     };
     int a_stride, b_stride;
     ptrdiff_t stride_src = (2 * HEVC_MAX_PB_SIZE + 
AV_INPUT_BUFFER_PADDING_SIZE);
-- 
2.52.0


>From 63f816566112f13ef2eae7c12bf86a00339b7537 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Tue, 15 Sep 2026 11:45:31 +0800
Subject: [PATCH 2/3] avcodec/aarch64: add NEON HEVC SAO edge 10 and 12 bit

                              Cortex-A510       Cortex-A715       Cortex-A725   
      Cortex-X3       Cortex-X925
hevc_sao_edge_8_10_neon:    332.5 (4.07x)      56.9 (5.50x)      57.4 (4.64x)   
   37.0 (7.38x)      24.2 (7.96x)
hevc_sao_edge_8_12_neon:    321.9 (4.22x)      57.1 (5.58x)      57.7 (4.70x)   
   37.0 (7.27x)      23.7 (8.12x)
hevc_sao_edge_16_10_neon:  1280.4 (4.11x)     227.3 (5.30x)     227.9 (4.61x)   
  146.4 (7.27x)      94.9 (8.38x)
hevc_sao_edge_16_12_neon:  1353.7 (3.96x)     227.9 (5.40x)     227.1 (4.63x)   
  146.4 (7.22x)      94.7 (8.40x)
hevc_sao_edge_32_10_neon:  5249.9 (3.84x)     912.9 (5.15x)     911.5 (4.48x)   
  592.8 (7.21x)     372.7 (8.95x)
hevc_sao_edge_32_12_neon:  5483.8 (3.67x)     912.6 (5.29x)     912.3 (4.47x)   
  579.8 (7.21x)     365.2 (9.11x)
hevc_sao_edge_48_10_neon: 11147.5 (4.07x)    2035.5 (5.14x)    2033.9 (4.48x)   
 1324.3 (7.27x)     815.7 (9.29x)
hevc_sao_edge_48_12_neon: 11179.6 (4.06x)    2035.5 (5.29x)    2035.9 (4.47x)   
 1324.9 (7.05x)     816.8 (9.25x)
hevc_sao_edge_64_10_neon: 19965.6 (4.03x)    3606.1 (5.15x)    3603.6 (4.49x)   
 2340.0 (7.27x)    1440.0 (9.42x)
hevc_sao_edge_64_12_neon: 20015.7 (4.02x)    3604.2 (5.30x)    3603.3 (4.49x)   
 2342.3 (7.07x)    1439.2 (9.40x)

Signed-off-by: Zhao Zhili <[email protected]>
---
 libavcodec/aarch64/h26x/dsp.h             |  22 ++--
 libavcodec/aarch64/h26x/sao_neon.S        | 118 +++++++++++++++++++++-
 libavcodec/aarch64/hevcdsp_init_aarch64.c |  10 ++
 3 files changed, 139 insertions(+), 11 deletions(-)

diff --git a/libavcodec/aarch64/h26x/dsp.h b/libavcodec/aarch64/h26x/dsp.h
index 0cbbdc3157..dab5163c60 100644
--- a/libavcodec/aarch64/h26x/dsp.h
+++ b/libavcodec/aarch64/h26x/dsp.h
@@ -32,15 +32,21 @@ void ff_h26x_sao_band_filter_16x16_8_neon(uint8_t *_dst, 
const uint8_t *_src,
                                         ptrdiff_t stride_dst, ptrdiff_t 
stride_src,
                                         const int16_t *sao_offset_val, int 
sao_left_class,
                                         int width, int height);
-void ff_hevc_sao_edge_filter_16x16_8_neon(uint8_t *dst, const uint8_t *src, 
ptrdiff_t stride_dst,
-                                          const int16_t *sao_offset_val, int 
eo, int width, int height);
-void ff_hevc_sao_edge_filter_8x8_8_neon(uint8_t *dst, const uint8_t *src, 
ptrdiff_t stride_dst,
-                                        const int16_t *sao_offset_val, int eo, 
int width, int height);
+#define SAO_EDGE_FILTER_PROTO(codec, size, depth)                          \
+    void ff_##codec##_sao_edge_filter_##size##_##depth##_neon(             \
+        uint8_t *dst, const uint8_t *src, ptrdiff_t stride_dst,            \
+        const int16_t *sao_offset_val, int eo, int width, int height)
 
-void ff_vvc_sao_edge_filter_16x16_8_neon(uint8_t *dst, const uint8_t *src, 
ptrdiff_t stride_dst,
-                                         const int16_t *sao_offset_val, int 
eo, int width, int height);
-void ff_vvc_sao_edge_filter_8x8_8_neon(uint8_t *dst, const uint8_t *src, 
ptrdiff_t stride_dst,
-                                       const int16_t *sao_offset_val, int eo, 
int width, int height);
+SAO_EDGE_FILTER_PROTO(hevc, 16x16, 8);
+SAO_EDGE_FILTER_PROTO(hevc, 8x8,   8);
+SAO_EDGE_FILTER_PROTO(hevc, 16x16, 10);
+SAO_EDGE_FILTER_PROTO(hevc, 8x8,   10);
+SAO_EDGE_FILTER_PROTO(hevc, 16x16, 12);
+SAO_EDGE_FILTER_PROTO(hevc, 8x8,   12);
+SAO_EDGE_FILTER_PROTO(vvc,  16x16, 8);
+SAO_EDGE_FILTER_PROTO(vvc,  8x8,   8);
+
+#undef SAO_EDGE_FILTER_PROTO
 
 #define NEON8_FNPROTO_PARTIAL_6(fn, args, ext) \
     void ff_hevc_put_hevc_##fn##_h4_8_neon##ext args;  \
diff --git a/libavcodec/aarch64/h26x/sao_neon.S 
b/libavcodec/aarch64/h26x/sao_neon.S
index 90022fcfc7..bc6e323deb 100644
--- a/libavcodec/aarch64/h26x/sao_neon.S
+++ b/libavcodec/aarch64/h26x/sao_neon.S
@@ -100,12 +100,18 @@ function ff_h26x_sao_band_filter_16x16_8_neon, export=1
         ret
 endfunc
 
-.Lhevc_sao_edge_pos:
+.Lhevc_sao_edge_pos_8:
 .word 1 // horizontal
 .word HEVC_SAO_STRIDE // vertical
 .word HEVC_SAO_STRIDE + 1 // 135 degree
 .word HEVC_SAO_STRIDE - 1 // 45 degree
 
+.Lhevc_sao_edge_pos_16bit:
+.word 2 // horizontal
+.word HEVC_SAO_STRIDE // vertical
+.word HEVC_SAO_STRIDE + 2 // 135 degree
+.word HEVC_SAO_STRIDE - 2 // 45 degree
+
 .Lvvc_sao_edge_pos:
 .word 1 // horizontal
 .word VVC_SAO_STRIDE // vertical
@@ -121,7 +127,7 @@ endfunc
 // ff_hevc_sao_edge_filter_16x16_8_neon(char *dst, char *src, ptrdiff 
stride_dst,
 //                                      int16 *sao_offset_val, int eo, int 
width, int height)
 function ff_hevc_sao_edge_filter_16x16_8_neon, export=1
-        adr             x7, .Lhevc_sao_edge_pos
+        adr             x7, .Lhevc_sao_edge_pos_8
         mov             x15, #HEVC_SAO_STRIDE
 1:
         ld1             {v3.8h}, [x3]              // load sao_offset_val
@@ -179,6 +185,83 @@ function ff_hevc_sao_edge_filter_16x16_8_neon, export=1
         ret
 endfunc
 
+/* Permutes sao_offset_val into the byte table v28 by edge_idx = { 1, 2, 0, 3, 
4 }.
+ * v29: bias 2
+ * v30: 0 for clip lower bound
+ * v31: clip higher bound, not set by this macro
+ */
+.macro  sao_edge_offsets_init
+        ld1             {v6.8h}, [x3]
+        movi            v29.8b, #2
+        movi            v30.8h, #0
+        mov             v7.16b, v6.16b
+        mov             v6.h[0], v7.h[1]
+        mov             v6.h[1], v7.h[2]
+        mov             v6.h[2], v7.h[0]
+        xtn             v28.8b, v6.8h
+.endm
+
+/* Filters one 8-pixel chunk
+ * v0: cur, and hold clipped result
+ * v1: a
+ * v2: b
+ */
+.macro  sao_edge_filter8
+        cmhi            v16.8h, v1.8h, v0.8h
+        cmhi            v17.8h, v0.8h, v1.8h
+        cmhi            v18.8h, v2.8h, v0.8h
+        cmhi            v19.8h, v0.8h, v2.8h
+        add             v20.8h, v16.8h, v18.8h
+        add             v21.8h, v17.8h, v19.8h
+        sub             v20.8h, v20.8h, v21.8h      // CMP(cur, a) + CMP(cur, 
b)
+        xtn             v20.8b, v20.8h
+        add             v20.8b, v20.8b, v29.8b      // offset table index
+        tbl             v16.8b, {v28.16b}, v20.8b
+        saddw           v0.8h, v0.8h, v16.8b
+        smax            v0.8h, v0.8h, v30.8h
+        smin            v0.8h, v0.8h, v31.8h
+.endm
+
+// ff_hevc_sao_edge_filter_16x16_12_neon(char *dst, char *src, ptrdiff 
stride_dst,
+//                                       int16 *sao_offset_val, int eo, int 
width, int height)
+function ff_hevc_sao_edge_filter_16x16_12_neon, export=1
+        mvni            v31.8h, #0xf0, lsl #8       // 4095
+        b               .Lhevc_sao_edge_16bit
+endfunc
+
+// ff_hevc_sao_edge_filter_16x16_10_neon(char *dst, char *src, ptrdiff 
stride_dst,
+//                                       int16 *sao_offset_val, int eo, int 
width, int height)
+function ff_hevc_sao_edge_filter_16x16_10_neon, export=1
+        mvni            v31.8h, #0xfc, lsl #8       // 1023
+.Lhevc_sao_edge_16bit:
+        adr             x7, .Lhevc_sao_edge_pos_16bit
+        mov             x15, #HEVC_SAO_STRIDE
+        add             w5, w5, #7
+        ldr             w4, [x7, w4, uxtw #2]       // a/b offsets in bytes
+        bic             w5, w5, #7
+        sao_edge_offsets_init
+        lsl             w5, w5, #1                  // width in bytes, 
multiple of 16
+        sub             x15, x15, x5                // src step to the next 
line
+        sub             x16, x2, x5                 // dst step to the next 
line
+1:
+        lsr             x14, x5, #4                 // 8-pixel groups
+        sub             x12, x1, x4
+        add             x13, x1, x4
+2:
+        ldr             q0, [x1], #16
+        ldr             q1, [x12], #16
+        ldr             q2, [x13], #16
+        subs            x14, x14, #1
+        sao_edge_filter8
+        str             q0, [x0], #16
+        b.ne            2b
+        subs            w6, w6, #1
+        add             x1, x1, x15
+        add             x0, x0, x16
+        b.ne            1b
+        ret
+endfunc
+
 function ff_vvc_sao_edge_filter_8x8_8_neon, export=1
         adr             x7, .Lvvc_sao_edge_pos
         mov             x15, #VVC_SAO_STRIDE
@@ -188,7 +271,7 @@ endfunc
 // ff_hevc_sao_edge_filter_8x8_8_neon(char *dst, char *src, ptrdiff stride_dst,
 //                                    int16 *sao_offset_val, int eo, int 
width, int height)
 function ff_hevc_sao_edge_filter_8x8_8_neon, export=1
-        adr             x7, .Lhevc_sao_edge_pos
+        adr             x7, .Lhevc_sao_edge_pos_8
         mov             x15, #HEVC_SAO_STRIDE
 1:
         ldr             w4, [x7, w4, uxtw #2]
@@ -235,3 +318,32 @@ function ff_hevc_sao_edge_filter_8x8_8_neon, export=1
         b.ne            1b
         ret
 endfunc
+
+// ff_hevc_sao_edge_filter_8x8_12_neon(char *dst, char *src, ptrdiff 
stride_dst,
+//                                     int16 *sao_offset_val, int eo, int 
width, int height)
+function ff_hevc_sao_edge_filter_8x8_12_neon, export=1
+        mvni            v31.8h, #0xf0, lsl #8     // 4095
+        b               .Lhevc_sao_edge_8x8_16bit
+endfunc
+
+// ff_hevc_sao_edge_filter_8x8_10_neon(char *dst, char *src, ptrdiff 
stride_dst,
+//                                     int16 *sao_offset_val, int eo, int 
width, int height)
+function ff_hevc_sao_edge_filter_8x8_10_neon, export=1
+        mvni            v31.8h, #0xfc, lsl #8     // 1023
+.Lhevc_sao_edge_8x8_16bit:
+        adr             x7, .Lhevc_sao_edge_pos_16bit
+        mov             x15, #HEVC_SAO_STRIDE
+        ldr             w4, [x7, w4, uxtw #2]      // a/b offsets in bytes
+        sao_edge_offsets_init
+        sub             x9,  x1, x4                // a neighbours
+        add             x10, x1, x4                // b neighbours
+1:
+        ld1             {v0.8h}, [x1], x15
+        ld1             {v1.8h}, [x9], x15
+        ld1             {v2.8h}, [x10], x15
+        subs            w6, w6, #1
+        sao_edge_filter8
+        st1             {v0.8h}, [x0], x2
+        b.ne            1b
+        ret
+endfunc
diff --git a/libavcodec/aarch64/hevcdsp_init_aarch64.c 
b/libavcodec/aarch64/hevcdsp_init_aarch64.c
index a2ca8aa124..12f8d93803 100644
--- a/libavcodec/aarch64/hevcdsp_init_aarch64.c
+++ b/libavcodec/aarch64/hevcdsp_init_aarch64.c
@@ -338,6 +338,11 @@ av_cold void ff_hevc_dsp_init_aarch64(HEVCDSPContext *c, 
const int bit_depth)
         c->idct_dc[2]                  = ff_hevc_idct_16x16_dc_10_neon;
         c->idct_dc[3]                  = ff_hevc_idct_32x32_dc_10_neon;
         c->dequant                     = hevc_dequant_10_neon;
+        c->sao_edge_filter[0]          = ff_hevc_sao_edge_filter_8x8_10_neon;
+        c->sao_edge_filter[1]          =
+        c->sao_edge_filter[2]          =
+        c->sao_edge_filter[3]          =
+        c->sao_edge_filter[4]          = ff_hevc_sao_edge_filter_16x16_10_neon;
     }
     if (bit_depth == 12) {
         c->hevc_h_loop_filter_luma     = ff_hevc_h_loop_filter_luma_12_neon;
@@ -353,5 +358,10 @@ av_cold void ff_hevc_dsp_init_aarch64(HEVCDSPContext *c, 
const int bit_depth)
         c->idct_dc[2]                  = ff_hevc_idct_16x16_dc_12_neon;
         c->idct_dc[3]                  = ff_hevc_idct_32x32_dc_12_neon;
         c->dequant                     = hevc_dequant_12_neon;
+        c->sao_edge_filter[0]          = ff_hevc_sao_edge_filter_8x8_12_neon;
+        c->sao_edge_filter[1]          =
+        c->sao_edge_filter[2]          =
+        c->sao_edge_filter[3]          =
+        c->sao_edge_filter[4]          = ff_hevc_sao_edge_filter_16x16_12_neon;
     }
 }
-- 
2.52.0


>From a4b9409bf4339f6dfa9e929b1ccdec0f6aa6becf Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Tue, 15 Sep 2026 11:46:01 +0800
Subject: [PATCH 3/3] avcodec/aarch64: add NEON VVC SAO edge 10 and 12 bit

                              Cortex-A510       Cortex-A715       Cortex-A725   
      Cortex-X3       Cortex-X925
vvc_sao_edge_8_10_neon:     308.9 (4.29x)      57.1 (5.47x)      57.4 (4.66x)   
   36.4 (7.35x)      23.7 (8.13x)
vvc_sao_edge_8_12_neon:     314.6 (4.19x)      57.3 (5.58x)      58.3 (4.59x)   
   36.4 (7.34x)      23.9 (8.08x)
vvc_sao_edge_16_10_neon:   1202.1 (4.28x)     228.1 (5.26x)     228.6 (4.54x)   
  144.6 (7.28x)      95.0 (8.39x)
vvc_sao_edge_16_12_neon:   1203.3 (4.28x)     229.4 (5.37x)     239.4 (4.33x)   
  144.6 (7.22x)      95.0 (8.38x)
vvc_sao_edge_32_10_neon:   4718.8 (4.28x)     913.1 (5.12x)     911.5 (4.42x)   
  571.5 (7.30x)     366.9 (9.08x)
vvc_sao_edge_32_12_neon:   4737.9 (4.26x)     913.3 (5.28x)     913.0 (4.41x)   
  571.8 (7.31x)     365.6 (9.15x)
vvc_sao_edge_48_10_neon:  10970.3 (4.16x)    2035.0 (5.13x)    2036.2 (4.43x)   
 1307.6 (7.14x)     823.6 (9.19x)
vvc_sao_edge_48_12_neon:  10980.7 (4.15x)    2037.3 (5.29x)    2035.0 (4.43x)   
 1311.2 (7.14x)     824.1 (9.20x)
vvc_sao_edge_64_10_neon:  19127.9 (4.23x)    3605.3 (5.13x)    3628.5 (4.41x)   
 2315.9 (7.17x)    1452.7 (9.33x)
vvc_sao_edge_64_12_neon:  19136.2 (4.23x)    3606.3 (5.29x)    3629.0 (4.40x)   
 2320.9 (7.17x)    1448.9 (9.36x)
vvc_sao_edge_80_10_neon:  29560.9 (4.29x)    5624.0 (5.30x)    5657.5 (4.64x)   
 3610.8 (7.17x)    2252.1 (9.44x)
vvc_sao_edge_80_12_neon:  29644.8 (4.28x)    5626.8 (5.48x)    5661.2 (4.64x)   
 3614.4 (7.18x)    2250.3 (9.46x)
vvc_sao_edge_96_10_neon:  42316.0 (4.29x)    8088.8 (5.28x)    8135.3 (4.61x)   
 5188.5 (7.20x)    3236.3 (9.49x)
vvc_sao_edge_96_12_neon:  42863.0 (4.23x)    8091.9 (5.45x)    8136.9 (4.61x)   
 5190.6 (7.21x)    3236.1 (9.48x)
vvc_sao_edge_112_10_neon: 57410.2 (4.30x)   11001.8 (5.27x)   11056.2 (4.59x)   
 7066.3 (7.19x)    4407.8 (9.48x)
vvc_sao_edge_112_12_neon: 57372.5 (4.30x)   11006.7 (5.44x)   11054.2 (4.59x)   
 7074.3 (7.19x)    4400.6 (9.52x)
vvc_sao_edge_128_10_neon: 75737.0 (4.26x)   14401.4 (5.26x)   14468.5 (4.57x)   
 9227.8 (7.22x)    5729.6 (9.54x)
vvc_sao_edge_128_12_neon: 75824.1 (4.25x)   14404.0 (5.42x)   14472.1 (4.57x)   
 9222.1 (7.20x)    5730.8 (9.55x)

Signed-off-by: Zhao Zhili <[email protected]>
---
 libavcodec/aarch64/h26x/dsp.h      |  4 +++
 libavcodec/aarch64/h26x/sao_neon.S | 42 ++++++++++++++++++++++++++++++
 libavcodec/aarch64/vvc/dsp_init.c  |  8 ++++++
 3 files changed, 54 insertions(+)

diff --git a/libavcodec/aarch64/h26x/dsp.h b/libavcodec/aarch64/h26x/dsp.h
index dab5163c60..2b8eae130a 100644
--- a/libavcodec/aarch64/h26x/dsp.h
+++ b/libavcodec/aarch64/h26x/dsp.h
@@ -45,6 +45,10 @@ SAO_EDGE_FILTER_PROTO(hevc, 16x16, 12);
 SAO_EDGE_FILTER_PROTO(hevc, 8x8,   12);
 SAO_EDGE_FILTER_PROTO(vvc,  16x16, 8);
 SAO_EDGE_FILTER_PROTO(vvc,  8x8,   8);
+SAO_EDGE_FILTER_PROTO(vvc,  16x16, 10);
+SAO_EDGE_FILTER_PROTO(vvc,  8x8,   10);
+SAO_EDGE_FILTER_PROTO(vvc,  16x16, 12);
+SAO_EDGE_FILTER_PROTO(vvc,  8x8,   12);
 
 #undef SAO_EDGE_FILTER_PROTO
 
diff --git a/libavcodec/aarch64/h26x/sao_neon.S 
b/libavcodec/aarch64/h26x/sao_neon.S
index bc6e323deb..b2c3cdc8d3 100644
--- a/libavcodec/aarch64/h26x/sao_neon.S
+++ b/libavcodec/aarch64/h26x/sao_neon.S
@@ -118,6 +118,12 @@ endfunc
 .word VVC_SAO_STRIDE + 1 // 135 degree
 .word VVC_SAO_STRIDE - 1 // 45 degree
 
+.Lvvc_sao_edge_pos_16bit:
+.word 2 // horizontal
+.word VVC_SAO_STRIDE // vertical
+.word VVC_SAO_STRIDE + 2 // 135 degree
+.word VVC_SAO_STRIDE - 2 // 45 degree
+
 function ff_vvc_sao_edge_filter_16x16_8_neon, export=1
         adr             x7, .Lvvc_sao_edge_pos
         mov             x15, #VVC_SAO_STRIDE
@@ -222,6 +228,23 @@ endfunc
         smin            v0.8h, v0.8h, v31.8h
 .endm
 
+// ff_vvc_sao_edge_filter_16x16_12_neon(char *dst, char *src, ptrdiff 
stride_dst,
+//                                      int16 *sao_offset_val, int eo, int 
width, int height)
+function ff_vvc_sao_edge_filter_16x16_12_neon, export=1
+        mvni            v31.8h, #0xf0, lsl #8       // 4095
+        b               .Lvvc_sao_edge_16x16_16bit
+endfunc
+
+// ff_vvc_sao_edge_filter_16x16_10_neon(char *dst, char *src, ptrdiff 
stride_dst,
+//                                      int16 *sao_offset_val, int eo, int 
width, int height)
+function ff_vvc_sao_edge_filter_16x16_10_neon, export=1
+        mvni            v31.8h, #0xfc, lsl #8       // 1023
+.Lvvc_sao_edge_16x16_16bit:
+        adr             x7, .Lvvc_sao_edge_pos_16bit
+        mov             x15, #VVC_SAO_STRIDE
+        b               .Lh26x_sao_edge_16x16_16bit
+endfunc
+
 // ff_hevc_sao_edge_filter_16x16_12_neon(char *dst, char *src, ptrdiff 
stride_dst,
 //                                       int16 *sao_offset_val, int eo, int 
width, int height)
 function ff_hevc_sao_edge_filter_16x16_12_neon, export=1
@@ -236,6 +259,7 @@ function ff_hevc_sao_edge_filter_16x16_10_neon, export=1
 .Lhevc_sao_edge_16bit:
         adr             x7, .Lhevc_sao_edge_pos_16bit
         mov             x15, #HEVC_SAO_STRIDE
+.Lh26x_sao_edge_16x16_16bit:
         add             w5, w5, #7
         ldr             w4, [x7, w4, uxtw #2]       // a/b offsets in bytes
         bic             w5, w5, #7
@@ -319,6 +343,23 @@ function ff_hevc_sao_edge_filter_8x8_8_neon, export=1
         ret
 endfunc
 
+// ff_vvc_sao_edge_filter_8x8_12_neon(char *dst, char *src, ptrdiff stride_dst,
+//                                    int16 *sao_offset_val, int eo, int 
width, int height)
+function ff_vvc_sao_edge_filter_8x8_12_neon, export=1
+        mvni            v31.8h, #0xf0, lsl #8       // 4095
+        b               .Lvvc_sao_edge_8x8_16bit
+endfunc
+
+// ff_vvc_sao_edge_filter_8x8_10_neon(char *dst, char *src, ptrdiff stride_dst,
+//                                    int16 *sao_offset_val, int eo, int 
width, int height)
+function ff_vvc_sao_edge_filter_8x8_10_neon, export=1
+        mvni            v31.8h, #0xfc, lsl #8       // 1023
+.Lvvc_sao_edge_8x8_16bit:
+        adr             x7, .Lvvc_sao_edge_pos_16bit
+        mov             x15, #VVC_SAO_STRIDE
+        b               .Lh26x_sao_edge_8x8_16bit
+endfunc
+
 // ff_hevc_sao_edge_filter_8x8_12_neon(char *dst, char *src, ptrdiff 
stride_dst,
 //                                     int16 *sao_offset_val, int eo, int 
width, int height)
 function ff_hevc_sao_edge_filter_8x8_12_neon, export=1
@@ -333,6 +374,7 @@ function ff_hevc_sao_edge_filter_8x8_10_neon, export=1
 .Lhevc_sao_edge_8x8_16bit:
         adr             x7, .Lhevc_sao_edge_pos_16bit
         mov             x15, #HEVC_SAO_STRIDE
+.Lh26x_sao_edge_8x8_16bit:
         ldr             w4, [x7, w4, uxtw #2]      // a/b offsets in bytes
         sao_edge_offsets_init
         sub             x9,  x1, x4                // a neighbours
diff --git a/libavcodec/aarch64/vvc/dsp_init.c 
b/libavcodec/aarch64/vvc/dsp_init.c
index 53bf9f9edd..8936a4606a 100644
--- a/libavcodec/aarch64/vvc/dsp_init.c
+++ b/libavcodec/aarch64/vvc/dsp_init.c
@@ -372,6 +372,10 @@ void ff_vvc_dsp_init_aarch64(VVCDSPContext *const c, const 
int bd)
         c->inter.put[1][5][1][1] =
         c->inter.put[1][6][1][1] = ff_vvc_put_chroma_hv_x16_10_neon;
 
+        c->sao.edge_filter[0] = ff_vvc_sao_edge_filter_8x8_10_neon;
+        for (int i = 1; i < FF_ARRAY_ELEMS(c->sao.edge_filter); i++)
+            c->sao.edge_filter[i] = ff_vvc_sao_edge_filter_16x16_10_neon;
+
         c->alf.filter[LUMA] = alf_filter_luma_10_neon;
         c->alf.filter[CHROMA] = alf_filter_chroma_10_neon;
         c->alf.classify = alf_classify_10_neon;
@@ -424,6 +428,10 @@ void ff_vvc_dsp_init_aarch64(VVCDSPContext *const c, const 
int bd)
         c->inter.put[1][5][1][1] =
         c->inter.put[1][6][1][1] = ff_vvc_put_chroma_hv_x16_12_neon;
 
+        c->sao.edge_filter[0] = ff_vvc_sao_edge_filter_8x8_12_neon;
+        for (int i = 1; i < FF_ARRAY_ELEMS(c->sao.edge_filter); i++)
+            c->sao.edge_filter[i] = ff_vvc_sao_edge_filter_16x16_12_neon;
+
         c->alf.filter[LUMA] = alf_filter_luma_12_neon;
         c->alf.filter[CHROMA] = alf_filter_chroma_12_neon;
         c->alf.classify = alf_classify_12_neon;
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to