//Culling CS.
#version 450
#extension GL_EXT_nonuniform_qualifier : require
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;

struct FlrInput
{
    mat4 viewMatrix;
    mat4 projectionMatrix;
    vec4 frustumPlanes[6];
    uvec2 screenSize;
    uint mipLevelCount;
    uint instanceCount;
    uint invocation_index;
    vec3 mainViewPos;
    vec2 nearFarPlaneDistances;
};

struct CullingData
{
    vec4 worldBoundingSphere;
    vec4 aabbMin;
    vec4 aabbMax;
    uvec3 batchIndex;
    float renderDistance;
};

struct IndirectDrawData
{
    uint indexCount;
    uint instanceCount;
    uint firstIndex;
    uint vertexOffset;
    uint firstInstance;
};

struct IndirectionData
{
    uint globalInstanceIndex;
};

// Vk3DVision (http://3dsurroundgaming.com/) - Stereo Injection
layout(set = 0, binding = 110, std140) uniform Vk3DParams
{
    vec4 stereo;
    vec4 custom_params;
} vk3d_params;

layout(set = 0, binding = 3, std430) buffer InstanceCullingBuffer
{
    CullingData _InstanceCullingBuffer[];
} _63;

layout(set = 0, binding = 2, std430) buffer IndirectDrawBuffer
{
    IndirectDrawData _IndirectDrawBuffer[];
} _607;

layout(set = 0, binding = 1, std430) buffer IndirectionBuffer
{
    IndirectionData _IndirectionBuffer[];
} _633;

layout(set = 0, binding = 0, std140) uniform PerInstance
{
    mat4 viewMatrix;
    mat4 projectionMatrix;
    vec4 frustumPlanes[6];
    uvec4 instanceCount;
    vec4 mainViewPos;
    vec4 projParameters;
} _657;

layout(set = 0, binding = 102, std430) buffer DebugBuffer
{
    uint debugBuffer[];
} _730;

layout(set = 0, binding = 5) uniform sampler2D HiZBuffer;
layout(set = 2, binding = 0) uniform texture2D globalTextures[];
layout(set = 2, binding = 0) uniform textureCube globalCubeTextures[];
layout(set = 2, binding = 0) uniform texture3D global3DTextures[];
layout(set = 1, binding = 0) uniform sampler linearClampSampler;
layout(set = 1, binding = 1) uniform sampler linearRepeatSampler;
layout(set = 1, binding = 2) uniform sampler nearestClampSampler;
layout(set = 1, binding = 3) uniform sampler nearestRepeatSampler;
layout(set = 1, binding = 4) uniform sampler shadowSampler;

float deviceDepthToLinear(float deviceDepth, float near, float far)
{
    return -((far * near) / ((deviceDepth * (far - near)) + near));
}

void flrMain(FlrInput flrInput)
{
    if (flrInput.invocation_index >= flrInput.instanceCount)
    {
        return;
    }
    CullingData cullingData;
    cullingData.worldBoundingSphere = _63._InstanceCullingBuffer[flrInput.invocation_index].worldBoundingSphere;
    cullingData.aabbMin = _63._InstanceCullingBuffer[flrInput.invocation_index].aabbMin;
    cullingData.aabbMax = _63._InstanceCullingBuffer[flrInput.invocation_index].aabbMax;
    cullingData.batchIndex = _63._InstanceCullingBuffer[flrInput.invocation_index].batchIndex;
    cullingData.renderDistance = _63._InstanceCullingBuffer[flrInput.invocation_index].renderDistance;
    vec3 cameraWorldPosition = flrInput.mainViewPos;
    vec3 dist = cullingData.worldBoundingSphere.xyz - cameraWorldPosition;
    //dist.x -= vk3d_params.stereo.x *(1)*-10.5;
    float squaredDistance = dot(dist, dist);
    if (squaredDistance > (cullingData.renderDistance * cullingData.renderDistance) && vk3d_params.custom_params.y!=1)
    {
        return;
    }
    uint visible = 0u;
    for (uint i = 0u; i < 6u; i++)
    {
        vec4 p = flrInput.frustumPlanes[i];
        float d = dot(p.xyz, cullingData.worldBoundingSphere.xyz) + p.w;
        visible |= (uint(d >= (-cullingData.worldBoundingSphere.w)) << i);
    }
    if (visible != 63u && vk3d_params.custom_params.y!=1)
    {
        return;
    }
    uvec2 screenSize = flrInput.screenSize;
    vec3 center = cullingData.worldBoundingSphere.xyz;
    float radius = cullingData.worldBoundingSphere.w;
    vec3 extent = vec3(radius);
    vec3 verticesWs[8] = vec3[](center + vec3(extent.x, extent.y, extent.z), center + vec3(-extent.x, extent.y, extent.z), center + vec3(extent.x, -extent.y, extent.z), center + vec3(extent.x, extent.y, -extent.z), center + vec3(-extent.x, -extent.y, -extent.z), center + vec3(-extent.x, -extent.y, extent.z), center + vec3(extent.x, -extent.y, -extent.z), center + vec3(-extent.x, extent.y, -extent.z));
    vec4 verticesVs[8];
    for (uint vi = 0u; vi < 8u; vi++)
    {
        verticesVs[vi] = flrInput.viewMatrix * vec4(verticesWs[vi], 1.0);
    }
    vec4 maxExtentVs = max(verticesVs[0], max(verticesVs[1], max(verticesVs[2], max(verticesVs[3], max(verticesVs[4], max(verticesVs[5], max(verticesVs[6], verticesVs[7])))))));
    vec4 verticesSs[8];
    for (uint vi_1 = 0u; vi_1 < 8u; vi_1++)
    {
        verticesSs[vi_1] = flrInput.projectionMatrix * verticesVs[vi_1];
        verticesSs[vi_1] /= vec4(verticesSs[vi_1].w);
    }
    vec3 minExtentSs = min(verticesSs[0].xyz, min(verticesSs[1].xyz, min(verticesSs[2].xyz, min(verticesSs[3].xyz, min(verticesSs[4].xyz, min(verticesSs[5].xyz, min(verticesSs[6].xyz, verticesSs[7].xyz)))))));
    vec3 maxExtentSs = max(verticesSs[0].xyz, max(verticesSs[1].xyz, max(verticesSs[2].xyz, max(verticesSs[3].xyz, max(verticesSs[4].xyz, max(verticesSs[5].xyz, max(verticesSs[6].xyz, verticesSs[7].xyz)))))));
    vec2 _411 = clamp((minExtentSs.xy * 0.5) + vec2(0.5), vec2(0.0), vec2(1.0));
    minExtentSs = vec3(_411.x, _411.y, minExtentSs.z);
    vec2 _421 = clamp((maxExtentSs.xy * 0.5) + vec2(0.5), vec2(0.0), vec2(1.0));
    maxExtentSs = vec3(_421.x, _421.y, maxExtentSs.z);
    vec2 _430 = minExtentSs.xy * vec2(screenSize - uvec2(1u));
    minExtentSs = vec3(_430.x, _430.y, minExtentSs.z);
    vec2 _439 = maxExtentSs.xy * vec2(screenSize - uvec2(1u));
    
    //_439.x-= vk3d_params.stereo.x*340;
    
    maxExtentSs = vec3(_439.x, _439.y, maxExtentSs.z);
    vec3 newMinExtentSs = min(minExtentSs, maxExtentSs);
    vec3 newMaxExtentSs = max(minExtentSs, maxExtentSs);
    vec3 halfSizeSs = (newMaxExtentSs - newMinExtentSs) * 0.5;
    vec3 centerSs = newMinExtentSs + halfSizeSs;
    uint mipLevel = uint(ceil(log2(max(halfSizeSs.x, halfSizeSs.y) * 2.0)));
    uint clampedMipLevel = min(mipLevel, (flrInput.mipLevelCount - 1u));
    uint cellScale = uint(pow(2.0, float(clampedMipLevel)));
    uvec2 maxSamplePos = uvec2(screenSize / uvec2(cellScale)) - uvec2(1u);
    
    
    //uvec2 sampleBasePosa = uvec2((centerSs.xy / vec2(float(cellScale))) - vec2(0.5));
    //uvec2 samplePos00a = min((sampleBasePosa + uvec2(0u)), maxSamplePos);
    //uvec2 samplePos01a = min((sampleBasePosa + uvec2(1u, 0u)), maxSamplePos);
    //uvec2 samplePos10a = min((sampleBasePosa + uvec2(0u, 1u)), maxSamplePos);
    //uvec2 samplePos11a = min((sampleBasePosa + uvec2(1u)), maxSamplePos);
    //float depth00a = texelFetch(HiZBuffer, ivec2(samplePos00a), int(clampedMipLevel)).x;
    //float depth01a = texelFetch(HiZBuffer, ivec2(samplePos01a), int(clampedMipLevel)).x;
    //float depth10a = texelFetch(HiZBuffer, ivec2(samplePos10a), int(clampedMipLevel)).x;
    //float depth11a = texelFetch(HiZBuffer, ivec2(samplePos11a), int(clampedMipLevel)).x;
    //float minDeptha = min(min(depth00a, depth01a), min(depth10a, depth11a));
    //float parama = minDeptha;
    
    
    centerSs.x-= vk3d_params.stereo.x*screenSize.x*0.5;
    //centerSs.x+= vk3d_params.stereo.x*vk3d_params.stereo.y*screenSize.x*0.0025;
    
    uvec2 sampleBasePos = uvec2((centerSs.xy / vec2(float(cellScale))) - vec2(0.5));
    uvec2 samplePos00 = min((sampleBasePos + uvec2(0u)), maxSamplePos);
    uvec2 samplePos01 = min((sampleBasePos + uvec2(1u, 0u)), maxSamplePos);
    uvec2 samplePos10 = min((sampleBasePos + uvec2(0u, 1u)), maxSamplePos);
    uvec2 samplePos11 = min((sampleBasePos + uvec2(1u)), maxSamplePos);
    float depth00 = texelFetch(HiZBuffer, ivec2(samplePos00), int(clampedMipLevel)).x;
    float depth01 = texelFetch(HiZBuffer, ivec2(samplePos01), int(clampedMipLevel)).x;
    float depth10 = texelFetch(HiZBuffer, ivec2(samplePos10), int(clampedMipLevel)).x;
    float depth11 = texelFetch(HiZBuffer, ivec2(samplePos11), int(clampedMipLevel)).x;
    float minDepth = min(min(depth00, depth01), min(depth10, depth11));
    float param = minDepth;
    float param_1 = flrInput.nearFarPlaneDistances.x;
    float param_2 = flrInput.nearFarPlaneDistances.y;
    
    //param_1-= vk3d_params.stereo.x *param*5120;
    //param_2-= vk3d_params.stereo.x *(10.5);
    //maxExtentVs.z-= vk3d_params.stereo.x *(2000);
    
    bool occluded = maxExtentVs.z < deviceDepthToLinear(param, param_1, param_2);
    if (occluded)
    {
        return;
    }
    uint indirectBatchOffset = cullingData.batchIndex.x;
    uint batchOffset = cullingData.batchIndex.y;
    uint indirectBatchSubMeshCount = cullingData.batchIndex.z;
    uint _611 = atomicAdd(_607._IndirectDrawBuffer[indirectBatchOffset].instanceCount, 1u);
    uint instanceIndex = _611;
    for (uint subMeshOffset = 1u; subMeshOffset < indirectBatchSubMeshCount; subMeshOffset++)
    {
        uint _626 = atomicAdd(_607._IndirectDrawBuffer[indirectBatchOffset + subMeshOffset].instanceCount, 1u);
        uint tempInstIdx = _626;
    }
    _633._IndirectionBuffer[batchOffset + instanceIndex].globalInstanceIndex = flrInput.invocation_index;
}

void main()
{
    FlrInput flrInput;
    flrInput.invocation_index = (gl_WorkGroupID.x * 64u) + gl_LocalInvocationID.x;
    flrInput.instanceCount = _657.instanceCount.x;
    flrInput.frustumPlanes[0] = _657.frustumPlanes[0];
    flrInput.frustumPlanes[1] = _657.frustumPlanes[1];
    flrInput.frustumPlanes[2] = _657.frustumPlanes[2];
    flrInput.frustumPlanes[3] = _657.frustumPlanes[3];
    flrInput.frustumPlanes[4] = _657.frustumPlanes[4];
    flrInput.frustumPlanes[5] = _657.frustumPlanes[5];
    flrInput.viewMatrix = _657.viewMatrix;
    flrInput.projectionMatrix = _657.projectionMatrix;
    flrInput.screenSize = _657.instanceCount.zw;
    flrInput.mainViewPos = _657.mainViewPos.xyz;
    flrInput.nearFarPlaneDistances = _657.projParameters.xy;
    flrInput.mipLevelCount = uint(textureQueryLevels(HiZBuffer));
    FlrInput param = flrInput;
    flrMain(param);
}






