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:
@@ -37,6 +37,7 @@
|
|||||||
<ClInclude Include="Graphics\Direct3D12\D3D12Core.h" />
|
<ClInclude Include="Graphics\Direct3D12\D3D12Core.h" />
|
||||||
<ClInclude Include="Graphics\Direct3D12\D3D12Interface.h" />
|
<ClInclude Include="Graphics\Direct3D12\D3D12Interface.h" />
|
||||||
<ClInclude Include="Graphics\Direct3D12\D3D12Resources.h" />
|
<ClInclude Include="Graphics\Direct3D12\D3D12Resources.h" />
|
||||||
|
<ClInclude Include="Graphics\Direct3D12\D3D12Surface.h" />
|
||||||
<ClInclude Include="Graphics\GraphicsPlatformInterface.h" />
|
<ClInclude Include="Graphics\GraphicsPlatformInterface.h" />
|
||||||
<ClInclude Include="Graphics\Renderer.h" />
|
<ClInclude Include="Graphics\Renderer.h" />
|
||||||
<ClInclude Include="Platform\IncludeWindowCpp.h" />
|
<ClInclude Include="Platform\IncludeWindowCpp.h" />
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
<ClCompile Include="Graphics\Direct3D12\D3D12Core.cpp" />
|
<ClCompile Include="Graphics\Direct3D12\D3D12Core.cpp" />
|
||||||
<ClCompile Include="Graphics\Direct3D12\D3D12Interface.cpp" />
|
<ClCompile Include="Graphics\Direct3D12\D3D12Interface.cpp" />
|
||||||
<ClCompile Include="Graphics\Direct3D12\D3D12Resource.cpp" />
|
<ClCompile Include="Graphics\Direct3D12\D3D12Resource.cpp" />
|
||||||
|
<ClCompile Include="Graphics\Direct3D12\D3D12Surface.cpp" />
|
||||||
<ClCompile Include="Graphics\Renderer.cpp" />
|
<ClCompile Include="Graphics\Renderer.cpp" />
|
||||||
<ClCompile Include="Platform\PlatformWin32.cpp" />
|
<ClCompile Include="Platform\PlatformWin32.cpp" />
|
||||||
<ClCompile Include="Platform\Window.cpp" />
|
<ClCompile Include="Platform\Window.cpp" />
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
<ClInclude Include="Graphics\Direct3D12\D3D12Interface.h" />
|
<ClInclude Include="Graphics\Direct3D12\D3D12Interface.h" />
|
||||||
<ClInclude Include="Graphics\GraphicsPlatformInterface.h" />
|
<ClInclude Include="Graphics\GraphicsPlatformInterface.h" />
|
||||||
<ClInclude Include="Graphics\Direct3D12\D3D12Resources.h" />
|
<ClInclude Include="Graphics\Direct3D12\D3D12Resources.h" />
|
||||||
|
<ClInclude Include="Graphics\Direct3D12\D3D12Surface.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="Components\Entity.cpp" />
|
<ClCompile Include="Components\Entity.cpp" />
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
<ClCompile Include="Graphics\Direct3D12\D3D12Core.cpp" />
|
<ClCompile Include="Graphics\Direct3D12\D3D12Core.cpp" />
|
||||||
<ClCompile Include="Graphics\Direct3D12\D3D12Interface.cpp" />
|
<ClCompile Include="Graphics\Direct3D12\D3D12Interface.cpp" />
|
||||||
<ClCompile Include="Graphics\Direct3D12\D3D12Resource.cpp" />
|
<ClCompile Include="Graphics\Direct3D12\D3D12Resource.cpp" />
|
||||||
|
<ClCompile Include="Graphics\Direct3D12\D3D12Surface.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Graphics\Direct3D12\D3D12Resources" />
|
<None Include="Graphics\Direct3D12\D3D12Resources" />
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
#include "D3D12Core.h"
|
#include "D3D12Core.h"
|
||||||
#include "D3D12CommonHeader.h"
|
|
||||||
#include "D3D12Resources.h"
|
#include "D3D12Resources.h"
|
||||||
|
#include "D3D12Surface.h"
|
||||||
|
|
||||||
using namespace Microsoft::WRL;
|
using namespace Microsoft::WRL;
|
||||||
namespace XEngine::graphics::d3d12::core {
|
namespace XEngine::graphics::d3d12::core {
|
||||||
namespace {
|
namespace {
|
||||||
|
using suface_collection = utl::free_list<d3d12_surface>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief D3D12命令管理类设计说明
|
* @brief D3D12命令管理类设计说明
|
||||||
* @details 本类采用RAII设计模式封装Direct3D 12的命令队列和命令列表,提供类型安全的GPU命令提交机制
|
* @details 本类采用RAII设计模式封装Direct3D 12的命令队列和命令列表,提供类型安全的GPU命令提交机制
|
||||||
@@ -249,6 +251,9 @@ IDXGIFactory7* dxgi_factory{ nullptr };
|
|||||||
*/
|
*/
|
||||||
d3d12_command gfx_command;
|
d3d12_command gfx_command;
|
||||||
|
|
||||||
|
suface_collection surfaces;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -531,25 +536,6 @@ shutdown()
|
|||||||
release(main_device);
|
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
|
ID3D12Device *const
|
||||||
device()
|
device()
|
||||||
{
|
{
|
||||||
@@ -583,5 +569,65 @@ set_deferred_release_flag()
|
|||||||
deferred_release_flag[current_frame_index()] = 1;
|
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
|
}// namespace XEngine::graphics::d3d12::core
|
||||||
|
|||||||
@@ -22,12 +22,6 @@ bool initialize();
|
|||||||
* @details 调用 Direct3D 12 设备的关闭函数,释放所有资源
|
* @details 调用 Direct3D 12 设备的关闭函数,释放所有资源
|
||||||
*/
|
*/
|
||||||
void shutdown();
|
void shutdown();
|
||||||
/**
|
|
||||||
* @brief 渲染 Direct3D 12 核心功能
|
|
||||||
* @details 调用 Direct3D 12 设备的渲染函数,渲染当前渲染表面
|
|
||||||
*/
|
|
||||||
void render();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 立即释放 DirectX COM 对象并将指针置空
|
* @brief 立即释放 DirectX COM 对象并将指针置空
|
||||||
@@ -104,6 +98,12 @@ DXGI_FORMAT default_render_target_format();
|
|||||||
*/
|
*/
|
||||||
void set_deferred_release_flag();
|
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
|
}// namespace XEngine::graphics::d3d12::core
|
||||||
14
Engine/Graphics/Direct3D12/D3D12Helpers.h
Normal file
14
Engine/Graphics/Direct3D12/D3D12Helpers.h
Normal 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;
|
||||||
|
}
|
||||||
@@ -7,7 +7,14 @@ void get_platform_interface(platform_interface& pi)
|
|||||||
{
|
{
|
||||||
pi.initialize = core::initialize;
|
pi.initialize = core::initialize;
|
||||||
pi.shutdown = core::shutdown;
|
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
|
}// namespace XEngine::graphics::d3d12
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
#include "D3D12Resources.h"
|
#include "D3D12Resources.h"
|
||||||
#include "D3D12Core.h"
|
#include "D3D12Core.h"
|
||||||
|
#include "D3D12Helpers.h"
|
||||||
|
|
||||||
namespace XEngine::graphics::d3d12{
|
namespace XEngine::graphics::d3d12{
|
||||||
//////////// DESCRIPTOR HEAP ////////////
|
//////////// DESCRIPTOR HEAP ////////////
|
||||||
|
|
||||||
|
|
||||||
// 该类将被多个线程并发访问:资源创建(如纹理)与资源销毁/释放可能发生在不同线程,
|
// 该类将被多个线程并发访问:资源创建(如纹理)与资源销毁/释放可能发生在不同线程,
|
||||||
// 因此需要同步机制保护内部数据结构
|
// 因此需要同步机制保护内部数据结构
|
||||||
bool
|
bool
|
||||||
@@ -134,4 +137,63 @@ descriptor_heap::free(descriptor_handle 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
|
} //XEngine::graphics::d3d12
|
||||||
@@ -417,4 +417,64 @@ private:
|
|||||||
const D3D12_DESCRIPTOR_HEAP_TYPE _type;
|
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
|
} // namespace XEngine::graphics::d3d12
|
||||||
|
|||||||
@@ -15,22 +15,28 @@ to_non_srgb(DXGI_FORMAT format)
|
|||||||
void
|
void
|
||||||
d3d12_surface::create_swap_chain(IDXGIFactory7* factory, ID3D12CommandQueue* cmd_queue, DXGI_FORMAT format)
|
d3d12_surface::create_swap_chain(IDXGIFactory7* factory, ID3D12CommandQueue* cmd_queue, DXGI_FORMAT format)
|
||||||
{
|
{
|
||||||
assert(!factory && cmd_queue);
|
assert(factory && cmd_queue);
|
||||||
|
|
||||||
// 应为可以多次调用,所以需要先释放旧的 swap chain
|
// 应为可以多次调用,所以需要先释放旧的 swap chain
|
||||||
release();
|
release();
|
||||||
|
|
||||||
|
if (SUCCEEDED(factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &_allow_tearing, sizeof(u32))) && _allow_tearing)
|
||||||
|
{
|
||||||
|
_present_flags = DXGI_PRESENT_ALLOW_TEARING;
|
||||||
|
}
|
||||||
|
|
||||||
DXGI_SWAP_CHAIN_DESC1 desc{};
|
DXGI_SWAP_CHAIN_DESC1 desc{};
|
||||||
desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; // Alpha通道模式(窗口透明度相关)
|
desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; // Alpha通道模式(窗口透明度相关)
|
||||||
desc.BufferCount = frame_buffer_count; // 后台缓冲区数量(用于双缓冲/多缓冲)
|
desc.BufferCount = buffer_count; // 后台缓冲区数量(用于双缓冲/多缓冲)
|
||||||
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 缓冲区用途(作为渲染目标输出)
|
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 缓冲区用途(作为渲染目标输出)
|
||||||
desc.Flags = 0; // 交换链标志位(无)
|
desc.Flags = _allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; // 交换链标志位(无)
|
||||||
desc.Format = to_non_srgb(format); // 像素格式(转换为非SRGB格式)
|
desc.Format = to_non_srgb(format); // 像素格式(转换为非SRGB格式)
|
||||||
desc.Height = _window.height(); // 交换链高度(与窗口高度一致)
|
desc.Height = _window.height(); // 交换链高度(与窗口高度一致)
|
||||||
desc.Width = _window.width(); // 交换链宽度(与窗口宽度一致)
|
desc.Width = _window.width(); // 交换链宽度(与窗口宽度一致)
|
||||||
desc.SampleDesc.Count = 1; // 多重采样数量(1表示禁用MSAA)
|
desc.SampleDesc.Count = 1; // 多重采样数量(1表示禁用MSAA)
|
||||||
desc.SampleDesc.Quality = 0; // 多重采样质量等级(0表示禁用)
|
desc.SampleDesc.Quality = 0; // 多重采样质量等级(0表示禁用)
|
||||||
desc.Scaling = DXGI_SCALING_STRETCH; // 缓冲区缩放模式(窗口大小变化时的处理方式)
|
desc.Scaling = DXGI_SCALING_STRETCH; // 缓冲区缩放模式(窗口大小变化时的处理方式)
|
||||||
|
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // 交换链效果(翻转并丢弃旧缓冲区)
|
||||||
desc.Stereo = false; // 立体显示模式(3D显示)
|
desc.Stereo = false; // 立体显示模式(3D显示)
|
||||||
|
|
||||||
IDXGISwapChain1* swap_chain;
|
IDXGISwapChain1* swap_chain;
|
||||||
@@ -47,7 +53,7 @@ d3d12_surface::create_swap_chain(IDXGIFactory7* factory, ID3D12CommandQueue* cmd
|
|||||||
|
|
||||||
_current_bb_index = _swap_chain->GetCurrentBackBufferIndex();
|
_current_bb_index = _swap_chain->GetCurrentBackBufferIndex();
|
||||||
|
|
||||||
for(u32 i = 0; i < frame_buffer_count; ++i)
|
for (u32 i = 0; i < buffer_count; ++i)
|
||||||
{
|
{
|
||||||
_render_target_data[i].rtv = core::rtv_heap().allocate();
|
_render_target_data[i].rtv = core::rtv_heap().allocate();
|
||||||
}
|
}
|
||||||
@@ -62,7 +68,7 @@ d3d12_surface::present() const
|
|||||||
// 设置是否使用垂直同步
|
// 设置是否使用垂直同步
|
||||||
// 0 表示不使用垂直同步,1 表示使用垂直同步
|
// 0 表示不使用垂直同步,1 表示使用垂直同步
|
||||||
// 第二个参数设置有无特殊行为,0 表示无特殊行为
|
// 第二个参数设置有无特殊行为,0 表示无特殊行为
|
||||||
DXCall(_swap_chain->Present(0, 0));
|
DXCall(_swap_chain->Present(0, _present_flags));
|
||||||
_current_bb_index = _swap_chain->GetCurrentBackBufferIndex();
|
_current_bb_index = _swap_chain->GetCurrentBackBufferIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +76,7 @@ void
|
|||||||
d3d12_surface::finalize()
|
d3d12_surface::finalize()
|
||||||
{
|
{
|
||||||
// 为每个缓冲区创建渲染目标视图
|
// 为每个缓冲区创建渲染目标视图
|
||||||
for(u32 i = 0; i < frame_buffer_count; ++i)
|
for (u32 i = 0; i < buffer_count; ++i)
|
||||||
{
|
{
|
||||||
render_target_data& data{ _render_target_data[i] };
|
render_target_data& data{ _render_target_data[i] };
|
||||||
assert(!data.resource);
|
assert(!data.resource);
|
||||||
@@ -101,14 +107,32 @@ d3d12_surface::finalize()
|
|||||||
void
|
void
|
||||||
d3d12_surface::release()
|
d3d12_surface::release()
|
||||||
{
|
{
|
||||||
for(u32 i = 0; i < frame_buffer_count; ++i)
|
for (u32 i = 0; i < buffer_count; ++i)
|
||||||
{
|
{
|
||||||
render_target_data& data{ _render_target_data[i] };
|
render_target_data& data{ _render_target_data[i] };
|
||||||
core::release(data.resource);
|
core::release(data.resource);
|
||||||
core::rtv_heap().free(data.rtv);
|
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
|
} // namespace XEngine::graphics::d3d12
|
||||||
@@ -71,6 +71,60 @@ public:
|
|||||||
*/
|
*/
|
||||||
~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 创建交换链和渲染目标视图
|
* @brief 创建交换链和渲染目标视图
|
||||||
*
|
*
|
||||||
@@ -167,6 +221,43 @@ public:
|
|||||||
constexpr const D3D12_RECT& scissor_rect() const {return _scissor_rect;}
|
constexpr const D3D12_RECT& scissor_rect() const {return _scissor_rect;}
|
||||||
|
|
||||||
private:
|
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 释放所有资源
|
* @brief 释放所有资源
|
||||||
*
|
*
|
||||||
@@ -200,11 +291,13 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
IDXGISwapChain4* _swap_chain{nullptr}; ///< DXGI 交换链对象
|
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{}; ///< 关联的平台窗口
|
platform::window _window{}; ///< 关联的平台窗口
|
||||||
mutable u32 _current_bb_index{0}; ///< 当前后台缓冲区索引
|
mutable u32 _current_bb_index{0}; ///< 当前后台缓冲区索引
|
||||||
D3D12_VIEWPORT _viewport{}; ///< 视口描述
|
D3D12_VIEWPORT _viewport{}; ///< 视口描述
|
||||||
D3D12_RECT _scissor_rect{}; ///< 裁剪矩形
|
D3D12_RECT _scissor_rect{}; ///< 裁剪矩形
|
||||||
|
u32 _allow_tearing{ 0 }; ///< 是否允许撕裂
|
||||||
|
u32 _present_flags{ 0 }; ///< 呈现标志位
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ namespace XEngine::graphics{
|
|||||||
struct platform_interface{
|
struct platform_interface{
|
||||||
bool(*initialize)(void);
|
bool(*initialize)(void);
|
||||||
void(*shutdown)(void);
|
void(*shutdown)(void);
|
||||||
void(*render)(void);
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
surface(*create)(platform::window);
|
surface(*create)(platform::window);
|
||||||
|
|||||||
@@ -51,12 +51,6 @@ shutdown()
|
|||||||
gfx.shutdown();
|
gfx.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
render()
|
|
||||||
{
|
|
||||||
gfx.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
surface
|
surface
|
||||||
create_surface(platform::window window)
|
create_surface(platform::window window)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -91,12 +91,6 @@ bool initialize(graphics_platform platform);
|
|||||||
*/
|
*/
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 渲染调用接口
|
|
||||||
* @details 调用渲染函数指针,渲染当前渲染表面
|
|
||||||
*/
|
|
||||||
void render();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建渲染表面
|
* @brief 创建渲染表面
|
||||||
* @details window 渲染表面的窗口
|
* @details window 渲染表面的窗口
|
||||||
|
|||||||
@@ -1,11 +1,97 @@
|
|||||||
/**
|
/**
|
||||||
* @file FreeList.h
|
* @file FreeList.h
|
||||||
* @brief 带复用槽位的稀疏对象容器模板。
|
* @brief 带槽位复用机制的稀疏对象容器模板
|
||||||
|
*
|
||||||
* @details
|
* @details
|
||||||
* free_list 维护“已使用槽位 + 空闲链”结构,支持:
|
* free_list 是一种特殊的容器,维护"已使用槽位 + 空闲链表"结构,
|
||||||
* - O(1) 近似开销的新增与删除;
|
* 特别适合需要频繁增删元素且使用索引作为外部句柄的场景。
|
||||||
* - 删除后槽位复用,降低频繁分配释放成本;
|
*
|
||||||
* - 以索引作为外部句柄,与 id 系统协同使用。
|
* ## 核心特性
|
||||||
|
*
|
||||||
|
* - **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
|
#pragma once
|
||||||
#include "CommonHeader.h"
|
#include "CommonHeader.h"
|
||||||
@@ -13,23 +99,42 @@
|
|||||||
namespace XEngine::utl {
|
namespace XEngine::utl {
|
||||||
|
|
||||||
#if USE_STL_VECTOR
|
#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
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class free_list
|
||||||
|
* @brief 带槽位复用机制的稀疏对象容器
|
||||||
|
*
|
||||||
|
* @tparam T 元素类型,必须满足 `sizeof(T) >= sizeof(u32)`
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 该容器使用空闲链表管理已删除的槽位,实现 O(1) 的增删操作。
|
||||||
|
* 特别适合需要稳定索引句柄的场景,如游戏对象管理、资源句柄等。
|
||||||
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class free_list
|
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:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief 构造空容器。
|
* @brief 默认构造函数,创建空容器
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 创建一个空的 free_list,不预分配任何内存。
|
||||||
|
* 首次添加元素时会触发内存分配。
|
||||||
*/
|
*/
|
||||||
free_list() = default;
|
free_list() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 预留底层存储容量。
|
* @brief 预留容量的构造函数
|
||||||
* @param count 预留元素数量。
|
*
|
||||||
|
* @param count 预留的元素槽位数量
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 预分配指定数量的槽位,避免后续添加元素时的多次扩容。
|
||||||
|
* 适用于已知大概元素数量的场景。
|
||||||
*/
|
*/
|
||||||
explicit free_list(u32 count)
|
explicit free_list(u32 count)
|
||||||
{
|
{
|
||||||
@@ -37,7 +142,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 销毁容器并校验无存活元素。
|
* @brief 析构函数,销毁容器
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 断言检查容器为空(_size == 0),确保所有元素已被正确移除。
|
||||||
|
* 在 DEBUG 模式下使用 USE_STL_VECTOR 时,会清空内存以便检测悬空引用。
|
||||||
*/
|
*/
|
||||||
~free_list()
|
~free_list()
|
||||||
{
|
{
|
||||||
@@ -49,10 +158,23 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 新增一个元素并返回槽位 ID。
|
* @brief 添加新元素并返回其槽位 ID
|
||||||
* @tparam params 构造参数类型。
|
*
|
||||||
* @param p 构造参数。
|
* @tparam params 构造参数类型包
|
||||||
* @return 新元素 ID。
|
* @param p 传递给元素构造函数的参数
|
||||||
|
* @return 新分配的槽位 ID(u32 索引)
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 添加元素的策略:
|
||||||
|
* 1. **有空闲槽位**:从空闲链表头部取出一个槽位复用
|
||||||
|
* 2. **无空闲槽位**:在数组末尾追加新元素
|
||||||
|
*
|
||||||
|
* 复用槽位时:
|
||||||
|
* - 从该槽位读取下一个空闲索引,更新 `_next_free_index`
|
||||||
|
* - 使用 placement new 在该槽位构造新元素
|
||||||
|
*
|
||||||
|
* @note 返回的 ID 在元素被删除前保持有效
|
||||||
|
* @note 删除后该 ID 可能被新元素复用
|
||||||
*/
|
*/
|
||||||
template<class ... params>
|
template<class ... params>
|
||||||
constexpr u32 add(params&&... p)
|
constexpr u32 add(params&&... p)
|
||||||
@@ -76,8 +198,19 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 删除指定 ID 的元素并回收到空闲链。
|
* @brief 移除指定 ID 的元素并回收其槽位
|
||||||
* @param id 元素 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)
|
constexpr void remove(u32 id)
|
||||||
{
|
{
|
||||||
@@ -91,28 +224,35 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取当前有效元素数量。
|
* @brief 获取当前有效元素数量
|
||||||
* @return 元素数量。
|
*
|
||||||
|
* @return 容器中有效元素的个数
|
||||||
|
*
|
||||||
|
* @note 这与 capacity() 不同,size() 返回实际存储的元素数
|
||||||
*/
|
*/
|
||||||
constexpr u32 size() const
|
constexpr u32 size() const
|
||||||
{
|
{
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取当前已分配槽位总数。
|
* @brief 获取已分配槽位总数
|
||||||
* @return 容量值。
|
*
|
||||||
|
* @return 底层数组的大小(包括空闲槽位)
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 返回值 >= size(),因为可能存在已删除但未释放的槽位。
|
||||||
|
* 这些槽位会在后续添加元素时被复用。
|
||||||
*/
|
*/
|
||||||
constexpr u32 capacity() const
|
constexpr u32 capacity() const
|
||||||
{
|
{
|
||||||
return _array.size();
|
return _array.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 判断容器是否为空。
|
* @brief 检查容器是否为空
|
||||||
* @return 空返回 true。
|
*
|
||||||
|
* @return 如果容器中没有有效元素则返回 true
|
||||||
*/
|
*/
|
||||||
constexpr bool empty() const
|
constexpr bool empty() const
|
||||||
{
|
{
|
||||||
@@ -120,9 +260,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 按 ID 访问元素。
|
* @brief 通过 ID 访问元素(可修改)
|
||||||
* @param id 元素 ID。
|
*
|
||||||
* @return 元素引用。
|
* @param id 元素的槽位 ID
|
||||||
|
* @return 元素的引用
|
||||||
|
*
|
||||||
|
* @pre id 必须有效且指向未删除的元素
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr T& operator[](u32 id)
|
[[nodiscard]] constexpr T& operator[](u32 id)
|
||||||
{
|
{
|
||||||
@@ -131,9 +274,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 按 ID 访问常量元素。
|
* @brief 通过 ID 访问元素(只读)
|
||||||
* @param id 元素 ID。
|
*
|
||||||
* @return 常量元素引用。
|
* @param id 元素的槽位 ID
|
||||||
|
* @return 元素的常量引用
|
||||||
|
*
|
||||||
|
* @pre id 必须有效且指向未删除的元素
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr const T& operator[](u32 id) const
|
[[nodiscard]] constexpr const T& operator[](u32 id) const
|
||||||
{
|
{
|
||||||
@@ -142,6 +288,18 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 检查指定槽位是否已被删除
|
||||||
|
*
|
||||||
|
* @param id 要检查的槽位 ID
|
||||||
|
* @return 如果槽位已被删除则返回 true
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 通过检查槽位内容是否被 0xcc 填充来判断是否已删除。
|
||||||
|
* 仅在 sizeof(T) > sizeof(u32) 时有效,否则直接返回 true。
|
||||||
|
*
|
||||||
|
* @note 仅用于断言检查,确保不会访问已删除的元素
|
||||||
|
*/
|
||||||
constexpr bool already_removed(u32 id) const
|
constexpr bool already_removed(u32 id) const
|
||||||
{
|
{
|
||||||
if constexpr (sizeof(T) > sizeof(u32))
|
if constexpr (sizeof(T) > sizeof(u32))
|
||||||
|
|||||||
@@ -1,11 +1,66 @@
|
|||||||
/**
|
/**
|
||||||
* @file Vector.h
|
* @file Vector.h
|
||||||
* @brief 自定义动态数组容器实现。
|
* @brief 自定义动态数组容器实现
|
||||||
|
*
|
||||||
* @details
|
* @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
|
#pragma once
|
||||||
#include "CommonHeader.h"
|
#include "CommonHeader.h"
|
||||||
@@ -13,24 +68,40 @@
|
|||||||
namespace XEngine::utl {
|
namespace XEngine::utl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 自定义连续内存动态数组。
|
* @class vector
|
||||||
* @tparam T 元素类型。
|
* @brief 自定义连续内存动态数组
|
||||||
* @tparam destruct 是否在删除时调用析构。
|
*
|
||||||
|
* @tparam T 元素类型
|
||||||
|
* @tparam destruct 是否在删除时调用析构函数,默认为 true
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 提供动态数组的完整功能,包括动态扩容、插入、删除等操作。
|
||||||
|
* 通过模板参数控制析构行为,适用于不同性能需求场景。
|
||||||
*/
|
*/
|
||||||
template<typename T, bool destruct = true>
|
template<typename T, bool destruct = true>
|
||||||
class vector
|
class vector
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Default constructor. Doesn~t allocate memory.
|
|
||||||
/**
|
/**
|
||||||
* @brief 构造空容器。
|
* @brief 默认构造函数,创建空容器
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 创建一个空的 vector,不分配任何内存。
|
||||||
|
* 首次添加元素时会触发内存分配。
|
||||||
*/
|
*/
|
||||||
vector() = default;
|
vector() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 构造并调整到指定元素数量。
|
* @brief 构造并调整到指定元素数量
|
||||||
* @param count 目标元素数量。
|
*
|
||||||
|
* @param count 目标元素数量
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 创建包含 count 个默认构造元素的容器。
|
||||||
|
* 要求类型 T 必须可默认构造。
|
||||||
|
*
|
||||||
|
* @pre T 必须满足 is_default_constructible
|
||||||
*/
|
*/
|
||||||
constexpr explicit vector(u64 count)
|
constexpr explicit vector(u64 count)
|
||||||
{
|
{
|
||||||
@@ -38,20 +109,45 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 构造并填充指定数量元素。
|
* @brief 构造并填充指定数量元素
|
||||||
* @param count 目标元素数量。
|
*
|
||||||
* @param value 填充值。
|
* @param count 目标元素数量
|
||||||
|
* @param value 用于填充的值
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 创建包含 count 个元素副本的容器。
|
||||||
|
* 要求类型 T 必须可拷贝构造。
|
||||||
|
*
|
||||||
|
* @pre T 必须满足 is_copy_constructible
|
||||||
*/
|
*/
|
||||||
constexpr explicit vector(u64 count, const T& value)
|
constexpr explicit vector(u64 count, const T& value)
|
||||||
{
|
{
|
||||||
resize(count, value);
|
resize(count, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 拷贝构造函数
|
||||||
|
*
|
||||||
|
* @param o 要拷贝的源容器
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 创建源容器的深拷贝,包括所有元素。
|
||||||
|
* 新容器具有独立的内存空间。
|
||||||
|
*/
|
||||||
constexpr vector(const vector& o)
|
constexpr vector(const vector& o)
|
||||||
{
|
{
|
||||||
*this = o;
|
*this = o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 移动构造函数
|
||||||
|
*
|
||||||
|
* @param o 要移动的源容器
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 转移源容器的所有权,不进行元素拷贝。
|
||||||
|
* 移动后源容器处于空状态。
|
||||||
|
*/
|
||||||
constexpr vector(vector&& o)
|
constexpr vector(vector&& o)
|
||||||
:_capacity{o._capacity},
|
:_capacity{o._capacity},
|
||||||
_size{o._size},
|
_size{o._size},
|
||||||
@@ -60,7 +156,15 @@ public:
|
|||||||
o.reset();
|
o.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 拷贝赋值运算符
|
||||||
|
*
|
||||||
|
* @param o 要拷贝的源容器
|
||||||
|
* @return 当前容器的引用
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 清空当前容器,然后深拷贝源容器的所有元素。
|
||||||
|
*/
|
||||||
constexpr vector& operator=(const vector& o)
|
constexpr vector& operator=(const vector& o)
|
||||||
{
|
{
|
||||||
assert(this != std::addressof(o));
|
assert(this != std::addressof(o));
|
||||||
@@ -78,6 +182,15 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 移动赋值运算符
|
||||||
|
*
|
||||||
|
* @param o 要移动的源容器
|
||||||
|
* @return 当前容器的引用
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 销毁当前容器的资源,然后转移源容器的所有权。
|
||||||
|
*/
|
||||||
constexpr vector& operator=(vector&& o)
|
constexpr vector& operator=(vector&& o)
|
||||||
{
|
{
|
||||||
assert(this != std::addressof(o));
|
assert(this != std::addressof(o));
|
||||||
@@ -90,11 +203,19 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 析构函数,释放所有资源
|
||||||
|
*/
|
||||||
~vector() { destroy(); }
|
~vector() { destroy(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 追加拷贝元素。
|
* @brief 在末尾追加元素的拷贝
|
||||||
* @param value 元素值。
|
*
|
||||||
|
* @param value 要追加的元素值
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 如果容量不足,会自动扩容。
|
||||||
|
* 内部调用 emplace_back 实现。
|
||||||
*/
|
*/
|
||||||
constexpr void push_back(const T& value)
|
constexpr void push_back(const T& value)
|
||||||
{
|
{
|
||||||
@@ -102,8 +223,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 追加右值元素。
|
* @brief 在末尾追加元素的右值引用
|
||||||
* @param value 元素值。
|
*
|
||||||
|
* @param value 要追加的元素值(右值)
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 使用移动语义追加元素,避免不必要的拷贝。
|
||||||
*/
|
*/
|
||||||
constexpr void push_back(const T&& value)
|
constexpr void push_back(const T&& value)
|
||||||
{
|
{
|
||||||
@@ -111,17 +236,24 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 原位构造并追加元素。
|
* @brief 原位构造并追加元素
|
||||||
* @tparam params 构造参数类型。
|
*
|
||||||
* @param p 构造参数。
|
* @tparam params 构造参数类型包
|
||||||
* @return 新元素引用。
|
* @param p 传递给元素构造函数的参数
|
||||||
|
* @return 新构造元素的引用
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 在容器末尾原位构造新元素,避免额外的拷贝或移动操作。
|
||||||
|
* 如果容量不足,按 1.5 倍扩容。
|
||||||
|
*
|
||||||
|
* @note 扩容公式:`new_capacity = (old_capacity + 1) * 3 / 2`
|
||||||
*/
|
*/
|
||||||
template<typename... params>
|
template<typename... params>
|
||||||
constexpr decltype(auto) emplace_back(params&&... p)
|
constexpr decltype(auto) emplace_back(params&&... p)
|
||||||
{
|
{
|
||||||
if (_size == _capacity)
|
if (_size == _capacity)
|
||||||
{
|
{
|
||||||
reserve(((_capacity + 1) * 3) >> 1); // reserve 50% more
|
reserve(((_capacity + 1) * 3) >> 1);
|
||||||
}
|
}
|
||||||
assert(_size < _capacity);
|
assert(_size < _capacity);
|
||||||
|
|
||||||
@@ -131,8 +263,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 调整元素数量,新增元素默认构造。
|
* @brief 调整元素数量,新增元素默认构造
|
||||||
* @param new_size 新大小。
|
*
|
||||||
|
* @param new_size 目标元素数量
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* - 如果 new_size > 当前大小,在末尾默认构造新元素
|
||||||
|
* - 如果 new_size < 当前大小,删除末尾多余元素
|
||||||
|
*
|
||||||
|
* @pre T 必须满足 is_default_constructible
|
||||||
*/
|
*/
|
||||||
constexpr void resize(u64 new_size)
|
constexpr void resize(u64 new_size)
|
||||||
{
|
{
|
||||||
@@ -161,11 +300,17 @@ public:
|
|||||||
assert(new_size == _size);
|
assert(new_size == _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 调整元素数量,新增元素使用给定值填充。
|
* @brief 调整元素数量,新增元素使用给定值填充
|
||||||
* @param new_size 新大小。
|
*
|
||||||
* @param value 填充值。
|
* @param new_size 目标元素数量
|
||||||
|
* @param value 用于填充新元素的值
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* - 如果 new_size > 当前大小,在末尾拷贝构造新元素
|
||||||
|
* - 如果 new_size < 当前大小,删除末尾多余元素
|
||||||
|
*
|
||||||
|
* @pre T 必须满足 is_copy_constructible
|
||||||
*/
|
*/
|
||||||
constexpr void resize(u64 new_size, const T& value)
|
constexpr void resize(u64 new_size, const T& value)
|
||||||
{
|
{
|
||||||
@@ -194,17 +339,21 @@ public:
|
|||||||
assert(new_size == _size);
|
assert(new_size == _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 预留最小容量。
|
* @brief 预留最小容量
|
||||||
* @param new_capacity 目标容量。
|
*
|
||||||
|
* @param new_capacity 目标容量
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 如果 new_capacity > 当前容量,重新分配更大的内存块。
|
||||||
|
* 使用 realloc 实现,会自动复制原有数据。
|
||||||
|
*
|
||||||
|
* @note 此函数只会增加容量,不会减少
|
||||||
*/
|
*/
|
||||||
constexpr void reserve(u64 new_capacity)
|
constexpr void reserve(u64 new_capacity)
|
||||||
{
|
{
|
||||||
if (new_capacity > _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)) };
|
void* new_buffer{ realloc(_data, new_capacity * sizeof(T)) };
|
||||||
assert(new_buffer);
|
assert(new_buffer);
|
||||||
if (new_buffer)
|
if (new_buffer)
|
||||||
@@ -216,9 +365,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 删除指定下标元素并保持顺序。
|
* @brief 删除指定下标元素并保持顺序
|
||||||
* @param index 删除下标。
|
*
|
||||||
* @return 指向删除位置的指针。
|
* @param index 要删除元素的下标
|
||||||
|
* @return 指向删除位置后一个元素的指针
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 删除元素后,将后续所有元素向前移动一位。
|
||||||
|
* 时间复杂度 O(n)。
|
||||||
|
*
|
||||||
|
* @pre index 必须小于当前元素数量
|
||||||
*/
|
*/
|
||||||
constexpr T *const erase(u64 index)
|
constexpr T *const erase(u64 index)
|
||||||
{
|
{
|
||||||
@@ -227,9 +383,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 删除指定位置元素并保持顺序。
|
* @brief 删除指定位置元素并保持顺序
|
||||||
* @param item 待删除元素指针。
|
*
|
||||||
* @return 指向删除位置的指针。
|
* @param item 指向要删除元素的指针
|
||||||
|
* @return 指向删除位置后一个元素的指针
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 删除元素后,将后续所有元素向前移动一位。
|
||||||
|
* 时间复杂度 O(n)。
|
||||||
|
*
|
||||||
|
* @pre item 必须指向容器内的有效元素
|
||||||
*/
|
*/
|
||||||
constexpr T *const erase(T *const item)
|
constexpr T *const erase(T *const item)
|
||||||
{
|
{
|
||||||
@@ -247,9 +410,18 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 无序删除指定下标元素。
|
* @brief 无序删除指定下标元素
|
||||||
* @param index 删除下标。
|
*
|
||||||
* @return 指向删除位置的指针。
|
* @param index 要删除元素的下标
|
||||||
|
* @return 指向删除位置的指针
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 用末尾元素替换被删除元素,然后减少大小。
|
||||||
|
* 时间复杂度 O(1),但不保证元素顺序。
|
||||||
|
*
|
||||||
|
* @pre index 必须小于当前元素数量
|
||||||
|
*
|
||||||
|
* @note 适用于元素顺序不重要的场景
|
||||||
*/
|
*/
|
||||||
constexpr T *const erase_unordered(u64 index)
|
constexpr T *const erase_unordered(u64 index)
|
||||||
{
|
{
|
||||||
@@ -258,9 +430,18 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 无序删除指定位置元素。
|
* @brief 无序删除指定位置元素
|
||||||
* @param item 待删除元素指针。
|
*
|
||||||
* @return 指向删除位置的指针。
|
* @param item 指向要删除元素的指针
|
||||||
|
* @return 指向删除位置的指针
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 用末尾元素替换被删除元素,然后减少大小。
|
||||||
|
* 时间复杂度 O(1),但不保证元素顺序。
|
||||||
|
*
|
||||||
|
* @pre item 必须指向容器内的有效元素
|
||||||
|
*
|
||||||
|
* @note 适用于元素顺序不重要的场景
|
||||||
*/
|
*/
|
||||||
constexpr T *const erase_unordered(T *const item)
|
constexpr T *const erase_unordered(T *const item)
|
||||||
{
|
{
|
||||||
@@ -279,7 +460,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 清空容器中的有效元素。
|
* @brief 清空容器中的所有元素
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 调用所有元素的析构函数(如果 destruct = true),
|
||||||
|
* 然后将大小设为 0。不释放底层内存。
|
||||||
*/
|
*/
|
||||||
constexpr void clear()
|
constexpr void clear()
|
||||||
{
|
{
|
||||||
@@ -290,10 +475,14 @@ public:
|
|||||||
_size = 0;
|
_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 与另一个容器交换内容。
|
* @brief 与另一个容器交换内容
|
||||||
* @param o 目标容器。
|
*
|
||||||
|
* @param o 目标容器
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 交换两个容器的所有权,不进行元素拷贝。
|
||||||
|
* 时间复杂度 O(1)。
|
||||||
*/
|
*/
|
||||||
constexpr void swap(vector& o)
|
constexpr void swap(vector& o)
|
||||||
{
|
{
|
||||||
@@ -305,20 +494,20 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 返回底层数据指针。
|
* @brief 返回底层数据指针
|
||||||
* @return 数据首地址。
|
*
|
||||||
|
* @return 指向第一个元素的指针
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr T* data()
|
[[nodiscard]] constexpr T* data()
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 返回只读底层数据指针。
|
* @brief 返回只读底层数据指针
|
||||||
* @return 数据首地址。
|
*
|
||||||
|
* @return 指向第一个元素的常量指针
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr T *const data() const
|
[[nodiscard]] constexpr T *const data() const
|
||||||
{
|
{
|
||||||
@@ -326,8 +515,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 判断容器是否为空。
|
* @brief 判断容器是否为空
|
||||||
* @return 空返回 true。
|
*
|
||||||
|
* @return 如果容器中没有元素则返回 true
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr bool empty() const
|
[[nodiscard]] constexpr bool empty() const
|
||||||
{
|
{
|
||||||
@@ -335,87 +525,160 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取元素个数。
|
* @brief 获取元素个数
|
||||||
* @return 元素数量。
|
*
|
||||||
|
* @return 容器中当前存储的元素数量
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr u64 size() const
|
[[nodiscard]] constexpr u64 size() const
|
||||||
{
|
{
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取当前容量。
|
* @brief 获取当前容量
|
||||||
* @return 容量值。
|
*
|
||||||
|
* @return 容器当前分配的存储空间可容纳的元素数量
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr u64 capacity() const
|
[[nodiscard]] constexpr u64 capacity() const
|
||||||
{
|
{
|
||||||
return _capacity;
|
return _capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 下标访问运算符(可修改)
|
||||||
|
*
|
||||||
|
* @param index 元素下标
|
||||||
|
* @return 元素的引用
|
||||||
|
*
|
||||||
|
* @pre index 必须小于当前元素数量
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr T& operator [](u64 index)
|
[[nodiscard]] constexpr T& operator [](u64 index)
|
||||||
{
|
{
|
||||||
assert(_data && index < _size);
|
assert(_data && index < _size);
|
||||||
return _data[index];
|
return _data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 下标访问运算符(只读)
|
||||||
|
*
|
||||||
|
* @param index 元素下标
|
||||||
|
* @return 元素的常量引用
|
||||||
|
*
|
||||||
|
* @pre index 必须小于当前元素数量
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr const T& operator [](u64 index) const
|
[[nodiscard]] constexpr const T& operator [](u64 index) const
|
||||||
{
|
{
|
||||||
assert(_data && index < _size);
|
assert(_data && index < _size);
|
||||||
return _data[index];
|
return _data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 访问第一个元素(可修改)
|
||||||
|
*
|
||||||
|
* @return 第一个元素的引用
|
||||||
|
*
|
||||||
|
* @pre 容器必须非空
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr T& front()
|
[[nodiscard]] constexpr T& front()
|
||||||
{
|
{
|
||||||
assert(_data && _size);
|
assert(_data && _size);
|
||||||
return _data[0];
|
return _data[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 访问第一个元素(只读)
|
||||||
|
*
|
||||||
|
* @return 第一个元素的常量引用
|
||||||
|
*
|
||||||
|
* @pre 容器必须非空
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr const T& front() const
|
[[nodiscard]] constexpr const T& front() const
|
||||||
{
|
{
|
||||||
assert(_data && _size);
|
assert(_data && _size);
|
||||||
return _data[0];
|
return _data[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 访问最后一个元素(可修改)
|
||||||
|
*
|
||||||
|
* @return 最后一个元素的引用
|
||||||
|
*
|
||||||
|
* @pre 容器必须非空
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr T& back()
|
[[nodiscard]] constexpr T& back()
|
||||||
{
|
{
|
||||||
assert(_data && _size);
|
assert(_data && _size);
|
||||||
return _data[_size -1];
|
return _data[_size -1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 访问最后一个元素(只读)
|
||||||
|
*
|
||||||
|
* @return 最后一个元素的常量引用
|
||||||
|
*
|
||||||
|
* @pre 容器必须非空
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr const T& back() const
|
[[nodiscard]] constexpr const T& back() const
|
||||||
{
|
{
|
||||||
assert(_data && _size);
|
assert(_data && _size);
|
||||||
return _data[_size - 1];
|
return _data[_size - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 返回指向首元素的迭代器
|
||||||
|
*
|
||||||
|
* @return 指向第一个元素的指针
|
||||||
|
*
|
||||||
|
* @details 支持范围 for 循环
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr T* begin()
|
[[nodiscard]] constexpr T* begin()
|
||||||
{
|
{
|
||||||
return std::addressof(_data[0]);
|
return std::addressof(_data[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 返回指向首元素的常量迭代器
|
||||||
|
*
|
||||||
|
* @return 指向第一个元素的常量指针
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr const T* begin() const
|
[[nodiscard]] constexpr const T* begin() const
|
||||||
{
|
{
|
||||||
return std::addressof(_data[0]);
|
return std::addressof(_data[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 返回指向尾后位置的迭代器
|
||||||
|
*
|
||||||
|
* @return 指向最后一个元素之后位置的指针
|
||||||
|
*
|
||||||
|
* @details 支持范围 for 循环
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr T* end()
|
[[nodiscard]] constexpr T* end()
|
||||||
{
|
{
|
||||||
assert(!(_data == nullptr && _size > 0));
|
assert(!(_data == nullptr && _size > 0));
|
||||||
return std::addressof(_data[_size]);
|
return std::addressof(_data[_size]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 返回指向尾后位置的常量迭代器
|
||||||
|
*
|
||||||
|
* @return 指向最后一个元素之后位置的常量指针
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr const T* end() const
|
[[nodiscard]] constexpr const T* end() const
|
||||||
{
|
{
|
||||||
assert(!(_data == nullptr && _size > 0));
|
assert(!(_data == nullptr && _size > 0));
|
||||||
return std::addressof(_data[_size]);
|
return std::addressof(_data[_size]);
|
||||||
}
|
}
|
||||||
private:
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 从另一个容器转移所有权
|
||||||
|
*
|
||||||
|
* @param o 源容器
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 复制源容器的成员变量,然后将源容器重置为空状态。
|
||||||
|
*/
|
||||||
constexpr void move(vector& o)
|
constexpr void move(vector& o)
|
||||||
{
|
{
|
||||||
_capacity = o._capacity;
|
_capacity = o._capacity;
|
||||||
@@ -424,6 +687,12 @@ private:
|
|||||||
o.reset();
|
o.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 重置容器为空状态
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 将所有成员变量设为初始值,不释放内存。
|
||||||
|
*/
|
||||||
constexpr void reset()
|
constexpr void reset()
|
||||||
{
|
{
|
||||||
_capacity = 0;
|
_capacity = 0;
|
||||||
@@ -431,6 +700,16 @@ private:
|
|||||||
_data = nullptr;
|
_data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 析构指定范围内的元素
|
||||||
|
*
|
||||||
|
* @param first 起始索引
|
||||||
|
* @param last 结束索引(不包含)
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 对 [first, last) 范围内的每个元素调用析构函数。
|
||||||
|
* 仅在 destruct = true 时有效。
|
||||||
|
*/
|
||||||
constexpr void destruct_range(u64 first, u64 last)
|
constexpr void destruct_range(u64 first, u64 last)
|
||||||
{
|
{
|
||||||
assert(destruct);
|
assert(destruct);
|
||||||
@@ -444,6 +723,12 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 销毁容器,释放所有资源
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* 清空所有元素,释放底层内存,重置容量为 0。
|
||||||
|
*/
|
||||||
constexpr void destroy()
|
constexpr void destroy()
|
||||||
{
|
{
|
||||||
assert([&] {return _capacity ? _data != nullptr : _data == nullptr; }());
|
assert([&] {return _capacity ? _data != nullptr : _data == nullptr; }());
|
||||||
@@ -458,6 +743,4 @@ private:
|
|||||||
T* _data{ nullptr };
|
T* _data{ nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @file TestRenderer.cpp
|
* @file TestRenderer.cpp
|
||||||
* @brief 渲染功能综合测试实现。
|
* @brief 渲染功能综合测试实现。
|
||||||
*/
|
*/
|
||||||
|
#include "Test.h"
|
||||||
#include "TestRenderer.h"
|
#include "TestRenderer.h"
|
||||||
#include "Graphics/Renderer.h"
|
#include "Graphics/Renderer.h"
|
||||||
#include "Platform/Platform.h"
|
#include "Platform/Platform.h"
|
||||||
@@ -13,9 +13,9 @@
|
|||||||
|
|
||||||
#ifdef TEST_RENDERER
|
#ifdef TEST_RENDERER
|
||||||
using namespace XEngine;
|
using namespace XEngine;
|
||||||
|
time_it timer{};
|
||||||
graphics::render_surface _surfaces[4];
|
graphics::render_surface _surfaces[4];
|
||||||
|
void destroy_render_surface(graphics::render_surface& surface);
|
||||||
LRESULT win_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
LRESULT win_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||||
{
|
{
|
||||||
bool toggle_fullscreen{ false };
|
bool toggle_fullscreen{ false };
|
||||||
@@ -26,11 +26,17 @@ LRESULT win_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
|||||||
bool all_closed{ true };
|
bool all_closed{ true };
|
||||||
for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
|
for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
|
||||||
{
|
{
|
||||||
if(!_surfaces[i].window.is_closed())
|
if(_surfaces[i].window.is_valid()){
|
||||||
|
if(_surfaces[i].window.is_closed())
|
||||||
|
{
|
||||||
|
destroy_render_surface(_surfaces[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
all_closed = false;
|
all_closed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (all_closed)
|
if (all_closed)
|
||||||
{
|
{
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
@@ -49,6 +55,12 @@ LRESULT win_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case WM_KEYDOWN:
|
||||||
|
if(wparam == VK_ESCAPE)
|
||||||
|
{
|
||||||
|
PostMessage(hwnd,WM_CLOSE,0,0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,12 +71,16 @@ void
|
|||||||
create_render_surface(graphics::render_surface& surface, const platform::window_init_info info)
|
create_render_surface(graphics::render_surface& surface, const platform::window_init_info info)
|
||||||
{
|
{
|
||||||
surface.window = platform::create_window(&info);
|
surface.window = platform::create_window(&info);
|
||||||
|
surface.surface = graphics::create_surface(surface.window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_destroy_render_surface(graphics::render_surface& surface)
|
destroy_render_surface(graphics::render_surface& surface)
|
||||||
{
|
{
|
||||||
platform::remove_window(surface.window.get_id());
|
graphics::render_surface temp{surface};
|
||||||
|
surface = {};
|
||||||
|
if(temp.surface.is_valid())graphics::remove_surface(temp.surface.get_id());
|
||||||
|
if(temp.window.is_valid())platform::remove_window(temp.window.get_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -93,8 +109,16 @@ engine_test::initialize()
|
|||||||
void
|
void
|
||||||
engine_test::run()
|
engine_test::run()
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
timer.begin();
|
||||||
graphics::render();
|
//std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
|
||||||
|
{
|
||||||
|
if(_surfaces[i].surface.is_valid())
|
||||||
|
{
|
||||||
|
_surfaces[i].surface.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timer.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -102,7 +126,7 @@ engine_test::shutdown()
|
|||||||
{
|
{
|
||||||
for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
|
for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
|
||||||
{
|
{
|
||||||
_destroy_render_surface(_surfaces[i]);
|
destroy_render_surface(_surfaces[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
graphics::shutdown();
|
graphics::shutdown();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# 变更记录:交换链与渲染表面实现
|
# 变更记录:交换链与渲染表面实现
|
||||||
|
|
||||||
**提交日期**: 2026-03-31
|
**提交日期**: 2026-03-31
|
||||||
**提交哈希**: `待定`
|
**提交哈希**: `95d8893`
|
||||||
**变更类型**: 功能新增
|
**变更类型**: 功能新增
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
404
docs/changelogs/2026-03/20260331-surface-freelist-refactor.md
Normal file
404
docs/changelogs/2026-03/20260331-surface-freelist-refactor.md
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
# 变更记录:Surface 管理重构、纹理资源与文档完善
|
||||||
|
|
||||||
|
**提交日期**: 2026-04-01
|
||||||
|
**提交哈希**: `b72fcf4`
|
||||||
|
**变更类型**: Bug修复 + 功能新增 + 文档完善
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 变更概述
|
||||||
|
|
||||||
|
本次提交修复了 surface 管理中的资源重复释放问题,新增纹理资源类,并为容器和 D3D12 资源添加了完整的文档。
|
||||||
|
|
||||||
|
## 修改文件
|
||||||
|
|
||||||
|
### 核心修复
|
||||||
|
|
||||||
|
| 文件 | 变更说明 |
|
||||||
|
|------|----------|
|
||||||
|
| `D3D12Core.cpp` | 改用 `utl::free_list<d3d12_surface>` 管理 surface |
|
||||||
|
| `D3D12Surface.h` | 添加移动语义,禁用拷贝,支持撕裂检测 |
|
||||||
|
| `D3D12Surface.cpp` | 完善交换链创建和资源释放逻辑 |
|
||||||
|
|
||||||
|
### 新增功能
|
||||||
|
|
||||||
|
| 文件 | 变更说明 |
|
||||||
|
|------|----------|
|
||||||
|
| `D3D12Helpers.h` | 新增堆属性辅助结构 `d3dx::heap_properties` |
|
||||||
|
| `D3D12Resources.h` | 新增 `d3d12_texture_init_info`、`d3d12_texture`、`d3d12_render_texture` 类 |
|
||||||
|
| `D3D12Resource.cpp` | 实现纹理资源创建和 SRV 绑定 |
|
||||||
|
|
||||||
|
### 文档完善
|
||||||
|
|
||||||
|
| 文件 | 变更说明 |
|
||||||
|
|------|----------|
|
||||||
|
| `FreeList.h` | 添加完整 Doxygen 中文注释 |
|
||||||
|
| `Vector.h` | 添加完整 Doxygen 中文注释 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bug 修复详情
|
||||||
|
|
||||||
|
### 问题:Surface 重复释放
|
||||||
|
|
||||||
|
**现象**:关闭多个窗口时,`release()` 被调用超过预期次数,导致空指针崩溃。
|
||||||
|
|
||||||
|
**根本原因**:
|
||||||
|
|
||||||
|
1. **手动调用析构函数**:
|
||||||
|
```cpp
|
||||||
|
// 错误做法
|
||||||
|
surfaces[id].~d3d12_surface(); // 对象仍在 vector 中
|
||||||
|
// vector 析构时会再次调用析构函数
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Vector 扩容浅拷贝**:
|
||||||
|
```
|
||||||
|
vector 扩容时:
|
||||||
|
1. 分配新内存
|
||||||
|
2. 移动元素(默认移动是浅拷贝)
|
||||||
|
3. 析构旧元素 → 释放资源
|
||||||
|
4. 新元素持有悬空指针 → 崩溃
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **描述符句柄未重置**:
|
||||||
|
```cpp
|
||||||
|
core::rtv_heap().free(data.rtv);
|
||||||
|
// free() 只修改局部变量,data.rtv 仍指向已释放的描述符
|
||||||
|
```
|
||||||
|
|
||||||
|
### 解决方案
|
||||||
|
|
||||||
|
#### 1. 使用 `utl::free_list` 替代 `utl::vector`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// D3D12Core.cpp
|
||||||
|
using surface_collection = utl::free_list<d3d12_surface>;
|
||||||
|
surface_collection surfaces;
|
||||||
|
|
||||||
|
// 创建 surface
|
||||||
|
surface create_surface(platform::window window)
|
||||||
|
{
|
||||||
|
surfaces.emplace_back(window);
|
||||||
|
surface_id id{ (u32)surfaces.size() - 1 };
|
||||||
|
surfaces[id].create_swap_chain(...);
|
||||||
|
return surface{id};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 surface
|
||||||
|
void remove_surface(surface_id id)
|
||||||
|
{
|
||||||
|
gfx_command.flush();
|
||||||
|
surfaces.remove(id); // free_list 的 remove 正确处理
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 实现移动语义
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class d3d12_surface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// 移动构造函数
|
||||||
|
d3d12_surface(d3d12_surface&& other) noexcept
|
||||||
|
: _swap_chain{other._swap_chain}
|
||||||
|
, _window{other._window}
|
||||||
|
// ... 转移所有资源
|
||||||
|
{
|
||||||
|
other._swap_chain = nullptr; // 源对象置空
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用拷贝
|
||||||
|
d3d12_surface(const d3d12_surface&) = delete;
|
||||||
|
d3d12_surface& operator=(const d3d12_surface&) = delete;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 重置描述符句柄
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
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);
|
||||||
|
data.rtv = {}; // 重置句柄,防止重复释放
|
||||||
|
}
|
||||||
|
core::release(_swap_chain);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FreeList 容器详解
|
||||||
|
|
||||||
|
### 数据结构原理
|
||||||
|
|
||||||
|
`utl::free_list` 使用空闲链表管理已删除的槽位:
|
||||||
|
|
||||||
|
```
|
||||||
|
初始状态:
|
||||||
|
_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 存储下一个空闲索引
|
||||||
|
_next_free_index = 1
|
||||||
|
_size = 2
|
||||||
|
|
||||||
|
添加新元素 D 时(复用槽位 0):
|
||||||
|
_array: [ D | ->2 | C | 空 ]
|
||||||
|
_next_free_index = 1
|
||||||
|
_size = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 核心优势
|
||||||
|
|
||||||
|
| 特性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **O(1) 增删** | 新增和删除操作近似常数时间 |
|
||||||
|
| **槽位复用** | 删除后的槽位被回收,避免频繁内存分配 |
|
||||||
|
| **稳定索引** | 使用 u32 索引作为外部句柄,与 ID 系统配合 |
|
||||||
|
| **内存紧凑** | 底层使用连续内存,缓存友好 |
|
||||||
|
|
||||||
|
### 与 Vector 的区别
|
||||||
|
|
||||||
|
| 特性 | free_list | vector |
|
||||||
|
|------|-----------|--------|
|
||||||
|
| 删除操作 | O(1),槽位复用 | O(n) 需要移动元素 |
|
||||||
|
| 索引稳定性 | 删除后索引可能被复用 | 删除后索引失效 |
|
||||||
|
| 内存碎片 | 无碎片,槽位复用 | 删除后内存不释放 |
|
||||||
|
| 适用场景 | 游戏对象、资源句柄 | 顺序容器、临时数据 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Vector 容器详解
|
||||||
|
|
||||||
|
### 析构策略
|
||||||
|
|
||||||
|
模板参数 `destruct` 控制元素析构行为:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 默认:删除时调用析构函数
|
||||||
|
utl::vector<std::string> strings; // destruct = true
|
||||||
|
|
||||||
|
// POD 类型优化:跳过析构
|
||||||
|
utl::vector<float, false> vertices; // destruct = false
|
||||||
|
```
|
||||||
|
|
||||||
|
### 扩容策略
|
||||||
|
|
||||||
|
```
|
||||||
|
扩容公式:new_capacity = (old_capacity + 1) * 3 / 2
|
||||||
|
|
||||||
|
初始容量 0 → 1
|
||||||
|
容量 4 → 6
|
||||||
|
容量 10 → 15
|
||||||
|
```
|
||||||
|
|
||||||
|
### 无序删除优化
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 普通删除:O(n),保持顺序
|
||||||
|
vec.erase(index);
|
||||||
|
|
||||||
|
// 无序删除:O(1),用末尾元素替换
|
||||||
|
vec.erase_unordered(index); // 适用于顺序不重要的场景
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 交换链详解
|
||||||
|
|
||||||
|
### 核心职责
|
||||||
|
|
||||||
|
交换链连接窗口与渲染管线,管理一组后台缓冲区,并通过 `Present()` 实现缓冲区翻转,将绘制内容显示到窗口。
|
||||||
|
|
||||||
|
**交换链只负责**:
|
||||||
|
- 缓冲区的分配与翻转
|
||||||
|
|
||||||
|
**开发者需显式完成**:
|
||||||
|
- 绑定渲染目标(`OMSetRenderTargets`)
|
||||||
|
- GPU 同步(Fence)
|
||||||
|
- 状态转换(资源屏障)
|
||||||
|
|
||||||
|
### 标准使用流程
|
||||||
|
|
||||||
|
```
|
||||||
|
1. 获取当前后台缓冲区(GetBuffer)
|
||||||
|
↓
|
||||||
|
2. 将其绑定为渲染目标(OMSetRenderTargets)
|
||||||
|
↓
|
||||||
|
3. 执行绘制命令(写入该缓冲区)
|
||||||
|
↓
|
||||||
|
4. 提交命令并同步(Fence)
|
||||||
|
↓
|
||||||
|
5. 调用 Present(翻转缓冲区,显示图像)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 职责边界
|
||||||
|
|
||||||
|
| 操作 | 负责方 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 缓冲区分配 | 交换链 | 创建指定数量的后台缓冲区 |
|
||||||
|
| 缓冲区翻转 | 交换链 | `Present()` 切换前后缓冲区 |
|
||||||
|
| 渲染目标绑定 | 开发者 | `OMSetRenderTargets()` 绑定 RTV |
|
||||||
|
| GPU 同步 | 开发者 | 使用 Fence 确保 GPU 完成渲染 |
|
||||||
|
| 状态转换 | 开发者 | 资源屏障管理缓冲区状态 |
|
||||||
|
| 窗口大小调整 | 开发者 | 调用 `ResizeBuffers()` 重新分配 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SRV 创建与资源创建方式
|
||||||
|
|
||||||
|
### CreateShaderResourceView 参数说明
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void CreateShaderResourceView(
|
||||||
|
ID3D12Resource *pResource,
|
||||||
|
const D3D12_SHADER_RESOURCE_VIEW_DESC *pDesc,
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE DestDescriptor
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `pResource` | 要创建 SRV 的 GPU 资源指针(必须是有效资源,不能为 nullptr) |
|
||||||
|
| `pDesc` | 视图描述符,指定格式、维度、Mip 级别范围等。为 `nullptr` 时使用资源默认属性 |
|
||||||
|
| `DestDescriptor` | SRV 描述符堆中 CPU 描述符句柄的位置 |
|
||||||
|
|
||||||
|
**关键点**:
|
||||||
|
- `pDesc` 为 `nullptr` 时,视图描述符默认使用资源本身的格式和全部子资源(空描述符初始化)
|
||||||
|
- 同一个资源可以创建多个不同的 SRV(不同格式、不同 Mip 切片)
|
||||||
|
|
||||||
|
### D3D12 资源创建函数对比
|
||||||
|
|
||||||
|
| 函数 | 堆类型 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| `CreateCommittedResource` | 隐式堆 | D3D12 自动分配堆,资源直接映射。适用于大多数常规资源 |
|
||||||
|
| `CreatePlacedResource` | 显式堆 | 资源放置在用户创建的堆的特定偏移位置。用于精确控制内存布局 |
|
||||||
|
| `CreateReservedResource` | 预留资源 | 仅预留虚拟地址,不提交物理内存。用于稀疏资源,支持流式加载 |
|
||||||
|
|
||||||
|
**选择建议**:
|
||||||
|
- **Committed**:最常用,堆由系统隐式管理
|
||||||
|
- **Placed**:需要显式堆,资源放置于堆的指定偏移
|
||||||
|
- **Reserved**:仅预留虚拟地址,用于稀疏资源,实现内存的按需提交
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 纹理资源类
|
||||||
|
|
||||||
|
### d3d12_texture_init_info 结构
|
||||||
|
|
||||||
|
纹理初始化信息结构,支持三种资源创建方式:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct d3d12_texture_init_info
|
||||||
|
{
|
||||||
|
ID3D12Heap1* heap{nullptr}; // 显式堆(Placed Resource)
|
||||||
|
ID3D12Resource* resource{nullptr}; // 已有资源(直接使用)
|
||||||
|
D3D12_SHADER_RESOURCE_VIEW_DESC* srv_desc{nullptr}; // SRV 描述(nullptr 使用默认)
|
||||||
|
D3D12_RESOURCE_DESC* desc{nullptr}; // 资源描述
|
||||||
|
D3D12_RESOURCE_ALLOCATION_INFO1 allocation_info{}; // 分配信息(偏移量)
|
||||||
|
D3D12_RESOURCE_STATES initial_state{}; // 初始状态
|
||||||
|
D3D12_CLEAR_VALUE clear_value{}; // 清除值(RTV/DSV)
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### d3d12_texture 类
|
||||||
|
|
||||||
|
基础纹理类,封装资源创建和 SRV 绑定:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class d3d12_texture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr static u32 max_mips{ 14 };
|
||||||
|
|
||||||
|
explicit d3d12_texture(d3d12_texture_init_info info);
|
||||||
|
|
||||||
|
// 移动语义
|
||||||
|
d3d12_texture(d3d12_texture&& o);
|
||||||
|
d3d12_texture& operator=(d3d12_texture&& o);
|
||||||
|
|
||||||
|
// 禁用拷贝
|
||||||
|
DISABLE_COPY(d3d12_texture);
|
||||||
|
|
||||||
|
void release();
|
||||||
|
ID3D12Resource* resource() const;
|
||||||
|
descriptor_handle srv() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ID3D12Resource* _resource{nullptr};
|
||||||
|
descriptor_handle _srv;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 资源创建逻辑
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
d3d12_texture::d3d12_texture(d3d12_texture_init_info info)
|
||||||
|
{
|
||||||
|
// 优先级 1:使用已有资源
|
||||||
|
if (info.resource) {
|
||||||
|
_resource = info.resource;
|
||||||
|
}
|
||||||
|
// 优先级 2:Placed Resource(显式堆)
|
||||||
|
else if (info.heap && info.desc) {
|
||||||
|
device->CreatePlacedResource(
|
||||||
|
info.heap,
|
||||||
|
info.allocation_info.Offset,
|
||||||
|
info.desc, ...);
|
||||||
|
}
|
||||||
|
// 优先级 3:Committed Resource(隐式堆)
|
||||||
|
else if (info.desc) {
|
||||||
|
device->CreateCommittedResource(
|
||||||
|
&d3dx::heap_properties.default_heap,
|
||||||
|
D3D12_HEAP_FLAG_NONE,
|
||||||
|
info.desc, ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建 SRV
|
||||||
|
_srv = core::srv_heap().allocate();
|
||||||
|
device->CreateShaderResourceView(_resource, info.srv_desc, _srv.cpu);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### d3dx::heap_properties 辅助结构
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace XEngine::graphics::d3d12::d3dx
|
||||||
|
{
|
||||||
|
constexpr struct {
|
||||||
|
D3D12_HEAP_PROPERTIES default_heap{
|
||||||
|
D3D12_HEAP_TYPE_DEFAULT, // GPU 可读写,CPU 不可访问
|
||||||
|
D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
|
||||||
|
D3D12_MEMORY_POOL_UNKNOWN,
|
||||||
|
0, // 单 GPU 系统
|
||||||
|
0
|
||||||
|
};
|
||||||
|
} heap_properties;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 后续工作
|
||||||
|
|
||||||
|
- [ ] 实现深度模板视图
|
||||||
|
- [ ] 渲染第一个三角形
|
||||||
|
- [ ] 实现根签名和管线状态对象
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 相关文档
|
||||||
|
|
||||||
|
- [D3D12学习Wiki](../wiki/D3D12学习Wiki.md)
|
||||||
@@ -489,12 +489,307 @@ void allocate() {
|
|||||||
} // 自动解锁
|
} // 自动解锁
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 7.9 着色器资源视图(SRV)
|
||||||
|
|
||||||
|
#### CreateShaderResourceView 函数
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void CreateShaderResourceView(
|
||||||
|
ID3D12Resource *pResource,
|
||||||
|
const D3D12_SHADER_RESOURCE_VIEW_DESC *pDesc,
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE DestDescriptor
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `pResource` | 要创建 SRV 的 GPU 资源指针(必须是有效资源,不能为 nullptr) |
|
||||||
|
| `pDesc` | 视图描述符,指定格式、维度、Mip 级别范围等。为 `nullptr` 时使用资源默认属性 |
|
||||||
|
| `DestDescriptor` | SRV 描述符堆中 CPU 描述符句柄的位置 |
|
||||||
|
|
||||||
|
#### 空描述符初始化
|
||||||
|
|
||||||
|
当 `pDesc` 为 `nullptr` 时,视图描述符默认使用资源本身的格式和全部子资源:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 使用默认视图属性
|
||||||
|
device->CreateShaderResourceView(texture, nullptr, rtv_handle);
|
||||||
|
|
||||||
|
// 等价于显式指定完整描述
|
||||||
|
D3D12_SHADER_RESOURCE_VIEW_DESC desc{};
|
||||||
|
desc.Format = texture->GetDesc().Format;
|
||||||
|
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||||
|
desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||||
|
desc.Texture2D.MostDetailedMip = 0;
|
||||||
|
desc.Texture2D.MipLevels = texture->GetDesc().MipLevels;
|
||||||
|
device->CreateShaderResourceView(texture, &desc, rtv_handle);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 多视图支持
|
||||||
|
|
||||||
|
同一个资源可以创建多个不同的 SRV:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 原始格式视图
|
||||||
|
device->CreateShaderResourceView(texture, nullptr, srv_handle0);
|
||||||
|
|
||||||
|
// 不同格式视图(如 R32_FLOAT 作为 RGBA 视图)
|
||||||
|
D3D12_SHADER_RESOURCE_VIEW_DESC rgba_desc{};
|
||||||
|
rgba_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
// ...
|
||||||
|
device->CreateShaderResourceView(texture, &rgba_desc, srv_handle1);
|
||||||
|
|
||||||
|
// 特定 Mip 切片视图
|
||||||
|
D3D12_SHADER_RESOURCE_VIEW_DESC mip_desc{};
|
||||||
|
mip_desc.Texture2D.MostDetailedMip = 2;
|
||||||
|
mip_desc.Texture2D.MipLevels = 1;
|
||||||
|
// ...
|
||||||
|
device->CreateShaderResourceView(texture, &mip_desc, srv_handle2);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.10 资源创建方式
|
||||||
|
|
||||||
|
D3D12 提供三种资源创建函数,对应不同的堆管理策略:
|
||||||
|
|
||||||
|
#### 函数对比
|
||||||
|
|
||||||
|
| 函数 | 堆类型 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| `CreateCommittedResource` | 隐式堆 | D3D12 自动分配堆,资源直接映射。适用于大多数常规资源 |
|
||||||
|
| `CreatePlacedResource` | 显式堆 | 资源放置在用户创建的堆的特定偏移位置。用于精确控制内存布局 |
|
||||||
|
| `CreateReservedResource` | 预留资源 | 仅预留虚拟地址,不提交物理内存。用于稀疏资源,支持流式加载 |
|
||||||
|
|
||||||
|
#### CreateCommittedResource(最常用)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 系统自动管理堆,最简单的方式
|
||||||
|
D3D12_HEAP_PROPERTIES heap_props{
|
||||||
|
.Type = D3D12_HEAP_TYPE_DEFAULT
|
||||||
|
};
|
||||||
|
|
||||||
|
D3D12_RESOURCE_DESC resource_desc{
|
||||||
|
.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D,
|
||||||
|
.Width = width,
|
||||||
|
.Height = height,
|
||||||
|
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
ID3D12Resource* texture;
|
||||||
|
device->CreateCommittedResource(
|
||||||
|
&heap_props,
|
||||||
|
D3D12_HEAP_FLAG_NONE,
|
||||||
|
&resource_desc,
|
||||||
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
||||||
|
nullptr,
|
||||||
|
IID_PPV_ARGS(&texture)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CreatePlacedResource(精确控制)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 先创建堆
|
||||||
|
ID3D12Heap* heap;
|
||||||
|
D3D12_HEAP_DESC heap_desc{
|
||||||
|
.SizeInBytes = heap_size,
|
||||||
|
.Properties = { .Type = D3D12_HEAP_TYPE_DEFAULT },
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
device->CreateHeap(&heap_desc, IID_PPV_ARGS(&heap));
|
||||||
|
|
||||||
|
// 在堆的特定偏移放置资源
|
||||||
|
ID3D12Resource* texture;
|
||||||
|
device->CreatePlacedResource(
|
||||||
|
heap,
|
||||||
|
0, // 偏移量(必须满足对齐要求)
|
||||||
|
&resource_desc,
|
||||||
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
||||||
|
nullptr,
|
||||||
|
IID_PPV_ARGS(&texture)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**适用场景**:
|
||||||
|
- 资源复用(同一堆位置放置不同资源)
|
||||||
|
- 精确内存对齐
|
||||||
|
- 自定义内存管理
|
||||||
|
|
||||||
|
#### CreateReservedResource(稀疏资源)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 仅预留虚拟地址,不分配物理内存
|
||||||
|
ID3D12Resource* sparse_texture;
|
||||||
|
device->CreateReservedResource(
|
||||||
|
&resource_desc,
|
||||||
|
D3D12_RESOURCE_STATE_COMMON,
|
||||||
|
nullptr,
|
||||||
|
IID_PPV_ARGS(&sparse_texture)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 后续按需提交物理内存页面
|
||||||
|
// 使用 UpdateTileMappings 和 MakeResident
|
||||||
|
```
|
||||||
|
|
||||||
|
**适用场景**:
|
||||||
|
- 超大纹理(如地形纹理)按需加载
|
||||||
|
- 流式资源管理
|
||||||
|
- 虚拟纹理系统
|
||||||
|
|
||||||
|
#### 选择建议
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 资源创建方式选择 │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 常规纹理/缓冲区? │
|
||||||
|
│ │ │
|
||||||
|
│ ├── 是 ──► CreateCommittedResource │
|
||||||
|
│ │ (简单、自动管理) │
|
||||||
|
│ │ │
|
||||||
|
│ └── 否 ──► 需要精确内存控制? │
|
||||||
|
│ │ │
|
||||||
|
│ ├── 是 ──► CreatePlacedResource │
|
||||||
|
│ │ (资源复用、对齐控制) │
|
||||||
|
│ │ │
|
||||||
|
│ └── 否 ──► 超大资源按需加载? │
|
||||||
|
│ │ │
|
||||||
|
│ ├── 是 ──► │
|
||||||
|
│ │ CreateReservedResource│
|
||||||
|
│ │ (稀疏资源) │
|
||||||
|
│ │ │
|
||||||
|
│ └── 否 ──► 重新评估需求 │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.11 纹理资源类
|
||||||
|
|
||||||
|
#### d3d12_texture_init_info 结构
|
||||||
|
|
||||||
|
纹理初始化信息结构,统一管理三种资源创建方式的参数:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct d3d12_texture_init_info
|
||||||
|
{
|
||||||
|
ID3D12Heap1* heap{nullptr}; // 显式堆(Placed Resource)
|
||||||
|
ID3D12Resource* resource{nullptr}; // 已有资源(直接使用)
|
||||||
|
D3D12_SHADER_RESOURCE_VIEW_DESC* srv_desc{nullptr}; // SRV 描述(nullptr 使用默认)
|
||||||
|
D3D12_RESOURCE_DESC* desc{nullptr}; // 资源描述
|
||||||
|
D3D12_RESOURCE_ALLOCATION_INFO1 allocation_info{}; // 分配信息(偏移量)
|
||||||
|
D3D12_RESOURCE_STATES initial_state{}; // 初始状态
|
||||||
|
D3D12_CLEAR_VALUE clear_value{}; // 清除值(RTV/DSV)
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### d3d12_texture 类
|
||||||
|
|
||||||
|
基础纹理类,封装资源创建和 SRV 绑定:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class d3d12_texture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr static u32 max_mips{ 14 };
|
||||||
|
|
||||||
|
explicit d3d12_texture(d3d12_texture_init_info info);
|
||||||
|
|
||||||
|
// 移动语义
|
||||||
|
d3d12_texture(d3d12_texture&& o);
|
||||||
|
d3d12_texture& operator=(d3d12_texture&& o);
|
||||||
|
|
||||||
|
// 禁用拷贝
|
||||||
|
DISABLE_COPY(d3d12_texture);
|
||||||
|
|
||||||
|
void release();
|
||||||
|
ID3D12Resource* resource() const;
|
||||||
|
descriptor_handle srv() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ID3D12Resource* _resource{nullptr};
|
||||||
|
descriptor_handle _srv;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 资源创建优先级
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 纹理资源创建优先级 │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ info.resource != nullptr ? │
|
||||||
|
│ │ │
|
||||||
|
│ ├── 是 ──► 直接使用已有资源 │
|
||||||
|
│ │ (适用于外部管理的资源) │
|
||||||
|
│ │ │
|
||||||
|
│ └── 否 ──► info.heap != nullptr ? │
|
||||||
|
│ │ │
|
||||||
|
│ ├── 是 ──► CreatePlacedResource │
|
||||||
|
│ │ (显式堆,精确控制) │
|
||||||
|
│ │ │
|
||||||
|
│ └── 否 ──► CreateCommittedResource │
|
||||||
|
│ (隐式堆,最常用) │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
#### d3dx::heap_properties 辅助结构
|
||||||
|
|
||||||
|
提供常用的堆属性配置:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace XEngine::graphics::d3d12::d3dx
|
||||||
|
{
|
||||||
|
constexpr struct {
|
||||||
|
D3D12_HEAP_PROPERTIES default_heap{
|
||||||
|
D3D12_HEAP_TYPE_DEFAULT, // GPU 可读写,CPU 不可访问
|
||||||
|
D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
|
||||||
|
D3D12_MEMORY_POOL_UNKNOWN,
|
||||||
|
0, // 单 GPU 系统
|
||||||
|
0
|
||||||
|
};
|
||||||
|
} heap_properties;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 使用示例
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 创建默认纹理(Committed Resource)
|
||||||
|
D3D12_RESOURCE_DESC desc{
|
||||||
|
.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D,
|
||||||
|
.Width = 1024,
|
||||||
|
.Height = 1024,
|
||||||
|
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
d3d12_texture_init_info info{
|
||||||
|
.desc = &desc,
|
||||||
|
.initial_state = D3D12_RESOURCE_STATE_COPY_DEST,
|
||||||
|
};
|
||||||
|
|
||||||
|
d3d12_texture texture{info};
|
||||||
|
|
||||||
|
// 获取资源用于后续操作
|
||||||
|
ID3D12Resource* resource = texture.resource();
|
||||||
|
descriptor_handle srv = texture.srv();
|
||||||
|
```
|
||||||
|
|
||||||
## 8. 交换链(Swap Chain)
|
## 8. 交换链(Swap Chain)
|
||||||
|
|
||||||
### 8.1 什么是交换链?
|
### 8.1 什么是交换链?
|
||||||
|
|
||||||
交换链是 DXGI 提供的机制,用于管理前后缓冲区的交换,实现流畅的画面显示。
|
交换链是 DXGI 提供的机制,用于管理前后缓冲区的交换,实现流畅的画面显示。
|
||||||
|
|
||||||
|
**核心职责**:
|
||||||
|
|
||||||
|
- **连接窗口与渲染管线**:将渲染输出与显示器关联
|
||||||
|
- **管理后台缓冲区**:分配和维护一组用于渲染的缓冲区
|
||||||
|
- **缓冲区翻转**:通过 `Present()` 实现前后缓冲区交换,将绘制内容显示到窗口
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
│ 交换链工作原理 │
|
│ 交换链工作原理 │
|
||||||
@@ -520,7 +815,65 @@ void allocate() {
|
|||||||
└─────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
### 8.2 三重缓冲
|
### 8.2 交换链的职责边界
|
||||||
|
|
||||||
|
交换链**只负责**缓冲区的分配与翻转,以下操作需开发者显式完成:
|
||||||
|
|
||||||
|
| 操作 | 负责方 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 缓冲区分配 | 交换链 | 创建指定数量的后台缓冲区 |
|
||||||
|
| 缓冲区翻转 | 交换链 | `Present()` 切换前后缓冲区 |
|
||||||
|
| 渲染目标绑定 | 开发者 | `OMSetRenderTargets()` 绑定 RTV |
|
||||||
|
| GPU 同步 | 开发者 | 使用 Fence 确保 GPU 完成渲染 |
|
||||||
|
| 状态转换 | 开发者 | 资源屏障管理缓冲区状态 |
|
||||||
|
| 窗口大小调整 | 开发者 | 调用 `ResizeBuffers()` 重新分配 |
|
||||||
|
|
||||||
|
### 8.3 标准渲染流程
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 交换链标准使用流程 │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 1. 获取当前后台缓冲区 │
|
||||||
|
│ ┌─────────────────────────────────────────┐ │
|
||||||
|
│ │ ID3D12Resource* buffer = swap_chain-> │ │
|
||||||
|
│ │ GetBuffer(current_index); │ │
|
||||||
|
│ └─────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ 2. 创建并绑定渲染目标视图 │
|
||||||
|
│ ┌─────────────────────────────────────────┐ │
|
||||||
|
│ │ device->CreateRenderTargetView( │ │
|
||||||
|
│ │ buffer, nullptr, rtv_handle); │ │
|
||||||
|
│ │ cmd_list->OMSetRenderTargets( │ │
|
||||||
|
│ │ 1, &rtv_handle, nullptr); │ │
|
||||||
|
│ └─────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ 3. 执行绘制命令(写入后台缓冲区) │
|
||||||
|
│ ┌─────────────────────────────────────────┐ │
|
||||||
|
│ │ cmd_list->ClearRenderTargetView(...); │ │
|
||||||
|
│ │ cmd_list->DrawInstanced(...); │ │
|
||||||
|
│ └─────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ 4. 提交命令并同步 │
|
||||||
|
│ ┌─────────────────────────────────────────┐ │
|
||||||
|
│ │ cmd_queue->ExecuteCommandLists(...); │ │
|
||||||
|
│ │ // 等待 GPU 完成(Fence 同步) │ │
|
||||||
|
│ └─────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ 5. 呈现(翻转缓冲区) │
|
||||||
|
│ ┌─────────────────────────────────────────┐ │
|
||||||
|
│ │ swap_chain->Present(sync_interval, 0); │ │
|
||||||
|
│ └─────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.5 三重缓冲
|
||||||
|
|
||||||
项目使用三重缓冲(`frame_buffer_count = 3`):
|
项目使用三重缓冲(`frame_buffer_count = 3`):
|
||||||
|
|
||||||
@@ -530,7 +883,7 @@ void allocate() {
|
|||||||
| **提高并行性** | CPU 可提前录制多帧命令 |
|
| **提高并行性** | CPU 可提前录制多帧命令 |
|
||||||
| **平滑帧率** | 缓冲区平滑帧时间波动 |
|
| **平滑帧率** | 缓冲区平滑帧时间波动 |
|
||||||
|
|
||||||
### 8.3 d3d12_surface 类
|
### 8.6 d3d12_surface 类
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
class d3d12_surface
|
class d3d12_surface
|
||||||
@@ -559,7 +912,7 @@ private:
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### 8.4 交换链创建流程
|
### 8.7 交换链创建流程
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void create_swap_chain(...)
|
void create_swap_chain(...)
|
||||||
@@ -587,7 +940,7 @@ void create_swap_chain(...)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 8.5 视口与裁剪矩形
|
### 8.8 视口与裁剪矩形
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// 视口:定义光栅化区域
|
// 视口:定义光栅化区域
|
||||||
@@ -604,6 +957,75 @@ D3D12_VIEWPORT viewport{
|
|||||||
D3D12_RECT scissor_rect{0, 0, width, height};
|
D3D12_RECT scissor_rect{0, 0, width, height};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 8.9 Surface 管理与 free_list
|
||||||
|
|
||||||
|
#### 问题:Vector 扩容导致的资源重复释放
|
||||||
|
|
||||||
|
使用 `utl::vector` 管理 surface 时,扩容会触发元素移动:
|
||||||
|
|
||||||
|
```
|
||||||
|
vector 扩容流程:
|
||||||
|
1. 分配新内存块
|
||||||
|
2. 移动元素到新内存(默认移动是浅拷贝)
|
||||||
|
3. 析构旧位置的元素 → 调用 release()
|
||||||
|
4. 新位置的元素持有悬空指针 → 崩溃!
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 解决方案:使用 free_list
|
||||||
|
|
||||||
|
`utl::free_list` 是带槽位复用机制的容器:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 定义 surface 集合类型
|
||||||
|
using surface_collection = utl::free_list<d3d12_surface>;
|
||||||
|
surface_collection surfaces;
|
||||||
|
|
||||||
|
// 创建 surface
|
||||||
|
surface create_surface(platform::window window)
|
||||||
|
{
|
||||||
|
surfaces.emplace_back(window);
|
||||||
|
surface_id id{ (u32)surfaces.size() - 1 };
|
||||||
|
surfaces[id].create_swap_chain(...);
|
||||||
|
return surface{id};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 surface(槽位被回收)
|
||||||
|
void remove_surface(surface_id id)
|
||||||
|
{
|
||||||
|
gfx_command.flush();
|
||||||
|
surfaces.remove(id);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### free_list 数据结构
|
||||||
|
|
||||||
|
```
|
||||||
|
初始状态:
|
||||||
|
_array: [ 空 | 空 | 空 | 空 ]
|
||||||
|
_next_free_index = invalid_id
|
||||||
|
|
||||||
|
添加元素 A、B、C 后:
|
||||||
|
_array: [ A | B | C | 空 ]
|
||||||
|
_next_free_index = invalid_id
|
||||||
|
|
||||||
|
删除元素 B 后:
|
||||||
|
_array: [ A | ->2 | C | 空 ] // 槽位1存储下一个空闲索引
|
||||||
|
_next_free_index = 1
|
||||||
|
|
||||||
|
添加新元素 D:
|
||||||
|
_array: [ D | ->2 | C | 空 ] // 复用槽位1
|
||||||
|
_next_free_index = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
#### free_list vs vector
|
||||||
|
|
||||||
|
| 特性 | free_list | vector |
|
||||||
|
|------|-----------|--------|
|
||||||
|
| 删除复杂度 | O(1) | O(n) |
|
||||||
|
| 索引稳定性 | 删除后可复用 | 删除后失效 |
|
||||||
|
| 内存管理 | 槽位复用 | 可能扩容移动 |
|
||||||
|
| 适用场景 | 资源句柄管理 | 顺序数据存储 |
|
||||||
|
|
||||||
## 9. 渲染表面与窗口
|
## 9. 渲染表面与窗口
|
||||||
|
|
||||||
### 9.1 render_surface 结构
|
### 9.1 render_surface 结构
|
||||||
|
|||||||
Reference in New Issue
Block a user