feat(d3d12): 新增纹理资源类,修复 Surface 重复释放问题

核心变更:
- 新增 d3d12_texture 和 d3d12_render_texture 类
- 新增 d3d12_texture_init_info 结构,支持三种资源创建方式
- 新增 D3D12Helpers.h,提供堆属性辅助结构
- 改用 utl::free_list 管理 surface,解决重复释放问题
- 为 d3d12_surface 添加移动语义,支持撕裂检测

文档完善:
- 为 FreeList.h 和 Vector.h 添加完整 Doxygen 中文注释
- 更新 D3D12 学习 Wiki,添加 SRV、资源创建方式、纹理资源类章节
- 新增变更记录文档
This commit is contained in:
SpecialX
2026-04-01 16:15:12 +08:00
parent 95d8893182
commit 4d13d8df89
19 changed files with 1821 additions and 233 deletions

View File

@@ -37,6 +37,7 @@
<ClInclude Include="Graphics\Direct3D12\D3D12Core.h" />
<ClInclude Include="Graphics\Direct3D12\D3D12Interface.h" />
<ClInclude Include="Graphics\Direct3D12\D3D12Resources.h" />
<ClInclude Include="Graphics\Direct3D12\D3D12Surface.h" />
<ClInclude Include="Graphics\GraphicsPlatformInterface.h" />
<ClInclude Include="Graphics\Renderer.h" />
<ClInclude Include="Platform\IncludeWindowCpp.h" />
@@ -61,6 +62,7 @@
<ClCompile Include="Graphics\Direct3D12\D3D12Core.cpp" />
<ClCompile Include="Graphics\Direct3D12\D3D12Interface.cpp" />
<ClCompile Include="Graphics\Direct3D12\D3D12Resource.cpp" />
<ClCompile Include="Graphics\Direct3D12\D3D12Surface.cpp" />
<ClCompile Include="Graphics\Renderer.cpp" />
<ClCompile Include="Platform\PlatformWin32.cpp" />
<ClCompile Include="Platform\Window.cpp" />

View File

@@ -30,6 +30,7 @@
<ClInclude Include="Graphics\Direct3D12\D3D12Interface.h" />
<ClInclude Include="Graphics\GraphicsPlatformInterface.h" />
<ClInclude Include="Graphics\Direct3D12\D3D12Resources.h" />
<ClInclude Include="Graphics\Direct3D12\D3D12Surface.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Components\Entity.cpp" />
@@ -46,6 +47,7 @@
<ClCompile Include="Graphics\Direct3D12\D3D12Core.cpp" />
<ClCompile Include="Graphics\Direct3D12\D3D12Interface.cpp" />
<ClCompile Include="Graphics\Direct3D12\D3D12Resource.cpp" />
<ClCompile Include="Graphics\Direct3D12\D3D12Surface.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="Graphics\Direct3D12\D3D12Resources" />

View File

@@ -1,10 +1,12 @@
#include "D3D12Core.h"
#include "D3D12CommonHeader.h"
#include "D3D12Resources.h"
#include "D3D12Surface.h"
using namespace Microsoft::WRL;
namespace XEngine::graphics::d3d12::core {
namespace {
using suface_collection = utl::free_list<d3d12_surface>;
/**
* @brief D3D12命令管理类设计说明
* @details 本类采用RAII设计模式封装Direct3D 12的命令队列和命令列表提供类型安全的GPU命令提交机制
@@ -249,6 +251,9 @@ IDXGIFactory7* dxgi_factory{ nullptr };
*/
d3d12_command gfx_command;
suface_collection surfaces;
/**
@@ -531,25 +536,6 @@ shutdown()
release(main_device);
}
void
render()
{
// 等待GPU完成命令列表,并重置命令分配器和命令列表
gfx_command.begin_frame();
ID3D12GraphicsCommandList6* cmd_list{ gfx_command.command_list() };
const u32 frame_index{ current_frame_index() };
if(deferred_release_flag[frame_index])
{
process_deferred_release(frame_index);
}
// 记录命令
//
// 完成命令记录,立即提交命令列表到命令队列执行
// 为下一帧标记并增加围栏值
gfx_command.end_frame();
}
ID3D12Device *const
device()
{
@@ -583,5 +569,65 @@ set_deferred_release_flag()
deferred_release_flag[current_frame_index()] = 1;
}
#pragma region surface
surface
create_surface(platform::window window)
{
surface_id id{ surfaces.add(window) };
surfaces[id].create_swap_chain(dxgi_factory,gfx_command.command_queue(),render_target_format);
return surface{id};
}
void
remove_surface(surface_id id)
{
gfx_command.flush();
surfaces.remove(id);
}
void
resize_surface(surface_id id, u32 width, u32 height)
{
gfx_command.flush();
surfaces[id].resize();
}
u32
surface_width(surface_id id)
{
return surfaces[id].width();
}
u32
surface_height(surface_id id)
{
return surfaces[id].height();
}
void
render_surface(surface_id id)
{
// 等待GPU完成命令列表,并重置命令分配器和命令列表
gfx_command.begin_frame();
ID3D12GraphicsCommandList6* cmd_list{ gfx_command.command_list() };
const u32 frame_index{ current_frame_index() };
if(deferred_release_flag[frame_index])
{
process_deferred_release(frame_index);
}
// 呈现交换链
surfaces[id].present();
// 记录命令
//
// 完成命令记录,立即提交命令列表到命令队列执行
// 为下一帧标记并增加围栏值
gfx_command.end_frame();
}
#pragma endregion
}// namespace XEngine::graphics::d3d12::core

View File

@@ -22,12 +22,6 @@ bool initialize();
* @details 调用 Direct3D 12 设备的关闭函数,释放所有资源
*/
void shutdown();
/**
* @brief 渲染 Direct3D 12 核心功能
* @details 调用 Direct3D 12 设备的渲染函数,渲染当前渲染表面
*/
void render();
/**
* @brief 立即释放 DirectX COM 对象并将指针置空
@@ -104,6 +98,12 @@ DXGI_FORMAT default_render_target_format();
*/
void set_deferred_release_flag();
surface create_surface(platform::window window);
void remove_surface(surface_id id);
void resize_surface(surface_id id, u32 width, u32 height);
u32 surface_width(surface_id id);
u32 surface_height(surface_id id);
void render_surface(surface_id id);
}// namespace XEngine::graphics::d3d12::core

View File

@@ -0,0 +1,14 @@
#pragma once
#include "D3D12CommonHeader.h"
namespace XEngine::graphics::d3d12::d3dx
{
constexpr struct{
D3D12_HEAP_PROPERTIES default_heap{
D3D12_HEAP_TYPE_DEFAULT, // 堆类型默认堆GPU可读写CPU不可直接访问。选项D3D12_HEAP_TYPE_DEFAULT默认堆、D3D12_HEAP_TYPE_UPLOAD上传堆CPU写入GPU读取、D3D12_HEAP_TYPE_READBACK回读堆GPU写入CPU读取、D3D12_HEAP_TYPE_CUSTOM自定义堆
D3D12_CPU_PAGE_PROPERTY_UNKNOWN, // CPU页属性未知使用默认设置。选项D3D12_CPU_PAGE_PROPERTY_UNKNOWN未知、D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE不可用、D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE写入合并、D3D12_CPU_PAGE_PROPERTY_WRITE_BACK写回
D3D12_MEMORY_POOL_UNKNOWN, // 内存池未知由驱动自动选择。选项D3D12_MEMORY_POOL_UNKNOWN未知、D3D12_MEMORY_POOL_L0系统内存池、D3D12_MEMORY_POOL_L1显存池
0, // 节点掩码单GPU系统设为0多GPU系统中指定使用哪个GPU节点
0, // 保留字段必须为0为将来扩展预留
};
}heap_properties;
}

View File

@@ -7,7 +7,14 @@ void get_platform_interface(platform_interface& pi)
{
pi.initialize = core::initialize;
pi.shutdown = core::shutdown;
pi.render = core::render;
pi.surface.create = core::create_surface;
pi.surface.remove = core::remove_surface;
pi.surface.resize = core::resize_surface;
pi.surface.width = core::surface_width;
pi.surface.height = core::surface_height;
pi.surface.render = core::render_surface;
}
}// namespace XEngine::graphics::d3d12

View File

@@ -1,8 +1,11 @@
#include "D3D12Resources.h"
#include "D3D12Core.h"
#include "D3D12Helpers.h"
namespace XEngine::graphics::d3d12{
//////////// DESCRIPTOR HEAP ////////////
// 该类将被多个线程并发访问:资源创建(如纹理)与资源销毁/释放可能发生在不同线程,
// 因此需要同步机制保护内部数据结构
bool
@@ -134,4 +137,63 @@ descriptor_heap::free(descriptor_handle handle)
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 ////////////
} //XEngine::graphics::d3d12

View File

@@ -417,4 +417,64 @@ private:
const D3D12_DESCRIPTOR_HEAP_TYPE _type;
};
struct d3d12_texture_init_info
{
ID3D12Heap1* heap{nullptr};
ID3D12Resource* resource{nullptr};
D3D12_SHADER_RESOURCE_VIEW_DESC* srv_desc{nullptr};
D3D12_RESOURCE_DESC* desc{nullptr};
D3D12_RESOURCE_ALLOCATION_INFO1 allocation_info{};
D3D12_RESOURCE_STATES initial_state{};
D3D12_CLEAR_VALUE clear_value{};
};
class d3d12_texture
{
public:
constexpr static u32 max_mips{ 14 };
d3d12_texture() = default;
explicit d3d12_texture(d3d12_texture_init_info info);
DISABLE_COPY(d3d12_texture);
constexpr d3d12_texture(d3d12_texture&& o)
: _resource(o._resource), _srv(o._srv) //这些值只是指针和句柄不需要move
{
o.reset();
}
constexpr d3d12_texture& operator=(d3d12_texture&& o)
{
assert(this != &o);
if(this != &o)
{
release();
move(o);
}
return *this;
}
void release();
constexpr ID3D12Resource *const resource() const { return _resource; }
constexpr descriptor_handle srv() const { return _srv; }
private:
constexpr void move(d3d12_texture& o)
{
_resource = o._resource;
_srv = o._srv;
o.reset();
}
constexpr void reset()
{
_resource = nullptr;
_srv = {};
}
ID3D12Resource* _resource{nullptr};
descriptor_handle _srv;
};
} // namespace XEngine::graphics::d3d12

View File

@@ -2,113 +2,137 @@
#include "D3D12Core.h"
namespace XEngine::graphics::d3d12 {
namespace{
namespace XEngine::graphics::d3d12 {
namespace {
constexpr DXGI_FORMAT
to_non_srgb(DXGI_FORMAT format)
{
if(format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) return DXGI_FORMAT_R8G8B8A8_UNORM;
return format;
if (format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) return DXGI_FORMAT_R8G8B8A8_UNORM;
return format;
}
} // anonymous namespace
void
d3d12_surface::create_swap_chain(IDXGIFactory7* factory, ID3D12CommandQueue* cmd_queue, DXGI_FORMAT format)
{
assert(!factory && cmd_queue);
assert(factory && cmd_queue);
// 应为可以多次调用,所以需要先释放旧的 swap chain
release();
// 应为可以多次调用,所以需要先释放旧的 swap chain
release();
DXGI_SWAP_CHAIN_DESC1 desc{};
desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; // Alpha通道模式窗口透明度相关
desc.BufferCount = frame_buffer_count; // 后台缓冲区数量(用于双缓冲/多缓冲)
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 缓冲区用途(作为渲染目标输出)
desc.Flags = 0; // 交换链标志位(无)
desc.Format = to_non_srgb(format); // 像素格式转换为非SRGB格式
desc.Height = _window.height(); // 交换链高度(与窗口高度一致)
desc.Width = _window.width(); // 交换链宽度(与窗口宽度一致)
desc.SampleDesc.Count = 1; // 多重采样数量1表示禁用MSAA
desc.SampleDesc.Quality = 0; // 多重采样质量等级0表示禁用
desc.Scaling = DXGI_SCALING_STRETCH; // 缓冲区缩放模式(窗口大小变化时的处理方式)
desc.Stereo = false; // 立体显示模式3D显示
if (SUCCEEDED(factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &_allow_tearing, sizeof(u32))) && _allow_tearing)
{
_present_flags = DXGI_PRESENT_ALLOW_TEARING;
}
IDXGISwapChain1* swap_chain;
HWND hwnd {(HWND)_window.handle()};
// 为窗口创建交换链,指定交换链描述结构体
DXCall(factory->CreateSwapChainForHwnd(cmd_queue, hwnd, &desc, nullptr, nullptr, &swap_chain));
// 控制 DXGI 如何响应特定的系统按键,阻止 DXGI 响应 Alt+Enter 按键序列,由应用程序手动处理全屏切换逻辑。
DXCall(factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER));
// 将局部变量 swap_chain 创建的交换链对象“转移”给类成员 _swap_chain 来长期持有。
// 免了直接赋值_swap_chain = swap_chain可能导致的引用计数问题直接赋值不增加
// 计数,后续释放 swap_chain 会使对象计数归零而被销毁_swap_chain 就会变成悬空指针。
DXCall(swap_chain->QueryInterface(IID_PPV_ARGS(&_swap_chain)));
core::release(swap_chain);
DXGI_SWAP_CHAIN_DESC1 desc{};
desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; // Alpha通道模式窗口透明度相关
desc.BufferCount = buffer_count; // 后台缓冲区数量(用于双缓冲/多缓冲)
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 缓冲区用途(作为渲染目标输出)
desc.Flags = _allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; // 交换链标志位(无)
desc.Format = to_non_srgb(format); // 像素格式转换为非SRGB格式
desc.Height = _window.height(); // 交换链高度(与窗口高度一致)
desc.Width = _window.width(); // 交换链宽度(与窗口宽度一致)
desc.SampleDesc.Count = 1; // 多重采样数量1表示禁用MSAA
desc.SampleDesc.Quality = 0; // 多重采样质量等级0表示禁用
desc.Scaling = DXGI_SCALING_STRETCH; // 缓冲区缩放模式(窗口大小变化时的处理方式)
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // 交换链效果(翻转并丢弃旧缓冲区)
desc.Stereo = false; // 立体显示模式3D显示
_current_bb_index = _swap_chain->GetCurrentBackBufferIndex();
IDXGISwapChain1* swap_chain;
HWND hwnd{ (HWND)_window.handle() };
// 为窗口创建交换链,指定交换链描述结构体
DXCall(factory->CreateSwapChainForHwnd(cmd_queue, hwnd, &desc, nullptr, nullptr, &swap_chain));
// 控制 DXGI 如何响应特定的系统按键,阻止 DXGI 响应 Alt+Enter 按键序列,由应用程序手动处理全屏切换逻辑。
DXCall(factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER));
// 将局部变量 swap_chain 创建的交换链对象“转移”给类成员 _swap_chain 来长期持有。
// 免了直接赋值_swap_chain = swap_chain可能导致的引用计数问题直接赋值不增加
// 计数,后续释放 swap_chain 会使对象计数归零而被销毁_swap_chain 就会变成悬空指针。
DXCall(swap_chain->QueryInterface(IID_PPV_ARGS(&_swap_chain)));
core::release(swap_chain);
for(u32 i = 0; i < frame_buffer_count; ++i)
{
_render_target_data[i].rtv = core::rtv_heap().allocate();
}
_current_bb_index = _swap_chain->GetCurrentBackBufferIndex();
finalize();
for (u32 i = 0; i < buffer_count; ++i)
{
_render_target_data[i].rtv = core::rtv_heap().allocate();
}
finalize();
}
void
d3d12_surface::present() const
{
assert(_swap_chain);
// 设置是否使用垂直同步
// 0 表示不使用垂直同步1 表示使用垂直同步
// 第二个参数设置有无特殊行为0 表示无特殊行为
DXCall(_swap_chain->Present(0, 0));
_current_bb_index = _swap_chain->GetCurrentBackBufferIndex();
assert(_swap_chain);
// 设置是否使用垂直同步
// 0 表示不使用垂直同步1 表示使用垂直同步
// 第二个参数设置有无特殊行为0 表示无特殊行为
DXCall(_swap_chain->Present(0, _present_flags));
_current_bb_index = _swap_chain->GetCurrentBackBufferIndex();
}
void
d3d12_surface::finalize()
{
// 为每个缓冲区创建渲染目标视图
for(u32 i = 0; i < frame_buffer_count; ++i)
{
render_target_data& data{ _render_target_data[i] };
assert(!data.resource);
DXCall(_swap_chain->GetBuffer(i, IID_PPV_ARGS(&data.resource)));
D3D12_RENDER_TARGET_VIEW_DESC desc{};
desc.Format = core::default_render_target_format();
desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
core::device()->CreateRenderTargetView(data.resource, &desc, data.rtv.cpu);
}
// 为每个缓冲区创建渲染目标视图
for (u32 i = 0; i < buffer_count; ++i)
{
render_target_data& data{ _render_target_data[i] };
assert(!data.resource);
DXCall(_swap_chain->GetBuffer(i, IID_PPV_ARGS(&data.resource)));
D3D12_RENDER_TARGET_VIEW_DESC desc{};
desc.Format = core::default_render_target_format();
desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
core::device()->CreateRenderTargetView(data.resource, &desc, data.rtv.cpu);
}
DXGI_SWAP_CHAIN_DESC desc{};
DXCall(_swap_chain->GetDesc(&desc));
const u32 width{ desc.BufferDesc.Width };
const u32 height{ desc.BufferDesc.Height };
assert(_window.width() == width && _window.height() == height);
DXGI_SWAP_CHAIN_DESC desc{};
DXCall(_swap_chain->GetDesc(&desc));
const u32 width{ desc.BufferDesc.Width };
const u32 height{ desc.BufferDesc.Height };
assert(_window.width() == width && _window.height() == height);
_viewport.TopLeftX = 0.0f;
_viewport.TopLeftY = 0.0f;
_viewport.Width = (float)width;
_viewport.Height = (float)height;
_viewport.MaxDepth = 1.0f;
_viewport.MinDepth = 0.0f;
_viewport.TopLeftX = 0.0f;
_viewport.TopLeftY = 0.0f;
_viewport.Width = (float)width;
_viewport.Height = (float)height;
_viewport.MaxDepth = 1.0f;
_viewport.MinDepth = 0.0f;
_scissor_rect = {0, 0, (s32)width, (s32)height};
_scissor_rect = { 0, 0, (s32)width, (s32)height };
}
void
d3d12_surface::release()
{
for(u32 i = 0; i < frame_buffer_count; ++i)
{
render_target_data& data{ _render_target_data[i] };
core::release(data.resource);
core::rtv_heap().free(data.rtv);
}
for (u32 i = 0; i < buffer_count; ++i)
{
render_target_data& data{ _render_target_data[i] };
core::release(data.resource);
core::rtv_heap().free(data.rtv);
core::release(_swap_chain);
}
core::release(_swap_chain);
}
void
d3d12_surface::resize()
{
}
u32
d3d12_surface::width()
{
return _window.width();
}
u32
d3d12_surface::height()
{
return _window.height();
}
} // namespace XEngine::graphics::d3d12

View File

@@ -69,7 +69,61 @@ public:
* @details
* 调用 release() 清理交换链和渲染目标资源。
*/
~d3d12_surface(){release();}
~d3d12_surface(){ release(); }
constexpr static u32 buffer_count{ 3 };
#if USE_STL_VECTOR
DISABLE_COPY(d3d12_surface)
/**
* @brief 移动构造函数
*
* @param o 要移动的源对象
*
* @details
* 转移所有资源所有权,将源对象置为空状态。
* 用于 vector 扩容时安全转移资源。
*/
constexpr d3d12_surface(d3d12_surface&& o) noexcept
: _swap_chain{o._swap_chain}, _window{o._window}, _current_bb_index{o._current_bb_index}
, _viewport{o._viewport}, _scissor_rect{o._scissor_rect}
, _allow_tearing{o._allow_tearing}, _present_flags{o._present_flags}
{
for (u32 i = 0; i < frame_buffer_count; ++i)
{
_render_target_data[i].resource = o._render_target_data[i].resource;
_render_target_data[i].rtv = o._render_target_data[i].rtv;
}
o.reset();
}
/**
* @brief 移动赋值运算符
*
* @param o 要移动的源对象
* @return 当前对象引用
*
* @details
* 先释放当前资源,再转移所有权。
*/
constexpr d3d12_surface& operator=(d3d12_surface&& o) noexcept
{
assert(this != &o);
if (this != &o)
{
release();
move(o);
}
return *this;
}
#else
DISABLE_COPY_AND_MOVE(d3d12_surface);
#endif
/**
* @brief 创建交换链和渲染目标视图
@@ -167,6 +221,43 @@ public:
constexpr const D3D12_RECT& scissor_rect() const {return _scissor_rect;}
private:
#if USE_STL_VECTOR
constexpr void move(d3d12_surface& o)
{
_swap_chain = o._swap_chain;
for (u32 i = 0; i < frame_buffer_count; ++i)
{
_render_target_data[i] = o._render_target_data[i];
}
_window = o._window;
_current_bb_index = o._current_bb_index;
_viewport = o._viewport;
_scissor_rect = o._scissor_rect;
_allow_tearing = o._allow_tearing;
_present_flags = o._present_flags;
o.reset();
}
constexpr void reset()
{
_swap_chain = nullptr;
for (u32 i = 0; i < frame_buffer_count; ++i)
{
_render_target_data[i] = {};
}
_window = {};
_current_bb_index = 0;
_viewport = {};
_scissor_rect = {};
_allow_tearing = 0;
_present_flags = 0;
}
#endif
/**
* @brief 释放所有资源
*
@@ -200,11 +291,13 @@ private:
};
IDXGISwapChain4* _swap_chain{nullptr}; ///< DXGI 交换链对象
render_target_data _render_target_data[frame_buffer_count];///< 后台缓冲区数据数组
render_target_data _render_target_data[buffer_count]; ///< 后台缓冲区数据数组
platform::window _window{}; ///< 关联的平台窗口
mutable u32 _current_bb_index{0}; ///< 当前后台缓冲区索引
D3D12_VIEWPORT _viewport{}; ///< 视口描述
D3D12_RECT _scissor_rect{}; ///< 裁剪矩形
u32 _allow_tearing{ 0 }; ///< 是否允许撕裂
u32 _present_flags{ 0 }; ///< 呈现标志位
};
}

View File

@@ -11,7 +11,6 @@ namespace XEngine::graphics{
struct platform_interface{
bool(*initialize)(void);
void(*shutdown)(void);
void(*render)(void);
struct {
surface(*create)(platform::window);

View File

@@ -51,12 +51,6 @@ shutdown()
gfx.shutdown();
}
void
render()
{
gfx.render();
}
surface
create_surface(platform::window window)
{

View File

@@ -91,12 +91,6 @@ bool initialize(graphics_platform platform);
*/
void shutdown();
/**
* @brief 渲染调用接口
* @details 调用渲染函数指针,渲染当前渲染表面
*/
void render();
/**
* @brief 创建渲染表面
* @details window 渲染表面的窗口

View File

@@ -1,11 +1,97 @@
/**
* @file FreeList.h
* @brief 带复用槽位的稀疏对象容器模板
* @brief 带槽位复用机制的稀疏对象容器模板
*
* @details
* free_list 维护已使用槽位 + 空闲链结构,支持:
* - O(1) 近似开销的新增与删除;
* - 删除后槽位复用,降低频繁分配释放成本;
* - 以索引作为外部句柄,与 id 系统协同使用。
* free_list 是一种特殊的容器,维护"已使用槽位 + 空闲链表"结构,
* 特别适合需要频繁增删元素且使用索引作为外部句柄的场景。
*
* ## 核心特性
*
* - **O(1) 增删操作**:新增和删除操作近似常数时间复杂度
* - **槽位复用**:删除后的槽位会被回收并重新利用,避免频繁内存分配
* - **索引句柄**:使用 u32 索引作为外部句柄,与 id 系统完美配合
* - **内存紧凑**:底层使用连续内存,缓存友好
*
* ## 数据结构原理
*
* ```
* 初始状态:
* _array: [ 空 | 空 | 空 | 空 ]
* _next_free_index = invalid_id
* _size = 0
*
* 添加元素 A、B、C 后:
* _array: [ A | B | C | 空 ]
* _next_free_index = invalid_id
* _size = 3
*
* 删除元素 B 后(槽位 1 被标记为空闲):
* _array: [ A | ->2 | C | 空 ] // 槽位 1 存储下一个空闲索引 2
* _next_free_index = 1 // 指向第一个空闲槽位
* _size = 2
*
* 再删除元素 A 后:
* _array: [ ->1 | ->2 | C | 空 ] // 槽位 0 存储下一个空闲索引 1
* _next_free_index = 0
* _size = 1
*
* 添加新元素 D 时(复用槽位 0:
* _array: [ D | ->2 | C | 空 ]
* _next_free_index = 1 // 指向下一个空闲槽位
* _size = 2
* ```
*
* ## 空闲链表机制
*
* 删除元素时,该槽位被加入空闲链表:
* 1. 调用元素的析构函数
* 2. 将当前 `_next_free_index` 值写入该槽位(作为链表下一个节点)
* 3. 更新 `_next_free_index` 指向该槽位
*
* 添加元素时,优先从空闲链表获取槽位:
* 1. 如果 `_next_free_index` 有效,复用该槽位
* 2. 从槽位中读取下一个空闲索引,更新 `_next_free_index`
* 3. 否则,在数组末尾追加新元素
*
* ## 类型要求
*
* 由于空闲槽位需要存储 u32 索引,要求:
* - `sizeof(T) >= sizeof(u32)`(至少 4 字节)
*
* ## 使用场景
*
* - 游戏对象管理(实体 ID 系统)
* - 资源句柄管理(纹理、网格等)
* - 渲染表面管理(多窗口场景)
* - 任何需要稳定索引句柄的系统
*
* ## 注意事项
*
* - 删除后的索引可能被新元素复用,外部系统需要处理这种情况
* - 不保证元素在内存中的顺序
* - 与 std::vector 配合使用时可能触发重复构造(见 USE_STL_VECTOR 宏)
*
* @example
* @code
* utl::free_list<game_object> objects;
*
* // 添加对象
* u32 id1 = objects.add(player_data);
* u32 id2 = objects.add(enemy_data);
*
* // 访问对象
* objects[id1].update();
*
* // 删除对象(槽位被回收)
* objects.remove(id1);
*
* // 新对象可能复用 id1 的槽位
* u32 id3 = objects.add(new_data); // id3 可能等于 id1
* @endcode
*
* @see utl::vector
* @see DEFINE_TYPED_ID
*/
#pragma once
#include "CommonHeader.h"
@@ -13,23 +99,42 @@
namespace XEngine::utl {
#if USE_STL_VECTOR
#pragma message("WARNING: using utl::free_list with std::vector result in duplicate calls to class constructor!");
#pragma message("WARNING: using utl::free_list with std::vector result in duplicate calls to class constructor!")
#endif
/**
* @class free_list
* @brief 带槽位复用机制的稀疏对象容器
*
* @tparam T 元素类型,必须满足 `sizeof(T) >= sizeof(u32)`
*
* @details
* 该容器使用空闲链表管理已删除的槽位,实现 O(1) 的增删操作。
* 特别适合需要稳定索引句柄的场景,如游戏对象管理、资源句柄等。
*/
template<typename T>
class free_list
{
static_assert(sizeof(T) >= sizeof(u32));
static_assert(sizeof(T) >= sizeof(u32), "Type T must be at least 4 bytes to store free list indices");
public:
/**
* @brief 构造空容器
* @brief 默认构造函数,创建空容器
*
* @details
* 创建一个空的 free_list不预分配任何内存。
* 首次添加元素时会触发内存分配。
*/
free_list() = default;
/**
* @brief 预留底层存储容量。
* @param count 预留元素数量。
* @brief 预留容量的构造函数
*
* @param count 预留的元素槽位数量
*
* @details
* 预分配指定数量的槽位,避免后续添加元素时的多次扩容。
* 适用于已知大概元素数量的场景。
*/
explicit free_list(u32 count)
{
@@ -37,7 +142,11 @@ public:
}
/**
* @brief 销毁容器并校验无存活元素。
* @brief 析构函数,销毁容器
*
* @details
* 断言检查容器为空_size == 0确保所有元素已被正确移除。
* 在 DEBUG 模式下使用 USE_STL_VECTOR 时,会清空内存以便检测悬空引用。
*/
~free_list()
{
@@ -49,10 +158,23 @@ public:
}
/**
* @brief 新增一个元素并返回槽位 ID
* @tparam params 构造参数类型。
* @param p 构造参数
* @return 新元素 ID。
* @brief 添加新元素并返回槽位 ID
*
* @tparam params 构造参数类型包
* @param p 传递给元素构造函数的参数
* @return 新分配的槽位 IDu32 索引)
*
* @details
* 添加元素的策略:
* 1. **有空闲槽位**:从空闲链表头部取出一个槽位复用
* 2. **无空闲槽位**:在数组末尾追加新元素
*
* 复用槽位时:
* - 从该槽位读取下一个空闲索引,更新 `_next_free_index`
* - 使用 placement new 在该槽位构造新元素
*
* @note 返回的 ID 在元素被删除前保持有效
* @note 删除后该 ID 可能被新元素复用
*/
template<class ... params>
constexpr u32 add(params&&... p)
@@ -76,8 +198,19 @@ public:
}
/**
* @brief 除指定 ID 的元素并回收到空闲链。
* @param id 元素 ID。
* @brief 除指定 ID 的元素并回收其槽位
*
* @param id 要移除元素的槽位 ID
*
* @details
* 移除元素的步骤:
* 1. 调用元素的析构函数
* 2. DEBUG 模式下填充 0xcc 标记已删除
* 3. 将当前 `_next_free_index` 写入该槽位(链表链接)
* 4. 更新 `_next_free_index` 指向该槽位
*
* @pre id 必须是有效的已分配槽位
* @pre 该槽位未被删除(不能重复删除)
*/
constexpr void remove(u32 id)
{
@@ -91,28 +224,35 @@ public:
}
/**
* @brief 获取当前有效元素数量
* @return 元素数量。
* @brief 获取当前有效元素数量
*
* @return 容器中有效元素的个数
*
* @note 这与 capacity() 不同size() 返回实际存储的元素数
*/
constexpr u32 size() const
{
return _size;
}
/**
* @brief 获取当前已分配槽位总数
* @return 容量值。
* @brief 获取已分配槽位总数
*
* @return 底层数组的大小(包括空闲槽位)
*
* @details
* 返回值 >= size(),因为可能存在已删除但未释放的槽位。
* 这些槽位会在后续添加元素时被复用。
*/
constexpr u32 capacity() const
{
return _array.size();
}
/**
* @brief 判断容器是否为空
* @return 空返回 true。
* @brief 检查容器是否为空
*
* @return 如果容器中没有有效元素则返回 true
*/
constexpr bool empty() const
{
@@ -120,9 +260,12 @@ public:
}
/**
* @brief ID 访问元素
* @param id 元素 ID。
* @return 元素引用。
* @brief 通过 ID 访问元素(可修改)
*
* @param id 元素的槽位 ID
* @return 元素的引用
*
* @pre id 必须有效且指向未删除的元素
*/
[[nodiscard]] constexpr T& operator[](u32 id)
{
@@ -131,9 +274,12 @@ public:
}
/**
* @brief ID 访问常量元素。
* @param id 元素 ID。
* @return 常量元素引用。
* @brief 通过 ID 访问元素(只读)
*
* @param id 元素的槽位 ID
* @return 元素的常量引用
*
* @pre id 必须有效且指向未删除的元素
*/
[[nodiscard]] constexpr const T& operator[](u32 id) const
{
@@ -142,6 +288,18 @@ public:
}
private:
/**
* @brief 检查指定槽位是否已被删除
*
* @param id 要检查的槽位 ID
* @return 如果槽位已被删除则返回 true
*
* @details
* 通过检查槽位内容是否被 0xcc 填充来判断是否已删除。
* 仅在 sizeof(T) > sizeof(u32) 时有效,否则直接返回 true。
*
* @note 仅用于断言检查,确保不会访问已删除的元素
*/
constexpr bool already_removed(u32 id) const
{
if constexpr (sizeof(T) > sizeof(u32))

View File

@@ -1,11 +1,66 @@
/**
* @file Vector.h
* @brief 自定义动态数组容器实现
* @brief 自定义动态数组容器实现
*
* @details
* 该容器提供接近 std::vector 的常用能力,并支持可选析构策略
* - 动态扩容、插入、删除与无序删除;
* - 连续内存访问与迭代器接口;
* - 在性能敏感场景下复用底层内存分配行为。
* 该容器提供 std::vector 相似的功能,同时支持可选析构策略
* 设计目标是在性能敏感场景下提供更精细的内存管理控制。
*
* ## 核心特性
*
* - **动态扩容**:自动管理内存,支持预留容量避免频繁分配
* - **连续内存**:元素在内存中连续存储,缓存友好
* - **可选析构**:通过模板参数控制是否在删除时调用元素析构函数
* - **迭代器支持**:提供 begin()/end() 支持范围 for 循环
*
* ## 与 std::vector 的区别
*
* | 特性 | utl::vector | std::vector |
* |------|-------------|-------------|
* | 析构控制 | 可选模板参数 | 始终析构 |
* | 内存分配 | realloc | allocator |
* | 无序删除 | erase_unordered | 无 |
*
* ## 析构策略
*
* 模板参数 `destruct` 控制元素析构行为:
* - `destruct = true`(默认):删除元素时调用析构函数
* - `destruct = false`:跳过析构,适用于 POD 类型或外部管理生命周期
*
* ## 扩容策略
*
* 当容量不足时,按 `(capacity + 1) * 1.5` 扩容:
* - 初始容量 0 → 1
* - 容量 4 → 6
* - 容量 10 → 15
*
* ## 无序删除优化
*
* `erase_unordered()` 通过将末尾元素移动到删除位置实现 O(1) 删除,
* 适用于元素顺序不重要的场景。
*
* @example
* @code
* // 基本使用
* utl::vector<int> nums;
* nums.push_back(1);
* nums.push_back(2);
* nums.emplace_back(3);
*
* // 遍历
* for (int& n : nums) {
* n *= 2;
* }
*
* // 无序删除(高效但不保证顺序)
* nums.erase_unordered(0); // 用末尾元素替换位置 0
*
* // POD 类型优化(跳过析构)
* utl::vector<float, false> vertices;
* vertices.resize(1000); // 不调用析构函数
* @endcode
*
* @see utl::free_list
*/
#pragma once
#include "CommonHeader.h"
@@ -13,24 +68,40 @@
namespace XEngine::utl {
/**
* @brief 自定义连续内存动态数组。
* @tparam T 元素类型。
* @tparam destruct 是否在删除时调用析构。
* @class vector
* @brief 自定义连续内存动态数组
*
* @tparam T 元素类型
* @tparam destruct 是否在删除时调用析构函数,默认为 true
*
* @details
* 提供动态数组的完整功能,包括动态扩容、插入、删除等操作。
* 通过模板参数控制析构行为,适用于不同性能需求场景。
*/
template<typename T, bool destruct = true>
class vector
{
public:
// Default constructor. Doesn~t allocate memory.
/**
* @brief 构造空容器
* @brief 默认构造函数,创建空容器
*
* @details
* 创建一个空的 vector不分配任何内存。
* 首次添加元素时会触发内存分配。
*/
vector() = default;
/**
* @brief 构造并调整到指定元素数量
* @param count 目标元素数量。
* @brief 构造并调整到指定元素数量
*
* @param count 目标元素数量
*
* @details
* 创建包含 count 个默认构造元素的容器。
* 要求类型 T 必须可默认构造。
*
* @pre T 必须满足 is_default_constructible
*/
constexpr explicit vector(u64 count)
{
@@ -38,20 +109,45 @@ public:
}
/**
* @brief 构造并填充指定数量元素
* @param count 目标元素数量。
* @param value 填充值。
* @brief 构造并填充指定数量元素
*
* @param count 目标元素数量
* @param value 用于填充的值
*
* @details
* 创建包含 count 个元素副本的容器。
* 要求类型 T 必须可拷贝构造。
*
* @pre T 必须满足 is_copy_constructible
*/
constexpr explicit vector(u64 count, const T& value)
{
resize(count, value);
}
/**
* @brief 拷贝构造函数
*
* @param o 要拷贝的源容器
*
* @details
* 创建源容器的深拷贝,包括所有元素。
* 新容器具有独立的内存空间。
*/
constexpr vector(const vector& o)
{
*this = o;
}
/**
* @brief 移动构造函数
*
* @param o 要移动的源容器
*
* @details
* 转移源容器的所有权,不进行元素拷贝。
* 移动后源容器处于空状态。
*/
constexpr vector(vector&& o)
:_capacity{o._capacity},
_size{o._size},
@@ -60,7 +156,15 @@ public:
o.reset();
}
/**
* @brief 拷贝赋值运算符
*
* @param o 要拷贝的源容器
* @return 当前容器的引用
*
* @details
* 清空当前容器,然后深拷贝源容器的所有元素。
*/
constexpr vector& operator=(const vector& o)
{
assert(this != std::addressof(o));
@@ -78,6 +182,15 @@ public:
return *this;
}
/**
* @brief 移动赋值运算符
*
* @param o 要移动的源容器
* @return 当前容器的引用
*
* @details
* 销毁当前容器的资源,然后转移源容器的所有权。
*/
constexpr vector& operator=(vector&& o)
{
assert(this != std::addressof(o));
@@ -90,11 +203,19 @@ public:
return *this;
}
/**
* @brief 析构函数,释放所有资源
*/
~vector() { destroy(); }
/**
* @brief 追加拷贝元素。
* @param value 元素值。
* @brief 在末尾追加元素的拷贝
*
* @param value 要追加的元素值
*
* @details
* 如果容量不足,会自动扩容。
* 内部调用 emplace_back 实现。
*/
constexpr void push_back(const T& value)
{
@@ -102,8 +223,12 @@ public:
}
/**
* @brief 追加右值元素。
* @param value 元素值。
* @brief 在末尾追加元素的右值引用
*
* @param value 要追加的元素值(右值)
*
* @details
* 使用移动语义追加元素,避免不必要的拷贝。
*/
constexpr void push_back(const T&& value)
{
@@ -111,17 +236,24 @@ public:
}
/**
* @brief 原位构造并追加元素
* @tparam params 构造参数类型。
* @param p 构造参数
* @return 新元素引用。
* @brief 原位构造并追加元素
*
* @tparam params 构造参数类型包
* @param p 传递给元素构造函数的参数
* @return 新构造元素的引用
*
* @details
* 在容器末尾原位构造新元素,避免额外的拷贝或移动操作。
* 如果容量不足,按 1.5 倍扩容。
*
* @note 扩容公式:`new_capacity = (old_capacity + 1) * 3 / 2`
*/
template<typename... params>
constexpr decltype(auto) emplace_back(params&&... p)
{
if (_size == _capacity)
{
reserve(((_capacity + 1) * 3) >> 1); // reserve 50% more
reserve(((_capacity + 1) * 3) >> 1);
}
assert(_size < _capacity);
@@ -131,8 +263,15 @@ public:
}
/**
* @brief 调整元素数量,新增元素默认构造
* @param new_size 新大小。
* @brief 调整元素数量,新增元素默认构造
*
* @param new_size 目标元素数量
*
* @details
* - 如果 new_size > 当前大小,在末尾默认构造新元素
* - 如果 new_size < 当前大小,删除末尾多余元素
*
* @pre T 必须满足 is_default_constructible
*/
constexpr void resize(u64 new_size)
{
@@ -161,11 +300,17 @@ public:
assert(new_size == _size);
}
/**
* @brief 调整元素数量,新增元素使用给定值填充
* @param new_size 新大小。
* @param value 填充值。
* @brief 调整元素数量,新增元素使用给定值填充
*
* @param new_size 目标元素数量
* @param value 用于填充新元素的值
*
* @details
* - 如果 new_size > 当前大小,在末尾拷贝构造新元素
* - 如果 new_size < 当前大小,删除末尾多余元素
*
* @pre T 必须满足 is_copy_constructible
*/
constexpr void resize(u64 new_size, const T& value)
{
@@ -194,17 +339,21 @@ public:
assert(new_size == _size);
}
/**
* @brief 预留最小容量
* @param new_capacity 目标容量。
* @brief 预留最小容量
*
* @param new_capacity 目标容量
*
* @details
* 如果 new_capacity > 当前容量,重新分配更大的内存块。
* 使用 realloc 实现,会自动复制原有数据。
*
* @note 此函数只会增加容量,不会减少
*/
constexpr void reserve(u64 new_capacity)
{
if (new_capacity > _capacity)
{
// NOTE: realoc() will automatically copy the data in the buffer
// if a new region of memory iss allocated.
void* new_buffer{ realloc(_data, new_capacity * sizeof(T)) };
assert(new_buffer);
if (new_buffer)
@@ -216,9 +365,16 @@ public:
}
/**
* @brief 删除指定下标元素并保持顺序
* @param index 删除下标。
* @return 指向删除位置的指针。
* @brief 删除指定下标元素并保持顺序
*
* @param index 要删除元素的下标
* @return 指向删除位置后一个元素的指针
*
* @details
* 删除元素后,将后续所有元素向前移动一位。
* 时间复杂度 O(n)。
*
* @pre index 必须小于当前元素数量
*/
constexpr T *const erase(u64 index)
{
@@ -227,9 +383,16 @@ public:
}
/**
* @brief 删除指定位置元素并保持顺序
* @param item 待删除元素指针。
* @return 指向删除位置的指针
* @brief 删除指定位置元素并保持顺序
*
* @param item 指向删除元素的指针
* @return 指向删除位置后一个元素的指针
*
* @details
* 删除元素后,将后续所有元素向前移动一位。
* 时间复杂度 O(n)。
*
* @pre item 必须指向容器内的有效元素
*/
constexpr T *const erase(T *const item)
{
@@ -247,9 +410,18 @@ public:
}
/**
* @brief 无序删除指定下标元素
* @param index 删除下标。
* @return 指向删除位置的指针。
* @brief 无序删除指定下标元素
*
* @param index 要删除元素的下标
* @return 指向删除位置的指针
*
* @details
* 用末尾元素替换被删除元素,然后减少大小。
* 时间复杂度 O(1),但不保证元素顺序。
*
* @pre index 必须小于当前元素数量
*
* @note 适用于元素顺序不重要的场景
*/
constexpr T *const erase_unordered(u64 index)
{
@@ -258,9 +430,18 @@ public:
}
/**
* @brief 无序删除指定位置元素
* @param item 待删除元素指针。
* @return 指向删除位置的指针
* @brief 无序删除指定位置元素
*
* @param item 指向删除元素的指针
* @return 指向删除位置的指针
*
* @details
* 用末尾元素替换被删除元素,然后减少大小。
* 时间复杂度 O(1),但不保证元素顺序。
*
* @pre item 必须指向容器内的有效元素
*
* @note 适用于元素顺序不重要的场景
*/
constexpr T *const erase_unordered(T *const item)
{
@@ -279,7 +460,11 @@ public:
}
/**
* @brief 清空容器中的有元素
* @brief 清空容器中的有元素
*
* @details
* 调用所有元素的析构函数(如果 destruct = true
* 然后将大小设为 0。不释放底层内存。
*/
constexpr void clear()
{
@@ -290,10 +475,14 @@ public:
_size = 0;
}
/**
* @brief 与另一个容器交换内容
* @param o 目标容器。
* @brief 与另一个容器交换内容
*
* @param o 目标容器
*
* @details
* 交换两个容器的所有权,不进行元素拷贝。
* 时间复杂度 O(1)。
*/
constexpr void swap(vector& o)
{
@@ -305,20 +494,20 @@ public:
}
}
/**
* @brief 返回底层数据指针
* @return 数据首地址。
* @brief 返回底层数据指针
*
* @return 指向第一个元素的指针
*/
[[nodiscard]] constexpr T* data()
{
return _data;
}
/**
* @brief 返回只读底层数据指针
* @return 数据首地址。
* @brief 返回只读底层数据指针
*
* @return 指向第一个元素的常量指针
*/
[[nodiscard]] constexpr T *const data() const
{
@@ -326,8 +515,9 @@ public:
}
/**
* @brief 判断容器是否为空
* @return 空返回 true。
* @brief 判断容器是否为空
*
* @return 如果容器中没有元素则返回 true
*/
[[nodiscard]] constexpr bool empty() const
{
@@ -335,87 +525,160 @@ public:
}
/**
* @brief 获取元素个数
* @return 元素数量。
* @brief 获取元素个数
*
* @return 容器中当前存储的元素数量
*/
[[nodiscard]] constexpr u64 size() const
{
return _size;
}
/**
* @brief 获取当前容量
* @return 容量值。
* @brief 获取当前容量
*
* @return 容器当前分配的存储空间可容纳的元素数量
*/
[[nodiscard]] constexpr u64 capacity() const
{
return _capacity;
}
/**
* @brief 下标访问运算符(可修改)
*
* @param index 元素下标
* @return 元素的引用
*
* @pre index 必须小于当前元素数量
*/
[[nodiscard]] constexpr T& operator [](u64 index)
{
assert(_data && index < _size);
return _data[index];
}
/**
* @brief 下标访问运算符(只读)
*
* @param index 元素下标
* @return 元素的常量引用
*
* @pre index 必须小于当前元素数量
*/
[[nodiscard]] constexpr const T& operator [](u64 index) const
{
assert(_data && index < _size);
return _data[index];
}
/**
* @brief 访问第一个元素(可修改)
*
* @return 第一个元素的引用
*
* @pre 容器必须非空
*/
[[nodiscard]] constexpr T& front()
{
assert(_data && _size);
return _data[0];
}
/**
* @brief 访问第一个元素(只读)
*
* @return 第一个元素的常量引用
*
* @pre 容器必须非空
*/
[[nodiscard]] constexpr const T& front() const
{
assert(_data && _size);
return _data[0];
}
/**
* @brief 访问最后一个元素(可修改)
*
* @return 最后一个元素的引用
*
* @pre 容器必须非空
*/
[[nodiscard]] constexpr T& back()
{
assert(_data && _size);
return _data[_size -1];
}
/**
* @brief 访问最后一个元素(只读)
*
* @return 最后一个元素的常量引用
*
* @pre 容器必须非空
*/
[[nodiscard]] constexpr const T& back() const
{
assert(_data && _size);
return _data[_size - 1];
}
/**
* @brief 返回指向首元素的迭代器
*
* @return 指向第一个元素的指针
*
* @details 支持范围 for 循环
*/
[[nodiscard]] constexpr T* begin()
{
return std::addressof(_data[0]);
}
/**
* @brief 返回指向首元素的常量迭代器
*
* @return 指向第一个元素的常量指针
*/
[[nodiscard]] constexpr const T* begin() const
{
return std::addressof(_data[0]);
}
/**
* @brief 返回指向尾后位置的迭代器
*
* @return 指向最后一个元素之后位置的指针
*
* @details 支持范围 for 循环
*/
[[nodiscard]] constexpr T* end()
{
assert(!(_data == nullptr && _size > 0));
return std::addressof(_data[_size]);
}
/**
* @brief 返回指向尾后位置的常量迭代器
*
* @return 指向最后一个元素之后位置的常量指针
*/
[[nodiscard]] constexpr const T* end() const
{
assert(!(_data == nullptr && _size > 0));
return std::addressof(_data[_size]);
}
private:
private:
/**
* @brief 从另一个容器转移所有权
*
* @param o 源容器
*
* @details
* 复制源容器的成员变量,然后将源容器重置为空状态。
*/
constexpr void move(vector& o)
{
_capacity = o._capacity;
@@ -424,6 +687,12 @@ private:
o.reset();
}
/**
* @brief 重置容器为空状态
*
* @details
* 将所有成员变量设为初始值,不释放内存。
*/
constexpr void reset()
{
_capacity = 0;
@@ -431,6 +700,16 @@ private:
_data = nullptr;
}
/**
* @brief 析构指定范围内的元素
*
* @param first 起始索引
* @param last 结束索引(不包含)
*
* @details
* 对 [first, last) 范围内的每个元素调用析构函数。
* 仅在 destruct = true 时有效。
*/
constexpr void destruct_range(u64 first, u64 last)
{
assert(destruct);
@@ -444,6 +723,12 @@ private:
}
}
/**
* @brief 销毁容器,释放所有资源
*
* @details
* 清空所有元素,释放底层内存,重置容量为 0。
*/
constexpr void destroy()
{
assert([&] {return _capacity ? _data != nullptr : _data == nullptr; }());
@@ -458,6 +743,4 @@ private:
T* _data{ nullptr };
};
}