# 变更记录：交换链与渲染表面实现

**提交日期**: 2026-03-31  
**提交哈希**: `95d8893`  
**变更类型**: 功能新增

---

## 变更概述

本次提交实现了 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)
