#pragma only_renderers d3d11 vulkan metal glcore
#define UNIFIED_RT_GROUP_SIZE_X 64
#define UNIFIED_RT_GROUP_SIZE_Y 1
#define UNIFIED_RT_RAYGEN_FUNC AccumulateInternal

#include "PathTracing.hlsl"
#include "LightmapIntegrationHelpers.hlsl"

int g_SampleOffset;
uint g_ReceiveShadows;
float g_PushOff;
RWStructuredBuffer<float4> g_ExpandedOutput;
RWStructuredBuffer<float4> g_ExpandedSampleCountInW;

void AccumulateInternal(UnifiedRT::DispatchInfo dispatchInfo)
{
    float3 worldPosition = 0.f;
    float3 worldNormal = 0.f;
    float3 worldFaceNormal = 0.f;
    uint localSampleOffset = 0;
    uint2 instanceTexelPos = 0;
    const bool gotSample = GetExpandedSample(dispatchInfo.dispatchThreadID.x, localSampleOffset, instanceTexelPos, worldPosition, worldNormal, worldFaceNormal);
    if (!gotSample)
        return;

    UnifiedRT::RayTracingAccelStruct accelStruct = UNIFIED_RT_GET_ACCEL_STRUCT(g_SceneAccelStruct);

    // now sample direct lighting with the gBuffer data
    const uint sampleOffset = g_SampleOffset + localSampleOffset;
    PathTracingSampler rngState;
    rngState.Init(instanceTexelPos, sampleOffset);

    // now sample direct lighting with the gBuffer data
    uint numLights = g_NumLights;
    uint dimsOffset = 2; // first two dimensions are used for the stochastic gBuffer sampling
    float visibility[4] = { 0.f, 0.f, 0.f, 0.f };
    for (uint lightIndex = 0; lightIndex < numLights; ++lightIndex)
    {
        PTLight light = FetchLight(lightIndex);
        if (light.shadowMaskChannel == -1)
            continue;
        if (light.type == EMISSIVE_MESH || light.type == ENVIRONMENT_LIGHT) // Shadowmask only makes sense for punctual lights
            continue;
        uint dimsUsed = 0;

        float3 origin = OffsetRayOrigin(worldPosition, worldFaceNormal, g_PushOff);

        float3 attenuation = 1.0f;
        float isVisible = IsLightVisibleFromPoint(dispatchInfo, accelStruct, g_AccelStructInstanceList, ShadowRayMask(), origin, rngState, dimsOffset, dimsUsed, light, g_ReceiveShadows, attenuation) ? dot(float3(1.0f, 1.0f, 1.0f), attenuation)/3.0f : 0.0f;
        // always start at an even dimension
        if (dimsUsed % 2 == 1)
            dimsUsed++;
        dimsOffset += dimsUsed;
        if (light.shadowMaskChannel < 0 || light.shadowMaskChannel >= 4)
            continue;
        visibility[light.shadowMaskChannel] += isVisible;
    }

    // store new accumulated radiance
    g_ExpandedOutput[dispatchInfo.dispatchThreadID.x] += float4(visibility[0], visibility[1], visibility[2], visibility[3]);
    g_ExpandedSampleCountInW[dispatchInfo.dispatchThreadID.x] += float4(0.f, 0.f, 0.f, 1.f);
}