#include <BlockRegistry/BlockRegistry.hpp>
#include <GraphicsContext/GraphicsContext.hpp>
#include <PipelineStage/LightingStage/LightingStage.hpp>
#include <FrameDrawer/FrameDrawer.hpp>
#include <Display/WidgetDisplay/WorldDisplay/WorldDisplay.hpp>
#include <Vk/DescriptorSet/DescriptorSet.hpp>
#include <Vk/ShaderConst.hpp>
#include <ChunkWindow/ChunkWindow.hpp>
#include <memory>

LightingStage::LightSource::LightSource(glm::uvec3 sourceVoxel, glm::vec3 color, float variance)
{
    color = glm::clamp(color, glm::vec3(0.0f), glm::vec3(1.0f));

    // Compute peak light intensity after HDR scaling.
    float maxColor = glm::max(color.x, glm::max(color.y, color.z));
    float I0 = (maxColor + variance) * GLSL_RADIANCE_SCALE;

    const float epsilon = 0.05f;
    float radius = std::sqrt(I0 / epsilon);

    uint32_t r = (uint32_t)std::ceil(radius);
    sourceVoxelAndRadius = glm::uvec4(sourceVoxel, r);
    colorAndVariance = glm::vec4(color, variance);
}

LightingStage::Session::Session() :
    lightSources(GraphicsContext::instance->getDevice())
{}

void LightingStage::checkScopeBuffer()
{
    auto &context = *GraphicsContext::instance;

    if (session->lightStorageDescPool == nullptr)
    {
        session->lightStorageDescPool = std::make_shared<Vk::DescriptorPool>(
            context.getDevice(),
            VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
            16,
            16
        );

        session->lightIndexBuffer = std::make_shared<Vk::Buffer>(
            context.getDevice(),
            sizeof(uint32_t) * GLSL_LIGHT_MAX_INDEXES,
            VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
            VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
        );

        session->lightIndexDescSet = std::make_shared<Vk::DescriptorSet>(
            context.getDevice(),
            session->lightStorageDescPool,
            GraphicsContext::arrayDescriptorSetLayout
        );

        session->lightIndexDescSet->setStorageBuffer(0, *session->lightIndexBuffer, 0, VK_WHOLE_SIZE);
    }

    if (session->currentLightScopeCapacity < session->lightSources.size())
    {
        context.holdResource(session->lightScopeBuffer);
        context.holdResource(session->lightScopeDescSet);

        session->currentLightScopeCapacity = session->lightSources.capacity();
        session->lightScopeBuffer = std::make_shared<Vk::Buffer>(
            context.getDevice(),
            session->currentLightScopeCapacity * sizeof(LightScope),
            VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
            VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
        );

        session->lightScopeDescSet = std::make_shared<Vk::DescriptorSet>(
            context.getDevice(),
            session->lightStorageDescPool,
            GraphicsContext::arrayDescriptorSetLayout
        );

        session->lightScopeDescSet->setStorageBuffer(0, *session->lightScopeBuffer, 0, VK_WHOLE_SIZE);
    }
}

void LightingStage::generateTileDepthBuffer(Vk::CommandBuffer &cmdbuf, glm::uvec2 screenTiles)
{
    cmdbuf.barrier(
        *frameDrawer.worldDisplay.normalImage,
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
        VK_IMAGE_LAYOUT_GENERAL,
        VK_IMAGE_ASPECT_COLOR_BIT
    );

    cmdbuf.barrier(
        *frameDrawer.worldDisplay.posSpecularImage,
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
        VK_IMAGE_LAYOUT_GENERAL,
        VK_IMAGE_ASPECT_COLOR_BIT
    );

    cmdbuf.barrier(
        *frameDrawer.worldDisplay.tileDepthBuffer,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_WRITE_BIT
    );

    cmdbuf.bindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *frameDrawer.worldDisplay.tileDepthPipeline);
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.tileDepthPipeline,
        GLSL_TILE_DEPTH_FRAMEDATA_DESCSET,
        *dataDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.tileDepthPipeline,
        GLSL_TILE_DEPTH_GBUFFER_DESCSET,
        *frameDrawer.worldDisplay.lightApplyGbufferDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.tileDepthPipeline,
        GLSL_TILE_DEPTH_RESULT_DESCSET,
        *frameDrawer.worldDisplay.tileDepthDescSet
    );
    cmdbuf.dispatch(glm::uvec3(screenTiles, 1));
}

void LightingStage::resetLightClusters(Vk::CommandBuffer &cmdbuf, glm::uvec2 screenTiles)
{
    cmdbuf.barrier(
        *frameDrawer.worldDisplay.lightClusterBuffer,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_WRITE_BIT
    );

    cmdbuf.bindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *frameDrawer.worldDisplay.lightClusterResetPipeline);
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightClusterResetPipeline,
        GLSL_LIGHT_CLUSTER_RESET_FRAMEDATA_DESCSET,
        *dataDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightClusterResetPipeline,
        GLSL_LIGHT_CLUSTER_RESET_BUFFER_DESCSET,
        *frameDrawer.worldDisplay.lightClusterDescSet
    );

    uint32_t numClusters = screenTiles.x * screenTiles.y * GLSL_LIGHT_DEPTH_SLICES;
    uint32_t numWorkGroups = (numClusters + GLSL_LIGHT_RESET_LOCAL_SIZE - 1) / GLSL_LIGHT_RESET_LOCAL_SIZE;

    cmdbuf.dispatch(glm::uvec3(numWorkGroups, 1, 1));
}

void LightingStage::computeLightScopes(Vk::CommandBuffer &cmdbuf, Vk::DescriptorSet &sourcesDescSet)
{
    uint32_t numLightSources = session->lightSources.size();

    cmdbuf.barrier(
        *session->lightScopeBuffer,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_WRITE_BIT
    );

    cmdbuf.barrier(
        *frameDrawer.worldDisplay.lightClusterBuffer,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_WRITE_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT
    );

    cmdbuf.barrier(
        *frameDrawer.worldDisplay.tileDepthBuffer,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_WRITE_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT
    );

    cmdbuf.bindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *frameDrawer.worldDisplay.lightScopesPipeline);
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightScopesPipeline,
        GLSL_LIGHT_SCOPES_FRAMEDATA_DESCSET,
        *dataDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightScopesPipeline,
        GLSL_LIGHT_SCOPES_SOURCES_DESCSET,
        sourcesDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightScopesPipeline,
        GLSL_LIGHT_SCOPES_BUFFER_DESCSET,
        *session->lightScopeDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightScopesPipeline,
        GLSL_LIGHT_SCOPES_CLUSTERS_DESCSET,
        *frameDrawer.worldDisplay.lightClusterDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightScopesPipeline,
        GLSL_LIGHT_SCOPES_DEPTH_DESCSET,
        *frameDrawer.worldDisplay.tileDepthDescSet
    );
    cmdbuf.pushConstants(
        *frameDrawer.worldDisplay.lightScopesPipeline,
        VK_SHADER_STAGE_COMPUTE_BIT,
        0, sizeof(uint32_t),
        &numLightSources
    );

    uint32_t numWorkGroups = (numLightSources + GLSL_LIGHT_SCOPES_LOCAL_SIZE - 1) / GLSL_LIGHT_SCOPES_LOCAL_SIZE;
    cmdbuf.dispatch(glm::uvec3(numWorkGroups, 1, 1));
}

void LightingStage::countLights(Vk::CommandBuffer &cmdbuf, glm::uvec2 screenTiles)
{
    uint32_t numLightSources = session->lightSources.size();

    cmdbuf.barrier(
        *frameDrawer.worldDisplay.lightClusterBuffer,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT
    );

    cmdbuf.barrier(
        *session->lightIndexBuffer,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_WRITE_BIT
    );

    cmdbuf.barrier(
        *session->lightScopeBuffer,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_WRITE_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT
    );

    cmdbuf.bindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *frameDrawer.worldDisplay.lightCountPipeline);
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightCountPipeline,
        GLSL_LIGHT_COUNT_FRAMEDATA_DESCSET,
        *dataDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightCountPipeline,
        GLSL_LIGHT_COUNT_CLUSTERS_DESCSET,
        *frameDrawer.worldDisplay.lightClusterDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightCountPipeline,
        GLSL_LIGHT_COUNT_SCOPES_DESCSET,
        *session->lightScopeDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightCountPipeline,
        GLSL_LIGHT_COUNT_INDEX_DESCSET,
        *session->lightIndexDescSet
    );
    cmdbuf.pushConstants(
        *frameDrawer.worldDisplay.lightCountPipeline,
        VK_SHADER_STAGE_COMPUTE_BIT,
        0, sizeof(uint32_t),
        &numLightSources
    );

    uint32_t numClusters = screenTiles.x * screenTiles.y * GLSL_LIGHT_DEPTH_SLICES;
    uint32_t numWorkGroups = (numClusters + GLSL_LIGHT_COUNT_LOCAL_SIZE - 1) / GLSL_LIGHT_COUNT_LOCAL_SIZE;

    cmdbuf.dispatch(glm::uvec3(numWorkGroups, 1, 1));
}

void LightingStage::applyLights(Vk::CommandBuffer &cmdbuf, glm::uvec2 screenTiles, Vk::DescriptorSet &sourcesDescSet)
{
    cmdbuf.barrier(
        *frameDrawer.worldDisplay.lightClusterBuffer,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT
    );

    cmdbuf.barrier(
        *session->lightIndexBuffer,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_WRITE_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT
    );

    cmdbuf.barrier(
        *frameDrawer.worldDisplay.lightImage,
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
        VK_IMAGE_LAYOUT_GENERAL,
        VK_IMAGE_ASPECT_COLOR_BIT
    );

    cmdbuf.barrier(
        *frameDrawer.worldDisplay.normalImage,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_IMAGE_LAYOUT_GENERAL,
        VK_IMAGE_LAYOUT_GENERAL,
        VK_IMAGE_ASPECT_COLOR_BIT
    );

    cmdbuf.barrier(
        *frameDrawer.worldDisplay.posSpecularImage,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_IMAGE_LAYOUT_GENERAL,
        VK_IMAGE_LAYOUT_GENERAL,
        VK_IMAGE_ASPECT_COLOR_BIT
    );

    cmdbuf.bindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *frameDrawer.worldDisplay.lightApplyPipeline);
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightApplyPipeline,
        GLSL_LIGHT_APPLY_FRAMEDATA_DESCSET,
        *dataDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightApplyPipeline,
        GLSL_LIGHT_APPLY_CLUSTERS_DESCSET,
        *frameDrawer.worldDisplay.lightClusterDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightApplyPipeline,
        GLSL_LIGHT_APPLY_INDEX_DESCSET,
        *session->lightIndexDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightApplyPipeline,
        GLSL_LIGHT_APPLY_SOURCES_DESCSET,
        sourcesDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightApplyPipeline,
        GLSL_LIGHT_APPLY_GBUFFER_DESCSET,
        *frameDrawer.worldDisplay.lightApplyGbufferDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightApplyPipeline,
        GLSL_LIGHT_APPLY_IRRADIANCE_DESCSET,
        *frameDrawer.worldDisplay.lightApplyIrradianceDescSet
    );
    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightApplyPipeline,
        GLSL_LIGHT_APPLY_SVO_DESCSET,
        frameDrawer.worldDisplay.getWorld()->window->svo.getDescSet(GraphicsContext::instance->cmdbuf())
    );

    cmdbuf.bindDescriptorSet(
        VK_PIPELINE_BIND_POINT_COMPUTE,
        *frameDrawer.worldDisplay.lightApplyPipeline,
        GLSL_LIGHT_APPLY_PALETTE_DESCSET,
        blockRegistry.getVoxelPaletteDescSet()
    );
    cmdbuf.dispatch(glm::uvec3(screenTiles, 1));

    cmdbuf.barrier(
        *frameDrawer.worldDisplay.lightImage,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_WRITE_BIT,
        VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_IMAGE_LAYOUT_GENERAL,
        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
        VK_IMAGE_ASPECT_COLOR_BIT
    );

    cmdbuf.barrier(
        *frameDrawer.worldDisplay.normalImage,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_WRITE_BIT,
        VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_IMAGE_LAYOUT_GENERAL,
        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
        VK_IMAGE_ASPECT_COLOR_BIT
    );

    cmdbuf.barrier(
        *frameDrawer.worldDisplay.posSpecularImage,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
        VK_ACCESS_SHADER_WRITE_BIT,
        VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_IMAGE_LAYOUT_GENERAL,
        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
        VK_IMAGE_ASPECT_COLOR_BIT
    );
}

LightingStage::LightingStage(
    FrameDrawer &frameDrawer,
    std::shared_ptr<Vk::DescriptorSet> dataDescSet,
    std::shared_ptr<Vk::DescriptorPool> frameDataDescriptorPool
) :
    PipelineStage("Lighting"),
    dataDescSet(dataDescSet),
    frameDrawer(frameDrawer)
{
    session = frameDrawer.worldDisplay.getSessionObject<Session>("/renderer/light-sources");
    
    for (size_t i = 0; i < maxEntityLights; i++)
    {
        session->lightSources.append(LightingStage::LightSource({}, {}));
    }
}

void LightingStage::addEntityLight(LightSource lightSource)
{
    if (numEntityLights == maxEntityLights)
    {
        return;
    }

    size_t lightIndex = numEntityLights++;
    session->lightSources.replace(lightIndex, lightSource);
}

LightingStage::LightID LightingStage::addBlockLight(LightingStage::LightSource lightSource)
{
    if (session->lightReclaimStack.size() != 0)
    {
        LightingStage::LightID id = session->lightReclaimStack.back();
        session->lightReclaimStack.resize(session->lightReclaimStack.size() - 1);
        session->lightSources.replace(id, lightSource);
        return id;
    }

    session->lightSources.append(lightSource);
    return session->lightSources.size() - 1;
}

void LightingStage::setBlockLight(LightingStage::LightID id, LightingStage::LightSource lightSource)
{
    session->lightSources.replace(id, lightSource);
}

void LightingStage::releaseBlockLight(LightingStage::LightID id)
{
    session->lightSources.replace(id, LightingStage::LightSource({}, {}));
    session->lightReclaimStack.push_back(id);
}

size_t LightingStage::getActiveLights()
{
    return session->lightSources.size() - session->lightReclaimStack.size();
}

void LightingStage::initFrame()
{
    numEntityLights = 0;
}

void LightingStage::recordStageCommands(Vk::CommandBuffer &cmdbuf)
{
    if (session->lightSources.size() == 0)
    {
        cmdbuf.transitionImageLayout(
            *frameDrawer.worldDisplay.lightImage,
            VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
            VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
        );

        return;
    }

    glm::uvec2 framebufferSize = frameDrawer.worldDisplay.lightImage->getDims();
    glm::uvec2 screenTiles = (framebufferSize + glm::uvec2(GLSL_LIGHT_TILE_SIZE - 1)) /
        glm::uvec2(GLSL_LIGHT_TILE_SIZE);
    
    checkScopeBuffer();
    auto &lightSourcesDescSet = session->lightSources.getDescSet(cmdbuf);

    generateTileDepthBuffer(cmdbuf, screenTiles);
    resetLightClusters(cmdbuf, screenTiles);
    computeLightScopes(cmdbuf, lightSourcesDescSet);
    countLights(cmdbuf, screenTiles);
    applyLights(cmdbuf, screenTiles, lightSourcesDescSet);
}