# 变更记录：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)
