;
; Copyright (c) 2016, Alliance for Open Media. All rights reserved
;
; This source code is subject to the terms of the BSD 2 Clause License and
; the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
; was not distributed with this source code in the LICENSE file, you can
; obtain it at www.aomedia.org/license/software. If the Alliance for Open
; Media Patent License 1.0 was not distributed with this source code in the
; PATENTS file, you can obtain it at www.aomedia.org/license/patent.
;

;

%include "aom_ports/x86_abi_support.asm"

; tabulate_ssim - sums sum_s,sum_r,sum_sq_s,sum_sq_r, sum_sxr
%macro TABULATE_SSIM 0
        paddusw         xmm15, xmm3  ; sum_s
        paddusw         xmm14, xmm4  ; sum_r
        movdqa          xmm1, xmm3
        pmaddwd         xmm1, xmm1
        paddd           xmm13, xmm1 ; sum_sq_s
        movdqa          xmm2, xmm4
        pmaddwd         xmm2, xmm2
        paddd           xmm12, xmm2 ; sum_sq_r
        pmaddwd         xmm3, xmm4
        paddd           xmm11, xmm3  ; sum_sxr
%endmacro

; Sum across the register %1 starting with q words
%macro SUM_ACROSS_Q 1
        movdqa          xmm2,%1
        punpckldq       %1,xmm0
        punpckhdq       xmm2,xmm0
        paddq           %1,xmm2
        movdqa          xmm2,%1
        punpcklqdq      %1,xmm0
        punpckhqdq      xmm2,xmm0
        paddq           %1,xmm2
%endmacro

; Sum across the register %1 starting with q words
%macro SUM_ACROSS_W 1
        movdqa          xmm1, %1
        punpcklwd       %1,xmm0
        punpckhwd       xmm1,xmm0
        paddd           %1, xmm1
        SUM_ACROSS_Q    %1
%endmacro

SECTION .text

;void ssim_parms_sse2(
;    unsigned char *s,
;    int sp,
;    unsigned char *r,
;    int rp
;    uint32_t *sum_s,
;    uint32_t *sum_r,
;    uint32_t *sum_sq_s,
;    uint32_t *sum_sq_r,
;    uint32_t *sum_sxr);
;
; TODO: Use parm passing through structure, probably don't need the pxors
; ( calling app will initialize to 0 ) could easily fit everything in sse2
; without too much hastle, and can probably do better estimates with psadw
; or pavgb At this point this is just meant to be first pass for calculating
; all the parms needed for 16x16 ssim so we can play with dssim as distortion
; in mode selection code.
global sym(aom_ssim_parms_16x16_sse2) PRIVATE
sym(aom_ssim_parms_16x16_sse2):
    push        rbp
    mov         rbp, rsp
    SHADOW_ARGS_TO_STACK 9
    SAVE_XMM 15
    push        rsi
    push        rdi
    ; end prolog

    mov             rsi,        arg(0) ;s
    mov             rcx,        arg(1) ;sp
    mov             rdi,        arg(2) ;r
    mov             rax,        arg(3) ;rp

    pxor            xmm0, xmm0
    pxor            xmm15,xmm15  ;sum_s
    pxor            xmm14,xmm14  ;sum_r
    pxor            xmm13,xmm13  ;sum_sq_s
    pxor            xmm12,xmm12  ;sum_sq_r
    pxor            xmm11,xmm11  ;sum_sxr

    mov             rdx, 16      ;row counter
.NextRow:

    ;grab source and reference pixels
    movdqu          xmm5, [rsi]
    movdqu          xmm6, [rdi]
    movdqa          xmm3, xmm5
    movdqa          xmm4, xmm6
    punpckhbw       xmm3, xmm0 ; high_s
    punpckhbw       xmm4, xmm0 ; high_r

    TABULATE_SSIM

    movdqa          xmm3, xmm5
    movdqa          xmm4, xmm6
    punpcklbw       xmm3, xmm0 ; low_s
    punpcklbw       xmm4, xmm0 ; low_r

    TABULATE_SSIM

    add             rsi, rcx   ; next s row
    add             rdi, rax   ; next r row

    dec             rdx        ; counter
    jnz .NextRow

    SUM_ACROSS_W    xmm15
    SUM_ACROSS_W    xmm14
    SUM_ACROSS_Q    xmm13
    SUM_ACROSS_Q    xmm12
    SUM_ACROSS_Q    xmm11

    mov             rdi,arg(4)
    movd            [rdi], xmm15;
    mov             rdi,arg(5)
    movd            [rdi], xmm14;
    mov             rdi,arg(6)
    movd            [rdi], xmm13;
    mov             rdi,arg(7)
    movd            [rdi], xmm12;
    mov             rdi,arg(8)
    movd            [rdi], xmm11;

    ; begin epilog
    pop         rdi
    pop         rsi
    RESTORE_XMM
    UNSHADOW_ARGS
    pop         rbp
    ret

;void ssim_parms_sse2(
;    unsigned char *s,
;    int sp,
;    unsigned char *r,
;    int rp
;    uint32_t *sum_s,
;    uint32_t *sum_r,
;    uint32_t *sum_sq_s,
;    uint32_t *sum_sq_r,
;    uint32_t *sum_sxr);
;
; TODO: Use parm passing through structure, probably don't need the pxors
; ( calling app will initialize to 0 ) could easily fit everything in sse2
; without too much hastle, and can probably do better estimates with psadw
; or pavgb At this point this is just meant to be first pass for calculating
; all the parms needed for 16x16 ssim so we can play with dssim as distortion
; in mode selection code.
global sym(aom_ssim_parms_8x8_sse2) PRIVATE
sym(aom_ssim_parms_8x8_sse2):
    push        rbp
    mov         rbp, rsp
    SHADOW_ARGS_TO_STACK 9
    SAVE_XMM 15
    push        rsi
    push        rdi
    ; end prolog

    mov             rsi,        arg(0) ;s
    mov             rcx,        arg(1) ;sp
    mov             rdi,        arg(2) ;r
    mov             rax,        arg(3) ;rp

    pxor            xmm0, xmm0
    pxor            xmm15,xmm15  ;sum_s
    pxor            xmm14,xmm14  ;sum_r
    pxor            xmm13,xmm13  ;sum_sq_s
    pxor            xmm12,xmm12  ;sum_sq_r
    pxor            xmm11,xmm11  ;sum_sxr

    mov             rdx, 8      ;row counter
.NextRow:

    ;grab source and reference pixels
    movq            xmm3, [rsi]
    movq            xmm4, [rdi]
    punpcklbw       xmm3, xmm0 ; low_s
    punpcklbw       xmm4, xmm0 ; low_r

    TABULATE_SSIM

    add             rsi, rcx   ; next s row
    add             rdi, rax   ; next r row

    dec             rdx        ; counter
    jnz .NextRow

    SUM_ACROSS_W    xmm15
    SUM_ACROSS_W    xmm14
    SUM_ACROSS_Q    xmm13
    SUM_ACROSS_Q    xmm12
    SUM_ACROSS_Q    xmm11

    mov             rdi,arg(4)
    movd            [rdi], xmm15;
    mov             rdi,arg(5)
    movd            [rdi], xmm14;
    mov             rdi,arg(6)
    movd            [rdi], xmm13;
    mov             rdi,arg(7)
    movd            [rdi], xmm12;
    mov             rdi,arg(8)
    movd            [rdi], xmm11;

    ; begin epilog
    pop         rdi
    pop         rsi
    RESTORE_XMM
    UNSHADOW_ARGS
    pop         rbp
    ret
