// Copyright 2020 Tencent
// SPDX-License-Identifier: BSD-3-Clause

#version 450

layout (constant_id = 0) const int c = 0;

#define shape_constant_id_offset 1
layout (constant_id = shape_constant_id_offset + 0) const int w = 0;
layout (constant_id = shape_constant_id_offset + 1) const int h = 0;
layout (constant_id = shape_constant_id_offset + 2) const int cstep = 0;

layout (constant_id = shape_constant_id_offset + 3) const int outcstep = 0;

layout (constant_id = shape_constant_id_offset + 4) const int block_x = 0;
layout (constant_id = shape_constant_id_offset + 5) const int block_y = 0;

layout (binding = 0) readonly buffer bottom_blob { sfpvec8 bottom_blob_data[]; };
layout (binding = 1) writeonly buffer bottom_tm_blob { sfpvec8 bottom_tm_blob_data[]; };

layout (push_constant) uniform parameter
{
    int w;
    int h;
    int cstep;

    int outcstep;

    int block_x;
    int block_y;
} p;

void main()
{
    int gx = int(gl_GlobalInvocationID.x);
    int gy = int(gl_GlobalInvocationID.y);
    int gz = int(gl_GlobalInvocationID.z);

    if (gx >= psc(block_x) || gy >= psc(block_y) || gz >= c)
        return;

    // load 4x4
    int sx = gx * 2;
    int sy = gy * 2;

    int v_offset_0 = gz * psc(cstep) + sy * psc(w) + sx;
    ivec4 v_offset = v_offset_0 + ivec4(0, 1, 2, 3) * psc(w);

    afpvec8 v00 = buffer_ld8(bottom_blob_data, v_offset.r + 0);
    afpvec8 v01 = sx + 1 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.r + 1) : afpvec8(afpvec4(0.f), afpvec4(0.f));
    afpvec8 v02 = sx + 2 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.r + 2) : afpvec8(afpvec4(0.f), afpvec4(0.f));
    afpvec8 v03 = sx + 3 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.r + 3) : afpvec8(afpvec4(0.f), afpvec4(0.f));

    afpvec8 v10 = sy + 1 < psc(h) ? buffer_ld8(bottom_blob_data, v_offset.g + 0) : afpvec8(afpvec4(0.f), afpvec4(0.f));
    afpvec8 v11 = sy + 1 < psc(h) && sx + 1 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.g + 1) : afpvec8(afpvec4(0.f), afpvec4(0.f));
    afpvec8 v12 = sy + 1 < psc(h) && sx + 2 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.g + 2) : afpvec8(afpvec4(0.f), afpvec4(0.f));
    afpvec8 v13 = sy + 1 < psc(h) && sx + 3 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.g + 3) : afpvec8(afpvec4(0.f), afpvec4(0.f));

    afpvec8 v20 = sy + 2 < psc(h) ? buffer_ld8(bottom_blob_data, v_offset.b + 0) : afpvec8(afpvec4(0.f), afpvec4(0.f));
    afpvec8 v21 = sy + 2 < psc(h) && sx + 1 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.b + 1) : afpvec8(afpvec4(0.f), afpvec4(0.f));
    afpvec8 v22 = sy + 2 < psc(h) && sx + 2 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.b + 2) : afpvec8(afpvec4(0.f), afpvec4(0.f));
    afpvec8 v23 = sy + 2 < psc(h) && sx + 3 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.b + 3) : afpvec8(afpvec4(0.f), afpvec4(0.f));

    afpvec8 v30 = sy + 3 < psc(h) ? buffer_ld8(bottom_blob_data, v_offset.a + 0) : afpvec8(afpvec4(0.f), afpvec4(0.f));
    afpvec8 v31 = sy + 3 < psc(h) && sx + 1 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.a + 1) : afpvec8(afpvec4(0.f), afpvec4(0.f));
    afpvec8 v32 = sy + 3 < psc(h) && sx + 2 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.a + 2) : afpvec8(afpvec4(0.f), afpvec4(0.f));
    afpvec8 v33 = sy + 3 < psc(h) && sx + 3 < psc(w) ? buffer_ld8(bottom_blob_data, v_offset.a + 3) : afpvec8(afpvec4(0.f), afpvec4(0.f));

    // const float itm[4][4] = {
    //     {1.0f,  0.0f, -1.0f,  0.0f},
    //     {0.0f,  1.0f,  1.0f,  0.0f},
    //     {0.0f, -1.0f,  1.0f,  0.0f},
    //     {0.0f, -1.0f,  0.0f,  1.0f}
    // };

    // implicit transpose
    afpvec8 m00 = v00 - v02;
    afpvec8 m01 = v10 - v12;
    afpvec8 m02 = v20 - v22;
    afpvec8 m03 = v30 - v32;

    afpvec8 m10 = v02 + v01;
    afpvec8 m11 = v12 + v11;
    afpvec8 m12 = v22 + v21;
    afpvec8 m13 = v32 + v31;

    afpvec8 m20 = v02 - v01;
    afpvec8 m21 = v12 - v11;
    afpvec8 m22 = v22 - v21;
    afpvec8 m23 = v32 - v31;

    afpvec8 m30 = v03 - v01;
    afpvec8 m31 = v13 - v11;
    afpvec8 m32 = v23 - v21;
    afpvec8 m33 = v33 - v31;

    v00 = m00 - m02;
    v10 = m10 - m12;
    v20 = m20 - m22;
    v30 = m30 - m32;

    v01 = m02 + m01;
    v11 = m12 + m11;
    v21 = m22 + m21;
    v31 = m32 + m31;

    v02 = m02 - m01;
    v12 = m12 - m11;
    v22 = m22 - m21;
    v32 = m32 - m31;

    v03 = m03 - m01;
    v13 = m13 - m11;
    v23 = m23 - m21;
    v33 = m33 - m31;

    // store 16
    int v_tm_offset = gz * psc(outcstep) + gy * psc(block_x) + gx;

    buffer_st8(bottom_tm_blob_data, v_tm_offset + 0 * psc(outcstep) * c, v00);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 1 * psc(outcstep) * c, v01);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 2 * psc(outcstep) * c, v02);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 3 * psc(outcstep) * c, v03);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 4 * psc(outcstep) * c, v10);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 5 * psc(outcstep) * c, v11);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 6 * psc(outcstep) * c, v12);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 7 * psc(outcstep) * c, v13);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 8 * psc(outcstep) * c, v20);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 9 * psc(outcstep) * c, v21);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 10 * psc(outcstep) * c, v22);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 11 * psc(outcstep) * c, v23);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 12 * psc(outcstep) * c, v30);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 13 * psc(outcstep) * c, v31);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 14 * psc(outcstep) * c, v32);
    buffer_st8(bottom_tm_blob_data, v_tm_offset + 15 * psc(outcstep) * c, v33);
}
