From 95d8893182a873a567986c420a3b92c8aba3cbe6 Mon Sep 17 00:00:00 2001
From: SpecialX <47072643+wangxiner55@users.noreply.github.com>
Date: Tue, 31 Mar 2026 11:12:11 +0800
Subject: [PATCH] =?UTF-8?q?feat(d3d12):=20=E5=AE=9E=E7=8E=B0=E4=BA=A4?=
=?UTF-8?q?=E6=8D=A2=E9=93=BE=E4=B8=8E=E6=B8=B2=E6=9F=93=E8=A1=A8=E9=9D=A2?=
=?UTF-8?q?=E7=AE=A1=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 d3d12_surface 类,管理交换链和渲染目标
- 实现三重缓冲后台缓冲区管理
- 添加视口和裁剪矩形配置
- 修复 GraphicsPlatformInterface.h 循环包含问题
- 添加完整的中文 Doxygen 注释
- 更新 D3D12 学习 Wiki,添加交换链章节
---
Engine/Engine.vcxproj | 1 +
.../Graphics/Direct3D12/D3D12CommonHeader.h | 5 +-
Engine/Graphics/Direct3D12/D3D12Core.cpp | 18 +-
Engine/Graphics/Direct3D12/D3D12Core.h | 21 +-
Engine/Graphics/Direct3D12/D3D12Interface.cpp | 1 -
Engine/Graphics/Direct3D12/D3D12Resource.cpp | 4 +-
Engine/Graphics/Direct3D12/D3D12Resources.h | 6 +-
Engine/Graphics/Direct3D12/D3D12Surface.cpp | 114 +++++++++
Engine/Graphics/Direct3D12/D3D12Surface.h | 210 ++++++++++++++++
Engine/Graphics/GraphicsPlatformInterface.h | 12 +-
Engine/Graphics/Renderer.cpp | 40 +++
Engine/Graphics/Renderer.h | 63 ++++-
.../2026-03/20260331-d3d12-swap-chain.md | 231 ++++++++++++++++++
docs/wiki/D3D12学习Wiki.md | 141 ++++++++++-
14 files changed, 840 insertions(+), 27 deletions(-)
create mode 100644 Engine/Graphics/Direct3D12/D3D12Surface.cpp
create mode 100644 Engine/Graphics/Direct3D12/D3D12Surface.h
create mode 100644 docs/changelogs/2026-03/20260331-d3d12-swap-chain.md
diff --git a/Engine/Engine.vcxproj b/Engine/Engine.vcxproj
index 5853a51..2bafe54 100644
--- a/Engine/Engine.vcxproj
+++ b/Engine/Engine.vcxproj
@@ -138,6 +138,7 @@
FastCall
false
$(ProjectDir);$(ProjectDir)/Common
+ 4819
diff --git a/Engine/Graphics/Direct3D12/D3D12CommonHeader.h b/Engine/Graphics/Direct3D12/D3D12CommonHeader.h
index ea98483..fb0f4bd 100644
--- a/Engine/Graphics/Direct3D12/D3D12CommonHeader.h
+++ b/Engine/Graphics/Direct3D12/D3D12CommonHeader.h
@@ -1,7 +1,8 @@
-#pragma once
+#pragma once
#include "CommonHeader.h"
#include "Graphics\Renderer.h"
+#include "Platform\Window.h"
// 引入 DirectX Graphics Infrastructure(DXGI)6.0
// 头文件,用于访问 DXGI 接口(如 IDXGIFactory6、
@@ -23,7 +24,7 @@
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d12.lib")
-namespace XEngine::graphics::d3d12::core {
+namespace XEngine::graphics::d3d12 {
constexpr u32 frame_buffer_count{ 3 };
}
diff --git a/Engine/Graphics/Direct3D12/D3D12Core.cpp b/Engine/Graphics/Direct3D12/D3D12Core.cpp
index 7f083be..0249e53 100644
--- a/Engine/Graphics/Direct3D12/D3D12Core.cpp
+++ b/Engine/Graphics/Direct3D12/D3D12Core.cpp
@@ -1,4 +1,4 @@
-#include "D3D12Core.h"
+#include "D3D12Core.h"
#include "D3D12CommonHeader.h"
#include "D3D12Resources.h"
@@ -290,6 +290,9 @@ u32 deferred_release_flag[frame_buffer_count]{};
*/
std::mutex deferred_release_mutex{};
+// 默认渲染目标格式
+constexpr DXGI_FORMAT render_target_format{ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB };
+
// 最小支持的 Direct3D 特本级别
constexpr D3D_FEATURE_LEVEL minumum_feature_level{ D3D_FEATURE_LEVEL_11_0 };
@@ -553,12 +556,25 @@ device()
return main_device;
}
+descriptor_heap& rtv_heap() {return rtv_descriptor_heap;}
+descriptor_heap& dsv_heap() {return dsv_descriptor_heap;}
+descriptor_heap& uav_heap() {return uav_descriptor_heap;}
+descriptor_heap& srv_heap() {return srv_descriptor_heap;}
+
+
u32
current_frame_index()
{
return gfx_command.frame_index();
}
+DXGI_FORMAT
+default_render_target_format()
+{
+ return render_target_format;
+}
+
+
// X86结构上的整数访问权架构原子的,所以不需要加锁
void
diff --git a/Engine/Graphics/Direct3D12/D3D12Core.h b/Engine/Graphics/Direct3D12/D3D12Core.h
index 9f7ef53..c7b5825 100644
--- a/Engine/Graphics/Direct3D12/D3D12Core.h
+++ b/Engine/Graphics/Direct3D12/D3D12Core.h
@@ -1,10 +1,15 @@
-#pragma once
+#pragma once
#include "D3D12CommonHeader.h"
/**
* @brief Direct3D 12 核心类
* @details 定义了 Direct3D 12 核心功能的初始化与关闭函数
*/
+
+namespace XEngine::graphics::d3d12 {
+class descriptor_heap;
+}
+
namespace XEngine::graphics::d3d12::core{
/**
* @brief 初始化 Direct3D 12 核心功能
@@ -81,10 +86,24 @@ ID3D12Device *const device();
*/
u32 current_frame_index();
+descriptor_heap& rtv_heap();
+descriptor_heap& dsv_heap();
+descriptor_heap& srv_heap();
+descriptor_heap& uav_heap();
+
+/**
+ * @brief 获取默认渲染目标格式
+ * @details 返回 Direct3D 12 设备的默认渲染目标格式
+ * @return DXGI_FORMAT 默认渲染目标格式
+ */
+DXGI_FORMAT default_render_target_format();
+
/**
* @brief 设置延迟释放标志
* @details 用于在渲染循环中设置延迟释放标志,通知资源管理器在当前帧渲染完成后释放资源
*/
void set_deferred_release_flag();
+
+
}// namespace XEngine::graphics::d3d12::core
\ No newline at end of file
diff --git a/Engine/Graphics/Direct3D12/D3D12Interface.cpp b/Engine/Graphics/Direct3D12/D3D12Interface.cpp
index eaac64b..0dc2671 100644
--- a/Engine/Graphics/Direct3D12/D3D12Interface.cpp
+++ b/Engine/Graphics/Direct3D12/D3D12Interface.cpp
@@ -1,4 +1,3 @@
-#include "CommonHeader.h"
#include "D3D12Interface.h"
#include "D3D12Core.h"
#include "Graphics\GraphicsPlatformInterface.h"
diff --git a/Engine/Graphics/Direct3D12/D3D12Resource.cpp b/Engine/Graphics/Direct3D12/D3D12Resource.cpp
index 2e27bae..9b73b65 100644
--- a/Engine/Graphics/Direct3D12/D3D12Resource.cpp
+++ b/Engine/Graphics/Direct3D12/D3D12Resource.cpp
@@ -1,7 +1,7 @@
-#include "D3D12Resources.h"
+#include "D3D12Resources.h"
#include "D3D12Core.h"
-namespace XEngine::graphics::d3d12::core{
+namespace XEngine::graphics::d3d12{
//////////// DESCRIPTOR HEAP ////////////
// 该类将被多个线程并发访问:资源创建(如纹理)与资源销毁/释放可能发生在不同线程,
// 因此需要同步机制保护内部数据结构
diff --git a/Engine/Graphics/Direct3D12/D3D12Resources.h b/Engine/Graphics/Direct3D12/D3D12Resources.h
index 07280a0..533d928 100644
--- a/Engine/Graphics/Direct3D12/D3D12Resources.h
+++ b/Engine/Graphics/Direct3D12/D3D12Resources.h
@@ -1,7 +1,7 @@
-#pragma once
+#pragma once
#include "D3D12CommonHeader.h"
-namespace XEngine::graphics::d3d12::core {
+namespace XEngine::graphics::d3d12 {
/**
* @brief 前向声明,用于友元类关系
@@ -417,4 +417,4 @@ private:
const D3D12_DESCRIPTOR_HEAP_TYPE _type;
};
-} // namespace XEngine::graphics::d3d12::core
+} // namespace XEngine::graphics::d3d12
diff --git a/Engine/Graphics/Direct3D12/D3D12Surface.cpp b/Engine/Graphics/Direct3D12/D3D12Surface.cpp
new file mode 100644
index 0000000..f5a816b
--- /dev/null
+++ b/Engine/Graphics/Direct3D12/D3D12Surface.cpp
@@ -0,0 +1,114 @@
+#include "D3D12Surface.h"
+#include "D3D12Core.h"
+
+
+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;
+}
+} // anonymous namespace
+
+void
+d3d12_surface::create_swap_chain(IDXGIFactory7* factory, ID3D12CommandQueue* cmd_queue, DXGI_FORMAT format)
+{
+ assert(!factory && cmd_queue);
+
+ // 应为可以多次调用,所以需要先释放旧的 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显示)
+
+ 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);
+
+ _current_bb_index = _swap_chain->GetCurrentBackBufferIndex();
+
+ for(u32 i = 0; i < frame_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();
+}
+
+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);
+ }
+
+ 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;
+
+ _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);
+ }
+
+ core::release(_swap_chain);
+}
+
+} // namespace XEngine::graphics::d3d12
\ No newline at end of file
diff --git a/Engine/Graphics/Direct3D12/D3D12Surface.h b/Engine/Graphics/Direct3D12/D3D12Surface.h
new file mode 100644
index 0000000..0fd8131
--- /dev/null
+++ b/Engine/Graphics/Direct3D12/D3D12Surface.h
@@ -0,0 +1,210 @@
+#pragma once
+#include "D3D12CommonHeader.h"
+#include "D3D12Resources.h"
+
+namespace XEngine::graphics::d3d12 {
+
+/**
+ * @class d3d12_surface
+ * @brief 管理 D3D12 交换链和渲染目标的表面类
+ *
+ * @details
+ * 该类封装了 Direct3D 12 的交换链(Swap Chain)和后台缓冲区管理,
+ * 提供渲染表面的完整生命周期管理。
+ *
+ * ## 核心职责
+ *
+ * - **交换链管理**:创建和管理 IDXGISwapChain4,处理前后缓冲区交换
+ * - **渲染目标管理**:为每个后台缓冲区创建和维护渲染目标视图(RTV)
+ * - **视口和裁剪矩形**:管理渲染区域的视口和裁剪设置
+ * - **窗口关联**:与平台窗口绑定,处理窗口大小调整
+ *
+ * ## 多缓冲机制
+ *
+ * 使用 `frame_buffer_count`(通常为 3)个后台缓冲区实现三重缓冲:
+ * - 减少画面撕裂
+ * - 提高 CPU-GPU 并行性
+ * - 平滑帧率波动
+ *
+ * ## 生命周期
+ *
+ * @code
+ * d3d12_surface surface(window);
+ * surface.create_swap_chain(factory, cmd_queue, DXGI_FORMAT_R8G8B8A8_UNORM);
+ *
+ * // 渲染循环
+ * surface.present(); // 呈现当前帧
+ * surface.resize(); // 窗口大小改变时调用
+ *
+ * // 自动析构时释放资源
+ * @endcode
+ *
+ * @see IDXGISwapChain4
+ * @see ID3D12Resource
+ * @see descriptor_handle
+ */
+class d3d12_surface
+{
+public:
+ /**
+ * @brief 构造函数,绑定到指定窗口
+ *
+ * @param window 平台窗口对象
+ *
+ * @details
+ * 仅保存窗口引用,不创建交换链。
+ * 必须随后调用 create_swap_chain() 完成初始化。
+ *
+ * @pre window.handle() 必须返回有效的窗口句柄
+ */
+ explicit d3d12_surface(platform::window window)
+ :_window(window)
+ {
+ assert(window.handle());
+ }
+
+ /**
+ * @brief 析构函数,自动释放所有资源
+ *
+ * @details
+ * 调用 release() 清理交换链和渲染目标资源。
+ */
+ ~d3d12_surface(){release();}
+
+ /**
+ * @brief 创建交换链和渲染目标视图
+ *
+ * @param factory DXGI 工厂对象,用于创建交换链
+ * @param cmd_queue 命令队列,交换链将与此队列同步
+ * @param format 后台缓冲区的像素格式(如 DXGI_FORMAT_R8G8B8A8_UNORM)
+ *
+ * @details
+ * 执行以下操作:
+ * 1. 创建 IDXGISwapChain4 对象
+ * 2. 获取所有后台缓冲区资源
+ * 3. 为每个后台缓冲区创建 RTV
+ * 4. 初始化视口和裁剪矩形
+ *
+ * @pre factory 和 cmd_queue 必须有效
+ * @pre 尚未创建交换链(或已调用 release())
+ */
+ void create_swap_chain(IDXGIFactory7* factory, ID3D12CommandQueue* cmd_queue, DXGI_FORMAT format);
+
+ /**
+ * @brief 呈现当前渲染帧
+ *
+ * @details
+ * 调用 IDXGISwapChain::Present() 将当前后台缓冲区内容显示到屏幕。
+ * 呈现后,后台缓冲区索引自动递增(轮转)。
+ *
+ * @note 使用 DXGI_SWAP_EFFECT_FLIP_DISCARD 模式时,
+ * 呈现后当前后台缓冲区内容不再有效
+ */
+ void present() const;
+
+ /**
+ * @brief 调整交换链大小以匹配窗口
+ *
+ * @details
+ * 当窗口大小改变时调用此方法:
+ * 1. 释放旧的渲染目标资源
+ * 2. 调整交换链缓冲区大小
+ * 3. 重新创建渲染目标视图
+ * 4. 更新视口和裁剪矩形
+ *
+ * @note 必须在 GPU 完成使用当前资源后调用
+ */
+ void resize();
+
+ /**
+ * @brief 获取渲染表面宽度
+ * @return 宽度(像素)
+ */
+ u32 width();
+
+ /**
+ * @brief 获取渲染表面高度
+ * @return 高度(像素)
+ */
+ u32 height();
+
+ /**
+ * @brief 获取当前后台缓冲区资源
+ * @return 指向 ID3D12Resource 的指针
+ *
+ * @details
+ * 返回当前帧应渲染到的后台缓冲区。
+ * 索引由 _current_bb_index 指定,每次 present() 后更新。
+ */
+ constexpr ID3D12Resource *const back_buffer() const {return _render_target_data[_current_bb_index].resource;}
+
+ /**
+ * @brief 获取当前后台缓冲区的渲染目标视图句柄
+ * @return RTV 描述符句柄
+ *
+ * @details
+ * 用于 OMSetRenderTargets() 绑定渲染目标。
+ */
+ constexpr descriptor_handle rtv() const {return _render_target_data[_current_bb_index].rtv;}
+
+ /**
+ * @brief 获取视口描述
+ * @return D3D12_VIEWPORT 常量引用
+ *
+ * @details
+ * 用于 RSSetViewports() 设置光栅化视口。
+ */
+ constexpr const D3D12_VIEWPORT& viewport() const {return _viewport;}
+
+ /**
+ * @brief 获取裁剪矩形描述
+ * @return D3D12_RECT 常量引用
+ *
+ * @details
+ * 用于 RSSetScissorRects() 设置裁剪区域。
+ * 裁剪区域外的像素将被丢弃。
+ */
+ constexpr const D3D12_RECT& scissor_rect() const {return _scissor_rect;}
+
+private:
+ /**
+ * @brief 释放所有资源
+ *
+ * @details
+ * 释放交换链和所有后台缓冲区资源。
+ * 使用延迟释放机制确保 GPU 安全。
+ */
+ void release();
+
+ /**
+ * @brief 完成资源创建的最终设置
+ *
+ * @details
+ * 在创建或调整大小后调用,设置视口和裁剪矩形。
+ */
+ void finalize();
+
+ /**
+ * @struct render_target_data
+ * @brief 单个后台缓冲区的渲染目标数据
+ *
+ * @details
+ * 每个后台缓冲区对应一个此结构体,存储:
+ * - 资源指针:实际的缓冲区纹理资源
+ * - RTV 句柄:用于绑定到渲染管线
+ */
+ struct render_target_data
+ {
+ ID3D12Resource* resource{nullptr}; ///< 后台缓冲区资源
+ descriptor_handle rtv{}; ///< 渲染目标视图描述符句柄
+ };
+
+ IDXGISwapChain4* _swap_chain{nullptr}; ///< DXGI 交换链对象
+ render_target_data _render_target_data[frame_buffer_count];///< 后台缓冲区数据数组
+ platform::window _window{}; ///< 关联的平台窗口
+ mutable u32 _current_bb_index{0}; ///< 当前后台缓冲区索引
+ D3D12_VIEWPORT _viewport{}; ///< 视口描述
+ D3D12_RECT _scissor_rect{}; ///< 裁剪矩形
+};
+
+}
diff --git a/Engine/Graphics/GraphicsPlatformInterface.h b/Engine/Graphics/GraphicsPlatformInterface.h
index 148648f..a293de7 100644
--- a/Engine/Graphics/GraphicsPlatformInterface.h
+++ b/Engine/Graphics/GraphicsPlatformInterface.h
@@ -1,5 +1,6 @@
-#pragma once
+#pragma once
#include "CommonHeader.h"
+#include "Platform/Window.h"
#include "Renderer.h"
namespace XEngine::graphics{
@@ -11,6 +12,15 @@ struct platform_interface{
bool(*initialize)(void);
void(*shutdown)(void);
void(*render)(void);
+
+ struct {
+ surface(*create)(platform::window);
+ void(*remove)(surface_id);
+ void(*resize)(surface_id, u32, u32);
+ u32(*width)(surface_id);
+ u32(*height)(surface_id);
+ void(*render)(surface_id);
+ } surface;
};
}// namespace XEngine::graphics
diff --git a/Engine/Graphics/Renderer.cpp b/Engine/Graphics/Renderer.cpp
index 097cdfb..634c53e 100644
--- a/Engine/Graphics/Renderer.cpp
+++ b/Engine/Graphics/Renderer.cpp
@@ -57,5 +57,45 @@ render()
gfx.render();
}
+surface
+create_surface(platform::window window)
+{
+ return gfx.surface.create(window);
+}
+
+void
+remove_surface(surface_id id)
+{
+ assert(id::is_valid(id));
+ gfx.surface.remove(id);
+}
+
+void
+surface::resize(u32 width, u32 height) const
+{
+ assert(is_valid());
+ gfx.surface.resize(_id, width, height);
+}
+
+u32
+surface::width() const
+{
+ assert(is_valid());
+ return gfx.surface.width(_id);
+}
+
+u32
+surface::height() const
+{
+ assert(is_valid());
+ return gfx.surface.height(_id);
+}
+
+void
+surface::render() const
+{
+ assert(is_valid());
+ gfx.surface.render(_id);
+}
}// namespace XEngine::graphics
diff --git a/Engine/Graphics/Renderer.h b/Engine/Graphics/Renderer.h
index aa7c6a6..c977901 100644
--- a/Engine/Graphics/Renderer.h
+++ b/Engine/Graphics/Renderer.h
@@ -1,16 +1,60 @@
-#pragma once
-#include "CommonHeader.h"
-#include "..\Platform\Window.h"
+#pragma once
+#include "Platform/Window.h"
namespace XEngine::graphics {
+DEFINE_TYPED_ID(surface_id);
+
/**
* @brief 表面类
* @details 定义了渲染表面的属性与操作
*/
class surface
{
+public:
+ /**
+ * @brief 由表面 ID 构造句柄。
+ * @param id 表面 ID。
+ */
+ constexpr explicit surface(surface_id id) : _id{ id } {}
+ /**
+ * @brief 构造无效表面句柄。
+ */
+ constexpr surface() = default;
+ /**
+ * @brief 获取表面 ID。
+ * @return 表面 ID。
+ */
+ constexpr surface_id get_id() const { return _id; }
+ /**
+ * @brief 判断表面句柄是否有效。
+ * @return 有效返回 true。
+ */
+ constexpr bool is_valid() const { return id::is_valid(_id); }
+
+
+ /**
+ * @brief 调整表面大小。
+ * @param width 目标宽度。
+ * @param height 目标高度。
+ */
+ void resize(u32 width, u32 height) const;
+ /**
+ * @brief 获取表面宽度。
+ * @return 宽度像素值。
+ */
+ u32 width() const;
+ /**
+ * @brief 获取表面高度。
+ * @return 高度像素值。
+ */
+ u32 height() const;
+
+ void render() const;
+
+private:
+ surface_id _id{ id::invalid_id };
};
/**
@@ -53,4 +97,17 @@ void shutdown();
*/
void render();
+/**
+ * @brief 创建渲染表面
+ * @details window 渲染表面的窗口
+ * @return 渲染表面对象柄
+ */
+surface create_surface(platform::window window);
+
+/**
+ * @brief 移除渲染表面
+ * @details id 渲染表面的 ID
+ */
+void remove_surface(surface_id id);
+
}
\ No newline at end of file
diff --git a/docs/changelogs/2026-03/20260331-d3d12-swap-chain.md b/docs/changelogs/2026-03/20260331-d3d12-swap-chain.md
new file mode 100644
index 0000000..bf8d989
--- /dev/null
+++ b/docs/changelogs/2026-03/20260331-d3d12-swap-chain.md
@@ -0,0 +1,231 @@
+# 变更记录:交换链与渲染表面实现
+
+**提交日期**: 2026-03-31
+**提交哈希**: `待定`
+**变更类型**: 功能新增
+
+---
+
+## 变更概述
+
+本次提交实现了 D3D12 交换链(Swap Chain)和渲染表面管理,完成了渲染输出的基础架构。同时修复了循环包含问题,确保编译正确。
+
+## 修改文件
+
+### 新增文件
+
+| 文件 | 说明 |
+|------|------|
+| `D3D12Surface.h` | 交换链和渲染目标管理类头文件 |
+| `D3D12Surface.cpp` | 交换链实现 |
+
+### 修改文件
+
+| 文件 | 变更说明 |
+|------|----------|
+| `GraphicsPlatformInterface.h` | 修复循环包含,添加 `surface_id` 定义 |
+| `Renderer.h` | 移除循环包含,保持类型定义 |
+| `Renderer.cpp` | 实现表面相关函数 |
+| `D3D12Core.h/cpp` | 添加描述符堆访问函数 |
+| `D3D12Interface.cpp` | 更新平台接口 |
+| `D3D12CommonHeader.h` | 编码修复 |
+
+---
+
+## 技术要点
+
+### 1. 交换链(Swap Chain)原理
+
+#### 什么是交换链?
+
+交换链是 DXGI 提供的机制,用于管理前后缓冲区的交换:
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ 交换链工作原理 │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
+│ │ 后台缓冲 │ │ 后台缓冲 │ │ 后台缓冲 │ │
+│ │ 0 │ │ 1 │ │ 2 │ │
+│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
+│ │ │ │ │
+│ └─────────────┼─────────────┘ │
+│ │ │
+│ ▼ │
+│ ┌──────────────┐ │
+│ │ Present() │ │
+│ └──────┬───────┘ │
+│ │ │
+│ ▼ │
+│ ┌──────────────┐ │
+│ │ 前台缓冲 │ ───► 显示器 │
+│ └──────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────┘
+```
+
+#### 三重缓冲优势
+
+| 特性 | 说明 |
+|------|------|
+| **减少撕裂** | 前台缓冲独立于后台缓冲,避免部分更新 |
+| **提高并行性** | CPU 可提前录制多帧命令 |
+| **平滑帧率** | 缓冲区平滑帧时间波动 |
+
+### 2. d3d12_surface 类设计
+
+#### 核心职责
+
+```cpp
+class d3d12_surface
+{
+ // 交换链管理
+ IDXGISwapChain4* _swap_chain;
+
+ // 渲染目标管理(每个后台缓冲区一个)
+ render_target_data _render_target_data[frame_buffer_count];
+
+ // 视口和裁剪
+ D3D12_VIEWPORT _viewport;
+ D3D12_RECT _scissor_rect;
+};
+```
+
+#### 生命周期
+
+```
+构造 d3d12_surface(window)
+ │
+ ▼
+create_swap_chain(factory, cmd_queue, format)
+ │
+ ├─► 创建 IDXGISwapChain4
+ ├─► 分配 RTV 描述符
+ └─► 初始化视口/裁剪矩形
+ │
+ ▼
+ 渲染循环
+ │
+ ├─► back_buffer() 获取当前缓冲区
+ ├─► rtv() 获取渲染目标视图
+ └─► present() 呈现帧
+ │
+ ▼
+析构 ~d3d12_surface()
+ │
+ └─► release() 释放资源
+```
+
+### 3. 交换链创建流程
+
+```cpp
+void d3d12_surface::create_swap_chain(...)
+{
+ // 1. 配置交换链描述
+ DXGI_SWAP_CHAIN_DESC1 desc{};
+ desc.BufferCount = frame_buffer_count; // 3 个后台缓冲区
+ desc.Format = to_non_srgb(format); // 像素格式
+ desc.Width = _window.width();
+ desc.Height = _window.height();
+ desc.SampleDesc.Count = 1; // 禁用 MSAA
+
+ // 2. 创建交换链
+ factory->CreateSwapChainForHwnd(cmd_queue, hwnd, &desc, ...);
+
+ // 3. 禁用 Alt+Enter 全屏切换(由应用处理)
+ factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
+
+ // 4. 分配 RTV 描述符
+ for(u32 i = 0; i < frame_buffer_count; ++i)
+ {
+ _render_target_data[i].rtv = core::rtv_heap().allocate();
+ }
+
+ // 5. 创建渲染目标视图
+ finalize();
+}
+```
+
+### 4. 渲染目标视图创建
+
+```cpp
+void d3d12_surface::finalize()
+{
+ for(u32 i = 0; i < frame_buffer_count; ++i)
+ {
+ // 获取后台缓冲区资源
+ _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);
+ }
+
+ // 设置视口和裁剪矩形
+ _viewport = {0, 0, (float)width, (float)height, 0.0f, 1.0f};
+ _scissor_rect = {0, 0, (s32)width, (s32)height};
+}
+```
+
+### 5. 循环包含修复
+
+#### 问题
+
+```
+GraphicsPlatformInterface.h → Renderer.h → GraphicsPlatformInterface.h (循环!)
+```
+
+#### 解决方案
+
+将 `surface_id` 定义移到 `Renderer.h`,`GraphicsPlatformInterface.h` 包含 `Renderer.h`:
+
+```
+Renderer.h (定义 surface_id)
+ ↓
+GraphicsPlatformInterface.h (使用 surface_id)
+```
+
+---
+
+## 视口与裁剪矩形
+
+### 视口(Viewport)
+
+```cpp
+D3D12_VIEWPORT _viewport{
+ .TopLeftX = 0.0f,
+ .TopLeftY = 0.0f,
+ .Width = (float)width,
+ .Height = (float)height,
+ .MinDepth = 0.0f,
+ .MaxDepth = 1.0f
+};
+```
+
+定义光栅化阶段的渲染区域和深度范围。
+
+### 裁剪矩形(Scissor Rect)
+
+```cpp
+D3D12_RECT _scissor_rect{0, 0, (s32)width, (s32)height};
+```
+
+定义像素输出的裁剪区域,区域外的像素将被丢弃。
+
+---
+
+## 后续工作
+
+- [ ] 实现深度模板视图
+- [ ] 渲染第一个三角形
+- [ ] 实现根签名和管线状态对象
+
+---
+
+## 相关文档
+
+- [D3D12学习Wiki](../wiki/D3D12学习Wiki.md)
diff --git a/docs/wiki/D3D12学习Wiki.md b/docs/wiki/D3D12学习Wiki.md
index 1a34d6c..32bfac4 100644
--- a/docs/wiki/D3D12学习Wiki.md
+++ b/docs/wiki/D3D12学习Wiki.md
@@ -489,9 +489,124 @@ void allocate() {
} // 自动解锁
```
-## 8. 渲染表面与窗口
+## 8. 交换链(Swap Chain)
-### 8.1 render_surface 结构
+### 8.1 什么是交换链?
+
+交换链是 DXGI 提供的机制,用于管理前后缓冲区的交换,实现流畅的画面显示。
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ 交换链工作原理 │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
+│ │ 后台缓冲 │ │ 后台缓冲 │ │ 后台缓冲 │ │
+│ │ 0 │ │ 1 │ │ 2 │ │
+│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
+│ │ │ │ │
+│ └─────────────┼─────────────┘ │
+│ │ │
+│ ▼ │
+│ ┌──────────────┐ │
+│ │ Present() │ │
+│ └──────┬───────┘ │
+│ │ │
+│ ▼ │
+│ ┌──────────────┐ │
+│ │ 前台缓冲 │ ───► 显示器 │
+│ └──────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────┘
+```
+
+### 8.2 三重缓冲
+
+项目使用三重缓冲(`frame_buffer_count = 3`):
+
+| 特性 | 说明 |
+|------|------|
+| **减少撕裂** | 前台缓冲独立于后台缓冲,避免部分更新 |
+| **提高并行性** | CPU 可提前录制多帧命令 |
+| **平滑帧率** | 缓冲区平滑帧时间波动 |
+
+### 8.3 d3d12_surface 类
+
+```cpp
+class d3d12_surface
+{
+public:
+ // 创建交换链
+ void create_swap_chain(IDXGIFactory7* factory,
+ ID3D12CommandQueue* cmd_queue,
+ DXGI_FORMAT format);
+
+ // 呈现当前帧
+ void present() const;
+
+ // 调整大小
+ void resize();
+
+ // 获取当前后台缓冲区
+ ID3D12Resource* back_buffer() const;
+ descriptor_handle rtv() const;
+
+private:
+ IDXGISwapChain4* _swap_chain;
+ render_target_data _render_target_data[frame_buffer_count];
+ D3D12_VIEWPORT _viewport;
+ D3D12_RECT _scissor_rect;
+};
+```
+
+### 8.4 交换链创建流程
+
+```cpp
+void create_swap_chain(...)
+{
+ // 1. 配置描述
+ DXGI_SWAP_CHAIN_DESC1 desc{};
+ desc.BufferCount = frame_buffer_count;
+ desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ desc.Width = window.width();
+ desc.Height = window.height();
+ desc.SampleDesc.Count = 1; // 禁用 MSAA
+
+ // 2. 创建交换链
+ factory->CreateSwapChainForHwnd(cmd_queue, hwnd, &desc, ...);
+
+ // 3. 禁用 Alt+Enter(由应用处理)
+ factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
+
+ // 4. 创建渲染目标视图
+ for(u32 i = 0; i < frame_buffer_count; ++i)
+ {
+ _swap_chain->GetBuffer(i, &resource);
+ device->CreateRenderTargetView(resource, &desc, rtv.cpu);
+ }
+}
+```
+
+### 8.5 视口与裁剪矩形
+
+```cpp
+// 视口:定义光栅化区域
+D3D12_VIEWPORT viewport{
+ .TopLeftX = 0.0f,
+ .TopLeftY = 0.0f,
+ .Width = (float)width,
+ .Height = (float)height,
+ .MinDepth = 0.0f,
+ .MaxDepth = 1.0f
+};
+
+// 裁剪矩形:定义像素输出区域
+D3D12_RECT scissor_rect{0, 0, width, height};
+```
+
+## 9. 渲染表面与窗口
+
+### 9.1 render_surface 结构
```cpp
struct render_surface {
@@ -500,7 +615,7 @@ struct render_surface {
};
```
-### 8.2 多窗口支持
+### 9.2 多窗口支持
TestRenderer 测试展示了多窗口渲染:
@@ -513,7 +628,7 @@ for (u32 i{0}; i < _countof(_surfaces); ++i) {
}
```
-### 8.3 全屏切换
+### 9.3 全屏切换
通过 `WM_SYSCHAR` 消息处理 Alt+Enter:
@@ -523,42 +638,42 @@ if (wparam == VK_RETURN && (HIWORD(lparam) & KF_ALTDOWN)) {
}
```
-## 9. 后续学习路径
+## 10. 后续学习路径
-### 9.1 基础阶段
+### 10.1 基础阶段
- [x] 完成设备创建和适配器枚举
- [x] 创建命令队列和命令列表
- [x] 描述符堆管理
-- [ ] 实现交换链和后台缓冲区
+- [x] 实现交换链和后台缓冲区
- [ ] 渲染第一个三角形
-### 9.2 进阶阶段
+### 10.2 进阶阶段
- [ ] 根签名和管线状态对象
- [ ] 资源屏障和同步
- [ ] 常量缓冲区和着色器资源
-### 9.3 高级阶段
+### 10.3 高级阶段
- [ ] 多线程渲染
- [ ] 资源绑定策略
- [ ] 动态资源管理
- [ ] 性能优化
-## 10. 参考资源
+## 11. 参考资源
-### 10.1 官方文档
+### 11.1 官方文档
- [Microsoft D3D12 文档](https://docs.microsoft.com/en-us/windows/win32/direct3d12/direct3d-12-graphics)
- [DXGI 文档](https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/dx-graphics-dxgi)
-### 10.2 推荐书籍
+### 11.2 推荐书籍
- 《Introduction to 3D Game Programming with DirectX 12》
- 《Real-Time 3D Rendering with DirectX and HLSL》
-### 10.3 项目相关文档
+### 11.3 项目相关文档
- [Graphics渲染架构分析](./Graphics渲染架构分析.md)
- [项目约定规范](./项目约定规范.md)