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

#version 450

layout (constant_id = 0) const int across_spatial = 0;
layout (constant_id = 1) const int across_channel = 0;
layout (constant_id = 2) const float eps = 0.f;
layout (constant_id = 3) const int eps_mode = 0;

layout (binding = 0) readonly buffer sqsum_blob { vec4 sqsum_blob_data[]; };
layout (binding = 1) writeonly buffer coeffs_blob { sfpvec4 coeffs_blob_data[]; };

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

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

    if (gx >= p.w || gy >= p.h || gz >= p.c)
        return;

    int v_offset = gz * p.cstep + gx;

    vec4 sqsum_4 = sqsum_blob_data[v_offset];

    if (across_channel == 0)
    {
        afpvec4 sqsum = afpvec4(sqsum_4);

        afpvec4 a;

        if (eps_mode == 0) // caffe/mxnet
        {
            a = afp(1.f) / sqrt(sqsum + afp(eps));
        }

        if (eps_mode == 1) // pytorch
        {
            a = afp(1.f) / max(sqrt(sqsum), afp(eps));
        }

        if (eps_mode == 2) // tensorflow
        {
            a = afp(1.f) / sqrt(max(sqsum, afp(eps)));
        }

        int gi = gz * p.w + gx;

        buffer_st4(coeffs_blob_data, gi, a);
    }

    if (across_channel == 1)
    {
        afp sqsum = afp(sqsum_4.r + sqsum_4.g + sqsum_4.b + sqsum_4.a);

        afp a;

        if (eps_mode == 0) // caffe/mxnet
        {
            a = afp(1.f) / sqrt(sqsum + afp(eps));
        }

        if (eps_mode == 1) // pytorch
        {
            a = afp(1.f) / max(sqrt(sqsum), afp(eps));
        }

        if (eps_mode == 2) // tensorflow
        {
            a = afp(1.f) / sqrt(max(sqsum, afp(eps)));
        }

        int gi = gz * p.w + gx;

        buffer_st4(coeffs_blob_data, gi, afpvec4(a));
    }
}
