#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;
    
    _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()
{
}
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);

    handle = {};
}

} //XEngine::graphics::d3d12