#include "D3D12Resources.h" #include "D3D12Core.h" #include "D3D12Helpers.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(capacity)); _capacity = capacity; _size = 0; for(u32 i = 0; i < capacity; ++i) _free_handles[i] = i; DEBUG_OP(for(u32 i{0}; iGetDescriptorHandleIncrementSize(_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& 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 = {}; } //////////// D3D12 TEXTURE //////////// d3d12_texture::d3d12_texture( d3d12_texture_init_info info) { auto *const device {core::device()}; assert(device); D3D12_CLEAR_VALUE *const clear_value{ (info.desc && (info.desc->Flags &D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET || info.desc && info.desc->Flags &D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) ? &info.clear_value : nullptr }; if(info.resource) { _resource = info.resource; } else if(info.heap && info.desc) { assert(!info.resource); DXCall(device->CreatePlacedResource( info.heap, info.allocation_info.Offset, info.desc, info.initial_state, clear_value, IID_PPV_ARGS(&_resource) )); } else if (info.desc) { assert(!info.srv_desc); DXCall(device->CreateCommittedResource( &d3dx::heap_properties.default_heap, D3D12_HEAP_FLAG_NONE, info.desc, info.initial_state, clear_value, IID_PPV_ARGS(&_resource) )); } assert(_resource); _srv = core::srv_heap().allocate(); device->CreateShaderResourceView(_resource, info.srv_desc, _srv.cpu); } void d3d12_texture::release() { core::srv_heap().free(_srv); core::deferred_release(_resource); } //////////// D3D12 RENDER TEXTURE //////////// d3d12_render_texture::d3d12_render_texture(d3d12_texture_init_info info) : _texture(info) { assert(info.desc); _mip_count = resource()->GetDesc().MipLevels; assert(_mip_count && _mip_count <= d3d12_texture::max_mips); descriptor_heap& rtv_heap {core::rtv_heap()}; D3D12_RENDER_TARGET_VIEW_DESC desc{}; desc.Format = info.desc->Format; desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; desc.Texture2D.MipSlice = 0; auto *const device {core::device()}; assert(device); for(u32 i{0}; i<_mip_count;++i) { _rtv[i] = rtv_heap.allocate(); device->CreateRenderTargetView(resource(), &desc, _rtv[i].cpu); ++desc.Texture2D.MipSlice; } } void d3d12_render_texture::release() { for(u32 i{0}; i<_mip_count;++i) core::srv_heap().free(_rtv[i]); _texture.release(); _mip_count = 0; } //////////// D3D12 DEPTH TEXTURE //////////// d3d12_depth_buffer::d3d12_depth_buffer(d3d12_texture_init_info info) : _texture(info) { assert(info.desc); const DXGI_FORMAT dsv_format {info.desc->Format}; D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc{}; if(info.desc->Format == DXGI_FORMAT_D32_FLOAT) { info.desc->Format = DXGI_FORMAT_R32_TYPELESS; srv_desc.Format = DXGI_FORMAT_R32_FLOAT; } srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; srv_desc.Texture2D.MipLevels = 1; srv_desc.Texture2D.MostDetailedMip = 0; srv_desc.Texture2D.PlaneSlice = 0; srv_desc.Texture2D.ResourceMinLODClamp = 0.f; assert(!info.srv_desc && !info.resource); info.srv_desc = &srv_desc; _texture = d3d12_texture(info); D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc{}; dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; dsv_desc.Flags = D3D12_DSV_FLAG_NONE; dsv_desc.Format = dsv_format; dsv_desc.Texture2D.MipSlice = 0; _dsv = core::dsv_heap().allocate(); auto *const device {core::device()}; assert(device); device->CreateDepthStencilView(resource(), &dsv_desc, _dsv.cpu); } void d3d12_depth_buffer::release() { core::dsv_heap().free(_dsv); _texture.release(); _dsv = {}; } } //XEngine::graphics::d3d12