x86/hevc: add ff_hevc_sao_band_filter_{8,10,12}_{sse2,avx,avx2}
authorJames Almer <jamrial@gmail.com>
Sun, 1 Feb 2015 18:01:36 +0000 (15:01 -0300)
committerJames Almer <jamrial@gmail.com>
Sun, 1 Feb 2015 23:22:35 +0000 (20:22 -0300)
Original x86 intrinsics code and initial 8bit yasm port by Pierre-Edouard Lepere.
10/12bit yasm ports, refactoring and optimizations by James Almer

Benchmarks of BQTerrace_1920x1080_60_qp22.bin with an Intel Core i5-4200U

width 32
40338 decicycles in sao_band_filter_0_8, 2048 runs, 0 skips
8056 decicycles in ff_hevc_sao_band_filter_8_32_sse2, 2048 runs, 0 skips
7458 decicycles in ff_hevc_sao_band_filter_8_32_avx, 2048 runs, 0 skips
4504 decicycles in ff_hevc_sao_band_filter_8_32_avx2, 2048 runs, 0 skips

width 64
136046 decicycles in sao_band_filter_0_8, 16384 runs, 0 skips
28576 decicycles in ff_hevc_sao_band_filter_8_32_sse2, 16384 runs, 0 skips
26707 decicycles in ff_hevc_sao_band_filter_8_32_avx, 16384 runs, 0 skips
14387 decicycles in ff_hevc_sao_band_filter_8_32_avx2, 16384 runs, 0 skips

Reviewed-by: Christophe Gisquet <christophe.gisquet@gmail.com>
Signed-off-by: James Almer <jamrial@gmail.com>
libavcodec/hevc_filter.c
libavcodec/hevcdsp.c
libavcodec/hevcdsp.h
libavcodec/x86/Makefile
libavcodec/x86/hevc_sao.asm [new file with mode: 0644]
libavcodec/x86/hevcdsp_init.c

index fb1aa37..9354c14 100644 (file)
@@ -187,6 +187,7 @@ static void restore_tqb_pixels(HEVCContext *s, int x0, int y0, int width, int he
 
 static void sao_filter_CTB(HEVCContext *s, int x, int y)
 {
+    static const uint8_t band_tab[8] = { 0, 1, 2, 2, 3, 3, 4, 4 };
     int c_idx;
     int edges[4];  // 0 left 1 top 2 right 3 bottom
     int x_ctb                = x >> s->sps->log2_ctb_size;
@@ -252,15 +253,16 @@ static void sao_filter_CTB(HEVCContext *s, int x, int y)
         int ctb_size_v = (1 << (s->sps->log2_ctb_size)) >> s->sps->vshift[c_idx];
         int width    = FFMIN(ctb_size_h, (s->sps->width  >> s->sps->hshift[c_idx]) - x0);
         int height   = FFMIN(ctb_size_v, (s->sps->height >> s->sps->vshift[c_idx]) - y0);
+        int tab      = band_tab[(FFALIGN(width, 8) >> 3) - 1];
         uint8_t *src = &s->frame->data[c_idx][y0 * stride_src + (x0 << s->sps->pixel_shift)];
         uint8_t *dst = &s->sao_frame->data[c_idx][y0 * stride_dst + (x0 << s->sps->pixel_shift)];
 
         switch (sao->type_idx[c_idx]) {
         case SAO_BAND:
             copy_CTB(dst, src, width << s->sps->pixel_shift, height, stride_dst, stride_src);
-            s->hevcdsp.sao_band_filter(src, dst, stride_src, stride_dst,
-                                       sao->offset_val[c_idx], sao->band_position[c_idx],
-                                       width, height);
+            s->hevcdsp.sao_band_filter[tab](src, dst, stride_src, stride_dst,
+                                            sao->offset_val[c_idx], sao->band_position[c_idx],
+                                            width, height);
             restore_tqb_pixels(s, x, y, width, height, c_idx);
             sao->type_idx[c_idx] = SAO_APPLIED;
             break;
index 3eae541..8acad1b 100644 (file)
@@ -212,7 +212,11 @@ void ff_hevc_dsp_init(HEVCDSPContext *hevcdsp, int bit_depth)
     hevcdsp->idct_dc[2]             = FUNC(idct_16x16_dc, depth);           \
     hevcdsp->idct_dc[3]             = FUNC(idct_32x32_dc, depth);           \
                                                                             \
-    hevcdsp->sao_band_filter    = FUNC(sao_band_filter_0, depth);              \
+    hevcdsp->sao_band_filter[0] =                                              \
+    hevcdsp->sao_band_filter[1] =                                              \
+    hevcdsp->sao_band_filter[2] =                                              \
+    hevcdsp->sao_band_filter[3] =                                              \
+    hevcdsp->sao_band_filter[4] = FUNC(sao_band_filter_0, depth);              \
     hevcdsp->sao_edge_filter[0] = FUNC(sao_edge_filter_0, depth);              \
     hevcdsp->sao_edge_filter[1] = FUNC(sao_edge_filter_1, depth);              \
                                                                                \
index a798fb1..6fb161b 100644 (file)
@@ -58,8 +58,8 @@ typedef struct HEVCDSPContext {
 
     void (*idct_dc[4])(int16_t *coeffs);
 
-    void (*sao_band_filter)(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);
+    void (*sao_band_filter[5])(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);
 
     void (*sao_edge_filter[2])(uint8_t *_dst, uint8_t *_src, ptrdiff_t _stride_dst, ptrdiff_t _stride_src,
                                struct SAOParams *sao, int *borders, int _width,
index 7c8e7aa..00dacda 100644 (file)
@@ -134,7 +134,8 @@ YASM-OBJS-$(CONFIG_DCA_DECODER)        += x86/dcadsp.o
 YASM-OBJS-$(CONFIG_HEVC_DECODER)       += x86/hevc_mc.o                 \
                                           x86/hevc_deblock.o            \
                                           x86/hevc_idct.o               \
-                                          x86/hevc_res_add.o
+                                          x86/hevc_res_add.o            \
+                                          x86/hevc_sao.o
 YASM-OBJS-$(CONFIG_MLP_DECODER)        += x86/mlpdsp.o
 YASM-OBJS-$(CONFIG_PNG_DECODER)        += x86/pngdsp.o
 YASM-OBJS-$(CONFIG_PRORES_DECODER)     += x86/proresdsp.o
diff --git a/libavcodec/x86/hevc_sao.asm b/libavcodec/x86/hevc_sao.asm
new file mode 100644 (file)
index 0000000..7f36fd0
--- /dev/null
@@ -0,0 +1,238 @@
+;******************************************************************************
+;* SIMD optimized SAO functions for HEVC decoding
+;*
+;* Copyright (c) 2013 Pierre-Edouard LEPERE
+;* Copyright (c) 2014 James Almer
+;*
+;* 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/x86/x86util.asm"
+
+%if ARCH_X86_64
+SECTION_RODATA 32
+
+pw_mask10: times 16 dw 0x03FF
+pw_mask12: times 16 dw 0x0FFF
+
+SECTION_TEXT
+
+%macro HEVC_SAO_BAND_FILTER_INIT 1
+    and            leftq, 31
+    movd             xm0, leftd
+    add            leftq, 1
+    and            leftq, 31
+    movd             xm1, leftd
+    add            leftq, 1
+    and            leftq, 31
+    movd             xm2, leftd
+    add            leftq, 1
+    and            leftq, 31
+    movd             xm3, leftd
+
+    SPLATW            m0, xm0
+    SPLATW            m1, xm1
+    SPLATW            m2, xm2
+    SPLATW            m3, xm3
+%if mmsize > 16
+    SPLATW            m4, [offsetq + 2]
+    SPLATW            m5, [offsetq + 4]
+    SPLATW            m6, [offsetq + 6]
+    SPLATW            m7, [offsetq + 8]
+%else
+    movq              m7, [offsetq + 2]
+    SPLATW            m4, m7, 0
+    SPLATW            m5, m7, 1
+    SPLATW            m6, m7, 2
+    SPLATW            m7, m7, 3
+%endif
+
+%if %1 > 8
+    mova             m13, [pw_mask %+ %1]
+%endif
+    pxor             m14, m14
+
+DEFINE_ARGS dst, src, dststride, srcstride, offset, height
+    mov          heightd, r7m
+%endmacro
+
+%macro HEVC_SAO_BAND_FILTER_COMPUTE 3
+    psraw             %2, %3, %1-5
+    pcmpeqw          m10, %2, m0
+    pcmpeqw          m11, %2, m1
+    pcmpeqw          m12, %2, m2
+    pcmpeqw           %2, m3
+    pand             m10, m4
+    pand             m11, m5
+    pand             m12, m6
+    pand              %2, m7
+    por              m10, m11
+    por              m12, %2
+    por              m10, m12
+    paddw             %3, m10
+%endmacro
+
+;void ff_hevc_sao_band_filter_<width>_8_<opt>(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);
+%macro HEVC_SAO_BAND_FILTER_8 2
+cglobal hevc_sao_band_filter_%1_8, 6, 6, 15, dst, src, dststride, srcstride, offset, left
+    HEVC_SAO_BAND_FILTER_INIT 8
+
+align 16
+.loop
+%if %1 == 8
+    movq              m8, [srcq]
+    punpcklbw         m8, m14
+    HEVC_SAO_BAND_FILTER_COMPUTE 8, m9, m8
+    packuswb          m8, m14
+    movq          [dstq], m8
+%endif ; %1 == 8
+
+%assign i 0
+%rep %2
+    movu             m13, [srcq + i]
+    punpcklbw         m8, m13, m14
+    HEVC_SAO_BAND_FILTER_COMPUTE 8, m9,  m8
+    punpckhbw        m13, m14
+    HEVC_SAO_BAND_FILTER_COMPUTE 8, m9, m13
+    packuswb          m8, m13
+    movu      [dstq + i], m8
+%assign i i+mmsize
+%endrep
+
+%if %1 == 48
+INIT_XMM cpuname
+
+    movu             m13, [srcq + i]
+    punpcklbw         m8, m13, m14
+    HEVC_SAO_BAND_FILTER_COMPUTE 8, m9,  m8
+    punpckhbw        m13, m14
+    HEVC_SAO_BAND_FILTER_COMPUTE 8, m9, m13
+    packuswb          m8, m13
+    movu      [dstq + i], m8
+%assign i i+16
+%endif ; %1 == 48
+
+    add             dstq, dststrideq             ; dst += dststride
+    add             srcq, srcstrideq             ; src += srcstride
+    dec          heightd                         ; cmp height
+    jnz               .loop                      ; height loop
+    REP_RET
+%endmacro
+
+;void ff_hevc_sao_band_filter_<width>_<depth>_<opt>(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);
+%macro HEVC_SAO_BAND_FILTER_16 3
+cglobal hevc_sao_band_filter_%2_%1, 6, 6, 15, dst, src, dststride, srcstride, offset, left
+    HEVC_SAO_BAND_FILTER_INIT %1
+
+align 16
+.loop
+%if %2 == 8
+    movu              m8, [srcq]
+    HEVC_SAO_BAND_FILTER_COMPUTE %1, m9, m8
+    CLIPW             m8, m14, m13
+    movu          [dstq], m8
+%endif
+
+%assign i 0
+%rep %3
+    movu              m8, [srcq + i]
+    HEVC_SAO_BAND_FILTER_COMPUTE %1, m9, m8
+    CLIPW             m8, m14, m13
+    movu      [dstq + i], m8
+
+    movu              m9, [srcq + i + mmsize]
+    HEVC_SAO_BAND_FILTER_COMPUTE %1, m8, m9
+    CLIPW             m9, m14, m13
+    movu      [dstq + i + mmsize], m9
+%assign i i+mmsize*2
+%endrep
+
+%if %2 == 48
+INIT_XMM cpuname
+    movu              m8, [srcq + i]
+    HEVC_SAO_BAND_FILTER_COMPUTE %1, m9, m8
+    CLIPW             m8, m14, m13
+    movu      [dstq + i], m8
+
+    movu              m9, [srcq + i + mmsize]
+    HEVC_SAO_BAND_FILTER_COMPUTE %1, m8, m9
+    CLIPW             m9, m14, m13
+    movu      [dstq + i + mmsize], m9
+%assign i i+32
+%endif ; %1 == 48
+
+    add             dstq, dststrideq
+    add             srcq, srcstrideq
+    dec          heightd
+    jg .loop
+    REP_RET
+%endmacro
+
+%macro HEVC_SAO_BAND_FILTER_FUNCS 0
+HEVC_SAO_BAND_FILTER_8       8, 0
+HEVC_SAO_BAND_FILTER_8      16, 1
+HEVC_SAO_BAND_FILTER_8      32, 2
+HEVC_SAO_BAND_FILTER_8      48, 2
+HEVC_SAO_BAND_FILTER_8      64, 4
+
+HEVC_SAO_BAND_FILTER_16 10,  8, 0
+HEVC_SAO_BAND_FILTER_16 10, 16, 1
+HEVC_SAO_BAND_FILTER_16 10, 32, 2
+HEVC_SAO_BAND_FILTER_16 10, 48, 2
+HEVC_SAO_BAND_FILTER_16 10, 64, 4
+
+HEVC_SAO_BAND_FILTER_16 12,  8, 0
+HEVC_SAO_BAND_FILTER_16 12, 16, 1
+HEVC_SAO_BAND_FILTER_16 12, 32, 2
+HEVC_SAO_BAND_FILTER_16 12, 48, 2
+HEVC_SAO_BAND_FILTER_16 12, 64, 4
+%endmacro
+
+INIT_XMM sse2
+HEVC_SAO_BAND_FILTER_FUNCS
+INIT_XMM avx
+HEVC_SAO_BAND_FILTER_FUNCS
+
+INIT_XMM avx2
+HEVC_SAO_BAND_FILTER_8       8, 0
+HEVC_SAO_BAND_FILTER_8      16, 1
+INIT_YMM avx2
+HEVC_SAO_BAND_FILTER_8      32, 1
+HEVC_SAO_BAND_FILTER_8      48, 1
+INIT_YMM avx2
+HEVC_SAO_BAND_FILTER_8      64, 2
+
+INIT_XMM avx2
+HEVC_SAO_BAND_FILTER_16 10,  8, 0
+HEVC_SAO_BAND_FILTER_16 10, 16, 1
+INIT_YMM avx2
+HEVC_SAO_BAND_FILTER_16 10, 32, 1
+HEVC_SAO_BAND_FILTER_16 10, 48, 1
+INIT_YMM avx2
+HEVC_SAO_BAND_FILTER_16 10, 64, 2
+
+INIT_XMM avx2
+HEVC_SAO_BAND_FILTER_16 12,  8, 0
+HEVC_SAO_BAND_FILTER_16 12, 16, 1
+INIT_YMM avx2
+HEVC_SAO_BAND_FILTER_16 12, 32, 1
+HEVC_SAO_BAND_FILTER_16 12, 48, 1
+INIT_YMM avx2
+HEVC_SAO_BAND_FILTER_16 12, 64, 2
+%endif
index eaa97e1..5a01ff6 100644 (file)
@@ -478,6 +478,27 @@ mc_bi_w_funcs(qpel_v, 12, sse4);
 mc_bi_w_funcs(qpel_hv, 12, sse4);
 #endif //ARCH_X86_64 && HAVE_SSE4_EXTERNAL
 
+#define SAO_BAND_FILTER_FUNCS(bitd, opt)                                                                                   \
+void ff_hevc_sao_band_filter_8_##bitd##_##opt(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);           \
+void ff_hevc_sao_band_filter_16_##bitd##_##opt(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);           \
+void ff_hevc_sao_band_filter_32_##bitd##_##opt(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);           \
+void ff_hevc_sao_band_filter_48_##bitd##_##opt(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);           \
+void ff_hevc_sao_band_filter_64_##bitd##_##opt(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)
+
+SAO_BAND_FILTER_FUNCS(8,  sse2);
+SAO_BAND_FILTER_FUNCS(10, sse2);
+SAO_BAND_FILTER_FUNCS(12, sse2);
+SAO_BAND_FILTER_FUNCS(8,   avx);
+SAO_BAND_FILTER_FUNCS(10,  avx);
+SAO_BAND_FILTER_FUNCS(12,  avx);
+SAO_BAND_FILTER_FUNCS(8,  avx2);
+SAO_BAND_FILTER_FUNCS(10, avx2);
+SAO_BAND_FILTER_FUNCS(12, avx2);
 
 #define EPEL_LINKS(pointer, my, mx, fname, bitd, opt )           \
         PEL_LINK(pointer, 1, my , mx , fname##4 ,  bitd, opt ); \
@@ -499,6 +520,13 @@ mc_bi_w_funcs(qpel_hv, 12, sse4);
         PEL_LINK(pointer, 8, my , mx , fname##48,  bitd, opt ); \
         PEL_LINK(pointer, 9, my , mx , fname##64,  bitd, opt )
 
+#define SAO_BAND_INIT(bitd, opt) do {                                       \
+    c->sao_band_filter[0]      = ff_hevc_sao_band_filter_8_##bitd##_##opt;  \
+    c->sao_band_filter[1]      = ff_hevc_sao_band_filter_16_##bitd##_##opt; \
+    c->sao_band_filter[2]      = ff_hevc_sao_band_filter_32_##bitd##_##opt; \
+    c->sao_band_filter[3]      = ff_hevc_sao_band_filter_48_##bitd##_##opt; \
+    c->sao_band_filter[4]      = ff_hevc_sao_band_filter_64_##bitd##_##opt; \
+} while (0)
 
 void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
 {
@@ -516,6 +544,8 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
             if (ARCH_X86_64) {
                 c->hevc_v_loop_filter_luma = ff_hevc_v_loop_filter_luma_8_sse2;
                 c->hevc_h_loop_filter_luma = ff_hevc_h_loop_filter_luma_8_sse2;
+
+                SAO_BAND_INIT(8, sse2);
             }
             c->idct_dc[1] = ff_hevc_idct8x8_dc_8_sse2;
             c->idct_dc[2] = ff_hevc_idct16x16_dc_8_sse2;
@@ -547,6 +577,8 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
             if (ARCH_X86_64) {
                 c->hevc_v_loop_filter_luma = ff_hevc_v_loop_filter_luma_8_avx;
                 c->hevc_h_loop_filter_luma = ff_hevc_h_loop_filter_luma_8_avx;
+
+                SAO_BAND_INIT(8, avx);
             }
             c->transform_add[1]    = ff_hevc_transform_add8_8_avx;
             c->transform_add[2]    = ff_hevc_transform_add16_8_avx;
@@ -555,6 +587,9 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
         if (EXTERNAL_AVX2(cpu_flags)) {
             c->idct_dc[2] = ff_hevc_idct16x16_dc_8_avx2;
             c->idct_dc[3] = ff_hevc_idct32x32_dc_8_avx2;
+            if (ARCH_X86_64) {
+                SAO_BAND_INIT(8, avx2);
+            }
 
             c->transform_add[3]    = ff_hevc_transform_add32_8_avx2;
         }
@@ -570,6 +605,8 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
             if (ARCH_X86_64) {
                 c->hevc_v_loop_filter_luma = ff_hevc_v_loop_filter_luma_10_sse2;
                 c->hevc_h_loop_filter_luma = ff_hevc_h_loop_filter_luma_10_sse2;
+
+                SAO_BAND_INIT(10, sse2);
             }
 
             c->idct_dc[1] = ff_hevc_idct8x8_dc_10_sse2;
@@ -601,12 +638,17 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
             if (ARCH_X86_64) {
                 c->hevc_v_loop_filter_luma = ff_hevc_v_loop_filter_luma_10_avx;
                 c->hevc_h_loop_filter_luma = ff_hevc_h_loop_filter_luma_10_avx;
+
+                SAO_BAND_INIT(10, avx);
             }
         }
         if (EXTERNAL_AVX2(cpu_flags)) {
 
             c->idct_dc[2] = ff_hevc_idct16x16_dc_10_avx2;
             c->idct_dc[3] = ff_hevc_idct32x32_dc_10_avx2;
+            if (ARCH_X86_64) {
+                SAO_BAND_INIT(10, avx2);
+            }
 
             c->transform_add[2] = ff_hevc_transform_add16_10_avx2;
             c->transform_add[3] = ff_hevc_transform_add32_10_avx2;
@@ -623,6 +665,8 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
             if (ARCH_X86_64) {
                 c->hevc_v_loop_filter_luma = ff_hevc_v_loop_filter_luma_12_sse2;
                 c->hevc_h_loop_filter_luma = ff_hevc_h_loop_filter_luma_12_sse2;
+
+                SAO_BAND_INIT(12, sse2);
             }
 
             c->idct_dc[1] = ff_hevc_idct8x8_dc_12_sse2;
@@ -650,11 +694,16 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
             if (ARCH_X86_64) {
                 c->hevc_v_loop_filter_luma = ff_hevc_v_loop_filter_luma_12_avx;
                 c->hevc_h_loop_filter_luma = ff_hevc_h_loop_filter_luma_12_avx;
+
+                SAO_BAND_INIT(12, avx);
             }
         }
         if (EXTERNAL_AVX2(cpu_flags)) {
             c->idct_dc[2] = ff_hevc_idct16x16_dc_12_avx2;
             c->idct_dc[3] = ff_hevc_idct32x32_dc_12_avx2;
+            if (ARCH_X86_64) {
+                SAO_BAND_INIT(12, avx2);
+            }
         }
     }
 }