#include "D3D12Resources.h"
#include "D3D12Core.h"

namespace XEngine::graphics::d3d12{
////////////	DESCRIPTOR HEAP     ////////////
// 该类将被多个线程并发访问：资源创建（如纹理）与资源销毁/释放可能发生在不同线程，
// 因此需要同步机制保护内部数据结构
bool
descriptor_heap::initialize(u32 capacity, bool is_shader_visible)
{
    std::lock_guard lock(_mutex);
    // 检查容量有效性：必须大于0且不超过最大限制
    assert(capacity && capacity <= D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_2);
    // 对于采样器描述符堆，需要额外检查容量是否超过最大采样器堆大小限制
    assert(!(_type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER &&
        capacity > D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE));

    // 对于DSV和RTV他们在描述符堆的内存模型中本身就是GPU不可见的
    if(_type == D3D12_DESCRIPTOR_HEAP_TYPE_DSV ||
       _type == D3D12_DESCRIPTOR_HEAP_TYPE_RTV)
    {
        is_shader_visible = false;
    }

    // 应为这个功能将被调用多次,所以需要先释放之前的资源
    release();

    ID3D12Device *const device {core::device()};
    assert(device);

    D3D12_DESCRIPTOR_HEAP_DESC desc{};
    desc.Flags = is_shader_visible
         ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE
         : D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    desc.NumDescriptors = capacity;
    desc.Type = _type;
    desc.NodeMask = 0;

    HRESULT hr {S_OK};
    DXCall(hr = device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&_heap)));
    if(FAILED(hr)) return false;
    
    _free_handles = std::move(std::make_unique<u32[]>(capacity));
    _capacity = capacity;
    _size = 0;

    for(u32 i = 0; i < capacity; ++i) _free_handles[i] = i;
    DEBUG_OP(for(u32 i{0}; i<frame_buffer_count;++i) assert(_deferred_free_indices[i].empty()));

    _descriptor_size = device->GetDescriptorHandleIncrementSize(_type);
    _cpu_start = _heap->GetCPUDescriptorHandleForHeapStart();
    _gpu_start = is_shader_visible 
        ? _heap->GetGPUDescriptorHandleForHeapStart() 
        : D3D12_GPU_DESCRIPTOR_HANDLE{0};
    
    return true;

}

void
descriptor_heap::release()
{
    assert(!_size);
    core::deferred_release(_heap);
}

// 处理延迟释放
// 注意这里的free_handles对应的是一整块连续的内存,而不是对应的描述符句柄的索引index
// 描述符自生是记录了自己的地址(通过简单的计算可以得出偏移和索引)
// 所以释放后,只需要指向释放出来的索引index即可,这样下次新增时即可以
// 放在对应的空闲区域.
// 这一切都是应为freelist和内存是一对一对应的关系
// 延迟释放机制确保GPU完成使用后才回收描述符，避免GPU仍在访问时重用
// 使用双缓冲/多帧缓冲机制，当前帧释放的描述符会在若干帧后安全回收
// _free_handles作为空闲索引栈，_size指向栈顶，回收的索引压入栈中供后续分配使用
void
descriptor_heap::process_deferred_release(u32 frame_index)
{
    std::lock_guard lock(_mutex);
    assert(frame_index < frame_buffer_count);

    utl::vector<u32>& indices { _deferred_free_indices[frame_index] };
    if(!indices.empty())
    {
        for(auto index : indices)
        {
            --_size;
            _free_handles[_size] = index;
        }
        indices.clear();
    }
}



descriptor_handle
descriptor_heap::allocate()
{
    std::lock_guard lock(_mutex);
    assert(_heap);
    assert(_size < _capacity);

    const u32 index { _free_handles[_size] };
    const u32 offset { index * _descriptor_size };
    ++_size;

    descriptor_handle handle{};
    handle.cpu.ptr = _cpu_start.ptr + offset;
    if(is_shader_visible())
    {
        handle.gpu.ptr = _gpu_start.ptr + offset;
    }   

    DEBUG_OP(handle.container = this);
    DEBUG_OP(handle.index = index);
    return handle;
}
void
descriptor_heap::free(descriptor_handle handle)
{
    if(!handle.is_valid()) return;
    std::lock_guard lock(_mutex);
    assert(_heap && _size);
    assert(handle.container == this);
    assert(handle.cpu.ptr  >= _cpu_start.ptr);
    assert((handle.cpu.ptr - _cpu_start.ptr) % _descriptor_size == 0);
    assert(handle.index < _capacity);
    const u32 index{ (u32)(handle.cpu.ptr - _cpu_start.ptr) / _descriptor_size };
    assert(handle.index == index);

    const u32 frame_index{ core::current_frame_index() };
    _deferred_free_indices[frame_index].push_back(index);
    core::set_deferred_release_flag();
    handle = {};
}

} //XEngine::graphics::d3d12