Files
DX12/Engine/Graphics/Direct3D12/D3D12Resource.cpp
SpecialX b6c0211d6a feat(d3d12): 完善描述符堆延迟释放机制与FreeList栈式索引管理
- 添加完整的中文Doxygen注释文档
- 实现process_deferred_release()延迟释放处理
- 添加deferred_release模板函数和current_frame_index()
- 实现延迟释放队列和帧索引管理
- 详细说明FreeList栈式索引分配/释放算法
- 更新D3D12学习Wiki,添加延迟释放机制章节
2026-03-30 16:59:27 +08:00

137 lines
4.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "D3D12Resources.h"
#include "D3D12Core.h"
namespace XEngine::graphics::d3d12::core{
//////////// 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