shithub: dav1d

Download patch

ref: 791ec2199a6773eaef6abd6457b492fb40207422
parent: 80650d4ce3ff977420fab8fc9deeb3b4de98fb70
author: Victorien Le Couviour--Tuffet <[email protected]>
date: Fri Feb 8 10:30:48 EST 2019

x86: add SSSE3 cdef filters implementation

AVX2 adaption

---------------------
x86_64:
------------------------------------------
cdef_filter_4x4_8bpc_c: 1370.2
cdef_filter_4x4_8bpc_ssse3: 142.3
cdef_filter_4x4_8bpc_avx2: 106.7
------------------------------------------
cdef_filter_4x8_8bpc_c: 2749.3
cdef_filter_4x8_8bpc_ssse3: 257.2
cdef_filter_4x8_8bpc_avx2: 178.8
------------------------------------------
cdef_filter_8x8_8bpc_c: 5609.5
cdef_filter_8x8_8bpc_ssse3: 438.1
cdef_filter_8x8_8bpc_avx2: 250.6
------------------------------------------

---------------------
x86_32:
------------------------------------------
cdef_filter_4x4_8bpc_c: 1548.7
cdef_filter_4x4_8bpc_ssse3: 179.8
------------------------------------------
cdef_filter_4x8_8bpc_c: 3128.2
cdef_filter_4x8_8bpc_ssse3: 328.1
------------------------------------------
cdef_filter_8x8_8bpc_c: 6454.5
cdef_filter_8x8_8bpc_ssse3: 584.4
------------------------------------------

--- a/src/meson.build
+++ b/src/meson.build
@@ -119,6 +119,7 @@
         # NASM source files
         libdav1d_sources_asm = files(
             'x86/cdef.asm',
+            'x86/cdef_ssse3.asm',
             'x86/cpuid.asm',
             'x86/ipred.asm',
             'x86/itx.asm',
--- a/src/x86/cdef_init_tmpl.c
+++ b/src/x86/cdef_init_tmpl.c
@@ -29,13 +29,26 @@
 #include "src/cdef.h"
 
 decl_cdef_fn(dav1d_cdef_filter_8x8_avx2);
+decl_cdef_fn(dav1d_cdef_filter_8x8_ssse3);
+
 decl_cdef_fn(dav1d_cdef_filter_4x8_avx2);
+decl_cdef_fn(dav1d_cdef_filter_4x8_ssse3);
+
 decl_cdef_fn(dav1d_cdef_filter_4x4_avx2);
+decl_cdef_fn(dav1d_cdef_filter_4x4_ssse3);
 
 decl_cdef_dir_fn(dav1d_cdef_dir_avx2);
 
 void bitfn(dav1d_cdef_dsp_init_x86)(Dav1dCdefDSPContext *const c) {
     const unsigned flags = dav1d_get_cpu_flags();
+
+    if (!(flags & DAV1D_X86_CPU_FLAG_SSSE3)) return;
+
+#if BITDEPTH ==8
+    c->fb[0] = dav1d_cdef_filter_8x8_ssse3;
+    c->fb[1] = dav1d_cdef_filter_4x8_ssse3;
+    c->fb[2] = dav1d_cdef_filter_4x4_ssse3;
+#endif
 
     if (!(flags & DAV1D_X86_CPU_FLAG_AVX2)) return;
 
--- /dev/null
+++ b/src/x86/cdef_ssse3.asm
@@ -1,0 +1,713 @@
+; Copyright © 2018, VideoLAN and dav1d authors
+; Copyright © 2018, Two Orioles, LLC
+; Copyright © 2019, VideoLabs
+; All rights reserved.
+;
+; Redistribution and use in source and binary forms, with or without
+; modification, are permitted provided that the following conditions are met:
+;
+; 1. Redistributions of source code must retain the above copyright notice, this
+;    list of conditions and the following disclaimer.
+;
+; 2. Redistributions in binary form must reproduce the above copyright notice,
+;    this list of conditions and the following disclaimer in the documentation
+;    and/or other materials provided with the distribution.
+;
+; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+; ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+%include "config.asm"
+%include "ext/x86/x86inc.asm"
+
+SECTION_RODATA 16
+
+pb_0: times 16 db 0
+pw_256: times 8 dw 256
+pw_2048: times 8 dw 2048
+pw_0x7FFF: times 8 dw 0x7FFF
+tap_table: dw 4, 2, 3, 3, 2, 1
+           db -1 * 16 + 1, -2 * 16 + 2
+           db  0 * 16 + 1, -1 * 16 + 2
+           db  0 * 16 + 1,  0 * 16 + 2
+           db  0 * 16 + 1,  1 * 16 + 2
+           db  1 * 16 + 1,  2 * 16 + 2
+           db  1 * 16 + 0,  2 * 16 + 1
+           db  1 * 16 + 0,  2 * 16 + 0
+           db  1 * 16 + 0,  2 * 16 - 1
+           ; the last 6 are repeats of the first 6 so we don't need to & 7
+           db -1 * 16 + 1, -2 * 16 + 2
+           db  0 * 16 + 1, -1 * 16 + 2
+           db  0 * 16 + 1,  0 * 16 + 2
+           db  0 * 16 + 1,  1 * 16 + 2
+           db  1 * 16 + 1,  2 * 16 + 2
+           db  1 * 16 + 0,  2 * 16 + 1
+
+SECTION .text
+
+INIT_XMM ssse3
+
+%macro movif32 2
+ %if ARCH_X86_32
+    mov     %1, %2
+ %endif
+%endmacro
+
+%macro SAVE_ARG 2   ; varname, argnum
+ %define %1_stkloc  [rsp+%2*gprsize]
+ %define %1_argnum  %2
+    mov             r2, r%2m
+    mov      %1_stkloc, r2
+%endmacro
+
+%macro LOAD_ARG 1-2 0 ; varname, load_to_varname_register
+ %if %2 == 0
+    mov r %+ %{1}_argnum, %1_stkloc
+ %else
+    mov            %1q, %1_stkloc
+ %endif
+%endmacro
+
+%macro LOAD_ARG32 1-2 ; varname, load_to_varname_register
+ %if ARCH_X86_32
+  %if %0 == 1
+    LOAD_ARG %1
+  %else
+    LOAD_ARG %1, %2
+  %endif
+ %endif
+%endmacro
+
+%if ARCH_X86_32
+ %define PIC_base_offset $$
+ %define PIC_sym(sym) (PIC_reg+(sym)-PIC_base_offset)
+%else
+ %define PIC_sym(sym) sym
+%endif
+
+%macro SAVE_PIC_REG 1
+ %if ARCH_X86_32
+    mov       [esp+%1], PIC_reg
+ %endif
+%endmacro
+
+%macro LOAD_PIC_REG 1
+ %if ARCH_X86_32
+    mov        PIC_reg, [esp+%1]
+ %endif
+%endmacro
+
+%macro ACCUMULATE_TAP 6 ; tap_offset, shift, strength, mul_tap, w, stride
+ %if ARCH_X86_64
+    ; load p0/p1
+    movsx         offq, byte [dirq+kq+%1]       ; off1
+  %if %5 == 4
+    movq            m5, [stkq+offq*2+%6*0]      ; p0
+    movhps          m5, [stkq+offq*2+%6*1]
+  %else
+    movu            m5, [stkq+offq*2+%6*0]      ; p0
+  %endif
+    neg           offq                          ; -off1
+  %if %5 == 4
+    movq            m6, [stkq+offq*2+%6*0]      ; p1
+    movhps          m6, [stkq+offq*2+%6*1]
+  %else
+    movu            m6, [stkq+offq*2+%6*0]      ; p1
+  %endif
+    pcmpeqw         m9, m14, m5
+    pcmpeqw        m10, m14, m6
+    pandn           m9, m5
+    pandn          m10, m6
+    pmaxsw          m7, m9                      ; max after p0
+    pminsw          m8, m5                      ; min after p0
+    pmaxsw          m7, m10                     ; max after p1
+    pminsw          m8, m6                      ; min after p1
+
+    ; accumulate sum[m13] over p0/p1
+    psubw           m5, m4                      ; diff_p0(p0 - px)
+    psubw           m6, m4                      ; diff_p1(p1 - px)
+    pabsw           m9, m5
+    pabsw          m10, m6
+    mova           m12, m9
+    psrlw           m9, %2
+    psignw         m11, %4, m5
+    psubusw         m5, %3, m9
+    mova            m9, m10
+    pminsw          m5, m12                     ; constrain(diff_p0)
+    psrlw          m10, %2
+    psignw         m12, %4, m6
+    psubusw         m6, %3, m10
+    pmullw          m5, m11                     ; constrain(diff_p0) * taps
+    pminsw          m6, m9                      ; constrain(diff_p1)
+    pmullw          m6, m12                     ; constrain(diff_p1) * taps
+    paddw          m13, m5
+    paddw          m13, m6
+ %else
+    ; load p0
+    movsx         offq, byte [dirq+kq+%1]       ; off1
+  %if %5 == 4
+    movq            m5, [stkq+offq*2+%6*0]      ; p0
+    movhps          m5, [stkq+offq*2+%6*1]
+  %else
+    movu            m5, [stkq+offq*2+%6*0]      ; p0
+  %endif
+    pcmpeqw         m3, m5, [PIC_sym(pw_0x7FFF)]
+    pandn           m3, m5
+    pmaxsw          m7, m3                      ; max after p0
+    pminsw          m8, m5                      ; min after p0
+
+    ; accumulate sum[m7] over p0
+    psubw           m5, m4                      ; diff_p0(p0 - px)
+    psignw          m6, %4, m5                      ; constrain(diff_p0)
+    pabsw           m5, m5
+    mova            m3, m5
+    psrlw           m5, %2
+    paddsw          m5, %3
+    pandn           m5, [PIC_sym(pw_0x7FFF)]
+    pminsw          m5, m3
+    pmullw          m5, m6                      ; constrain(diff_p0) * taps
+    paddw          m13, m5
+
+    ; load p1
+    neg           offq                          ; -off1
+  %if %5 == 4
+    movq            m5, [stkq+offq*2+%6*0]      ; p1
+    movhps          m5, [stkq+offq*2+%6*1]
+  %else
+    movu            m5, [stkq+offq*2+%6*0]      ; p1
+  %endif
+    pcmpeqw         m3, m5, [PIC_sym(pw_0x7FFF)]
+    pandn           m3, m5
+    pmaxsw          m7, m3                      ; max after p1
+    pminsw          m8, m5                      ; min after p1
+
+    ; accumulate sum[m7] over p1
+    psubw           m5, m4                      ; diff_p1(p1 - px)
+    psignw          m6, %4, m5                  ; constrain(diff_p1)
+    pabsw           m5, m5
+    mova            m3, m5
+    psrlw           m5, %2
+    paddsw          m5, %3
+    pandn           m5, [PIC_sym(pw_0x7FFF)]
+    pminsw          m5, m3
+    pmullw          m5, m6                      ; constrain(diff_p1) * taps
+    paddw          m13, m5
+ %endif
+%endmacro
+
+%macro PMOVZXBW 2-3 0 ; %3 = half
+ %if %3 == 1
+    movd            %1, %2
+ %else
+    movq            %1, %2
+ %endif
+    punpcklbw       %1, m15
+%endmacro
+
+%macro LOAD_BODY 4  ; dst, src, block_width, tmp_stride
+ %if %3 == 4
+    PMOVZXBW        m0, [%2+strideq*0]
+    PMOVZXBW        m1, [%2+strideq*1]
+    PMOVZXBW        m2, [%2+strideq*2]
+    PMOVZXBW        m3, [%2+stride3q]
+ %else
+    movu            m0, [%2+strideq*0]
+    movu            m1, [%2+strideq*1]
+    movu            m2, [%2+strideq*2]
+    movu            m3, [%2+stride3q]
+    punpckhbw       m4, m0, m15
+    punpcklbw       m0, m15
+    punpckhbw       m5, m1, m15
+    punpcklbw       m1, m15
+    punpckhbw       m6, m2, m15
+    punpcklbw       m2, m15
+    punpckhbw       m7, m3, m15
+    punpcklbw       m3, m15
+ %endif
+    mova     [%1+0*%4], m0
+    mova     [%1+1*%4], m1
+    mova     [%1+2*%4], m2
+    mova     [%1+3*%4], m3
+ %if %3 == 8
+    mova [%1+0*%4+2*8], m4
+    mova [%1+1*%4+2*8], m5
+    mova [%1+2*%4+2*8], m6
+    mova [%1+3*%4+2*8], m7
+ %endif
+%endmacro
+
+%macro cdef_filter_fn 3 ; w, h, stride
+ %if ARCH_X86_64
+cglobal cdef_filter_%1x%2, 4, 9, 16, 3 * 16 + (%2+4)*%3, \
+                           dst, stride, left, top, pri, sec, stride3, dst4, edge
+    pcmpeqw        m14, m14
+    psrlw          m14, 1                   ; 0x7FFF
+    pxor           m15, m15
+
+  %define px rsp+3*16+2*%3
+ %else
+cglobal cdef_filter_%1x%2, 2, 7, 8, - 5 * 16 - (%2+4)*%3, \
+                           dst, stride, left, top, stride3, dst4, edge
+    SAVE_ARG      left, 2
+    SAVE_ARG       top, 3
+    SAVE_ARG       pri, 4
+    SAVE_ARG       sec, 5
+    SAVE_ARG       dir, 6
+    SAVE_ARG   damping, 7
+
+  %define PIC_reg r2
+    LEA        PIC_reg, PIC_base_offset
+
+  %define m15 [PIC_sym(pb_0)]
+
+  %define px esp+5*16+2*%3
+ %endif
+
+    mov          edged, r8m
+
+    ; prepare pixel buffers - body/right
+ %if %2 == 8
+    lea          dst4q, [dstq+strideq*4]
+ %endif
+    lea       stride3q, [strideq*3]
+    test         edged, 2                   ; have_right
+    jz .no_right
+    LOAD_BODY       px, dstq, %1, %3
+ %if %2 == 8
+    LOAD_BODY  px+4*%3, dst4q, %1, %3
+ %endif
+    jmp .body_done
+.no_right:
+    PMOVZXBW        m0, [dstq+strideq*0], %1 == 4
+    PMOVZXBW        m1, [dstq+strideq*1], %1 == 4
+    PMOVZXBW        m2, [dstq+strideq*2], %1 == 4
+    PMOVZXBW        m3, [dstq+stride3q ], %1 == 4
+ %if %2 == 8
+    PMOVZXBW        m4, [dst4q+strideq*0], %1 == 4
+    PMOVZXBW        m5, [dst4q+strideq*1], %1 == 4
+    PMOVZXBW        m6, [dst4q+strideq*2], %1 == 4
+    PMOVZXBW        m7, [dst4q+stride3q ], %1 == 4
+ %endif
+    mova     [px+0*%3], m0
+    mova     [px+1*%3], m1
+    mova     [px+2*%3], m2
+    mova     [px+3*%3], m3
+ %if %2 == 8
+    mova     [px+4*%3], m4
+    mova     [px+5*%3], m5
+    mova     [px+6*%3], m6
+    mova     [px+7*%3], m7
+    mov dword [px+4*%3+%1*2], 0x7FFF7FFF
+    mov dword [px+5*%3+%1*2], 0x7FFF7FFF
+    mov dword [px+6*%3+%1*2], 0x7FFF7FFF
+    mov dword [px+7*%3+%1*2], 0x7FFF7FFF
+ %endif
+    mov dword [px+0*%3+%1*2], 0x7FFF7FFF
+    mov dword [px+1*%3+%1*2], 0x7FFF7FFF
+    mov dword [px+2*%3+%1*2], 0x7FFF7FFF
+    mov dword [px+3*%3+%1*2], 0x7FFF7FFF
+.body_done:
+
+    ; top
+ %if ARCH_X86_64
+    DEFINE_ARGS dst, stride, left, top2, pri, sec, stride3, top1, edge
+ %else
+    DEFINE_ARGS dst, stride, left, top2, stride3, top1, edge
+ %endif
+    LOAD_ARG32     top
+    test         edged, 4                    ; have_top
+    jz .no_top
+    mov          top1q, [top2q+0*gprsize]
+    mov          top2q, [top2q+1*gprsize]
+    test         edged, 1                    ; have_left
+    jz .top_no_left
+    test         edged, 2                    ; have_right
+    jz .top_no_right
+ %if %1 == 4
+    PMOVZXBW        m0, [top1q-2]
+    PMOVZXBW        m1, [top2q-2]
+ %else
+    movu            m0, [top1q-4]
+    movu            m1, [top2q-4]
+    punpckhbw       m2, m0, m15
+    punpcklbw       m0, m15
+    punpckhbw       m3, m1, m15
+    punpcklbw       m1, m15
+    movu  [px-2*%3+8], m2
+    movu  [px-1*%3+8], m3
+ %endif
+    movu  [px-2*%3-%1], m0
+    movu  [px-1*%3-%1], m1
+    jmp .top_done
+.top_no_right:
+ %if %1 == 4
+    PMOVZXBW        m0, [top1q-%1]
+    PMOVZXBW        m1, [top2q-%1]
+    movu [px-2*%3-4*2], m0
+    movu [px-1*%3-4*2], m1
+ %else
+    movu            m0, [top1q-%1]
+    movu            m1, [top2q-%2]
+    punpckhbw       m2, m0, m15
+    punpcklbw       m0, m15
+    punpckhbw       m3, m1, m15
+    punpcklbw       m1, m15
+    mova [px-2*%3-8*2], m0
+    mova [px-2*%3-0*2], m2
+    mova [px-1*%3-8*2], m1
+    mova [px-1*%3-0*2], m3
+ %endif
+    mov dword [px-2*%3+%1*2], 0x7FFF7FFF
+    mov dword [px-1*%3+%1*2], 0x7FFF7FFF
+    jmp .top_done
+.top_no_left:
+    test         edged, 2                   ; have_right
+    jz .top_no_left_right
+ %if %1 == 4
+    PMOVZXBW        m0, [top1q]
+    PMOVZXBW        m1, [top2q]
+ %else
+    movu            m0, [top1q]
+    movu            m1, [top2q]
+    punpckhbw       m2, m0, m15
+    punpcklbw       m0, m15
+    punpckhbw       m3, m1, m15
+    punpcklbw       m1, m15
+    movd [px-2*%3+8*2], m2
+    movd [px-1*%3+8*2], m3
+ %endif
+    mova     [px-2*%3], m0
+    mova     [px-1*%3], m1
+    mov dword [px-2*%3-4], 0x7FFF7FFF
+    mov dword [px-1*%3-4], 0x7FFF7FFF
+    jmp .top_done
+.top_no_left_right:
+    PMOVZXBW        m0, [top1q], %1 == 4
+    PMOVZXBW        m1, [top2q], %1 == 4
+    mova     [px-2*%3], m0
+    mova     [px-1*%3], m1
+    mov dword [px-2*%3+%1*2], 0x7FFF7FFF
+    mov dword [px-1*%3+%1*2], 0x7FFF7FFF
+    mov dword [px-2*%3-4], 0X7FFF7FFF
+    mov dword [px-1*%3-4], 0X7FFF7FFF
+    jmp .top_done
+.no_top:
+ %if ARCH_X86_64
+    SWAP            m0, m14
+ %else
+    mova            m0, [PIC_sym(pw_0x7FFF)]
+ %endif
+    movu   [px-2*%3-4], m0
+    movu   [px-1*%3-4], m0
+ %if %1 == 8
+    movq   [px-2*%3+12], m0
+    movq   [px-1*%3+12], m0
+ %endif
+ %if ARCH_X86_64
+    SWAP            m0, m14
+ %endif
+.top_done:
+
+    ; left
+    test         edged, 1                   ; have_left
+    jz .no_left
+    SAVE_PIC_REG     0
+    LOAD_ARG32    left
+ %if %2 == 4
+    movq            m0, [leftq]
+ %else
+    movu            m0, [leftq]
+ %endif
+    LOAD_PIC_REG     0
+ %if %2 == 4
+    punpcklbw       m0, m15
+ %else
+    punpckhbw       m1, m0, m15
+    punpcklbw       m0, m15
+    movhlps         m3, m1
+    movd   [px+4*%3-4], m1
+    movd   [px+6*%3-4], m3
+    psrlq           m1, 32
+    psrlq           m3, 32
+    movd   [px+5*%3-4], m1
+    movd   [px+7*%3-4], m3
+ %endif
+    movhlps         m2, m0
+    movd   [px+0*%3-4], m0
+    movd   [px+2*%3-4], m2
+    psrlq           m0, 32
+    psrlq           m2, 32
+    movd   [px+1*%3-4], m0
+    movd   [px+3*%3-4], m2
+    jmp .left_done
+.no_left:
+    mov dword [px+0*%3-4], 0x7FFF7FFF
+    mov dword [px+1*%3-4], 0x7FFF7FFF
+    mov dword [px+2*%3-4], 0x7FFF7FFF
+    mov dword [px+3*%3-4], 0x7FFF7FFF
+ %if %2 == 8
+    mov dword [px+4*%3-4], 0x7FFF7FFF
+    mov dword [px+5*%3-4], 0x7FFF7FFF
+    mov dword [px+6*%3-4], 0x7FFF7FFF
+    mov dword [px+7*%3-4], 0x7FFF7FFF
+ %endif
+.left_done:
+
+    ; bottom
+ %if ARCH_X86_64
+    DEFINE_ARGS dst, stride, dummy1, dst8, pri, sec, stride3, dummy2, edge
+ %else
+    DEFINE_ARGS dst, stride, dummy1, dst8, stride3, dummy2, edge
+ %endif
+    test         edged, 8                   ; have_bottom
+    jz .no_bottom
+    lea          dst8q, [dstq+%2*strideq]
+    test         edged, 1                   ; have_left
+    jz .bottom_no_left
+    test         edged, 2                   ; have_right
+    jz .bottom_no_right
+ %if %1 == 4
+    PMOVZXBW        m0, [dst8q-(%1/2)]
+    PMOVZXBW        m1, [dst8q+strideq-(%1/2)]
+ %else
+    movu            m0, [dst8q-4]
+    movu            m1, [dst8q+strideq-4]
+    punpckhbw       m2, m0, m15
+    punpcklbw       m0, m15
+    punpckhbw       m3, m1, m15
+    punpcklbw       m1, m15
+    movu [px+(%2+0)*%3+8], m2
+    movu [px+(%2+1)*%3+8], m3
+ %endif
+    movu [px+(%2+0)*%3-%1], m0
+    movu [px+(%2+1)*%3-%1], m1
+    jmp .bottom_done
+.bottom_no_right:
+ %if %1 == 4
+    PMOVZXBW        m0, [dst8q-4]
+    PMOVZXBW        m1, [dst8q+strideq-4]
+    movu [px+(%2+0)*%3-4*2], m0
+    movu [px+(%2+1)*%3-4*2], m1
+ %else
+    movu            m0, [dst8q-8]
+    movu            m1, [dst8q+strideq-8]
+    punpckhbw       m2, m0, m15
+    punpcklbw       m0, m15
+    punpckhbw       m3, m1, m15
+    punpcklbw       m1, m15
+    mova [px+(%2+0)*%3-8*2], m0
+    mova [px+(%2+0)*%3-0*2], m2
+    mova [px+(%2+1)*%3-8*2], m1
+    mova [px+(%2+1)*%3-0*2], m3
+    mov dword [px+(%2-1)*%3+8*2], 0x7FFF7FFF    ; overwritten by first mova
+ %endif
+    mov dword [px+(%2+0)*%3+%1*2], 0x7FFF7FFF
+    mov dword [px+(%2+1)*%3+%1*2], 0x7FFF7FFF
+    jmp .bottom_done
+.bottom_no_left:
+    test          edged, 2                  ; have_right
+    jz .bottom_no_left_right
+ %if %1 == 4
+    PMOVZXBW        m0, [dst8q]
+    PMOVZXBW        m1, [dst8q+strideq]
+ %else
+    movu            m0, [dst8q]
+    movu            m1, [dst8q+strideq]
+    punpckhbw       m2, m0, m15
+    punpcklbw       m0, m15
+    punpckhbw       m3, m1, m15
+    punpcklbw       m1, m15
+    mova [px+(%2+0)*%3+8*2], m2
+    mova [px+(%2+1)*%3+8*2], m3
+ %endif
+    mova [px+(%2+0)*%3], m0
+    mova [px+(%2+1)*%3], m1
+    mov dword [px+(%2+0)*%3-4], 0x7FFF7FFF
+    mov dword [px+(%2+1)*%3-4], 0x7FFF7FFF
+    jmp .bottom_done
+.bottom_no_left_right:
+    PMOVZXBW        m0, [dst8q+strideq*0], %1 == 4
+    PMOVZXBW        m1, [dst8q+strideq*1], %1 == 4
+    mova [px+(%2+0)*%3], m0
+    mova [px+(%2+1)*%3], m1
+    mov dword [px+(%2+0)*%3+%1*2], 0x7FFF7FFF
+    mov dword [px+(%2+1)*%3+%1*2], 0x7FFF7FFF
+    mov dword [px+(%2+0)*%3-4], 0x7FFF7FFF
+    mov dword [px+(%2+1)*%3-4], 0x7FFF7FFF
+    jmp .bottom_done
+.no_bottom:
+ %if ARCH_X86_64
+    SWAP            m0, m14
+ %else
+    mova            m0, [PIC_sym(pw_0x7FFF)]
+ %endif
+    movu [px+(%2+0)*%3-4], m0
+    movu [px+(%2+1)*%3-4], m0
+ %if %1 == 8
+    movq [px+(%2+0)*%3+12], m0
+    movq [px+(%2+1)*%3+12], m0
+ %endif
+ %if ARCH_X86_64
+    SWAP            m0, m14
+ %endif
+.bottom_done:
+
+    ; actual filter
+    DEFINE_ARGS dst, stride, pridmp, damping, pri, sec, secdmp
+ %if ARCH_X86_64
+    movifnidn     prid, prim
+    movifnidn     secd, secm
+    mov       dampingd, r7m
+ %else
+    LOAD_ARG       pri
+    LOAD_ARG       sec
+    LOAD_ARG   damping, 1
+ %endif
+
+    SAVE_PIC_REG     8
+    mov        pridmpd, prid
+    mov        secdmpd, secd
+    or         pridmpd, 1
+    or         secdmpd, 1
+    bsr        pridmpd, pridmpd
+    bsr        secdmpd, secdmpd
+    sub        pridmpd, dampingd
+    sub        secdmpd, dampingd
+    xor       dampingd, dampingd
+    neg        pridmpd
+    cmovl      pridmpd, dampingd
+    neg        secdmpd
+    cmovl      secdmpd, dampingd
+    mov       [rsp+ 0], pridmpq                 ; pri_shift
+    mov       [rsp+16], secdmpq                 ; sec_shift
+ %if ARCH_X86_32
+    mov dword [esp+ 4], 0                       ; zero upper 32 bits of psraw
+    mov dword [esp+20], 0                       ; source operand in ACCUMULATE_TAP
+  %define PIC_reg r6
+    LOAD_PIC_REG     8
+ %endif
+
+    ; pri/sec_taps[k] [4 total]
+    DEFINE_ARGS dst, stride, tap, dummy, pri, sec
+ %if ARCH_X86_64
+    mova           m14, [pw_256]
+ %else
+  %define m14   [PIC_sym(pw_256)]
+ %endif
+    movd            m0, prid
+    movd            m1, secd
+    pshufb          m0, m14
+    pshufb          m1, m14
+ %if ARCH_X86_32
+    mova            m2, [PIC_sym(pw_0x7FFF)]
+    pandn           m0, m2
+    pandn           m1, m2
+    mova    [esp+0x20], m0
+    mova    [esp+0x30], m1
+ %endif
+    and           prid, 1
+    lea           tapq, [PIC_sym(tap_table)]
+    lea           priq, [tapq+priq*4]           ; pri_taps
+    lea           secq, [tapq+8]                ; sec_taps
+
+    ; off1/2/3[k] [6 total] from [tapq+12+(dir+0/2/6)*2+k]
+    DEFINE_ARGS dst, stride, tap, dir, pri, sec
+ %if ARCH_X86_64
+    mov           dird, r6m
+    lea           tapq, [tapq+dirq*2+12]
+    DEFINE_ARGS dst, stride, dir, stk, pri, sec, h, off, k
+ %else
+    LOAD_ARG       dir, 1
+    lea           tapd, [tapd+dird*2+12]
+    DEFINE_ARGS dst, stride, dir, stk, pri, sec
+  %define hd    dword [esp+8]
+  %define offq  dstq
+  %define kq    strideq
+ %endif
+    mov             hd, %1*%2*2/mmsize
+    lea           stkq, [px]
+    movif32 [esp+0x1C], strided
+.v_loop:
+    movif32 [esp+0x18], dstd
+    mov             kq, 1
+ %if %1 == 4
+    movq            m4, [stkq+%3*0]
+    movhps          m4, [stkq+%3*1]
+ %else
+    mova            m4, [stkq+%3*0]             ; px
+ %endif
+
+ %if ARCH_X86_32
+  %xdefine m11  m6
+  %xdefine m13  m7
+  %xdefine  m7  m0
+  %xdefine  m8  m1
+ %endif
+
+    pxor           m13, m13                     ; sum
+    mova            m7, m4                      ; max
+    mova            m8, m4                      ; min
+.k_loop:
+ %if ARCH_X86_64
+    movd            m2, [priq+kq*2]             ; pri_taps
+    movd            m3, [secq+kq*2]             ; sec_taps
+    pshufb          m2, m14
+    pshufb          m3, m14
+    ACCUMULATE_TAP 0*2, [rsp+ 0], m0, m2, %1, %3
+    ACCUMULATE_TAP 2*2, [rsp+16], m1, m3, %1, %3
+    ACCUMULATE_TAP 6*2, [rsp+16], m1, m3, %1, %3
+ %else
+    movd            m2, [priq+kq*2]             ; pri_taps
+    pshufb          m2, m14
+    ACCUMULATE_TAP 0*2, [esp+0x00], [esp+0x20], m2, %1, %3
+
+    movd            m2, [secq+kq*2]             ; sec_taps
+    pshufb          m2, m14
+    ACCUMULATE_TAP 2*2, [esp+0x10], [esp+0x30], m2, %1, %3
+    ACCUMULATE_TAP 6*2, [esp+0x10], [esp+0x30], m2, %1, %3
+ %endif
+
+    dec             kq
+    jge .k_loop
+
+    pcmpgtw        m11, m15, m13
+    paddw          m13, m11
+    pmulhrsw       m13, [PIC_sym(pw_2048)]
+    paddw           m4, m13
+    pminsw          m4, m7
+    pmaxsw          m4, m8
+    packuswb        m4, m4
+    movif32       dstd, [esp+0x18]
+    movif32    strided, [esp+0x1C]
+ %if %1 == 4
+    movd [dstq+strideq*0], m4
+    psrlq           m4, 32
+    movd [dstq+strideq*1], m4
+ %else
+    movq [dstq], m4
+ %endif
+
+ %if %1 == 4
+ %define vloop_lines (mmsize/(%1*2))
+    lea           dstq, [dstq+strideq*vloop_lines]
+    add           stkq, %3*vloop_lines
+ %else
+    lea           dstq, [dstq+strideq]
+    add           stkq, %3
+ %endif
+    dec             hd
+    jg .v_loop
+
+    RET
+%endmacro
+
+cdef_filter_fn 8, 8, 32
+cdef_filter_fn 4, 8, 32
+cdef_filter_fn 4, 4, 32