#include "cl_viv_vx_ext.h"

_viv_uniform VXC_512Bits uniVecShift10;
_viv_uniform VXC_512Bits uniAddRShift;
_viv_uniform VXC_512Bits uniGetTempVal;
_viv_uniform VXC_512Bits uniExtractBytes;
_viv_uniform VXC_512Bits uniUnpackToR;
_viv_uniform VXC_512Bits uniUnpackToG;
_viv_uniform VXC_512Bits uniUnpackToB;
_viv_uniform VXC_512Bits uniDataMulAlpha_4x4;
_viv_uniform VXC_512Bits uniDataSubMean_4x4;

_viv_uniform VXC_512Bits uniConvertIntergetoF32_4x4;
_viv_uniform float outputScale;
_viv_uniform VXC_512Bits uniExtactInteger_2x8;

#define DESCALE(x) (((x) + (1<<19)) >> 20)
__kernel void ScaletoTensor_Int8
    (
    __read_only image2d_t        input,
    __write_only image2d_array_t output,
        global int               *xRatio,
        global int               *yRatio,
        global int               *xOffset,
        global int               *yOffset,
               float             rMean,
               float             gMean,
               float             bMean,
               float             f32Var
    )
{
    int2 ratioXY = (int2)(*xRatio, *yRatio);

    int4 xPos        = get_global_id(0);
    int yPos        = get_global_id(1);

    int2 ratioSufXY = (ratioXY >> 1) - (1 << 14);
    xPos += (int4)(0, 1, 2, 3);

    //x
    int4 fx0 = xPos * ratioXY.x + ratioSufXY.x;
    int4 sx = fx0 & 0xffff8000;
    fx0 -= sx;
    sx = sx >> 15;

    vxc_short4 fx;
    VXC_DP4x4(fx, fx0, 1 << 4, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniAddRShift);
    //y
    int fy = yPos * ratioXY.y + ratioSufXY.y;
    int sy = fy & 0xffff8000; // Floor

    fy -= sy;
    sy = sy >> 15;

    fy = (fy + (1<< 4)) >> 5;

    //R
    vxc_uchar16 line0RGB1, line0RGB2;
    vxc_uchar16 line1RGB3, line1RGB4;
    int4 coord;
    sx = sx * 3 + *xOffset;
    coord.xyz    = sx.xyz;
    coord.w        = sy + *yOffset;
    int2 coord1 = (int2)(sx.w, coord.w);
    VXC_ReadImage(line0RGB1, input, coord.xw, VXC_5BITOFFSET_XY(0, 0),\
        VXC_MODIFIER(0, 5, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line0RGB1, input, coord.yw, VXC_5BITOFFSET_XY(0, 0),\
        VXC_MODIFIER(6, 11, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line0RGB2, input, coord.zw, VXC_5BITOFFSET_XY(0, 0),\
        VXC_MODIFIER(0, 5, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line0RGB2, input, coord1, VXC_5BITOFFSET_XY(0, 0),\
        VXC_MODIFIER(6, 11, 0, VXC_RM_TowardZero, 0));

    VXC_ReadImage(line1RGB3, input, coord.xw, VXC_5BITOFFSET_XY(0, 1),\
        VXC_MODIFIER(0, 5, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line1RGB3, input, coord.yw, VXC_5BITOFFSET_XY(0, 1),\
        VXC_MODIFIER(6, 11, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line1RGB4, input, coord.zw, VXC_5BITOFFSET_XY(0, 1),\
        VXC_MODIFIER(0, 5, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line1RGB4, input, coord1, VXC_5BITOFFSET_XY(0, 1),\
        VXC_MODIFIER(6, 11, 0, VXC_RM_TowardZero, 0));

    float4      bgrMean = (float4)(bMean, gMean, rMean, 0);

    bgrMean *= f32Var;

    int4 test01, temp1;
    int4 test02, temp2;
    int4 tt;
    vxc_uchar4 val;
    int4 coord_out = (int4)(xPos.x, yPos, 2, 0);

    vxc_uchar8 line1, line2;

    //R
    VXC_DP2x8(line1, line0RGB1, line0RGB2,\
        VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToR);
    VXC_DP2x8(line2, line1RGB3, line1RGB4,\
        VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToR);

    VXC_DP4x4(test01, line1, line1, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp1, line1, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp1 = temp1 + test01;

    VXC_DP4x4(test02, line2, line2, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp2, line2, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp2 = temp2 + test02;
    temp2 = fy * (temp2 - temp1) + (temp1 << 10);

    vxc_float4    tmp_dst;
    vxc_uchar4 u8_dst;
    VXC_DP4x4(u8_dst, temp2, 1 << 19, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 1), uniExtractBytes);
    VXC_DP4x4(tmp_dst, u8_dst, u8_dst,\
        VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 1), uniConvertIntergetoF32_4x4);

    //convert U8 to dfp8
    int4 dst0;
    vxc_char4 dst;
    tmp_dst = tmp_dst * f32Var - bgrMean.z;
    tmp_dst *= outputScale;
    dst0 = convert_int4_rte(tmp_dst);
    VXC_DP2x8(dst, dst0, dst0, VXC_MODIFIER(0, 3, 0, VXC_RM_ToNearestEven, 1), uniExtactInteger_2x8);

    VXC_WriteImage2DArray(output, coord_out, dst, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0));

    //G
    VXC_DP2x8(line1, line0RGB1, line0RGB2, VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToG);
    VXC_DP2x8(line2, line1RGB3, line1RGB4, VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToG);

    coord_out.z = 1;
    VXC_DP4x4(test01, line1, line1, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp1, line1, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp1 = temp1 + test01;

    VXC_DP4x4(test02, line2, line2, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp2, line2, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp2 = temp2 + test02;
    temp2 = fy * (temp2 - temp1) + (temp1 << 10);

    VXC_DP4x4(u8_dst, temp2, 1 << 19, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 1), uniExtractBytes);
    VXC_DP4x4(tmp_dst, u8_dst, u8_dst,\
        VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 1), uniConvertIntergetoF32_4x4);

    tmp_dst = tmp_dst * f32Var - bgrMean.y;
    tmp_dst *= outputScale;
    dst0 = convert_int4_rte(tmp_dst);
    VXC_DP2x8(dst, dst0, dst0, VXC_MODIFIER(0, 3, 0, VXC_RM_ToNearestEven, 1), uniExtactInteger_2x8);
    VXC_WriteImage2DArray(output, coord_out, dst, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0));

    //B
    VXC_DP2x8(line1, line0RGB1, line0RGB2, VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToB);
    VXC_DP2x8(line2, line1RGB3, line1RGB4, VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToB);

    coord_out.z = 0;
    VXC_DP4x4(test01, line1, line1, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp1, line1, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp1 = temp1 + test01;

    VXC_DP4x4(test02, line2, line2, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp2, line2, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp2 = temp2 + test02;
    temp2 = fy * (temp2 - temp1) + (temp1 << 10);

    VXC_DP4x4(u8_dst, temp2, 1 << 19,\
        VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 1), uniExtractBytes);
    VXC_DP4x4(tmp_dst, u8_dst, u8_dst,\
        VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 1), uniConvertIntergetoF32_4x4);

    tmp_dst = tmp_dst * f32Var - bgrMean.x;
    tmp_dst *= outputScale;
    dst0 = convert_int4_rte(tmp_dst);
    VXC_DP2x8(dst, dst0, dst0, VXC_MODIFIER(0, 3, 0, VXC_RM_ToNearestEven, 1), uniExtactInteger_2x8);
    VXC_WriteImage2DArray(output, coord_out, dst, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0));
}

__kernel void ScaletoTensor_Fp16
    (
    __read_only image2d_t        input,
    __write_only image2d_array_t output,
        global int               *xRatio,
        global int               *yRatio,
        global int               *xOffset,
        global int               *yOffset,
                float            rMean,
                float            gMean,
                float            bMean,
                float            f32Var
    )
{
    int2 ratioXY = (int2)(*xRatio, *yRatio);

    int4 xPos       = get_global_id(0);
    int yPos        = get_global_id(1);

    int2 ratioSufXY = (ratioXY >> 1) - (1 << 14);
    xPos += (int4)(0, 1, 2, 3);

    //x
    int4 fx0 = xPos * ratioXY.x + ratioSufXY.x;
    int4 sx = fx0 & 0xffff8000;
    fx0 -= sx;
    sx = sx >> 15;

    vxc_short4 fx;
    VXC_DP4x4(fx, fx0, 1 << 4, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniAddRShift);
    //y
    int fy = yPos * ratioXY.y + ratioSufXY.y;
    int sy = fy & 0xffff8000; // Floor

    fy -= sy;
    sy = sy >> 15;

    fy = (fy + (1<< 4)) >> 5;

    //R
    vxc_uchar16 line0RGB1, line0RGB2;
    vxc_uchar16 line1RGB3, line1RGB4;
    int4 coord;
    sx = sx * 3 + *xOffset;
    coord.xyz    = sx.xyz;
    coord.w        = sy + *yOffset;
    int2 coord1 = (int2)(sx.w, coord.w);
    VXC_ReadImage(line0RGB1, input, coord.xw,\
        VXC_5BITOFFSET_XY(0, 0), VXC_MODIFIER(0, 5, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line0RGB1, input, coord.yw,\
        VXC_5BITOFFSET_XY(0, 0), VXC_MODIFIER(6, 11, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line0RGB2, input, coord.zw,\
        VXC_5BITOFFSET_XY(0, 0), VXC_MODIFIER(0, 5, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line0RGB2, input, coord1,\
        VXC_5BITOFFSET_XY(0, 0), VXC_MODIFIER(6, 11, 0, VXC_RM_TowardZero, 0));

    VXC_ReadImage(line1RGB3, input, coord.xw,\
        VXC_5BITOFFSET_XY(0, 1), VXC_MODIFIER(0, 5, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line1RGB3, input, coord.yw,\
        VXC_5BITOFFSET_XY(0, 1), VXC_MODIFIER(6, 11, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line1RGB4, input, coord.zw,\
        VXC_5BITOFFSET_XY(0, 1), VXC_MODIFIER(0, 5, 0, VXC_RM_TowardZero, 0));
    VXC_ReadImage(line1RGB4, input, coord1,\
        VXC_5BITOFFSET_XY(0, 1), VXC_MODIFIER(6, 11, 0, VXC_RM_TowardZero, 0));

    float4      bgrMean = (float4)(bMean, gMean, rMean, 0);

    int4 test01, temp1;
    int4 test02, temp2;
    int4 tt;
    vxc_uchar4 val;
    int4 coord_out = (int4)(xPos.x, yPos, 2, 0);

    vxc_uchar8 line1, line2;

    //R
    VXC_DP2x8(line1, line0RGB1, line0RGB2, VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToR);
    VXC_DP2x8(line2, line1RGB3, line1RGB4, VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToR);

    VXC_DP4x4(test01, line1, line1, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp1, line1, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp1 = temp1 + test01;

    VXC_DP4x4(test02, line2, line2, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp2, line2, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp2 = temp2 + test02;
    temp2 = fy * (temp2 - temp1) + (temp1 << 10);

    VXC_DP4x4(val, temp2, 1 << 19, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 1), uniExtractBytes);

    //convert U8 to FP16
    half4 f16mean;
    half f16alpha;
    vxc_half4    dst;
    vxc_short4 tmp_dst;
    _viv_asm(CONV, f16mean, bgrMean);
    _viv_asm(CONV, f16alpha, f32Var);
    VXC_DP4x4(dst, val, f16mean.z, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniDataSubMean_4x4);
    VXC_DP4x4(dst, dst, f16alpha, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniDataMulAlpha_4x4);
    _viv_asm(COPY, tmp_dst, dst, 8);
    VXC_WriteImage2DArray(output, coord_out, tmp_dst, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0));

    //G
    VXC_DP2x8(line1, line0RGB1, line0RGB2, VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToG);
    VXC_DP2x8(line2, line1RGB3, line1RGB4, VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToG);

    coord_out.z = 1;
    VXC_DP4x4(test01, line1, line1, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp1, line1, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp1 = temp1 + test01;

    VXC_DP4x4(test02, line2, line2, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp2, line2, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp2 = temp2 + test02;
    temp2 = fy * (temp2 - temp1) + (temp1 << 10);

    VXC_DP4x4(val, temp2, 1 << 19, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 1), uniExtractBytes);

    VXC_DP4x4(dst, val, f16mean.y, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniDataSubMean_4x4);
    VXC_DP4x4(dst, dst, f16alpha, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniDataMulAlpha_4x4);
    _viv_asm(COPY, tmp_dst, dst, 8);
    VXC_WriteImage2DArray(output, coord_out, tmp_dst, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0));

    //B
    VXC_DP2x8(line1, line0RGB1, line0RGB2, VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToB);
    VXC_DP2x8(line2, line1RGB3, line1RGB4, VXC_MODIFIER(0, 7, 0, VXC_RM_TowardZero, 0), uniUnpackToB);

    coord_out.z = 0;
    VXC_DP4x4(test01, line1, line1, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp1, line1, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp1 = temp1 + test01;

    VXC_DP4x4(test02, line2, line2, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniVecShift10);
    VXC_DP4x4(temp2, line2, fx, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniGetTempVal);
    temp2 = temp2 + test02;
    temp2 = fy * (temp2 - temp1) + (temp1 << 10);

    VXC_DP4x4(val, temp2, 1 << 19, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 1), uniExtractBytes);

    VXC_DP4x4(dst, val, f16mean.x, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniDataSubMean_4x4);
    VXC_DP4x4(dst, dst, f16alpha, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0), uniDataMulAlpha_4x4);
    _viv_asm(COPY, tmp_dst, dst, 8);
    VXC_WriteImage2DArray(output, coord_out, tmp_dst, VXC_MODIFIER(0, 3, 0, VXC_RM_TowardZero, 0));

}

