From b0cb307c253d2c9f4b94a54dfc74ddb83af984cc Mon Sep 17 00:00:00 2001
From: Seppo Tomperi <seppo.tomperi@vtt.fi>
Date: Mon, 8 Dec 2014 13:24:40 +0200
Subject: [PATCH 1/9] added ARM NEON optimized SAO band offset

---
 libavcodec/arm/Makefile            |   3 +-
 libavcodec/arm/hevcdsp_init_neon.c |  47 +++++++++
 libavcodec/arm/hevcdsp_sao_neon.S  | 204 +++++++++++++++++++++++++++++++++++++
 3 files changed, 253 insertions(+), 1 deletion(-)
 create mode 100644 libavcodec/arm/hevcdsp_sao_neon.S

diff --git a/libavcodec/arm/Makefile b/libavcodec/arm/Makefile
index 6051ec8..093a2e8 100644
--- a/libavcodec/arm/Makefile
+++ b/libavcodec/arm/Makefile
@@ -133,7 +133,8 @@ NEON-OBJS-$(CONFIG_HEVC_DECODER)       += arm/hevcdsp_init_neon.o       \
                                           arm/hevcdsp_deblock_neon.o    \
                                           arm/hevcdsp_epel_neon.o       \
                                           arm/hevcdsp_idct_neon.o       \
-                                          arm/hevcdsp_qpel_neon.o
+                                          arm/hevcdsp_qpel_neon.o       \
+                                          arm/hevcdsp_sao_neon.o
 NEON-OBJS-$(CONFIG_RV30_DECODER)       += arm/rv34dsp_neon.o
 NEON-OBJS-$(CONFIG_RV40_DECODER)       += arm/rv34dsp_neon.o            \
                                           arm/rv40dsp_neon.o
diff --git a/libavcodec/arm/hevcdsp_init_neon.c b/libavcodec/arm/hevcdsp_init_neon.c
index 733ff08..69e2b2c 100644
--- a/libavcodec/arm/hevcdsp_init_neon.c
+++ b/libavcodec/arm/hevcdsp_init_neon.c
@@ -22,6 +22,7 @@
 #include "libavutil/arm/cpu.h"
 #include "libavcodec/hevcdsp.h"
 #include "hevcdsp_arm.h"
+#include "../bit_depth_template.c"
 
 void ff_hevc_v_loop_filter_luma_neon(uint8_t *_pix, ptrdiff_t _stride, int _beta, int *_tc, uint8_t *_no_p, uint8_t *_no_q);
 void ff_hevc_h_loop_filter_luma_neon(uint8_t *_pix, ptrdiff_t _stride, int _beta, int *_tc, uint8_t *_no_p, uint8_t *_no_q);
@@ -43,6 +44,11 @@ void ff_hevc_transform_add_16x16_neon_8(uint8_t *_dst, int16_t *coeffs,
 void ff_hevc_transform_add_32x32_neon_8(uint8_t *_dst, int16_t *coeffs,
                                       ptrdiff_t stride);
 
+void ff_hevc_sao_band_w8_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
+void ff_hevc_sao_band_w16_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
+void ff_hevc_sao_band_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
+void ff_hevc_sao_band_w64_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
+
 #define PUT_PIXELS(name) \
     void name(int16_t *dst, uint8_t *src, \
                                 ptrdiff_t srcstride, int height, \
@@ -151,6 +157,44 @@ void ff_hevc_put_qpel_bi_neon_wrapper(uint8_t *dst, ptrdiff_t dststride, uint8_t
     put_hevc_qpel_uw_neon[my][mx](dst, dststride, src, srcstride, width, height, src2, MAX_PB_SIZE);
 }
 
+static void ff_hevc_sao_band_neon_wrapper(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src,
+                                          int16_t *sao_offset_val, int sao_left_class, int width, int height)
+{
+    pixel *dst = (pixel *)_dst;
+    pixel *src = (pixel *)_src;
+    int8_t offset_table[32] = { 0 };
+    int k, y, x;
+    int shift  = 3; // BIT_DEPTH - 5
+
+    stride_src /= sizeof(pixel);
+    stride_dst /= sizeof(pixel);
+
+    for (k = 0; k < 4; k++)
+        offset_table[(k + sao_left_class) & 31] = sao_offset_val[k + 1];
+
+    switch(width){
+    case 8:
+        ff_hevc_sao_band_w8_neon_8(_dst, _src, stride_dst, stride_src, height, offset_table);
+        break;
+    case 16:
+        ff_hevc_sao_band_w16_neon_8(_dst, _src, stride_dst, stride_src, height, offset_table);
+        break;
+    case 32:
+        ff_hevc_sao_band_w32_neon_8(_dst, _src, stride_dst, stride_src, height, offset_table);
+        break;
+    case 64:
+        ff_hevc_sao_band_w64_neon_8(_dst, _src, stride_dst, stride_src, height, offset_table);
+        break;
+    default:
+        for (y = 0; y < height; y++) {
+            for (x = 0; x < width; x++)
+                dst[x] = av_clip_pixel(src[x] + offset_table[src[x] >> shift]);
+            dst += stride_dst;
+            src += stride_src;
+        }
+    }
+}
+
 av_cold void ff_hevcdsp_init_neon(HEVCDSPContext *c, const int bit_depth)
 {
     if (bit_depth == 8) {
@@ -170,6 +214,9 @@ av_cold void ff_hevcdsp_init_neon(HEVCDSPContext *c, const int bit_depth)
         c->transform_add[2]            = ff_hevc_transform_add_16x16_neon_8;
         c->transform_add[3]            = ff_hevc_transform_add_32x32_neon_8;
         c->idct_4x4_luma               = ff_hevc_transform_luma_4x4_neon_8;
+        for (x = 0; x < sizeof c->sao_band_filter / sizeof *c->sao_band_filter; x++) {
+          c->sao_band_filter[x]        = ff_hevc_sao_band_neon_wrapper;
+        }
         put_hevc_qpel_neon[1][0]       = ff_hevc_put_qpel_v1_neon_8;
         put_hevc_qpel_neon[2][0]       = ff_hevc_put_qpel_v2_neon_8;
         put_hevc_qpel_neon[3][0]       = ff_hevc_put_qpel_v3_neon_8;
diff --git a/libavcodec/arm/hevcdsp_sao_neon.S b/libavcodec/arm/hevcdsp_sao_neon.S
new file mode 100644
index 0000000..1f0ad64
--- /dev/null
+++ b/libavcodec/arm/hevcdsp_sao_neon.S
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2014 Seppo Tomperi <seppo.tomperi@vtt.fi>
+ *
+ * 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/arm/asm.S"
+#include "neon.S"
+
+function ff_hevc_sao_band_w8_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // offset_table
+        vpush {d8-d15}
+        vld1.8  {q0, q1}, [r5] // offset table
+
+1:      subs    r4, #1
+        vld1.8   {d24}, [r1], r3
+        vshr.u8  d16, d24, #3
+        vtbl.8   d16, {q0, q1}, d16
+        vmovl.s8 q2, d16
+        vmovl.u8 q6, d24
+        vadd.s16 q2, q6
+        vqmovun.s16 d4, q2
+        vst1.8  {d4}, [r0], r2
+        bne    1b
+
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
+
+function ff_hevc_sao_band_w16_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // offset_table
+        vpush {d8-d15}
+        vld1.8  {q0, q1}, [r5] // offset table
+
+1:      subs    r4, #1
+        vld1.8  {q12}, [r1], r3
+
+        vshr.u8   q8, q12, #3
+
+        vtbl.8  d16, {q0, q1}, d16
+        vtbl.8  d17, {q0, q1}, d17
+
+        vmovl.s8 q2, d16
+        vmovl.s8 q3, d17
+
+        vmovl.u8 q6, d24
+        vmovl.u8 q7, d25
+
+        vadd.s16 q2, q6
+        vadd.s16 q3, q7
+
+        vqmovun.s16 d4, q2
+        vqmovun.s16 d5, q3
+
+        vstm.8   r0, {q2}
+        add    r0, r2
+        bne    1b
+
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
+
+function ff_hevc_sao_band_w32_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // offset_table
+        vpush {d8-d15}
+        vld1.8  {q0, q1}, [r5] // offset table
+
+1:      subs    r4, #1
+        vld1.8  {q12-q13}, [r1], r3
+
+        vshr.u8   q8, q12, #3
+        vshr.u8   q9, q13, #3
+
+        vtbl.8  d16, {q0, q1}, d16
+        vtbl.8  d17, {q0, q1}, d17
+        vtbl.8  d18, {q0, q1}, d18
+        vtbl.8  d19, {q0, q1}, d19
+
+        vmovl.s8 q2, d16
+        vmovl.s8 q3, d17 // q8 free
+        vmovl.s8 q4, d18
+        vmovl.s8 q5, d19 // q9 free
+
+        vmovl.u8 q6, d24
+        vmovl.u8 q7, d25 // q12 free
+        vmovl.u8 q8, d26
+        vmovl.u8 q9, d27 // q13 free
+
+        vadd.s16 q2, q6
+        vadd.s16 q3, q7
+        vadd.s16 q4, q8
+        vadd.s16 q5, q9
+
+        vqmovun.s16 d4, q2
+        vqmovun.s16 d5, q3
+        vqmovun.s16 d6, q4 // q4 free
+        vqmovun.s16 d7, q5 // q5 free
+
+        vst1.8 {q2-q3}, [r0], r2
+        bne    1b
+
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
+
+function ff_hevc_sao_band_w64_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // offset_table
+        vpush {d8-d15}
+        vld1.8  {q0, q1}, [r5] // offset table
+
+1:      subs    r4, #1
+        vld1.8  {q12-q13}, [r1]!
+        vld1.8  {q14-q15}, [r1], r3
+        sub     r1, #32
+
+        vshr.u8   q8, q12, #3
+        vshr.u8   q9, q13, #3
+        vshr.u8  q10, q14, #3
+        vshr.u8  q11, q15, #3
+
+        vtbl.8  d16, {q0, q1}, d16
+        vtbl.8  d17, {q0, q1}, d17
+        vtbl.8  d18, {q0, q1}, d18
+        vtbl.8  d19, {q0, q1}, d19
+        vtbl.8  d20, {q0, q1}, d20
+        vtbl.8  d21, {q0, q1}, d21
+        vtbl.8  d22, {q0, q1}, d22
+        vtbl.8  d23, {q0, q1}, d23
+
+        vmovl.s8 q2, d16
+        vmovl.s8 q3, d17 // q8 free
+        vmovl.s8 q4, d18
+        vmovl.s8 q5, d19 // q9 free
+
+        vmovl.u8 q6, d24
+        vmovl.u8 q7, d25 // q12 free
+        vmovl.u8 q8, d26
+        vmovl.u8 q9, d27 // q13 free
+
+        vadd.s16 q2, q6
+        vadd.s16 q3, q7
+        vadd.s16 q4, q8
+        vadd.s16 q5, q9
+
+        vqmovun.s16 d4, q2
+        vqmovun.s16 d5, q3
+        vqmovun.s16 d6, q4 // q4 free
+        vqmovun.s16 d7, q5 // q5 free
+
+        // free q4 -q9, q12 - q13
+        vmovl.s8 q4, d20
+        vmovl.s8 q5, d21 // q10 free
+        vmovl.s8 q6, d22
+        vmovl.s8 q7, d23 // q11 free
+
+        vmovl.u8  q8, d28
+        vmovl.u8  q9, d29 // q14 free
+        vmovl.u8 q10, d30
+        vmovl.u8 q11, d31 // q15 free
+
+        vadd.s16 q4, q8
+        vadd.s16 q5, q9
+        vadd.s16 q6, q10
+        vadd.s16 q7, q11
+
+        vqmovun.s16  d8, q4
+        vqmovun.s16  d9, q5
+        vqmovun.s16 d10, q6
+        vqmovun.s16 d11, q7
+
+        vstm.8   r0, {q2-q5}
+        add    r0, r2
+        bne    1b
+
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
+
-- 
2.5.0


From 8429b1de64bb871d57651ecfe3b084e2dfe0af51 Mon Sep 17 00:00:00 2001
From: Seppo Tomperi <seppo.tomperi@vtt.fi>
Date: Wed, 27 May 2015 18:10:20 +0100
Subject: [PATCH 2/9] added NEON optimized sao edge for eo1 width 64

---
 libavcodec/arm/hevcdsp_init_neon.c |  47 ++++++++++++
 libavcodec/arm/hevcdsp_sao_neon.S  | 147 +++++++++++++++++++++++++++++++++++++
 2 files changed, 194 insertions(+)

diff --git a/libavcodec/arm/hevcdsp_init_neon.c b/libavcodec/arm/hevcdsp_init_neon.c
index 69e2b2c..c7b5404 100644
--- a/libavcodec/arm/hevcdsp_init_neon.c
+++ b/libavcodec/arm/hevcdsp_init_neon.c
@@ -22,6 +22,7 @@
 #include "libavutil/arm/cpu.h"
 #include "libavcodec/hevcdsp.h"
 #include "hevcdsp_arm.h"
+#include "libavcodec/avcodec.h"
 #include "../bit_depth_template.c"
 
 void ff_hevc_v_loop_filter_luma_neon(uint8_t *_pix, ptrdiff_t _stride, int _beta, int *_tc, uint8_t *_no_p, uint8_t *_no_q);
@@ -48,6 +49,7 @@ void ff_hevc_sao_band_w8_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_d
 void ff_hevc_sao_band_w16_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
 void ff_hevc_sao_band_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
 void ff_hevc_sao_band_w64_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
+void ff_hevc_sao_edge_eo1_w64_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
 
 #define PUT_PIXELS(name) \
     void name(int16_t *dst, uint8_t *src, \
@@ -195,6 +197,50 @@ static void ff_hevc_sao_band_neon_wrapper(uint8_t *_dst, uint8_t *_src, ptrdiff_
     }
 }
 
+#define CMP(a, b) ((a) > (b) ? 1 : ((a) == (b) ? 0 : -1))
+static void ff_hevc_sao_edge_neon_wrapper(uint8_t *_dst /* align 16 */, uint8_t *_src /* align 32 */, ptrdiff_t stride_dst,
+                                          int16_t *_sao_offset_val, int eo, int width, int height)
+{
+    static const uint8_t edge_idx[] = { 1, 2, 0, 3, 4 };
+    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
+    };
+    int8_t sao_offset_val[8];  // padding of 3 for vld
+    ptrdiff_t stride_src = (2*MAX_PB_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
+    pixel *dst = (pixel *)_dst;
+    pixel *src = (pixel *)_src;
+    int a_stride, b_stride;
+    int x, y;
+
+    for (x = 0; x < 5; x++) {
+        sao_offset_val[x] = _sao_offset_val[x];
+    }
+
+    stride_src /= sizeof(pixel);
+    stride_dst /= sizeof(pixel);
+
+    if (eo == 1 && width == 64) {
+        ff_hevc_sao_edge_eo1_w64_neon_8(dst, src, stride_dst, stride_src, height, sao_offset_val);
+    } else {
+        a_stride = pos[eo][0][0] + pos[eo][0][1] * stride_src;
+        b_stride = pos[eo][1][0] + pos[eo][1][1] * stride_src;
+        for (y = 0; y < height; y++) {
+            for (x = 0; x < width; x++) {
+                int diff0         = CMP(src[x], src[x + a_stride]);
+                int diff1         = CMP(src[x], src[x + b_stride]);
+                int offset_val    = edge_idx[2 + diff0 + diff1];
+                dst[x] = av_clip_pixel(src[x] + sao_offset_val[offset_val]);
+            }
+            src += stride_src;
+            dst += stride_dst;
+        }
+    }
+}
+#undef CMP
+
 av_cold void ff_hevcdsp_init_neon(HEVCDSPContext *c, const int bit_depth)
 {
     if (bit_depth == 8) {
@@ -216,6 +262,7 @@ av_cold void ff_hevcdsp_init_neon(HEVCDSPContext *c, const int bit_depth)
         c->idct_4x4_luma               = ff_hevc_transform_luma_4x4_neon_8;
         for (x = 0; x < sizeof c->sao_band_filter / sizeof *c->sao_band_filter; x++) {
           c->sao_band_filter[x]        = ff_hevc_sao_band_neon_wrapper;
+          c->sao_edge_filter[x]        = ff_hevc_sao_edge_neon_wrapper;
         }
         put_hevc_qpel_neon[1][0]       = ff_hevc_put_qpel_v1_neon_8;
         put_hevc_qpel_neon[2][0]       = ff_hevc_put_qpel_v2_neon_8;
diff --git a/libavcodec/arm/hevcdsp_sao_neon.S b/libavcodec/arm/hevcdsp_sao_neon.S
index 1f0ad64..5ec2de9 100644
--- a/libavcodec/arm/hevcdsp_sao_neon.S
+++ b/libavcodec/arm/hevcdsp_sao_neon.S
@@ -202,3 +202,150 @@ function ff_hevc_sao_band_w64_neon_8, export=1
         bx lr
 endfunc
 
+function ff_hevc_sao_edge_eo1_w64_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // sao_offset_val_table
+        ldr    r6, =0x02
+        vpush {d8-d15}
+1:      subs    r4, #1
+        // load a
+        sub     r1, r3
+        vld1.8  {q0-q1}, [r1]!
+        vld1.8  {q2-q3}, [r1], r3
+        sub     r1, #32
+        // load c
+        vld1.8  {q4-q5}, [r1]!
+        vld1.8  {q6-q7}, [r1], r3
+        sub     r1, #32
+        // load b
+        vld1.8  {q8-q9}, [r1]!
+        vld1.8  {q10-q11}, [r1], r3
+        sub     r1, #32
+
+        vcgt.u8 q12, q4, q0 // c > a -> -1 , otherwise 0
+        vcgt.u8 q0,  q0, q4 // a > c -> -1 , otherwise 0
+        vcgt.u8 q13, q5, q1
+        vcgt.u8 q1,  q1, q5
+        vcgt.u8 q14, q6, q2
+        vcgt.u8 q2,  q2, q6
+        vcgt.u8 q15, q7, q3
+        vcgt.u8 q3,  q3, q7
+
+        vsub.s8 q12, q0, q12 // diff0
+        vsub.s8 q13, q1, q13
+        vsub.s8 q14, q2, q14
+        vsub.s8 q15, q3, q15
+
+        vcgt.u8  q0,  q4, q8 // c > b
+        vcgt.u8  q8,  q8, q4 // b > c
+        vcgt.u8  q1,  q5, q9
+        vcgt.u8  q9,  q9, q5
+        vcgt.u8  q2,  q6, q10
+        vcgt.u8 q10, q10, q6
+        vcgt.u8  q3,  q7, q11
+        vcgt.u8 q11, q11, q7
+
+        vsub.s8 q0, q8, q0 // diff1
+        vsub.s8 q1, q9, q1
+        vsub.s8 q2, q10, q2
+        vsub.s8 q3, q11, q3
+
+        veor.u8 q8, q8  // zero register
+        vdup.s8 q9, r6  // 2 to all elements
+        add     r6, #1
+        vdup.s8 q10, r6 // 3 to all elements
+        sub     r6, #1
+
+        vadd.s8 q0, q12 //diff0 + diff1
+        vadd.s8 q1, q13
+        vadd.s8 q2, q14
+        vadd.s8 q3, q15
+
+        vcgt.s8 q4, q0, q8 // diff0 + diff1 > 0
+        vcgt.s8 q5, q1, q8
+        vcgt.s8 q6, q2, q8
+        vcgt.s8 q7, q3, q8
+
+        vclt.s8 q11, q0, q8 // diff0 + diff1 < 0
+        vclt.s8 q12, q1, q8
+        vclt.s8 q13, q2, q8
+        vclt.s8 q14, q3, q8
+
+        vadd.s8  q8,  q0, q9  // diff0 + diff1 + 2
+        vand.8  q15,  q8, q4
+        vadd.s8  q8,  q0, q10 // diff0 + diff1 + 3
+        vand.8   q8,  q8, q11
+        vadd.s8  q0, q15, q8  // offset_idx
+
+        vadd.s8  q8,  q1, q9  // diff0 + diff1 + 2
+        vand.8  q15,  q8, q5
+        vadd.s8  q8,  q1, q10 // diff0 + diff1 + 3
+        vand.8   q8,  q8, q12
+        vadd.s8  q1, q15, q8  // offset_idx
+
+        vadd.s8  q8,  q2, q9  // diff0 + diff1 + 2 + 2
+        vand.8  q15,  q8, q6
+        vadd.s8  q8,  q2, q10 // diff0 + diff1 + 2 + 3
+        vand.8   q8,  q8, q13
+        vadd.s8  q2, q15, q8  // offset_idx
+
+        vadd.s8  q8,  q3, q9  // diff0 + diff1 + 2 + 2
+        vand.8  q15,  q8, q7
+        vadd.s8  q8,  q3, q10 // diff0 + diff1 + 2 + 3
+        vand.8   q8,  q8, q14
+        vadd.s8  q3, q15, q8  // offset_idx
+        // TODO: load only once
+        vld1.8   d16, [r5]
+
+        vtbl.8   d0, {d16}, d0
+        vtbl.8   d1, {d16}, d1
+        vtbl.8   d2, {d16}, d2
+        vtbl.8   d3, {d16}, d3
+        vtbl.8   d4, {d16}, d4
+        vtbl.8   d5, {d16}, d5
+        vtbl.8   d6, {d16}, d6
+        vtbl.8   d7, {d16}, d7
+
+        // TODO: load only once
+        // load c again
+        sub     r1, r3
+        sub     r1, r3
+        vld1.8  {q4-q5}, [r1]!
+        vld1.8  {q6-q7}, [r1], r3
+        sub     r1, #32
+
+        vmovl.u8   q8, d8
+        vmovl.u8   q9, d9
+        vmovl.u8  q10, d10
+        vmovl.u8  q11, d11
+        vmovl.u8  q12, d12
+        vmovl.u8  q13, d13
+        vmovl.u8  q14, d14
+        vmovl.u8  q15, d15
+
+        vaddw.s8  q8, d0
+        vaddw.s8  q9, d1
+        vaddw.s8 q10, d2
+        vaddw.s8 q11, d3
+        vaddw.s8 q12, d4
+        vaddw.s8 q13, d5
+        vaddw.s8 q14, d6
+        vaddw.s8 q15, d7
+
+        vqmovun.s16  d0, q8
+        vqmovun.s16  d1, q9
+        vqmovun.s16  d2, q10
+        vqmovun.s16  d3, q11
+        vqmovun.s16  d4, q12
+        vqmovun.s16  d5, q13
+        vqmovun.s16  d6, q14
+        vqmovun.s16  d7, q15
+
+        vstm r0, {q0-q3}
+        add  r0, r2
+        bne   1b
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
-- 
2.5.0


From 402e2bd1c5ad659c757bf9734abe6331904fb9e2 Mon Sep 17 00:00:00 2001
From: Seppo Tomperi <seppo.tomperi@vtt.fi>
Date: Tue, 16 Dec 2014 16:28:25 +0200
Subject: [PATCH 3/9] Added SAO edge offset for ARM NEON w32 and w64

---
 libavcodec/arm/hevcdsp_init_neon.c |  46 +++-
 libavcodec/arm/hevcdsp_sao_neon.S  | 510 +++++++++++++++++++++++++++++++------
 2 files changed, 474 insertions(+), 82 deletions(-)

diff --git a/libavcodec/arm/hevcdsp_init_neon.c b/libavcodec/arm/hevcdsp_init_neon.c
index c7b5404..c32940e 100644
--- a/libavcodec/arm/hevcdsp_init_neon.c
+++ b/libavcodec/arm/hevcdsp_init_neon.c
@@ -49,7 +49,16 @@ void ff_hevc_sao_band_w8_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_d
 void ff_hevc_sao_band_w16_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
 void ff_hevc_sao_band_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
 void ff_hevc_sao_band_w64_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
+
+void ff_hevc_sao_edge_eo0_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
+void ff_hevc_sao_edge_eo1_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
+void ff_hevc_sao_edge_eo2_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
+void ff_hevc_sao_edge_eo3_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
+
+void ff_hevc_sao_edge_eo0_w64_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
 void ff_hevc_sao_edge_eo1_w64_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
+void ff_hevc_sao_edge_eo2_w64_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
+void ff_hevc_sao_edge_eo3_w64_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
 
 #define PUT_PIXELS(name) \
     void name(int16_t *dst, uint8_t *src, \
@@ -222,9 +231,40 @@ static void ff_hevc_sao_edge_neon_wrapper(uint8_t *_dst /* align 16 */, uint8_t
     stride_src /= sizeof(pixel);
     stride_dst /= sizeof(pixel);
 
-    if (eo == 1 && width == 64) {
-        ff_hevc_sao_edge_eo1_w64_neon_8(dst, src, stride_dst, stride_src, height, sao_offset_val);
-    } else {
+    switch (width) {
+    case 32:
+        switch(eo) {
+        case 0:
+            ff_hevc_sao_edge_eo0_w32_neon_8(dst, src, stride_dst, stride_src, height, sao_offset_val);
+            break;
+        case 1:
+            ff_hevc_sao_edge_eo1_w32_neon_8(dst, src, stride_dst, stride_src, height, sao_offset_val);
+            break;
+        case 2:
+            ff_hevc_sao_edge_eo2_w32_neon_8(dst, src, stride_dst, stride_src, height, sao_offset_val);
+            break;
+        case 3:
+            ff_hevc_sao_edge_eo3_w32_neon_8(dst, src, stride_dst, stride_src, height, sao_offset_val);
+            break;
+        }
+        break;
+    case 64:
+        switch(eo) {
+        case 0:
+            ff_hevc_sao_edge_eo0_w64_neon_8(dst, src, stride_dst, stride_src, height, sao_offset_val);
+            break;
+        case 1:
+            ff_hevc_sao_edge_eo1_w64_neon_8(dst, src, stride_dst, stride_src, height, sao_offset_val);
+            break;
+        case 2:
+            ff_hevc_sao_edge_eo2_w64_neon_8(dst, src, stride_dst, stride_src, height, sao_offset_val);
+            break;
+        case 3:
+            ff_hevc_sao_edge_eo3_w64_neon_8(dst, src, stride_dst, stride_src, height, sao_offset_val);
+            break;
+        }
+        break;
+    default:
         a_stride = pos[eo][0][0] + pos[eo][0][1] * stride_src;
         b_stride = pos[eo][1][0] + pos[eo][1][1] * stride_src;
         for (y = 0; y < height; y++) {
diff --git a/libavcodec/arm/hevcdsp_sao_neon.S b/libavcodec/arm/hevcdsp_sao_neon.S
index 5ec2de9..4687012 100644
--- a/libavcodec/arm/hevcdsp_sao_neon.S
+++ b/libavcodec/arm/hevcdsp_sao_neon.S
@@ -202,27 +202,7 @@ function ff_hevc_sao_band_w64_neon_8, export=1
         bx lr
 endfunc
 
-function ff_hevc_sao_edge_eo1_w64_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // sao_offset_val_table
-        ldr    r6, =0x02
-        vpush {d8-d15}
-1:      subs    r4, #1
-        // load a
-        sub     r1, r3
-        vld1.8  {q0-q1}, [r1]!
-        vld1.8  {q2-q3}, [r1], r3
-        sub     r1, #32
-        // load c
-        vld1.8  {q4-q5}, [r1]!
-        vld1.8  {q6-q7}, [r1], r3
-        sub     r1, #32
-        // load b
-        vld1.8  {q8-q9}, [r1]!
-        vld1.8  {q10-q11}, [r1], r3
-        sub     r1, #32
-
+.macro edge_w64_body
         vcgt.u8 q12, q4, q0 // c > a -> -1 , otherwise 0
         vcgt.u8 q0,  q0, q4 // a > c -> -1 , otherwise 0
         vcgt.u8 q13, q5, q1
@@ -251,69 +231,61 @@ function ff_hevc_sao_edge_eo1_w64_neon_8, export=1
         vsub.s8 q2, q10, q2
         vsub.s8 q3, q11, q3
 
-        veor.u8 q8, q8  // zero register
-        vdup.s8 q9, r6  // 2 to all elements
-        add     r6, #1
-        vdup.s8 q10, r6 // 3 to all elements
-        sub     r6, #1
-
         vadd.s8 q0, q12 //diff0 + diff1
         vadd.s8 q1, q13
         vadd.s8 q2, q14
         vadd.s8 q3, q15
 
-        vcgt.s8 q4, q0, q8 // diff0 + diff1 > 0
-        vcgt.s8 q5, q1, q8
-        vcgt.s8 q6, q2, q8
-        vcgt.s8 q7, q3, q8
-
-        vclt.s8 q11, q0, q8 // diff0 + diff1 < 0
-        vclt.s8 q12, q1, q8
-        vclt.s8 q13, q2, q8
-        vclt.s8 q14, q3, q8
-
-        vadd.s8  q8,  q0, q9  // diff0 + diff1 + 2
-        vand.8  q15,  q8, q4
-        vadd.s8  q8,  q0, q10 // diff0 + diff1 + 3
-        vand.8   q8,  q8, q11
-        vadd.s8  q0, q15, q8  // offset_idx
-
-        vadd.s8  q8,  q1, q9  // diff0 + diff1 + 2
-        vand.8  q15,  q8, q5
-        vadd.s8  q8,  q1, q10 // diff0 + diff1 + 3
-        vand.8   q8,  q8, q12
-        vadd.s8  q1, q15, q8  // offset_idx
-
-        vadd.s8  q8,  q2, q9  // diff0 + diff1 + 2 + 2
-        vand.8  q15,  q8, q6
-        vadd.s8  q8,  q2, q10 // diff0 + diff1 + 2 + 3
-        vand.8   q8,  q8, q13
-        vadd.s8  q2, q15, q8  // offset_idx
-
-        vadd.s8  q8,  q3, q9  // diff0 + diff1 + 2 + 2
-        vand.8  q15,  q8, q7
-        vadd.s8  q8,  q3, q10 // diff0 + diff1 + 2 + 3
-        vand.8   q8,  q8, q14
-        vadd.s8  q3, q15, q8  // offset_idx
-        // TODO: load only once
-        vld1.8   d16, [r5]
-
-        vtbl.8   d0, {d16}, d0
-        vtbl.8   d1, {d16}, d1
-        vtbl.8   d2, {d16}, d2
-        vtbl.8   d3, {d16}, d3
-        vtbl.8   d4, {d16}, d4
-        vtbl.8   d5, {d16}, d5
-        vtbl.8   d6, {d16}, d6
-        vtbl.8   d7, {d16}, d7
-
-        // TODO: load only once
-        // load c again
-        sub     r1, r3
-        sub     r1, r3
-        vld1.8  {q4-q5}, [r1]!
-        vld1.8  {q6-q7}, [r1], r3
-        sub     r1, #32
+        vdup.s8 q9, r6 // 3 to all elements
+        sub     r6, #1
+
+        vclt.s8 q12, q0, #0 // diff0 + diff1 < 0
+        vclt.s8 q13, q1, #0
+        vclt.s8 q14, q2, #0
+        vclt.s8 q15, q3, #0
+
+        vadd.s8  q8,  q0, q9 // diff0 + diff1 + 3
+        vadd.s8  q10,  q1, q9
+        vand.8   q12, q8, q12 // if (diff0 + diff1 < 0) then (diff0 + diff1 + 3) else 0
+        vand.8   q13, q10, q13
+        vadd.s8  q8,  q2, q9
+        vadd.s8  q10,  q3, q9
+        vand.8   q14, q8, q14
+        vand.8   q15, q10, q15
+
+        vdup.s8 q9, r6  // 2 to all elements
+        add     r6, #1
+
+        vcgt.s8  q10, q0, #0 // diff0 + diff1 > 0
+        vadd.s8   q8, q0, q9 // diff0 + diff1 + 2
+        vand.8   q11, q8, q10 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
+        vcgt.s8  q10, q1, #0
+        vadd.s8   q0, q11, q12  // offset_idx
+
+        vadd.s8   q8, q1, q9 // diff0 + diff1 + 2
+        vcgt.s8  q12, q2, #0
+        vand.8   q11, q8, q10 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
+        vadd.s8   q8, q2, q9 // diff0 + diff1 + 2
+        vadd.s8   q1, q11, q13
+
+        vand.8   q11, q8, q12 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
+        vcgt.s8  q10, q3, #0
+        vadd.s8   q2, q11, q14
+
+        vadd.s8   q8, q3, q9 // diff0 + diff1 + 2
+        vmov.32  d18[0], r7  // load offset table from general registers
+        vand.8   q11, q8, q10 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
+        vmov.32  d18[1], r5  // load rest of offset table
+        vadd.s8   q3, q11, q15
+
+        vtbl.8   d0, {d18}, d0
+        vtbl.8   d1, {d18}, d1
+        vtbl.8   d2, {d18}, d2
+        vtbl.8   d3, {d18}, d3
+        vtbl.8   d4, {d18}, d4
+        vtbl.8   d5, {d18}, d5
+        vtbl.8   d6, {d18}, d6
+        vtbl.8   d7, {d18}, d7
 
         vmovl.u8   q8, d8
         vmovl.u8   q9, d9
@@ -344,8 +316,388 @@ function ff_hevc_sao_edge_eo1_w64_neon_8, export=1
 
         vstm r0, {q0-q3}
         add  r0, r2
+.endm
+
+.macro edge_w32_body
+        vcgt.u8 q12, q4, q0 // c > a -> -1 , otherwise 0
+        vcgt.u8 q0,  q0, q4 // a > c -> -1 , otherwise 0
+        vcgt.u8 q13, q5, q1
+        vcgt.u8 q1,  q1, q5
+
+        vsub.s8 q12, q0, q12 // diff0
+        vcgt.u8  q0,  q4, q8 // c > b
+        vsub.s8 q13, q1, q13 // diff0 part 2
+
+        vcgt.u8  q6,  q8, q4 // b > c
+        vcgt.u8  q1,  q5, q9
+        vcgt.u8  q7,  q9, q5
+
+        vsub.s8 q0, q6, q0 // diff1
+        vsub.s8 q1, q7, q1 // diff1 part 2
+        vadd.s8 q0, q12 //diff0 + diff1
+
+        vdup.s8 q7, r6 // 3 to all elements
+        sub     r6, #1
+        vadd.s8 q1, q13
+
+        vclt.s8 q12, q0, #0 // diff0 + diff1 < 0
+        vclt.s8 q13, q1, #0
+
+        vadd.s8  q6,  q0, q7 // diff0 + diff1 + 3
+        vadd.s8  q10,  q1, q7
+        vdup.s8 q7, r6  // 2 to all elements
+        add     r6, #1
+        vand.8   q12, q6, q12 // if (diff0 + diff1 < 0) then (diff0 + diff1 + 3) else 0
+        vand.8   q13, q10, q13
+
+
+        vcgt.s8  q10, q0, #0 // diff0 + diff1 > 0
+        vadd.s8   q6, q0, q7 // diff0 + diff1 + 2
+        vand.8   q11, q6, q10 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
+        vcgt.s8  q10, q1, #0
+        vadd.s8   q0, q11, q12  // offset_idx
+
+        vadd.s8   q6, q1, q7 // diff0 + diff1 + 2
+        vmov.32  d14[0], r7  // load offset table from general registers
+        vand.8   q11, q6, q10 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
+        vmov.32  d14[1], r5  // load rest of offset table
+        vadd.s8   q1, q11, q13
+
+        vtbl.8   d0, {d14}, d0
+        vtbl.8   d1, {d14}, d1
+        vtbl.8   d2, {d14}, d2
+        vtbl.8   d3, {d14}, d3
+
+        vmovl.u8   q6, d8
+        vmovl.u8   q7, d9
+        vmovl.u8  q10, d10
+        vmovl.u8  q11, d11
+
+        vaddw.s8  q6, d0
+        vaddw.s8  q7, d1
+        vaddw.s8 q10, d2
+        vaddw.s8 q11, d3
+
+        vqmovun.s16  d0, q6
+        vqmovun.s16  d1, q7
+        vqmovun.s16  d2, q10
+        vqmovun.s16  d3, q11
+
+        vstm r0, {q0-q1}
+        add  r0, r2
+.endm
+
+function ff_hevc_sao_edge_eo0_w64_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // sao_offset_val_table
+        ldr    r6, =0x03
+        ldr    r7, [r5]
+        add    r5, #4
+        ldr    r5, [r5]
+        vpush {d8-d15}
+        sub    r1, #8
+1:      subs    r4, #1
+        vld1.64  {q10-q11}, [r1]!
+        vld1.64  {q12-q13}, [r1]!
+        vld1.64  {q14}, [r1], r3
+        sub      r1, #64
+        // load a
+        vext.8 q0, q10, q11, #7
+        vext.8 q1, q11, q12, #7
+        vext.8 q2, q12, q13, #7
+        vext.8 q3, q13, q14, #7
+        // load c
+        vext.8 q4, q10, q11, #8
+        vext.8 q5, q11, q12, #8
+        vext.8 q6, q12, q13, #8
+        vext.8 q7, q13, q14, #8
+        // load b
+        vext.8 q8, q10, q11, #9
+        vext.8 q9, q11, q12, #9
+        vext.8 q10, q12, q13, #9
+        vext.8 q11, q13, q14, #9
+        edge_w64_body
+        bne   1b
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
+
+function ff_hevc_sao_edge_eo1_w64_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // sao_offset_val_table
+        ldr    r6, =0x03
+        ldr    r7, [r5]
+        add    r5, #4
+        ldr    r5, [r5]
+        vpush {d8-d15}
+        sub     r1, r3
+        // load a
+        vld1.8  {q0-q1}, [r1]!
+        vld1.8  {q2-q3}, [r1], r3
+        sub     r1, #32
+1:      subs    r4, #1
+        // load c
+        vld1.8  {q4-q5}, [r1]!
+        vld1.8  {q6-q7}, [r1], r3
+        sub     r1, #32
+        // load b
+        vld1.8  {q8-q9}, [r1]!
+        vld1.8  {q10-q11}, [r1]
+        sub     r1, #32
+        edge_w64_body
+        // copy c to a
+        vmov.64 q0, q4
+        vmov.64 q1, q5
+        vmov.64 q2, q6
+        vmov.64 q3, q7
         bne   1b
         vpop  {d8-d15}
         pop   {r4-r8}
         bx lr
 endfunc
+
+function ff_hevc_sao_edge_eo2_w64_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // sao_offset_val_table
+        ldr    r6, =0x03
+        ldr    r7, [r5]
+        add    r5, #4
+        ldr    r5, [r5]
+        vpush {d8-d15}
+1:      sub     r1, r3
+        // load a
+        // TODO: fix unaligned load
+        //       don't reload a like in eo1
+        sub     r1, #1
+        vld1.8  {q0-q1}, [r1]!
+        vld1.8  {q2-q3}, [r1], r3
+        sub     r1, #31
+        subs    r4, #1
+        // load c
+        vld1.8  {q4-q5}, [r1]!
+        vld1.8  {q6-q7}, [r1], r3
+        sub     r1, #32
+        // load b
+        add     r1, #1
+        vld1.8  {q8-q9}, [r1]!
+        vld1.8  {q10-q11}, [r1]
+        sub     r1, #33
+        edge_w64_body
+        // copy c to a
+        vmov.64 q0, q4
+        vmov.64 q1, q5
+        vmov.64 q2, q6
+        vmov.64 q3, q7
+        bne   1b
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
+
+function ff_hevc_sao_edge_eo3_w64_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // sao_offset_val_table
+        ldr    r6, =0x03
+        ldr    r7, [r5]
+        add    r5, #4
+        ldr    r5, [r5]
+        vpush {d8-d15}
+1:      sub     r1, r3
+        // load a
+        // TODO: fix unaligned load
+        //       don't reload a like in eo1
+        add     r1, #1
+        vld1.8  {q0-q1}, [r1]!
+        vld1.8  {q2-q3}, [r1], r3
+        sub     r1, #33
+        subs    r4, #1
+        // load c
+        vld1.8  {q4-q5}, [r1]!
+        vld1.8  {q6-q7}, [r1], r3
+        sub     r1, #32
+        // load b
+        sub     r1, #1
+        vld1.8  {q8-q9}, [r1]!
+        vld1.8  {q10-q11}, [r1]
+        sub     r1, #31
+        edge_w64_body
+        // copy c to a
+        vmov.64 q0, q4
+        vmov.64 q1, q5
+        vmov.64 q2, q6
+        vmov.64 q3, q7
+        bne   1b
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
+
+function ff_hevc_sao_edge_eo0_w32_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // sao_offset_val_table
+        ldr    r6, =0x03
+        ldr    r7, [r5]
+        add    r5, #4
+        ldr    r5, [r5]
+        vpush {d8-d15}
+        sub    r1, #8 // load 8 extra bytes
+1:      subs    r4, #1
+        vld1.8  {q10-q11}, [r1]
+        add    r1, #32
+        vld1.8  {q12}, [r1], r3 // only first 9 bytes are used
+        sub    r1, #32
+        // a
+        vext.8  q0, q10, q11, #7
+        vext.8  q1, q11, q12, #7
+        // c
+        vext.8  q4, q10, q11, #8
+        vext.8  q5, q11, q12, #8
+        // b
+        vext.8  q8, q10, q11, #9
+        vext.8  q9, q11, q12, #9
+        edge_w32_body
+        bne   1b
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
+
+function ff_hevc_sao_edge_eo1_w32_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // sao_offset_val_table
+        ldr    r6, =0x03
+        ldr    r7, [r5]
+        add    r5, #4
+        ldr    r5, [r5]
+        vpush {d8-d15}
+        // load a
+        sub     r1, r3
+        vld1.8  {q0-q1}, [r1], r3
+        // load c
+        vld1.8  {q4-q5}, [r1], r3
+1:      subs    r4, #1
+        // load b
+        vld1.8  {q8-q9}, [r1], r3
+        edge_w32_body
+        // inputs for next loop iteration
+        // a
+        vmov.64 q0, q4
+        vmov.64 q1, q5
+        // c
+        vmov.64 q4, q8
+        vmov.64 q5, q9
+        bne   1b
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
+
+function ff_hevc_sao_edge_eo2_w32_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // sao_offset_val_table
+        ldr    r6, =0x03
+        ldr    r7, [r5]
+        add    r5, #4
+        ldr    r5, [r5]
+        vpush {d8-d15}
+        // load a
+        sub     r1, r3
+        sub    r1, #8
+        vld1.8  {q10-q11}, [r1]
+        add    r1, #32
+        vld1.8  {q12}, [r1], r3
+        sub    r1, #32
+        vext.8  q0, q10, q11, #7
+        vext.8  q1, q11, q12, #7
+        // load c
+        vld1.8  {q10-q11}, [r1]
+        add    r1, #32
+        vld1.8  {q12}, [r1], r3
+        sub    r1, #32
+        vext.8  q4, q10, q11, #8
+        vext.8  q5, q11, q12, #8
+        vext.8  q2, q10, q11, #7
+1:      subs    r4, #1
+        // load b
+        vld1.8  {q10-q11}, [r1]
+        add    r1, #32
+        vld1.8  {q12}, [r1], r3
+        sub    r1, #32
+        vext.8  q8, q10, q11, #9
+        vext.8  q9, q11, q12, #9
+        vext.8  q14, q10, q11, #8
+        vext.8  q15, q11, q12, #8
+        vext.8  q3, q10, q11, #7
+        edge_w32_body
+        // inputs for next loop iteration
+        // a
+        vmov.8 q0, q2
+        vext.8 q1, q4, q5, #15
+        // c
+        vmov.8  q4, q14
+        vmov.8  q5, q15
+        vmov.8  q2, q3
+        bne   1b
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
+
+function ff_hevc_sao_edge_eo3_w32_neon_8, export=1
+        push  {r4-r8}
+        ldr    r4, [sp, #20] // height
+        ldr    r5, [sp, #24] // sao_offset_val_table
+        ldr    r6, =0x03
+        ldr    r7, [r5]
+        add    r5, #4
+        sub    r1, r3
+        ldr    r5, [r5]
+        sub    r1, #8
+        vpush {d8-d15}
+        // load a
+        vld1.8  {q10-q11}, [r1]
+        add    r1, #32
+        vld1.8  {q12}, [r1], r3
+        sub    r1, #32
+        vext.8  q0, q10, q11, #9
+        vext.8  q1, q11, q12, #9
+        // load c
+        vld1.8  {q10-q11}, [r1]
+        add    r1, #32
+        vld1.8  {q12}, [r1], r3
+        sub    r1, #32
+        vext.8  q4, q10, q11, #8
+        vext.8  q5, q11, q12, #8
+        vext.8  q2, q12, q11, #8
+1:      subs    r4, #1
+        // load b
+        vld1.8  {q10-q11}, [r1]
+        add    r1, #32
+        vld1.8  {q12}, [r1], r3
+        sub    r1, #32
+        vext.8  q8, q10, q11, #7
+        vext.8  q9, q11, q12, #7
+        vext.8  q3, q12, q10, #7
+        edge_w32_body
+        // inputs for next loop iteration
+        // a
+        vext.8 q0, q4, q5, #1
+        vext.8 q1, q5, q2, #1
+        // c
+        vext.8  q4, q8, q9, #1
+        vext.8  q5, q9, q3, #1
+        vext.8  q2, q3, q1, #1
+        bne   1b
+        vpop  {d8-d15}
+        pop   {r4-r8}
+        bx lr
+endfunc
+
-- 
2.5.0


From 1898d052a73370166d57e17cc7c52b7275887df3 Mon Sep 17 00:00:00 2001
From: Seppo Tomperi <seppo.tomperi@vtt.fi>
Date: Fri, 19 Dec 2014 09:44:10 +0200
Subject: [PATCH 4/9] Improved SAO band NEON opimizations made SAO buffer 16
 byte aligned added alignment hints to loads and stores optimized register
 usage in SAO band neon assembly

---
 libavcodec/arm/hevcdsp_sao_neon.S | 212 +++++++++++++++-----------------------
 1 file changed, 82 insertions(+), 130 deletions(-)

diff --git a/libavcodec/arm/hevcdsp_sao_neon.S b/libavcodec/arm/hevcdsp_sao_neon.S
index 4687012..ac21013 100644
--- a/libavcodec/arm/hevcdsp_sao_neon.S
+++ b/libavcodec/arm/hevcdsp_sao_neon.S
@@ -22,120 +22,84 @@
 #include "neon.S"
 
 function ff_hevc_sao_band_w8_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // offset_table
-        vpush {d8-d15}
-        vld1.8  {q0, q1}, [r5] // offset table
+        ldr      r12, [sp, #4]    // offset_table address
+        vld1.8   {q0, q1}, [r12]  // offset table
+        ldr      r12, [sp, #0]    // height
 
-1:      subs    r4, #1
-        vld1.8   {d24}, [r1], r3
+1:      subs     r12, #1
+        vld1.8   {d24}, [r1,:64], r3
         vshr.u8  d16, d24, #3
         vtbl.8   d16, {q0, q1}, d16
-        vmovl.s8 q2, d16
         vmovl.u8 q6, d24
-        vadd.s16 q2, q6
+        vaddw.s8 q6, d16
         vqmovun.s16 d4, q2
-        vst1.8  {d4}, [r0], r2
+        vst1.8  {d4}, [r0,:64], r2
         bne    1b
 
-        vpop  {d8-d15}
-        pop   {r4-r8}
         bx lr
 endfunc
 
 function ff_hevc_sao_band_w16_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // offset_table
-        vpush {d8-d15}
-        vld1.8  {q0, q1}, [r5] // offset table
-
-1:      subs    r4, #1
-        vld1.8  {q12}, [r1], r3
+        ldr      r12, [sp, #4]    // offset_table address
+        vld1.8   {q0, q1}, [r12]  // offset table
+        ldr      r12, [sp, #0]    // height
 
+1:      subs     r12, #1
+        vld1.8  {q12}, [r1,:128], r3
         vshr.u8   q8, q12, #3
-
         vtbl.8  d16, {q0, q1}, d16
         vtbl.8  d17, {q0, q1}, d17
-
-        vmovl.s8 q2, d16
-        vmovl.s8 q3, d17
-
-        vmovl.u8 q6, d24
-        vmovl.u8 q7, d25
-
-        vadd.s16 q2, q6
-        vadd.s16 q3, q7
-
-        vqmovun.s16 d4, q2
-        vqmovun.s16 d5, q3
-
-        vstm.8   r0, {q2}
-        add    r0, r2
+        vmovl.u8 q10, d24
+        vmovl.u8 q11, d25
+        vaddw.s8 q10, d16
+        vaddw.s8 q11, d17
+        vqmovun.s16 d4, q10
+        vqmovun.s16 d5, q11
+        vst1.8   {q2}, [r0,:128], r2
         bne    1b
 
-        vpop  {d8-d15}
-        pop   {r4-r8}
         bx lr
 endfunc
 
 function ff_hevc_sao_band_w32_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // offset_table
-        vpush {d8-d15}
-        vld1.8  {q0, q1}, [r5] // offset table
-
-1:      subs    r4, #1
-        vld1.8  {q12-q13}, [r1], r3
-
-        vshr.u8   q8, q12, #3
-        vshr.u8   q9, q13, #3
-
-        vtbl.8  d16, {q0, q1}, d16
-        vtbl.8  d17, {q0, q1}, d17
-        vtbl.8  d18, {q0, q1}, d18
-        vtbl.8  d19, {q0, q1}, d19
-
-        vmovl.s8 q2, d16
-        vmovl.s8 q3, d17 // q8 free
-        vmovl.s8 q4, d18
-        vmovl.s8 q5, d19 // q9 free
-
-        vmovl.u8 q6, d24
-        vmovl.u8 q7, d25 // q12 free
-        vmovl.u8 q8, d26
-        vmovl.u8 q9, d27 // q13 free
-
-        vadd.s16 q2, q6
-        vadd.s16 q3, q7
-        vadd.s16 q4, q8
-        vadd.s16 q5, q9
-
-        vqmovun.s16 d4, q2
-        vqmovun.s16 d5, q3
-        vqmovun.s16 d6, q4 // q4 free
-        vqmovun.s16 d7, q5 // q5 free
-
-        vst1.8 {q2-q3}, [r0], r2
-        bne    1b
-
-        vpop  {d8-d15}
-        pop   {r4-r8}
-        bx lr
+        ldr      r12, [sp, #4]    // offset_table address
+        vld1.8   {q0, q1}, [r12]  // offset table
+        ldr      r12, [sp, #0]    // height
+
+1:      subs     r12, #1
+        vld1.8   {q2-q3}, [r1,:128], r3
+        vshr.u8  q8, q2, #3
+        vshr.u8  q9, q3, #3
+        vtbl.8   d16, {q0, q1}, d16
+        vtbl.8   d17, {q0, q1}, d17
+        vtbl.8   d18, {q0, q1}, d18
+        vtbl.8   d19, {q0, q1}, d19
+        vmovl.u8 q12, d4
+        vmovl.u8 q13, d5
+        vmovl.u8 q14, d6
+        vmovl.u8 q15, d7
+        vaddw.s8 q12, d16
+        vaddw.s8 q13, d17
+        vaddw.s8 q14, d18
+        vaddw.s8 q15, d19
+        vqmovun.s16 d4, q12
+        vqmovun.s16 d5, q13
+        vqmovun.s16 d6, q14
+        vqmovun.s16 d7, q15
+        vst1.8   {q2-q3}, [r0,:128], r2
+        bne      1b
+
+        bx       lr
 endfunc
 
 function ff_hevc_sao_band_w64_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // offset_table
-        vpush {d8-d15}
-        vld1.8  {q0, q1}, [r5] // offset table
+        ldr      r12, [sp, #4]    // offset_table address
+        vld1.8   {q0, q1}, [r12]  // offset table
+        ldr      r12, [sp, #0]    // height
 
-1:      subs    r4, #1
-        vld1.8  {q12-q13}, [r1]!
-        vld1.8  {q14-q15}, [r1], r3
+1:      subs     r12, #1
+        vld1.8  {q12-q13}, [r1,:128]!
+        vld1.8  {q14-q15}, [r1,:128], r3
         sub     r1, #32
 
         vshr.u8   q8, q12, #3
@@ -152,53 +116,41 @@ function ff_hevc_sao_band_w64_neon_8, export=1
         vtbl.8  d22, {q0, q1}, d22
         vtbl.8  d23, {q0, q1}, d23
 
-        vmovl.s8 q2, d16
-        vmovl.s8 q3, d17 // q8 free
-        vmovl.s8 q4, d18
-        vmovl.s8 q5, d19 // q9 free
+        vmovl.u8 q2, d24
+        vmovl.u8 q3, d25
+        vmovl.u8 q12, d26
+        vmovl.u8 q13, d27
 
-        vmovl.u8 q6, d24
-        vmovl.u8 q7, d25 // q12 free
-        vmovl.u8 q8, d26
-        vmovl.u8 q9, d27 // q13 free
-
-        vadd.s16 q2, q6
-        vadd.s16 q3, q7
-        vadd.s16 q4, q8
-        vadd.s16 q5, q9
+        vaddw.s8 q2, d16
+        vaddw.s8 q3, d17
+        vaddw.s8 q12, d18
+        vaddw.s8 q13, d19
 
         vqmovun.s16 d4, q2
         vqmovun.s16 d5, q3
-        vqmovun.s16 d6, q4 // q4 free
-        vqmovun.s16 d7, q5 // q5 free
-
-        // free q4 -q9, q12 - q13
-        vmovl.s8 q4, d20
-        vmovl.s8 q5, d21 // q10 free
-        vmovl.s8 q6, d22
-        vmovl.s8 q7, d23 // q11 free
-
-        vmovl.u8  q8, d28
-        vmovl.u8  q9, d29 // q14 free
-        vmovl.u8 q10, d30
-        vmovl.u8 q11, d31 // q15 free
-
-        vadd.s16 q4, q8
-        vadd.s16 q5, q9
-        vadd.s16 q6, q10
-        vadd.s16 q7, q11
-
-        vqmovun.s16  d8, q4
-        vqmovun.s16  d9, q5
-        vqmovun.s16 d10, q6
-        vqmovun.s16 d11, q7
-
-        vstm.8   r0, {q2-q5}
-        add    r0, r2
+        vqmovun.s16 d6, q12
+        vqmovun.s16 d7, q13
+
+        vmovl.u8 q12, d28
+        vmovl.u8 q13, d29
+        vmovl.u8 q14, d30
+        vmovl.u8 q15, d31
+
+        vaddw.s8 q12, d20
+        vaddw.s8 q13, d21
+        vaddw.s8 q14, d22
+        vaddw.s8 q15, d23
+
+        vqmovun.s16  d8, q12
+        vqmovun.s16  d9, q13
+        vqmovun.s16 d10, q14
+        vqmovun.s16 d11, q15
+
+        vst1.8     {q2-q3}, [r0,:128]!
+        vst1.8     {q4-q5}, [r0,:128], r2
+        sub    r0, #32
         bne    1b
 
-        vpop  {d8-d15}
-        pop   {r4-r8}
         bx lr
 endfunc
 
-- 
2.5.0


From 26bd536800db2f50ff6a021e1fda0d0394d1ea01 Mon Sep 17 00:00:00 2001
From: Seppo Tomperi <seppo.tomperi@vtt.fi>
Date: Mon, 29 Dec 2014 15:00:49 +0200
Subject: [PATCH 5/9] better code reuse in NEON SAO band

---
 libavcodec/arm/hevcdsp_init_neon.c |  16 ++--
 libavcodec/arm/hevcdsp_sao_neon.S  | 155 +++++++++++++------------------------
 2 files changed, 61 insertions(+), 110 deletions(-)

diff --git a/libavcodec/arm/hevcdsp_init_neon.c b/libavcodec/arm/hevcdsp_init_neon.c
index c32940e..6379810 100644
--- a/libavcodec/arm/hevcdsp_init_neon.c
+++ b/libavcodec/arm/hevcdsp_init_neon.c
@@ -45,10 +45,10 @@ void ff_hevc_transform_add_16x16_neon_8(uint8_t *_dst, int16_t *coeffs,
 void ff_hevc_transform_add_32x32_neon_8(uint8_t *_dst, int16_t *coeffs,
                                       ptrdiff_t stride);
 
-void ff_hevc_sao_band_w8_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
-void ff_hevc_sao_band_w16_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
-void ff_hevc_sao_band_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
-void ff_hevc_sao_band_w64_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t * offset_table);
+void ff_hevc_sao_band_w8_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int8_t * offset_table, int height);
+void ff_hevc_sao_band_w16_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int8_t * offset_table, int height);
+void ff_hevc_sao_band_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int8_t * offset_table, int height);
+void ff_hevc_sao_band_w64_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int8_t * offset_table, int height);
 
 void ff_hevc_sao_edge_eo0_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
 void ff_hevc_sao_edge_eo1_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
@@ -185,16 +185,16 @@ static void ff_hevc_sao_band_neon_wrapper(uint8_t *_dst, uint8_t *_src, ptrdiff_
 
     switch(width){
     case 8:
-        ff_hevc_sao_band_w8_neon_8(_dst, _src, stride_dst, stride_src, height, offset_table);
+        ff_hevc_sao_band_w8_neon_8(_dst, _src, stride_dst, stride_src, offset_table, height);
         break;
     case 16:
-        ff_hevc_sao_band_w16_neon_8(_dst, _src, stride_dst, stride_src, height, offset_table);
+        ff_hevc_sao_band_w16_neon_8(_dst, _src, stride_dst, stride_src, offset_table, height);
         break;
     case 32:
-        ff_hevc_sao_band_w32_neon_8(_dst, _src, stride_dst, stride_src, height, offset_table);
+        ff_hevc_sao_band_w32_neon_8(_dst, _src, stride_dst, stride_src, offset_table, height);
         break;
     case 64:
-        ff_hevc_sao_band_w64_neon_8(_dst, _src, stride_dst, stride_src, height, offset_table);
+        ff_hevc_sao_band_w64_neon_8(_dst, _src, stride_dst, stride_src, offset_table, height);
         break;
     default:
         for (y = 0; y < height; y++) {
diff --git a/libavcodec/arm/hevcdsp_sao_neon.S b/libavcodec/arm/hevcdsp_sao_neon.S
index ac21013..8852550 100644
--- a/libavcodec/arm/hevcdsp_sao_neon.S
+++ b/libavcodec/arm/hevcdsp_sao_neon.S
@@ -21,53 +21,13 @@
 #include "libavutil/arm/asm.S"
 #include "neon.S"
 
-function ff_hevc_sao_band_w8_neon_8, export=1
-        ldr      r12, [sp, #4]    // offset_table address
+.macro init_sao_band
+        ldr      r12, [sp, #0]    // offset_table address
         vld1.8   {q0, q1}, [r12]  // offset table
-        ldr      r12, [sp, #0]    // height
-
-1:      subs     r12, #1
-        vld1.8   {d24}, [r1,:64], r3
-        vshr.u8  d16, d24, #3
-        vtbl.8   d16, {q0, q1}, d16
-        vmovl.u8 q6, d24
-        vaddw.s8 q6, d16
-        vqmovun.s16 d4, q2
-        vst1.8  {d4}, [r0,:64], r2
-        bne    1b
-
-        bx lr
-endfunc
-
-function ff_hevc_sao_band_w16_neon_8, export=1
-        ldr      r12, [sp, #4]    // offset_table address
-        vld1.8   {q0, q1}, [r12]  // offset table
-        ldr      r12, [sp, #0]    // height
-
-1:      subs     r12, #1
-        vld1.8  {q12}, [r1,:128], r3
-        vshr.u8   q8, q12, #3
-        vtbl.8  d16, {q0, q1}, d16
-        vtbl.8  d17, {q0, q1}, d17
-        vmovl.u8 q10, d24
-        vmovl.u8 q11, d25
-        vaddw.s8 q10, d16
-        vaddw.s8 q11, d17
-        vqmovun.s16 d4, q10
-        vqmovun.s16 d5, q11
-        vst1.8   {q2}, [r0,:128], r2
-        bne    1b
-
-        bx lr
-endfunc
-
-function ff_hevc_sao_band_w32_neon_8, export=1
-        ldr      r12, [sp, #4]    // offset_table address
-        vld1.8   {q0, q1}, [r12]  // offset table
-        ldr      r12, [sp, #0]    // height
+        ldr      r12, [sp, #4]    // height
+.endm
 
-1:      subs     r12, #1
-        vld1.8   {q2-q3}, [r1,:128], r3
+.macro sao_band_32
         vshr.u8  q8, q2, #3
         vshr.u8  q9, q3, #3
         vtbl.8   d16, {q0, q1}, d16
@@ -86,6 +46,43 @@ function ff_hevc_sao_band_w32_neon_8, export=1
         vqmovun.s16 d5, q13
         vqmovun.s16 d6, q14
         vqmovun.s16 d7, q15
+.endm
+
+function ff_hevc_sao_band_w8_neon_8, export=1
+        init_sao_band
+1:      subs     r12, #4
+        vld1.8   {d4}, [r1,:64], r3
+        vld1.8   {d5}, [r1,:64], r3
+        vld1.8   {d6}, [r1,:64], r3
+        vld1.8   {d7}, [r1,:64], r3
+        sao_band_32
+        vst1.8  {d4}, [r0,:64], r2
+        vst1.8  {d5}, [r0,:64], r2
+        vst1.8  {d6}, [r0,:64], r2
+        vst1.8  {d7}, [r0,:64], r2
+        bne    1b
+
+        bx lr
+endfunc
+
+function ff_hevc_sao_band_w16_neon_8, export=1
+        init_sao_band
+1:      subs     r12, #2
+        vld1.8  {q2}, [r1,:128], r3
+        vld1.8  {q3}, [r1,:128], r3
+        sao_band_32
+        vst1.8   {q2}, [r0,:128], r2
+        vst1.8   {q3}, [r0,:128], r2
+        bne    1b
+
+        bx lr
+endfunc
+
+function ff_hevc_sao_band_w32_neon_8, export=1
+        init_sao_band
+1:      subs     r12, #1
+        vld1.8   {q2-q3}, [r1,:128], r3
+        sao_band_32
         vst1.8   {q2-q3}, [r0,:128], r2
         bne      1b
 
@@ -93,63 +90,17 @@ function ff_hevc_sao_band_w32_neon_8, export=1
 endfunc
 
 function ff_hevc_sao_band_w64_neon_8, export=1
-        ldr      r12, [sp, #4]    // offset_table address
-        vld1.8   {q0, q1}, [r12]  // offset table
-        ldr      r12, [sp, #0]    // height
-
-1:      subs     r12, #1
-        vld1.8  {q12-q13}, [r1,:128]!
-        vld1.8  {q14-q15}, [r1,:128], r3
-        sub     r1, #32
-
-        vshr.u8   q8, q12, #3
-        vshr.u8   q9, q13, #3
-        vshr.u8  q10, q14, #3
-        vshr.u8  q11, q15, #3
-
-        vtbl.8  d16, {q0, q1}, d16
-        vtbl.8  d17, {q0, q1}, d17
-        vtbl.8  d18, {q0, q1}, d18
-        vtbl.8  d19, {q0, q1}, d19
-        vtbl.8  d20, {q0, q1}, d20
-        vtbl.8  d21, {q0, q1}, d21
-        vtbl.8  d22, {q0, q1}, d22
-        vtbl.8  d23, {q0, q1}, d23
-
-        vmovl.u8 q2, d24
-        vmovl.u8 q3, d25
-        vmovl.u8 q12, d26
-        vmovl.u8 q13, d27
-
-        vaddw.s8 q2, d16
-        vaddw.s8 q3, d17
-        vaddw.s8 q12, d18
-        vaddw.s8 q13, d19
-
-        vqmovun.s16 d4, q2
-        vqmovun.s16 d5, q3
-        vqmovun.s16 d6, q12
-        vqmovun.s16 d7, q13
-
-        vmovl.u8 q12, d28
-        vmovl.u8 q13, d29
-        vmovl.u8 q14, d30
-        vmovl.u8 q15, d31
-
-        vaddw.s8 q12, d20
-        vaddw.s8 q13, d21
-        vaddw.s8 q14, d22
-        vaddw.s8 q15, d23
-
-        vqmovun.s16  d8, q12
-        vqmovun.s16  d9, q13
-        vqmovun.s16 d10, q14
-        vqmovun.s16 d11, q15
-
-        vst1.8     {q2-q3}, [r0,:128]!
-        vst1.8     {q4-q5}, [r0,:128], r2
-        sub    r0, #32
-        bne    1b
+        init_sao_band
+1:      subs      r12, #1
+        vld1.8    {q2-q3}, [r1,:128]!
+        sao_band_32
+        vst1.8    {q2-q3}, [r0,:128]!
+        vld1.8    {q2-q3}, [r1,:128], r3
+        sub       r1, #32
+        sao_band_32
+        vst1.8    {q2-q3}, [r0,:128], r2
+        sub       r0, #32
+        bne       1b
 
         bx lr
 endfunc
-- 
2.5.0


From f93646a97bc885b81759e774d04be3781916a3e7 Mon Sep 17 00:00:00 2001
From: Seppo Tomperi <seppo.tomperi@vtt.fi>
Date: Wed, 7 Jan 2015 15:27:38 +0200
Subject: [PATCH 6/9] More SAO NEON optimizations Now uses only 8 bit integers
 for SAO calculations

---
 libavcodec/arm/hevcdsp_init_neon.c |   7 +-
 libavcodec/arm/hevcdsp_sao_neon.S  | 664 +++++++++++++++----------------------
 2 files changed, 272 insertions(+), 399 deletions(-)

diff --git a/libavcodec/arm/hevcdsp_init_neon.c b/libavcodec/arm/hevcdsp_init_neon.c
index 6379810..8d6e863 100644
--- a/libavcodec/arm/hevcdsp_init_neon.c
+++ b/libavcodec/arm/hevcdsp_init_neon.c
@@ -225,7 +225,7 @@ static void ff_hevc_sao_edge_neon_wrapper(uint8_t *_dst /* align 16 */, uint8_t
     int x, y;
 
     for (x = 0; x < 5; x++) {
-        sao_offset_val[x] = _sao_offset_val[x];
+        sao_offset_val[x] = _sao_offset_val[edge_idx[x]];
     }
 
     stride_src /= sizeof(pixel);
@@ -271,8 +271,9 @@ static void ff_hevc_sao_edge_neon_wrapper(uint8_t *_dst /* align 16 */, uint8_t
             for (x = 0; x < width; x++) {
                 int diff0         = CMP(src[x], src[x + a_stride]);
                 int diff1         = CMP(src[x], src[x + b_stride]);
-                int offset_val    = edge_idx[2 + diff0 + diff1];
-                dst[x] = av_clip_pixel(src[x] + sao_offset_val[offset_val]);
+                int idx           = diff0 + diff1;
+                if (idx)
+                    dst[x] = av_clip_pixel(src[x] + sao_offset_val[idx+2]);
             }
             src += stride_src;
             dst += stride_dst;
diff --git a/libavcodec/arm/hevcdsp_sao_neon.S b/libavcodec/arm/hevcdsp_sao_neon.S
index 8852550..5fc482b 100644
--- a/libavcodec/arm/hevcdsp_sao_neon.S
+++ b/libavcodec/arm/hevcdsp_sao_neon.S
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Seppo Tomperi <seppo.tomperi@vtt.fi>
+ * Copyright (c) 2014 - 2015 Seppo Tomperi <seppo.tomperi@vtt.fi>
  *
  * This file is part of FFmpeg.
  *
@@ -23,6 +23,7 @@
 
 .macro init_sao_band
         ldr      r12, [sp, #0]    // offset_table address
+        pld      [r1]
         vld1.8   {q0, q1}, [r12]  // offset table
         ldr      r12, [sp, #4]    // height
 .endm
@@ -30,36 +31,31 @@
 .macro sao_band_32
         vshr.u8  q8, q2, #3
         vshr.u8  q9, q3, #3
+        vmov.u8  q14, #128
         vtbl.8   d16, {q0, q1}, d16
         vtbl.8   d17, {q0, q1}, d17
         vtbl.8   d18, {q0, q1}, d18
         vtbl.8   d19, {q0, q1}, d19
-        vmovl.u8 q12, d4
-        vmovl.u8 q13, d5
-        vmovl.u8 q14, d6
-        vmovl.u8 q15, d7
-        vaddw.s8 q12, d16
-        vaddw.s8 q13, d17
-        vaddw.s8 q14, d18
-        vaddw.s8 q15, d19
-        vqmovun.s16 d4, q12
-        vqmovun.s16 d5, q13
-        vqmovun.s16 d6, q14
-        vqmovun.s16 d7, q15
+        vadd.s8  q2, q14
+        vadd.s8  q3, q14
+        vqadd.s8 q2, q8
+        vqadd.s8 q3, q9
+        vsub.s8  q2, q14
+        vsub.s8  q3, q14
 .endm
 
 function ff_hevc_sao_band_w8_neon_8, export=1
         init_sao_band
 1:      subs     r12, #4
-        vld1.8   {d4}, [r1,:64], r3
-        vld1.8   {d5}, [r1,:64], r3
-        vld1.8   {d6}, [r1,:64], r3
-        vld1.8   {d7}, [r1,:64], r3
+        vld1.8   {d4}, [r1, :64], r3
+        vld1.8   {d5}, [r1, :64], r3
+        vld1.8   {d6}, [r1, :64], r3
+        vld1.8   {d7}, [r1, :64], r3
         sao_band_32
-        vst1.8  {d4}, [r0,:64], r2
-        vst1.8  {d5}, [r0,:64], r2
-        vst1.8  {d6}, [r0,:64], r2
-        vst1.8  {d7}, [r0,:64], r2
+        vst1.8  {d4}, [r0, :64], r2
+        vst1.8  {d5}, [r0, :64], r2
+        vst1.8  {d6}, [r0, :64], r2
+        vst1.8  {d7}, [r0, :64], r2
         bne    1b
 
         bx lr
@@ -68,11 +64,11 @@ endfunc
 function ff_hevc_sao_band_w16_neon_8, export=1
         init_sao_band
 1:      subs     r12, #2
-        vld1.8  {q2}, [r1,:128], r3
-        vld1.8  {q3}, [r1,:128], r3
+        vld1.8  {q2}, [r1, :128], r3
+        vld1.8  {q3}, [r1, :128], r3
         sao_band_32
-        vst1.8   {q2}, [r0,:128], r2
-        vst1.8   {q3}, [r0,:128], r2
+        vst1.8   {q2}, [r0, :128], r2
+        vst1.8   {q3}, [r0, :128], r2
         bne    1b
 
         bx lr
@@ -81,9 +77,9 @@ endfunc
 function ff_hevc_sao_band_w32_neon_8, export=1
         init_sao_band
 1:      subs     r12, #1
-        vld1.8   {q2-q3}, [r1,:128], r3
+        vld1.8   {q2-q3}, [r1, :128], r3
         sao_band_32
-        vst1.8   {q2-q3}, [r0,:128], r2
+        vst1.8   {q2-q3}, [r0, :128], r2
         bne      1b
 
         bx       lr
@@ -92,263 +88,153 @@ endfunc
 function ff_hevc_sao_band_w64_neon_8, export=1
         init_sao_band
 1:      subs      r12, #1
-        vld1.8    {q2-q3}, [r1,:128]!
+        pld       [r1, r3]
+        vld1.8    {q2-q3}, [r1, :128]!
         sao_band_32
-        vst1.8    {q2-q3}, [r0,:128]!
-        vld1.8    {q2-q3}, [r1,:128], r3
+        vst1.8    {q2-q3}, [r0, :128]!
+        vld1.8    {q2-q3}, [r1, :128], r3
         sub       r1, #32
         sao_band_32
-        vst1.8    {q2-q3}, [r0,:128], r2
+        vst1.8    {q2-q3}, [r0, :128], r2
         sub       r0, #32
         bne       1b
 
         bx lr
 endfunc
-
+// input
+// a in q0 - q3
+// c in q4 - q7
+// b in q8 - q11
+// offset table in r7 and r5
+// output in q0 - q3
+// clobbers q12 - q15
 .macro edge_w64_body
-        vcgt.u8 q12, q4, q0 // c > a -> -1 , otherwise 0
-        vcgt.u8 q0,  q0, q4 // a > c -> -1 , otherwise 0
-        vcgt.u8 q13, q5, q1
-        vcgt.u8 q1,  q1, q5
-        vcgt.u8 q14, q6, q2
-        vcgt.u8 q2,  q2, q6
-        vcgt.u8 q15, q7, q3
-        vcgt.u8 q3,  q3, q7
-
-        vsub.s8 q12, q0, q12 // diff0
-        vsub.s8 q13, q1, q13
-        vsub.s8 q14, q2, q14
-        vsub.s8 q15, q3, q15
-
+        vcgt.u8 q12,  q4, q0 // c > a -> -1 , otherwise 0
+        vcgt.u8  q0,  q0, q4 // a > c -> -1 , otherwise 0
+        vcgt.u8 q13,  q5, q1
+        vcgt.u8  q1,  q1, q5
+        vsub.s8 q12,  q0, q12 // diff0
         vcgt.u8  q0,  q4, q8 // c > b
-        vcgt.u8  q8,  q8, q4 // b > c
+        vsub.s8 q13,  q1, q13
+
+        vcgt.u8 q14,  q8, q4 // b > c
         vcgt.u8  q1,  q5, q9
-        vcgt.u8  q9,  q9, q5
-        vcgt.u8  q2,  q6, q10
-        vcgt.u8 q10, q10, q6
-        vcgt.u8  q3,  q7, q11
-        vcgt.u8 q11, q11, q7
+        vcgt.u8 q15,  q9, q5
+        vsub.s8  q0, q14, q0 // diff1
 
-        vsub.s8 q0, q8, q0 // diff1
-        vsub.s8 q1, q9, q1
-        vsub.s8 q2, q10, q2
-        vsub.s8 q3, q11, q3
+        vsub.s8  q1, q15, q1
 
-        vadd.s8 q0, q12 //diff0 + diff1
-        vadd.s8 q1, q13
-        vadd.s8 q2, q14
-        vadd.s8 q3, q15
-
-        vdup.s8 q9, r6 // 3 to all elements
-        sub     r6, #1
-
-        vclt.s8 q12, q0, #0 // diff0 + diff1 < 0
-        vclt.s8 q13, q1, #0
-        vclt.s8 q14, q2, #0
-        vclt.s8 q15, q3, #0
-
-        vadd.s8  q8,  q0, q9 // diff0 + diff1 + 3
-        vadd.s8  q10,  q1, q9
-        vand.8   q12, q8, q12 // if (diff0 + diff1 < 0) then (diff0 + diff1 + 3) else 0
-        vand.8   q13, q10, q13
-        vadd.s8  q8,  q2, q9
-        vadd.s8  q10,  q3, q9
-        vand.8   q14, q8, q14
-        vand.8   q15, q10, q15
-
-        vdup.s8 q9, r6  // 2 to all elements
-        add     r6, #1
-
-        vcgt.s8  q10, q0, #0 // diff0 + diff1 > 0
-        vadd.s8   q8, q0, q9 // diff0 + diff1 + 2
-        vand.8   q11, q8, q10 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
-        vcgt.s8  q10, q1, #0
-        vadd.s8   q0, q11, q12  // offset_idx
-
-        vadd.s8   q8, q1, q9 // diff0 + diff1 + 2
-        vcgt.s8  q12, q2, #0
-        vand.8   q11, q8, q10 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
-        vadd.s8   q8, q2, q9 // diff0 + diff1 + 2
-        vadd.s8   q1, q11, q13
-
-        vand.8   q11, q8, q12 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
-        vcgt.s8  q10, q3, #0
-        vadd.s8   q2, q11, q14
-
-        vadd.s8   q8, q3, q9 // diff0 + diff1 + 2
-        vmov.32  d18[0], r7  // load offset table from general registers
-        vand.8   q11, q8, q10 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
-        vmov.32  d18[1], r5  // load rest of offset table
-        vadd.s8   q3, q11, q15
-
-        vtbl.8   d0, {d18}, d0
-        vtbl.8   d1, {d18}, d1
-        vtbl.8   d2, {d18}, d2
-        vtbl.8   d3, {d18}, d3
-        vtbl.8   d4, {d18}, d4
-        vtbl.8   d5, {d18}, d5
-        vtbl.8   d6, {d18}, d6
-        vtbl.8   d7, {d18}, d7
-
-        vmovl.u8   q8, d8
-        vmovl.u8   q9, d9
-        vmovl.u8  q10, d10
-        vmovl.u8  q11, d11
-        vmovl.u8  q12, d12
-        vmovl.u8  q13, d13
-        vmovl.u8  q14, d14
-        vmovl.u8  q15, d15
-
-        vaddw.s8  q8, d0
-        vaddw.s8  q9, d1
-        vaddw.s8 q10, d2
-        vaddw.s8 q11, d3
-        vaddw.s8 q12, d4
-        vaddw.s8 q13, d5
-        vaddw.s8 q14, d6
-        vaddw.s8 q15, d7
-
-        vqmovun.s16  d0, q8
-        vqmovun.s16  d1, q9
-        vqmovun.s16  d2, q10
-        vqmovun.s16  d3, q11
-        vqmovun.s16  d4, q12
-        vqmovun.s16  d5, q13
-        vqmovun.s16  d6, q14
-        vqmovun.s16  d7, q15
-
-        vstm r0, {q0-q3}
-        add  r0, r2
-.endm
+        vadd.s8  q0, q12 //diff0 + diff1
+        vadd.s8  q1, q13
 
-.macro edge_w32_body
-        vcgt.u8 q12, q4, q0 // c > a -> -1 , otherwise 0
-        vcgt.u8 q0,  q0, q4 // a > c -> -1 , otherwise 0
-        vcgt.u8 q13, q5, q1
-        vcgt.u8 q1,  q1, q5
+        vcgt.u8 q14,  q6, q2
+        vcgt.u8  q2,  q2, q6
+        vcgt.u8 q15,  q7, q3
+        vcgt.u8  q3,  q3, q7
 
-        vsub.s8 q12, q0, q12 // diff0
-        vcgt.u8  q0,  q4, q8 // c > b
-        vsub.s8 q13, q1, q13 // diff0 part 2
+        vsub.s8 q14,  q2, q14
+        vcgt.u8  q2,  q6, q10
+        vsub.s8 q15,  q3, q15
 
-        vcgt.u8  q6,  q8, q4 // b > c
-        vcgt.u8  q1,  q5, q9
-        vcgt.u8  q7,  q9, q5
+        vcgt.u8 q12, q10, q6
+        vcgt.u8  q3,  q7, q11
+        vcgt.u8 q13, q11, q7
+        vsub.s8  q2, q12, q2
+        vsub.s8  q3, q13, q3
 
-        vsub.s8 q0, q6, q0 // diff1
-        vsub.s8 q1, q7, q1 // diff1 part 2
-        vadd.s8 q0, q12 //diff0 + diff1
+        vmov.s8 q13, #2 // 2 to all elements
 
-        vdup.s8 q7, r6 // 3 to all elements
-        sub     r6, #1
-        vadd.s8 q1, q13
+        vadd.s8  q2, q14
+        vadd.s8  q3, q15
+
+        vmov.32  d24[0], r4  // load offset table from general registers
+        vmov.32  d24[1], r5  // load rest of offset table
 
-        vclt.s8 q12, q0, #0 // diff0 + diff1 < 0
-        vclt.s8 q13, q1, #0
-
-        vadd.s8  q6,  q0, q7 // diff0 + diff1 + 3
-        vadd.s8  q10,  q1, q7
-        vdup.s8 q7, r6  // 2 to all elements
-        add     r6, #1
-        vand.8   q12, q6, q12 // if (diff0 + diff1 < 0) then (diff0 + diff1 + 3) else 0
-        vand.8   q13, q10, q13
-
-
-        vcgt.s8  q10, q0, #0 // diff0 + diff1 > 0
-        vadd.s8   q6, q0, q7 // diff0 + diff1 + 2
-        vand.8   q11, q6, q10 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
-        vcgt.s8  q10, q1, #0
-        vadd.s8   q0, q11, q12  // offset_idx
-
-        vadd.s8   q6, q1, q7 // diff0 + diff1 + 2
-        vmov.32  d14[0], r7  // load offset table from general registers
-        vand.8   q11, q6, q10 // if (diff0 + diff1 > 0) then (diff0 + diff1 + 2) else 0
-        vmov.32  d14[1], r5  // load rest of offset table
-        vadd.s8   q1, q11, q13
-
-        vtbl.8   d0, {d14}, d0
-        vtbl.8   d1, {d14}, d1
-        vtbl.8   d2, {d14}, d2
-        vtbl.8   d3, {d14}, d3
-
-        vmovl.u8   q6, d8
-        vmovl.u8   q7, d9
-        vmovl.u8  q10, d10
-        vmovl.u8  q11, d11
-
-        vaddw.s8  q6, d0
-        vaddw.s8  q7, d1
-        vaddw.s8 q10, d2
-        vaddw.s8 q11, d3
-
-        vqmovun.s16  d0, q6
-        vqmovun.s16  d1, q7
-        vqmovun.s16  d2, q10
-        vqmovun.s16  d3, q11
-
-        vstm r0, {q0-q1}
-        add  r0, r2
+        vadd.s8 q0, q13
+        vadd.s8 q1, q13
+        vadd.s8 q2, q13
+        vadd.s8 q3, q13
+
+        vmov.u8  q15, #128 // s8 #-128
+        vtbl.8   d0, {d24}, d0
+        vtbl.8   d1, {d24}, d1
+        vtbl.8   d2, {d24}, d2
+        vtbl.8   d3, {d24}, d3
+        vtbl.8   d4, {d24}, d4
+        vtbl.8   d5, {d24}, d5
+        vtbl.8   d6, {d24}, d6
+        vtbl.8   d7, {d24}, d7
+
+        vadd.s8  q12,  q4, q15
+        vadd.s8  q13,  q5, q15
+        vadd.s8  q14,  q6, q15
+        vadd.s8  q15,  q7, q15
+        vqadd.s8 q12,  q0
+        vqadd.s8 q15,  q3
+        vmov.u8   q3, #128 // s8 #-128
+        vqadd.s8 q13,  q1
+        vqadd.s8 q14,  q2
+        vsub.s8   q0, q12, q3
+        vsub.s8   q1, q13, q3
+        vsub.s8   q2, q14, q3
+        vsub.s8   q3, q15, q3
+        vst1.8  {q0-q1}, [r0, :128]!
+        vst1.8  {q2-q3}, [r0, :128], r2
+        sub     r0, #32
 .endm
 
-function ff_hevc_sao_edge_eo0_w64_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // sao_offset_val_table
-        ldr    r6, =0x03
-        ldr    r7, [r5]
+.macro init_edge_64
+        push   {r4-r5}
+        ldr    r12, [sp, #8] // height
+        ldr    r5, [sp, #12] // sao_offset_val_table
+        ldr    r4, [r5]
         add    r5, #4
         ldr    r5, [r5]
+.endm
+
+function ff_hevc_sao_edge_eo0_w64_neon_8, export=1
+        init_edge_64
         vpush {d8-d15}
         sub    r1, #8
-1:      subs    r4, #1
-        vld1.64  {q10-q11}, [r1]!
-        vld1.64  {q12-q13}, [r1]!
-        vld1.64  {q14}, [r1], r3
-        sub      r1, #64
+1:      subs    r12, #1
+        vld1.64  {d7}, [r1, :64]!
+        vld1.64  {q4-q5}, [r1, :128]! // load c
+        vld1.64  {q6-q7}, [r1, :128]!
+        vld1.64  {d24}, [r1, :64], r3
+        sub      r1, #72
         // load a
-        vext.8 q0, q10, q11, #7
-        vext.8 q1, q11, q12, #7
-        vext.8 q2, q12, q13, #7
-        vext.8 q3, q13, q14, #7
-        // load c
-        vext.8 q4, q10, q11, #8
-        vext.8 q5, q11, q12, #8
-        vext.8 q6, q12, q13, #8
-        vext.8 q7, q13, q14, #8
+        vext.8 q0, q3, q4, #15
+        vext.8 q1, q4, q5, #15
+        vext.8 q2, q5, q6, #15
+        vext.8 q3, q6, q7, #15
         // load b
-        vext.8 q8, q10, q11, #9
-        vext.8 q9, q11, q12, #9
-        vext.8 q10, q12, q13, #9
-        vext.8 q11, q13, q14, #9
+        vext.8 q8, q4, q5, #1
+        vext.8 q9, q5, q6, #1
+        vext.8 q10, q6, q7, #1
+        vext.8 q11, q7, q12, #1
         edge_w64_body
         bne   1b
         vpop  {d8-d15}
-        pop   {r4-r8}
+        pop   {r4-r5}
         bx lr
 endfunc
 
 function ff_hevc_sao_edge_eo1_w64_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // sao_offset_val_table
-        ldr    r6, =0x03
-        ldr    r7, [r5]
-        add    r5, #4
-        ldr    r5, [r5]
+        init_edge_64
         vpush {d8-d15}
         sub     r1, r3
         // load a
-        vld1.8  {q0-q1}, [r1]!
-        vld1.8  {q2-q3}, [r1], r3
+        vld1.8  {q0-q1}, [r1, :128]!
+        vld1.8  {q2-q3}, [r1, :128], r3
         sub     r1, #32
-1:      subs    r4, #1
         // load c
-        vld1.8  {q4-q5}, [r1]!
-        vld1.8  {q6-q7}, [r1], r3
+        vld1.8  {q4-q5}, [r1, :128]!
+        vld1.8  {q6-q7}, [r1, :128], r3
         sub     r1, #32
+1:      subs    r12, #1
         // load b
-        vld1.8  {q8-q9}, [r1]!
-        vld1.8  {q10-q11}, [r1]
+        vld1.8  {q8-q9}, [r1, :128]!
+        vld1.8  {q10-q11}, [r1, :128], r3
         sub     r1, #32
         edge_w64_body
         // copy c to a
@@ -356,20 +242,19 @@ function ff_hevc_sao_edge_eo1_w64_neon_8, export=1
         vmov.64 q1, q5
         vmov.64 q2, q6
         vmov.64 q3, q7
+        // copy b to c
+        vmov.64 q4, q8
+        vmov.64 q5, q9
+        vmov.64 q6, q10
+        vmov.64 q7, q11
         bne   1b
         vpop  {d8-d15}
-        pop   {r4-r8}
+        pop   {r4-r5}
         bx lr
 endfunc
 
 function ff_hevc_sao_edge_eo2_w64_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // sao_offset_val_table
-        ldr    r6, =0x03
-        ldr    r7, [r5]
-        add    r5, #4
-        ldr    r5, [r5]
+        init_edge_64
         vpush {d8-d15}
 1:      sub     r1, r3
         // load a
@@ -379,10 +264,10 @@ function ff_hevc_sao_edge_eo2_w64_neon_8, export=1
         vld1.8  {q0-q1}, [r1]!
         vld1.8  {q2-q3}, [r1], r3
         sub     r1, #31
-        subs    r4, #1
+        subs    r12, #1
         // load c
-        vld1.8  {q4-q5}, [r1]!
-        vld1.8  {q6-q7}, [r1], r3
+        vld1.8  {q4-q5}, [r1, :128]!
+        vld1.8  {q6-q7}, [r1, :128], r3
         sub     r1, #32
         // load b
         add     r1, #1
@@ -390,25 +275,14 @@ function ff_hevc_sao_edge_eo2_w64_neon_8, export=1
         vld1.8  {q10-q11}, [r1]
         sub     r1, #33
         edge_w64_body
-        // copy c to a
-        vmov.64 q0, q4
-        vmov.64 q1, q5
-        vmov.64 q2, q6
-        vmov.64 q3, q7
         bne   1b
         vpop  {d8-d15}
-        pop   {r4-r8}
+        pop   {r4-r5}
         bx lr
 endfunc
 
 function ff_hevc_sao_edge_eo3_w64_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // sao_offset_val_table
-        ldr    r6, =0x03
-        ldr    r7, [r5]
-        add    r5, #4
-        ldr    r5, [r5]
+        init_edge_64
         vpush {d8-d15}
 1:      sub     r1, r3
         // load a
@@ -418,10 +292,10 @@ function ff_hevc_sao_edge_eo3_w64_neon_8, export=1
         vld1.8  {q0-q1}, [r1]!
         vld1.8  {q2-q3}, [r1], r3
         sub     r1, #33
-        subs    r4, #1
+        subs    r12, #1
         // load c
-        vld1.8  {q4-q5}, [r1]!
-        vld1.8  {q6-q7}, [r1], r3
+        vld1.8  {q4-q5}, [r1, :128]!
+        vld1.8  {q6-q7}, [r1, :128], r3
         sub     r1, #32
         // load b
         sub     r1, #1
@@ -429,178 +303,176 @@ function ff_hevc_sao_edge_eo3_w64_neon_8, export=1
         vld1.8  {q10-q11}, [r1]
         sub     r1, #31
         edge_w64_body
-        // copy c to a
-        vmov.64 q0, q4
-        vmov.64 q1, q5
-        vmov.64 q2, q6
-        vmov.64 q3, q7
         bne   1b
         vpop  {d8-d15}
-        pop   {r4-r8}
+        pop   {r4-r5}
         bx lr
 endfunc
 
+// inputs:
+// a in q0, q1
+// c in q2, q3
+// b in q8, q9
+// offset table in d31
+// clobbered registers q0, q1, q10, q11, q12, q13
+// output q0, q1
+.macro edge_w32_body
+        vcgt.u8 q12, q2, q0 // c > a -> -1 , otherwise 0
+        vcgt.u8 q0,  q0, q2 // a > c -> -1 , otherwise 0
+        vcgt.u8 q13, q3, q1
+        vcgt.u8 q1,  q1, q3
+
+        vsub.s8 q12, q0, q12 // diff0
+        vcgt.u8  q0,  q2, q8 // c > b
+        vsub.s8 q13, q1, q13 // diff0 part 2
+
+        vcgt.u8  q10,  q8, q2 // b > c
+        vcgt.u8  q1,  q3, q9
+        vcgt.u8  q11,  q9, q3
+
+        vsub.s8 q0, q10, q0 // diff1
+
+        vmov.s8 q10, #2 // 2 to all elements
+        vsub.s8 q1, q11, q1 // diff1 part 2
+        vadd.s8 q0, q12 //diff0 + diff1
+        vadd.s8 q1, q13
+
+        vadd.s8 q0, q10
+        vadd.s8 q1, q10
+
+        vmov.u8  q10, #128
+        vtbl.8   d0, {d31}, d0
+        vtbl.8   d1, {d31}, d1
+        vtbl.8   d2, {d31}, d2
+        vtbl.8   d3, {d31}, d3
+
+        vadd.s8    q11, q2, q10
+        vadd.s8    q12, q3, q10
+        vqadd.s8   q11, q0
+        vqadd.s8   q12, q1
+        vsub.s8    q0, q11, q10
+        vsub.s8    q1, q12, q10
+        vst1.8   {q0-q1}, [r0, :128], r2
+.endm
+
+.macro init_edge_32
+        ldr     r12, [sp, #4] // sao_offset_val_table
+        vld1.32 {d31}, [r12]
+        ldr     r12, [sp] // height
+.endm
+
 function ff_hevc_sao_edge_eo0_w32_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // sao_offset_val_table
-        ldr    r6, =0x03
-        ldr    r7, [r5]
-        add    r5, #4
-        ldr    r5, [r5]
-        vpush {d8-d15}
-        sub    r1, #8 // load 8 extra bytes
-1:      subs    r4, #1
-        vld1.8  {q10-q11}, [r1]
-        add    r1, #32
-        vld1.8  {q12}, [r1], r3 // only first 9 bytes are used
-        sub    r1, #32
+        init_edge_32
+        sub     r1, #4 // load 4 extra bytes
+1:      subs    r12, #1
+        vld1.32 d3[1], [r1]!
+        vld1.8  {q2-q3}, [r1, :128]! // c
+        vld1.32 d20[0], [r1], r3
+        sub     r1, #36
         // a
-        vext.8  q0, q10, q11, #7
-        vext.8  q1, q11, q12, #7
-        // c
-        vext.8  q4, q10, q11, #8
-        vext.8  q5, q11, q12, #8
+        vext.8  q0, q1, q2, #15
+        vext.8  q1, q2, q3, #15
         // b
-        vext.8  q8, q10, q11, #9
-        vext.8  q9, q11, q12, #9
+        vext.8  q8, q2, q3, #1
+        vext.8  q9, q3, q10, #1
         edge_w32_body
-        bne   1b
-        vpop  {d8-d15}
-        pop   {r4-r8}
-        bx lr
+        bne     1b
+        bx      lr
 endfunc
 
 function ff_hevc_sao_edge_eo1_w32_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // sao_offset_val_table
-        ldr    r6, =0x03
-        ldr    r7, [r5]
-        add    r5, #4
-        ldr    r5, [r5]
-        vpush {d8-d15}
+        init_edge_32
         // load a
         sub     r1, r3
-        vld1.8  {q0-q1}, [r1], r3
+        vld1.8  {q0-q1}, [r1, :128], r3
         // load c
-        vld1.8  {q4-q5}, [r1], r3
-1:      subs    r4, #1
+        vld1.8  {q2-q3}, [r1, :128], r3
+1:      subs    r12, #1
         // load b
-        vld1.8  {q8-q9}, [r1], r3
+        vld1.8  {q8-q9}, [r1, :128], r3
         edge_w32_body
         // inputs for next loop iteration
         // a
-        vmov.64 q0, q4
-        vmov.64 q1, q5
+        vmov.64 q0, q2
+        vmov.64 q1, q3
         // c
-        vmov.64 q4, q8
-        vmov.64 q5, q9
-        bne   1b
-        vpop  {d8-d15}
-        pop   {r4-r8}
-        bx lr
+        vmov.64 q2, q8
+        vmov.64 q3, q9
+        bne     1b
+        bx      lr
 endfunc
 
 function ff_hevc_sao_edge_eo2_w32_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // sao_offset_val_table
-        ldr    r6, =0x03
-        ldr    r7, [r5]
-        add    r5, #4
-        ldr    r5, [r5]
-        vpush {d8-d15}
+        init_edge_32
+        vpush   {d8-d15}
         // load a
         sub     r1, r3
-        sub    r1, #8
-        vld1.8  {q10-q11}, [r1]
-        add    r1, #32
-        vld1.8  {q12}, [r1], r3
-        sub    r1, #32
+        sub     r1, #8
+        vld1.8  {q10-q11}, [r1, :64]!
+        vld1.8  {d24}, [r1, :64], r3
+        sub     r1, #32
         vext.8  q0, q10, q11, #7
         vext.8  q1, q11, q12, #7
         // load c
-        vld1.8  {q10-q11}, [r1]
-        add    r1, #32
-        vld1.8  {q12}, [r1], r3
-        sub    r1, #32
-        vext.8  q4, q10, q11, #8
-        vext.8  q5, q11, q12, #8
-        vext.8  q2, q10, q11, #7
-1:      subs    r4, #1
+        vld1.8  {d9}, [r1, :64]!
+        vld1.8  {q2-q3}, [r1, :64], r3
+        sub     r1, #8
+        vext.8  q4, q4, q2, #15
+1:      subs    r12, #1
         // load b
-        vld1.8  {q10-q11}, [r1]
-        add    r1, #32
-        vld1.8  {q12}, [r1], r3
-        sub    r1, #32
+        vld1.8  {q10-q11}, [r1, :64]!
+        vld1.8  {q12}, [r1, :64], r3
+        sub     r1, #32
         vext.8  q8, q10, q11, #9
         vext.8  q9, q11, q12, #9
-        vext.8  q14, q10, q11, #8
-        vext.8  q15, q11, q12, #8
-        vext.8  q3, q10, q11, #7
+        vext.8  q6, q10, q11, #8
+        vext.8  q7, q11, q12, #8
+        vext.8  q5, q10, q11, #7
         edge_w32_body
         // inputs for next loop iteration
         // a
-        vmov.8 q0, q2
-        vext.8 q1, q4, q5, #15
+        vmov.8  q0, q4
+        vext.8  q1, q2, q3, #15
         // c
-        vmov.8  q4, q14
-        vmov.8  q5, q15
-        vmov.8  q2, q3
-        bne   1b
-        vpop  {d8-d15}
-        pop   {r4-r8}
-        bx lr
+        vmov.8  q2, q6
+        vmov.8  q3, q7
+        vmov.8  q4, q5
+        bne     1b
+        vpop    {d8-d15}
+        bx      lr
 endfunc
 
 function ff_hevc_sao_edge_eo3_w32_neon_8, export=1
-        push  {r4-r8}
-        ldr    r4, [sp, #20] // height
-        ldr    r5, [sp, #24] // sao_offset_val_table
-        ldr    r6, =0x03
-        ldr    r7, [r5]
-        add    r5, #4
-        sub    r1, r3
-        ldr    r5, [r5]
-        sub    r1, #8
-        vpush {d8-d15}
+        init_edge_32
+        sub     r1, r3
         // load a
-        vld1.8  {q10-q11}, [r1]
-        add    r1, #32
-        vld1.8  {q12}, [r1], r3
-        sub    r1, #32
-        vext.8  q0, q10, q11, #9
-        vext.8  q1, q11, q12, #9
+        vld1.8  {q10-q11}, [r1, :64]!
+        vld1.8  {d24}, [r1, :64], r3
+        sub     r1, #32
+        vext.8  q0, q10, q11, #1
+        vext.8  q1, q11, q12, #1
         // load c
-        vld1.8  {q10-q11}, [r1]
-        add    r1, #32
-        vld1.8  {q12}, [r1], r3
-        sub    r1, #32
-        vext.8  q4, q10, q11, #8
-        vext.8  q5, q11, q12, #8
-        vext.8  q2, q12, q11, #8
-1:      subs    r4, #1
+        vld1.8  {q2-q3}, [r1, :64]!
+        vld1.8  {d30}, [r1, :64], r3
+        sub     r1, #40
+1:      subs    r12, #1
         // load b
-        vld1.8  {q10-q11}, [r1]
-        add    r1, #32
-        vld1.8  {q12}, [r1], r3
-        sub    r1, #32
+        vld1.8  {q10-q11}, [r1, :64]!
+        vld1.8  {q12}, [r1, :64], r3
+        sub     r1, #32
         vext.8  q8, q10, q11, #7
         vext.8  q9, q11, q12, #7
-        vext.8  q3, q12, q10, #7
+        vext.8  q14, q12, q10, #7
         edge_w32_body
         // inputs for next loop iteration
         // a
-        vext.8 q0, q4, q5, #1
-        vext.8 q1, q5, q2, #1
+        vext.8  q0, q2, q3, #1
+        vext.8  q1, q3, q15, #1
         // c
-        vext.8  q4, q8, q9, #1
-        vext.8  q5, q9, q3, #1
-        vext.8  q2, q3, q1, #1
-        bne   1b
-        vpop  {d8-d15}
-        pop   {r4-r8}
-        bx lr
+        vext.8  q2, q8, q9, #1
+        vext.8  q3, q9, q14, #1
+        vext.8  d30, d28, d2, #1
+        bne     1b
+        bx      lr
 endfunc
 
-- 
2.5.0


From 016c39d46b86830204a4519590332d2a38f7ee51 Mon Sep 17 00:00:00 2001
From: Seppo Tomperi <seppo.tomperi@vtt.fi>
Date: Thu, 8 Jan 2015 09:58:55 +0200
Subject: [PATCH 7/9] small optimization to SAO BAND. correct path for
 bit_depth_template.c

---
 libavcodec/arm/hevcdsp_init_neon.c | 2 +-
 libavcodec/arm/hevcdsp_sao_neon.S  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavcodec/arm/hevcdsp_init_neon.c b/libavcodec/arm/hevcdsp_init_neon.c
index 8d6e863..385c35d 100644
--- a/libavcodec/arm/hevcdsp_init_neon.c
+++ b/libavcodec/arm/hevcdsp_init_neon.c
@@ -23,7 +23,7 @@
 #include "libavcodec/hevcdsp.h"
 #include "hevcdsp_arm.h"
 #include "libavcodec/avcodec.h"
-#include "../bit_depth_template.c"
+#include "libavcodec/bit_depth_template.c"
 
 void ff_hevc_v_loop_filter_luma_neon(uint8_t *_pix, ptrdiff_t _stride, int _beta, int *_tc, uint8_t *_no_p, uint8_t *_no_q);
 void ff_hevc_h_loop_filter_luma_neon(uint8_t *_pix, ptrdiff_t _stride, int _beta, int *_tc, uint8_t *_no_p, uint8_t *_no_q);
diff --git a/libavcodec/arm/hevcdsp_sao_neon.S b/libavcodec/arm/hevcdsp_sao_neon.S
index 5fc482b..710b32b 100644
--- a/libavcodec/arm/hevcdsp_sao_neon.S
+++ b/libavcodec/arm/hevcdsp_sao_neon.S
@@ -26,12 +26,12 @@
         pld      [r1]
         vld1.8   {q0, q1}, [r12]  // offset table
         ldr      r12, [sp, #4]    // height
+        vmov.u8  q14, #128
 .endm
 
 .macro sao_band_32
         vshr.u8  q8, q2, #3
         vshr.u8  q9, q3, #3
-        vmov.u8  q14, #128
         vtbl.8   d16, {q0, q1}, d16
         vtbl.8   d17, {q0, q1}, d17
         vtbl.8   d18, {q0, q1}, d18
-- 
2.5.0


From 579f1584d688e1ac24fb7d22697e2a7b64f62e8e Mon Sep 17 00:00:00 2001
From: Seppo Tomperi <seppo.tomperi@vtt.fi>
Date: Fri, 9 Jan 2015 10:28:52 +0200
Subject: [PATCH 8/9] Added height check for SAO NEON optimizations. Faster SAO
 band NEON Some reordering to use NEON pipelines more efficiently

---
 libavcodec/arm/hevcdsp_init_neon.c |  12 +++-
 libavcodec/arm/hevcdsp_sao_neon.S  | 142 ++++++++++++++++++++++---------------
 2 files changed, 93 insertions(+), 61 deletions(-)

diff --git a/libavcodec/arm/hevcdsp_init_neon.c b/libavcodec/arm/hevcdsp_init_neon.c
index 385c35d..6d0689c 100644
--- a/libavcodec/arm/hevcdsp_init_neon.c
+++ b/libavcodec/arm/hevcdsp_init_neon.c
@@ -176,6 +176,7 @@ static void ff_hevc_sao_band_neon_wrapper(uint8_t *_dst, uint8_t *_src, ptrdiff_
     int8_t offset_table[32] = { 0 };
     int k, y, x;
     int shift  = 3; // BIT_DEPTH - 5
+    int cwidth = 0;
 
     stride_src /= sizeof(pixel);
     stride_dst /= sizeof(pixel);
@@ -183,7 +184,10 @@ static void ff_hevc_sao_band_neon_wrapper(uint8_t *_dst, uint8_t *_src, ptrdiff_
     for (k = 0; k < 4; k++)
         offset_table[(k + sao_left_class) & 31] = sao_offset_val[k + 1];
 
-    switch(width){
+    if (height % 8 == 0)
+        cwidth = width;
+
+    switch(cwidth){
     case 8:
         ff_hevc_sao_band_w8_neon_8(_dst, _src, stride_dst, stride_src, offset_table, height);
         break;
@@ -223,15 +227,19 @@ static void ff_hevc_sao_edge_neon_wrapper(uint8_t *_dst /* align 16 */, uint8_t
     pixel *src = (pixel *)_src;
     int a_stride, b_stride;
     int x, y;
+    int cwidth = 0;
 
     for (x = 0; x < 5; x++) {
         sao_offset_val[x] = _sao_offset_val[edge_idx[x]];
     }
 
+    if (height % 8 == 0)
+        cwidth = width;
+
     stride_src /= sizeof(pixel);
     stride_dst /= sizeof(pixel);
 
-    switch (width) {
+    switch (cwidth) {
     case 32:
         switch(eo) {
         case 0:
diff --git a/libavcodec/arm/hevcdsp_sao_neon.S b/libavcodec/arm/hevcdsp_sao_neon.S
index 710b32b..08f50b8 100644
--- a/libavcodec/arm/hevcdsp_sao_neon.S
+++ b/libavcodec/arm/hevcdsp_sao_neon.S
@@ -26,36 +26,59 @@
         pld      [r1]
         vld1.8   {q0, q1}, [r12]  // offset table
         ldr      r12, [sp, #4]    // height
-        vmov.u8  q14, #128
+        vmov.u8  q3, #128
 .endm
 
-.macro sao_band_32
-        vshr.u8  q8, q2, #3
-        vshr.u8  q9, q3, #3
-        vtbl.8   d16, {q0, q1}, d16
-        vtbl.8   d17, {q0, q1}, d17
-        vtbl.8   d18, {q0, q1}, d18
-        vtbl.8   d19, {q0, q1}, d19
-        vadd.s8  q2, q14
-        vadd.s8  q3, q14
-        vqadd.s8 q2, q8
-        vqadd.s8 q3, q9
-        vsub.s8  q2, q14
-        vsub.s8  q3, q14
+// 128 in q3
+// input q8 - q11
+// 32 cycles
+.macro sao_band_64
+        vshr.u8  q12, q8, #3
+        vshr.u8  q13, q9, #3
+        vshr.u8  q14, q10, #3
+        vshr.u8  q15, q11, #3
+        vtbl.8   d24, {d0, d1, d2, d3}, d24
+        vadd.s8  q8, q3
+        vtbl.8   d25, {d0, d1, d2, d3}, d25
+        vadd.s8  q9, q3
+        vtbl.8   d26, {d0, d1, d2, d3}, d26
+        vadd.s8  q10, q3
+        vtbl.8   d27, {d0, d1, d2, d3}, d27
+        vadd.s8  q11, q3
+        vtbl.8   d28, {d0, d1, d2, d3}, d28
+        vqadd.s8 q8, q12
+        vtbl.8   d29, {d0, d1, d2, d3}, d29
+        vqadd.s8 q9, q13
+        vtbl.8   d30, {d0, d1, d2, d3}, d30
+        vqadd.s8 q10, q14
+        vtbl.8   d31, {d0, d1, d2, d3}, d31
+        vqadd.s8 q11, q15
+        vsub.s8  q8, q3
+        vsub.s8  q9, q3
+        vsub.s8  q10, q3
+        vsub.s8  q11, q3
 .endm
 
 function ff_hevc_sao_band_w8_neon_8, export=1
         init_sao_band
-1:      subs     r12, #4
-        vld1.8   {d4}, [r1, :64], r3
-        vld1.8   {d5}, [r1, :64], r3
-        vld1.8   {d6}, [r1, :64], r3
-        vld1.8   {d7}, [r1, :64], r3
-        sao_band_32
-        vst1.8  {d4}, [r0, :64], r2
-        vst1.8  {d5}, [r0, :64], r2
-        vst1.8  {d6}, [r0, :64], r2
-        vst1.8  {d7}, [r0, :64], r2
+1:      subs     r12, #8
+        vld1.8   {d16}, [r1, :64], r3
+        vld1.8   {d17}, [r1, :64], r3
+        vld1.8   {d18}, [r1, :64], r3
+        vld1.8   {d19}, [r1, :64], r3
+        vld1.8   {d20}, [r1, :64], r3
+        vld1.8   {d21}, [r1, :64], r3
+        vld1.8   {d22}, [r1, :64], r3
+        vld1.8   {d23}, [r1, :64], r3
+        sao_band_64
+        vst1.8  {d16}, [r0, :64], r2
+        vst1.8  {d17}, [r0, :64], r2
+        vst1.8  {d18}, [r0, :64], r2
+        vst1.8  {d19}, [r0, :64], r2
+        vst1.8  {d20}, [r0, :64], r2
+        vst1.8  {d21}, [r0, :64], r2
+        vst1.8  {d22}, [r0, :64], r2
+        vst1.8  {d23}, [r0, :64], r2
         bne    1b
 
         bx lr
@@ -63,12 +86,16 @@ endfunc
 
 function ff_hevc_sao_band_w16_neon_8, export=1
         init_sao_band
-1:      subs     r12, #2
-        vld1.8  {q2}, [r1, :128], r3
-        vld1.8  {q3}, [r1, :128], r3
-        sao_band_32
-        vst1.8   {q2}, [r0, :128], r2
-        vst1.8   {q3}, [r0, :128], r2
+1:      subs     r12, #4
+        vld1.8  {q8}, [r1, :128], r3
+        vld1.8  {q9}, [r1, :128], r3
+        vld1.8  {q10}, [r1, :128], r3
+        vld1.8  {q11}, [r1, :128], r3
+        sao_band_64
+        vst1.8   {q8}, [r0, :128], r2
+        vst1.8   {q9}, [r0, :128], r2
+        vst1.8   {q10}, [r0, :128], r2
+        vst1.8   {q11}, [r0, :128], r2
         bne    1b
 
         bx lr
@@ -76,10 +103,12 @@ endfunc
 
 function ff_hevc_sao_band_w32_neon_8, export=1
         init_sao_band
-1:      subs     r12, #1
-        vld1.8   {q2-q3}, [r1, :128], r3
-        sao_band_32
-        vst1.8   {q2-q3}, [r0, :128], r2
+1:      subs     r12, #2
+        vld1.8   {q8-q9}, [r1, :128], r3
+        vld1.8   {q10-q11}, [r1, :128], r3
+        sao_band_64
+        vst1.8   {q8-q9}, [r0, :128], r2
+        vst1.8   {q10-q11}, [r0, :128], r2
         bne      1b
 
         bx       lr
@@ -89,13 +118,12 @@ function ff_hevc_sao_band_w64_neon_8, export=1
         init_sao_band
 1:      subs      r12, #1
         pld       [r1, r3]
-        vld1.8    {q2-q3}, [r1, :128]!
-        sao_band_32
-        vst1.8    {q2-q3}, [r0, :128]!
-        vld1.8    {q2-q3}, [r1, :128], r3
+        vld1.8    {q8-q9}, [r1, :128]!
+        vld1.8    {q10-q11}, [r1, :128], r3
         sub       r1, #32
-        sao_band_32
-        vst1.8    {q2-q3}, [r0, :128], r2
+        sao_band_64
+        vst1.8    {q8-q9}, [r0, :128]!
+        vst1.8    {q10-q11}, [r0, :128], r2
         sub       r0, #32
         bne       1b
 
@@ -121,7 +149,6 @@ endfunc
         vcgt.u8  q1,  q5, q9
         vcgt.u8 q15,  q9, q5
         vsub.s8  q0, q14, q0 // diff1
-
         vsub.s8  q1, q15, q1
 
         vadd.s8  q0, q12 //diff0 + diff1
@@ -157,27 +184,25 @@ endfunc
 
         vmov.u8  q15, #128 // s8 #-128
         vtbl.8   d0, {d24}, d0
+        vadd.s8  q13,  q4, q15
         vtbl.8   d1, {d24}, d1
+        vadd.s8  q14,  q5, q15
         vtbl.8   d2, {d24}, d2
+        vqadd.s8 q0, q13
         vtbl.8   d3, {d24}, d3
+        vqadd.s8 q1, q14
         vtbl.8   d4, {d24}, d4
+        vadd.s8  q13,  q6, q15
         vtbl.8   d5, {d24}, d5
+        vadd.s8  q14,  q7, q15
         vtbl.8   d6, {d24}, d6
+        vqadd.s8 q2, q13
         vtbl.8   d7, {d24}, d7
-
-        vadd.s8  q12,  q4, q15
-        vadd.s8  q13,  q5, q15
-        vadd.s8  q14,  q6, q15
-        vadd.s8  q15,  q7, q15
-        vqadd.s8 q12,  q0
-        vqadd.s8 q15,  q3
-        vmov.u8   q3, #128 // s8 #-128
-        vqadd.s8 q13,  q1
-        vqadd.s8 q14,  q2
-        vsub.s8   q0, q12, q3
-        vsub.s8   q1, q13, q3
-        vsub.s8   q2, q14, q3
-        vsub.s8   q3, q15, q3
+        vqadd.s8 q3, q14
+        vsub.s8   q0, q15
+        vsub.s8   q1, q15
+        vsub.s8   q2, q15
+        vsub.s8   q3, q15
         vst1.8  {q0-q1}, [r0, :128]!
         vst1.8  {q2-q3}, [r0, :128], r2
         sub     r0, #32
@@ -342,13 +367,12 @@ endfunc
 
         vmov.u8  q10, #128
         vtbl.8   d0, {d31}, d0
+        vadd.s8  q11, q2, q10
         vtbl.8   d1, {d31}, d1
+        vadd.s8  q12, q3, q10
         vtbl.8   d2, {d31}, d2
+        vqadd.s8 q11, q0
         vtbl.8   d3, {d31}, d3
-
-        vadd.s8    q11, q2, q10
-        vadd.s8    q12, q3, q10
-        vqadd.s8   q11, q0
         vqadd.s8   q12, q1
         vsub.s8    q0, q11, q10
         vsub.s8    q1, q12, q10
-- 
2.5.0


From 026bac1824e4936e948e6b1efec82868c520ea66 Mon Sep 17 00:00:00 2001
From: Seppo Tomperi <seppo.tomperi@vtt.fi>
Date: Mon, 2 Feb 2015 16:08:27 +0200
Subject: [PATCH 9/9] Further SAO NEON optimisations

---
 libavcodec/arm/hevcdsp_init_neon.c |  16 +--
 libavcodec/arm/hevcdsp_sao_neon.S  | 224 +++++++++++++++++++------------------
 2 files changed, 124 insertions(+), 116 deletions(-)

diff --git a/libavcodec/arm/hevcdsp_init_neon.c b/libavcodec/arm/hevcdsp_init_neon.c
index 6d0689c..e5da7e9 100644
--- a/libavcodec/arm/hevcdsp_init_neon.c
+++ b/libavcodec/arm/hevcdsp_init_neon.c
@@ -45,10 +45,10 @@ void ff_hevc_transform_add_16x16_neon_8(uint8_t *_dst, int16_t *coeffs,
 void ff_hevc_transform_add_32x32_neon_8(uint8_t *_dst, int16_t *coeffs,
                                       ptrdiff_t stride);
 
-void ff_hevc_sao_band_w8_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int8_t * offset_table, int height);
-void ff_hevc_sao_band_w16_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int8_t * offset_table, int height);
-void ff_hevc_sao_band_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int8_t * offset_table, int height);
-void ff_hevc_sao_band_w64_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int8_t * offset_table, int height);
+void ff_hevc_sao_band_w8_neon_8(uint8_t *_dst, uint8_t *_src, int8_t * offset_table, ptrdiff_t stride_src, ptrdiff_t stride_dst, int height);
+void ff_hevc_sao_band_w16_neon_8(uint8_t *_dst, uint8_t *_src, int8_t * offset_table, ptrdiff_t stride_src, ptrdiff_t stride_dst, int height);
+void ff_hevc_sao_band_w32_neon_8(uint8_t *_dst, uint8_t *_src, int8_t * offset_table, ptrdiff_t stride_src, ptrdiff_t stride_dst, int height);
+void ff_hevc_sao_band_w64_neon_8(uint8_t *_dst, uint8_t *_src, int8_t * offset_table, ptrdiff_t stride_src, ptrdiff_t stride_dst, int height);
 
 void ff_hevc_sao_edge_eo0_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
 void ff_hevc_sao_edge_eo1_w32_neon_8(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int height, int8_t *sao_offset_table);
@@ -189,16 +189,16 @@ static void ff_hevc_sao_band_neon_wrapper(uint8_t *_dst, uint8_t *_src, ptrdiff_
 
     switch(cwidth){
     case 8:
-        ff_hevc_sao_band_w8_neon_8(_dst, _src, stride_dst, stride_src, offset_table, height);
+        ff_hevc_sao_band_w8_neon_8(_dst, _src, offset_table, stride_src, stride_dst, height);
         break;
     case 16:
-        ff_hevc_sao_band_w16_neon_8(_dst, _src, stride_dst, stride_src, offset_table, height);
+        ff_hevc_sao_band_w16_neon_8(_dst, _src, offset_table, stride_src, stride_dst, height);
         break;
     case 32:
-        ff_hevc_sao_band_w32_neon_8(_dst, _src, stride_dst, stride_src, offset_table, height);
+        ff_hevc_sao_band_w32_neon_8(_dst, _src, offset_table, stride_src, stride_dst, height);
         break;
     case 64:
-        ff_hevc_sao_band_w64_neon_8(_dst, _src, stride_dst, stride_src, offset_table, height);
+        ff_hevc_sao_band_w64_neon_8(_dst, _src, offset_table, stride_src, stride_dst, height);
         break;
     default:
         for (y = 0; y < height; y++) {
diff --git a/libavcodec/arm/hevcdsp_sao_neon.S b/libavcodec/arm/hevcdsp_sao_neon.S
index 08f50b8..9c7808d 100644
--- a/libavcodec/arm/hevcdsp_sao_neon.S
+++ b/libavcodec/arm/hevcdsp_sao_neon.S
@@ -22,21 +22,16 @@
 #include "neon.S"
 
 .macro init_sao_band
-        ldr      r12, [sp, #0]    // offset_table address
         pld      [r1]
-        vld1.8   {q0, q1}, [r12]  // offset table
-        ldr      r12, [sp, #4]    // height
+        vld1.8   {q0, q1}, [r2]  // offset table
+        ldr       r2, [sp, #0]   // stride_dst
+        ldr      r12, [sp, #4]   // height
         vmov.u8  q3, #128
 .endm
 
 // 128 in q3
 // input q8 - q11
-// 32 cycles
 .macro sao_band_64
-        vshr.u8  q12, q8, #3
-        vshr.u8  q13, q9, #3
-        vshr.u8  q14, q10, #3
-        vshr.u8  q15, q11, #3
         vtbl.8   d24, {d0, d1, d2, d3}, d24
         vadd.s8  q8, q3
         vtbl.8   d25, {d0, d1, d2, d3}, d25
@@ -52,8 +47,8 @@
         vtbl.8   d30, {d0, d1, d2, d3}, d30
         vqadd.s8 q10, q14
         vtbl.8   d31, {d0, d1, d2, d3}, d31
-        vqadd.s8 q11, q15
         vsub.s8  q8, q3
+        vqadd.s8 q11, q15
         vsub.s8  q9, q3
         vsub.s8  q10, q3
         vsub.s8  q11, q3
@@ -64,12 +59,16 @@ function ff_hevc_sao_band_w8_neon_8, export=1
 1:      subs     r12, #8
         vld1.8   {d16}, [r1, :64], r3
         vld1.8   {d17}, [r1, :64], r3
+        vshr.u8  q12, q8, #3
         vld1.8   {d18}, [r1, :64], r3
         vld1.8   {d19}, [r1, :64], r3
+        vshr.u8  q13, q9, #3
         vld1.8   {d20}, [r1, :64], r3
         vld1.8   {d21}, [r1, :64], r3
+        vshr.u8  q14, q10, #3
         vld1.8   {d22}, [r1, :64], r3
         vld1.8   {d23}, [r1, :64], r3
+        vshr.u8  q15, q11, #3
         sao_band_64
         vst1.8  {d16}, [r0, :64], r2
         vst1.8  {d17}, [r0, :64], r2
@@ -88,9 +87,13 @@ function ff_hevc_sao_band_w16_neon_8, export=1
         init_sao_band
 1:      subs     r12, #4
         vld1.8  {q8}, [r1, :128], r3
+        vshr.u8  q12, q8, #3
         vld1.8  {q9}, [r1, :128], r3
+        vshr.u8  q13, q9, #3
         vld1.8  {q10}, [r1, :128], r3
+        vshr.u8  q14, q10, #3
         vld1.8  {q11}, [r1, :128], r3
+        vshr.u8  q15, q11, #3
         sao_band_64
         vst1.8   {q8}, [r0, :128], r2
         vst1.8   {q9}, [r0, :128], r2
@@ -105,7 +108,11 @@ function ff_hevc_sao_band_w32_neon_8, export=1
         init_sao_band
 1:      subs     r12, #2
         vld1.8   {q8-q9}, [r1, :128], r3
+        vshr.u8  q12, q8, #3
+        vshr.u8  q13, q9, #3
         vld1.8   {q10-q11}, [r1, :128], r3
+        vshr.u8  q14, q10, #3
+        vshr.u8  q15, q11, #3
         sao_band_64
         vst1.8   {q8-q9}, [r0, :128], r2
         vst1.8   {q10-q11}, [r0, :128], r2
@@ -119,7 +126,11 @@ function ff_hevc_sao_band_w64_neon_8, export=1
 1:      subs      r12, #1
         pld       [r1, r3]
         vld1.8    {q8-q9}, [r1, :128]!
+        vshr.u8  q12, q8, #3
+        vshr.u8  q13, q9, #3
         vld1.8    {q10-q11}, [r1, :128], r3
+        vshr.u8  q14, q10, #3
+        vshr.u8  q15, q11, #3
         sub       r1, #32
         sao_band_64
         vst1.8    {q8-q9}, [r0, :128]!
@@ -129,51 +140,18 @@ function ff_hevc_sao_band_w64_neon_8, export=1
 
         bx lr
 endfunc
-// input
-// a in q0 - q3
-// c in q4 - q7
-// b in q8 - q11
-// offset table in r7 and r5
-// output in q0 - q3
-// clobbers q12 - q15
-.macro edge_w64_body
-        vcgt.u8 q12,  q4, q0 // c > a -> -1 , otherwise 0
-        vcgt.u8  q0,  q0, q4 // a > c -> -1 , otherwise 0
-        vcgt.u8 q13,  q5, q1
-        vcgt.u8  q1,  q1, q5
-        vsub.s8 q12,  q0, q12 // diff0
-        vcgt.u8  q0,  q4, q8 // c > b
-        vsub.s8 q13,  q1, q13
-
-        vcgt.u8 q14,  q8, q4 // b > c
-        vcgt.u8  q1,  q5, q9
-        vcgt.u8 q15,  q9, q5
-        vsub.s8  q0, q14, q0 // diff1
-        vsub.s8  q1, q15, q1
 
-        vadd.s8  q0, q12 //diff0 + diff1
-        vadd.s8  q1, q13
-
-        vcgt.u8 q14,  q6, q2
-        vcgt.u8  q2,  q2, q6
-        vcgt.u8 q15,  q7, q3
-        vcgt.u8  q3,  q3, q7
-
-        vsub.s8 q14,  q2, q14
-        vcgt.u8  q2,  q6, q10
-        vsub.s8 q15,  q3, q15
-
-        vcgt.u8 q12, q10, q6
-        vcgt.u8  q3,  q7, q11
-        vcgt.u8 q13, q11, q7
-        vsub.s8  q2, q12, q2
-        vsub.s8  q3, q13, q3
+.macro diff32 out0, out1, tmp0, tmp1, in0, in1, in2, in3
+        vcgt.u8 \out0, \in2, \in0  // c > a -> -1 , otherwise 0
+        vcgt.u8 \tmp0,  \in0, \in2  // a > c -> -1 , otherwise 0
+        vcgt.u8 \out1, \in3, \in1  // c > a -> -1 , otherwise 0 part 2
+        vcgt.u8 \tmp1,  \in1, \in3  // a > c -> -1 , otherwise 0 part 2
+        vsub.s8 \out0, \tmp0, \out0 // diff0
+        vsub.s8 \out1, \tmp1, \out1 // diff0 part 2
+.endm
 
+.macro table64
         vmov.s8 q13, #2 // 2 to all elements
-
-        vadd.s8  q2, q14
-        vadd.s8  q3, q15
-
         vmov.32  d24[0], r4  // load offset table from general registers
         vmov.32  d24[1], r5  // load rest of offset table
 
@@ -208,6 +186,28 @@ endfunc
         sub     r0, #32
 .endm
 
+// input
+// a in q0 - q3
+// c in q4 - q7
+// b in q8 - q11
+// offset table in r7 and r5
+// output in q0 - q3
+// clobbers q12 - q15
+.macro edge_w64_body
+        diff32 q12, q13, q0, q1, q0, q1, q4, q5
+        diff32 q0, q1, q14, q15, q8, q9, q4, q5
+
+        vadd.s8  q0, q12 //diff0 + diff1
+        vadd.s8  q1, q13
+
+        diff32  q14, q15, q2, q3, q2, q3, q6, q7
+        diff32  q2, q3, q12, q13, q10, q11, q6, q7
+
+        vadd.s8  q2, q14
+        vadd.s8  q3, q15
+        table64
+.endm
+
 .macro init_edge_64
         push   {r4-r5}
         ldr    r12, [sp, #8] // height
@@ -334,38 +334,23 @@ function ff_hevc_sao_edge_eo3_w64_neon_8, export=1
         bx lr
 endfunc
 
-// inputs:
-// a in q0, q1
-// c in q2, q3
-// b in q8, q9
-// offset table in d31
-// clobbered registers q0, q1, q10, q11, q12, q13
-// output q0, q1
-.macro edge_w32_body
-        vcgt.u8 q12, q2, q0 // c > a -> -1 , otherwise 0
-        vcgt.u8 q0,  q0, q2 // a > c -> -1 , otherwise 0
-        vcgt.u8 q13, q3, q1
-        vcgt.u8 q1,  q1, q3
-
-        vsub.s8 q12, q0, q12 // diff0
-        vcgt.u8  q0,  q2, q8 // c > b
-        vsub.s8 q13, q1, q13 // diff0 part 2
-
-        vcgt.u8  q10,  q8, q2 // b > c
-        vcgt.u8  q1,  q3, q9
-        vcgt.u8  q11,  q9, q3
-
-        vsub.s8 q0, q10, q0 // diff1
-
-        vmov.s8 q10, #2 // 2 to all elements
-        vsub.s8 q1, q11, q1 // diff1 part 2
-        vadd.s8 q0, q12 //diff0 + diff1
-        vadd.s8 q1, q13
+.macro init_edge_32
+        ldr     r12, [sp, #4] // sao_offset_val_table
+        vld1.32 {d31}, [r12]
+        ldr     r12, [sp] // height
+.endm
 
-        vadd.s8 q0, q10
-        vadd.s8 q1, q10
+.macro diff out0, tmp0, in0, in1
+        vcgt.u8 \out0, \in1, \in0  // c > a -> -1 , otherwise 0
+        vcgt.u8 \tmp0,  \in0, \in1  // a > c -> -1 , otherwise 0
+        vsub.s8 \out0, \tmp0, \out0 // diff0
+.endm
 
-        vmov.u8  q10, #128
+.macro table32
+        vmov.s8  q10, #2
+        vadd.s8  q0, q10
+        vadd.s8  q1, q10
+        vmov.s8  q10, #128
         vtbl.8   d0, {d31}, d0
         vadd.s8  q11, q2, q10
         vtbl.8   d1, {d31}, d1
@@ -373,56 +358,68 @@ endfunc
         vtbl.8   d2, {d31}, d2
         vqadd.s8 q11, q0
         vtbl.8   d3, {d31}, d3
-        vqadd.s8   q12, q1
-        vsub.s8    q0, q11, q10
-        vsub.s8    q1, q12, q10
+        vqadd.s8 q12, q1
+        vsub.s8  q0, q11, q10
+        vsub.s8  q1, q12, q10
         vst1.8   {q0-q1}, [r0, :128], r2
 .endm
 
-.macro init_edge_32
-        ldr     r12, [sp, #4] // sao_offset_val_table
-        vld1.32 {d31}, [r12]
-        ldr     r12, [sp] // height
-.endm
-
 function ff_hevc_sao_edge_eo0_w32_neon_8, export=1
         init_edge_32
-        sub     r1, #4 // load 4 extra bytes
+        vpush {q4-q7}
+        sub     r1, #4
 1:      subs    r12, #1
-        vld1.32 d3[1], [r1]!
-        vld1.8  {q2-q3}, [r1, :128]! // c
-        vld1.32 d20[0], [r1], r3
-        sub     r1, #36
+        vld1.8  {q13-q14}, [r1]!
+        vld1.32 d30, [r1], r3
+        sub     r1, #32
         // a
-        vext.8  q0, q1, q2, #15
-        vext.8  q1, q2, q3, #15
-        // b
-        vext.8  q8, q2, q3, #1
-        vext.8  q9, q3, q10, #1
-        edge_w32_body
+        vext.8   q0, q13, q14, #3
+        vext.8   q1, q14, q15, #3
+        vshr.u64 d24, d30, #24
+        // c
+        vext.8   q2, q13, q14, #4
+        vext.8   q3, q14, q15, #4
+        vshr.u64 d16, d30, #32
+        // diff0
+        diff32 q13, q14, q4, q5, q0, q1, q2, q3
+        diff   d18, d25, d24, d16
+        // -diff1
+        vext.s8 q0, q13, q14, #1
+        vext.s8 q1, q14, q9, #1
+
+        vsub.s8 q0, q13, q0 //diff0 + diff1
+        vsub.s8 q1, q14, q1
+        table32
         bne     1b
+        vpop {q4-q7}
+
         bx      lr
 endfunc
 
 function ff_hevc_sao_edge_eo1_w32_neon_8, export=1
         init_edge_32
+        vpush {q4-q7}
         // load a
         sub     r1, r3
         vld1.8  {q0-q1}, [r1, :128], r3
         // load c
         vld1.8  {q2-q3}, [r1, :128], r3
+        diff32 q12, q13, q0, q1, q0, q1, q2, q3 // CMP ( c, a )
 1:      subs    r12, #1
         // load b
         vld1.8  {q8-q9}, [r1, :128], r3
-        edge_w32_body
-        // inputs for next loop iteration
-        // a
-        vmov.64 q0, q2
-        vmov.64 q1, q3
+        diff32 q4, q5, q10, q11, q8, q9, q2, q3 // CMP ( c, b )
+        vadd.s8 q0, q4, q12 //diff0 + diff1
+        vadd.s8 q1, q5, q13
+        table32
+        // CMP ( c, a )
+        vneg.s8 q12, q4
+        vneg.s8 q13, q5
         // c
         vmov.64 q2, q8
         vmov.64 q3, q9
         bne     1b
+        vpop {q4-q7}
         bx      lr
 endfunc
 
@@ -452,7 +449,11 @@ function ff_hevc_sao_edge_eo2_w32_neon_8, export=1
         vext.8  q6, q10, q11, #8
         vext.8  q7, q11, q12, #8
         vext.8  q5, q10, q11, #7
-        edge_w32_body
+        diff32 q12, q13, q0, q1, q0, q1, q2, q3
+        diff32 q0, q1, q10, q11, q8, q9, q2, q3
+        vadd.s8 q0, q12 //diff0 + diff1
+        vadd.s8 q1, q13
+        table32
         // inputs for next loop iteration
         // a
         vmov.8  q0, q4
@@ -487,7 +488,14 @@ function ff_hevc_sao_edge_eo3_w32_neon_8, export=1
         vext.8  q8, q10, q11, #7
         vext.8  q9, q11, q12, #7
         vext.8  q14, q12, q10, #7
-        edge_w32_body
+
+        diff32 q12, q13, q0, q1, q0, q1, q2, q3
+        diff32 q0, q1, q10, q11, q8, q9, q2, q3
+
+        vadd.s8 q0, q12 //diff0 + diff1
+        vadd.s8 q1, q13
+        table32
+
         // inputs for next loop iteration
         // a
         vext.8  q0, q2, q3, #1
-- 
2.5.0


