;******************************************************************************
;* VP9 MC SIMD optimizations
;*
;* Copyright (c) 2015 Ronald S. Bultje <rsbultje gmail com>
;*
;* 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"

SECTION_RODATA 32

pd_64: times 8 dd 64

cextern pw_1023
cextern pw_4095

SECTION .text

%macro filter_h4_fn 1-2 12
cglobal vp9_%1_8tap_1d_h_4_10, 6, 6, %2, dst, dstride, src, sstride, h, filtery
    mova        m5, [pw_1023]
.body:
%if notcpuflag(sse4) && ARCH_X86_64
    pxor       m11, m11
%endif
    mova        m6, [pd_64]
    mova        m7, [filteryq+ 0]
%if ARCH_X86_64 && mmsize > 8
    mova        m8, [filteryq+32]
    mova        m9, [filteryq+64]
    mova       m10, [filteryq+96]
%endif
.loop:
    movh        m0, [srcq-6]
    movh        m1, [srcq-4]
    movh        m2, [srcq-2]
    movh        m3, [srcq+0]
    movh        m4, [srcq+2]
    punpcklwd   m0, m1
    punpcklwd   m2, m3
    pmaddwd     m0, m7
%if ARCH_X86_64 && mmsize > 8
    pmaddwd     m2, m8
%else
    pmaddwd     m2, [filteryq+32]
%endif
    movu        m1, [srcq+4]
    movu        m3, [srcq+6]
    paddd       m0, m2
    movu        m2, [srcq+8]
    add       srcq, sstrideq
    punpcklwd   m4, m1
    punpcklwd   m3, m2
%if ARCH_X86_64 && mmsize > 8
    pmaddwd     m4, m9
    pmaddwd     m3, m10
%else
    pmaddwd     m4, [filteryq+64]
    pmaddwd     m3, [filteryq+96]
%endif
    paddd       m0, m4
    paddd       m0, m3
    paddd       m0, m6
    psrad       m0, 7
%if cpuflag(sse4)
    packusdw    m0, m0
%else
    packssdw    m0, m0
%endif
%ifidn %1, avg
    movh        m1, [dstq]
%endif
    pminsw      m0, m5
%if notcpuflag(sse4)
%if ARCH_X86_64
    pmaxsw      m0, m11
%else
    pxor        m2, m2
    pmaxsw      m0, m2
%endif
%endif
%ifidn %1, avg
    pavgw       m0, m1
%endif
    movh    [dstq], m0
    add       dstq, dstrideq
    dec         hd
    jg .loop
    RET

cglobal vp9_%1_8tap_1d_h_4_12, 6, 6, %2, dst, dstride, src, sstride, h, filtery
    mova        m5, [pw_4095]
    jmp mangle(private_prefix %+ _ %+ vp9_%1_8tap_1d_h_4_10 %+ SUFFIX).body
%endmacro

INIT_XMM sse2
filter_h4_fn put
filter_h4_fn avg

%macro filter_h_fn 1-2 12
%assign %%px mmsize/2
cglobal vp9_%1_8tap_1d_h_ %+ %%px %+ _10, 6, 6, %2, dst, dstride, src, sstride, h, filtery
    mova        m5, [pw_1023]
.body:
%if notcpuflag(sse4) && ARCH_X86_64
    pxor       m11, m11
%endif
    mova        m6, [pd_64]
    mova        m7, [filteryq+ 0]
%if ARCH_X86_64 && mmsize > 8
    mova        m8, [filteryq+32]
    mova        m9, [filteryq+64]
    mova       m10, [filteryq+96]
%endif
.loop:
    movu        m0, [srcq-6]
    movu        m1, [srcq-4]
    movu        m2, [srcq-2]
    movu        m3, [srcq+0]
    movu        m4, [srcq+2]
    pmaddwd     m0, m7
    pmaddwd     m1, m7
%if ARCH_X86_64 && mmsize > 8
    pmaddwd     m2, m8
    pmaddwd     m3, m8
    pmaddwd     m4, m9
%else
    pmaddwd     m2, [filteryq+32]
    pmaddwd     m3, [filteryq+32]
    pmaddwd     m4, [filteryq+64]
%endif
    paddd       m0, m2
    paddd       m1, m3
    paddd       m0, m4
    movu        m2, [srcq+4]
    movu        m3, [srcq+6]
    movu        m4, [srcq+8]
    add       srcq, sstrideq
%if ARCH_X86_64 && mmsize > 8
    pmaddwd     m2, m9
    pmaddwd     m3, m10
    pmaddwd     m4, m10
%else
    pmaddwd     m2, [filteryq+64]
    pmaddwd     m3, [filteryq+96]
    pmaddwd     m4, [filteryq+96]
%endif
    paddd       m1, m2
    paddd       m0, m3
    paddd       m1, m4
    paddd       m0, m6
    paddd       m1, m6
    psrad       m0, 7
    psrad       m1, 7
%if cpuflag(sse4)
    packusdw    m0, m0
    packusdw    m1, m1
%else
    packssdw    m0, m0
    packssdw    m1, m1
%endif
    punpcklwd   m0, m1
    pminsw      m0, m5
%if notcpuflag(sse4)
%if ARCH_X86_64
    pmaxsw      m0, m11
%else
    pxor        m2, m2
    pmaxsw      m0, m2
%endif
%endif
%ifidn %1, avg
    pavgw       m0, [dstq]
%endif
    mova    [dstq], m0
    add       dstq, dstrideq
    dec         hd
    jg .loop
    RET

cglobal vp9_%1_8tap_1d_h_ %+ %%px %+ _12, 6, 6, %2, dst, dstride, src, sstride, h, filtery
    mova        m5, [pw_4095]
    jmp mangle(private_prefix %+ _ %+ vp9_%1_8tap_1d_h_ %+ %%px %+ _10 %+ SUFFIX).body
%endmacro

INIT_XMM sse2
filter_h_fn put
filter_h_fn avg
%if HAVE_AVX2_EXTERNAL
INIT_YMM avx2
filter_h_fn put
filter_h_fn avg
%endif

%macro filter_v4_fn 1-2 12
%if ARCH_X86_64
cglobal vp9_%1_8tap_1d_v_4_10, 6, 8, %2, dst, dstride, src, sstride, h, filtery, src4, sstride3
%else
cglobal vp9_%1_8tap_1d_v_4_10, 4, 7, %2, dst, dstride, src, sstride, filtery, src4, sstride3
    mov   filteryq, r5mp
%define hd r4mp
%endif
    mova        m5, [pw_1023]
.body:
%if notcpuflag(sse4) && ARCH_X86_64
    pxor       m11, m11
%endif
    mova        m6, [pd_64]
    lea  sstride3q, [sstrideq*3]
    lea      src4q, [srcq+sstrideq]
    sub       srcq, sstride3q
    mova        m7, [filteryq+  0]
%if ARCH_X86_64 && mmsize > 8
    mova        m8, [filteryq+ 32]
    mova        m9, [filteryq+ 64]
    mova       m10, [filteryq+ 96]
%endif
.loop:
    ; FIXME maybe reuse loads from previous rows, or just
    ; more generally unroll this to prevent multiple loads of
    ; the same data?
    movh        m0, [srcq]
    movh        m1, [srcq+sstrideq]
    movh        m2, [srcq+sstrideq*2]
    movh        m3, [srcq+sstride3q]
    add       srcq, sstrideq
    movh        m4, [src4q]
    punpcklwd   m0, m1
    punpcklwd   m2, m3
    pmaddwd     m0, m7
%if ARCH_X86_64 && mmsize > 8
    pmaddwd     m2, m8
%else
    pmaddwd     m2, [filteryq+ 32]
%endif
    movh        m1, [src4q+sstrideq]
    movh        m3, [src4q+sstrideq*2]
    paddd       m0, m2
    movh        m2, [src4q+sstride3q]
    add      src4q, sstrideq
    punpcklwd   m4, m1
    punpcklwd   m3, m2
%if ARCH_X86_64 && mmsize > 8
    pmaddwd     m4, m9
    pmaddwd     m3, m10
%else
    pmaddwd     m4, [filteryq+ 64]
    pmaddwd     m3, [filteryq+ 96]
%endif
    paddd       m0, m4
    paddd       m0, m3
    paddd       m0, m6
    psrad       m0, 7
%if cpuflag(sse4)
    packusdw    m0, m0
%else
    packssdw    m0, m0
%endif
%ifidn %1, avg
    movh        m1, [dstq]
%endif
    pminsw      m0, m5
%if notcpuflag(sse4)
%if ARCH_X86_64
    pmaxsw      m0, m11
%else
    pxor        m2, m2
    pmaxsw      m0, m2
%endif
%endif
%ifidn %1, avg
    pavgw       m0, m1
%endif
    movh    [dstq], m0
    add       dstq, dstrideq
    dec         hd
    jg .loop
    RET

%if ARCH_X86_64
cglobal vp9_%1_8tap_1d_v_4_12, 6, 8, %2, dst, dstride, src, sstride, h, filtery, src4, sstride3
%else
cglobal vp9_%1_8tap_1d_v_4_12, 4, 7, %2, dst, dstride, src, sstride, filtery, src4, sstride3
    mov   filteryq, r5mp
%endif
    mova        m5, [pw_4095]
    jmp mangle(private_prefix %+ _ %+ vp9_%1_8tap_1d_v_4_10 %+ SUFFIX).body
%endmacro

INIT_XMM sse2
filter_v4_fn put
filter_v4_fn avg

%macro filter_v_fn 1-2 13
%assign %%px mmsize/2
%if ARCH_X86_64
cglobal vp9_%1_8tap_1d_v_ %+ %%px %+ _10, 6, 8, %2, dst, dstride, src, sstride, h, filtery, src4, sstride3
%else
cglobal vp9_%1_8tap_1d_v_ %+ %%px %+ _10, 4, 7, %2, dst, dstride, src, sstride, filtery, src4, sstride3
    mov   filteryq, r5mp
%define hd r4mp
%endif
    mova        m5, [pw_1023]
.body:
%if notcpuflag(sse4) && ARCH_X86_64
    pxor       m12, m12
%endif
%if ARCH_X86_64
    mova       m11, [pd_64]
%endif
    lea  sstride3q, [sstrideq*3]
    lea      src4q, [srcq+sstrideq]
    sub       srcq, sstride3q
    mova        m7, [filteryq+  0]
%if ARCH_X86_64 && mmsize > 8
    mova        m8, [filteryq+ 32]
    mova        m9, [filteryq+ 64]
    mova       m10, [filteryq+ 96]
%endif
.loop:
    ; FIXME maybe reuse loads from previous rows, or just
    ; more generally unroll this to prevent multiple loads of
    ; the same data?
    movu        m0, [srcq]
    movu        m1, [srcq+sstrideq]
    movu        m2, [srcq+sstrideq*2]
    movu        m3, [srcq+sstride3q]
    add       srcq, sstrideq
    movu        m4, [src4q]
    SBUTTERFLY  wd, 0, 1, 6
    SBUTTERFLY  wd, 2, 3, 6
    pmaddwd     m0, m7
    pmaddwd     m1, m7
%if ARCH_X86_64 && mmsize > 8
    pmaddwd     m2, m8
    pmaddwd     m3, m8
%else
    pmaddwd     m2, [filteryq+ 32]
    pmaddwd     m3, [filteryq+ 32]
%endif
    paddd       m0, m2
    paddd       m1, m3
    movu        m2, [src4q+sstrideq]
    movu        m3, [src4q+sstrideq*2]
    SBUTTERFLY  wd, 4, 2, 6
%if ARCH_X86_64 && mmsize > 8
    pmaddwd     m4, m9
    pmaddwd     m2, m9
%else
    pmaddwd     m4, [filteryq+ 64]
    pmaddwd     m2, [filteryq+ 64]
%endif
    paddd       m0, m4
    paddd       m1, m2
    movu        m4, [src4q+sstride3q]
    add      src4q, sstrideq
    SBUTTERFLY  wd, 3, 4, 6
%if ARCH_X86_64 && mmsize > 8
    pmaddwd     m3, m10
    pmaddwd     m4, m10
%else
    pmaddwd     m3, [filteryq+ 96]
    pmaddwd     m4, [filteryq+ 96]
%endif
    paddd       m0, m3
    paddd       m1, m4
%if ARCH_X86_64
    paddd       m0, m11
    paddd       m1, m11
%else
    paddd       m0, [pd_64]
    paddd       m1, [pd_64]
%endif
    psrad       m0, 7
    psrad       m1, 7
%if cpuflag(sse4)
    packusdw    m0, m1
%else
    packssdw    m0, m1
%endif
    pminsw      m0, m5
%if notcpuflag(sse4)
%if ARCH_X86_64
    pmaxsw      m0, m12
%else
    pxor        m2, m2
    pmaxsw      m0, m2
%endif
%endif
%ifidn %1, avg
    pavgw       m0, [dstq]
%endif
    mova    [dstq], m0
    add       dstq, dstrideq
    dec         hd
    jg .loop
    RET

%if ARCH_X86_64
cglobal vp9_%1_8tap_1d_v_ %+ %%px %+ _12, 6, 8, %2, dst, dstride, src, sstride, h, filtery, src4, sstride3
%else
cglobal vp9_%1_8tap_1d_v_ %+ %%px %+ _12, 4, 7, %2, dst, dstride, src, sstride, filtery, src4, sstride3
    mov   filteryq, r5mp
%endif
    mova        m5, [pw_4095]
    jmp mangle(private_prefix %+ _ %+ vp9_%1_8tap_1d_v_ %+ %%px %+ _10 %+ SUFFIX).body
%endmacro

INIT_XMM sse2
filter_v_fn put
filter_v_fn avg
%if HAVE_AVX2_EXTERNAL
INIT_YMM avx2
filter_v_fn put
filter_v_fn avg
%endif