feat: implement descriptor heap with thread-safe allocation
D3D12 Resources: - Add descriptor_handle struct with CPU/GPU handles - Add descriptor_heap class for descriptor management - Implement allocate() and free() methods - Add mutex for thread-safe access - Support all D3D12 descriptor heap types D3D12 Core: - Add device() function to expose main device - Add release() template function for COM objects Documentation: - Add changelog for descriptor heap implementation - Update D3D12 Wiki with descriptor heap section - Mark descriptor heap task as completed
This commit is contained in:
@@ -16,6 +16,9 @@
|
||||
// 用于简化 COM 对象的生命周期管理
|
||||
#include <wrl.h>
|
||||
|
||||
// 引入互斥锁头文件,用于保护资源的互斥锁
|
||||
#include <mutex>
|
||||
|
||||
|
||||
#pragma comment(lib, "dxgi.lib")
|
||||
#pragma comment(lib, "d3d12.lib")
|
||||
|
||||
@@ -421,4 +421,11 @@ render()
|
||||
// 为下一帧标记并增加围栏值
|
||||
gfx_command.end_frame();
|
||||
}
|
||||
|
||||
ID3D12Device *const
|
||||
device()
|
||||
{
|
||||
return main_device;
|
||||
}
|
||||
|
||||
}// namespace XEngine::graphics::d3d12::core
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "D3D12CommonHeader.h"
|
||||
|
||||
/**
|
||||
* @brief Direct3D 12 核心类
|
||||
@@ -22,6 +23,12 @@ void shutdown();
|
||||
*/
|
||||
void render();
|
||||
|
||||
|
||||
/**
|
||||
* @brief 通用资源释放模板函数
|
||||
* @details 用于安全释放 DirectX COM 对象,检查空指针后调用 Release 并置空
|
||||
* @tparam T COM 接口类型
|
||||
*/
|
||||
template<typename T>
|
||||
constexpr void release(T*& resource)
|
||||
{
|
||||
@@ -32,4 +39,11 @@ constexpr void release(T*& resource)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取 Direct3D 12 设备
|
||||
* @details 返回 Direct3D 12 设备的智能指针
|
||||
* @return ID3D12Device* Direct3D 12 设备的智能指针
|
||||
*/
|
||||
ID3D12Device *const device();
|
||||
|
||||
}// namespace XEngine::graphics::d3d12
|
||||
101
Engine/Graphics/Direct3D12/D3D12Resource.cpp
Normal file
101
Engine/Graphics/Direct3D12/D3D12Resource.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#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
|
||||
67
Engine/Graphics/Direct3D12/D3D12Resources.h
Normal file
67
Engine/Graphics/Direct3D12/D3D12Resources.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user