#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal glcore ps5

#pragma kernel Evict

#include "PatchUtil.hlsl"

StructuredBuffer<uint> _RingConfigBuffer;
StructuredBuffer<PatchUtil::PatchCounterSet> _PatchCounterSets;
RWStructuredBuffer<uint> _CellAllocationMarks;
RWStructuredBuffer<uint> _CellPatchIndices;
RWStructuredBuffer<uint> _PatchCellIndices;

uint _RingConfigOffset;
uint _FrameIdx;

uint ModuloDistance(uint a, uint b, uint modulo)
{
    int dif = abs(int(a) - int(b));
    return min(dif, modulo - dif);
}

[numthreads(256, 1, 1)]
void Evict(uint patchIdx : SV_DispatchThreadID)
{
    if (patchCapacity <= patchIdx)
        return;

    if (RingBuffer::IsPositionUnused(_RingConfigBuffer, _RingConfigOffset, patchIdx))
        return;

    const uint cellIdx = _PatchCellIndices[patchIdx];
    if (cellIdx == PatchUtil::invalidCellIndex)
        return;

    const PatchUtil::PatchCounterSet counterSet = _PatchCounterSets[patchIdx];
    const uint lastAccessFrameIdx = PatchUtil::GetLastAccessFrame(counterSet);

    // Here we take into account that last frame access index is in [0, 2^16-1].
    // We use that the last frame index can never be later than current frame index.
    const uint modulo = 65536; // 2^16
    const uint frameSinceLastUse = ModuloDistance(_FrameIdx % modulo, lastAccessFrameIdx, modulo);

    const uint evictionThreshold = 60 * 4;
    if (evictionThreshold < frameSinceLastUse)
    {
        _PatchCellIndices[patchIdx] = PatchUtil::invalidCellIndex;
        _CellAllocationMarks[cellIdx] = 0;
        _CellPatchIndices[cellIdx] = PatchUtil::invalidPatchIndex;
    }
}
