#pragma once
#include "D3D12CommonHeaders.h"

namespace XEngine::graphics::d3d12{
class descriptor_heap;
struct descriptor_handle
{
    D3D12_CPU_DESCRIPTOR_HANDLE cpu{};
    D3D12_GPU_DESCRIPTOR_HANDLE gpu{};

    constexpr bool is_valid() const { return cpu.ptr != 0; }
    constexpr bool is_shader_visible() const { return gpu.ptr != 0; }
#ifdef _DEBUG
private:
    friend class descriptor_heap;
    descriptor_heap*    container{ nullptr };
    u32                 index{ u32_invalid_id };       
#endif
}; // descriptor_handle
class descriptor_heap
{
public:
    explicit descriptor_heap(const D3D12_DESCRIPTOR_HEAP_TYPE type): _type(type){}
    DISABLE_COPY_AND_MOVE(descriptor_heap);
    ~descriptor_heap(){assert(!_heap);}

    bool initialize(u32 capacity, bool is_shader_visible);
    void release();

    [[nodiscard]] descriptor_handle allocate();
    void free(descriptor_handle handle);

    constexpr D3D12_DESCRIPTOR_HEAP_TYPE type() const { return _type; }
    constexpr D3D12_CPU_DESCRIPTOR_HANDLE cpu_start() const { return _cpu_start; }
    constexpr D3D12_GPU_DESCRIPTOR_HANDLE gpu_start() const { return _gpu_start; }
    constexpr ID3D12DescriptorHeap *const heap() const { return _heap; }
    constexpr u32 capacity() const { return _capacity; }
    constexpr u32 size() const { return _size; }
    constexpr u32 descriptor_size() const { return _descriptor_size; }
    constexpr bool is_shader_visible() const { return _gpu_start.ptr != 0; }
private:
    // 一个描述符堆是一个内存块,基于他是否着色器可见,分配在系统内存还是显存
    ID3D12DescriptorHeap*                   _heap;

    // CPU起始句柄（用于CPU端描述符操作）
    D3D12_CPU_DESCRIPTOR_HANDLE             _cpu_start{};
    // GPU起始句柄（仅当堆为着色器可见时有效）
    D3D12_GPU_DESCRIPTOR_HANDLE             _gpu_start{};

    // 空闲的描述符句柄
    std::unique_ptr<u32[]>                  _free_handles{};

    // 用于保护资源初始化的互斥锁
    std::mutex                              _mutex; 

    // 描述符堆的容量
    u32                                     _capacity{0};
    // 已经分配的描述符数量
    u32                                     _size{0};
    // 不同的描述符堆类型在不同的硬件上有不同的大小,所以需要记录下描述符的大小
    u32                                     _descriptor_size{0};
    const D3D12_DESCRIPTOR_HEAP_TYPE        _type;
};
}



