#version 460 compatibility
#define COMP 1
#extension GL_KHR_shader_subgroup_basic : enable
#define SST_DEBUG_PASS a
/*
    References:
        [Ulu14] Uludag, Yasin. "Hi-Z Screen-Space Cone-Traced Reflectionss".
            GPU Pro 5. 2014.
        [BEU20] Beug, Anthony Paul. "Screen Space Reflection Techniques"
            University of Regina Master Thesis. 2020.
            https:
        [Lee21] Lee, Sugu. "Screen Space Reflections : Implementation and optimization – Part 2 : HI-Z Tracing Method"
            Computer Graphics for Games. 2021.
            https:
        [SUP20] Supnik, Benjamin. "A Tip for HiZ SSR - Parametric 't' Tracing".
            The Hacks of Life. 2020.
            https:
        [GRE17] Grenier, Jean-Philippe. "Notes On Screen Space HIZ Tracing".
            jpg's blog.
            https:

    Other Credits:
        - GeforceLegend (https:
*/
#ifdef SST_DEBUG_PASS
#define GLOBAL_DATA_MODIFIER buffer
layout(std430, binding = 3) buffer TestBuffer {
    vec4 ssbo_testBuffer[];
};
#endif
/*
    References:
        [WRE18] Wrensch, Benjamin. "Reverse Z Cheatsheet". IOLITE Development Blog. 2023.
*/
/*
    References:
        [WHY23] Whyte, Killian. "Soft Clamp Function". 2023.
            https:

        You can find full license texts in /licenses
*/
// This file is generated by options.main.kts. Do not edit this file manually.
#define SETTING_SHADOW_MAP_RESOLUTION 2048//[1024 2048 3072 4096]
const float shadowDistance = 512.0;//[64.0 128.0 192.0 256.0 384.0 512.0]
const float sunPathRotation = -20.0;//[-90.0 -89.0 -88.0 -87.0 -86.0 -85.0 -84.0 -83.0 -82.0 -81.0 -80.0 -79.0 -78.0 -77.0 -76.0 -75.0 -74.0 -73.0 -72.0 -71.0 -70.0 -69.0 -68.0 -67.0 -66.0 -65.0 -64.0 -63.0 -62.0 -61.0 -60.0 -59.0 -58.0 -57.0 -56.0 -55.0 -54.0 -53.0 -52.0 -51.0 -50.0 -49.0 -48.0 -47.0 -46.0 -45.0 -44.0 -43.0 -42.0 -41.0 -40.0 -39.0 -38.0 -37.0 -36.0 -35.0 -34.0 -33.0 -32.0 -31.0 -30.0 -29.0 -28.0 -27.0 -26.0 -25.0 -24.0 -23.0 -22.0 -21.0 -20.0 -19.0 -18.0 -17.0 -16.0 -15.0 -14.0 -13.0 -12.0 -11.0 -10.0 -9.0 -8.0 -7.0 -6.0 -5.0 -4.0 -3.0 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0 31.0 32.0 33.0 34.0 35.0 36.0 37.0 38.0 39.0 40.0 41.0 42.0 43.0 44.0 45.0 46.0 47.0 48.0 49.0 50.0 51.0 52.0 53.0 54.0 55.0 56.0 57.0 58.0 59.0 60.0 61.0 62.0 63.0 64.0 65.0 66.0 67.0 68.0 69.0 70.0 71.0 72.0 73.0 74.0 75.0 76.0 77.0 78.0 79.0 80.0 81.0 82.0 83.0 84.0 85.0 86.0 87.0 88.0 89.0 90.0]
//#define SETTING_DEBUG_AE
#ifdef SETTING_DEBUG_AE
#endif
//#define SETTING_DEBUG_SST
#ifdef SETTING_DEBUG_SST
#endif
//#define SETTING_DEBUG_SST_STEPS
#ifdef SETTING_DEBUG_SST_STEPS
#endif
#if SETTING_SHADOW_MAP_RESOLUTION == 1024
#define SHADOW_MAP_SIZE_D16 64
const int shadowMapResolution = 1024;
#elif SETTING_SHADOW_MAP_RESOLUTION == 2048
#define SHADOW_MAP_SIZE_D16 128
const int shadowMapResolution = 2048;
#elif SETTING_SHADOW_MAP_RESOLUTION == 3072
#define SHADOW_MAP_SIZE_D16 192
const int shadowMapResolution = 3072;
#elif SETTING_SHADOW_MAP_RESOLUTION == 4096
#define SHADOW_MAP_SIZE_D16 256
const int shadowMapResolution = 4096;
#endif
uniform float near;
uniform float far;
uniform mat4 gbufferModelView;
uniform mat4 gbufferPreviousModelView;
uniform mat4 textureMatrix = mat4(1.0);
#ifdef DISTANT_HORIZONS
uniform float dhNearPlane;
uniform float dhFarPlane;
float farPlane = max(dhFarPlane, far);
float nearPlane = min(dhNearPlane, near);
#else
float farPlane = far;
float nearPlane = near;
#endif
uniform vec2 uval_mainImageSize;
uniform vec2 uval_mainImageSizeRcp;
uniform int uval_mainImageSizeIX;
uniform int uval_mainImageSizeIY;
ivec2 uval_mainImageSizeI = ivec2(uval_mainImageSizeIX, uval_mainImageSizeIY);
#ifndef GLOBAL_DATA_MODIFIER
#define GLOBAL_DATA_MODIFIER restrict readonly buffer
#endif
struct AEData {
    vec4 lastScreenAvgLum;
    vec4 screenAvgLum;
    vec4 expValues;
    vec2 hsPercents;
    uint shadowCount;
    uint highlightCount;
    uint weightSum;
    #ifdef SETTING_DEBUG_AE
    uint lumHistogram[256];
    uint lumHistogramMaxBinCount;
    float finalAvgLum;
    #endif
};
struct CameraData {
    uint frustumPlaneCount;
    vec4 frustumPlanes[6];
};
mat4 gbufferPrevModelView = gbufferPreviousModelView;
layout(std430, binding = 0) GLOBAL_DATA_MODIFIER GlobalData {
    uvec4 global_dispatchSize1;
    uvec4 global_dispatchSize2;
    uvec4 global_dispatchSize3;
    uvec4 global_dispatchSize4;
    mat4 gbufferPrevModelViewInverse;
    mat4 global_shadowRotationMatrix;
    mat4 global_shadowRotationMatrixInverse;
    mat4 global_taaJitterMat;
    mat4 global_camProj;
    mat4 global_camProjInverse;
    mat4 global_prevCamProj;
    mat4 global_prevCamProjInverse;
    mat4 global_shadowProjPrev;
    mat4 global_shadowProjInversePrev;
    mat4 global_shadowProj;
    mat4 global_shadowProjInverse;
    mat4 global_shadowView;
    mat4 global_shadowViewInverse;
    vec3 global_prevCameraDelta;
    vec2 global_prevTaaJitter;
    vec2 global_taaJitter;
    CameraData global_cameraData;
    vec2 global_mipmapSizes[16];
    vec2 global_mipmapSizesRcp[16];
    ivec4 global_mipmapTiles3[16];
    ivec2 global_mipmapSizesI[16];
    ivec2 global_mipmapSizePrefixesPadded[16];
    ivec2 global_mipmapSizePrefixes[16];
    ivec4 global_hizTiles[16];
    ivec4 global_mipmapTileCeil[16];
    ivec4 global_mipmapTileCeilPadded[16];
    ivec3 global_shadowAABBMinPrev;
    ivec3 global_shadowAABBMaxPrev;
    ivec3 global_shadowAABBMin;
    ivec3 global_shadowAABBMax;
    vec3 global_shadowAABBMinHistory;
    vec3 global_shadowAABBMaxHistory;
    ivec3 global_shadowAABBMinNew;
    ivec3 global_shadowAABBMaxNew;
    float global_focusDistance;
    int global_lastWorldTime;
    float global_historyResetFactor;
    vec4 global_motionFactor;
    vec4 global_taaResetFactor;
    float global_turbidity;
    AEData global_aeData;
    uint global_atomicCounters[16];
};
layout(std430, binding = 1) GLOBAL_DATA_MODIFIER IndirectComputeData {
    uint indirectComputeData[];
};
const vec2 SHADOW_MAP_SIZE = vec2(float(shadowMapResolution), 1.0 / float(shadowMapResolution));
uniform usampler2D colortex8;
uniform usampler2D colortex9;
uniform sampler2D colortex10;
uniform sampler2D usam_rgba8;
uniform sampler2D usam_hiz;
#define saturate(x) clamp(x, 0.0, 1.0)
ivec2 _textile_texelToTexel(ivec2 texelPos, ivec2 tileOffset, ivec2 tileSize) {
    return clamp(texelPos, ivec2(0), tileSize - 1) + tileOffset;
}
vec2 _textile_uvToUV(vec2 uv, vec2 tileOffsetF, vec2 tileSizeF, vec2 atlastSizeRcp) {
    vec2 textureTexelPos = clamp(uv * tileSizeF, vec2(0.5), tileSizeF - 0.5) + tileOffsetF;
    return saturate(textureTexelPos * atlastSizeRcp);
}
vec2 _textile_uvToGatherUV(vec2 uv, vec2 tileOffsetF, vec2 tileSizeF, vec2 atlastSizeRcp) {
    vec2 textureTexelPos = clamp(uv * tileSizeF, vec2(1.0), tileSizeF - 1.0) + tileOffsetF;
    return saturate(textureTexelPos * atlastSizeRcp);
}
vec2 _textile_texelToGatherUV(vec2 texelPos, vec2 tileOffsetF, vec2 tileSizeF, vec2 atlastSizeRcp) {
    vec2 textureTexelPos = clamp(texelPos, vec2(1.0), tileSizeF - 1.0) + tileOffsetF;
    return saturate(textureTexelPos * atlastSizeRcp);
}
#undef saturate
#define _shadesmith_RGBA8_ATLAS_SIZE (uval_mainImageSize * vec2(3, 3))
#define _shadesmith_RGBA8_ATLAS_SIZE_RCP (vec2(1.0) / _shadesmith_RGBA8_ATLAS_SIZE)
#define _shadesmith_RGBA8_4_OFFSET_F (uval_mainImageSize * vec2(0, 2))
#define _shadesmith_RGBA8_4_SIZE_F uval_mainImageSize
#define _shadesmith_RGBA8_4_UV_TO_UV(uv) _textile_uvToUV(uv, _shadesmith_RGBA8_4_OFFSET_F, _shadesmith_RGBA8_4_SIZE_F, _shadesmith_RGBA8_ATLAS_SIZE_RCP)
#define transient_solidAlbedo_sample(x) texture(usam_rgba8, _shadesmith_RGBA8_4_UV_TO_UV(x))
#if defined MC_GL_AMD_gpu_shader_half_float
#extension GL_AMD_gpu_shader_half_float : enable
#elif defined MC_GL_NV_gpu_shader5
#extension GL_NV_gpu_shader5 : enable
#else
#define NO_HALF
#define hf
#define float16_t float
#define f16vec2 vec2
#define f16vec3 vec3
#define f16vec4 vec4
#define f16mat2 mat2
#define f16mat3 mat3
#define f16mat4 mat4
#endif
#define PI 3.14159265358979323846
#define PI_2 (2.0 * PI)
#define PI_HALF (0.5 * PI)
#define PI_QUARTER (0.25 * PI)
#define RCP_PI (1.0 / PI)
#define RCP_PI_2 (1.0 / PI_2)
#define RCP_PI_HALF (1.0 / PI_HALF)
#define GOLDEN_RATIO 1.618033988749
#define GOLDEN_ANGLE 2.39996322972865332
#define SPHERE_SOLID_ANGLE (4.0 * PI)
#define FLT_MAX uintBitsToFloat(0x7F7FFFFF)
#define FLT_MIN uintBitsToFloat(0x00800000)
#define FLT_TRUE_MIN uintBitsToFloat(0x00000001)
#define FLT_POS_INF uintBitsToFloat(0x7F800000)
#define FLT_NEG_INF uintBitsToFloat(0xFF800000)
#define FP16_MAX 65504.0
#define INT32_MAX 2147483647
#define INT32_MIN -2147483648
#define rcp(x) (1.0 / (x))
#define saturate(x) clamp(x, 0.0, 1.0)
float coords_reversedZToViewZ(float revZ, float near) {
    return near / -revZ;
}
vec3 coords_toViewCoord(vec2 texCoord, float viewZ, mat4 projInv) {
    vec2 ndcXY = texCoord * 2.0 - 1.0;
    vec2 clipXY = ndcXY * -viewZ;
    vec2 viewXY = clipXY * vec2(projInv[0][0], projInv[1][1]);
    return vec3(viewXY, viewZ);
}
vec3 coords_octDecode11(vec2 f) {
    vec3 n = vec3(f.x, f.y, 1.0 - abs(f.x) - abs(f.y));
    float t = saturate(-n.z);
    n.xy += mix(vec2(t), vec2(-t), greaterThanEqual(n.xy, vec2(0.0)));
    return normalize(n);
}
vec2 coords_texelToUV(ivec2 texelPos, vec2 rcpTextureSize) {
    return saturate((vec2(texelPos) + 0.5) * rcpTextureSize);
}
const mat3 _COORDS_EQUATORIAL_TO_GALACTIC = mat3(
    -0.0548755604, 0.4941094279, -0.8676661490,
    -0.8734370902, -0.4448296300, -0.1980763734,
    -0.4838350155, 0.7469822445, 0.4559837762
);
const mat3 _COORDS_WORLD_TO_EQUATORIAL = mat3(
    0.0, 1.0, 0.0,
    1.0, 0.0, 0.0,
    0.0, 0.0, -1.0
);
vec3 coords_dir_worldToView(vec3 dirWorld) {
    return normalize((mat3(gbufferModelView) * dirWorld));
}
struct SSTResult {
    bool hit;
    vec3 hitScreenPos;
    vec3 lastMissScreenPos;
    vec4 test;
};
SSTResult sst_initResult() {
    SSTResult result;
    result.hit = false;
    result.hitScreenPos = vec3(-1.0);
    result.lastMissScreenPos = vec3(-1.0);
    result.test = vec4(-111.0);
    return result;
}
float intersectCellBoundary(vec3 invD, vec2 cellIdOffset, vec2 cond2, vec2 cellId, vec2 invCellCount) {
    vec2 a = cellId * invD.xy + cellIdOffset;
    vec2 t = a * invCellCount + cond2;
    float tEdge = min(t.x, t.y);
    return tEdge;
}
shared ivec2 shared_mipmapTilesOffsets[16];
shared uint shared_maxMipLevel;
shared vec4 shared_cellCounts[16];
void sst_init() {
    if (gl_LocalInvocationIndex < 16u) {
        uint maxMip = min(findMSB(min(uval_mainImageSizeI.x, uval_mainImageSizeI.y)), 16u);
        if (gl_LocalInvocationIndex == 0u) {
            shared_maxMipLevel = maxMip;
        }
        uint mipLevel = min(gl_LocalInvocationIndex, maxMip);
        shared_mipmapTilesOffsets[mipLevel] = global_hizTiles[mipLevel].xy;
        int mipLevelI = int(mipLevel);
        vec4 mainImageSizeParams = vec4(uval_mainImageSize, uval_mainImageSizeRcp);
        vec2 cellCount = ldexp(mainImageSizeParams.xy, ivec2(-mipLevelI));
        vec2 invCellCount = ldexp(mainImageSizeParams.zw, ivec2(mipLevelI));
        shared_cellCounts[mipLevel] = vec4(cellCount, invCellCount);
    }
    barrier();
}
SSTResult sst_trace(vec3 originView, vec3 rayDirView, float maxThickness) {
    vec4 originClip = global_camProj * vec4(originView, 1.0);
    vec3 originNDC = originClip.xyz / originClip.w;
    vec3 originScreen = originNDC;
    originScreen.xy = originScreen.xy * 0.5 + 0.5;
    float maxViewT = 1.0;
    maxViewT = rayDirView.z > 0.0 ? min((-nearPlane - originView.z) / rayDirView.z, maxViewT) : maxViewT;
    vec4 rayDirTempClip = global_camProj * vec4(originView + rayDirView * maxViewT, 1.0);
    vec3 rayDirTempNDC = rayDirTempClip.xyz / rayDirTempClip.w;
    vec3 rayDirTempScreen = rayDirTempNDC;
    rayDirTempScreen.xy = rayDirTempScreen.xy * 0.5 + 0.5;
    vec3 rayDirScreen = normalize(rayDirTempScreen - originScreen);
    vec3 rcpRayDirScreen = rcp(rayDirScreen);
    SSTResult result = sst_initResult();
    float maxT = 10000.0;
    maxT = rayDirScreen.z != 0.0f ? min((float(rayDirScreen.z > 0.0f) - originScreen.z) * rcpRayDirScreen.z, maxT) : maxT;
    maxT = rayDirScreen.x != 0.0f ? min((float(rayDirScreen.x > 0.0f) - originScreen.x) * rcpRayDirScreen.x, maxT) : maxT;
    maxT = rayDirScreen.y != 0.0f ? min((float(rayDirScreen.y > 0.0f) - originScreen.y) * rcpRayDirScreen.y, maxT) : maxT;
    vec3 pRayStart = originScreen;
    vec3 pRayVector = rayDirScreen * maxT;
    bool isBackwardRay = pRayVector.z > 0.0;
    float minZ = pRayStart.z;
    float maxZ = pRayStart.z + pRayVector.z;
    float deltaZ = maxZ - minZ;
    float maxThicknessFactor = rcp(1.0 - maxThickness);
    const float NEAR_Z_THICKNESS_CLAMP = 0.05;
    #define START_LEVEL 1
    #define STOP_LEVEL 0
    int level = START_LEVEL;
    float currT = 0.0;
    #ifdef SST_DEBUG_PASS
    const uvec2 DEBUG_COORD = uvec2(970, 760);
    #endif
    const uint HI_Z_STEPS = 128;
    vec4 mainImageSizeParams = vec4(uval_mainImageSize, uval_mainImageSizeRcp);
    vec3 invD = rcp(pRayVector);
    bvec2 intersectCond1 = greaterThan(pRayVector.xy, vec2(0.0));
    bvec2 vec0Cond = equal(pRayVector.xy, vec2(0.0));
    invD.xy = mix(invD.xy, vec2(0.0), vec0Cond);
    vec2 vec0Fix = vec2(vec0Cond) * 114514.0 - pRayStart.xy * invD.xy;
    vec2 cellIdOffset = mix(vec2(-0.01), vec2(1.01), intersectCond1) * invD.xy;
    vec2 crossStep = sign(pRayVector.xy) * mainImageSizeParams.zw;
    {
        vec3 currScreenPos = pRayStart + pRayVector * currT;
        vec2 currTexelPos = currScreenPos.xy * mainImageSizeParams.xy;
        vec4 cellCountData = shared_cellCounts[0];
        vec2 cellCount = cellCountData.xy;
        vec2 invCellCount = cellCountData.zw;
        vec2 cellIdx = trunc((currScreenPos.xy + crossStep) * cellCount);
        currT = max(intersectCellBoundary(invD, cellIdOffset, vec0Fix, cellIdx, invCellCount), currT);
    }
    result.hit = false;
    float negRayEndZ = -pRayStart.z * invD.z;
    int maxMip = int(shared_maxMipLevel);
    for (uint i = 0; i < HI_Z_STEPS; i++) {
        vec4 cellCountData = shared_cellCounts[level];
        ivec2 mipTileOffsets = shared_mipmapTilesOffsets[level];
        vec3 currScreenPos = pRayStart + pRayVector * currT;
        result.lastMissScreenPos = currScreenPos;
        vec2 cellCount = cellCountData.xy;
        vec2 invCellCount = cellCountData.zw;
        vec2 oldCellIdx = currScreenPos.xy * cellCount;
        ivec2 oldCellIdxI = ivec2(oldCellIdx);
        oldCellIdx = vec2(oldCellIdxI);
        vec2 cellZ = texelFetch(usam_hiz, mipTileOffsets.xy + oldCellIdxI, 0).rg;
        float cellMinZ = cellZ.x;
        float cellMaxZ = cellZ.y;
        float maxZThicknessFactor = min(NEAR_Z_THICKNESS_CLAMP, currScreenPos.z) * maxThicknessFactor;
        level--;
        if (isBackwardRay) {
            #ifdef SST_DEBUG_PASS
            #ifdef SETTING_DEBUG_SST_STEPS
            if (gl_GlobalInvocationID.xy == DEBUG_COORD) {
                ssbo_testBuffer[i + 2048] = vec4(currT, currScreenPos.z, cellMinZ, cellMaxZ);
            }
            #endif
            #endif
            if (cellMaxZ < currScreenPos.z || cellMaxZ > maxZThicknessFactor) {
                float depthT = cellMaxZ * invD.z + negRayEndZ;
                if (depthT > currT && any(notEqual(oldCellIdxI, ivec2((pRayStart.xy + pRayVector.xy * depthT) * cellCount)))) {
                    float newT = intersectCellBoundary(invD, cellIdOffset, vec0Fix, oldCellIdx, invCellCount);
                    currT = min(newT, depthT);
                    level += 2;
                    level = min(level, maxMip);
                } else {
                    if (currScreenPos.z <= cellMinZ) {
                        currT = max(currT, depthT);
                    } else {
                        currT = intersectCellBoundary(invD, cellIdOffset, vec0Fix, oldCellIdx, invCellCount);
                        level += 2;
                        level = min(level, maxMip);
                    }
                }
            }
        } else {
            float depthT = cellMinZ * invD.z + negRayEndZ;
            if (depthT > currT && any(notEqual(oldCellIdxI, ivec2((pRayStart.xy + pRayVector.xy * depthT) * cellCount)))) {
                float newT = intersectCellBoundary(invD, cellIdOffset, vec0Fix, oldCellIdx, invCellCount);
                currT = min(newT, depthT);
                level += 2;
                level = min(level, maxMip);
            } else {
                if (maxZThicknessFactor >= cellMaxZ) {
                    currT = max(depthT, currT);
                } else {
                    currT = intersectCellBoundary(invD, cellIdOffset, vec0Fix, oldCellIdx, invCellCount);
                    level += 2;
                    level = min(level, maxMip);
                }
            }
        }
        #ifdef SST_DEBUG_PASS
        #ifdef SETTING_DEBUG_SST_STEPS
        if (gl_GlobalInvocationID.xy == DEBUG_COORD) {
            ssbo_testBuffer[i] = vec4(currScreenPos.xy, 0.0, float(level));
            atomicMax(global_atomicCounters[15], i);
        }
        #endif
        #endif
        if (currT >= 1.0 || level < STOP_LEVEL) {
            result.hit = level < STOP_LEVEL;
            result.hitScreenPos = pRayStart + pRayVector * currT;
            break;
        }
    }
    #if 0 
    #ifndef SETTING_DEBUG_SST
    if (!result.hit && currT > 0.01) {
        level = 0;
        #define FIX_STEPS 4
        float stepRcp = rcp(float(FIX_STEPS));
        float minT = currT - 0.01;
        float maxT = 1.0;
        float deltaT = maxT - minT;
        for (uint i = 0; i < FIX_STEPS; i++) {
            float t = minT + ((float(i) + 1.0) * stepRcp * deltaT);
            vec3 screenPos = pRayStart + pRayVector * t;
            vec2 texelPos = saturate(screenPos.xy) * mainImageSizeParams.xy;
            vec2 cellIdx = texelPos;
            ivec2 readPos = ivec2(cellIdx);
            readPos = min(readPos, uval_mainImageSizeI);
            float cellMinZ = texelFetch(usam_hiz, readPos, 0).r;
            if (cellMinZ > screenPos.z) {
                float linearCurr = coords_reversedZToViewZ(screenPos.z, near);
                float linearDepth = coords_reversedZToViewZ(cellMinZ, near);
                float diff = linearDepth - linearCurr;
                float thickness = maxThickness * abs(linearCurr) * 0.01;
                uint cond = uint(diff < thickness) | uint(linearDepth < -65000.0);
                if (bool(cond)) {
                    result.hitScreenPos = screenPos;
                    result.hit = true;
                    return result;
                } else {
                    return result;
                }
            }
        }
    }
    #endif
    #endif
    return result;
}
vec3 nzpacking_unpackNormalOct32(uint packedNormal) {
    return coords_octDecode11(unpackSnorm2x16(packedNormal));
}
void nzpacking_unpackNormalOct16(uint packedNormal, out vec3 normal1, out vec3 normal2) {
    vec4 xyzw = unpackSnorm4x8(packedNormal);
    normal1 = coords_octDecode11(xyzw.xy);
    normal2 = coords_octDecode11(xyzw.zw);
}
const uint MATERIAL_ID_UNDEFINED = 65535u;
struct GBufferData {
    vec3 geomNormal;
    vec3 geomTangent;
    vec4 pbrSpecular;
    vec3 normal;
    vec2 lmCoord;
    uint materialID;
    vec3 albedo;
    bool isHand;
    int bitangentSign;
};
GBufferData gbufferData_init() {
    GBufferData gData;
    gData.geomNormal = vec3(0.0);
    gData.geomTangent = vec3(0.0);
    gData.pbrSpecular = vec4(0.0);
    gData.normal = vec3(0.0);
    gData.lmCoord = vec2(0.0);
    gData.materialID = MATERIAL_ID_UNDEFINED;
    gData.albedo = vec3(0.0);
    gData.isHand = false;
    return gData;
}
void gbufferData1_unpack_world(uvec4 packedData, inout GBufferData gData) {
    nzpacking_unpackNormalOct16(packedData.r, gData.geomNormal, gData.geomTangent);
    gData.geomNormal = gData.geomNormal;
    gData.geomTangent = gData.geomTangent;
    gData.pbrSpecular = unpackUnorm4x8(packedData.g);
    gData.normal = nzpacking_unpackNormalOct32(packedData.b);
    gData.lmCoord = unpackUnorm4x8(packedData.a).xy;
    gData.materialID = (packedData.a >> 16) & 0xFFFFu;
}
void gbufferData1_unpack(uvec4 packedData, inout GBufferData gData) {
    gbufferData1_unpack_world(packedData, gData);
    gData.geomNormal = coords_dir_worldToView(gData.geomNormal);
    gData.geomTangent = coords_dir_worldToView(gData.geomTangent);
    gData.normal = coords_dir_worldToView(gData.normal);
}
void gbufferData2_unpack(uvec4 packedData, inout GBufferData gData) {
    gData.albedo = unpackUnorm4x8(packedData.r).rgb;
    gData.isHand = bool(bitfieldExtract(packedData.r, 24, 1));
    gData.bitangentSign = int(bitfieldExtract(packedData.r, 25, 1)) * 2 - 1;
}
uvec2 morton_8bDecode(uint code) {
    uvec2 result = uvec2(code, code >> 1) & 0x55u;
    result = (result | (result >> 1)) & 0x33u;
    result = (result | (result >> 2)) & 0x0fu;
    return result;
}
layout(std430, binding = 7) readonly buffer ThreadGroupTilingData {
    uvec2 ssbo_threadGroupTiling[];
};
layout(local_size_x = 16, local_size_y = 16) in;
const vec2 workGroupsRender = vec2(1.0, 1.0);
layout(rgba8) uniform restrict writeonly image2D colorimg5;
void main() {
    sst_init();
    uint workGroupIdx = gl_WorkGroupID.y * gl_NumWorkGroups.x + gl_WorkGroupID.x;
    uvec2 swizzledWGPos = ssbo_threadGroupTiling[workGroupIdx];
    uvec2 workGroupOrigin = swizzledWGPos << 4u;
    uint threadIdx = gl_SubgroupID * gl_SubgroupSize + gl_SubgroupInvocationID;
    uvec2 mortonPos = morton_8bDecode(threadIdx);
    uvec2 mortonGlobalPosU = workGroupOrigin + mortonPos;
    ivec2 texelPos = ivec2(mortonGlobalPosU);
    if (all(lessThan(texelPos, uval_mainImageSizeI))) {
        GBufferData gData = gbufferData_init();
        gbufferData1_unpack(texelFetch(colortex8, texelPos, 0), gData);
        gbufferData2_unpack(texelFetch(colortex9, texelPos, 0), gData);
        float viewZ = texelFetch(colortex10, texelPos, 0).r;
        vec2 screenPos = coords_texelToUV(texelPos, uval_mainImageSizeRcp);
        vec3 viewPos = coords_toViewCoord(screenPos, viewZ, global_camProjInverse);
        vec3 viewDir = normalize(-viewPos);
        vec3 refDir = reflect(-viewDir, gData.normal);
        SSTResult sstResult = sst_trace(viewPos, refDir, 0.01);
        vec3 hitColor = vec3(0.0);
        if (sstResult.hit){
            hitColor = transient_solidAlbedo_sample(sstResult.hitScreenPos.xy).rgb;
        }
        imageStore(colorimg5, texelPos, vec4(hitColor, 1.0));
    }
}