chore: sync workspace state

This commit is contained in:
2026-03-29 01:36:53 +08:00
parent eb5de3e3d4
commit e5cb79f3ce
4935 changed files with 35593 additions and 360696 deletions

View File

@@ -4,51 +4,86 @@
**类型**: `app-module`
**描述**: 编辑器应用层 API 文档入口,镜像 `editor/src`目录结构,覆盖编辑器启动、上下文、面板工作区、项目浏览、场景编辑与 UI 基础设施。
**描述**: XCEngine 独立编辑器应用层 API 文档入口,对应 `editor/src/**`模块结构,覆盖应用启动、上下文、面板、布局、命令、动作路由与 UI 基础设施。
## 概述
这一组文档对应的不是 `engine/include/XCEngine` 的 public engine headers,而是独立编辑器应用 `editor/src/**`
这一组文档对应的不是 `engine/include/XCEngine/**` 那类“引擎公共运行时头文件”,而是编辑器应用本身的源码模块。换句话说,这里的 API 更接近“工具层架构说明”和“编辑器内部可复用接口”,而不是给游戏运行时代码直接依赖的稳定 SDK
因此这里需要先建立一个正确心智模型
当前 Editor 的主链路可以概括为
- `XCEngine` 引擎模块负责运行时系统。
- `Editor` 模块负责围绕这些运行时系统搭建编辑器应用
- 它更接近“应用层/工具层 API”而不是给游戏代码直接依赖的稳定引擎 ABI。
1. [Application](Application/Application.md)
启动 Win32 窗口、D3D12 窗口渲染器、ImGui 会话与编辑器上下文
2. [Core](Core/Core.md)
组织事件总线、选择系统、撤销系统、场景与项目管理接口。
3. [Layers](Layers/Layers.md)
`EditorLayer` 承担编辑器工作区生命周期。
4. [Layout](Layout/Layout.md)
构建 dockspace、默认布局与工作区停靠关系。
5. [panels](panels/panels.md)
承载 Scene / Game / Hierarchy / Inspector / Console / Project 等主要工作面板。
6. [UI](UI/UI.md)
承载 Dear ImGui 上层的主题 token、chrome、树视图、属性布局和通用 widget。
当前编辑器的主链路大致是:
## 架构定位
1. [Application](Application/Application.md) 启动 Win32 窗口、D3D12 窗口渲染器和 ImGui 会话
2. [Core::EditorContext](Core/EditorContext/EditorContext.md) 组装事件总线、场景管理、项目管理、选择管理和撤销系统。
3. [Layers::EditorLayer](Layers/EditorLayer/EditorLayer.md) 承载编辑器工作区生命周期。
4. [Core::EditorWorkspace](Core/EditorWorkspace/EditorWorkspace.md) 组织菜单、层级、场景视图、GameView、Inspector、Console 和 Project 等面板。
从架构视角看,`Editor` 模块解决的是“如何把引擎运行时能力组织成一个可编辑、可观察、可操作的工具应用”
这和商业游戏引擎的常见分层非常一致:
- 引擎运行时负责场景、组件、渲染、资源和脚本能力。
- 编辑器应用负责把这些能力包装成面板、命令、菜单、工作区与交互流。
因此阅读这套文档时最重要的前置认知是Editor 层的很多接口是为工具体验服务的,它们允许比运行时 API 更强的耦合、更明确的 UI 约束和更直接的产品导向。
## 当前重构重点
结合当前 `editor/src` 的实现Editor 文档当前有几个结构上非常重要的重构点:
- `UI.h` 现在明确作为 umbrella header 处理,其职责是聚合常用 UI helper而不是声明新的运行时类型。
- `TreeView``PropertyLayout``BuiltInIcons``DockTabBarChrome` 等新基础设施已经成为多个面板共享的 UI 基建。
- `HierarchyPanel``ProjectPanel` 的实现已经明显转向“面板只负责呈现与路由,动作层 / 命令层负责执行业务”的分层。
这意味着 Editor 文档不能只做目录映射,还必须解释这些共享基础设施为什么存在、它们如何把编辑器代码从面板私有技巧提升为稳定的产品层模式。
## 聚合与辅助文件
并不是 `editor/src/**` 下每个文件都值得成为单独 API 页。当前有几类文件更适合在模块页说明:
- `editor/src/UI/UI.h`
这是 UI 子模块的 umbrella header文档并入 [UI](UI/UI.md)。
- `editor/src/EditorResources.h`
当前只定义了 `IDI_APP_ICON 101`,用于 `EditorApp.rc` 的资源 id 绑定,更适合作为 Editor 应用资源入口说明,而不是独立类型页。
- `editor/src/EditorApp.rc``editor/src/main.cpp`
属于应用启动与资源清单支撑文件,不是面向上层复用的编辑器 API。
这种处理方式符合当前文档规范:只有真正承担模块职责或可复用契约的文件才进入 canonical API 树;纯资源声明和应用入口支撑文件优先并入模块说明。
## 当前实现边界
- 当前编辑器主要是 Windows + D3D12 + ImGui 路径
-代码整体应用层源码,不像 engine public headers 那样已经完全按稳定 SDK 方式整理
- 当前文档页会优先标注 `源文件`,而不是 `头文件`,以反映它们来自 `editor/src/**`
- 当前自动审计脚本仍以 `engine/include/XCEngine` 为主,因此 `Editor` 这组页主要靠链接完整性和人工结构约束维护。
- 当前编辑器主路径仍然以 Windows + D3D12 + Dear ImGui 为核心
-代码整体属于应用层源码,不应误解为已经整理成稳定插件 SDK
- 自动审计脚本目前主要覆盖 `engine/include/XCEngine/**` 的 public headersEditor 这组文档更依赖目录并行约束、链接校验和人工核对源码行为
## 目录
- [Application](Application/Application.md) - 顶层编辑器应用入口。
- [Theme](Theme/Theme.md) - 顶层主题入口
- [Core](Core/Core.md) - 上下文、事件、撤销、选择与基础数据结构
- [Managers](Managers/Managers.md) - 项目与场景管理实现。
- [panels](panels/panels.md) - 编辑器面板基础设施
- [Actions](Actions/Actions.md) - 菜单、快捷键、按钮动作绑定与路由
- [Commands](Commands/Commands.md) - 面向场景与项目操作的命令封装
- [ComponentEditors](ComponentEditors/ComponentEditors.md) - 组件编辑器注册与实现。
- [Core](Core/Core.md) - 上下文、事件、撤销、选择与基础接口
- [Layers](Layers/Layers.md) - 编辑器 layer 封装。
- [Platform](Platform/Platform.md) - Win32 窗口宿主与 D3D12 窗口渲染器。
- [UI](UI/UI.md) - ImGui 会话与编辑器 UI 基础设施。
- [Actions](Actions/Actions.md) - 菜单/按钮/快捷键动作绑定与路由层。
- [Commands](Commands/Commands.md) - 面向场景与项目操作的高层命令封装。
- [ComponentEditors](ComponentEditors/ComponentEditors.md) - 组件属性编辑器注册与实现层。
- [Layout](Layout/Layout.md) - Dock 布局控制。
- [Managers](Managers/Managers.md) - 项目和场景管理实现。
- [panels](panels/panels.md) - 主要工作面板。
- [Platform](Platform/Platform.md) - Win32 宿主与 D3D12 窗口渲染路径。
- [Theme](Theme/Theme.md) - 顶层主题入口。
- [UI](UI/UI.md) - Editor UI 基础设施。
- [Utils](Utils/Utils.md) - 场景编辑与撤销相关辅助函数。
## 相关文档
- [XCEngine 根目录](../XCEngine.md)
- [XCEngine](../XCEngine.md)
- [Scene](../Scene/Scene.md)
- [Components](../Components/Components.md)
- [Rendering](../Rendering/Rendering.md)

View File

@@ -0,0 +1,107 @@
# BuiltInIcons
**命名空间**: `XCEngine::Editor::UI`
**类型**: `header-helper + enum class`
**源文件**: `editor/src/UI/BuiltInIcons.h`
**描述**: 为编辑器的资源树、资源网格和层级树提供内置图标枚举、初始化入口与统一绘制函数。
## 概述
`BuiltInIcons.h` 解决的不是“如何显示一张纹理”这个底层问题,而是“编辑器里哪些地方应该用统一图标语言表达文件夹、文件和场景对象”这个产品层问题。
当前源码里,这一层主要服务于三个界面:
- `HierarchyPanel` 通过 `GameObject` 图标给场景树节点补前缀。
- `ProjectPanel` 通过 `Folder` / `File` 图标绘制左侧目录树和右侧资源卡片。
- `ProjectActionRouter` 在拖拽预览等交互里复用同一套图标语义。
这和商业级编辑器常见的做法一致:业务面板不直接持有纹理资源,不自己决定某一类对象该显示什么图标,而是把“对象类别 -> 图标表现”的决定集中到一层共享 helper 中。这样做的好处是统一、可替换,而且不会让每个面板都自己重复写上传纹理、绘制 fallback 图形、适配尺寸的逻辑。
## 公开 API
| 成员 | 说明 |
|------|------|
| `enum class AssetIconKind { Folder, File, GameObject }` | 定义当前编辑器可识别的内置图标类别。 |
| `InitializeBuiltInIcons(ImGuiBackendBridge&, ID3D12Device*, ID3D12CommandQueue*)` | 初始化图标系统,上传贴图并分配 ImGui 可见的 SRV 描述符。 |
| `ShutdownBuiltInIcons()` | 释放内置图标占用的 GPU 资源与描述符。 |
| `DrawAssetIcon(ImDrawList*, const ImVec2& min, const ImVec2& max, AssetIconKind)` | 在给定矩形内绘制指定类别的图标。 |
## 生命周期
- 这一层本质上是一个进程级的全局状态,当前实现通过匿名命名空间里的 `g_icons` 保存后端指针和已上传纹理。
- 正确顺序是先初始化 `ImGuiBackendBridge`,再调用 `InitializeBuiltInIcons(...)`
- 关闭时应先停止使用这些图标,再调用 `ShutdownBuiltInIcons()`,最后销毁 ImGui 后端。
- 当前 `Application::InitializeImGui()``Application::Shutdown()` 已经按照这个顺序接线。
## 当前实现行为
`editor/src/UI/BuiltInIcons.cpp` 的实现,当前版本有几个值得明确写进文档的事实:
- 只有 `Folder``GameObject` 两类图标会尝试从磁盘加载 PNG。
- 图标路径不是从项目目录查找,而是从可执行文件目录推导到 `../../resources/Icons/folder_icon.png``../../resources/Icons/gameobject_icon.png`
- 贴图上传路径目前只支持 D3D12上传时会创建默认堆纹理、上传堆 buffer、一次性命令列表并在结束后主动等待命令队列空闲。
- `File` 图标当前不是贴图资源,而是运行时用 `ImDrawList` 直接画出的简化文件形状。
- 如果 `Folder` / `GameObject` 的 PNG 加载失败,系统不会抛出错误或写日志;它会静默退回到程序化绘制的 fallback 图标。
这意味着它更接近“编辑器内部视觉资源辅助层”,而不是一个对外稳定暴露的通用图标资源系统。
## 设计说明
如果把它和 Unity 的使用体验类比,可以把这层理解成“轻量版的内建编辑器图标入口”。它的目标不是提供一个完整的图标数据库,而是确保最核心的编辑器对象类型在不同面板里看起来一致。
这样设计的收益是:
- 面板代码只关心“我要画文件夹图标”,而不关心纹理从哪里来。
- 资源加载失败时仍然有 fallback不会导致整个面板失去可用性。
- 以后如果要统一替换图标风格,只需要改这一层,而不是同时改 `HierarchyPanel``ProjectPanel` 和拖拽预览逻辑。
代价也很明确:
- 当前实现和 D3D12 / ImGui 后端强绑定,还不是渲染后端无关的接口。
- 图标集合很小,只覆盖当前编辑器刚需。
- 路径解析和加载失败处理还比较工程化,不是面向内容制作流程的资源系统。
## 线程语义
- 这套 API 应被视为仅限编辑器 UI / 渲染线程使用。
- 初始化和销毁会触发 GPU 资源分配、命令提交与队列等待,不适合在任意后台线程上调用。
- `DrawAssetIcon(...)` 依赖当前帧的 `ImDrawList` 和 ImGui 绘制上下文,也只能在 ImGui 绘制阶段调用。
## 典型用法
```cpp
// 初始化阶段
m_imguiBackend.Initialize(...);
UI::InitializeBuiltInIcons(
m_imguiBackend,
m_windowRenderer.GetDevice(),
m_windowRenderer.GetCommandQueue());
// 面板绘制阶段
UI::DrawAssetIcon(
ImGui::GetWindowDrawList(),
iconMin,
iconMax,
UI::AssetIconKind::Folder);
// 关闭阶段
UI::ShutdownBuiltInIcons();
```
## 当前限制
- 当前只支持 D3D12 上传路径,没有 OpenGL / Vulkan / 软件后备实现。
- 图标种类固定为 `Folder``File``GameObject` 三类。
- 没有热重载、主题切换或 DPI 变体资源管理。
- 加载失败时以静默 fallback 为主,没有诊断级错误反馈。
## 相关文档
- [UI](../UI.md)
- [StyleTokens](../StyleTokens/StyleTokens.md)
- [Application](../../Application/Application.md)
- [HierarchyPanel](../../panels/HierarchyPanel/HierarchyPanel.md)
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)

View File

@@ -0,0 +1,73 @@
# DividerChrome
**命名空间**: `XCEngine::Editor::UI`
**类型**: `header-helper`
**源文件**: `editor/src/UI/DividerChrome.h`
**描述**: 提供编辑器面板分隔线的轻量绘制函数,用统一的 `StyleTokens` 控制颜色与线宽。
## 概述
`DividerChrome.h` 很小,但它承担的是“视觉一致性”而不是“复杂交互”。它把横向分隔线、纵向分隔线和当前窗口底边线抽成统一 helper避免各个面板自己散落写 `AddLine(...)` 与硬编码颜色。
在商业编辑器里,这类 helper 很常见,因为它们能把“只是画一条线”这件小事也纳入统一设计系统:线宽、颜色、出现位置都不再是面板作者自己临时决定,而是由共享 token 决定。
## 公开 API
| 成员 | 说明 |
|------|------|
| `DrawHorizontalDivider(...)` | 在任意 `ImDrawList` 上绘制一条横向分隔线。 |
| `DrawVerticalDivider(...)` | 在任意 `ImDrawList` 上绘制一条纵向分隔线。 |
| `DrawCurrentWindowBottomDivider(...)` | 读取当前 ImGui 窗口的矩形并在底边绘制横线。 |
## 当前实现行为
`editor/src/UI/DividerChrome.h` 的实现:
- 默认颜色来自 `PanelDividerColor()`
- 默认厚度来自 `PanelDividerThickness()`
- 如果传入的 `drawList` 为空,或绘制范围是退化区间,函数会直接返回。
- `DrawCurrentWindowBottomDivider(...)` 不会创建额外布局空间;它只是读取当前窗口的 `Pos``Size` 后直接在底边绘线。
这意味着它是纯绘制 helper不管理布局、不创建交互区域也不记录任何状态。
## 设计说明
把 divider 单独抽出来有两个直接好处:
- `ProjectPanel``PanelChrome`、自定义标签栏都可以复用同一套边界视觉语言。
- 当设计风格变化时,只要改 token而不是到处找 `AddLine(...)` 的裸调用。
可以把它理解成商业引擎编辑器里“panel chrome”的最小组成部分。它不像 splitter 那样负责拖拽,也不像 toolbar scope 那样负责结构;它只负责把层次边界画正确。
## 使用建议
- 如果你已经有明确的屏幕坐标,就直接调用 `DrawHorizontalDivider(...)``DrawVerticalDivider(...)`
- 如果你只是想给当前 child/window 补一条底边线,优先用 `DrawCurrentWindowBottomDivider(...)`
- 这类 helper 适合放在面板收尾阶段调用,而不是提前调用后再继续写会改变窗口高度的控件。
## 典型用法
```cpp
ImDrawList* drawList = ImGui::GetWindowDrawList();
const ImVec2 min = ImGui::GetWindowPos();
const ImVec2 max(min.x + ImGui::GetWindowSize().x, min.y + ImGui::GetWindowSize().y);
UI::DrawHorizontalDivider(drawList, min.x, max.x, max.y - 0.5f);
```
## 当前限制
- 不负责布局留白,调用者必须自己决定线的位置。
- 没有 hover / active 等交互状态,它不是 splitter。
- 只是一层对 `ImDrawList::AddLine(...)` 的统一封装,不提供更复杂的边框系统。
## 相关文档
- [UI](../UI.md)
- [StyleTokens](../StyleTokens/StyleTokens.md)
- [Core](../Core/Core.md)
- [PanelChrome](../PanelChrome/PanelChrome.md)
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)

View File

@@ -0,0 +1,103 @@
# DockTabBarChrome
**命名空间**: `XCEngine::Editor::UI`
**类型**: `header-helper`
**源文件**: `editor/src/UI/DockTabBarChrome.h`
**描述**: 基于 ImGui Docking 内部节点实现自定义停靠标签栏外观、选中逻辑、拖拽排序与拖出浮动行为。
## 概述
`DockTabBarChrome.h` 是当前 Editor UI 里技术耦合度最高的一层 helper 之一。它的职责不是创建 dockspace而是在 ImGui 已经提供 docking 能力的前提下,把默认标签栏替换成一套更像商业编辑器的自定义标签栏。
当前这套方案由两部分组成:
- `ConfigureDockTabBarChrome(...)` 递归配置 dock node关闭原生 tab bar并确保叶子节点处于可绘制的稳定状态。
- `DrawDockedWindowTabStrip()` 在具体面板窗口内部补画自定义标签条。
从调用关系上看:
- `DockLayoutController::RenderDockspace()` 负责每帧配置 dockspace 节点。
- `PanelChrome::PanelWindowScope``ImGui::Begin(...)` 成功后调用 `DrawDockedWindowTabStrip()`
也就是说,这一层本质上是“布局层 + 面板 chrome 层”的协作产物,而不是某一个面板自己的私有实现。
## 建议视为公开入口的函数
虽然头文件里存在很多 inline helper但从设计意图上真正应该被其他模块直接依赖的入口主要只有下面三个
| 成员 | 说明 |
|------|------|
| `ConfigureDockTabBarChrome(ImGuiDockNode* node)` | 对指定 dock node 递归应用自定义 tab bar 策略。 |
| `ConfigureDockTabBarChrome(ImGuiID dockspaceId)` | 通过 dockspace id 找到 root node 并应用同样策略。 |
| `DrawDockedWindowTabStrip()` | 在当前面板窗口顶部绘制自定义标签条。 |
其他诸如 `DockTabOrderCache()``ReorderDockTab(...)``BeginCustomDockTabUndock(...)` 之类的函数,虽然在头文件可见,但更适合作为当前实现细节理解,而不是稳定的上层调用契约。
## 当前实现行为
`editor/src/UI/DockTabBarChrome.h` 的实现,当前版本具备以下行为:
- 对叶子 `ImGuiDockNode` 会强制设置 `ImGuiDockNodeFlags_NoTabBar``NoWindowMenuButton``NoCloseButton`,隐藏 ImGui 原生标签栏。
- 通过静态 `std::unordered_map<ImGuiID, std::vector<ImGuiID>>` 维护每个 dock node 的 tab 顺序缓存。
- 如果当前叶子节点没有显式选中页签,系统会把第一个窗口设为选中页签。
- 自定义标签条支持:
- 单击切换选中页签。
- 左键拖拽改变页签顺序。
- 满足阈值时将页签从当前 dock 节点拖出,进入 ImGui 原生的 undock 流程。
- 标签颜色、背景色与分隔线使用 `StyleTokens` 和 ImGui dock style 的组合结果。
- 标签宽度当前按文本宽度加固定水平 padding 计算,没有做更复杂的压缩或滚动策略。
这套实现明显不是简单换皮,而是接管了 tab strip 的一部分交互语义。
## 设计说明
为什么要做这一层,而不是直接接受 ImGui 默认标签栏?
- 商业编辑器通常会把工作区标签视觉做得更克制、更稳定,以便和工具栏、面板边框形成统一外观。
- 一旦需要控制页签排序、激活行为、拖出阈值、底部分隔线这些细节,直接用默认样式往往不够。
- 把这部分逻辑集中在 `DockTabBarChrome.h` 中,能避免 `Hierarchy``Scene``Console` 这类面板各自处理 dock 标签外观。
如果和 Unity 的使用体验类比,这层更像“编辑器工作区标签栏的定制实现”,而不是某个单独窗口组件。
## 前置条件与线程语义
- 只能在启用了 ImGui Docking 的编辑器 UI 帧里使用。
- `ConfigureDockTabBarChrome(...)` 应在 dockspace 建立后、面板开始绘制前调用;当前由 `DockLayoutController` 统一负责。
- `DrawDockedWindowTabStrip()` 需要当前窗口已经 `Begin(...)` 成功且窗口处于 docked 状态;当前由 `PanelWindowScope` 自动调用。
- 这一层依赖 `imgui_internal.h` 暴露的内部结构,应视为 UI 主线程专用逻辑,不具备线程安全保证。
## 典型用法
```cpp
const ImGuiID dockspaceId = ImGui::GetID("MainDockspace.Root");
UI::ConfigureDockTabBarChrome(dockspaceId);
{
UI::DockHostStyleScope dockHostStyle;
ImGui::DockSpace(dockspaceId, ImVec2(0.0f, 0.0f), dockFlags);
}
```
面板窗口不需要自己手写标签条调用,使用 `PanelWindowScope` 时会自动进入:
```cpp
UI::PanelWindowScope panel("Hierarchy");
```
## 当前限制
- 强依赖 `imgui_internal.h``ImGuiWindow``ImGuiDockNode` 等内部实现细节,升级 ImGui 时需要重点回归。
- 当前没有 close button、window menu button也没有更复杂的 tab overflow 处理。
- tab 顺序缓存是进程内静态状态,不是跨会话布局资产。
- 这是 Editor 专用 UI 基建,不适合直接当作通用运行时 UI API 使用。
## 相关文档
- [UI](../UI.md)
- [StyleTokens](../StyleTokens/StyleTokens.md)
- [PanelChrome](../PanelChrome/PanelChrome.md)
- [DockHostStyle](../DockHostStyle/DockHostStyle.md)
- [DockLayoutController](../../Layout/DockLayoutController/DockLayoutController.md)

View File

@@ -0,0 +1,105 @@
# PropertyLayout
**命名空间**: `XCEngine::Editor::UI`
**类型**: `header-helper + struct`
**源文件**: `editor/src/UI/PropertyLayout.h`
**描述**: 为 Inspector 属性行提供统一的标签列 / 控件列几何计算,并把具体控件绘制委托给回调。
## 概述
`PropertyLayout.h` 是当前 Inspector 体验能否“像一个成熟编辑器”而不是“像一堆临时摆放的 ImGui 控件”的关键基础设施。
它不直接定义浮点框、复选框或向量输入框,而是先回答一个更底层的问题:
- 标签列从哪里开始?
- 控件列从哪里开始?
- 当前行有多高?
- 控件应该拿到多宽的可用空间?
把这层几何计算独立出来以后,`ScalarControls``VectorControls``PropertyGrid` 这些上层 helper 就可以共享同一套 Inspector 排版规则,而不需要每种控件都重复手算列宽和光标位置。
## 公开 API
| 成员 | 说明 |
|------|------|
| `PropertyLayoutSpec` | 描述标签缩进、控件列起点、标签和控件间距、控件右侧留白。 |
| `PropertyLayoutMetrics` | 保存一行属性被计算出的实际几何结果。 |
| `MakePropertyLayout()` | 生成使用默认 token 的 `PropertyLayoutSpec`。 |
| `PushPropertyLayoutStyles()` | 推入当前属性行需要的样式变量。 |
| `GetPropertyControlWidth(...)` | 根据布局结果返回控件区域宽度。 |
| `SetNextPropertyControlWidth(...)` | 用布局结果直接设置下一个控件的宽度。 |
| `AlignPropertyControlToRight(...)` | 让较窄控件在属性列中右对齐。 |
| `DrawPropertyRow(...)` | 绘制一整行属性,并把具体控件绘制委托给回调。 |
## 当前实现行为
`editor/src/UI/PropertyLayout.h` 的实现:
- `PropertyLayoutSpec` 的默认值直接来自 `StyleTokens.h`,例如 `InspectorPropertyControlColumnStart()``InspectorPropertyLabelInset()`
- `PushPropertyLayoutStyles()` 当前只推入 `ImGuiStyleVar_FramePadding`,并不负责整套 Inspector 样式。
- `DrawPropertyRow(...)` 会:
-`label``PushID` 的依据。
- 读取当前内容区宽度并计算一整行的 label / control 几何。
- 直接用 `ImDrawList::AddText(...)` 绘制标签文本,而不是创建一个独立的 ImGui label item。
- 把光标移动到控件列起点,再调用外部回调绘制真正的控件。
- 最后用 `Dummy(...)` 吃掉这一行占用的高度,确保后续布局连续。
这说明它是一层“布局执行器”,不是单纯的常量集合。
## 设计说明
商业引擎编辑器通常会把 Inspector 的“字段布局规则”和“字段编辑控件”拆开,这一点和 Unity Inspector 的使用体验很像:
- 左边是稳定的属性标签列。
- 右边是不同类型字段共享的一列编辑区域。
- 上层组件编辑器只描述“这里是一条 Position / Rotation / Scale”而不是自己计算每个字段的横向坐标。
这样拆分的收益非常直接:
- 所有组件编辑器天然保持一致的列对齐。
- 以后如果想统一调整 Inspector 视觉密度,只需要修改 token 或布局层。
- `PropertyGrid` 可以专注在“属性语义 + 撤销接线”,而不必再重复几何计算。
## 使用模式
更推荐的使用方式不是直接手算坐标,而是把控件包进 `DrawPropertyRow(...)` 的回调:
```cpp
const UI::PropertyLayoutSpec layout = UI::MakePropertyLayout();
UI::DrawPropertyRow("Mass", layout, [&](const UI::PropertyLayoutMetrics& metrics) {
UI::SetNextPropertyControlWidth(metrics);
return ImGui::DragFloat("##value", &mass, 0.1f, 0.0f, 1000.0f, "%.2f");
});
```
如果只是常见属性类型,实际工程里更常见的入口是:
- `ScalarControls.h`
- `VectorControls.h`
- `PropertyGrid.h`
它们已经把 `PropertyLayout` 作为底层依赖封装好了。
## 线程语义
- 这一层完全依赖当前帧的 ImGui 上下文和当前窗口布局状态,只能在 UI 线程、ImGui 绘制阶段调用。
- 它不维护后台状态,也不适合离线计算布局。
## 当前限制
- `PushID(label)` 意味着同一作用域里若有重复标签,调用方应额外 `PushID` 以避免冲突。
- 标签是直接画到 draw list 上的,因此不是可交互的独立 ImGui item。
- 当前布局仍然是固定列起点模型,不是响应式表单系统。
- `PushPropertyLayoutStyles()` 目前只做最小样式压栈,离完整的属性主题系统还有距离。
## 相关文档
- [UI](../UI.md)
- [StyleTokens](../StyleTokens/StyleTokens.md)
- [ScalarControls](../ScalarControls/ScalarControls.md)
- [VectorControls](../VectorControls/VectorControls.md)
- [PropertyGrid](../PropertyGrid/PropertyGrid.md)

View File

@@ -0,0 +1,88 @@
# SplitterChrome
**命名空间**: `XCEngine::Editor::UI`
**类型**: `header-helper + enum class + struct`
**源文件**: `editor/src/UI/SplitterChrome.h`
**描述**: 为编辑器面板提供统一的 splitter 主题颜色与拖拽分隔条绘制 / 交互结果。
## 概述
`SplitterChrome.h` 处理的是“两个区域之间如何被拖拽改变尺寸”这个问题。和 `DividerChrome` 不同,它不只是画一条视觉边界线,而是同时承担:
- 交互命中区域创建。
- hover / active 状态计算。
- 鼠标光标切换。
- 拖拽位移返回。
当前源码中,最直接的使用者是 `ProjectPanel`:左侧目录树和右侧资源浏览区之间的宽度调节完全依赖这层 helper。
## 公开 API
| 成员 | 说明 |
|------|------|
| `enum class SplitterAxis { Vertical, Horizontal }` | 定义 splitter 的主轴方向。 |
| `struct SplitterResult` | 返回当前 splitter 的 hover、active 与拖拽增量。 |
| `ApplySplitterThemeColors(ImVec4* colors)` | 用当前 token 覆盖 ImGui separator / resize grip 相关颜色。 |
| `DrawSplitter(...)` | 绘制 splitter 并返回本帧交互结果。 |
## 当前实现行为
`editor/src/UI/SplitterChrome.h` 的实现:
- `DrawSplitter(...)` 使用 `ImGui::InvisibleButton(...)` 创建命中区域。
- 命中区域厚度默认来自 `PanelSplitterHitThickness()`,而真正画出来的线宽来自 `PanelSplitterVisibleThickness()`
- 当 splitter 处于 active 状态时:
- 纵向 splitter 返回 `MouseDelta.x`
- 横向 splitter 返回 `MouseDelta.y`
- hover 或 active 时会把鼠标指针切到 `ResizeEW``ResizeNS`
- 它只返回“这一帧用户拖了多少”,并不直接修改任何布局变量;调用者必须自己把 `delta` 累加到宽度 / 高度并做 clamp。
因此,这层是“交互采样 + 视觉呈现”,不是完整的布局管理器。
## 设计说明
把 splitter 从具体面板里抽出来是非常典型的商业编辑器实践:
- 面板只关心自己的布局状态,例如 `m_navigationWidth`
- splitter helper 只关心拖拽命中、光标和线条绘制。
- 主题层通过 `StyleTokens` 统一控制 splitter 粗细、颜色和命中区宽度。
这样 `ProjectPanel`、后续的多列 Inspector 或资源导入面板都能使用相同的分隔条行为,而不会出现每个面板“拖动手感都不一样”的问题。
## 典型用法
```cpp
const UI::SplitterResult splitter =
UI::DrawSplitter("##ProjectPaneSplitter", UI::SplitterAxis::Vertical, totalHeight);
if (splitter.active) {
m_navigationWidth += splitter.delta;
}
```
调用者通常还应该把结果夹紧到合理区间,例如:
- 最小导航宽度
- 最小内容区宽度
## 线程语义
- 只能在 ImGui UI 线程使用。
- 每帧结果是即时的,不会自动跨帧缓存。
- `ApplySplitterThemeColors(...)` 修改的是调用方传入的颜色数组,通常应在同一帧、同一上下文中使用。
## 当前限制
- 当前只提供基础 hover / active 与位移返回,不提供吸附、双击重置、动画过渡等高级行为。
- 颜色仍然是静态 token不支持运行时主题切换动画。
- 由调用者自己负责宽度约束和布局重算,没有更高层的布局容器抽象。
## 相关文档
- [UI](../UI.md)
- [StyleTokens](../StyleTokens/StyleTokens.md)
- [DividerChrome](../DividerChrome/DividerChrome.md)
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)

View File

@@ -2,45 +2,90 @@
**命名空间**: `XCEngine::Editor::UI`
**类型**: `header-helper`
**类型**: `header-helper + struct`
**源文件**: `editor/src/UI/StyleTokens.h`
**描述**: 统一定义编辑器 UI 使用的颜色、尺寸、间距和布局 token。
**描述**: 集中定义 Editor UI 的颜色、尺寸、间距与树视图样式,是当前编辑器设计系统的单一 token
## 概述
`StyleTokens.h` 是当前 UI 的设计 token 中心。
它集中提供了大量 inline token helper例如
`StyleTokens.h` 是当前 Editor UI 的设计语言中心。它把原本最容易散落在各个面板、各类 helper 和临时控件中的“魔法数字”集中成一组具名 token让代码能够表达设计意图而不是只暴露数值本身。
- Dock 标签颜色
- 工具栏高度与 padding
- 资产网格尺寸
- Inspector 标签列宽
- 弹窗按钮尺寸
- 向量控件按钮颜色
- Console 状态颜色
例如:
- `ProjectNavigationDefaultWidth()` 表达的是“项目导航栏默认宽度”。
- `InspectorPropertyControlColumnStart()` 表达的是“Inspector 控件列起点”。
- `PanelSplitterHitThickness()` 表达的是“splitter 命中区宽度”。
这比直接写 `248.0f``236.0f``4.0f` 更接近商业编辑器的工程实践,因为当 UI 逐步复杂起来后,真正难维护的从来不是控件本身,而是这些散落的视觉约束。
## 当前 token 分组
`editor/src/UI/StyleTokens.h` 的实现,当前 token 大致可以分成下面几类:
| 类别 | 代表 token | 主要消费者 |
|------|-------------|------------|
| Dock host / tab | `DockHostFramePadding()``DockTabSelectedColor()` | `DockHostStyle``DockTabBarChrome` |
| 面板与工具栏 | `PanelWindowPadding()``ToolbarPadding()` | `PanelChrome`、各类 panel toolbar |
| Project 浏览器 | `ProjectNavigationDefaultWidth()``AssetGridSpacing()` | `ProjectPanel``Widgets` |
| TreeView 导航树 | `NavigationTreeIconSize()``NavigationTreePrefixWidth()` | `TreeView``HierarchyPanel``ProjectPanel` |
| Inspector / 属性布局 | `InspectorPropertyControlColumnStart()``ControlFramePadding()` | `PropertyLayout``ScalarControls``VectorControls``PropertyGrid` |
| Popup / Combo | `PopupWindowPadding()``ComboPopupBackgroundColor()` | `Core``Widgets` |
| 资产卡片与图标 | `AssetTileSize()``BuiltInFolderIconBodyColor()` | `Widgets``BuiltInIcons` |
| Console / 状态栏 | `ConsoleRowHoverFillColor()``MenuBarStatusIdleColor()` | `Widgets`、菜单栏与 Console UI |
此外,`StyleTokens.h` 里还定义了:
- `struct TreeViewStyle`
- `NavigationTreeStyle()`
- `HierarchyTreeStyle()`
- `ProjectFolderTreeStyle()`
它们是树视图层的“组合 token”不是单个数值而是一组可复用的树节点视觉约束。
## 当前实现行为
按当前源码,这一层有几个很重要的特点:
- token 目前全部是 header-only inline 函数,不是配置资产,也不是运行时可热更新的数据表。
- 很多 helper 会把这里的 token 当默认值,而不是自己再声明一套局部常量。
- `PropertyLayout``SplitterChrome``DockTabBarChrome``BuiltInIcons``Widgets` 都直接依赖这里的 token。
- 某些 token 之间存在设计上的复用关系,例如 `PanelDividerColor()` 当前直接复用 splitter 的 idle 颜色。
这意味着 `StyleTokens.h` 不是“装饰性文件”,而是整个 Editor UI 复用层的上游依赖。
## 设计说明
这是商业编辑器 UI 非常推荐的做法。
不要在控件代码里到处写 `6.0f``0.24f``104.0f` 这种魔法数字,而应先抽象成 token。
为什么这层很重要?
收益包括:
- 当你在重构 `ProjectPanel` 时,不应该顺手发明一套新的资产卡片尺寸。
- 当你在做 `HierarchyPanel` 的树前缀图标时,不应该自己再决定一套图标槽宽。
- 当你在优化 Inspector 的密度时,真正应该改的是 token而不是 5 个不同 helper 里的重复常量。
- 调整风格时能集中修改
- 命名本身就表达设计意图
- 高层 widget 与具体数值解耦
这就是商业级编辑器常说的“设计系统落地到代码层”的意义。对于 XCEngine 来说,这一层还没有演化成完整的主题资产系统,但它已经承担了相同的架构角色。
## 与其他 UI helper 的关系
- `PropertyLayout` 使用它定义属性行的列宽、label inset 和 frame padding。
- `TreeView` 使用它定义缩进、前缀插槽宽度和图标尺寸。
- `SplitterChrome``DividerChrome` 使用它定义边界线和分隔条视觉。
- `Widgets` 使用它定义资产卡片、面包屑、组件 section 和各种通用控件的视觉细节。
- `BuiltInIcons` 使用它定义文件 / 文件夹 fallback 图标颜色。
从依赖方向上说,这一层应该尽量保持在 UI 栈的较底部,避免反向依赖更高层的业务 widget。
## 当前限制
- 仍然是 header inline 常量函数,而不是数据驱动主题系统
- token 数量已经较多,后续可能需要再分层
- 当前主要围绕唯一默认主题设计
- 当前仍然是“写死在代码里的默认主题”,不是运行时可切换的主题资源系统
- token 命名已经带有明显的面板语义,例如 `ProjectNavigationPanePadding()`,说明它还是偏应用内设计系统,而不是完全抽象的通用 design token。
- 还没有颜色模式切换、用户主题文件或样式编辑器一类更高阶能力。
## 相关文档
- [UI](../UI.md)
- [BaseTheme](../BaseTheme/BaseTheme.md)
- [PanelChrome](../PanelChrome/PanelChrome.md)
- [Widgets](../Widgets/Widgets.md)
- [DockHostStyle](../DockHostStyle/DockHostStyle.md)
- [PropertyLayout](../PropertyLayout/PropertyLayout.md)
- [TreeView](../TreeView/TreeView.md)
- [SplitterChrome](../SplitterChrome/SplitterChrome.md)

View File

@@ -0,0 +1,122 @@
# TreeView
**命名空间**: `XCEngine::Editor::UI`
**类型**: `header-helper + class + struct`
**源文件**: `editor/src/UI/TreeView.h`
**描述**: 提供编辑器树形视图的统一节点状态、前缀绘制槽、交互回调和展开状态持久化机制。
## 概述
`TreeView.h` 是当前 Editor UI 重构里最重要的公共基础设施之一。它把原本很容易散落在 `HierarchyPanel``ProjectPanel` 中的树节点绘制逻辑抽成一层统一接口,让不同面板能够共享:
- 相同的树节点视觉风格。
- 相同的展开 / 收起行为。
- 相同的前缀图标插槽模型。
- 相同的点击、双击、右键、拖拽扩展接入点。
如果从使用体验上理解,它很接近商业编辑器中“导航树控件”的内建基础层。`HierarchyPanel``ProjectPanel` 的左侧目录树并没有各写一套独立的 tree widget而是都基于这层构建。
## 公开类型与函数
| 成员 | 说明 |
|------|------|
| `class TreeViewState` | 保存节点展开状态的内存态容器。 |
| `TreeNodeOptions` | 控制选中、叶子节点、默认展开、点击展开策略等基础选项。 |
| `TreeNodeResult` | 返回节点本帧是否打开、是否点击、是否双击、是否右键等交互结果。 |
| `TreeNodePrefixContext` | 给前缀绘制回调提供绘制区域、选中状态和 hover 状态。 |
| `TreeNodePrefixSlot` | 定义一个可选的前缀绘制槽。 |
| `TreeNodeCallbacks` | 定义交互回调和额外渲染回调。 |
| `TreeNodeDefinition` | 组合节点选项、持久化 key、样式、前缀槽与回调。 |
| `ResetTreeLayout()` | 清空当前树布局的缩进栈,开始一棵新的树。 |
| `DrawTreeNode(...)` | 绘制节点并返回本帧结果。 |
| `EndTreeNode()` | 与打开状态的节点配对,弹出一层树缩进。 |
## TreeViewState 的角色
`TreeViewState` 当前只做一件事:根据 `std::string_view key` 保存节点展开状态。
它不是场景数据,也不是项目数据,只是某个 UI 实例自己的展开状态缓存。因此:
- `HierarchyPanel` 用它记住实体树哪些节点已经展开。
- `ProjectPanel` 用它记住目录树哪些文件夹已经展开。
当前实现采用 `std::unordered_map<std::string, bool>`,所以最好的 `persistenceKey` 是稳定且能跨帧复现的标识,例如:
- 场景对象的 UUID
- 资源目录的完整路径
## 当前实现行为
`editor/src/UI/TreeView.h` 的实现,当前版本有几个关键事实:
- 树缩进不是保存在某个对象里,而是保存在一个静态 `TreeIndentStack()` 中。
- 因此每次绘制一棵新的根树之前都应该先调用 `ResetTreeLayout()`
- `DrawTreeNode(...)` 若返回 `open = true`,调用方在绘制完所有子节点后必须调用 `EndTreeNode()` 与之配对。
- 节点背景当前直接使用 `ImGuiCol_Header` / `HeaderHovered` / `HeaderActive` 这一组颜色绘制选中与 hover 态。
- 展开箭头由 `Core.h` 里的 `DrawDisclosureArrow(...)` 手工绘制。
- prefix slot 允许调用方在标签前插入小图标、状态灯或其他自定义绘制内容。
- `callbacks.onInteraction` 适合接选择、右键菜单、双击重命名等语义。
- `callbacks.onRenderExtras` 适合接拖拽源 / 拖拽目标之类必须紧跟节点绘制的额外逻辑。
这说明它不是对 `ImGui::TreeNodeEx(...)` 的轻封装,而是自己接管了树节点的大部分视觉和交互行为。
## 设计说明
为什么要单独造这一层,而不是每个面板直接用 ImGui 原生 tree API
- `Hierarchy``Project` 都需要统一的左侧导航树风格,这种复用非常适合抽成共享 helper。
- 商业编辑器中的树视图往往不仅是“文本 + 箭头”,还需要图标前缀、整行高亮、拖放热点、重命名切换等语义。
- 一旦把节点定义、展开状态和回调插槽标准化,上层面板代码就能更聚焦于业务对象,而不是 UI 细节。
如果和 Unity 对照可以把它理解成“Hierarchy / Project 导航树的共用控件层”,而不是某个特定面板的局部技巧。
## 典型用法
```cpp
UI::ResetTreeLayout();
UI::TreeNodeDefinition node;
node.options.selected = isSelected;
node.options.leaf = !hasChildren;
node.persistenceKey = stablePath;
node.style = UI::ProjectFolderTreeStyle();
node.prefix.width = UI::NavigationTreePrefixWidth();
node.prefix.draw = DrawFolderPrefix;
node.callbacks.onInteraction = [&](const UI::TreeNodeResult& result) {
if (result.clicked) {
NavigateToFolder();
}
};
const UI::TreeNodeResult result =
UI::DrawTreeNode(&m_treeState, folder.get(), folder->name.c_str(), node);
if (result.open) {
DrawChildren();
UI::EndTreeNode();
}
```
## 生命周期与线程语义
- `TreeViewState` 由具体面板持有,通常作为成员变量存在于面板实例生命周期内。
- 整套 API 都依赖当前 ImGui 帧状态,只能在 UI 线程使用。
- 它不负责把展开状态写入磁盘或布局文件;当前只是内存级持久化。
## 当前限制
- 静态 `TreeIndentStack()` 让它更像“约定式 helper”而不是完全可重入的小部件对象。
- 没有键盘导航、滚动到可见、虚拟化大树等更高级的树控件能力。
- 展开状态目前只保存在面板实例中,重启编辑器后不会自动恢复。
- 当前回调模型偏即时模式,适合编辑器内部使用,但不是声明式 UI 框架。
## 相关文档
- [UI](../UI.md)
- [StyleTokens](../StyleTokens/StyleTokens.md)
- [BuiltInIcons](../BuiltInIcons/BuiltInIcons.md)
- [HierarchyPanel](../../panels/HierarchyPanel/HierarchyPanel.md)
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)

View File

@@ -4,47 +4,106 @@
**类型**: `submodule`
**描述**: 编辑器 ImGui 基础设施、主题、控件与面板绘制辅助层
**描述**: 编辑器基于 Dear ImGui 的 UI 基础设施层,负责会话、主题 token、面板 chrome、树视图、属性布局和通用控件封装
## 概述
`UI` 子模块当前内容很多,但核心方向很清楚:
`XCEngine::Editor::UI` 不是单个 widget 库,而是一组逐层叠加的编辑器 UI 基建。它的目标是把 Dear ImGui 这种非常自由、非常底层的即时模式 API 收束成一套可维护的“编辑器内建控件层”。
- 维护 ImGui 会话与布局文件
- 封装统一视觉主题
- 提供属性面板、工具栏、菜单、场景状态等 UI 辅助
当前源码里,这一层大致可以分成四层:
已文档化的核心页面:
1. **会话与后端层**
负责 ImGui 上下文、ini 持久化、后端桥接和纹理描述符分配。
2. **主题与设计 token 层**
负责颜色、尺寸、间距、dock host 风格与常用视觉常量。
3. **chrome 与布局层**
负责面板窗口、工具栏、树视图、splitter、divider、属性行布局和自定义 dock 标签栏。
4. **语义化控件层**
负责属性网格、资产卡片、面包屑、弹窗、组件折叠段和各种编辑器专用小部件。
- [ImGuiSession](ImGuiSession/ImGuiSession.md)
- [ImGuiBackendBridge](ImGuiBackendBridge/ImGuiBackendBridge.md)
- [BaseTheme](BaseTheme/BaseTheme.md)
- [StyleTokens](StyleTokens/StyleTokens.md)
- [Core](Core/Core.md)
- [PanelChrome](PanelChrome/PanelChrome.md)
- [Widgets](Widgets/Widgets.md)
- [PopupState](PopupState/PopupState.md)
- [ScalarControls](ScalarControls/ScalarControls.md)
- [VectorControls](VectorControls/VectorControls.md)
- [PropertyGrid](PropertyGrid/PropertyGrid.md)
- [DockHostStyle](DockHostStyle/DockHostStyle.md)
- [ConsoleFilterState](ConsoleFilterState/ConsoleFilterState.md)
- [ConsoleLogFormatter](ConsoleLogFormatter/ConsoleLogFormatter.md)
- [SceneStatusWidget](SceneStatusWidget/SceneStatusWidget.md)
- [AboutEditorDialog](AboutEditorDialog/AboutEditorDialog.md)
这类分层方式非常接近商业级游戏引擎编辑器的组织方式。原因很简单:如果每个面板都直接拼原始 ImGui 调用,代码很快就会变成不可维护的样式杂糅体;而先建设统一 UI 层,后续任何面板重构、主题调整和交互升级都会容易很多。
`UI/UI.h` 本身只是聚合入口,真正值得阅读的是这些分层 helper
## 聚合头文件
- `ImGuiSession + ImGuiBackendBridge` 负责上下文与后端绑定
- `BaseTheme + StyleTokens` 负责视觉规范
- `Core + PanelChrome + Widgets` 负责通用交互壳层
- `ScalarControls + VectorControls + PropertyGrid` 负责 Inspector 表单体验
**源文件**: `editor/src/UI/UI.h`
这样拆分的好处,是把 Dear ImGui 容易失控的即时模式代码收束成一套可复用部件库
`UI.h` 当前是一个标准的 umbrella header。它的职责是把 Editor UI 常用 helper 聚合到同一个入口,而不是声明新的运行时类型
因此这页文档直接承担 `UI.h` 的说明职责,不再额外创建一个重复的类型页。
## 当前架构重点
结合当前 `editor/src/UI/**` 与最近一次 Editor UI 重构,以下几层是现在最值得读的核心:
- `ImGuiSession + ImGuiBackendBridge`
管理 ImGui 上下文、DPI、SRV 描述符与窗口后端对接。
- `BaseTheme + StyleTokens + DockHostStyle`
管理设计 token 和 dock host 外观,是全局视觉风格的起点。
- `Core + DividerChrome + SplitterChrome + DockTabBarChrome + PanelChrome`
管理窗口 chrome、底边线、拖拽分隔条与自定义 dock 标签栏。
- `TreeView + BuiltInIcons`
管理层级树 / 目录树的共享节点表现与图标前缀。
- `PropertyLayout + ScalarControls + VectorControls + PropertyGrid`
管理 Inspector 的属性布局、标量控件、向量控件和高层属性编辑入口。
- `Widgets + PopupState + SceneStatusWidget + AboutEditorDialog`
提供更接近编辑器业务语义的通用 widget。
这次重构里,`TreeView``PropertyLayout``BuiltInIcons``DockTabBarChrome` 是最明显的新基础设施,它们共同把原先分散在具体面板中的 UI 技巧沉淀成了复用层。
## 设计说明
如果把它和 Unity 一类成熟编辑器对照,可以把 `Editor::UI` 理解成“编辑器内部控件系统”,而不是游戏运行时 UI 框架:
- 它面向工具开发,不面向游戏内 HUD。
- 它可以为编辑器体验服务而使用较强约束的 helper。
- 它允许把某些风格、布局和交互策略写死在共享层,以换取整个工具界面的统一性。
这种设计的好处是:
- 面板作者写的代码更接近业务语义,而不是样式拼装。
- 主题、间距、树视图和 Inspector 布局能在全局范围内保持一致。
- 面板之间可以共享拖拽、上下文菜单、资产卡片、树节点前缀等成熟模式。
代价也很明确:
- 当前 UI 层明显偏 Editor 私有实现,不是面向外部插件的稳定 ABI。
- 很多 helper 仍然是 header-only inline 形式,接口演化速度会比较快。
- 当前实现强依赖 Windows + D3D12 + Dear ImGui 的编辑器运行路径。
## 头文件
- [AboutEditorDialog](AboutEditorDialog/AboutEditorDialog.md) - `AboutEditorDialog.h`,关于对话框相关 UI。
- [BaseTheme](BaseTheme/BaseTheme.md) - `BaseTheme.h`,编辑器基础主题安装。
- [BuiltInIcons](BuiltInIcons/BuiltInIcons.md) - `BuiltInIcons.h`,内置资源/对象图标系统。
- [ConsoleFilterState](ConsoleFilterState/ConsoleFilterState.md) - `ConsoleFilterState.h`Console 过滤状态。
- [ConsoleLogFormatter](ConsoleLogFormatter/ConsoleLogFormatter.md) - `ConsoleLogFormatter.h`Console 日志格式化。
- [Core](Core/Core.md) - `Core.h`,底层 ImGui helper 与 popup chrome 包装。
- [DividerChrome](DividerChrome/DividerChrome.md) - `DividerChrome.h`,统一分隔线绘制。
- [DockHostStyle](DockHostStyle/DockHostStyle.md) - `DockHostStyle.h`dock host 风格压栈。
- [DockTabBarChrome](DockTabBarChrome/DockTabBarChrome.md) - `DockTabBarChrome.h`,自定义 dock 标签栏。
- [ImGuiBackendBridge](ImGuiBackendBridge/ImGuiBackendBridge.md) - `ImGuiBackendBridge.h`ImGui 与 D3D12 之间的桥接层。
- [ImGuiSession](ImGuiSession/ImGuiSession.md) - `ImGuiSession.h`ImGui 会话生命周期。
- [PanelChrome](PanelChrome/PanelChrome.md) - `PanelChrome.h`,面板窗口 / 工具栏 / 内容区 RAII 外壳。
- [PopupState](PopupState/PopupState.md) - `PopupState.h`,延迟弹窗与目标型弹窗状态。
- [PropertyGrid](PropertyGrid/PropertyGrid.md) - `PropertyGrid.h`Inspector 级属性编辑入口。
- [PropertyLayout](PropertyLayout/PropertyLayout.md) - `PropertyLayout.h`,属性行几何布局层。
- [ScalarControls](ScalarControls/ScalarControls.md) - `ScalarControls.h`,标量属性控件。
- [SceneStatusWidget](SceneStatusWidget/SceneStatusWidget.md) - `SceneStatusWidget.h`,场景状态部件。
- [SplitterChrome](SplitterChrome/SplitterChrome.md) - `SplitterChrome.h`,分隔条交互与绘制。
- [StyleTokens](StyleTokens/StyleTokens.md) - `StyleTokens.h`Editor UI 设计 token 中心。
- [TreeView](TreeView/TreeView.md) - `TreeView.h`,树视图共享基础设施。
- [VectorControls](VectorControls/VectorControls.md) - `VectorControls.h`,向量属性控件。
- [Widgets](Widgets/Widgets.md) - `Widgets.h`,资产卡片、面包屑、组件折叠段等通用 widget。
## 当前实现边界
- 这是 Editor 应用层 UI不是运行时 `engine/include/XCEngine` 风格的公共引擎 API。
- 当前很多接口仍然直接暴露 ImGui 类型,如 `ImDrawList``ImVec2``ImGuiID`
- 自定义 dock 标签栏、树视图和图标系统都明显面向当前编辑器产品形态,而不是通用 GUI 框架。
## 相关文档
- [Editor 模块](../Editor.md)
- [Application](../Application/Application.md)
- [Platform](../Platform/Platform.md)
- [Editor](../Editor.md)
- [Layout](../Layout/Layout.md)
- [ComponentEditors](../ComponentEditors/ComponentEditors.md)
- [Editor Architecture And Workflow](../../../_guides/Editor/Editor-Architecture-And-Workflow.md)

View File

@@ -2,49 +2,110 @@
**命名空间**: `XCEngine::Editor`
**类型**: `class + enum class`
**类型**: `class`
**源文件**: `editor/src/panels/HierarchyPanel.h`
**描述**: 层级面板,负责显示场景对象树、搜索、排序、拖放层级调整以及内联重命名
**描述**: 编辑器场景层级面板,负责绘制根实体树、处理选择与重命名事件,并把上下文菜单与拖放操作路由给 Action 层
## 概述
`HierarchyPanel` 是当前编辑器里交互最丰富的面板之一
`HierarchyPanel` 是当前编辑器里最典型的“视图层面板”:它自己拥有 UI 状态,但不直接承担场景编辑命令实现。它的主要任务是把当前场景对象以树形方式呈现出来,并把用户交互转换成选择、重命名、右键菜单和拖放请求
当前承载的功能包括
当前实现,这个面板主要负责
- 订阅选择变化重命名请求事件
- 层级树渲染
- 搜索过滤
- 排序选项
- 拖拽层级调整
- 右键上下文菜单
- 内联重命名
- 订阅选择变化与外部重命名请求事件
- 读取 `ISceneManager` 提供的根实体列表。
- 基于 `UI::TreeView` 递归绘制场景对象树。
- 维护内联重命名状态。
- 将选择、右键菜单、拖放等行为委托给 `Actions::HierarchyActionRouter` 一侧的 helper。
头文件里还定义了:
这和商业引擎编辑器的分层很一致Hierarchy 面板负责“看见和触发”,而不是直接“改数据和执行业务命令”。
- `enum class SortMode { Name, ComponentCount, TransformFirst }`
## 当前实现行为
## 当前实现说明
`editor/src/panels/HierarchyPanel.cpp` 的实现:
- `OnAttach()` 会订阅 `SelectionChangedEvent``EntityRenameRequestedEvent`
- 搜索过滤当前按名字子串匹配,并递归检查子节点。
- 排序支持:
- `Name`
- `ComponentCount`
- `TransformFirst`
- 双击节点会进入重命名状态
- 背景点击、右键菜单和拖放逻辑主要委托给 `Actions`
- `OnAttach()` 会订阅
- `SelectionChangedEvent`
- `EntityRenameRequestedEvent`
- `OnDetach()` 会正确取消订阅并清零 handler id。
- `Render()` 会:
- 应用 `HierarchyInspectorPanelBackgroundColor()`
- 通过 `PanelWindowScope` / `PanelContentScope` 建立标准面板外壳
- 调用 `Actions::ObserveFocusedActionRoute(...)` 将当前焦点路由标记为 `Hierarchy`
-`ISceneManager` 读取根实体并逐个递归渲染。
- 在树绘制完成后处理背景点击、背景右键菜单、实体右键菜单和根级 drop target。
## 当前实现边界
## 节点绘制模型
- 当前排序和过滤都在 UI 渲染阶段直接对实体列表做处理。
- `TransformFirst` 排序实际上是“有 Transform 的对象优先”,而不是更复杂的 transform-aware hierarchy order。
当前的节点绘制已经不再是旧版本那种局部自定义树,而是完全建立在 `UI::TreeView` 之上:
- 每个实体节点都会构造一个 `UI::TreeNodeDefinition`
- `selected` 状态来自 `ISelectionManager::IsSelected(...)`
- `leaf` 状态来自 `gameObject->GetChildCount() == 0`
- `persistenceKey` 使用 `gameObject->GetUUID()` 转成字符串,保证展开状态在同一面板实例中稳定。
- `style` 使用 `UI::HierarchyTreeStyle()`
- `prefix` 使用 `UI::NavigationTreePrefixWidth()``UI::DrawAssetIcon(..., AssetIconKind::GameObject)` 绘制对象图标。
这意味着当前 Hierarchy 的视觉和交互行为已经被统一纳入 Editor UI 基础设施,而不是面板自己手工维护整套树渲染细节。
## 交互行为
当前节点交互通过 `TreeNodeCallbacks` 接入:
- 单击左键:调用 `Actions::HandleHierarchySelectionClick(...)`,支持结合 `Ctrl` 执行多选。
- 右键:调用 `Actions::HandleHierarchyItemContextRequest(...)` 打开目标实体上下文菜单。
- 双击:调用 `BeginRename(...)` 进入内联重命名。
- 节点额外渲染阶段:调用 `Actions::BeginHierarchyEntityDrag(...)``Actions::AcceptHierarchyEntityDrop(...)` 接入拖放。
因此 `HierarchyPanel` 自己不直接执行“重排父子关系”之类操作,它只负责在正确的节点生命周期里提供 drop source / target 入口。
## 重命名模型
`HierarchyPanel` 当前用 `UI::InlineTextEditState<uint64_t, 256>` 保存重命名状态。
当前行为是:
- 外部若派发 `EntityRenameRequestedEvent`,面板会在 `OnRenameRequested(...)` 中找到实体并进入重命名。
- 若用户双击树节点,也会进入重命名。
- 编辑过程中:
- `Enter` 提交。
- `Escape` 取消。
- 输入框失焦且用户左键点击其他位置时提交。
- `CommitRename()` 最终通过 `Actions::CommitEntityRename(...)` 把改名请求发往动作层。
这类实现很像商业编辑器中的“inline rename controller”面板持有输入框状态但真正的实体改名仍然走统一命令路径。
## 生命周期与线程语义
- 该面板依赖有效的 `IEditorContext` 才能 attach、订阅事件并绘制。
- 事件订阅和 UI 绘制都应视为编辑器主线程行为。
- `m_treeState``m_renameState` 都属于面板实例私有状态,不会自动跨会话持久化。
## 设计说明
当前实现的一个正确方向是Hierarchy 面板只负责“把场景对象转成 Editor UI 语义”,而不是把所有业务都塞进面板里。
这样做的收益是:
- 树视图风格、图标前缀、展开行为可以和 `ProjectPanel` 共享。
- 选择、重命名、上下文菜单、拖放都能被 `Actions` 层集中治理。
- 面板本身更容易继续重构,比如后续加入搜索、过滤、批量操作时,不必推翻底层树控件。
## 当前限制
- 当前没有搜索、过滤、排序或大场景虚拟化能力。
- 展开状态只保存在 `UI::TreeViewState` 的内存态中,不会自动写入布局文件。
- 重命名输入框是替换式绘制,不是“树节点原位嵌入子控件”的更复杂实现。
- 目前只为每个节点提供统一的 `GameObject` 图标前缀,没有按组件类型做更细粒度图标区分。
## 相关文档
- [panels](../panels.md)
- [SelectionManager](../../Core/SelectionManager/SelectionManager.md)
- [SceneManager](../../Managers/SceneManager/SceneManager.md)
- [TreeView](../../UI/TreeView/TreeView.md)
- [BuiltInIcons](../../UI/BuiltInIcons/BuiltInIcons.md)
- [HierarchyActionRouter](../../Actions/HierarchyActionRouter/HierarchyActionRouter.md)
- [EditorEvents](../../Core/EditorEvents/EditorEvents.md)
- [ISceneManager](../../Core/ISceneManager/ISceneManager.md)
- [ISelectionManager](../../Core/ISelectionManager/ISelectionManager.md)

View File

@@ -6,36 +6,121 @@
**源文件**: `editor/src/panels/ProjectPanel.h`
**描述**: 项目面板,负责展示当前资产目录、面包屑导航、搜索、资产瓦片交互以及文件夹创建与上下文菜单
**描述**: 编辑器项目资源浏览面板,负责目录、面包屑、搜索、资源卡片和拖放交互的前端呈现
## 概述
`ProjectPanel` 当前是 [ProjectManager](../../Managers/ProjectManager/ProjectManager.md) 的主要 UI 前端。
`ProjectPanel` 当前 Editor 中最接近“资源浏览器”的面板。它本身不维护项目资源数据库,而是把 `IProjectManager` 暴露的数据模型转成一个典型的双栏浏览界面:
它负责:
- 左侧是目录树导航。
- 右侧是当前目录的资源浏览区。
- 顶部是搜索框与面包屑。
- 初始化项目浏览器
- 绘制工具栏与面包屑
- 绘制资产网格
- 搜索过滤
- 资产点击、打开、拖放和右键菜单
- 空白区域上下文菜单
- 新建文件夹弹窗
如果和 Unity 对照,可以把它理解成一个更轻量、当前聚焦于基础浏览和拖放的 Project Browser。
## 当前实现说明
## 当前实现行为
- `Initialize(projectPath)` 直接调用 `m_context->GetProjectManager().Initialize(projectPath)`
- `Render()` 中资产会按自适应列数排成网格。
- 搜索当前按名字子串过滤。
- 资产图标当前按 `isFolder` 区分 folder/file再配合 action/router 决定交互。
`editor/src/panels/ProjectPanel.cpp` 的实现:
## 当前实现边界
- `Initialize(projectPath)` 直接委托给 `m_context->GetProjectManager().Initialize(projectPath)`
- `Render()` 会:
- 建立标准 `PanelWindowScope`
- 把焦点动作路由标记为 `EditorActionRoute::Project`
- 先渲染 toolbar。
- 再创建左右分栏内容区。
- 使用 `UI::DrawSplitter(...)` 支持调整左侧导航栏宽度。
- 收尾时绘制“新建文件夹”对话框。
- 当前搜索是前端过滤,不是索引搜索。
- 资产预览目前还是轻量瓦片,不是完整导入数据库浏览器。
当前导航栏宽度会被显式夹紧在一个合理范围内:
- 最小值来自 `ProjectNavigationMinWidth()`
- 右侧浏览区最小宽度来自 `ProjectBrowserMinWidth()`
这说明当前实现已经从一开始就按“可调整工作区”的编辑器思路组织,而不是固定死尺寸。
## 左侧目录树
左侧目录树是当前 `ProjectPanel` 最典型的共享 UI 基建使用者之一:
- 根节点来自 `IProjectManager::GetRootFolder()`
- 当前目录来自 `IProjectManager::GetCurrentFolder()`
- 每个目录节点都通过 `UI::DrawTreeNode(...)` 绘制。
- 节点样式使用 `UI::ProjectFolderTreeStyle()`
- 节点前缀使用 `UI::DrawAssetIcon(..., AssetIconKind::Folder)`
- `defaultOpen` 会根据 `IsCurrentTreeBranch(...)` 判断当前目录是否位于该目录分支下。
- 展开状态保存在 `m_folderTreeState` 中。
用户单击树节点时,会立即调用 `manager.NavigateToFolder(folder)` 完成导航;右键则通过 `Actions::HandleProjectItemContextRequest(...)` 打开对应上下文菜单。
## 右侧浏览区
右侧浏览区分成两层:
- Header显示面包屑导航并在底部画一条 divider。
- Body以资源网格形式显示当前目录下通过搜索过滤后的条目。
资源卡片当前通过 `UI::DrawAssetTile(...)` 绘制,并根据 `item->isFolder` 区分:
- `Folder` 图标
- `File` 图标
交互结果统一收集到 `AssetItemInteraction` 中,再在循环结束后统一处理。这种写法有一个很实用的好处:可以避免在绘制过程中直接修改当前迭代容器或导航状态,让即时模式 UI 代码更稳定。
## 搜索与导航
当前搜索实现比较明确,也需要在文档里写清楚:
- 搜索框位于 toolbar 右侧。
- 搜索只对 `manager.GetCurrentItems()` 返回的当前目录条目生效。
- 匹配逻辑是大小写不敏感的子串匹配。
- 当前没有全文索引、标签过滤或类型过滤。
面包屑则通过 `UI::DrawToolbarBreadcrumbs(...)` 实现,点击非当前段会调用 `manager.NavigateToIndex(index)`
## 拖放与上下文菜单
当前 `ProjectPanel` 并不自己实现拖放协议和命令执行,而是按职责分层调用其他模块:
- `Actions::BeginProjectAssetDrag(item, iconKind)` 负责发起拖拽源。
- `Actions::AcceptProjectAssetDropPayload(item)` 负责检测是否有资源被拖到当前目标上。
- 若确实发生移动,则调用 `Commands::MoveAssetToFolder(...)`
- 右键菜单与空白区域菜单由 `Actions::*ContextPopup(...)` 系列 helper 负责绘制。
这种组织方式和 `HierarchyPanel` 一样,遵循的是“面板负责展示与采样,动作层 / 命令层负责实际编辑行为”的编辑器架构。
## 生命周期与线程语义
- 面板本身持有的是 UI 状态:搜索文本、导航栏宽度、目录树展开状态、弹窗状态。
- 真实项目数据由 `IProjectManager` 持有。
- 整套逻辑应视为编辑器 UI 主线程代码。
## 设计说明
当前 `ProjectPanel` 的设计方向是合理的,因为它先把“资源浏览器”拆成几个稳定的基础体验单元:
- 分栏布局
- 目录树
- 面包屑
- 资源网格
- 拖放与上下文菜单
这样做的好处是,后续无论是加缩略图、资源导入器状态、筛选器还是收藏夹,都可以在现有骨架上演进,而不是推倒重来。
## 当前限制
- 搜索只在当前目录里做前端子串过滤,不是全项目索引搜索。
- 当前没有资源缩略图生成或异步预览系统,图标仍以 `BuiltInIcons` 和简单卡片为主。
- 文件树展开状态只保存在内存中,不会自动跨重启恢复。
- 拖放移动是直接命令式处理,没有更复杂的批处理或事务预览。
## 相关文档
- [panels](../panels.md)
- [IProjectManager](../../Core/IProjectManager/IProjectManager.md)
- [ProjectManager](../../Managers/ProjectManager/ProjectManager.md)
- [AssetItem](../../Core/AssetItem/AssetItem.md)
- [ProjectActionRouter](../../Actions/ProjectActionRouter/ProjectActionRouter.md)
- [ProjectCommands](../../Commands/ProjectCommands/ProjectCommands.md)
- [TreeView](../../UI/TreeView/TreeView.md)
- [BuiltInIcons](../../UI/BuiltInIcons/BuiltInIcons.md)
- [SplitterChrome](../../UI/SplitterChrome/SplitterChrome.md)

View File

@@ -4,43 +4,125 @@
**类型**: `module`
**描述**: 渲染硬件抽象层及其后端
**描述**: 提供渲染硬件抽象层、后端选择入口、资源与命令提交接口,以及跨后端共享的描述符和枚举定义
## 概览
该目录与 `XCEngine/RHI` 对应的 public headers 保持平行,用于承载唯一的 canonical API 文档入口。
`XCEngine::RHI` 是当前引擎渲染栈里最靠近图形 API 的一层。它的职责不是提供高层渲染功能,而是把 D3D12、OpenGL 等后端收敛到一组共同接口之下:
## 子目录
- [RHIFactory](RHIFactory/RHIFactory.md) 负责选择并创建具体后端设备。
- [RHIDevice](RHIDevice/RHIDevice.md) 是核心抽象入口,承担“设备对象 + 资源工厂”的双重职责。
- [RHICommandQueue](RHICommandQueue/RHICommandQueue.md) 和 [RHICommandList](RHICommandList/RHICommandList.md) 负责提交与记录 GPU 工作。
- [RHITypes](RHITypes/RHITypes.md) 和 [RHIEnums](RHIEnums/RHIEnums.md) 定义跨后端共享的描述符语言。
- [D3D12](D3D12/D3D12.md)
- [OpenGL](OpenGL/OpenGL.md)
如果拿商用引擎做类比,这层更接近 Unreal 风格的低层 RHI / render backend而不是 Unity 面向 gameplay 的公共渲染 API。Unity 普通业务层一般不会直接碰设备、命令队列、资源视图;而 XCEngine 当前把这些对象直接暴露给引擎上层、测试和后端验证代码,用来支撑渲染系统构建、后端 bring-up 和图形测试。
## 头文件
## 当前架构理解
- [RHIBuffer](RHIBuffer/RHIBuffer.md) - `RHIBuffer.h`
- [RHICapabilities](RHICapabilities/RHICapabilities.md) - `RHICapabilities.h`
- [RHICommandList](RHICommandList/RHICommandList.md) - `RHICommandList.h`
- [RHICommandQueue](RHICommandQueue/RHICommandQueue.md) - `RHICommandQueue.h`
- [RHIDescriptorPool](RHIDescriptorPool/RHIDescriptorPool.md) - `RHIDescriptorPool.h`
- [RHIDescriptorSet](RHIDescriptorSet/RHIDescriptorSet.md) - `RHIDescriptorSet.h`
- [RHIDevice](RHIDevice/RHIDevice.md) - `RHIDevice.h`
- [RHIEnums](RHIEnums/RHIEnums.md) - `RHIEnums.h`
- [RHIFactory](RHIFactory/RHIFactory.md) - `RHIFactory.h`
- [RHIFence](RHIFence/RHIFence.md) - `RHIFence.h`
- [RHIFramebuffer](RHIFramebuffer/RHIFramebuffer.md) - `RHIFramebuffer.h`
- [RHIPipelineLayout](RHIPipelineLayout/RHIPipelineLayout.md) - `RHIPipelineLayout.h`
- [RHIPipelineState](RHIPipelineState/RHIPipelineState.md) - `RHIPipelineState.h`
- [RHIRenderPass](RHIRenderPass/RHIRenderPass.md) - `RHIRenderPass.h`
- [RHIResource](RHIResource/RHIResource.md) - `RHIResource.h`
- [RHIResourceView](RHIResourceView/RHIResourceView.md) - `RHIResourceView.h`
- [RHISampler](RHISampler/RHISampler.md) - `RHISampler.h`
- [RHIScreenshot](RHIScreenshot/RHIScreenshot.md) - `RHIScreenshot.h`
- [RHIShader](RHIShader/RHIShader.md) - `RHIShader.h`
- [RHISwapChain](RHISwapChain/RHISwapChain.md) - `RHISwapChain.h`
- [RHITexture](RHITexture/RHITexture.md) - `RHITexture.h`
- [RHITypes](RHITypes/RHITypes.md) - `RHITypes.h`
当前源码呈现出的主链路大致是:
1. 通过 [RHIFactory](RHIFactory/RHIFactory.md) 选择后端。
2. 通过 [RHIDevice](RHIDevice/RHIDevice.md) 初始化设备并查询 [RHICapabilities](RHICapabilities/RHICapabilities.md)。
3.`RHIDevice` 创建队列、命令列表、buffer、texture、render pass、pipeline state、descriptor pool / set 等对象。
4. 在 [RHICommandList](RHICommandList/RHICommandList.md) 上录制状态设置、clear、copy、draw、dispatch。
5. 通过 [RHICommandQueue](RHICommandQueue/RHICommandQueue.md) 提交并用 [RHIFence](RHIFence/RHIFence.md) 同步。
这套设计的好处是:
- 对接新后端时,上层接口面比较稳定。
- 单元测试和集成测试可以直接验证抽象层行为。
- 渲染模块可以在这一层之上继续搭更高层的 renderer、frame graph 或材质系统。
代价也很明确:
- 接口比较显式,样板代码多。
- 抽象层并不完全“纯净”,仍能看到 D3D12 / OpenGL 风格痕迹。
- 当前版本大量对象仍采用裸指针所有权,文档必须把生命周期约束写清楚。
## 后端可用性
按当前源码而不是设计愿景来看,`RHIFactory` 的真实后端可用性是:
- `D3D12`: 当前工厂始终可创建,是默认最完整的桌面后端之一。
- `OpenGL`: 仅在编译时定义 `XCENGINE_SUPPORT_OPENGL` 时可创建。
- `Vulkan`: 仅在编译时定义 `XCENGINE_SUPPORT_VULKAN` 时可创建。
- `Metal`: `RHIType` 中已有枚举项,但当前工厂直接返回 `nullptr`
这说明 `RHIType` 代表的是“抽象层计划支持的后端集合”,不等于“当前构建一定可用的后端集合”。
## 所有权与生命周期
这是 `RHI` 模块里最重要的使用约定之一。
当前抽象层大量 API 都返回裸指针,包括:
- `RHIFactory::CreateRHIDevice()`
- `RHIDevice::CreateBuffer()` / `CreateTexture()` / `CreateCommandList()` / `CreateCommandQueue()`
- descriptor pool、descriptor set、render pass、framebuffer、sampler、fence、resource view 等
按当前测试和调用模式,推荐把它们理解为:
1. 由创建者拥有返回对象。
2. 使用结束前先调用对象自己的 `Shutdown()`
3. 然后再 `delete`
也就是说,这一层目前不是 `Core::Ref` / RAII 优先的风格,而是更接近底层图形资源包装常见的“显式关闭 + 显式释放”模式。
## 设计现实与实现边界
当前 `RHI` 抽象层已经能表达核心 GPU 对象,但它还不是一个完全收敛的、无后端泄漏的终态接口:
- [RHITypes](RHITypes/RHITypes.md) 里既有通用描述符,也有明显偏 D3D12 的结构名,例如 `RootSignatureDesc``DescriptorHeapDesc``CommandAllocatorDesc`
- [RHICommandList](RHICommandList/RHICommandList.md) 同时暴露了 render pass、render target、descriptor set、copy、draw、dispatch 等多种路径,混合了不同代际的抽象思路。
- [RHICommandQueue](RHICommandQueue/RHICommandQueue.md) 的 `ExecuteCommandLists()` 采用 `void**`,说明抽象层仍带有一定后端适配痕迹。
- `Metal` 仍是占位,`Vulkan` 目录也还在逐步补全文档。
这并不意味着设计错误。对一个正在演进的商业级引擎来说,更现实的路径通常就是先把“能支撑后端实现和测试”的抽象做出来,再逐步收敛接口一致性。
## 后端目录
- [D3D12](D3D12/D3D12.md) - Direct3D 12 后端实现。
- [OpenGL](OpenGL/OpenGL.md) - OpenGL 后端实现。
- `Vulkan` - 头文件目录已存在,当前 canonical 文档仍在补建中。
## 顶层头文件
- [RHIBuffer](RHIBuffer/RHIBuffer.md) - `RHIBuffer.h`buffer 抽象资源接口。
- [RHICapabilities](RHICapabilities/RHICapabilities.md) - `RHICapabilities.h`,设备能力与限制信息。
- [RHICommandList](RHICommandList/RHICommandList.md) - `RHICommandList.h`,命令录制接口。
- [RHICommandQueue](RHICommandQueue/RHICommandQueue.md) - `RHICommandQueue.h`,命令提交与同步接口。
- [RHIDescriptorPool](RHIDescriptorPool/RHIDescriptorPool.md) - `RHIDescriptorPool.h`descriptor set 分配池。
- [RHIDescriptorSet](RHIDescriptorSet/RHIDescriptorSet.md) - `RHIDescriptorSet.h`,资源绑定集合。
- [RHIDevice](RHIDevice/RHIDevice.md) - `RHIDevice.h`,抽象设备与对象工厂。
- [RHIEnums](RHIEnums/RHIEnums.md) - `RHIEnums.h`,跨后端共享枚举。
- [RHIFactory](RHIFactory/RHIFactory.md) - `RHIFactory.h`,后端选择入口。
- [RHIFence](RHIFence/RHIFence.md) - `RHIFence.h`GPU 同步原语。
- [RHIFramebuffer](RHIFramebuffer/RHIFramebuffer.md) - `RHIFramebuffer.h`,帧缓冲对象。
- [RHIPipelineLayout](RHIPipelineLayout/RHIPipelineLayout.md) - `RHIPipelineLayout.h`,资源绑定布局。
- [RHIPipelineState](RHIPipelineState/RHIPipelineState.md) - `RHIPipelineState.h`,图形/计算管线状态。
- [RHIRenderPass](RHIRenderPass/RHIRenderPass.md) - `RHIRenderPass.h`render pass 描述与句柄。
- [RHIResource](RHIResource/RHIResource.md) - `RHIResource.h`,资源基类接口。
- [RHIResourceView](RHIResourceView/RHIResourceView.md) - `RHIResourceView.h`,资源视图接口。
- [RHISampler](RHISampler/RHISampler.md) - `RHISampler.h`,采样器对象。
- [RHIScreenshot](RHIScreenshot/RHIScreenshot.md) - `RHIScreenshot.h`,截图接口。
- [RHIShader](RHIShader/RHIShader.md) - `RHIShader.h`shader 抽象。
- [RHISwapChain](RHISwapChain/RHISwapChain.md) - `RHISwapChain.h`,窗口展示交换链。
- [RHITexture](RHITexture/RHITexture.md) - `RHITexture.h`texture 抽象资源接口。
- [RHITypes](RHITypes/RHITypes.md) - `RHITypes.h`,共享描述符与辅助结构。
## 推荐阅读顺序
1. 先读 [Devices, Queues, Command Lists, And Resource Creation](../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md),建立正确心智模型。
2. 再读 [RHIFactory](RHIFactory/RHIFactory.md) 和 [RHIDevice](RHIDevice/RHIDevice.md),理解后端选择和对象创建。
3. 然后读 [RHICommandQueue](RHICommandQueue/RHICommandQueue.md) 与 [RHICommandList](RHICommandList/RHICommandList.md),理解录制与提交。
4. 最后再查 [RHITypes](RHITypes/RHITypes.md)、[RHIEnums](RHIEnums/RHIEnums.md)、[RHICapabilities](RHICapabilities/RHICapabilities.md) 这些通用支持页。
## 相关指南
- [Devices, Queues, Command Lists, And Resource Creation](../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md) - 解释当前 RHI 的真实使用路径、生命周期约束以及和 Unity / 商用引擎常见设计的对应关系。
## 相关文档
- [上级目录](../XCEngine.md)
- [Rendering](../Rendering/Rendering.md)
- [API 总索引](../../main.md)

View File

@@ -6,39 +6,132 @@
**头文件**: `XCEngine/RHI/RHIBuffer.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIBuffer` public API
**描述**: 抽象 buffer 资源接口,负责 CPU 写入、尺寸与 stride 查询、资源状态记录以及调试命名
## 概述
## 角色概述
`RHIBuffer.h``XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。
`RHIBuffer`[RHIResource](../RHIResource/RHIResource.md) 的具体资源类型之一,用来承载:
## 声明概览
- vertex buffer
- index buffer
- constant buffer
- readback buffer
- 间接参数或其他 buffer 形态
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIBuffer` | `class` | 继承自 `RHIResource` 的公开声明。 |
当前 buffer 的“用途种类”主要由 `BufferType` 表达,而不是靠不同类名再拆成一堆具体 buffer 类型。
## 公共方法
## 创建方式
| 方法 | 描述 |
|------|------|
| [~RHIBuffer()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Map](Map.md) | 公开方法,详见头文件声明。 |
| [Unmap](Unmap.md) | 公开方法,详见头文件声明。 |
| [SetData](SetData.md) | 设置相关状态或配置。 |
| [GetSize](GetSize.md) | 获取相关状态或对象。 |
| [GetBufferType](GetBufferType.md) | 获取相关状态或对象。 |
| [SetBufferType](SetBufferType.md) | 设置相关状态或配置。 |
| [GetStride](GetStride.md) | 获取相关状态或对象。 |
| [SetStride](SetStride.md) | 设置相关状态或配置。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
| [GetState](GetState.md) | 获取相关状态或对象。 |
| [SetState](SetState.md) | 设置相关状态或配置。 |
| [GetName](GetName.md) | 获取相关状态或对象。 |
| [SetName](SetName.md) | 设置相关状态或配置。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
`RHIBuffer` 本身不是直接构造的,通常由 [RHIDevice](../RHIDevice/RHIDevice.md) 通过 [CreateBuffer](../RHIDevice/CreateBuffer.md) 创建,输入参数来自 [RHITypes](../RHITypes/RHITypes.md) 中的 `BufferDesc`
- `size`
- `stride`
- `bufferType`
- `flags`
## 当前接口分组
### CPU 访问
- [Map](Map.md)
- [Unmap](Unmap.md)
- [SetData](SetData.md)
这是当前 buffer 抽象里最值得注意的一组接口。它说明当前 `RHI` 不是把 buffer 完全当成“只能由 copy/transfer 写入的纯 GPU 资源”,而是允许上层直接进行 CPU 写入。
### 元数据
- [GetSize](GetSize.md)
- [GetBufferType](GetBufferType.md)
- [SetBufferType](SetBufferType.md)
- [GetStride](GetStride.md)
- [SetStride](SetStride.md)
- [GetName](GetName.md)
- [SetName](SetName.md)
### 资源基类能力
- [GetNativeHandle](GetNativeHandle.md)
- [GetState](GetState.md)
- [SetState](SetState.md)
- [Shutdown](Shutdown.md)
## `Map()` / `SetData()` 应该怎么理解
从当前 D3D12 / OpenGL / Vulkan 实现来看,`Map()` 都是直接返回后端可写入的 CPU 指针:
- D3D12 映射底层 `ID3D12Resource`
- OpenGL 使用 `glMapBuffer`
- Vulkan 映射底层 `VkDeviceMemory`
这意味着 `Map()` 在当前代码库里是真实可用能力,而不是占位接口。
但仍然要注意两点:
### 1. 抽象接口没有显式暴露内存访问策略
`RHIBuffer` 自己没有告诉你它位于 upload heap、device local 还是别的内存类型。具体策略由后端在 `CreateBuffer()` 时根据 `BufferType` 决定。
例如当前源码里:
- D3D12 对 `Constant` / `Vertex` / `Index` 倾向于使用 `UPLOAD` heap
- Vulkan 当前 `CreateBuffer()` 走的是 host-visible / host-coherent 内存
所以文档不能把它说成“所有 buffer 都适合任意频率 CPU 映射”,只能说当前常见路径支持映射和写入。
### 2. `Map()` 返回的指针应视为临时访问窗口
最安全的做法仍然是:
1. `Map()`
2. 写入数据
3. `Unmap()`
而不是长期持有裸指针跨帧使用。
## 测试体现出的真实语义
`tests/RHI/unit/test_buffer.cpp` 已经覆盖了这些行为:
- `Map()` / `Unmap()` 可用
- `SetData()` 可写入整块数据
- `GetSize()``GetStride()` 返回创建时的元数据
- `GetBufferType()` / `SetBufferType()` 可读写类型标记
- `GetState()` / `SetState()` 支持资源状态跟踪
- `SetName()` / `GetName()` 支持调试命名
- `GetNativeHandle()` 创建成功后通常非空
同时command list 与集成测试也在真实使用 buffer
- 创建 vertex / index buffer
- 调用 `SetData()`
- 再通过 [RHIResourceView](../RHIResourceView/RHIResourceView.md) 绑定到 draw path
## 当前设计理解
把 buffer 统一放进同一个接口,有几个工程上的好处:
- 渲染系统可以用一套创建入口处理大多数线性 GPU 数据
- 后端可以自行决定底层内存策略
- 测试更容易覆盖共同行为
代价是抽象层不会自动告诉你“这个 buffer 最适合做什么”,调用方仍然需要结合 `BufferType` 和使用场景自己判断。
## 生命周期
`RHIBuffer` 由 [RHIDevice](../RHIDevice/RHIDevice.md) 创建,并以裸指针返回。当前推荐流程是:
1. 创建 buffer
2. 可选地 `Map()` / `SetData()`
3. 通过 view 或其他路径参与渲染
4. 调用 [Shutdown](Shutdown.md)
5. `delete`
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIResource](../RHIResource/RHIResource.md)
- [RHIResourceView](../RHIResourceView/RHIResourceView.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [API 总索引](../../../main.md)

View File

@@ -6,56 +6,117 @@
**头文件**: `XCEngine/RHI/RHICapabilities.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHICapabilities` public API
**描述**: 描述当前设备支持的功能开关、资源上限、光栅限制和版本信息,是 renderer 初始化阶段的主要特性查询入口
## 概述
## 角色概述
`RHICapabilities.h``XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。
`RHICapabilities`[RHIDevice](../RHIDevice/RHIDevice.md) 暴露给上层的能力快照。它的定位很明确:
## 声明概览
- 在 renderer 启动时决定某条渲染路径是否可用
- 给资源系统提供尺寸和数量上限
- 为调试与日志输出提供设备能力摘要
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHICapabilities` | `struct` | 头文件中的公开声明。 |
这类结构在商业引擎里很常见,因为高层 renderer 往往需要先读一遍“当前设备能不能做某件事”,再决定启用哪些 feature set。
## 结构体成员
## 字段分组
| 成员 | 类型 | 描述 | 默认值 |
|------|------|------|--------|
| `bSupportsRayTracing` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsMeshShaders` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsExplicitMultiThreading` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsGeometryShaders` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsTessellation` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsComputeShaders` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsDepthBoundsTest` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsAlphaToCoverage` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsIndependentBlend` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsLogicOps` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsMultiViewport` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsConservativeRasterization` | `bool` | 结构体公开字段。 | `false` |
| `bSupportsProgrammableSamplePositions` | `bool` | 结构体公开字段。 | `false` |
| `maxTexture2DSize` | `uint32_t` | 结构体公开字段。 | `0` |
| `maxTexture3DSize` | `uint32_t` | 结构体公开字段。 | `0` |
| `maxTextureCubeSize` | `uint32_t` | 结构体公开字段。 | `0` |
| `maxRenderTargets` | `uint32_t` | 结构体公开字段。 | `0` |
| `maxViewports` | `uint32_t` | 结构体公开字段。 | `0` |
| `maxVertexAttribs` | `uint32_t` | 结构体公开字段。 | `0` |
| `maxConstantBufferSize` | `uint32_t` | 结构体公开字段。 | `0` |
| `maxAnisotropy` | `uint32_t` | 结构体公开字段。 | `0` |
| `maxColorAttachments` | `uint32_t` | 结构体公开字段。 | `0` |
| `minSmoothedLineWidth` | `float` | 结构体公开字段。 | `1.0f` |
| `maxSmoothedLineWidth` | `float` | 结构体公开字段。 | `1.0f` |
| `minPointSize` | `float` | 结构体公开字段。 | `1.0f` |
| `maxPointSize` | `float` | 结构体公开字段。 | `1.0f` |
| `maxPointSizeAA` | `float` | 结构体公开字段。 | `1.0f` |
| `maxLineWidth` | `float` | 结构体公开字段。 | `1.0f` |
| `maxLineWidthAA` | `float` | 结构体公开字段。 | `1.0f` |
| `majorVersion` | `int` | 结构体公开字段。 | `0` |
| `minorVersion` | `int` | 结构体公开字段。 | `0` |
| `shaderModel` | `std::string` | 结构体公开字段。 | - |
### 功能开关
- `bSupportsRayTracing`
- `bSupportsMeshShaders`
- `bSupportsExplicitMultiThreading`
- `bSupportsGeometryShaders`
- `bSupportsTessellation`
- `bSupportsComputeShaders`
- `bSupportsDepthBoundsTest`
- `bSupportsAlphaToCoverage`
- `bSupportsIndependentBlend`
- `bSupportsLogicOps`
- `bSupportsMultiViewport`
- `bSupportsConservativeRasterization`
- `bSupportsProgrammableSamplePositions`
### 资源与渲染上限
- `maxTexture2DSize`
- `maxTexture3DSize`
- `maxTextureCubeSize`
- `maxRenderTargets`
- `maxViewports`
- `maxVertexAttribs`
- `maxConstantBufferSize`
- `maxAnisotropy`
- `maxColorAttachments`
### 线宽、点大小相关限制
- `minSmoothedLineWidth`
- `maxSmoothedLineWidth`
- `minPointSize`
- `maxPointSize`
- `maxPointSizeAA`
- `maxLineWidth`
- `maxLineWidthAA`
### 版本与 shader 模型
- `majorVersion`
- `minorVersion`
- `shaderModel`
## 当前实现语义
这是一个纯 public struct带默认值没有 getter / setter也没有更复杂的 feature query 层级。也就是说:
- 默认构造时,布尔项基本都是 `false`
- 数值项基本是 `0``1.0f`
- 真正的能力数据由具体后端设备在初始化时填充
这种设计非常直接,适合当前阶段的引擎实现,但也意味着它不是那种高度抽象、可按扩展名逐项查询的 capability database。
## 测试揭示出的保证边界
`tests/RHI/unit/test_capabilities.cpp` 很适合拿来理解“这份结构当前到底保证到什么程度”。
现有测试主要验证的是:
- 常见纹理尺寸、render target 数量、viewport 数量等至少大于某些下限
- `majorVersion` / `minorVersion` 非负
- D3D12 下 `shaderModel` 非空
- 各项 line width / point size 数据不小于基础值
- `maxConstantBufferSize` 在一个合理范围内
这说明当前 `RHICapabilities` 更像“运行时可用的设备特征摘要”,而不是一份经过严格标准化校验的完整硬件契约。
## 怎么正确使用
推荐把它用于:
- 启动时 feature gating
- 选择 render path
- 做日志输出和诊断
- 对资源尺寸做防御性检查
不建议把它当成:
- 跨平台精确一致的能力数据库
- 替代后端专用 feature query 的唯一信息源
- 细粒度扩展发现系统
## 和商用引擎能力系统的关系
很多成熟引擎会把能力系统做得更分层,例如把 adapter info、feature support、format support、optional extensions 分开表示。当前 `RHICapabilities` 还没有走到那一步,它更像一个实用的第一层设备能力快照。
这对当前阶段反而是合理的:
- renderer 能快速消费
- 文档和测试更容易保持一致
- 后续如果要拆分也有基础数据模型可演进
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHITypes](../RHITypes/RHITypes.md)
- [Devices, Queues, Command Lists, And Resource Creation](../../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md)
- [API 总索引](../../../main.md)

View File

@@ -6,55 +6,167 @@
**头文件**: `XCEngine/RHI/RHICommandList.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHICommandList` public API
**描述**: 抽象命令录制接口负责记录资源状态切换、render pass、绑定状态、draw、dispatch、clear 和 copy 等 GPU 工作
## 概述
## 角色概述
`RHICommandList.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明
`RHICommandList`当前抽象层里最直接面向 GPU 录制语义的接口。按原生图形 API 的习惯来理解,它代表一段待提交的命令流,通常由 [RHIDevice](../RHIDevice/RHIDevice.md) 创建、由 [RHICommandQueue](../RHICommandQueue/RHICommandQueue.md) 执行
## 声明概览
和很多理想化的“最小 command buffer 抽象”不同,当前这份接口比较宽:
| 声明 | 类型 | 说明 |
|------|------|------|
| `DepthStencilState` | `struct` | 头文件中的公开声明。 |
| `BlendState` | `struct` | 头文件中的公开声明。 |
| `RHICommandList` | `class` | 头文件中的公开声明。 |
- 既有 draw 也有 dispatch
- 既有 render pass 路径也有直接 `SetRenderTargets()` 路径
- 既有 `SetPipelineState()`,也保留了 `SetShader()`
- 既覆盖 barrier / clear / copy也覆盖 descriptor set 绑定
这反映出当前抽象层更偏“支撑后端实现和测试的统一录制入口”,而不是已经极致收敛后的最终形态。
## 录制生命周期
按现有测试的真实使用顺序,最常见流程是:
1. 创建 `RHICommandList`
2. [Reset](Reset.md)
3. 录制状态、资源绑定和 draw / dispatch
4. [Close](Close.md)
5. 交给 [RHICommandQueue](../RHICommandQueue/RHICommandQueue.md) 提交
6. 使用结束后 [Shutdown](Shutdown.md) 并 `delete`
这和 D3D12 / Vulkan 一类显式 API 的工作流是一致的。
## 接口分组
### 同步与状态切换
- [TransitionBarrier](TransitionBarrier.md)
当前 barrier 接口接收的是 `RHIResourceView*`,而不是 `RHIResource*` 或更细粒度的 barrier 描述结构。这是一种相对简化、也带有当前实现痕迹的抽象选择。
### render pass 与附件绑定
- [BeginRenderPass](BeginRenderPass.md)
- [EndRenderPass](EndRenderPass.md)
- [SetRenderTargets](SetRenderTargets.md)
这说明当前接口同时保留了更现代的 render pass 语义和更传统的显式 render target 绑定语义。工程上应尽量在同一条渲染路径里保持一致,不要随意混用。
### 管线与 descriptor 绑定
- [SetShader](SetShader.md)
- [SetPipelineState](SetPipelineState.md)
- [SetGraphicsDescriptorSets](SetGraphicsDescriptorSets.md)
- [SetComputeDescriptorSets](SetComputeDescriptorSets.md)
- [SetPrimitiveTopology](SetPrimitiveTopology.md)
这里最值得注意的是 `SetShader()``SetPipelineState()` 并存,说明当前抽象并不是完全 PSO-only 的模型。
### 固定功能状态
- [SetViewport](SetViewport.md)
- [SetViewports](SetViewports.md)
- [SetScissorRect](SetScissorRect.md)
- [SetScissorRects](SetScissorRects.md)
- [SetStencilRef](SetStencilRef.md)
- [SetBlendFactor](SetBlendFactor.md)
### 资源绑定
- [SetVertexBuffers](SetVertexBuffers.md)
- [SetIndexBuffer](SetIndexBuffer.md)
现有测试里,这部分通常会先由 `RHIDevice` 创建 buffer再创建对应的 resource view最后绑定到 command list。
### 执行命令
- [Draw](Draw.md)
- [DrawIndexed](DrawIndexed.md)
- [Dispatch](Dispatch.md)
### clear、copy 与 native handle
- [Clear](Clear.md)
- [ClearRenderTarget](ClearRenderTarget.md)
- [ClearDepthStencil](ClearDepthStencil.md)
- [CopyResource](CopyResource.md)
- [GetNativeHandle](GetNativeHandle.md)
## 头文件中的辅助结构
`RHICommandList.h` 里还定义了两个结构:
- `DepthStencilState`
- `BlendState`
需要特别说明的是,这两个结构并不是当前主 pipeline 描述路径里最核心的状态结构。当前 `RHIPipelineState` / `GraphicsPipelineDesc` 更主要使用的是 [RHITypes](../RHITypes/RHITypes.md) 中的:
- `DepthStencilStateDesc`
- `BlendDesc`
所以这里更合理的理解是:`RHICommandList.h` 里的这两个结构代表了历史遗留或辅助性状态表达,而不是整个抽象层的唯一标准状态类型。
## 测试体现出的真实能力
`tests/RHI/unit/test_command_list.cpp` 对理解这份接口很重要。当前测试已经覆盖了:
- `Reset()` / `Close()`
- 拓扑、viewport、scissor 等基础状态设置
- 真实的 vertex / index buffer view 绑定
- `ClearRenderTarget()``ClearDepthStencil()`
- `TransitionBarrier()``CopyResource()`
- `BeginRenderPass()` / `EndRenderPass()`
- OpenGL 后端下的 descriptor set、framebuffer attachment 和 compute 路径验证
这说明 `RHICommandList` 不是纸面抽象,而是当前图形测试已经在直接依赖的一层。
## 当前设计边界
- 没有显式二级命令列表 / bundle / secondary command buffer 抽象。
- barrier 模型比较简化。
- shader 绑定模型和 pipeline 绑定模型并存,接口风格还在收敛中。
- render pass 路径和直接 render target 绑定路径并存。
这些边界在演进中的引擎里很常见。文档的职责不是替它美化,而是把这些事实讲清楚,帮助使用者减少误用。
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHICommandList()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [Reset](Reset.md) | 公开方法,详见头文件声明。 |
| [Close](Close.md) | 公开方法,详见头文件声明。 |
| [TransitionBarrier](TransitionBarrier.md) | 公开方法,详见头文件声明。 |
| [BeginRenderPass](BeginRenderPass.md) | 公开方法,详见头文件声明。 |
| [EndRenderPass](EndRenderPass.md) | 公开方法,详见头文件声明。 |
| [SetShader](SetShader.md) | 设置相关状态或配置。 |
| [SetPipelineState](SetPipelineState.md) | 设置相关状态或配置。 |
| [SetGraphicsDescriptorSets](SetGraphicsDescriptorSets.md) | 设置相关状态或配置。 |
| [SetComputeDescriptorSets](SetComputeDescriptorSets.md) | 设置相关状态或配置。 |
| [SetPrimitiveTopology](SetPrimitiveTopology.md) | 设置相关状态或配置。 |
| [SetViewport](SetViewport.md) | 设置相关状态或配置。 |
| [SetViewports](SetViewports.md) | 设置相关状态或配置。 |
| [SetScissorRect](SetScissorRect.md) | 设置相关状态或配置。 |
| [SetScissorRects](SetScissorRects.md) | 设置相关状态或配置。 |
| [SetRenderTargets](SetRenderTargets.md) | 设置相关状态或配置。 |
| [SetStencilRef](SetStencilRef.md) | 设置相关状态或配置。 |
| [SetBlendFactor](SetBlendFactor.md) | 设置相关状态或配置。 |
| [SetVertexBuffers](SetVertexBuffers.md) | 设置相关状态或配置。 |
| [SetIndexBuffer](SetIndexBuffer.md) | 设置相关状态或配置。 |
| [Draw](Draw.md) | 公开方法,详见头文件声明。 |
| [DrawIndexed](DrawIndexed.md) | 公开方法,详见头文件声明。 |
| [Clear](Clear.md) | 清空内部数据。 |
| [ClearRenderTarget](ClearRenderTarget.md) | 清空内部数据。 |
| [ClearDepthStencil](ClearDepthStencil.md) | 清空内部数据。 |
| [CopyResource](CopyResource.md) | 公开方法,详见头文件声明。 |
| [Dispatch](Dispatch.md) | 公开方法,详见头文件声明。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
- [Reset](Reset.md)
- [Close](Close.md)
- [TransitionBarrier](TransitionBarrier.md)
- [BeginRenderPass](BeginRenderPass.md)
- [EndRenderPass](EndRenderPass.md)
- [SetShader](SetShader.md)
- [SetPipelineState](SetPipelineState.md)
- [SetGraphicsDescriptorSets](SetGraphicsDescriptorSets.md)
- [SetComputeDescriptorSets](SetComputeDescriptorSets.md)
- [SetPrimitiveTopology](SetPrimitiveTopology.md)
- [SetViewport](SetViewport.md)
- [SetViewports](SetViewports.md)
- [SetScissorRect](SetScissorRect.md)
- [SetScissorRects](SetScissorRects.md)
- [SetRenderTargets](SetRenderTargets.md)
- [SetStencilRef](SetStencilRef.md)
- [SetBlendFactor](SetBlendFactor.md)
- [SetVertexBuffers](SetVertexBuffers.md)
- [SetIndexBuffer](SetIndexBuffer.md)
- [Draw](Draw.md)
- [DrawIndexed](DrawIndexed.md)
- [Clear](Clear.md)
- [ClearRenderTarget](ClearRenderTarget.md)
- [ClearDepthStencil](ClearDepthStencil.md)
- [CopyResource](CopyResource.md)
- [Dispatch](Dispatch.md)
- [GetNativeHandle](GetNativeHandle.md)
## 相关指南
- [Devices, Queues, Command Lists, And Resource Creation](../../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHICommandQueue](../RHICommandQueue/RHICommandQueue.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHIPipelineState](../RHIPipelineState/RHIPipelineState.md)
- [RHIRenderPass](../RHIRenderPass/RHIRenderPass.md)
- [RHITypes](../RHITypes/RHITypes.md)
- [API 总索引](../../../main.md)

View File

@@ -6,36 +6,99 @@
**头文件**: `XCEngine/RHI/RHICommandQueue.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHICommandQueue` public API
**描述**: 抽象命令提交队列,负责执行命令列表、管理 GPU 同步以及暴露基础队列信息
## 概述
## 角色概述
`RHICommandQueue.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。
`RHICommandQueue` 对应的是原生图形 API 里的提交队列概念。当前接口主要承担三类职责:
## 声明概览
- 提交命令列表
- 和 [RHIFence](../RHIFence/RHIFence.md) 协作完成 GPU 同步
- 暴露队列类型、帧索引和时间戳频率等队列级信息
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHICommandQueue` | `class` | 头文件中的公开声明。 |
在抽象层里,它位于 [RHIDevice](../RHIDevice/RHIDevice.md) 和 [RHICommandList](../RHICommandList/RHICommandList.md) 之间,是“录制好的 GPU 工作真正被执行”的入口。
## 当前接口语义
### 提交
- [ExecuteCommandLists](ExecuteCommandLists.md)
这是队列最核心的方法。但要注意,当前签名是 `ExecuteCommandLists(uint32_t count, void** lists)`,而不是 `RHICommandList**`。这说明抽象层在提交阶段仍保留了一定后端适配痕迹,接口类型安全性不算强。
### 同步
- [Signal](Signal.md)
- [Wait](Wait.md)
- [GetCompletedValue](GetCompletedValue.md)
- [WaitForIdle](WaitForIdle.md)
这组接口提供了最基础的 GPU timeline / fence 同步语义。
### 队列与帧信息
- [GetType](GetType.md)
- [GetTimestampFrequency](GetTimestampFrequency.md)
- [GetNativeHandle](GetNativeHandle.md)
- [WaitForPreviousFrame](WaitForPreviousFrame.md)
- [GetCurrentFrame](GetCurrentFrame.md)
其中 `WaitForPreviousFrame()``GetCurrentFrame()` 暗示当前抽象层除了通用队列语义,还兼顾了交换链驱动的 frame-loop 使用场景。
## 队列类型
队列类型由 [CommandQueueType](../RHIEnums/RHIEnums.md) 表达,当前枚举包括:
- `Direct`
- `Compute`
- `Copy`
但从当前测试覆盖和接口成熟度看,最常见、最稳定的仍是 `Direct` 路径。
## 生命周期与所有权
`RHICommandQueue` 通常由 [RHIDevice](../RHIDevice/RHIDevice.md) 创建,并以裸指针形式返回。和本模块其他对象一样,推荐使用模式是:
1. `RHIDevice::CreateCommandQueue()`
2. 使用队列提交命令或等待同步
3. `queue->Shutdown()`
4. `delete queue`
## 设计理解
从商业引擎实践看,把 queue 抽象单独暴露出来是合理的:
- 它让 renderer 可以明确区分“录制命令”和“提交命令”。
- 它为多队列或异步计算扩展保留了接口形状。
- 它便于测试在设备层之上直接验证提交和同步行为。
但当前实现仍然比较早期:
- 提交接口还不够类型安全。
- 队列抽象还没有扩展到更复杂的 submission batch、timeline semaphore 或跨队列调度模型。
- `WaitForPreviousFrame()` 这类接口说明它同时背负了一些具体 frame-loop 约定,而不是完全最小化的 queue 抽象。
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHICommandQueue()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [ExecuteCommandLists](ExecuteCommandLists.md) | 公开方法,详见头文件声明。 |
| [Signal](Signal.md) | 公开方法,详见头文件声明。 |
| [Wait](Wait.md) | 公开方法,详见头文件声明。 |
| [GetCompletedValue](GetCompletedValue.md) | 获取相关状态或对象。 |
| [WaitForIdle](WaitForIdle.md) | 公开方法,详见头文件声明。 |
| [GetType](GetType.md) | 获取相关状态或对象。 |
| [GetTimestampFrequency](GetTimestampFrequency.md) | 获取相关状态或对象。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
| [WaitForPreviousFrame](WaitForPreviousFrame.md) | 公开方法,详见头文件声明。 |
| [GetCurrentFrame](GetCurrentFrame.md) | 获取相关状态或对象。 |
- [ExecuteCommandLists](ExecuteCommandLists.md)
- [Signal](Signal.md)
- [Wait](Wait.md)
- [GetCompletedValue](GetCompletedValue.md)
- [WaitForIdle](WaitForIdle.md)
- [GetType](GetType.md)
- [GetTimestampFrequency](GetTimestampFrequency.md)
- [GetNativeHandle](GetNativeHandle.md)
- [WaitForPreviousFrame](WaitForPreviousFrame.md)
- [GetCurrentFrame](GetCurrentFrame.md)
## 相关指南
- [Devices, Queues, Command Lists, And Resource Creation](../../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [RHIFence](../RHIFence/RHIFence.md)
- [API 总索引](../../../main.md)

View File

@@ -6,32 +6,107 @@
**头文件**: `XCEngine/RHI/RHIDescriptorPool.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIDescriptorPool` public API
**描述**: descriptor set 分配池,负责按指定 heap 类型和容量创建、回收 `RHIDescriptorSet`
## 概述
## 角色概述
`RHIDescriptorPool.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。
`RHIDescriptorPool`当前资源绑定系统里的分配器入口。它的抽象目标比较清晰:
## 声明概览
- 上层先定义 descriptor pool 的类型和容量
- 再从 pool 中分配一个或多个 [RHIDescriptorSet](../RHIDescriptorSet/RHIDescriptorSet.md)
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIDescriptorPool` | `class` | 头文件中的公开声明。 |
从设计上看,它相当于在不同后端之间收敛:
- D3D12 风格的 descriptor heap / descriptor range 分配
- Vulkan 风格的 descriptor pool / descriptor set 分配
- OpenGL 当前实现中的绑定点管理
## `DescriptorPoolDesc` 的核心字段
当前 pool 描述来自 [RHITypes](../RHITypes/RHITypes.md) 中的 `DescriptorPoolDesc`,最重要的字段是:
- `type`
- `descriptorCount`
- `shaderVisible`
其中 `type` 对应 [DescriptorHeapType](../RHIEnums/RHIEnums.md)。
## 当前支持的池类型
现有测试已经覆盖了以下几种 pool 类型:
- `CBV_SRV_UAV`
- `Sampler`
- `RTV`
- `DSV`
这说明当前抽象层已经把常见资源视图和采样器池分开表达,而不是只做单一 descriptor arena。
## 当前使用路径
典型流程是:
1. 通过 [RHIDevice](../RHIDevice/RHIDevice.md) 创建 `RHIDescriptorPool`
2. 调用 [AllocateSet](AllocateSet.md) 传入 `DescriptorSetLayoutDesc`
3. 获得 [RHIDescriptorSet](../RHIDescriptorSet/RHIDescriptorSet.md)
4. 使用结束后销毁 set再关闭 pool
当前接口还提供 [FreeSet](FreeSet.md),说明 pool 设计时就考虑了 set 生命周期管理,而不是单纯一次性线性分配。
## 设计理解
这层抽象在商业引擎里很常见,因为 descriptor / binding 资源通常不是孤立对象,而是需要一个分配域:
- pool 负责容量和类型边界
- set 负责单次绑定内容
当前实现比较薄,但这是合理的第一步。它已经足够表达“这个 pass / material 需要从哪个类型的绑定池里拿一组 descriptor set”。
## 当前边界
当前 `RHIDescriptorPool` 没有直接暴露:
- 碎片率或剩余容量查询
- reset-all / frame allocator 语义
- 多线程分配保证
- 复杂回收策略
所以更准确的理解是:它是一个够用的基础分配接口,而不是完整的 descriptor allocator 子系统。
## 测试体现出的真实语义
`tests/RHI/unit/test_descriptor.cpp``test_descriptor_set.cpp` 表明:
- `GetType()``GetDescriptorCount()` 会反映创建时的 pool 配置
- `Shutdown()` 可以重复调用
- pool 创建成功后可连续 `AllocateSet()`
- 部分测试直接通过 `pool->FreeSet(set)` 回收 set
这说明它已经是 descriptor 相关测试的主要对象,而不是占位接口。
## 生命周期
`RHIDescriptorPool` 由 [RHIDevice](../RHIDevice/RHIDevice.md) 创建,使用完成后应:
1. 先释放或关闭从该 pool 分配出的 set
2. 调用 [Shutdown](Shutdown.md)
3. `delete`
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHIDescriptorPool()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Initialize](Initialize.md) | 初始化内部状态。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
| [GetDescriptorCount](GetDescriptorCount.md) | 获取相关状态或对象。 |
| [GetType](GetType.md) | 获取相关状态或对象。 |
| [AllocateSet](AllocateSet.md) | 公开方法,详见头文件声明。 |
| [FreeSet](FreeSet.md) | 公开方法,详见头文件声明。 |
- [Initialize](Initialize.md)
- [Shutdown](Shutdown.md)
- [GetNativeHandle](GetNativeHandle.md)
- [GetDescriptorCount](GetDescriptorCount.md)
- [GetType](GetType.md)
- [AllocateSet](AllocateSet.md)
- [FreeSet](FreeSet.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHIDescriptorSet](../RHIDescriptorSet/RHIDescriptorSet.md)
- [RHITypes](../RHITypes/RHITypes.md)
- [RHIEnums](../RHIEnums/RHIEnums.md)
- [API 总索引](../../../main.md)

View File

@@ -6,37 +6,124 @@
**头文件**: `XCEngine/RHI/RHIDescriptorSet.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIDescriptorSet` public API
**描述**: 资源绑定集合对象,负责把 resource view、sampler 和常量数据组织成一组可绑定描述
## 概述
## 角色概述
`RHIDescriptorSet.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明
`RHIDescriptorSet`当前抽象层里对“一组资源绑定”的统一表达。它通常不直接独立创建,而是从 [RHIDescriptorPool](../RHIDescriptorPool/RHIDescriptorPool.md) 中分配出来
## 声明概览
它承担三类职责:
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIDescriptorSet` | `class` | 头文件中的公开声明。 |
- 管理布局绑定信息
- 持有或引用 view / sampler 绑定
- 维护常量缓冲区的 CPU 侧写入状态
## 当前接口语义
### 生命周期与绑定
- [Bind](Bind.md)
- [Unbind](Unbind.md)
- [Shutdown](Shutdown.md)
这说明 descriptor set 在当前抽象里不是单纯静态数据对象,而是允许直接参与绑定/解绑流程。
### 资源更新
- [Update](Update.md)
- [UpdateSampler](UpdateSampler.md)
这两个接口分别用于写入资源视图和 sampler。
需要特别注意的是,`Update(uint32_t offset, ...)` 参数名虽然叫 `offset`,但从当前测试特别是 OpenGL 路径来看它的现实语义更接近“binding number”而不是“布局数组中的第几个元素”。
例如测试会定义 binding `3``7`,然后显式调用:
- `set->Update(7, srv)`
并验证只有 binding `7` 被更新。
### 常量数据
- [WriteConstant](WriteConstant.md)
- [GetConstantBufferData](GetConstantBufferData.md)
- [GetConstantBufferSize](GetConstantBufferSize.md)
- [IsConstantDirty](IsConstantDirty.md)
- [MarkConstantClean](MarkConstantClean.md)
这组接口说明当前 `RHIDescriptorSet` 不只是一个“引用外部 GPU 资源的句柄集合”,它还承担了一层常量数据 staging / dirty tracking 的职责。
## 布局信息
`RHIDescriptorSet` 通过以下接口暴露自身布局:
- [GetBindingCount](GetBindingCount.md)
- [GetBindings](GetBindings.md)
布局描述本身来自 [RHITypes](../RHITypes/RHITypes.md) 中的:
- `DescriptorSetLayoutBinding`
- `DescriptorSetLayoutDesc`
这意味着 set 的资源组织方式在创建时就已由布局确定,而不是运行时自由追加字段。
## 测试体现出的真实语义
`tests/RHI/unit/test_descriptor_set.cpp` 很能说明当前实现已经具备哪些行为:
- 可以分配空布局 set也可以分配带 SRV / Sampler / CBV 的布局
- `Update()` 可以写入 `RHIResourceView`
- `UpdateSampler()` 可以写入 `RHISampler`
- `WriteConstant()` 会分配或更新常量数据,并能通过 `GetConstantBufferData()` / `GetConstantBufferSize()` 读取状态
- `WriteConstant()` 会把 dirty 标记设为 true`MarkConstantClean()` 会清掉该标记
- OpenGL 路径会按 binding number 执行纹理和 UBO 绑定
- D3D12 路径还存在更底层的 descriptor offset、descriptor index 和常量缓冲上传行为
这说明 `RHIDescriptorSet` 当前已经不是纸面抽象,而是后端资源绑定机制的真实统一入口。
## 设计理解
如果参考商业引擎常见做法,这样的 descriptor set 抽象有几个明显好处:
- renderer 可以用统一布局描述去组织材质和 pass 资源
- 后端可以各自把这组绑定映射到 descriptor heap、binding point 或 native layout
- 常量缓冲和资源视图能在同一对象里统一管理修改状态
代价是当前接口也背了一些实现细节:
- `offset` / `binding` 语义不够直观
- 既有资源绑定,也有常量缓冲 staging
- 后端差异仍然会在具体行为上暴露出来
## 生命周期
通常流程是:
1. 从 [RHIDescriptorPool](../RHIDescriptorPool/RHIDescriptorPool.md) 分配
2. 调用 `Update()` / `UpdateSampler()` / `WriteConstant()` 填充数据
3. 交给 [RHICommandList](../RHICommandList/RHICommandList.md) 或后端绑定路径使用
4. 使用结束后 [Shutdown](Shutdown.md) 并 `delete`
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHIDescriptorSet()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [Bind](Bind.md) | 公开方法,详见头文件声明。 |
| [Unbind](Unbind.md) | 公开方法,详见头文件声明。 |
| [Update](Update.md) | 更新运行时状态。 |
| [UpdateSampler](UpdateSampler.md) | 更新运行时状态。 |
| [WriteConstant](WriteConstant.md) | 公开方法,详见头文件声明。 |
| [GetBindingCount](GetBindingCount.md) | 获取相关状态或对象。 |
| [GetBindings](GetBindings.md) | 获取相关状态或对象。 |
| [GetConstantBufferData](GetConstantBufferData.md) | 获取相关状态或对象。 |
| [GetConstantBufferSize](GetConstantBufferSize.md) | 获取相关状态或对象。 |
| [IsConstantDirty](IsConstantDirty.md) | 查询当前状态。 |
| [MarkConstantClean](MarkConstantClean.md) | 公开方法,详见头文件声明。 |
- [Shutdown](Shutdown.md)
- [Bind](Bind.md)
- [Unbind](Unbind.md)
- [Update](Update.md)
- [UpdateSampler](UpdateSampler.md)
- [WriteConstant](WriteConstant.md)
- [GetBindingCount](GetBindingCount.md)
- [GetBindings](GetBindings.md)
- [GetConstantBufferData](GetConstantBufferData.md)
- [GetConstantBufferSize](GetConstantBufferSize.md)
- [IsConstantDirty](IsConstantDirty.md)
- [MarkConstantClean](MarkConstantClean.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIDescriptorPool](../RHIDescriptorPool/RHIDescriptorPool.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [RHITypes](../RHITypes/RHITypes.md)
- [RHIEnums](../RHIEnums/RHIEnums.md)
- [API 总索引](../../../main.md)

View File

@@ -6,50 +6,168 @@
**头文件**: `XCEngine/RHI/RHIDevice.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIDevice` public API
**描述**: 抽象图形设备接口,负责设备初始化、能力查询,并作为大多数 RHI 对象的统一创建中心
## 概述
## 角色概述
`RHIDevice.h``XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。
`RHIDevice`当前 `RHI` 抽象层的中心对象。它不像某些引擎那样把资源工厂、提交器、能力查询器分拆成多套接口,而是采用一种更集中的“设备即工厂”设计:
## 声明概览
- 初始化后端上下文或 native device
- 持有能力信息和设备信息
- 创建资源对象、命令对象、pipeline 对象和 descriptor 对象
- 提供必要的 native handle 泄露口
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIDevice` | `class` | 头文件中的公开声明。 |
这和原生图形 API 的心智模型比较接近,也符合很多商业引擎底层渲染后端的设计习惯。好处是统一、直接,代价是接口面比较大。
## 生命周期
### 初始化
`Initialize(const RHIDeviceDesc&)` 是创建后的第一步。当前 `RHIDeviceDesc` 只暴露了三个设备级开关:
- `enableDebugLayer`
- `enableGPUValidation`
- `adapterIndex`
这说明当前设备初始化配置还比较克制,没有把所有平台差异都堆进入口参数里。
### 关闭
`Shutdown()` 负责销毁设备级状态。按当前测试习惯,调用方应在删除设备之前显式调用它,而不是只依赖析构函数。
### 查询
初始化成功后,调用方通常会继续读取:
- [GetCapabilities](GetCapabilities.md)
- [GetDeviceInfo](GetDeviceInfo.md)
- [GetNativeDevice](GetNativeDevice.md)
其中 `GetNativeDevice()` 明确是一个“打破抽象边界”的出口,供后端专用逻辑或调试逻辑使用。
## 它负责创建什么
当前接口覆盖了 RHI 里绝大多数对象类别。
### 资源与展示
- [CreateBuffer](CreateBuffer.md)
- [CreateTexture](CreateTexture.md)
- [CreateSwapChain](CreateSwapChain.md)
`CreateTexture()` 还有一个带 `initialData``rowPitch` 的重载,允许直接创建并上传初始数据。
### 执行与同步
- [CreateCommandList](CreateCommandList.md)
- [CreateCommandQueue](CreateCommandQueue.md)
- [CreateFence](CreateFence.md)
### shader 与管线
- [CreateShader](CreateShader.md)
- [CreatePipelineState](CreatePipelineState.md)
- [CreatePipelineLayout](CreatePipelineLayout.md)
- [CreateSampler](CreateSampler.md)
### render pass 与 framebuffer
- [CreateRenderPass](CreateRenderPass.md)
- [CreateFramebuffer](CreateFramebuffer.md)
### descriptor 与资源视图
- [CreateDescriptorPool](CreateDescriptorPool.md)
- [CreateDescriptorSet](CreateDescriptorSet.md)
- [CreateVertexBufferView](CreateVertexBufferView.md)
- [CreateIndexBufferView](CreateIndexBufferView.md)
- [CreateRenderTargetView](CreateRenderTargetView.md)
- [CreateDepthStencilView](CreateDepthStencilView.md)
- [CreateShaderResourceView](CreateShaderResourceView.md)
- [CreateUnorderedAccessView](CreateUnorderedAccessView.md)
从接口形状就能看出,当前 `RHIDevice` 同时承担了资源创建器、视图工厂、descriptor 工厂和 pipeline 工厂的职责。
## 所有权约定
这是这页最关键的现实语义。
当前 `RHIDevice` 的所有创建接口都返回裸指针。按 `tests/RHI/unit/test_device.cpp``test_command_list.cpp` 等现有测试来看,推荐使用方式是:
1. 通过 `RHIDevice` 创建对象。
2. 使用对象执行初始化、录制或资源操作。
3. 结束前调用对象自己的 `Shutdown()`
4. 最后 `delete`
这同样适用于 `buffer``texture``queue``command list``fence``sampler``render pass``framebuffer``descriptor pool``descriptor set``resource view` 等对象。
如果带着现代 RAII 预期来使用这层接口,很容易误判释放责任。当前文档必须把这一点写死。
## 当前设计理解
从架构方向看,`RHIDevice` 的定位是合理的:
- 抽象层统一把后端对象创建集中到设备上,方便 renderer 只依赖一套入口。
- 设备负责暴露能力信息,便于上层在初始化阶段建立 feature branch。
- 资源视图和 descriptor 也由设备创建,符合 D3D12 / Vulkan 风格资源管理直觉。
但从当前实现成熟度看,也要认识到几个边界:
- 还没有单独的内存分配器 / residency / heap 管理抽象。
- 资源创建描述符虽然统一,但很多字段仍是 `uint32_t` 承载枚举值,而不是更强类型的 API。
- `GetNativeDevice()` 表明上层仍可能在必要时下钻到后端实现。
这说明当前阶段的目标更偏“稳定支撑后端实现和测试”,而不是已经完成最终抽象收敛。
## 测试中体现出的真实用法
现有测试提供了比模板文档更可靠的使用线索:
- 创建设备后先 `Initialize()`
- 查询 `GetCapabilities()``GetDeviceInfo()`
- 创建 `Buffer``Texture``Fence``CommandQueue``CommandList``Sampler` 等对象。
- 对每个对象做基本验证后 `Shutdown()``delete`
这说明 `RHIDevice` 当前不仅是概念入口,而且已经是单元测试和集成测试里的真实生产入口。
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHIDevice()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Initialize](Initialize.md) | 初始化内部状态。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [CreateBuffer](CreateBuffer.md) | 创建新对象或资源。 |
| [CreateTexture](CreateTexture.md) | 创建新对象或资源。 |
| [CreateSwapChain](CreateSwapChain.md) | 创建新对象或资源。 |
| [CreateCommandList](CreateCommandList.md) | 创建新对象或资源。 |
| [CreateCommandQueue](CreateCommandQueue.md) | 创建新对象或资源。 |
| [CreateShader](CreateShader.md) | 创建新对象或资源。 |
| [CreatePipelineState](CreatePipelineState.md) | 创建新对象或资源。 |
| [CreatePipelineLayout](CreatePipelineLayout.md) | 创建新对象或资源。 |
| [CreateFence](CreateFence.md) | 创建新对象或资源。 |
| [CreateSampler](CreateSampler.md) | 创建新对象或资源。 |
| [CreateRenderPass](CreateRenderPass.md) | 创建新对象或资源。 |
| [CreateFramebuffer](CreateFramebuffer.md) | 创建新对象或资源。 |
| [CreateDescriptorPool](CreateDescriptorPool.md) | 创建新对象或资源。 |
| [CreateDescriptorSet](CreateDescriptorSet.md) | 创建新对象或资源。 |
| [CreateVertexBufferView](CreateVertexBufferView.md) | 创建新对象或资源。 |
| [CreateIndexBufferView](CreateIndexBufferView.md) | 创建新对象或资源。 |
| [CreateRenderTargetView](CreateRenderTargetView.md) | 创建新对象或资源。 |
| [CreateDepthStencilView](CreateDepthStencilView.md) | 创建新对象或资源。 |
| [CreateShaderResourceView](CreateShaderResourceView.md) | 创建新对象或资源。 |
| [CreateUnorderedAccessView](CreateUnorderedAccessView.md) | 创建新对象或资源。 |
| [GetCapabilities](GetCapabilities.md) | 获取相关状态或对象。 |
| [GetDeviceInfo](GetDeviceInfo.md) | 获取相关状态或对象。 |
| [GetNativeDevice](GetNativeDevice.md) | 获取相关状态或对象。 |
- [Initialize](Initialize.md) - 初始化设备。
- [Shutdown](Shutdown.md) - 关闭设备并释放设备级状态。
- [CreateBuffer](CreateBuffer.md)
- [CreateTexture](CreateTexture.md)
- [CreateSwapChain](CreateSwapChain.md)
- [CreateCommandList](CreateCommandList.md)
- [CreateCommandQueue](CreateCommandQueue.md)
- [CreateShader](CreateShader.md)
- [CreatePipelineState](CreatePipelineState.md)
- [CreatePipelineLayout](CreatePipelineLayout.md)
- [CreateFence](CreateFence.md)
- [CreateSampler](CreateSampler.md)
- [CreateRenderPass](CreateRenderPass.md)
- [CreateFramebuffer](CreateFramebuffer.md)
- [CreateDescriptorPool](CreateDescriptorPool.md)
- [CreateDescriptorSet](CreateDescriptorSet.md)
- [CreateVertexBufferView](CreateVertexBufferView.md)
- [CreateIndexBufferView](CreateIndexBufferView.md)
- [CreateRenderTargetView](CreateRenderTargetView.md)
- [CreateDepthStencilView](CreateDepthStencilView.md)
- [CreateShaderResourceView](CreateShaderResourceView.md)
- [CreateUnorderedAccessView](CreateUnorderedAccessView.md)
- [GetCapabilities](GetCapabilities.md)
- [GetDeviceInfo](GetDeviceInfo.md)
- [GetNativeDevice](GetNativeDevice.md)
## 相关指南
- [Devices, Queues, Command Lists, And Resource Creation](../../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIFactory](../RHIFactory/RHIFactory.md)
- [RHICommandQueue](../RHICommandQueue/RHICommandQueue.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [RHICapabilities](../RHICapabilities/RHICapabilities.md)
- [RHITypes](../RHITypes/RHITypes.md)
- [API 总索引](../../../main.md)

View File

@@ -2,72 +2,139 @@
**命名空间**: `XCEngine::RHI`
**类型**: `enum class`
**类型**: `header collection`
**头文件**: `XCEngine/RHI/RHIEnums.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIEnums` public API
**描述**: 提供 RHI 抽象层使用的公共枚举,用来描述 shader、资源、格式、队列、状态切换和后端类型等核心概念
## 概述
## 角色概述
`RHIEnums.h``XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。
`RHIEnums.h`整个 `RHI` 抽象层的词汇表。它本身不做任何运行时工作,但几乎所有描述符和接口都会引用这里的枚举:
## 声明概览
- [RHITypes](../RHITypes/RHITypes.md) 里大量 `uint32_t` 字段本质上都承载这些枚举值。
- [RHIDevice](../RHIDevice/RHIDevice.md) 的创建描述、[RHICommandList](../RHICommandList/RHICommandList.md) 的状态设置、[RHIPipelineState](../RHIPipelineState/RHIPipelineState.md) 的管线描述都依赖这套词汇。
| 声明 | 类型 | 说明 |
|------|------|------|
| `ShaderType` | `enum class` | 头文件中的公开声明。 |
| `ShaderLanguage` | `enum class` | 头文件中的公开声明。 |
| `CullMode` | `enum class` | 头文件中的公开声明。 |
| `FillMode` | `enum class` | 头文件中的公开声明。 |
| `FrontFace` | `enum class` | 头文件中的公开声明。 |
| `BlendOp` | `enum class` | 头文件中的公开声明。 |
| `BlendFactor` | `enum class` | 头文件中的公开声明。 |
| `ComparisonFunc` | `enum class` | 头文件中的公开声明。 |
| `StencilOp` | `enum class` | 头文件中的公开声明。 |
| `TextureType` | `enum class` | 头文件中的公开声明。 |
| `BufferType` | `enum class` | 头文件中的公开声明。 |
| `DescriptorType` | `enum class` | 头文件中的公开声明。 |
| `PipelineType` | `enum class` | 头文件中的公开声明。 |
| `CommandQueueType` | `enum class` | 头文件中的公开声明。 |
| `LoadAction` | `enum class` | 头文件中的公开声明。 |
| `StoreAction` | `enum class` | 头文件中的公开声明。 |
| `PresentFlags` | `enum class` | 头文件中的公开声明。 |
| `PrimitiveTopology` | `enum class` | 头文件中的公开声明。 |
| `PrimitiveTopologyType` | `enum class` | 头文件中的公开声明。 |
| `FilterMode` | `enum class` | 头文件中的公开声明。 |
| `TextureAddressMode` | `enum class` | 头文件中的公开声明。 |
| `BorderColor` | `enum class` | 头文件中的公开声明。 |
| `LogicOp` | `enum class` | 头文件中的公开声明。 |
| `ColorWriteMask` | `enum class` | 头文件中的公开声明。 |
| `ShaderVisibility` | `enum class` | 头文件中的公开声明。 |
| `RootParameterType` | `enum class` | 头文件中的公开声明。 |
| `DescriptorHeapType` | `enum class` | 头文件中的公开声明。 |
| `ResourceViewDimension` | `enum class` | 头文件中的公开声明。 |
| `ResourceViewType` | `enum class` | 头文件中的公开声明。 |
| `QueryType` | `enum class` | 头文件中的公开声明。 |
| `Format` | `enum class` | 头文件中的公开声明。 |
| `ResourceStates` | `enum class` | 头文件中的公开声明。 |
| `HeapType` | `enum class` | 头文件中的公开声明。 |
| `RHIType` | `enum class` | 头文件中的公开声明。 |
所以这页最重要的价值不是罗列每个值,而是说明这些枚举被分成了哪些语义组,以及使用时要注意什么。
## 枚举值
## 主要枚举分组
| 枚举值 | 数值 | 描述 |
|--------|------|------|
| `Vertex` | - | 枚举项。 |
| `Fragment` | - | 枚举项。 |
| `Geometry` | - | 枚举项。 |
| `Compute` | - | 枚举项。 |
| `TessControl` | - | 枚举项。 |
| `TessEvaluation` | - | 枚举项。 |
| `Hull` | - | 枚举项。 |
| `Domain` | - | 枚举项。 |
| `Amplification` | - | 枚举项。 |
| `Mesh` | - | 枚举项。 |
| `Library` | - | 枚举项。 |
### shader 与编译
- `ShaderType`
- `ShaderLanguage`
- `ShaderVisibility`
这些枚举定义 shader 阶段、源码语言和资源可见性。
### 光栅化与混合状态
- `CullMode`
- `FillMode`
- `FrontFace`
- `BlendOp`
- `BlendFactor`
- `ComparisonFunc`
- `StencilOp`
- `LogicOp`
- `ColorWriteMask`
这组枚举主要被 [RHITypes](../RHITypes/RHITypes.md) 里的 `RasterizerDesc``BlendDesc``DepthStencilStateDesc` 使用。
### 资源与视图
- `TextureType`
- `BufferType`
- `DescriptorType`
- `DescriptorHeapType`
- `ResourceViewDimension`
- `ResourceViewType`
- `Format`
- `HeapType`
这组是资源系统最常用的枚举集合。
### 命令提交与渲染流程
- `PipelineType`
- `CommandQueueType`
- `LoadAction`
- `StoreAction`
- `PresentFlags`
- `PrimitiveTopology`
- `PrimitiveTopologyType`
- `ResourceStates`
- `QueryType`
这组更多服务于 command list、queue、render pass 和 swapchain。
### 采样与地址模式
- `FilterMode`
- `TextureAddressMode`
- `BorderColor`
### pipeline layout / root parameter
- `RootParameterType`
### 后端选择
- `RHIType`
这是 [RHIFactory](../RHIFactory/RHIFactory.md) 用来选择后端设备的枚举。
## 使用时最值得注意的几点
### 1. 很多地方需要手动 `static_cast`
因为 [RHITypes](../RHITypes/RHITypes.md) 里很多字段是 `uint32_t`,常见写法会是:
```cpp
desc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
```
这意味着这些枚举虽然是强类型 `enum class`,但在描述符层最终经常会退化为整数存储。
### 2. `ColorWriteMask` 是当前少数带位运算帮助函数的枚举
头文件里只为 `ColorWriteMask` 提供了 `operator|`。这说明它被明确按 bitmask 使用而其他很多“Flags”字样的枚举目前并没有同样的位运算封装。
### 3. `RHIType` 不等于“当前构建一定可用的后端集合”
`RHIType` 里虽然定义了:
- `D3D12`
- `OpenGL`
- `Vulkan`
- `Metal`
但按当前 [RHIFactory](../RHIFactory/RHIFactory.md) 实现:
- `OpenGL``Vulkan` 受编译宏控制
- `Metal` 当前直接返回 `nullptr`
所以它更像“抽象层目标后端集合”,而不是运行时保证。
### 4. 名称中性,不代表抽象完全无偏
这些枚举尽量使用跨 API 共通词汇,但它们仍然是在真实后端压力下收敛出来的公共子集,不代表已经完整覆盖所有底层 API 的全部特性语义。
## 推荐怎么使用这页
实际工程里,最合理的阅读方式是:
1. 先知道某个接口或描述符需要哪类枚举。
2. 再回到这页查具体可选值。
不要把这页当成孤立知识点记忆;它必须结合 [RHITypes](../RHITypes/RHITypes.md) 和具体创建接口一起看,才有意义。
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHITypes](../RHITypes/RHITypes.md)
- [RHIFactory](../RHIFactory/RHIFactory.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [API 总索引](../../../main.md)

View File

@@ -6,25 +6,81 @@
**头文件**: `XCEngine/RHI/RHIFactory.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIFactory` public API
**描述**: 提供统一的 RHI 后端选择入口,根据枚举或字符串创建具体 `RHIDevice` 实现
## 概述
## 角色概述
`RHIFactory.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。
`RHIFactory`当前抽象层的引导入口。它的价值不在于复杂逻辑,而在于把“上层代码如何选中某个图形后端”这件事收敛到一个稳定入口里:
## 声明概览
- 测试代码可以按参数切换 D3D12 / OpenGL / Vulkan。
- 引擎启动流程不需要直接 `#include` 某个具体后端设备头。
- 以后如果要接配置文件、命令行或编辑器选项,也可以继续沿用这个入口。
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIFactory` | `class` | 头文件中的公开声明。 |
这类工厂在商用引擎里很常见,因为“后端选择”通常是启动层问题,而不是渲染业务层问题。
## 当前创建语义
`RHIFactory` 只暴露两个静态重载:
- `CreateRHIDevice(RHIType type)`
- `CreateRHIDevice(const std::string& typeName)`
它们都会直接 `new` 出具体后端设备,并返回 `RHIDevice*`。当前没有智能指针包装,也没有注册表或插件发现机制。
## 后端可用性
按当前 `engine/src/RHI/RHIFactory.cpp` 的实现,真实行为是:
- `RHIType::D3D12` 始终返回 `new D3D12Device()`
- `RHIType::OpenGL` 只有在定义 `XCENGINE_SUPPORT_OPENGL` 时才会返回 `new OpenGLDevice()`
- `RHIType::Vulkan` 只有在定义 `XCENGINE_SUPPORT_VULKAN` 时才会返回 `new VulkanDevice()`
- `RHIType::Metal` 当前直接返回 `nullptr`
这和很多商业引擎的“平台编译开关决定后端可用集合”是一致的,但当前实现还没有提供更友好的 fallback 策略。
## 字符串重载支持的名称
字符串重载当前支持的别名是源码硬编码的:
- `D3D12`, `d3d12`
- `OpenGL`, `opengl`, `GL`
- `Vulkan`, `vulkan`, `VK`, `vk`
如果名字不匹配,或者对应后端没有编译进当前构建,工厂会返回 `nullptr`
## 所有权约定
`CreateRHIDevice()` 返回的是裸指针。按当前测试和调用习惯,调用方负责:
1. 判断返回值是否为空。
2. 调用 `Initialize()` 完成设备初始化。
3. 使用完毕后先 `Shutdown()`
4. 最后 `delete`
这一点必须显式记住,因为它和 `Core` 模块里常见的 `Ref<T>` / `UniqueRef<T>` 使用习惯不同。
## 当前设计边界
`RHIFactory` 当前是一个非常薄的入口,因此它没有提供下面这些商业引擎常见增强能力:
- 没有“选择首选后端,否则回退到次选后端”的策略接口。
- 没有适配器探测或按能力自动挑选设备。
- 没有插件式后端注册。
- 没有返回错误原因,只有 `nullptr`
这不是缺陷掩盖,而是当前实现范围确实比较收敛。它的职责只是“根据已知后端名创建一个设备对象”。
## 公共方法
| 方法 | 描述 |
|------|------|
| [CreateRHIDevice](CreateRHIDevice.md) | 创建新对象或资源。 |
- [CreateRHIDevice](CreateRHIDevice.md) - 根据 `RHIType` 或后端名称创建抽象设备实例。
## 相关指南
- [Devices, Queues, Command Lists, And Resource Creation](../../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHIEnums](../RHIEnums/RHIEnums.md)
- [API 总索引](../../../main.md)

View File

@@ -6,30 +6,111 @@
**头文件**: `XCEngine/RHI/RHIFence.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIFence` public API
**描述**: GPU 同步原语接口,用于 signal、wait 和 completed value 查询,是命令队列同步和 frame pacing 的基础构件
## 概述
## 角色概述
`RHIFence.h``XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。
`RHIFence`当前 `RHI` 抽象层里最基础的同步对象。它通常和 [RHICommandQueue](../RHICommandQueue/RHICommandQueue.md) 配合使用,用来表达:
## 声明概览
- 某个 GPU 提交点是否已经完成
- CPU 是否需要等待某个值
- 当前完成值推进到了哪里
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIFence` | `class` | 头文件中的公开声明。 |
如果拿商用引擎常见心智模型来理解,它更接近 timeline-style fence / completion fence而不是图形 API 中所有同步原语的统一抽象。
## 公共方法
## 当前接口
| 方法 | 描述 |
|------|------|
| [~RHIFence()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [Signal](Signal.md) | 公开方法,详见头文件声明。 |
| [Wait](Wait.md) | 公开方法,详见头文件声明。 |
| [GetCompletedValue](GetCompletedValue.md) | 获取相关状态或对象。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
- [Signal](Signal.md)
- [Wait](Wait.md)
- [GetCompletedValue](GetCompletedValue.md)
- [GetNativeHandle](GetNativeHandle.md)
- [Shutdown](Shutdown.md)
## 一个必须写清楚的细节:无参 `Signal()` 并不完全统一
`RHIFence` 同时提供:
- `Signal()`
- `Signal(uint64_t value)`
但当前无参重载在不同后端上的实现并不完全一致:
- D3D12 当前实现等价于 `Signal(1)`
- OpenGL 当前实现也等价于 `Signal(1)`
- Vulkan 当前头文件实现是 `++m_value`
这意味着:
- 无参 `Signal()` 不是跨后端严格统一的 timeline 递增语义
- 如果你需要可移植、可推断的同步值行为,应该优先使用显式值重载 `Signal(value)`
这是一个非常值得文档明确指出的实现事实。
## `Wait()` 和 completed value 语义
当前测试已经覆盖:
- signal 到某个值后 wait
- completed value 递增到更大值
- wait 一个小于已完成值的目标
- 多次 signal / wait 组合
从这些测试和当前实现看,更合理的理解是:
- `Wait(value)` 表示“至少等到这个值”
- `GetCompletedValue()` 返回当前后端已完成的最大同步值
## 和 `RHICommandQueue` 的关系
有两条常见用法:
### fence 自己 signal / wait
测试里直接调用:
- `fence->Signal(value)`
- `fence->Wait(value)`
### 由 queue 推进 fence
队列也有:
- `queue->Signal(fence, value)`
- `queue->Wait(fence, value)`
这更接近真实 GPU 提交流程中的使用方式。
## 当前设计边界
`RHIFence` 当前没有提供:
- 多对象 join / fan-in 抽象
- semaphore 风格资源依赖表达
- timeline semaphore / binary semaphore 的统一细分类型
它的定位就是一层简单、可验证的 completion fence。
## native handle 语义
`GetNativeHandle()` 也是明显的后端逃逸口:
- D3D12 下是原生 fence 对象
- OpenGL 下是 `GLsync`
- Vulkan 当前 public 实现甚至可能返回 `nullptr`
因此调用方不能把它当成稳定跨后端能力。
## 生命周期
`RHIFence` 由 [RHIDevice](../RHIDevice/RHIDevice.md) 创建,并以裸指针返回。使用完成后推荐:
1. [Shutdown](Shutdown.md)
2. `delete`
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHICommandQueue](../RHICommandQueue/RHICommandQueue.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [Devices, Queues, Command Lists, And Resource Creation](../../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md)
- [API 总索引](../../../main.md)

View File

@@ -6,31 +6,169 @@
**头文件**: `XCEngine/RHI/RHIFramebuffer.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIFramebuffer` public API
**描述**: 抽象 framebuffer 对象,负责把 render pass 描述与一组具体 attachment view 绑定起来,形成一次可用于渲染输出的目标集合
## 概述
## 角色概述
`RHIFramebuffer.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。
`RHIFramebuffer` 不是纹理资源本体,也不是 render pass 本体。它更像是两者之间的一层“装配结果”:
## 声明概览
- [RHIRenderPass](../RHIRenderPass/RHIRenderPass.md) 负责描述这次 pass 需要怎样的颜色/深度附件语义
- [RHIResourceView](../RHIResourceView/RHIResourceView.md) 负责把具体纹理解释为 render target / depth stencil 视图
- `RHIFramebuffer` 则把它们组合成一份真正可绑定到 GPU 的输出目标配置
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIFramebuffer` | `class` | 头文件中的公开声明。 |
这种拆分与商业引擎、现代图形 API 的思路一致。它的价值在于:
- 同一个 render pass 可以反复搭配不同纹理视图
- swap chain resize 或离屏目标切换时,只需要重建 framebuffer而不必重写整套 pass 语义
- 后端可以各自用最合适的 native 方式存储这组 attachment 绑定关系
## 输入语义
当前抽象接口上的 `Initialize()` 需要这些核心输入:
- `renderPass`
- `width`
- `height`
- `colorAttachmentCount`
- `colorAttachments`
- `depthStencilAttachment`
这几个参数共同表达的是:
- 这份 framebuffer 应当匹配哪种 pass 附件布局
- 它的目标尺寸是多少
- 它绑定了哪些颜色视图
- 是否还绑定了深度/模板视图
## 当前后端差异
### D3D12
当前 D3D12 实现并不会创建真正的 native framebuffer 对象。
它做的事情非常轻量:
- 保存 `D3D12RenderPass*`
- 保存宽高
- 从每个 `RHIResourceView` 中提取 CPU descriptor handle
- 把颜色 RTV handle 和深度 DSV handle 缓存起来
有几个后果非常重要:
- [GetNativeHandle](GetNativeHandle.md) 当前返回 `nullptr`
- [IsValid](IsValid.md) 的判断条件只是 `m_renderPass != nullptr`
- 当前实现不会主动校验 attachment 数量、尺寸、格式是否与 render pass 严格匹配
- 即使宽高是 `0`,对象也可能创建成功
换句话说D3D12 路径里的 `RHIFramebuffer` 更像“供 command list 使用的一组 RTV/DSV 句柄快照”,而不是一个强校验、强约束的独立 native 对象。
### OpenGL
OpenGL 路径会创建真正的 FBO并把 resource view 中记录的附件信息挂到 FBO 上,然后调用 `glCheckFramebufferStatus()` 做完整性检查。
这条路径的特点是:
- [GetNativeHandle](GetNativeHandle.md) 返回的是 OpenGL framebuffer 对象名
- [IsValid](IsValid.md) 反映的是 FBO 是否已成功创建
- 会根据 resource view 的附件目标、mip、layer 去做实际挂接
- 当前实现最多处理前 16 个颜色附件
还需要特别注意一个实现事实:
当前 `OpenGLFramebuffer::Initialize(RHIRenderPass*, ...)` 里,`renderPass` 参数实际上没有被用来做匹配校验。也就是说OpenGL 的“有效性”更多来自 FBO 自身的 GL 完整性检查,而不是来自 render pass 与 framebuffer 的抽象层一致性校验。
### Vulkan
Vulkan 是当前校验最严格的一条路径。
在真正创建 `VkFramebuffer` 前,当前实现会检查:
- `VkDevice` 是否有效
- `renderPass` 是否非空且 `VkRenderPass` 有效
- `width` / `height` 是否大于 0
- `colorAttachmentCount > 0``colorAttachments` 不能为 `nullptr`
- `colorAttachmentCount` 必须与 render pass 的颜色附件数量一致
- 深度附件的有无必须与 render pass 是否声明 depth/stencil 一致
- 每个传入的 `VulkanResourceView` 都必须持有有效 `VkImageView`
只有全部满足时才会创建 `VkFramebuffer`
因此当前最保守、最可移植的工程习惯是:按 Vulkan 的严格要求去构造 framebuffer而不要依赖 D3D12 / OpenGL 某些较宽松的容忍行为。
## 测试体现出的真实语义
`tests/RHI/unit/test_framebuffer.cpp` 目前覆盖了这些场景:
- 单颜色附件创建
- 宽高查询
- `IsValid()` 基本语义
- 显式 `Shutdown()`
- 颜色 + 深度模板附件
- 多颜色附件
- 非法尺寸
- 颜色附件指针为 `nullptr`
测试里一个很关键的信号是:对于非法尺寸和空颜色视图,测试并没有要求“一定创建失败”,而是采用“如果创建成功则检查其返回状态”的写法。
这恰恰说明当前抽象层对非法输入没有统一拒绝语义,后端差异必须在文档里写明,而不能假装它已经完全抹平。
## 生命周期与所有权
`RHIFramebuffer` 通常通过 [RHIDevice::CreateFramebuffer](../RHIDevice/CreateFramebuffer.md) 创建,并以裸指针返回。
它只拥有自己内部的 native / backend 状态,不拥有:
- `RHIRenderPass`
- `RHIResourceView`
- 底层 `RHITexture`
销毁 framebuffer 不等于销毁附件纹理或其视图。当前最稳妥的释放方式仍然是:
1. [Shutdown](Shutdown.md)
2. `delete`
## 线程语义
当前接口没有声明线程安全保证。从源码和图形 API 约束出发,更安全的理解是:
- 创建、销毁和绑定相关操作应放在渲染线程或 RHI 线程
- 不要跨线程同时修改同一个 framebuffer
- 不要假设 resize、swap chain 重建、attachment 替换与旧 framebuffer 并发安全
## 设计理解
把 framebuffer 作为独立对象,而不是把 attachment 直接塞进 command list 调用里,是现代渲染架构里很常见的一种收敛方式。
它的好处在于:
- 附件绑定关系可以被缓存和复用
- render pass 语义与具体图像资源可以分离
- resize、离屏渲染、GBuffer、多目标输出这类场景更容易组织
如果从 Unity 风格去理解,它更接近“引擎内部组织 render target set 的对象”,而不是用户层直接面对的某个高层渲染资产。
## 当前实现限制
- 抽象接口没有提供“创建后反查 attachment 列表”的通用 API调用方需要自己记住输入
- D3D12 路径没有真正的 native framebuffer对 [GetNativeHandle](GetNativeHandle.md) 不能做可移植假设
- OpenGL 路径的 `renderPass` 当前主要起抽象层占位作用,不承担严格匹配校验
- Vulkan 路径最严格,意味着上层若想写出稳定的跨后端代码,最好按 Vulkan 约束准备 attachment
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHIFramebuffer()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [Initialize](Initialize.md) | 初始化内部状态。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
| [GetWidth](GetWidth.md) | 获取相关状态或对象。 |
| [GetHeight](GetHeight.md) | 获取相关状态或对象。 |
| [IsValid](IsValid.md) | 查询当前状态。 |
- [Initialize](Initialize.md)
- [Shutdown](Shutdown.md)
- [GetNativeHandle](GetNativeHandle.md)
- [GetWidth](GetWidth.md)
- [GetHeight](GetHeight.md)
- [IsValid](IsValid.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIRenderPass](../RHIRenderPass/RHIRenderPass.md)
- [RHIResourceView](../RHIResourceView/RHIResourceView.md)
- [RHITexture](../RHITexture/RHITexture.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [API 总索引](../../../main.md)

View File

@@ -6,28 +6,193 @@
**头文件**: `XCEngine/RHI/RHIPipelineLayout.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIPipelineLayout` public API
**描述**: 资源绑定布局抽象,负责描述一个 pipeline 预期会使用多少常量缓冲、纹理、采样器和 UAV以及这些绑定在后端中应如何组织
## 概述
## 角色概述
`RHIPipelineLayout.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明
`RHIPipelineLayout` 对应的是“渲染或计算管线在资源绑定层面长什么样”
## 声明概览
这类对象在现代图形 API 里都很关键,只是名字不同:
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIPipelineLayout` | `class` | 头文件中的公开声明。 |
- 在 D3D12 里更接近 root signature 及其参数布局
- 在 Vulkan 里更接近一组 descriptor set layout 加 pipeline layout
- 在 OpenGL 里则更像一份绑定点分配方案
如果从商业级游戏引擎的角度理解,它更像 Unity SRP / Unreal RHI 内部会维护的“资源绑定契约”,而不是面向 gameplay 层的材质接口。把这层单独抽出来的好处是:
- pipeline state 可以明确声明自己依赖哪些资源类别
- descriptor set / resource view 的更新路径有稳定的布局依据
- 后端可以把同一份高层描述翻译成各自的 native 绑定模型
## 两种建模方式
当前 `RHIPipelineLayoutDesc` 支持两种输入方式:
### 1. 扁平计数
直接填写:
- `constantBufferCount`
- `textureCount`
- `samplerCount`
- `uavCount`
这种方式简单直接,适合早期 bring-up、轻量测试和不关心 set slot 的场景。
### 2. 显式 set layout
通过:
- `setLayouts`
- `setLayoutCount`
- `DescriptorSetLayoutBinding`
显式声明每个 set 里有哪些 binding、类型是什么、数量多少、可见阶段是什么。
这条路径更接近现代引擎里“先设计绑定布局,再做资源写入”的工作方式,也更适合多 set、多资源类别并存的真实渲染管线。
## 当前实现里的重要事实
这是这一页最需要写清楚的部分。
当你传入 `setLayouts` 时,当前三套后端实现都会:
- 深拷贝 `DescriptorSetLayoutDesc`
- 深拷贝其中的 `DescriptorSetLayoutBinding`
- 重新统计 `constantBufferCount` / `textureCount` / `samplerCount` / `uavCount`
也就是说,`setLayouts` 一旦被成功创建,后端不会继续依赖调用方原始数组的生命周期。`tests/RHI/unit/test_pipeline_layout.cpp` 也专门验证了这一点:创建之后再修改原始 binding 数组,不会反向污染 layout 内部保存的数据。
这很符合商业引擎常见的设计取向:布局对象一旦建立,就应该成为稳定的、可复用的绑定契约,而不是继续悬挂在外部临时数组上。
## 后端差异
### D3D12
当前 D3D12 路径并不是通过抽象类上的 `Initialize()` 完成初始化,而是由 [RHIDevice](../RHIDevice/RHIDevice.md) 内部走 `D3D12PipelineLayout::InitializeWithDevice()` 创建。`D3D12PipelineLayout::Initialize(const RHIPipelineLayoutDesc&)` 当前直接返回 `false`
创建成功后D3D12 会把布局翻译为 root signature
- 常量缓冲通常拆成独立 root parameter
- SRV / UAV / Sampler 更偏向 descriptor table
- 如果使用显式 set layout还会为每个 set 单独维护 root parameter 映射
当前实现还提供了大量 D3D12 专属查询能力,例如:
- 是否存在某个常量缓冲 binding
- 某个 SRV table / UAV table / sampler table 对应哪个 root parameter index
- 在显式 set layout 模式下,某个 set slot 是否拥有指定资源类别
这些能力对于 D3D12 command list 的资源绑定翻译很重要,但它们不是 `RHIPipelineLayout` 抽象层的可移植契约,不能拿来当跨后端通用规则。
`GetNativeHandle()` 在 D3D12 下返回的是 `ID3D12RootSignature*`
### OpenGL
OpenGL 并不会创建一个独立的 native pipeline layout 对象。当前实现更像是在内存里建立一张“binding -> OpenGL binding point”的映射表。
有几个关键信息:
- 如果使用显式 set layoutOpenGL 会按资源类别分别分配 binding point
- 同一类资源的 binding point 会跨 set 递增
- 不同 set 中即使都用了 `binding = 0`,也会被分配到不同的最终 binding point
例如测试已经验证过:
- set0 的 CBV binding 0 会落到 binding point 0
- set1 的 CBV binding 0 会落到 binding point 1
- SRV / UAV / Sampler 也分别有自己独立的递增序列
这说明当前 OpenGL 路径并不是“简单照抄 binding 编号”,而是在用一层布局映射来解决跨 set 的重号问题。
如果没有显式 set layout当前 OpenGL 查询函数会把 set 0 下的 binding 本身当作最终 binding point 使用。
`GetNativeHandle()` 在 OpenGL 下并不是 GL 原生对象句柄,而是在 layout 已初始化时返回 `this`,本质上只是“这个抽象对象当前有效”的后端信号。
### Vulkan
Vulkan 路径的行为最接近现代显式图形 API 的常规理解:
- 显式 set layout 会被翻译成多个 `VkDescriptorSetLayout`
- 然后再组合成一个 `VkPipelineLayout`
如果调用方没有传 `setLayouts`,而是只给了四类扁平计数,当前 Vulkan 会主动合成“伪 set layout”
- 一组只包含 CBV 的 set
- 一组只包含 SRV 的 set
- 一组只包含 UAV 的 set
- 一组只包含 Sampler 的 set
这是一种很务实的过渡设计。它允许上层先用较简单的计数模型驱动 Vulkan backend而不必一上来就强制所有调用点显式构造 descriptor set layout 数组。
和 D3D12 一样,`VulkanPipelineLayout::Initialize(const RHIPipelineLayoutDesc&)` 当前并不是主创建入口;真实创建路径是设备内部调用带 `VkDevice` 参数的重载。
`GetNativeHandle()` 在 Vulkan 下返回的是 `VkPipelineLayout`
## 测试体现出的真实语义
`tests/RHI/unit/test_pipeline_layout.cpp` 目前已经覆盖了这些要点:
- 只给 CBV 计数也能创建
- 只给 Texture / Sampler / UAV 计数也能创建
- 零计数布局也允许创建
- `Shutdown()` 可以重复调用
- 显式 set layout 会被深拷贝
- 显式 set layout 会反推四类资源总数
- D3D12 能区分不同资源类别对应的 root parameter / descriptor table
- D3D12 和 OpenGL 都能正确分离不同 set slot 中的重叠 binding
这意味着当前 `RHIPipelineLayout` 已经不是空壳概念,而是 command list 资源绑定逻辑真正依赖的布局对象。
## 设计理解
把 pipeline layout 单独抽出来,而不是把一切都塞进 pipeline state本质上是在做“资源绑定契约”与“渲染状态契约”的解耦。
这样设计的收益很直接:
- shader / pipeline state 只关心资源布局结果,不需要重新解释每个 descriptor set
- descriptor set 可以围绕 layout 独立分配和更新
- 多后端翻译时root signature、descriptor set layout、OpenGL binding point 分配都能共享同一个上层输入模型
这也是商业引擎里常见的方向。对熟悉 Unity 的读者,可以把它理解成比 `Material.SetTexture()` 这类 API 更底层、更加接近 SRP backend 资源绑定约束的那一层。
## 生命周期
当前主路径是通过 [RHIDevice::CreatePipelineLayout](../RHIDevice/CreatePipelineLayout.md) 创建,并以裸指针返回。按照现有测试和实现,最稳妥的释放顺序仍然是:
1. [Shutdown](Shutdown.md)
2. `delete`
`RHIPipelineLayout` 自己不负责销毁 descriptor set、descriptor pool 或 shader 资源;它只是资源绑定布局契约。
## 线程语义
public header 没有给出任何线程安全保证,当前实现里也看不到同步设施。从现有源码与测试使用方式看,更安全的工程规则是:
- 在渲染线程或 RHI 线程上创建和销毁
- 不要在多线程下同时读写同一个 layout 实例
- 不要假设 backend 查询辅助函数是线程安全的
## 当前实现限制
- 抽象层只暴露了极简接口;很多真正有用的查询能力仍然停留在后端专属类里
- `D3D12PipelineLayout``VulkanPipelineLayout` 的抽象 `Initialize()` 当前都不是实际创建入口
- 当前布局模型只覆盖 CBV / SRV / UAV / Sampler 这类描述符资源,没有统一抽象 push constant、root constant、immutable sampler 等更高级能力
- OpenGL 的 native handle 不是真正的 GL 对象D3D12 / Vulkan 的 native handle 含义也完全不同,不能混用
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHIPipelineLayout()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Initialize](Initialize.md) | 初始化内部状态。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
- [Initialize](Initialize.md)
- [Shutdown](Shutdown.md)
- [GetNativeHandle](GetNativeHandle.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHIDescriptorPool](../RHIDescriptorPool/RHIDescriptorPool.md)
- [RHIDescriptorSet](../RHIDescriptorSet/RHIDescriptorSet.md)
- [RHIPipelineState](../RHIPipelineState/RHIPipelineState.md)
- [RHITypes](../RHITypes/RHITypes.md)
- [Devices, Queues, Command Lists, And Resource Creation](../../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md)
- [API 总索引](../../../main.md)

View File

@@ -6,47 +6,131 @@
**头文件**: `XCEngine/RHI/RHIPipelineState.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIPipelineState` public API
**描述**: 抽象图形/计算管线状态对象负责聚合输入布局、光栅化、混合、深度模板、render target 格式以及可选计算 shader
## 概述
## 角色概述
`RHIPipelineState.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明
`RHIPipelineState` 对应的是当前抽象层里的“可绑定管线状态包”。它的接口明显带有“统一图形状态描述”的设计意图,而不是简单把某个后端的 native PSO 结构原样暴露出去
## 声明概览
头文件注释里直接写了:
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIPipelineState` | `class` | 头文件中的公开声明。 |
- `Unity SRP style`
- `Shader independent of PSO`
这说明当前设计方向是希望用一套更统一的 pipeline 状态模型去支撑不同后端,而不是要求所有状态都只通过 shader 或 native API 自己的配置路径表达。
## 当前状态模型
`RHIPipelineState` 当前主要包含两类信息:
### 图形状态
- [SetInputLayout](SetInputLayout.md)
- [SetRasterizerState](SetRasterizerState.md)
- [SetBlendState](SetBlendState.md)
- [SetDepthStencilState](SetDepthStencilState.md)
- [SetTopology](SetTopology.md)
- [SetRenderTargetFormats](SetRenderTargetFormats.md)
- [SetSampleCount](SetSampleCount.md)
### 计算路径
- [SetComputeShader](SetComputeShader.md)
也就是说,当前同一个抽象接口既能表达 graphics pipeline也能表达 compute pipeline。
## 类型与有效性
这是这页最值得说明的地方。
### `GetType()`
从现有测试看:
- 默认创建出来的 pipeline state 类型是 `PipelineType::Graphics`
- 当设置了 compute shader 后,类型可以变成 `PipelineType::Compute`
### `IsValid()` / `EnsureValid()`
现有测试明确揭示了一个很重要的后端差异:
- 在 D3D12 下,默认或信息不足的 pipeline state 往往不是立即有效的。
- 在 OpenGL 路径下,默认 pipeline state 更容易被视为有效。
头文件注释甚至直接写出了当前统一语义:
- `D3D12` 需要编译
- `OpenGL` 总是有效
这意味着 `RHIPipelineState` 的“有效”不是完全抽象无差异的概念而是和后端实现成熟度、native API 要求直接相关。
## 设计理解
从商用引擎经验看,这样的 pipeline state 抽象是合理的:
- renderer 可以先在统一描述层拼好状态,再交给后端去编译或绑定
- 输入布局、混合、深度模板和 render target 格式等关键状态有明确归属
- 计算路径可以与图形路径复用一部分生命周期和绑定逻辑
但当前实现也有现实边界:
- 还保留了 `RHICommandList::SetShader()` 这种并行路径,所以整个系统并不是“只有 PSO”这一种绑定范式
- `GraphicsPipelineDesc``RHIPipelineState` 的关系更偏工程实现上的状态收敛,而不是已经完全封装成单一材质系统
- 有效性判定明显依赖后端
## 测试体现出的真实使用方式
`tests/RHI/unit/test_pipeline_state.cpp``test_compute.cpp` 给出了当前比较可信的使用路径:
- 可以先创建默认 `GraphicsPipelineDesc` 再逐步设置状态
- 可以读回 `RasterizerDesc``BlendDesc``DepthStencilStateDesc``InputLayoutDesc`
- 可以为 graphics pipeline 直接在 `GraphicsPipelineDesc` 里提供 vertex / fragment shader
- 可以用 `SetComputeShader()` 走 compute 路径
- 在部分后端下 `EnsureValid()` 会触发或推进有效性检查,但并不保证无条件成功
这说明当前 pipeline state 的职责更像“后端可消费的统一状态对象”,而不是文档层面随便说说的概念壳子。
## 生命周期
当前接口提供:
- [Bind](Bind.md)
- [Unbind](Unbind.md)
- [Shutdown](Shutdown.md)
- [GetNativeHandle](GetNativeHandle.md)
通常它由 [RHIDevice](../RHIDevice/RHIDevice.md) 创建,并以裸指针形式返回。使用完成后应显式 `Shutdown()``delete`
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHIPipelineState()](Destructor.md) | 销毁对象并释放相关资源。 |
| [SetInputLayout](SetInputLayout.md) | 设置相关状态或配置。 |
| [SetRasterizerState](SetRasterizerState.md) | 设置相关状态或配置。 |
| [SetBlendState](SetBlendState.md) | 设置相关状态或配置。 |
| [SetDepthStencilState](SetDepthStencilState.md) | 设置相关状态或配置。 |
| [SetTopology](SetTopology.md) | 设置相关状态或配置。 |
| [SetRenderTargetFormats](SetRenderTargetFormats.md) | 设置相关状态或配置。 |
| [SetSampleCount](SetSampleCount.md) | 设置相关状态或配置。 |
| [SetComputeShader](SetComputeShader.md) | 设置相关状态或配置。 |
| [GetRasterizerState](GetRasterizerState.md) | 获取相关状态或对象。 |
| [GetBlendState](GetBlendState.md) | 获取相关状态或对象。 |
| [GetDepthStencilState](GetDepthStencilState.md) | 获取相关状态或对象。 |
| [GetInputLayout](GetInputLayout.md) | 获取相关状态或对象。 |
| [GetHash](GetHash.md) | 获取相关状态或对象。 |
| [GetComputeShader](GetComputeShader.md) | 获取相关状态或对象。 |
| [HasComputeShader](HasComputeShader.md) | 判断是否具备指定状态或能力。 |
| [IsValid](IsValid.md) | 查询当前状态。 |
| [EnsureValid](EnsureValid.md) | 公开方法,详见头文件声明。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [Bind](Bind.md) | 公开方法,详见头文件声明。 |
| [Unbind](Unbind.md) | 公开方法,详见头文件声明。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
| [GetType](GetType.md) | 获取相关状态或对象。 |
- [SetInputLayout](SetInputLayout.md)
- [SetRasterizerState](SetRasterizerState.md)
- [SetBlendState](SetBlendState.md)
- [SetDepthStencilState](SetDepthStencilState.md)
- [SetTopology](SetTopology.md)
- [SetRenderTargetFormats](SetRenderTargetFormats.md)
- [SetSampleCount](SetSampleCount.md)
- [SetComputeShader](SetComputeShader.md)
- [GetRasterizerState](GetRasterizerState.md)
- [GetBlendState](GetBlendState.md)
- [GetDepthStencilState](GetDepthStencilState.md)
- [GetInputLayout](GetInputLayout.md)
- [GetHash](GetHash.md)
- [GetComputeShader](GetComputeShader.md)
- [HasComputeShader](HasComputeShader.md)
- [IsValid](IsValid.md)
- [EnsureValid](EnsureValid.md)
- [Shutdown](Shutdown.md)
- [Bind](Bind.md)
- [Unbind](Unbind.md)
- [GetNativeHandle](GetNativeHandle.md)
- [GetType](GetType.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHITypes](../RHITypes/RHITypes.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [RHIPipelineLayout](../RHIPipelineLayout/RHIPipelineLayout.md)
- [RHIShader](../RHIShader/RHIShader.md)
- [API 总索引](../../../main.md)

View File

@@ -6,32 +6,102 @@
**头文件**: `XCEngine/RHI/RHIRenderPass.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIRenderPass` public API
**描述**: 抽象 render pass 对象,负责描述颜色附件和深度模板附件的 load/store 行为与 clear 值
## 概述
## 角色概述
`RHIRenderPass.h``XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明
`RHIRenderPass`当前 `RHI` 抽象层里对“本次渲染通道要操作哪些附件、以什么方式进入和离开”的统一表达
## 声明概览
它由两个部分组成:
| 声明 | 类型 | 说明 |
|------|------|------|
| `AttachmentDesc` | `struct` | 头文件中的公开声明。 |
| `RHIRenderPass` | `class` | 头文件中的公开声明。 |
- `AttachmentDesc`
- `RHIRenderPass`
其中 `AttachmentDesc` 是最关键的输入描述。
## `AttachmentDesc` 表达什么
`AttachmentDesc` 当前包含:
- `format`
- `loadOp`
- `storeOp`
- `stencilLoadOp`
- `stencilStoreOp`
- `clearValue`
这说明当前 render pass 抽象主要关注的是附件格式、load/store 语义和 clear 参数,而不是更复杂的 subpass、dependency 或 transient attachment 模型。
对当前引擎阶段来说,这是一种合理且务实的收敛方式:
- 足够支撑基本颜色/深度通道
- 能和 command list 的 `BeginRenderPass()` 对接
- 不需要立刻把 Vulkan / D3D12 的全部 render pass 复杂度暴露出来
## 当前创建与使用路径
虽然 `RHIRenderPass` 自己暴露了 [Initialize](Initialize.md),但在真实使用里,上层更常见的入口是:
- [RHIDevice::CreateRenderPass](../RHIDevice/CreateRenderPass.md)
现有测试和 command list 用例基本都遵循这个路径:
1. 构造一个或多个 `AttachmentDesc`
2. 通过 `RHIDevice` 创建 `RHIRenderPass`
3. 创建配套 `RHIFramebuffer`
4. 在 [RHICommandList](../RHICommandList/RHICommandList.md) 上 `BeginRenderPass()` / `EndRenderPass()`
## 当前能力边界
按头文件和测试来看,当前 `RHIRenderPass` 能稳定表达的是:
- 纯颜色附件 pass
- 颜色 + 深度模板 pass
- 多个颜色附件
- 可选深度模板附件
但它没有直接表达:
- subpass
- attachment dependency
- transient attachment / input attachment
- 更复杂的 render graph 级资源声明
这说明它更像当前阶段的“基础渲染通道描述”,而不是最终的现代 render graph 节点模型。
## 测试体现出的现实语义
`tests/RHI/unit/test_render_pass.cpp` 给出了几个重要信号:
- `GetColorAttachmentCount()` 会反映创建时的颜色附件数量
- `GetColorAttachments()` 可以拿回附件描述
- 深度模板附件可以为 `nullptr`
- `Shutdown()` 可以安全调用两次
- `GetNativeHandle()` 当前测试接受 `nullptr`
最后一点很重要:不要假设 `RHIRenderPass` 一定会提供可直接操作的 native handle。至少在当前抽象层里这个返回值并不是用户级主路径依赖点。
## 生命周期
`RHIRenderPass` 通常由 [RHIDevice](../RHIDevice/RHIDevice.md) 创建,并和 [RHIFramebuffer](../RHIFramebuffer/RHIFramebuffer.md) 配套使用。使用完成后应显式:
1. [Shutdown](Shutdown.md)
2. `delete`
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHIRenderPass()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [Initialize](Initialize.md) | 初始化内部状态。 |
| [GetColorAttachmentCount](GetColorAttachmentCount.md) | 获取相关状态或对象。 |
| [GetColorAttachments](GetColorAttachments.md) | 获取相关状态或对象。 |
| [GetDepthStencilAttachment](GetDepthStencilAttachment.md) | 获取相关状态或对象。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
- [Initialize](Initialize.md)
- [Shutdown](Shutdown.md)
- [GetColorAttachmentCount](GetColorAttachmentCount.md)
- [GetColorAttachments](GetColorAttachments.md)
- [GetDepthStencilAttachment](GetDepthStencilAttachment.md)
- [GetNativeHandle](GetNativeHandle.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [RHIFramebuffer](../RHIFramebuffer/RHIFramebuffer.md)
- [RHITypes](../RHITypes/RHITypes.md)
- [API 总索引](../../../main.md)

View File

@@ -6,28 +6,78 @@
**头文件**: `XCEngine/RHI/RHIResource.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIResource` public API
**描述**: 资源基类抽象,提供 native handle 暴露口和统一的资源状态读写接口
## 概述
## 角色概述
`RHIResource.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。
`RHIResource`当前抽象层里最小的一层资源基类。它只关心两件事:
## 声明概览
- 这个资源背后的 native object 是什么
- 当前抽象层认为它处于什么 `ResourceStates`
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIResource` | `class` | 头文件中的公开声明。 |
这意味着它不是一个可直接独立使用的“完整资源对象”,而是为更具体的资源类型打底:
## 公共方法
- [RHIBuffer](../RHIBuffer/RHIBuffer.md)
- [RHITexture](../RHITexture/RHITexture.md)
| 方法 | 描述 |
|------|------|
| [~RHIResource()](Destructor.md) | 销毁对象并释放相关资源。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
| [GetState](GetState.md) | 获取相关状态或对象。 |
| [SetState](SetState.md) | 设置相关状态或配置。 |
## 为什么只有这三个接口
当前 `RHIResource` 只暴露:
- [GetNativeHandle](GetNativeHandle.md)
- [GetState](GetState.md)
- [SetState](SetState.md)
这种最小化接口设计是合理的,因为不同资源类型真正关心的元数据差异很大:
- buffer 关心大小、stride、buffer type
- texture 关心宽高深、mip、format、texture type
把这些都塞进基类反而会让抽象变脆。
## `ResourceStates` 的意义
`GetState()` / `SetState()` 使用的是 [RHIEnums](../RHIEnums/RHIEnums.md) 中的 `ResourceStates`
这套状态值的意义不是“完全等同某个后端原生状态枚举”,而是当前引擎抽象层用于追踪资源读写阶段和 barrier 目标的统一语义。比如:
- `Common`
- `VertexAndConstantBuffer`
- `RenderTarget`
- `PixelShaderResource`
- `CopyDst`
在实际使用里,这些状态通常会和 [RHICommandList](../RHICommandList/RHICommandList.md) 的 barrier、copy、render target 绑定路径一起使用。
## 需要注意的现实边界
### 1. 资源状态是抽象层跟踪值,不等于所有后端都原生保存同样的状态对象
例如 Vulkan 路径会把它用于 layout / access / stage 的转换推导OpenGL 路径则更多把它作为引擎侧状态记录。
### 2. 很多上层调用并不直接拿 `RHIResource*`
虽然 `RHIResource` 是 buffer / texture 的基类,但当前很多绑定和 barrier 路径其实更常拿的是 [RHIResourceView](../RHIResourceView/RHIResourceView.md)。这说明当前抽象层在“资源本体”和“资源视图”之间更偏视图驱动。
### 3. 基类没有 `Shutdown()`
`RHIResource` 没有定义统一的生命周期接口;关闭和释放由具体资源类型自己负责。这也是为什么不能把基类当成完整资源对象来用。
## 怎么理解 `GetNativeHandle()`
`GetNativeHandle()` 明确是一个后端逃逸口:
- D3D12 下可能是 `ID3D12Resource*`
- OpenGL 下可能是纹理 / buffer 的 GL object 相关句柄
- Vulkan 下可能是 `VkImage` / `VkBuffer` 一类 native handle
调用方不能把它当成跨后端稳定接口,只能在明确知道当前后端类型时使用。
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIBuffer](../RHIBuffer/RHIBuffer.md)
- [RHITexture](../RHITexture/RHITexture.md)
- [RHIResourceView](../RHIResourceView/RHIResourceView.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [API 总索引](../../../main.md)

View File

@@ -2,42 +2,137 @@
**命名空间**: `XCEngine::RHI`
**类型**: `class (abstract)`
**类型**: `class hierarchy`
**头文件**: `XCEngine/RHI/RHIResourceView.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIResourceView` public API
**描述**: 资源视图抽象层,用统一接口表达 render target、depth stencil、shader resource、unordered access 以及 buffer 视图等绑定对象
## 概述
## 角色概述
`RHIResourceView.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明
`RHIResourceView` 代表“如何把一个资源解释成某种可绑定视图”。这和 [RHIResource](../RHIResource/RHIResource.md) / [RHITexture](../RHITexture/RHITexture.md) / [RHIBuffer](../RHIBuffer/RHIBuffer.md) 的资源本体是两回事
## 声明概览
这种拆分是商业引擎和现代图形 API 都非常常见的设计:
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIResourceView` | `class` | 头文件中的公开声明。 |
| `RHIVertexBufferView` | `class` | 继承自 `RHIResourceView` 的公开声明。 |
| `RHIIndexBufferView` | `class` | 继承自 `RHIResourceView` 的公开声明。 |
| `RHIRenderTargetView` | `class` | 继承自 `RHIResourceView` 的公开声明。 |
| `RHIDepthStencilView` | `class` | 继承自 `RHIResourceView` 的公开声明。 |
| `RHIShaderResourceView` | `class` | 继承自 `RHIResourceView` 的公开声明。 |
| `RHIUnorderedAccessView` | `class` | 继承自 `RHIResourceView` 的公开声明。 |
| `RHIConstantBufferView` | `class` | 继承自 `RHIResourceView` 的公开声明。 |
- 资源本体负责存储
- 资源视图负责绑定语义
## 公共方法
例如同一张 texture 可能被解释为:
| 方法 | 描述 |
|------|------|
| [~RHIResourceView()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
| [IsValid](IsValid.md) | 查询当前状态。 |
| [GetViewType](GetViewType.md) | 获取相关状态或对象。 |
| [GetDimension](GetDimension.md) | 获取相关状态或对象。 |
| [GetFormat](GetFormat.md) | 获取相关状态或对象。 |
- render target
- shader resource
- depth stencil
- unordered access
## 当前层级
头文件里定义了以下层级:
- `RHIResourceView`
- `RHIVertexBufferView`
- `RHIIndexBufferView`
- `RHIRenderTargetView`
- `RHIDepthStencilView`
- `RHIShaderResourceView`
- `RHIUnorderedAccessView`
- `RHIConstantBufferView`
其中基础公共接口主要集中在 `RHIResourceView` 本体:
- [Shutdown](Shutdown.md)
- [GetNativeHandle](GetNativeHandle.md)
- [IsValid](IsValid.md)
- [GetViewType](GetViewType.md)
- [GetDimension](GetDimension.md)
- [GetFormat](GetFormat.md)
## 一个需要特别说明的现实差异
虽然头文件定义了更细分的子类,但当前 [RHIDevice](../RHIDevice/RHIDevice.md) 的公共工厂接口大多返回的是 `RHIResourceView*`,例如:
- `CreateVertexBufferView()`
- `CreateIndexBufferView()`
- `CreateRenderTargetView()`
- `CreateDepthStencilView()`
- `CreateShaderResourceView()`
- `CreateUnorderedAccessView()`
这说明当前抽象层在“类型系统上有细分层级”和“日常工厂接口以基类返回”为两套并存设计。对调用方来说,最稳妥的主路径仍然是通过:
- `GetViewType()`
- `GetDimension()`
- `GetFormat()`
来判断视图语义,而不是假定自己总能拿到某个强类型子类指针。
## 当前已经真实覆盖的视图类型
`tests/RHI/unit/test_views.cpp` 已经覆盖了这些创建路径:
- render target view
- depth stencil view
- shader resource view
- unordered access view
- vertex buffer view
- index buffer view
还覆盖了:
- array texture 的 SRV
- cube texture 的 SRV
- 多 mip 的 DSV
这说明当前视图系统已经在真实处理二维、数组、立方体和 buffer 视图,不是单一的纹理壳子。
## `RHIConstantBufferView` 的当前状态
这是一个很值得写清楚的点。
虽然 `RHIResourceView.h` 里定义了 `RHIConstantBufferView`,但当前公开的 [RHIDevice](../RHIDevice/RHIDevice.md) 接口并没有 `CreateConstantBufferView()` 工厂函数。
也就是说,在当前 public RHI 主路径里:
- `CBV` 类型概念存在
- 但常量数据绑定更多是通过 [RHIDescriptorSet](../RHIDescriptorSet/RHIDescriptorSet.md) 的 `WriteConstant()` 路径完成
这类“类型已经存在,但公共创建入口尚未统一开放”的情况,正是演进中抽象层常见的过渡状态。
## 视图与命令列表的关系
当前 [RHICommandList](../RHICommandList/RHICommandList.md) 和 descriptor 路径都高度依赖 view
- vertex/index buffer 绑定使用 buffer view
- render target / depth clear 使用相应 view
- `TransitionBarrier()` 当前接收的也是 `RHIResourceView*`
这说明在当前抽象层里view 不只是绑定对象,也是部分资源状态与渲染操作的实际工作单位。
## native handle 的语义
`GetNativeHandle()` 是强后端相关的:
- D3D12 可能对应 descriptor handle / view backing data
- OpenGL 可能对应 framebuffer、texture 或 buffer 相关对象
- Vulkan / 其他后端也会有各自实现
因此不能把它当成跨后端稳定格式来使用。
## 生命周期
resource view 通常由 [RHIDevice](../RHIDevice/RHIDevice.md) 创建,并以裸指针返回。使用完成后推荐:
1. [Shutdown](Shutdown.md)
2. `delete`
它们和资源本体的所有权是分开的;关闭 view 不等于关闭 texture / buffer 本体。
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIResource](../RHIResource/RHIResource.md)
- [RHIBuffer](../RHIBuffer/RHIBuffer.md)
- [RHITexture](../RHITexture/RHITexture.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHIDescriptorSet](../RHIDescriptorSet/RHIDescriptorSet.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [API 总索引](../../../main.md)

View File

@@ -6,30 +6,161 @@
**头文件**: `XCEngine/RHI/RHISampler.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHISampler` public API
**描述**: 采样状态抽象负责描述纹理采样时的过滤、寻址、LOD、比较采样和边框颜色等行为
## 概述
## 角色概述
`RHISampler.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明
`RHISampler` 的职责不是存储纹理数据,而是定义“如何读取纹理”
## 声明概览
把 sampler 从 texture 中拆出来,是现代图形 API 和商业引擎里非常常见的设计:
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHISampler` | `class` | 头文件中的公开声明。 |
- 同一张纹理可以搭配不同 sampler 反复复用
- 过滤模式、寻址模式、比较采样等状态不必重复绑定在每张纹理上
- descriptor set / material 系统更容易缓存和共享采样状态
如果从 Unity 风格理解,它更接近底层图形后端中的 sampler state而不是高层材质检查器里随手改一个纹理导入设置那么简单。
## `SamplerDesc` 表达什么
`RHITypes.h` 里的 `SamplerDesc` 当前包含这些核心字段:
- `filter`
- `addressU` / `addressV` / `addressW`
- `mipLodBias`
- `maxAnisotropy`
- `comparisonFunc`
- `borderColorR/G/B/A`
- `minLod` / `maxLod`
它本质上是在用一份统一描述去覆盖:
- 过滤策略
- UVW 方向寻址模式
- LOD 控制
- 阴影图等场景下的 comparison sampling
- 边框颜色
## 当前推荐使用路径
对上层来说,当前更可靠的主路径通常是:
1. 通过 [RHIDevice::CreateSampler](../RHIDevice/CreateSampler.md) 创建 `RHISampler`
2. 通过 [RHIDescriptorSet](../RHIDescriptorSet/RHIDescriptorSet.md) 的 `UpdateSampler()` 写入描述符集合
3. 在绘制或 dispatch 时由 descriptor set / pipeline 绑定链路消费
这也是更贴近商业引擎的方式。sampler 应该被看成“资源绑定系统的一部分”,而不是总是手工即时 bind 的状态对象。
## 后端差异
### D3D12
当前 D3D12 路径里的 `RHISampler` 更像 `D3D12_SAMPLER_DESC` 的一个轻量包装。
它的几个关键事实是:
- 创建时只缓存 `D3D12_SAMPLER_DESC`
- 当前不会单独创建一个 native GPU sampler 对象
- [GetNativeHandle](GetNativeHandle.md) 返回的是内部 `D3D12_SAMPLER_DESC` 地址
- [GetID](GetID.md) 当前恒为 `0`
- [Bind](Bind.md) / [Unbind](Unbind.md) 当前是 no-op
真正把这个 sampler 变成 GPU 可见描述符的步骤,当前发生在 `D3D12DescriptorSet::UpdateSampler()` 里:那里会把 `D3D12Sampler` 里缓存的 `D3D12_SAMPLER_DESC` 写入 sampler heap。
所以对 D3D12 来说,`RHISampler` 当前更像“用于后续描述符写入的配置对象”,而不是可直接 bind 的 runtime object。
### OpenGL
OpenGL 路径会创建真实的 GL sampler 对象:
- `glGenSamplers`
- `glSamplerParameteri`
- `glSamplerParameterf`
- `glBindSampler`
因此:
- [GetID](GetID.md) 返回的是真正的 OpenGL sampler name
- [Bind](Bind.md) / [Unbind](Unbind.md) 在 OpenGL 下有实际效果
- [GetNativeHandle](GetNativeHandle.md) 本质上是 GL sampler id 的指针化表示
`tests/RHI/unit/test_sampler.cpp` 还专门验证了 OpenGL 对这些状态的映射:
- Wrap / Mirror / Border
- 线性与各向异性过滤
- 比较采样模式
- `minLod` / `maxLod`
也要注意一个现实细节:当前 `OpenGLDevice::CreateSampler()` 本身没有像 `CreateShader()` 那样主动调用 `MakeContextCurrent()`。这意味着创建 sampler 时更稳妥的前提仍然是调用方已经处在有效的 OpenGL 上下文中。
### Vulkan
Vulkan 路径会创建真实的 `VkSampler`
- [GetNativeHandle](GetNativeHandle.md) 返回 `VkSampler` 的地址
- [Bind](Bind.md) / [Unbind](Unbind.md) 当前是 no-op
- [GetID](GetID.md) 当前恒为 `0`
这很符合 Vulkan 的实际工作方式sampler 不是靠即时状态绑定消费,而是通过 descriptor set 写入并由 pipeline 使用。
## 可移植使用建议
对跨后端代码来说,下面这些规则最稳妥:
- 不要把 [GetID](GetID.md) 当作通用标识符;它只有在 OpenGL 下才有明确可用意义
- 不要假设 [Bind](Bind.md) / [Unbind](Unbind.md) 在所有后端都有效
- 尽量通过 descriptor set 的 `UpdateSampler()` 路径去消费 sampler
- 把 [GetNativeHandle](GetNativeHandle.md) 视为后端逃逸口,而不是统一格式的句柄
## 测试体现出的真实语义
当前单测已经覆盖:
- 基本创建
- `Bind()` / `Unbind()`
- `GetID()`
- `GetNativeHandle()`
- OpenGL 下 sampler 参数是否按 `SamplerDesc` 正确落地
但这些测试没有宣称“所有后端都必须支持即时 bind 风格”,所以文档不能把 OpenGL 的表现误写成抽象层通用保证。
## 生命周期
`RHISampler` 通常由设备创建并以裸指针返回。当前最稳妥的释放方式仍然是:
1. [Shutdown](Shutdown.md)
2. `delete`
关闭 sampler 不会销毁纹理本体,也不会自动更新 descriptor set 里已经写入的资源绑定关系。
## 线程语义
当前 public API 没有提供线程安全保证。从实现和图形 API 约束来看,更安全的工程规则是:
- 在渲染线程或 RHI 线程创建和销毁 sampler
- 不要并发调用同一个 sampler 的 bind / unbind / shutdown
- 对 OpenGL 路径,创建和即时绑定都应假设存在正确的当前上下文
## 当前实现限制
- D3D12 / Vulkan 下的 [Bind](Bind.md) 与 [Unbind](Unbind.md) 当前不构成可移植工作流
- [GetID](GetID.md) 不是跨后端稳定语义
- OpenGL 对部分更高级 filter 族并没有一一精确映射;当前主要覆盖普通、比较和各向异性路径
- 抽象层还没有覆盖 immutable sampler、sampler reduction mode 等更高级能力
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHISampler()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [Bind](Bind.md) | 公开方法,详见头文件声明。 |
| [Unbind](Unbind.md) | 公开方法,详见头文件声明。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
| [GetID](GetID.md) | 获取相关状态或对象。 |
- [Shutdown](Shutdown.md)
- [Bind](Bind.md)
- [Unbind](Unbind.md)
- [GetNativeHandle](GetNativeHandle.md)
- [GetID](GetID.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHIDescriptorSet](../RHIDescriptorSet/RHIDescriptorSet.md)
- [RHITexture](../RHITexture/RHITexture.md)
- [RHITypes](../RHITypes/RHITypes.md)
- [RHIEnums](../RHIEnums/RHIEnums.md)
- [API 总索引](../../../main.md)

View File

@@ -6,28 +6,175 @@
**头文件**: `XCEngine/RHI/RHIScreenshot.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIScreenshot` public API
**描述**: 截图辅助对象,用于把当前 swap chain back buffer 读回到 CPU 并保存为文件,主要面向调试、测试和工具场景
## 概述
## 角色概述
`RHIScreenshot.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明
`RHIScreenshot` 不是核心渲染管线对象,而是一条诊断与工具链路径
## 声明概览
它解决的问题很直接:
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIScreenshot` | `class` | 头文件中的公开声明。 |
- 当前画面到底渲染出了什么
- swap chain 在 present 前后是否正确
- 集成测试能否把输出结果落盘
从定位上看,它更接近“引擎内置的轻量截图抓取辅助”,而不是面向最终玩家的完整截图系统。当前实现明显偏 debug / test 风格,而不是面向高性能运行时捕获。
## 创建方式
当前统一入口是:
- [Create](Create.md)
`RHIScreenshot::Create(RHIType)` 会按后端返回不同实现:
- `D3D12` 当前始终可创建
- `OpenGL` 取决于 `XCENGINE_SUPPORT_OPENGL`
- `Vulkan` 取决于 `XCENGINE_SUPPORT_VULKAN`
- 其他未实现后端返回 `nullptr`
这意味着截图能力和 RHI 工厂一样,也受编译开关与后端建设状态影响。
## 当前主工作流
对上层来说,最典型的使用顺序是:
1. 拿到有效的 [RHIDevice](../RHIDevice/RHIDevice.md)
2. 拿到有效的 [RHISwapChain](../RHISwapChain/RHISwapChain.md)
3. 调用 [Capture](Capture.md)
4. 用完后 [Shutdown](Shutdown.md) 并 `delete`
`tests/RHI/unit/test_screenshot.cpp` 也主要围绕这条路径进行验证:
- 可以创建截图对象
- 可以对当前 swap chain 进行基本截图
- `Present(0, 0)` 之后依然可以截图
## 当前输出行为
这是最需要提前告诉使用者的实现事实。
当前三套后端的截图实现都会把数据写成二进制 PPM也就是 `P6` 格式:
- 文件头写入 `P6`
- 紧接着写宽高与 `255`
- 再写 RGB 像素数据
这意味着:
- 即使你把文件名写成 `.png``.jpg` 或其他扩展名,当前写出的内容依然是 PPM 数据
- 当前抽象层没有提供输出格式选择、压缩、alpha 保留或 HDR 导出能力
## 这是同步、阻塞的调试路径
当前各后端实现都会显式等待 GPU / 驱动完成读回:
- OpenGL 调用 `glFinish()`
- D3D12 提交临时命令列表并等待 fence
- Vulkan 提交复制命令并 `vkQueueWaitIdle()`
所以 `RHIScreenshot` 现在应当被视为:
- 调试工具路径
- 自动化测试辅助路径
- Editor / 开发期排障路径
而不是适合每帧调用的实时功能。
## 后端差异
### D3D12
当前 D3D12 会:
- 创建 readback buffer
- 临时创建 command allocator / command list
- 把 back buffer 从 render target 转成 copy source
- 执行 `CopyTextureRegion`
- 再切回 render target
- 用 fence 等待 GPU 完成
- Map readback buffer 并写出 PPM
这条路径的关键现实约束是:
- 当前实现假定被捕获资源能从 `D3D12_RESOURCE_STATE_RENDER_TARGET` 转到 copy source 再切回去
- 它更偏向当前 swap chain back buffer 这种调试用途
- 它是一次完整的同步读回流程,代价较高
### OpenGL
OpenGL 路径会:
- 显式 `MakeContextCurrent()`
- `glFinish()`
- 根据 swap chain 当前 back buffer 状态,选择直接从 `GL_BACK` 读,或者临时创建 read FBO 从 back buffer texture 读
- 调用 `glReadPixels()`
- 逐行倒序写出文件
“逐行倒序写出”这一点很重要,它说明 OpenGL 路径当前会主动做一次垂直翻转,以适配 OpenGL 常见的左下角原点读取习惯。
### Vulkan
Vulkan 路径会:
- 创建 staging buffer 与 host visible 内存
- 创建临时 command pool / command buffer
- 把 swap chain image 从 `VK_IMAGE_LAYOUT_PRESENT_SRC_KHR` 转成 `VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL`
- 执行 `vkCmdCopyImageToBuffer`
- 再切回 present layout
- 提交命令并等待 graphics queue idle
- Map staging 内存并写出 PPM
当前 Vulkan 写文件时是按内存中的行顺序直接写出,没有像 OpenGL 那样做显式垂直翻转。
因此当前跨后端截图结果在“图像朝向”上不应被假定完全一致。OpenGL 路径显式翻转,而 D3D12 / Vulkan 路径当前没有这一层统一处理。
## 与商业引擎工作流的关系
商业级引擎里通常会有多层截图路径:
- 轻量级 back buffer dump
- 离屏 render target 抓取
- 异步 readback
- 带格式转换、压缩、UI 叠加或平台分享的最终用户截图
当前 `RHIScreenshot` 只覆盖了其中最基础的一层,也就是“把当前 swap chain back buffer 读回并存盘”。这对于底层 bring-up 和图形测试已经很有价值,但还远不是完整截图系统。
## 生命周期
当前三套后端的 [Shutdown](Shutdown.md) 基本都是 no-op但仍然建议统一按接口风格调用
1. [Shutdown](Shutdown.md)
2. `delete`
这样后续即使截图对象持有更多缓存资源,也不需要改调用习惯。
## 线程语义
当前接口没有声明线程安全保证。从源码行为看,更稳妥的工程规则是:
- 在渲染线程或 RHI 线程使用
- 调用时确保 device / swap chain 处于有效状态
- 不要并发对同一个 swap chain 做截图和重建
## 当前实现限制
- 当前抽象接口只能抓 swap chain不支持直接抓任意 `RHITexture`
- 只有同步读回,没有异步 capture 队列
- 只有 PPM 输出,没有 PNG/JPG/HDR 等格式支持
- 没有颜色空间、gamma、alpha、UI 合成等高级控制
- 输出目录若不存在,后端会直接失败,不会自动创建目录
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHIScreenshot()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Capture](Capture.md) | 公开方法,详见头文件声明。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [Create](Create.md) | 创建新对象或资源。 |
- [Capture](Capture.md)
- [Shutdown](Shutdown.md)
- [Create](Create.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHISwapChain](../RHISwapChain/RHISwapChain.md)
- [RHITexture](../RHITexture/RHITexture.md)
- [API 总索引](../../../main.md)

View File

@@ -6,33 +6,224 @@
**头文件**: `XCEngine/RHI/RHIShader.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHIShader` public API
**描述**: 单阶段 shader 抽象,负责承载编译后的 shader 代码、shader 类型信息以及可选的反射结果
## 概述
## 角色概述
`RHIShader.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明
`RHIShader` 当前表达的是“单个 shader stage 的编译产物”,而不是完整的材质系统,也不是完整的 graphics program
## 声明概览
从接口形状就能看出这一点:
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHIShader` | `class` | 头文件中的公开声明。 |
- 它只有一个 [GetType](GetType.md)
- [RHIPipelineState](../RHIPipelineState/RHIPipelineState.md) 的 `GraphicsPipelineDesc` 也分别持有 `vertexShader``fragmentShader``geometryShader`
这与现代商业引擎和底层图形 API 的常见组织方式一致shader module / bytecode 是 stage 级资源,真正的 graphics pipeline 则由多个 stage 组合而成。
如果从 Unity 去类比,更准确的理解不是“这等同于 Unity 的 Shader 资源”,而是“它更接近 Unity / SRP / 底层后端内部管理的单阶段编译产物”。
## 当前更可靠的创建路径
虽然 `RHIShader` 自身暴露了 [CompileFromFile](CompileFromFile.md) 和 [Compile](Compile.md),但对上层代码来说,当前更可靠的主路径通常仍然是:
- 先构造 `ShaderCompileDesc`
- 再通过 [RHIDevice::CreateShader](../RHIDevice/CreateShader.md) 创建
这么做的好处是:
- D3D12 / OpenGL / Vulkan 可以各自走最合适的预处理和编译路径
- Vulkan 这种需要先把 GLSL 编译成 SPIR-V 的后端,逻辑会被设备层代管
- 上层代码不必直接碰后端专属类
## `ShaderCompileDesc` 表达什么
`ShaderCompileDesc` 当前包含这些核心输入:
- `fileName`
- `source`
- `sourceLanguage`
- `entryPoint`
- `profile`
- `macros`
它试图提供一份统一的编译描述,但当前后端对这些字段的支持程度并不完全一致,这一点必须写清楚。
## 后端差异
### D3D12
D3D12 路径当前直接调用:
- `D3DCompileFromFile`
- `D3DCompile`
并使用:
- `D3DCOMPILE_DEBUG`
- `D3DCOMPILE_SKIP_OPTIMIZATION`
来生成调试友好的 bytecode。
这条路径的关键语义是:
- `profile` 决定 shader stage例如 `vs_5_0``ps_5_0`
- [GetNativeHandle](GetNativeHandle.md) 返回的是编译后的 `ID3DBlob*`
- `IsValid()` 本质上检查 bytecode blob 是否存在
- [GetUniformInfos](GetUniformInfos.md) / [GetUniformInfo](GetUniformInfo.md) 通过 `D3DReflect` 反射已绑定资源
但也有需要明确写出的限制:
- 当前 D3D12 代码没有把 `ShaderCompileDesc::macros` 传给 `D3DCompile*`
- 反射结果更接近“绑定资源信息”,不等于跨后端统一的高层 uniform 语义
### OpenGL
OpenGL 路径支持从文件或源码编译 GLSL但它的内部模型与 D3D12、Vulkan 都不一样。
当前实现会:
- 根据 `profile`、文件后缀或源码特征推断 shader 类型
- 忽略 `entryPoint`,因为 GLSL 当前仍默认使用 `main`
- 把单阶段源码编译并链接到一个 program object 中
因此:
- [GetNativeHandle](GetNativeHandle.md) 返回的是 OpenGL program id 的指针化表示
- 它不是独立的 GL shader object 句柄
- [GetUniformInfos](GetUniformInfos.md) 返回的是 program active uniform 反射结果
这一点很重要:当前 `RHIShader` 在 OpenGL 下承载的是“单阶段 program 包装”,不是与 D3D12 bytecode 或 Vulkan shader module 完全等价的数据结构。
### Vulkan
Vulkan 路径需要特别说明,因为它分成两层:
1. `VulkanDevice::CreateShader()` 先调用 `CompileVulkanShader()`
2. `VulkanShader` 再用得到的 SPIR-V 字节创建 `VkShaderModule`
当前 Vulkan 编译链的真实行为是:
- 支持 GLSL 或 SPIR-V 输入
- 当前不支持 HLSL source 直接走 Vulkan backend
- 如果是 GLSL会先编译成 SPIR-V
- `macros` 会被注入 GLSL 源码编译路径
- `entryPoint` 默认为 `main`
`VulkanShader` 对象本身的 `Compile()` 实际上要求输入已经是 SPIR-V 字节流。
当前 Vulkan 还有一个很关键的现实边界:
- `GetUniformInfos()` 目前返回空列表
- `GetUniformInfo()` 只会在内部 `m_uniformInfos` 被填充时才有结果
- 但当前实现并没有做统一反射填充
所以 Vulkan 下的 shader 反射能力目前还远不如 D3D12 / OpenGL 成熟,不能把“接口上有反射方法”误写成“所有后端都已有完整反射数据”。
## `GetType()` 与单阶段语义
`GetType()` 当前返回的是单阶段类型,例如:
- `Vertex`
- `Fragment`
- `Geometry`
- `Compute`
测试已经覆盖了 vertex 和 fragment 的主路径。这说明当前 `RHIShader` 的单位就是“一个 stage”而不是“一个完整 graphics shader program”。
## 反射信息应该如何理解
`RHIShader::UniformInfo` 当前包含:
- `name`
- `bindPoint`
- `size`
- `type`
- `arraySize`
- `offset`
但这些字段的语义并不是跨后端完全一致的稳定 ABI。
当前更安全的理解是:
- D3D12更偏向绑定资源反射
- OpenGL更偏向 active uniform / program 反射
- Vulkan当前基本没有统一反射数据
因此这套接口更适合:
- 调试
- 文档核验
- 工具辅助
而不适合被当成跨后端材质系统的唯一真相来源。
## 测试体现出的真实语义
`tests/RHI/unit/test_shader.cpp` 当前已经验证:
- 空描述符创建失败
- 从文件或源码创建 vertex / fragment shader
- 创建成功后 `IsValid()` 为真
- `GetType()` 与预期 stage 一致
- `GetNativeHandle()` 非空
- `Shutdown()` 会让对象失效
- 缺失文件会导致创建失败
也就是说,当前 shader 抽象至少已经覆盖了真实可用的编译与有效性判断路径,而不是模板化占位页。
## 生命周期
`RHIShader` 通常通过 [RHIDevice](../RHIDevice/RHIDevice.md) 创建,并以裸指针返回。当前最稳妥的释放方式仍然是:
1. [Shutdown](Shutdown.md)
2. `delete`
`Shutdown()` 后:
- D3D12 会释放 bytecode blob
- OpenGL 会删除 program
- Vulkan 会销毁 shader module
## 线程语义
当前 public API 没有提供线程安全保证。从源码可以得到更保守的工程结论:
- shader 创建、编译、销毁应视为显式的 RHI 线程 / 渲染线程操作
- OpenGL 编译依赖有效当前上下文
- 不要并发读写同一个 shader 实例
## 设计理解
`RHIShader` 保持为单阶段对象,是一种很务实的底层渲染架构选择:
- shader 资产可以独立编译和缓存
- graphics / compute pipeline 可以各自组合需要的 stage
- 反射、调试、编译错误处理不会全部绑死在 pipeline state 上
这种设计比“一个大而全的万能 shader 资源”更接近底层渲染后端的真实结构,也更便于后续引入 pipeline cache、shader variant、离线编译等能力。
## 当前实现限制
- `ShaderCompileDesc` 的跨后端支持并不对齐,尤其是 `macros``entryPoint``sourceLanguage`
- 当前没有统一的错误对象;失败通常体现在返回 `nullptr``false`
- OpenGL / D3D12 / Vulkan 的 native handle 完全不是同一类对象
- Vulkan 反射当前很弱,不能依赖 [GetUniformInfos](GetUniformInfos.md) 做统一工具链
## 公共方法
| 方法 | 描述 |
|------|------|
| [~RHIShader()](Destructor.md) | 销毁对象并释放相关资源。 |
| [CompileFromFile](CompileFromFile.md) | 公开方法,详见头文件声明。 |
| [Compile](Compile.md) | 公开方法,详见头文件声明。 |
| [GetType](GetType.md) | 获取相关状态或对象。 |
| [IsValid](IsValid.md) | 查询当前状态。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [GetUniformInfos](GetUniformInfos.md) | 获取相关状态或对象。 |
| [GetUniformInfo](GetUniformInfo.md) | 获取相关状态或对象。 |
- [CompileFromFile](CompileFromFile.md)
- [Compile](Compile.md)
- [GetType](GetType.md)
- [IsValid](IsValid.md)
- [GetNativeHandle](GetNativeHandle.md)
- [Shutdown](Shutdown.md)
- [GetUniformInfos](GetUniformInfos.md)
- [GetUniformInfo](GetUniformInfo.md)
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHIPipelineState](../RHIPipelineState/RHIPipelineState.md)
- [RHITypes](../RHITypes/RHITypes.md)
- [RHIEnums](../RHIEnums/RHIEnums.md)
- [API 总索引](../../../main.md)

View File

@@ -6,31 +6,119 @@
**头文件**: `XCEngine/RHI/RHISwapChain.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHISwapChain` public API
**描述**: 窗口展示交换链接口,负责管理 back buffer、present 和 resize是渲染输出到窗口的最终桥接层
## 概述
## 角色概述
`RHISwapChain.h``XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。
`RHISwapChain``RHI` 抽象层里最接近“屏幕展示”的对象。它把:
## 声明概览
- 窗口句柄
- 呈现队列
- back buffer 纹理
- present / resize 行为
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHISwapChain` | `class` | 头文件中的公开声明。 |
收敛成一套统一接口。
## 公共方法
如果从商用引擎视角理解,它就是 renderer 最后把颜色输出送到窗口的那道门。
| 方法 | 描述 |
|------|------|
| [~RHISwapChain()](Destructor.md) | 销毁对象并释放相关资源。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
| [GetCurrentBackBufferIndex](GetCurrentBackBufferIndex.md) | 获取相关状态或对象。 |
| [GetCurrentBackBuffer](GetCurrentBackBuffer.md) | 获取相关状态或对象。 |
| [Present](Present.md) | 公开方法,详见头文件声明。 |
| [Resize](Resize.md) | 公开方法,详见头文件声明。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
## 创建方式
swapchain 通常由 [RHIDevice](../RHIDevice/RHIDevice.md) 通过 [CreateSwapChain](../RHIDevice/CreateSwapChain.md) 创建,输入参数来自 [RHITypes](../RHITypes/RHITypes.md) 中的 `SwapChainDesc`,同时还需要一个 present queue。
## 当前接口语义
- [GetCurrentBackBufferIndex](GetCurrentBackBufferIndex.md)
- [GetCurrentBackBuffer](GetCurrentBackBuffer.md)
- [Present](Present.md)
- [Resize](Resize.md)
- [GetNativeHandle](GetNativeHandle.md)
- [Shutdown](Shutdown.md)
## 最关键的所有权规则
`GetCurrentBackBuffer()` 返回的是 `RHITexture*`,但这个纹理不是调用方拥有的独立对象。
按当前后端实现:
- D3D12 返回内部 `m_backBuffers` 向量中元素的地址
- OpenGL 返回 `m_backBufferTexture`
- Vulkan 返回内部 `unique_ptr<VulkanTexture>` 持有对象的裸指针
因此这里有一个必须写死的规则:
- 通过 swapchain 取得的 back buffer 由 swapchain 自己持有
- 调用方不应该对这个返回值执行 `Shutdown()``delete`
## `GetCurrentBackBufferIndex()` 的可移植语义
这个索引在不同后端上的行为并不完全一致:
- D3D12 / Vulkan 会跟随当前交换链图像轮换
- OpenGL 当前实现始终返回 `0`
所以最安全的理解是:
- 它是“当前展示图像的后端信息”
- 不是跨所有后端都等价的 frame slot 语义
## `Present()` / `Resize()` 的现实边界
### `Present()`
测试已经覆盖:
- `Present(0, 0)`
-`syncInterval` 的 present
- 连续多次 present
但是否真正使用 `syncInterval` / `flags` 取决于后端。比如当前 OpenGL 实现会忽略这两个参数。
### `Resize()`
`Resize()` 可能导致内部 back buffer 资源被重建或重新解释。对调用方来说,最安全的工程规则是:
- 不要在 `Resize()` 后继续长期持有旧的 back buffer 指针
- 需要时重新调用 `GetCurrentBackBuffer()`
## 测试体现出的真实语义
`tests/RHI/unit/test_swap_chain.cpp` 已经验证了:
- 创建成功
- 当前 back buffer index 落在有效范围内
- `GetCurrentBackBuffer()` 非空
- 可以 resize
- 可以反复 present
- 支持 2-buffer 与 3-buffer 路径
- `Shutdown()` 可重复调用
这说明 swapchain 在当前抽象层中已经是稳定主路径对象,而不是仅供单一后端调试。
## `GetNativeHandle()` 的后端差异
这个接口是非常典型的后端逃逸口:
- D3D12 返回 DXGI swapchain 对象
- OpenGL 当前返回窗口句柄
- Vulkan 返回 `VkSwapchainKHR`
因此不能把它当成统一结构使用。
## 生命周期
普通使用方式是:
1. 通过 [RHIDevice](../RHIDevice/RHIDevice.md) 创建 swapchain
2. 读取当前 back buffer并围绕它创建 view / framebuffer 等
3. 录制并提交命令
4. 调用 [Present](Present.md)
5. 结束时 [Shutdown](Shutdown.md) 并 `delete`
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHITexture](../RHITexture/RHITexture.md)
- [RHICommandQueue](../RHICommandQueue/RHICommandQueue.md)
- [RHIFramebuffer](../RHIFramebuffer/RHIFramebuffer.md)
- [API 总索引](../../../main.md)

View File

@@ -6,37 +6,117 @@
**头文件**: `XCEngine/RHI/RHITexture.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHITexture` public API
**描述**: 抽象纹理资源接口,负责暴露尺寸、格式、纹理类型、资源状态、调试命名和 native handle
## 概述
## 角色概述
`RHITexture.h``XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明
`RHITexture`[RHIResource](../RHIResource/RHIResource.md) 的另一类核心具体资源。它既用于普通纹理资源,也用于 render target、depth texture、swapchain back buffer 这类图形输出资源
## 声明概览
和很多成熟引擎类似,当前抽象层把“纹理是什么”与“纹理怎么被绑定/解释”分开处理:
| 声明 | 类型 | 说明 |
|------|------|------|
| `RHITexture` | `class` | 继承自 `RHIResource` 的公开声明。 |
- `RHITexture` 代表资源本体
- [RHIResourceView](../RHIResourceView/RHIResourceView.md) 代表对纹理的不同视图解释
## 公共方法
## 创建方式
| 方法 | 描述 |
|------|------|
| [~RHITexture()](Destructor.md) | 销毁对象并释放相关资源。 |
| [GetWidth](GetWidth.md) | 获取相关状态或对象。 |
| [GetHeight](GetHeight.md) | 获取相关状态或对象。 |
| [GetDepth](GetDepth.md) | 获取相关状态或对象。 |
| [GetMipLevels](GetMipLevels.md) | 获取相关状态或对象。 |
| [GetFormat](GetFormat.md) | 获取相关状态或对象。 |
| [GetTextureType](GetTextureType.md) | 获取相关状态或对象。 |
| [GetState](GetState.md) | 获取相关状态或对象。 |
| [SetState](SetState.md) | 设置相关状态或配置。 |
| [GetNativeHandle](GetNativeHandle.md) | 获取相关状态或对象。 |
| [GetName](GetName.md) | 获取相关状态或对象。 |
| [SetName](SetName.md) | 设置相关状态或配置。 |
| [Shutdown](Shutdown.md) | 关闭并清理内部状态。 |
`RHITexture` 通常由 [RHIDevice](../RHIDevice/RHIDevice.md) 通过以下重载创建:
- [CreateTexture](../RHIDevice/CreateTexture.md)
- [CreateTexture](../RHIDevice/CreateTexture.md) 的带初始数据重载
创建描述来自 [RHITypes](../RHITypes/RHITypes.md) 中的 `TextureDesc`
## 当前接口能直接告诉你什么
- [GetWidth](GetWidth.md)
- [GetHeight](GetHeight.md)
- [GetDepth](GetDepth.md)
- [GetMipLevels](GetMipLevels.md)
- [GetFormat](GetFormat.md)
- [GetTextureType](GetTextureType.md)
- [GetState](GetState.md)
- [SetState](SetState.md)
- [GetNativeHandle](GetNativeHandle.md)
- [GetName](GetName.md)
- [SetName](SetName.md)
- [Shutdown](Shutdown.md)
## 当前已经覆盖到的纹理类型
`tests/RHI/unit/test_texture.cpp` 已经验证了多个纹理形态:
- `Texture1D`
- `Texture2D`
- `Texture3D`
- `Texture2DArray`
- `TextureCube`
- `TextureCubeArray`
同时也覆盖了:
- 多 mip 级
- 多种格式
- 多采样创建路径
这说明当前 `RHITexture` 已经不是“只有 2D RGBA8”那种早期占位资源抽象。
## 状态语义
和 buffer 一样texture 也维护 [ResourceStates](../RHIEnums/RHIEnums.md)
- `Common`
- `RenderTarget`
- `PixelShaderResource`
- `CopyDst`
- `DepthWrite`
测试明确覆盖了多次状态切换,因此这里的状态不是死字段,而是当前引擎真实依赖的资源跟踪信息。它会和 [RHICommandList](../RHICommandList/RHICommandList.md) 的 barrier、clear、copy 路径发生关系。
## 需要注意的接口边界
### 1. 不是所有创建信息都能从对象接口直接读回
虽然 `TextureDesc` 里有:
- `arraySize`
- `sampleCount`
- `sampleQuality`
- `flags`
`RHITexture` 公共接口并没有提供这些字段的直接查询方法。当前文档不能假设对象页能完整还原所有创建描述。
### 2. 初始数据上传发生在设备创建阶段
`initialData` 的上传路径属于 `RHIDevice::CreateTexture()` 重载,不属于 `RHITexture` 对象自身的后续方法。
### 3. swapchain back buffer 也是 `RHITexture*`
但通过 [RHISwapChain](../RHISwapChain/RHISwapChain.md) 取回的 back buffer 不是由调用方拥有的独立纹理对象,不能按普通 `CreateTexture()` 的产物去 `delete`
## 测试体现出的真实语义
当前测试已经证明:
- 创建成功后可以查询宽高深、mip、格式、纹理类型
- `GetState()` / `SetState()` 可多次切换
- `SetName()` / `GetName()` 可用于调试命名
- `GetNativeHandle()` 创建成功后通常非空
- `Shutdown()` 可重复调用
## 生命周期
普通 `RHITexture` 由 [RHIDevice](../RHIDevice/RHIDevice.md) 创建并返回裸指针。使用完成后推荐:
1. [Shutdown](Shutdown.md)
2. `delete`
唯一需要特别区分的是 swapchain back buffer它由 swapchain 持有,不应由调用方释放。
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIResource](../RHIResource/RHIResource.md)
- [RHIResourceView](../RHIResourceView/RHIResourceView.md)
- [RHISwapChain](../RHISwapChain/RHISwapChain.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [API 总索引](../../../main.md)

View File

@@ -2,79 +2,154 @@
**命名空间**: `XCEngine::RHI`
**类型**: `struct`
**类型**: `header collection`
**头文件**: `XCEngine/RHI/RHITypes.h`
**描述**: 定义 `XCEngine/RHI` 子目录中的 `RHITypes` public API
**描述**: 提供跨后端共享的描述符、辅助结构和设备信息结构,是 `RHI` 抽象层的公共数据语言
## 概述
## 这不是一个单独的“类型”
`RHITypes.h` `XCEngine/RHI` 子目录 下的 public header当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明
`RHITypes.h` 并不声明一个叫 `RHITypes` 的运行时对象。它更准确的定位是:`RHI` 模块的公共描述符头文件集合
## 声明概览
几乎所有核心接口都会依赖它:
| 声明 | 类型 | 说明 |
|------|------|------|
| `Viewport` | `struct` | 头文件中的公开声明。 |
| `Rect` | `struct` | 头文件中的公开声明。 |
| `Color` | `struct` | 头文件中的公开声明。 |
| `ClearValue` | `struct` | 头文件中的公开声明。 |
| `ShaderCompileMacro` | `struct` | 头文件中的公开声明。 |
| `ShaderCompileDesc` | `struct` | 头文件中的公开声明。 |
| `InputElementDesc` | `struct` | 头文件中的公开声明。 |
| `InputLayoutDesc` | `struct` | 头文件中的公开声明。 |
| `VertexBufferBinding` | `struct` | 头文件中的公开声明。 |
| `TextureCopyLocation` | `struct` | 头文件中的公开声明。 |
| `DescriptorHandle` | `struct` | 头文件中的公开声明。 |
| `GPUDescriptorHandle` | `struct` | 头文件中的公开声明。 |
| `CPUDescriptorHandle` | `struct` | 头文件中的公开声明。 |
| `SubresourceRange` | `struct` | 头文件中的公开声明。 |
| `TextureDesc` | `struct` | 头文件中的公开声明。 |
| `BufferDesc` | `struct` | 头文件中的公开声明。 |
| `RenderTargetDesc` | `struct` | 头文件中的公开声明。 |
| `DepthStencilDesc` | `struct` | 头文件中的公开声明。 |
| `DescriptorHeapDesc` | `struct` | 头文件中的公开声明。 |
| `CommandQueueDesc` | `struct` | 头文件中的公开声明。 |
| `CommandListDesc` | `struct` | 头文件中的公开声明。 |
| `CommandAllocatorDesc` | `struct` | 头文件中的公开声明。 |
| `FenceDesc` | `struct` | 头文件中的公开声明。 |
| `QueryHeapDesc` | `struct` | 头文件中的公开声明。 |
| `SamplerDesc` | `struct` | 头文件中的公开声明。 |
| `SwapChainDesc` | `struct` | 头文件中的公开声明。 |
| `RenderTargetViewDesc` | `struct` | 头文件中的公开声明。 |
| `DepthStencilViewDesc` | `struct` | 头文件中的公开声明。 |
| `ShaderResourceViewDesc` | `struct` | 头文件中的公开声明。 |
| `ConstantBufferViewDesc` | `struct` | 头文件中的公开声明。 |
| `UnorderedAccessViewDesc` | `struct` | 头文件中的公开声明。 |
| `RootSignatureDesc` | `struct` | 头文件中的公开声明。 |
| `StencilOpDesc` | `struct` | 头文件中的公开声明。 |
| `DepthStencilStateDesc` | `struct` | 头文件中的公开声明。 |
| `BlendDesc` | `struct` | 头文件中的公开声明。 |
| `RasterizerDesc` | `struct` | 头文件中的公开声明。 |
| `PipelineStateHash` | `struct` | 头文件中的公开声明。 |
| `GraphicsPipelineDesc` | `struct` | 头文件中的公开声明。 |
| `RHIDeviceDesc` | `struct` | 头文件中的公开声明。 |
| `RHIDeviceInfo` | `struct` | 头文件中的公开声明。 |
| `RHIRenderPassDesc` | `struct` | 头文件中的公开声明。 |
| `DescriptorSetLayoutBinding` | `struct` | 头文件中的公开声明。 |
| `DescriptorSetLayoutDesc` | `struct` | 头文件中的公开声明。 |
| `RHIPipelineLayoutDesc` | `struct` | 头文件中的公开声明。 |
| `ResourceViewDesc` | `struct` | 头文件中的公开声明。 |
| `DescriptorPoolDesc` | `struct` | 头文件中的公开声明。 |
- [RHIDevice](../RHIDevice/RHIDevice.md) 的创建参数来自这里
- [RHICommandList](../RHICommandList/RHICommandList.md) 的 viewport / rect / clear value 来自这里
- [RHIPipelineState](../RHIPipelineState/RHIPipelineState.md) 的状态描述来自这里
- descriptor set / pipeline layout / resource view 的布局语言也来自这里
## 结构体成员
理解这页的关键不是死记字段,而是先理解这些结构被分成了哪些“语义家族”。
| 成员 | 类型 | 描述 | 默认值 |
|------|------|------|--------|
| `topLeftX` | `float` | 结构体公开字段。 | - |
| `topLeftY` | `float` | 结构体公开字段。 | - |
| `width` | `float` | 结构体公开字段。 | - |
| `height` | `float` | 结构体公开字段。 | - |
| `minDepth` | `float` | 结构体公开字段。 | - |
| `maxDepth` | `float` | 结构体公开字段。 | - |
## 类型家族
### 视口、区域与 clear 值
- `Viewport`
- `Rect`
- `Color`
- `ClearValue`
这些是 command list 和 render pass 最基础的辅助数据结构。
### shader 编译描述
- `ShaderCompileMacro`
- `ShaderCompileDesc`
这里可以看到当前 shader 输入既支持文件路径,也支持内存中的 `source` 字节序列,并且使用 `std::wstring` 表达文件名、入口点和 profile。
### 顶点输入与基础资源描述
- `InputElementDesc`
- `InputLayoutDesc`
- `TextureDesc`
- `BufferDesc`
- `SamplerDesc`
- `SwapChainDesc`
这些结构基本决定了资源和图形状态的创建方式。当前很多字段并不是强类型枚举,而是 `uint32_t` / `uint64_t`,调用方需要显式使用 [RHIEnums](../RHIEnums/RHIEnums.md) 里的枚举值进行 `static_cast`
### 资源视图与 descriptor 相关结构
- `RenderTargetViewDesc`
- `DepthStencilViewDesc`
- `ShaderResourceViewDesc`
- `ConstantBufferViewDesc`
- `UnorderedAccessViewDesc`
- `ResourceViewDesc`
- `DescriptorHandle`
- `GPUDescriptorHandle`
- `CPUDescriptorHandle`
- `DescriptorSetLayoutBinding`
- `DescriptorSetLayoutDesc`
- `DescriptorPoolDesc`
- `RHIPipelineLayoutDesc`
这部分是资源绑定系统的公共语言。它既要服务 D3D12 风格 descriptor heap / root signature 心智模型,也要兼容 OpenGL 当前的绑定实现,因此接口形状不是完全中性的。
### 设备、队列与同步描述
- `RHIDeviceDesc`
- `RHIDeviceInfo`
- `CommandQueueDesc`
- `CommandListDesc`
- `FenceDesc`
- `QueryHeapDesc`
这些类型支撑设备初始化、命令系统创建和设备信息查询。
### pipeline state 描述
- `StencilOpDesc`
- `DepthStencilStateDesc`
- `BlendDesc`
- `RasterizerDesc`
- `PipelineStateHash`
- `GraphicsPipelineDesc`
`RHIPipelineState.h` 头文件注释里明确提到这是 “Unity SRP style” 的状态配置方向。可以把它理解为:引擎希望用一套更统一的图形状态描述去收敛后端 PSO / program pipeline 差异,而不是把所有 native API 结构直接暴露给上层。
### 兼容层与后端痕迹明显的结构
- `DescriptorHeapDesc`
- `CommandAllocatorDesc`
- `RootSignatureDesc`
- `RHIRenderPassDesc`
这些名字和字段明显带有 D3D12 / 显式图形 API 背景。它们的存在并不奇怪,反而说明当前抽象层是在真实后端实现基础上生长出来的,而不是纯理论设计。
## 最重要的使用约定
### 1. 很多字段是“枚举值容器”,不是强类型枚举字段
例如:
- `TextureDesc::format`
- `TextureDesc::textureType`
- `BufferDesc::bufferType`
- `CommandQueueDesc::queueType`
- `SamplerDesc::filter`
这些字段往往使用 `uint32_t`,调用方通常写成:
```cpp
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
```
这种写法对跨后端兼容很直接,但类型约束较弱。
### 2. 这不是 C ABI 风格的 POD 头文件
`RHITypes.h` 里大量使用了:
- `std::vector`
- `std::wstring`
- 原始指针
- 指向其他 RHI 对象的裸指针
所以它更适合被理解为“引擎内部 C++ 描述符语言”,而不是稳定二进制 ABI 或数据驱动序列化格式。
### 3. 当前抽象层不是完全后端无痕的
如果你看到 `RootSignatureDesc``DescriptorHeapDesc``CommandAllocatorDesc` 这些结构,正确理解不是“抽象失败”,而是“当前抽象优先服务真实后端实现与演进,而不是先追求绝对纯净”。
## 建议怎么读
如果你第一次接触这页,不建议从上到下把所有结构都读完。更高效的方式是:
1. 先读 [RHIEnums](../RHIEnums/RHIEnums.md),理解可选枚举集合。
2. 再读 [RHIDevice](../RHIDevice/RHIDevice.md),看这些描述符分别喂给哪些创建接口。
3. 真正用到某个对象时,再回来查对应那一组结构。
这比把 `RHITypes.h` 当词典通读更符合工程实践。
## 相关文档
- [当前目录](../RHI.md) - 返回 `RHI` 平行目录
- [API 总索引](../../../main.md) - 返回顶层索引
- [当前模块](../RHI.md)
- [RHIEnums](../RHIEnums/RHIEnums.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [RHIPipelineState](../RHIPipelineState/RHIPipelineState.md)
- [API 总索引](../../../main.md)

View File

@@ -0,0 +1,81 @@
# Vulkan
**命名空间**: `XCEngine::RHI`
**类型**: `submodule`
**描述**: `RHI` 的 Vulkan 后端实现目录,负责把抽象设备、队列、命令列表、资源、描述符和管线状态翻译为 Vulkan 原生对象。
## 概览
`XCEngine/RHI/Vulkan` 是当前引擎 Vulkan backend 的 public API 入口树。它和上层抽象 [RHI](../RHI.md) 的关系可以这样理解:
- `RHI` 说明“跨后端统一契约”
- `Vulkan` 说明“这些契约在 Vulkan 上当前是怎么落地的”
当前这一层已经覆盖了:
- 设备、队列、命令列表与交换链
- buffer、texture、resource view
- render pass、framebuffer、pipeline layout、pipeline state
- descriptor pool、descriptor set、sampler、shader
- 截图与共享工具头
## 设计要点
- 目录结构严格与 `engine/include/XCEngine/RHI/Vulkan` 保持平行
- 文档区分抽象契约与当前 Vulkan 实现,避免把未来意图写成现状
- 优先写清楚当前真实行为、平台边界、所有权和限制
- 在能准确类比时补充商业引擎 / Unity 风格的解释性内容
## 头文件与类型页
- [VulkanBuffer](VulkanBuffer/VulkanBuffer.md) - `VulkanBuffer.h`
- [VulkanCommandList](VulkanCommandList/VulkanCommandList.md) - `VulkanCommandList.h`
- [VulkanCommandQueue](VulkanCommandQueue/VulkanCommandQueue.md) - `VulkanCommandQueue.h`
- [VulkanCommon](VulkanCommon/VulkanCommon.md) - `VulkanCommon.h`
- [VulkanDescriptorPool](VulkanDescriptorPool/VulkanDescriptorPool.md) - `VulkanDescriptorPool.h`
- [VulkanDescriptorSet](VulkanDescriptorSet/VulkanDescriptorSet.md) - `VulkanDescriptorSet.h`
- [VulkanDevice](VulkanDevice/VulkanDevice.md) - `VulkanDevice.h`
- [VulkanFence](VulkanFence/VulkanFence.md) - `VulkanFence.h`
- [VulkanFramebuffer](VulkanFramebuffer/VulkanFramebuffer.md) - `VulkanFramebuffer.h`
- [VulkanPipelineLayout](VulkanPipelineLayout/VulkanPipelineLayout.md) - `VulkanPipelineLayout.h`
- [VulkanPipelineState](VulkanPipelineState/VulkanPipelineState.md) - `VulkanPipelineState.h`
- [VulkanRenderPass](VulkanRenderPass/VulkanRenderPass.md) - `VulkanRenderPass.h`
- [VulkanResourceView](VulkanResourceView/VulkanResourceView.md) - `VulkanResourceView.h`
- [VulkanSampler](VulkanSampler/VulkanSampler.md) - `VulkanSampler.h`
- [VulkanScreenshot](VulkanScreenshot/VulkanScreenshot.md) - `VulkanScreenshot.h`
- [VulkanShader](VulkanShader/VulkanShader.md) - `VulkanShader.h`
- [VulkanShaderCompiler](VulkanShaderCompiler/VulkanShaderCompiler.md) - `VulkanShaderCompiler.h`
- [VulkanSwapChain](VulkanSwapChain/VulkanSwapChain.md) - `VulkanSwapChain.h`
- [VulkanTexture](VulkanTexture/VulkanTexture.md) - `VulkanTexture.h`
## 建议阅读顺序
如果你是第一次进入 Vulkan 后端,建议按下面顺序建立整体心智模型:
1. [VulkanDevice](VulkanDevice/VulkanDevice.md)
2. [VulkanCommandQueue](VulkanCommandQueue/VulkanCommandQueue.md)
3. [VulkanCommandList](VulkanCommandList/VulkanCommandList.md)
4. [VulkanSwapChain](VulkanSwapChain/VulkanSwapChain.md)
5. [VulkanPipelineLayout](VulkanPipelineLayout/VulkanPipelineLayout.md)
6. [VulkanPipelineState](VulkanPipelineState/VulkanPipelineState.md)
7. [VulkanDescriptorPool](VulkanDescriptorPool/VulkanDescriptorPool.md)
8. [VulkanDescriptorSet](VulkanDescriptorSet/VulkanDescriptorSet.md)
9. [VulkanBuffer](VulkanBuffer/VulkanBuffer.md) / [VulkanTexture](VulkanTexture/VulkanTexture.md) / [VulkanResourceView](VulkanResourceView/VulkanResourceView.md)
## 相关指南
- [Devices, Queues, Command Lists, And Resource Creation](../../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md)
## 相关文档
- [RHI](../RHI.md)
- [RHIDevice](../RHIDevice/RHIDevice.md)
- [RHICommandQueue](../RHICommandQueue/RHICommandQueue.md)
- [RHICommandList](../RHICommandList/RHICommandList.md)
- [RHIPipelineState](../RHIPipelineState/RHIPipelineState.md)
- [RHIPipelineLayout](../RHIPipelineLayout/RHIPipelineLayout.md)
- [RHISwapChain](../RHISwapChain/RHISwapChain.md)
- [RHIShader](../RHIShader/RHIShader.md)
- [API 总索引](../../../main.md)

View File

@@ -0,0 +1,94 @@
# VulkanBuffer
**命名空间**: `XCEngine::RHI`
**类型**: `class`
**头文件**: `XCEngine/RHI/Vulkan/VulkanBuffer.h`
**描述**: Vulkan 后端里的 `RHIBuffer` 实现,负责创建 `VkBuffer`、分配绑定 `VkDeviceMemory`,并提供映射写入能力。
## 概览
`VulkanBuffer` 是当前 Vulkan 后端最基础的资源对象之一。它封装了:
- `VkBuffer`
- 绑定到该 buffer 的 `VkDeviceMemory`
- RHI 层的软件状态、步长、名称与 buffer 类型
## 生命周期
典型顺序:
1. 由 [VulkanDevice](../VulkanDevice/VulkanDevice.md) 创建
2. `Initialize(device, desc, usageFlags, memoryProperties)`
3. 需要时 `Map()` / `SetData()` / `Unmap()`
4. 不再使用时 `Shutdown()`
析构函数会调用 `Shutdown()`
## 当前实现的真实行为
- 初始化时会先创建 `VkBuffer`
- 查询内存需求
- 通过 `VulkanDevice::FindMemoryType()` 选内存类型
- 分配并绑定 `VkDeviceMemory`
`Map()` / `Unmap()` 都直接作用于 `VkDeviceMemory``SetData()` 的行为是:
- 如果当前没映射,就临时 `Map()`
- `memcpy` 到目标偏移
- 如果是它自己临时映射的,再自动 `Unmap()`
因此它是标准的“主机映射写入”路径。
需要特别说明的是:虽然 `VulkanBuffer` 支持任意 `usageFlags``memoryProperties` 组合,但当前 [VulkanDevice](../VulkanDevice/VulkanDevice.md) 的 `CreateBuffer()` 默认传入的是 host-visible / host-coherent 内存属性,而不是更激进的 device-local 策略。
## 状态与所有权
- `GetState()` / `SetState()` 维护的是软件侧 `ResourceStates`
- `GetNativeHandle()` 返回 `VkBuffer`
- `VulkanBuffer` 拥有 `VkBuffer``VkDeviceMemory`
## 线程语义
没有内部锁。更稳妥的用法是:
- 同一时刻只在一个线程上修改同一个 buffer 的映射数据
- 不要把 `Map()` 出来的指针视为跨线程共享写入缓冲区
## 设计取向
当前实现明显偏向“先把 buffer 可创建、可写、可被命令列表使用”:
- 优点是简单直观,适合测试和基础渲染路径
- 代价是显存分层与上传优化还比较初级
## 当前限制
- 默认设备创建路径偏 host-visible
- 没有单独的上传堆 / 默认堆抽象
- 状态跟踪是软件侧记录,不是驱动可查询状态
## 主要公开方法
- `bool Initialize(VulkanDevice* device, const BufferDesc& desc, VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryProperties)`
- `void Shutdown()`
- `void* Map()`
- `void Unmap()`
- `void SetData(const void* data, size_t size, size_t offset = 0)`
- `uint64_t GetSize() const`
- `BufferType GetBufferType() const`
- `void SetBufferType(BufferType type)`
- `uint32_t GetStride() const`
- `void SetStride(uint32_t stride)`
- `void* GetNativeHandle()`
- `ResourceStates GetState() const`
- `void SetState(ResourceStates state)`
## 相关文档
- [Vulkan](../Vulkan.md)
- [VulkanDevice](../VulkanDevice/VulkanDevice.md)
- [VulkanResourceView](../VulkanResourceView/VulkanResourceView.md)
- [RHIBuffer](../../RHIBuffer/RHIBuffer.md)

View File

@@ -0,0 +1,31 @@
# VulkanCommandList::BeginRenderPass
```cpp
void BeginRenderPass(RHIRenderPass* renderPass, RHIFramebuffer* framebuffer,
const Rect& renderArea, uint32_t clearValueCount, const ClearValue* clearValues) override;
```
## 作用
显式开始一个 Vulkan render pass。
## 前置条件
- `renderPass``framebuffer` 都必须是 Vulkan 后端对象
- framebuffer 必须与 render pass 兼容
- command buffer 已经开始录制
## 当前实现行为
- 会检查 `VulkanRenderPass``VulkanFramebuffer` 和它们的 native handle 是否有效
- 若已有活跃 render pass会先结束旧的
- 会把当前颜色目标和深度目标更新为 framebuffer 的 attachment
- 会根据 `renderArea` 自动设置 viewport 和 scissor
- viewport 会用负高度保持 RHI 左上角原点语义
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [SetRenderTargets](SetRenderTargets.md)
- [VulkanRenderPass](../VulkanRenderPass/VulkanRenderPass.md)
- [VulkanFramebuffer](../VulkanFramebuffer/VulkanFramebuffer.md)

View File

@@ -0,0 +1,26 @@
# VulkanCommandList::Clear
```cpp
void Clear(float r, float g, float b, float a, uint32_t buffers) override;
```
## 作用
按抽象 RHI 的统一接口清理当前颜色和/或深度模板目标。
## 当前实现行为
- 如果需要清颜色,会结束活跃 render pass
- 把颜色目标切到 `CopyDst`
-`vkCmdClearColorImage(...)` 清理
- 如果需要清深度/模板,也会走 image clear 路径而不是 render pass loadOp 路径
## 注意事项
当前实现是显式 image clear不等价于“依赖 render pass clear attachment”。
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [ClearRenderTarget](ClearRenderTarget.md)
- [ClearDepthStencil](ClearDepthStencil.md)

View File

@@ -0,0 +1,20 @@
# VulkanCommandList::ClearDepthStencil
```cpp
void ClearDepthStencil(RHIResourceView* depthStencil, float depth, uint8_t stencil) override;
```
## 作用
清理指定深度模板目标。
## 当前实现行为
- 会结束活跃 render pass
- 把纹理切到 `CopyDst`
- 再调用 `vkCmdClearDepthStencilImage(...)`
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [Clear](Clear.md)

View File

@@ -0,0 +1,20 @@
# VulkanCommandList::ClearRenderTarget
```cpp
void ClearRenderTarget(RHIResourceView* renderTarget, const float color[4]) override;
```
## 作用
清理指定颜色目标。
## 当前实现行为
- 本质上走和 `Clear()` 类似的 image clear 路径
- 需要目标 view 对应有效 `VkImageView`
- 会处理必要的状态切换后调用 `vkCmdClearColorImage(...)`
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [Clear](Clear.md)

View File

@@ -0,0 +1,25 @@
# VulkanCommandList::Close
```cpp
void Close() override;
```
## 作用
结束当前命令录制,把 command buffer 置为可提交状态。
## 当前实现行为
- 会先结束活跃 render pass
- 如果当前颜色目标是交换链图像,会在结束前把它转到 `ResourceStates::Present`
- 最后调用 `vkEndCommandBuffer(...)`
## 注意事项
如果打算把命令列表提交给 [VulkanCommandQueue](../VulkanCommandQueue/VulkanCommandQueue.md),通常应先调用 `Close()`
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [Reset](Reset.md)
- [TransitionBarrier](TransitionBarrier.md)

View File

@@ -0,0 +1,24 @@
# VulkanCommandList::CopyResource
```cpp
void CopyResource(RHIResourceView* dst, RHIResourceView* src) override;
```
## 作用
执行资源拷贝。
## 当前实现行为
- 先验证源/目标 view 都有效
- 会先结束活跃 render pass
- 当前覆盖两类主要路径:
- texture 到 texture
- buffer 到 buffer
- texture 拷贝会处理必要的 layout / state 切换
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [TransitionBarrier](TransitionBarrier.md)
- [Dispatch](Dispatch.md)

View File

@@ -0,0 +1,29 @@
# VulkanCommandList::Dispatch
```cpp
void Dispatch(uint32_t x, uint32_t y, uint32_t z) override;
```
## 作用
发起一次 compute dispatch。
## 前置条件
- 当前 pipeline state 已设置
- pipeline state 持有有效 compute shader
- 需要的 descriptor set 已绑定
## 当前实现行为
- 会先结束活跃 render pass
- 如果当前 pipeline 没有 compute shader直接返回
- 会调用 `m_currentPipelineState->EnsureValid()`
- 如果 compute pipeline 尚未创建,可能在这里延迟创建
- 最终绑定 compute pipeline 并调用 `vkCmdDispatch(...)`
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [SetComputeDescriptorSets](SetComputeDescriptorSets.md)
- [VulkanPipelineState](../VulkanPipelineState/VulkanPipelineState.md)

View File

@@ -0,0 +1,27 @@
# VulkanCommandList::Draw
```cpp
void Draw(uint32_t vertexCount, uint32_t instanceCount = 1, uint32_t startVertex = 0, uint32_t startInstance = 0) override;
```
## 作用
发起一次非索引绘制。
## 前置条件
- 当前 pipeline state 有效
- 已设置 render target 或显式开始 render pass
## 当前实现行为
- 会先调用内部 `EnsureGraphicsRenderPass()`
- 该步骤可能隐式创建 transient framebuffer 并开始 render pass
- 之后绑定当前 graphics pipeline
- 最终调用 `vkCmdDraw(...)`
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [DrawIndexed](DrawIndexed.md)
- [SetRenderTargets](SetRenderTargets.md)

View File

@@ -0,0 +1,26 @@
# VulkanCommandList::DrawIndexed
```cpp
void DrawIndexed(uint32_t indexCount, uint32_t instanceCount = 1, uint32_t startIndex = 0, int32_t baseVertex = 0, uint32_t startInstance = 0) override;
```
## 作用
发起一次索引绘制。
## 当前实现行为
-`Draw()` 一样,会先确保 graphics render pass 可用
- 绑定当前 graphics pipeline
- 最终调用 `vkCmdDrawIndexed(...)`
## 前置条件
- 索引缓冲已经通过 `SetIndexBuffer()` 绑定
- 当前 pipeline state 与 render target 兼容
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [Draw](Draw.md)
- [SetIndexBuffer](SetIndexBuffer.md)

View File

@@ -0,0 +1,27 @@
# VulkanCommandList::Initialize
```cpp
bool Initialize(VulkanDevice* device);
```
## 作用
初始化命令列表,创建 command pool 和一个 primary command buffer。
## 当前实现行为
- 要求 `device``device->GetDevice()` 有效
- 使用 graphics queue family 创建 command pool
- command pool 带 `VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT`
- 只分配一个 `VK_COMMAND_BUFFER_LEVEL_PRIMARY` command buffer
## 返回值
- `true` - 初始化成功
- `false` - command pool 或 command buffer 分配失败
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [Reset](Reset.md)
- [Close](Close.md)

View File

@@ -0,0 +1,26 @@
# VulkanCommandList::Reset
```cpp
void Reset() override;
```
## 作用
重置命令列表并开始新一轮录制。
## 当前实现行为
- 若存在活跃 render pass会先结束它
- 调用 `vkResetCommandPool(...)`
- 销毁由自动 render pass 路径创建的 transient framebuffer
- 清空当前 render target、depth target、pipeline state、viewport/scissor 标记
- 重新以 `VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT` 开始录制
## 使用建议
每次正式录制前都应先 `Reset()`,不要假设新创建的命令列表天然处于已开始录制状态。
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [Close](Close.md)

View File

@@ -0,0 +1,25 @@
# VulkanCommandList::SetComputeDescriptorSets
```cpp
void SetComputeDescriptorSets(uint32_t firstSet, uint32_t count, RHIDescriptorSet** descriptorSets, RHIPipelineLayout* pipelineLayout) override;
```
## 作用
把一组 descriptor set 绑定到 compute bind point。
## 当前实现行为
- 要求 `pipelineLayout` 显式非空
- 会收集每个 `VulkanDescriptorSet` 的 native `VkDescriptorSet`
- 最终调用 `vkCmdBindDescriptorSets(..., VK_PIPELINE_BIND_POINT_COMPUTE, ...)`
## 与 graphics 版本的差异
graphics 版本可以从当前 pipeline state 推导 layoutcompute 版本当前要求调用方明确传入 pipeline layout。
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [SetGraphicsDescriptorSets](SetGraphicsDescriptorSets.md)
- [VulkanPipelineLayout](../VulkanPipelineLayout/VulkanPipelineLayout.md)

View File

@@ -0,0 +1,27 @@
# VulkanCommandList::SetGraphicsDescriptorSets
```cpp
void SetGraphicsDescriptorSets(uint32_t firstSet, uint32_t count, RHIDescriptorSet** descriptorSets, RHIPipelineLayout* pipelineLayout) override;
```
## 作用
把一组 descriptor set 绑定到 graphics bind point。
## 当前实现行为
- 要求 command buffer 有效,`descriptorSets` 非空且 `count > 0`
- 如果显式传入 `pipelineLayout`,优先使用它
- 否则会尝试从当前 `m_currentPipelineState` 推导 native pipeline layout
- 最终调用 `vkCmdBindDescriptorSets(..., VK_PIPELINE_BIND_POINT_GRAPHICS, ...)`
## 失败条件
- pipeline layout 解析失败时会直接返回,不会报错
- 任意 descriptor set 无效时也会中止绑定
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [SetComputeDescriptorSets](SetComputeDescriptorSets.md)
- [VulkanDescriptorSet](../VulkanDescriptorSet/VulkanDescriptorSet.md)

View File

@@ -0,0 +1,21 @@
# VulkanCommandList::SetIndexBuffer
```cpp
void SetIndexBuffer(RHIResourceView* buffer, uint64_t offset) override;
```
## 作用
绑定索引缓冲。
## 当前实现行为
- 要求传入有效的 index buffer 视图
- 索引格式来自 view 的 `Format`
- 最终调用 `vkCmdBindIndexBuffer(...)`
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [DrawIndexed](DrawIndexed.md)
- [VulkanResourceView](../VulkanResourceView/VulkanResourceView.md)

View File

@@ -0,0 +1,22 @@
# VulkanCommandList::SetPipelineState
```cpp
void SetPipelineState(RHIPipelineState* pso) override;
```
## 作用
设置当前 draw / dispatch 要使用的 pipeline state。
## 当前实现行为
- 当前只是把参数向下转为 `VulkanPipelineState*` 后保存到 `m_currentPipelineState`
- 不会立刻绑定到 command buffer
- 真正绑定发生在 `Draw()``DrawIndexed()``Dispatch()` 内部
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [Draw](Draw.md)
- [Dispatch](Dispatch.md)
- [VulkanPipelineState](../VulkanPipelineState/VulkanPipelineState.md)

View File

@@ -0,0 +1,22 @@
# VulkanCommandList::SetRenderTargets
```cpp
void SetRenderTargets(uint32_t count, RHIResourceView** renderTargets, RHIResourceView* depthStencil = nullptr) override;
```
## 作用
设置当前颜色目标和可选深度目标,供后续自动 render pass 路径使用。
## 当前实现行为
- 只保存当前颜色目标和深度目标指针
- 当前只真正记录第一个颜色目标
- 不会立刻创建 framebuffer也不会立刻开始 render pass
- 真正的 render pass 开始可能延迟到 `Draw()` / `DrawIndexed()` 时由内部 `EnsureGraphicsRenderPass()` 完成
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [BeginRenderPass](BeginRenderPass.md)
- [Draw](Draw.md)

View File

@@ -0,0 +1,19 @@
# VulkanCommandList::SetScissorRect
```cpp
void SetScissorRect(const Rect& rect) override;
```
## 作用
设置当前剪裁矩形。
## 当前实现行为
- 会更新内部 `VkRect2D`
- 同时把 scissor 直接写入当前 command buffer
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [SetViewport](SetViewport.md)

View File

@@ -0,0 +1,26 @@
# VulkanCommandList::SetVertexBuffers
```cpp
void SetVertexBuffers(uint32_t startSlot, uint32_t count, RHIResourceView** buffers, const uint64_t* offsets, const uint32_t* strides) override;
```
## 作用
绑定一组顶点缓冲。
## 当前实现行为
- 要求传入的是有效的 buffer 类型 `VulkanResourceView`
- 会从每个 view 中取出底层 `VkBuffer`
- 偏移优先取外部 `offsets`,并叠加 view 自身记录的 `bufferOffset`
- 最终调用 `vkCmdBindVertexBuffers(...)`
## 注意事项
- `strides` 参数不会直接传给 Vulkan实际 stride 主要用于 view / input layout 语义配合
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [SetIndexBuffer](SetIndexBuffer.md)
- [VulkanResourceView](../VulkanResourceView/VulkanResourceView.md)

View File

@@ -0,0 +1,21 @@
# VulkanCommandList::SetViewport
```cpp
void SetViewport(const Viewport& viewport) override;
```
## 作用
设置当前 Vulkan viewport。
## 当前实现行为
- 会把 `y` 设为 `topLeftY + height`
- 会把 `height` 设为负值
- 这样可以在 Vulkan 中维持与其他后端一致的左上角原点语义
- 同时把 viewport 直接写入当前 command buffer
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [SetScissorRect](SetScissorRect.md)

View File

@@ -0,0 +1,26 @@
# VulkanCommandList::TransitionBarrier
```cpp
void TransitionBarrier(RHIResourceView* resource, ResourceStates stateBefore, ResourceStates stateAfter) override;
```
## 作用
请求把资源状态切换到目标状态。
## 当前实现行为
- 当前主要处理 texture 视图
- `stateBefore` 在实现里被忽略
- 真正使用的是 `VulkanTexture` 当前记录的状态和 `stateAfter`
- buffer 状态切换主要走内部专用路径,不通过这个公开接口完成
## 影响
这意味着当前 barrier 模型是“软件状态跟踪优先”的简化实现,而不是严格校验调用方给出的 before/after。
## 相关文档
- [VulkanCommandList](VulkanCommandList.md)
- [CopyResource](CopyResource.md)
- [VulkanTexture](../VulkanTexture/VulkanTexture.md)

View File

@@ -0,0 +1,187 @@
# VulkanCommandList
**命名空间**: `XCEngine::RHI`
**类型**: `class`
**头文件**: `XCEngine/RHI/Vulkan/VulkanCommandList.h`
**描述**: Vulkan 后端里的 `RHICommandList` 实现,封装一个 primary `VkCommandBuffer`,负责录制 draw、dispatch、clear、copy、barrier 与 descriptor 绑定。
## 概览
`VulkanCommandList` 是 Vulkan 后端最直接面向 GPU 录制语义的类。它和抽象层的 [RHICommandList](../../RHICommandList/RHICommandList.md) 一一对应,但实现上保留了当前引擎自己的工程取舍。
当前版本有两个很重要的特点:
- 它既支持显式 `BeginRenderPass()` / `EndRenderPass()` 路径,也支持 `SetRenderTargets()` 后在绘制时懒创建 transient framebuffer 的路径。
- 它维持的是“对上层更友好”的 RHI 约定,而不是完全照搬 Vulkan 原生用法,例如 viewport 会通过负高度翻转来维持左上角原点语义。
这和很多商业引擎的 backend 包装思路一致:不把 Vulkan 原始复杂度直接暴露给上层,而是在 backend 内部吃掉坐标系、render pass 和状态转换的差异。
## 生命周期
推荐使用顺序:
1. 由 [VulkanDevice](../VulkanDevice/VulkanDevice.md) 创建命令列表
2. `Initialize(VulkanDevice*)`
3. 每帧或每次录制前 `Reset()`
4. 录制资源状态、绑定与 draw / dispatch / clear / copy
5. `Close()`
6. 交给 [VulkanCommandQueue](../VulkanCommandQueue/VulkanCommandQueue.md) 提交
7. 不再使用时 `Shutdown()`
当前实现里:
- `Initialize()` 创建一个 command pool 和一个 primary command buffer
- `Reset()``vkResetCommandPool()`,清空暂存 framebuffer并重新 `vkBeginCommandBuffer()`
- `Close()` 会结束活跃 render pass并在当前颜色目标是交换链图像时把它转到 `Present`
- `Shutdown()` 会销毁 command pool并释放 command buffer
## 当前实现的真实行为
### 命令缓冲模型
- 每个 `VulkanCommandList` 只拥有一个 primary `VkCommandBuffer`
- 没有 secondary command buffer 或 bundle 抽象
- command pool 使用 `VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT`
- `Reset()` 使用 `VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT` 开始录制
因此它更接近“短生命周期、一次性录制”的命令列表模型。
### render pass 两条路径并存
当前图形录制有两条路径:
1. 显式路径:`BeginRenderPass(renderPass, framebuffer, ...)`
2. 便捷路径:`SetRenderTargets()` 后,在 `Draw()` / `DrawIndexed()` 前由 `EnsureGraphicsRenderPass()` 自动创建临时 framebuffer 并开始 render pass
这对上层调用很方便,但也意味着两套语义并存。工程上最好在同一条渲染路径里保持一致,不要一会儿显式 render pass一会儿依赖懒创建路径。
### 资源状态转换
- `TransitionBarrier()` 当前只真正处理 texture 视图
- `stateBefore` 参数在实现里被显式忽略
- 实际转换更依赖 `VulkanTexture` 内部记录的当前状态
这说明当前 barrier 模型是“软件跟踪状态 + backend 内部翻译”的简化实现,不是完全按照调用方给出的 before/after 做严格校验。
### shader 与 pipeline
- `SetShader()` 当前是 no-op
- 真正影响 draw / dispatch 的是 `SetPipelineState()`
- `Draw()` / `DrawIndexed()` 会在确认 render pass 有效后直接绑定 `m_currentPipelineState->GetPipeline()`
- `Dispatch()` 会要求当前 pipeline state 拥有 compute shader并先 `EnsureValid()`
因此对 Vulkan 后端来说,`SetShader()` 只是保留抽象层接口兼容性,实际录制应以 pipeline state 为中心。
### descriptor 绑定
- `SetGraphicsDescriptorSets()` 可以从显式传入的 `pipelineLayout`,或当前 `m_currentPipelineState` 推导 native pipeline layout
- `SetComputeDescriptorSets()` 要求调用方显式传入 `pipelineLayout`
- 两者最终都用 `vkCmdBindDescriptorSets()`
这和 Vulkan 原生模型一致descriptor set 绑定必须匹配 pipeline layout。
### 固定功能状态
- `SetViewport()` 会把 `y` 设为 `topLeftY + height`,并把 `height` 设为负值
- 这样可以让上层继续使用和 D3D12/OpenGL 接近的左上角原点语义
- `SetViewports()` / `SetScissorRects()` 当前只取第一个元素
- `SetStencilRef()``SetBlendFactor()` 当前都是 no-op
这体现了当前 RHI 的设计取向:优先维持跨后端的一致调用体验,其次再补完 Vulkan 的细粒度状态控制。
### clear / copy / dispatch
- `Clear()``ClearRenderTarget()``ClearDepthStencil()` 会结束当前 render pass并通过 `vkCmdClearColorImage()` / `vkCmdClearDepthStencilImage()` 直接清图
- `CopyResource()` 当前覆盖了 texture-to-texture 与 buffer-to-buffer 路径
- `Dispatch()` 会先结束活跃 render pass再切到 compute pipeline bind point
这说明当前实现已经支持基础 graphics + compute 混合录制,但不是更高级的 render graph 调度模型。
## 线程语义
从实现看,`VulkanCommandList` 没有内部同步原语。更可靠的使用方式是:
- 同一个命令列表对象一次只在一个线程录制
- `Reset()`、录制、`Close()` 由调用方串行完成
- 提交后不要在 GPU 仍可能使用该命令缓冲时立刻跨线程复用它
## 所有权与临时资源
- 命令列表拥有自己的 command pool 和 command buffer
- 通过 `EnsureGraphicsRenderPass()` 创建的 transient framebuffer 由命令列表暂存,并在 `Reset()` / `Shutdown()` 时销毁
- 当前颜色/深度目标只是借用指针,不拥有对应 texture / view
这很像商业引擎里常见的“命令列表拥有短命 backend 辅助对象,但不拥有上层资源”的模式。
## 设计取向
`VulkanCommandList` 的实现不是“把 Vulkan API 原样套壳”,而是有明显的工程化折中:
- 保留 RHI 统一接口,减少上层为后端分支代码
- 在 backend 内部处理 viewport 翻转与 render pass 拼装
- 用软件状态跟踪简化 barrier 使用
好处是上层更容易写出跨后端测试和样例;代价是 Vulkan 高级能力目前没有全部暴露,某些接口行为也比名字看起来更受限。
如果拿 Unity 对照,可以把它理解成更靠近 SRP backend 内部 command recording 层,而不是把 Vulkan command buffer 语义原封不动扔给业务层。
## 当前限制
- 只封装一个 primary command buffer
- `SetShader()` 当前无效果
- `TransitionBarrier()` 忽略 `stateBefore`
- `SetViewports()` / `SetScissorRects()` 只使用第一个元素
- `SetStencilRef()` / `SetBlendFactor()` 当前无效果
- 自动 render pass 路径只围绕当前颜色目标和可选深度目标工作
## 主要公开方法
- `bool Initialize(VulkanDevice* device)`
- `void Shutdown()`
- `void Reset()`
- `void Close()`
- `void TransitionBarrier(RHIResourceView* resource, ResourceStates stateBefore, ResourceStates stateAfter)`
- `void BeginRenderPass(RHIRenderPass* renderPass, RHIFramebuffer* framebuffer, const Rect& renderArea, uint32_t clearValueCount, const ClearValue* clearValues)`
- `void EndRenderPass()`
- `void SetShader(RHIShader* shader)`
- `void SetPipelineState(RHIPipelineState* pso)`
- `void SetGraphicsDescriptorSets(...)`
- `void SetComputeDescriptorSets(...)`
- `void SetPrimitiveTopology(PrimitiveTopology topology)`
- `void SetViewport(const Viewport& viewport)`
- `void SetViewports(uint32_t count, const Viewport* viewports)`
- `void SetScissorRect(const Rect& rect)`
- `void SetScissorRects(uint32_t count, const Rect* rects)`
- `void SetRenderTargets(uint32_t count, RHIResourceView** renderTargets, RHIResourceView* depthStencil = nullptr)`
- `void SetVertexBuffers(...)`
- `void SetIndexBuffer(RHIResourceView* buffer, uint64_t offset)`
- `void Draw(...)`
- `void DrawIndexed(...)`
- `void Clear(...)`
- `void ClearRenderTarget(...)`
- `void ClearDepthStencil(...)`
- `void CopyResource(RHIResourceView* dst, RHIResourceView* src)`
- `void Dispatch(uint32_t x, uint32_t y, uint32_t z)`
## 相关测试与使用线索
- `tests/RHI/unit/test_command_list.cpp` 覆盖了抽象命令列表的大部分基础行为
- `tests/RHI/unit/test_vulkan_graphics.cpp` 直接验证了 Vulkan render pass、framebuffer、copy、graphics pipeline 与 compute dispatch 路径
- `tests/RHI/integration/fixtures/RHIIntegrationFixture.cpp` 展示了交换链回读、render target 设置和提交流程
## 相关指南
- [Devices, Queues, Command Lists, And Resource Creation](../../../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md)
## 相关文档
- [Vulkan](../Vulkan.md)
- [VulkanDevice](../VulkanDevice/VulkanDevice.md)
- [VulkanCommandQueue](../VulkanCommandQueue/VulkanCommandQueue.md)
- [VulkanPipelineState](../VulkanPipelineState/VulkanPipelineState.md)
- [VulkanRenderPass](../VulkanRenderPass/VulkanRenderPass.md)
- [VulkanFramebuffer](../VulkanFramebuffer/VulkanFramebuffer.md)
- [RHICommandList](../../RHICommandList/RHICommandList.md)

View File

@@ -0,0 +1,36 @@
# VulkanCommandQueue::ExecuteCommandLists
```cpp
void ExecuteCommandLists(uint32_t count, void** lists) override;
```
## 作用
提交一组 `VulkanCommandList` 到当前 `VkQueue` 执行。
## 前置条件
- 队列已经初始化
- `lists` 中的命令列表已经完成录制并 `Close()`
## 当前实现行为
- 会遍历输入数组,筛出有效的 `VkCommandBuffer`
- 用一个 `VkSubmitInfo` 提交全部命令缓冲
- 提交后立即调用 `vkQueueWaitIdle()`
- 成功提交流程后 `m_currentFrame` 自增
## 注意事项
当前实现是同步提交模型,不是多帧并发的异步提交模型。
## 参数
- `count` - 输入命令列表数量
- `lists` - 命令列表数组,元素应能向下转为 `VulkanCommandList`
## 相关文档
- [VulkanCommandQueue](VulkanCommandQueue.md)
- [WaitForIdle](WaitForIdle.md)
- [VulkanCommandList](../VulkanCommandList/VulkanCommandList.md)

View File

@@ -0,0 +1,23 @@
# VulkanCommandQueue::GetCompletedValue
```cpp
uint64_t GetCompletedValue() override;
```
## 作用
返回当前队列记录的完成值。
## 当前实现行为
- 当前直接返回 `m_currentFrame`
- 这个值在 `ExecuteCommandLists()` 提交并 `WaitIdle()` 之后自增
## 解释
它表示的是“本地已完成的提交计数”,不是 Vulkan 驱动侧某个原生同步对象的完成值。
## 相关文档
- [VulkanCommandQueue](VulkanCommandQueue.md)
- [ExecuteCommandLists](ExecuteCommandLists.md)

View File

@@ -0,0 +1,30 @@
# VulkanCommandQueue::Initialize
```cpp
bool Initialize(VulkanDevice* device, CommandQueueType type);
```
## 作用
把命令队列对象绑定到 Vulkan 设备和一个抽象队列类型。
## 当前实现行为
- 要求 `device` 非空且 `device->GetGraphicsQueue()` 有效
- 保存 `m_device`
-`m_queue` 直接设为 `device->GetGraphicsQueue()`
- 保存 `m_type`
## 重要限制
虽然方法接收 `CommandQueueType`,但当前底层总是使用 graphics queue不会区分独立的 compute / copy queue。
## 返回值
- `true` - 初始化成功
- `false` - 设备或原生队列无效
## 相关文档
- [VulkanCommandQueue](VulkanCommandQueue.md)
- [ExecuteCommandLists](ExecuteCommandLists.md)

View File

@@ -0,0 +1,20 @@
# VulkanCommandQueue::Signal
```cpp
void Signal(RHIFence* fence, uint64_t value) override;
```
## 作用
向抽象 fence 写入一个完成值。
## 当前实现行为
- 当前不会向 Vulkan native fence 或 semaphore 发信号
- 如果 `fence != nullptr`,只是转发调用 `fence->Signal(value)`
## 相关文档
- [VulkanCommandQueue](VulkanCommandQueue.md)
- [Wait](Wait.md)
- [VulkanFence](../VulkanFence/VulkanFence.md)

View File

@@ -0,0 +1,120 @@
# VulkanCommandQueue
**命名空间**: `XCEngine::RHI`
**类型**: `class`
**头文件**: `XCEngine/RHI/Vulkan/VulkanCommandQueue.h`
**描述**: Vulkan 后端里的 `RHICommandQueue` 实现,负责把 `VulkanCommandList` 提交到 `VkQueue` 执行。
## 概览
在抽象层里,[RHICommandQueue](../../RHICommandQueue/RHICommandQueue.md) 代表“命令提交入口”。在 Vulkan 后端中,`VulkanCommandQueue` 把这个概念落到一个 `VkQueue` 上,并承担提交、等待空闲和轻量 fence 协调。
它的职责边界比较清晰:
- 不负责录制命令,只负责提交已关闭的 [VulkanCommandList](../VulkanCommandList/VulkanCommandList.md)
- 不负责交换链图像获取,那属于 [VulkanSwapChain](../VulkanSwapChain/VulkanSwapChain.md)
- 不实现复杂调度、并发提交或跨队列同步
## 生命周期
推荐顺序是:
1. 由 [VulkanDevice](../VulkanDevice/VulkanDevice.md) 创建队列
2. 调用 `Initialize(VulkanDevice*, CommandQueueType)`
3. 多次 `ExecuteCommandLists()`
4. 退出前调用 `WaitForIdle()`
5. 最后 `Shutdown()`
按当前实现,`Shutdown()` 只是清空内部句柄与帧计数,不会销毁 Vulkan device 本身。
## 当前实现的真实行为
### 队列类型与 native queue 的对应关系
- `Initialize(device, type)` 会保存 `type`
- 但实际拿到的 native queue 始终是 `device->GetGraphicsQueue()`
这意味着当前 `CommandQueueType` 更像“抽象层标签”,不是严格映射到不同的 Vulkan queue family。即使创建的是 compute 或 copy 队列,底层当前仍然走 graphics queue。
### 提交行为
`ExecuteCommandLists()` 的实现流程是:
1. 从传入列表里筛出有效的 `VkCommandBuffer`
2. 组装一个 `VkSubmitInfo`
3. 调用 `vkQueueSubmit()`
4. 立刻调用 `vkQueueWaitIdle()`
5. `m_currentFrame` 自增
这说明当前实现是同步提交模型,而不是高吞吐的异步帧管线模型。它更适合测试、原型和早期后端打通,不适合把它误解成“已经完成商业级多帧并发提交”。
### Fence 语义
- `Signal()` 只是转发到 `RHIFence::Signal(value)`
- `Wait()` 只是转发到 `RHIFence::Wait(value)`
- `GetCompletedValue()` 返回的是 `m_currentFrame`
- `WaitForPreviousFrame()` 当前是空实现
结合 [VulkanFence](../VulkanFence/VulkanFence.md) 的实现看,当前 fence 不是 Vulkan 原生 `VkFence` 时间线同步封装,而是一个轻量软件计数器模型。
## 线程语义
从当前源码看,`VulkanCommandQueue` 本身没有内部加锁。更安全的使用假设是:
- 同一个队列对象由调用方串行使用
- 命令列表关闭后再提交
- 不要在多个线程上同时对同一个队列做提交与等待
商业引擎里真正复杂的多线程提交通常会放在 render scheduler 或 frame graph 层,而不是直接压在 queue 包装类本身。
## 设计取向
这个类的设计明显偏向“让抽象层测试先跑通”:
- 好处是行为直接、调试容易、后端差异小
- 代价是每次提交都会 `WaitIdle()`,吞吐量和 CPU/GPU 并行度都比较弱
如果把 Unity 作类比,它更像引擎内部一条被严格串行化的 backend submit lane而不是已经具备 frame overlap、parallel submit 和 timeline semaphore 协调的成熟提交系统。
## 当前限制
- 当前所有队列类型都落到同一个 graphics queue
- `ExecuteCommandLists()` 提交后立即阻塞到空闲
- `WaitForPreviousFrame()` 为空实现
- `GetCompletedValue()` 代表的是本地提交计数,不是 Vulkan 驱动侧完成值
## 主要公开方法
- `bool Initialize(VulkanDevice* device, CommandQueueType type)`
- `void Shutdown()`
- `void ExecuteCommandLists(uint32_t count, void** lists)`
- `void Signal(RHIFence* fence, uint64_t value)`
- `void Wait(RHIFence* fence, uint64_t value)`
- `uint64_t GetCompletedValue()`
- `void WaitForIdle()`
- `CommandQueueType GetType() const`
- `uint64_t GetTimestampFrequency() const`
- `void* GetNativeHandle()`
- `void WaitForPreviousFrame()`
- `uint64_t GetCurrentFrame() const`
## 相关测试与使用线索
- `tests/RHI/unit/test_command_queue.cpp` 覆盖了抽象命令队列的创建、提交、完成值与等待接口
- `tests/RHI/unit/test_device.cpp` 会通过设备创建队列并验证返回值
- `tests/RHI/integration/fixtures/RHIIntegrationFixture.cpp` 展示了队列与交换链、命令列表的组合使用
## 相关指南
- [Devices, Queues, Command Lists, And Resource Creation](../../../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md)
## 相关文档
- [Vulkan](../Vulkan.md)
- [VulkanDevice](../VulkanDevice/VulkanDevice.md)
- [VulkanCommandList](../VulkanCommandList/VulkanCommandList.md)
- [VulkanFence](../VulkanFence/VulkanFence.md)
- [RHICommandQueue](../../RHICommandQueue/RHICommandQueue.md)

View File

@@ -0,0 +1,24 @@
# VulkanCommandQueue::Wait
```cpp
void Wait(RHIFence* fence, uint64_t value) override;
```
## 作用
等待抽象 fence 至少达到目标值。
## 当前实现行为
- 当前不会做 Vulkan queue 级别的原生等待
- 如果 `fence != nullptr`,只是转发 `fence->Wait(value)`
## 注意事项
结合当前 `VulkanFence` 的实现,这更接近软件计数器同步,而不是 GPU 真正阻塞等待。
## 相关文档
- [VulkanCommandQueue](VulkanCommandQueue.md)
- [Signal](Signal.md)
- [VulkanFence](../VulkanFence/VulkanFence.md)

View File

@@ -0,0 +1,24 @@
# VulkanCommandQueue::WaitForIdle
```cpp
void WaitForIdle() override;
```
## 作用
阻塞直到当前 `VkQueue` 空闲。
## 当前实现行为
- 如果 `m_queue` 有效,直接调用 `vkQueueWaitIdle(m_queue)`
## 使用场景
- 交换链重建前
- 截图或调试路径需要强制同步时
- 关停设备或销毁依赖资源前
## 相关文档
- [VulkanCommandQueue](VulkanCommandQueue.md)
- [ExecuteCommandLists](ExecuteCommandLists.md)

View File

@@ -0,0 +1,83 @@
# VulkanCommon
**命名空间**: `XCEngine::RHI`
**类型**: `utility header`
**头文件**: `XCEngine/RHI/Vulkan/VulkanCommon.h`
**描述**: Vulkan 后端共享的公共内联工具头,集中定义平台开关、字符串转换以及 RHI 枚举到 Vulkan 枚举的翻译函数。
## 概览
`VulkanCommon.h` 不是资源对象或系统类,而是整个 Vulkan backend 的基础工具层。它承担的职责包括:
- 启用 `VK_USE_PLATFORM_WIN32_KHR`
- 引入 `<vulkan/vulkan.h>`
- 提供窄字节 / 宽字节辅助转换
- 提供 shader stage、格式、拓扑、采样、比较、混合、descriptor、load/store 等转换函数
几乎所有 Vulkan backend 类型都会直接或间接依赖这个头。
## 当前实现中包含的核心能力
### 字符串与 shader stage 辅助
- `NarrowAscii(const std::wstring&)`
- `WidenAscii(const char*)`
- `TryResolveShaderTypeFromTarget(const char* target, ShaderType& type)`
这部分主要服务 shader 编译与 shader module 初始化路径。
### 格式与图像相关转换
- `ToVulkanFormat(Format)`
- `ToRHIFormat(VkFormat)`
- `GetFormatSize(Format)`
- `GetImageAspectMask(Format)`
这部分直接影响 texture、resource view、render pass 和截图逻辑。
### 图形状态转换
文件中还包含大量 RHI 到 Vulkan 的枚举映射,例如:
- primitive topology
- polygon mode
- cull mode
- front face
- compare op
- blend factor / blend op
- sampler filter / address mode
- descriptor type / shader stage flags
- attachment load / store op
- index type
这些函数本质上是 Vulkan backend 的“词典层”。
## 设计取向
把 backend 共享的转换逻辑集中放在一个公共头里,是很常见的商业引擎做法:
- 好处是翻译规则集中、复用高、后续修订不容易遗漏
- 代价是这个头会变成很多 Vulkan 类型的基础依赖
## 当前限制
- 当前平台宏默认走 Win32
- 这里只提供转换与辅助,不负责资源生命周期或错误恢复
- 如果 RHI 枚举继续扩展,这里的映射函数也要同步补齐
## 典型使用位置
- [VulkanDevice](../VulkanDevice/VulkanDevice.md)
- [VulkanCommandList](../VulkanCommandList/VulkanCommandList.md)
- [VulkanPipelineState](../VulkanPipelineState/VulkanPipelineState.md)
- [VulkanResourceView](../VulkanResourceView/VulkanResourceView.md)
- [VulkanShaderCompiler](../VulkanShaderCompiler/VulkanShaderCompiler.md)
## 相关文档
- [Vulkan](../Vulkan.md)
- [RHIEnums](../../RHIEnums/RHIEnums.md)
- [RHITypes](../../RHITypes/RHITypes.md)

View File

@@ -0,0 +1,81 @@
# VulkanDescriptorPool
**命名空间**: `XCEngine::RHI`
**类型**: `class`
**头文件**: `XCEngine/RHI/Vulkan/VulkanDescriptorPool.h`
**描述**: Vulkan 后端里的 `RHIDescriptorPool` 实现,负责创建 `VkDescriptorPool`,并从中分配 `VulkanDescriptorSet`
## 概览
`VulkanDescriptorPool` 是 descriptor set 的分配器。当前实现里,它主要解决两件事:
- 按 heap 类型创建一个 `VkDescriptorPool`
- 根据传入 layout 即时创建 descriptor set layout并分配一个 `VulkanDescriptorSet`
## 当前实现的真实行为
### 支持的 pool 类型
`Initialize()` 当前只覆盖两类 `DescriptorHeapType`
- `CBV_SRV_UAV`
- `Sampler`
其中:
- `CBV_SRV_UAV` 会同时为 `UNIFORM_BUFFER``SAMPLED_IMAGE``STORAGE_IMAGE` 预留描述符容量
- `Sampler` 只为 `VK_DESCRIPTOR_TYPE_SAMPLER` 预留容量
### 分配策略
`AllocateSet(layout)` 的当前流程是:
1. 先根据 `DescriptorSetLayoutDesc` 临时构建一个 `VkDescriptorSetLayout`
2. 再从 pool 分配一个 `VkDescriptorSet`
3. 最后创建 `VulkanDescriptorSet`
也就是说,当前 descriptor set layout 不是被统一缓存复用的,而是“分配一个 set就顺手创建一份 layout”。
### 销毁策略
- `Shutdown()` 会清空已分配 set 列表,并销毁 `VkDescriptorPool`
- `FreeSet()` 会把目标 set 从列表中移除并 `delete`
- 真正的 descriptor set 与 descriptor set layout 释放,由 `VulkanDescriptorSet::Shutdown()` 完成
## 线程语义
没有内部锁。分配与释放应由调用方串行协调。
## 设计取向
当前实现优先的是“descriptor set 能快速用起来”而不是“descriptor layout / pool 有完善的缓存和复用体系”:
- 优点是实现简单,便于抽象层先跑通
- 代价是 layout 创建频率高,资源复用策略比较初级
## 当前限制
- 当前只覆盖 `CBV_SRV_UAV``Sampler` 两类 pool
- 每次 `AllocateSet()` 都会新建一个 `VkDescriptorSetLayout`
- 没有更高级的池分段、碎片管理或 layout 缓存
## 主要公开方法
- `bool Initialize(VkDevice device, const DescriptorPoolDesc& desc)`
- `bool Initialize(const DescriptorPoolDesc& desc)`
- `void Shutdown()`
- `void* GetNativeHandle()`
- `uint32_t GetDescriptorCount() const`
- `DescriptorHeapType GetType() const`
- `RHIDescriptorSet* AllocateSet(const DescriptorSetLayoutDesc& layout)`
- `void FreeSet(RHIDescriptorSet* set)`
## 相关文档
- [Vulkan](../Vulkan.md)
- [VulkanDescriptorSet](../VulkanDescriptorSet/VulkanDescriptorSet.md)
- [VulkanPipelineLayout](../VulkanPipelineLayout/VulkanPipelineLayout.md)
- [RHIDescriptorPool](../../RHIDescriptorPool/RHIDescriptorPool.md)

View File

@@ -0,0 +1,109 @@
# VulkanDescriptorSet
**命名空间**: `XCEngine::RHI`
**类型**: `class`
**头文件**: `XCEngine/RHI/Vulkan/VulkanDescriptorSet.h`
**描述**: Vulkan 后端里的 `RHIDescriptorSet` 实现,封装 `VkDescriptorSet`,并提供 image、sampler 与常量缓冲更新逻辑。
## 概览
`VulkanDescriptorSet` 是当前 Vulkan 后端里真正承载资源绑定内容的对象。它既保存布局绑定信息,也负责把资源写入 `VkDescriptorSet`
## 当前实现的真实行为
### 初始化
初始化时会保存:
- 所属 `VulkanDescriptorPool`
- `VkDescriptorSetLayout`
- `VkDescriptorSet`
- 深拷贝后的 layout bindings
- binding 到索引的映射表
### `Bind()` / `Unbind()`
这两个接口当前都是 no-op。真正把 descriptor set 送进命令缓冲的动作,发生在 [VulkanCommandList](../VulkanCommandList/VulkanCommandList.md) 的 `SetGraphicsDescriptorSets()` / `SetComputeDescriptorSets()` 中。
### 资源更新能力边界
`Update(offset, view)` 当前只处理 image 类描述符:
- 需要 `VulkanResourceView` 持有有效 `VkImageView`
- 会按 binding 类型写入 sampled image 或 storage image
它不负责一般 buffer SRV/UAV 的完整 typed/structured buffer 描述符体系。
`UpdateSampler(offset, sampler)` 当前处理 sampler 描述符:
- 要求 binding 类型是 `DescriptorType::Sampler`
- 要求传入有效 `VulkanSampler`
### 常量写入
`WriteConstant(binding, data, size, offset)` 是当前实现里最值得注意的部分:
- 只对 `DescriptorType::CBV` 生效
- 每个常量 binding 会维护一个 `ConstantBindingRecord`
- 当需要时,会动态创建一块 `VulkanBuffer`
- 这块 buffer 用 `VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT` 和 host-visible / host-coherent 内存创建
- 然后把常量数据写进去,并更新 descriptor
因此当前 CBV 路径并不是绑定一块外部统一管理的大常量缓冲而是“descriptor set 自己为每个 binding 管一块小 buffer”。
### Dirty 标记
- `WriteConstant()` 会把 `m_constantDirty` 置为 `true`
- `MarkConstantClean()` 由外部显式调用清理标记
## 线程语义
没有内部锁。descriptor 更新应由调用方在安全时机串行完成,避免与命令录制并发修改同一个 set。
## 所有权与资源管理
- `VulkanDescriptorSet` 拥有自己的 `VkDescriptorSetLayout`
- 拥有 `VkDescriptorSet`
- 还可能拥有为常量 binding 动态分配的 `VulkanBuffer`
- `Shutdown()` 会释放 descriptor set、销毁 layout并清空常量缓冲记录
## 设计取向
当前实现明显偏向“先把常见图像、采样器和 CBV 路径跑通”:
- 优点是 descriptor set 能快速服务当前 graphics / compute 测试
- 代价是 buffer 描述符体系、资源复用和常量缓冲管理都还比较朴素
## 当前限制
- `Bind()` / `Unbind()` 无实际行为
- `Update()` 主要处理 image 描述符
- `WriteConstant()` 为每个 CBV binding 动态维护小 buffer不是成熟的统一常量分配器
## 主要公开方法
- `bool Initialize(VkDevice device, VulkanDescriptorPool* pool, VkDescriptorSetLayout layout, VkDescriptorSet descriptorSet, const DescriptorSetLayoutDesc& desc)`
- `void Shutdown()`
- `void Bind()`
- `void Unbind()`
- `void Update(uint32_t offset, RHIResourceView* view)`
- `void UpdateSampler(uint32_t offset, RHISampler* sampler)`
- `void WriteConstant(uint32_t binding, const void* data, size_t size, size_t offset = 0)`
- `uint32_t GetBindingCount() const`
- `const DescriptorSetLayoutBinding* GetBindings() const`
- `void* GetConstantBufferData()`
- `size_t GetConstantBufferSize() const`
- `bool IsConstantDirty() const`
- `void MarkConstantClean()`
## 相关文档
- [Vulkan](../Vulkan.md)
- [VulkanDescriptorPool](../VulkanDescriptorPool/VulkanDescriptorPool.md)
- [VulkanSampler](../VulkanSampler/VulkanSampler.md)
- [VulkanResourceView](../VulkanResourceView/VulkanResourceView.md)
- [VulkanCommandList](../VulkanCommandList/VulkanCommandList.md)
- [RHIDescriptorSet](../../RHIDescriptorSet/RHIDescriptorSet.md)

View File

@@ -0,0 +1,36 @@
# VulkanDevice::CreateBuffer
```cpp
RHIBuffer* CreateBuffer(const BufferDesc& desc) override;
```
## 作用
创建一个 `VulkanBuffer`,并根据 `BufferDesc` 推导基础 `VkBufferUsageFlags`
## 当前实现行为
- 一定会附加 `TRANSFER_DST``TRANSFER_SRC`
- 根据 `desc.bufferType` 决定附加 `INDEX_BUFFER``UNIFORM_BUFFER``VERTEX_BUFFER`
- 当前默认使用 `HOST_VISIBLE | HOST_COHERENT` 内存属性创建 buffer
- 创建失败时返回 `nullptr`
## 参数
- `desc` - buffer 大小、步长和类型描述
## 返回值
- `RHIBuffer*` - 实际对象是 `VulkanBuffer`
- `nullptr` - 创建失败
## 设计说明
当前策略更偏“先易用、先可写”,而不是默认走 device-local 显存分层。
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanBuffer](../VulkanBuffer/VulkanBuffer.md)
- [CreateVertexBufferView](CreateVertexBufferView.md)
- [CreateIndexBufferView](CreateIndexBufferView.md)

View File

@@ -0,0 +1,30 @@
# VulkanDevice::CreateCommandList
```cpp
RHICommandList* CreateCommandList(const CommandListDesc& desc) override;
```
## 作用
创建一个 `VulkanCommandList`,用于录制图形或计算命令。
## 当前实现行为
- 当前实现并不使用 `desc` 里的细分字段
- 总是创建一个 `VulkanCommandList`
- 内部会调用 `VulkanCommandList::Initialize(this)`
- 失败时返回 `nullptr`
## 返回值
- `RHICommandList*` - 实际对象是 `VulkanCommandList`
- `nullptr` - 创建失败
## 注意事项
当前每个 `VulkanCommandList` 只持有一个 primary command buffer。
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanCommandList](../VulkanCommandList/VulkanCommandList.md)

View File

@@ -0,0 +1,25 @@
# VulkanDevice::CreateCommandQueue
```cpp
RHICommandQueue* CreateCommandQueue(const CommandQueueDesc& desc) override;
```
## 作用
创建一个 `VulkanCommandQueue`,作为命令提交入口。
## 当前实现行为
- 会把 `desc.queueType` 传给 `VulkanCommandQueue::Initialize(...)`
- 但当前 Vulkan 后端无论队列类型是什么,底层都仍然绑定到 graphics queue
- 创建失败返回 `nullptr`
## 返回值
- `RHICommandQueue*` - 实际对象是 `VulkanCommandQueue`
- `nullptr` - 创建失败
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanCommandQueue](../VulkanCommandQueue/VulkanCommandQueue.md)

View File

@@ -0,0 +1,21 @@
# VulkanDevice::CreateDepthStencilView
```cpp
RHIResourceView* CreateDepthStencilView(RHITexture* texture, const ResourceViewDesc& desc) override;
```
## 作用
为纹理创建 depth stencil view。
## 当前实现行为
- 实际对象是 `VulkanResourceView`
- 会调用 `InitializeAsDepthStencil(...)`
- 成功后持有深度/模板用途的 `VkImageView`
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [CreateTexture](CreateTexture.md)
- [VulkanResourceView](../VulkanResourceView/VulkanResourceView.md)

View File

@@ -0,0 +1,22 @@
# VulkanDevice::CreateDescriptorPool
```cpp
RHIDescriptorPool* CreateDescriptorPool(const DescriptorPoolDesc& desc) override;
```
## 作用
创建 Vulkan descriptor pool。
## 当前实现行为
- 实际对象是 `VulkanDescriptorPool`
- 在初始化前会先设置 `SetDeviceOwner(this)`
- 再调用 `Initialize(m_device, desc)`
- 失败时返回 `nullptr`
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanDescriptorPool](../VulkanDescriptorPool/VulkanDescriptorPool.md)
- [CreateDescriptorSet](CreateDescriptorSet.md)

View File

@@ -0,0 +1,26 @@
# VulkanDevice::CreateDescriptorSet
```cpp
RHIDescriptorSet* CreateDescriptorSet(RHIDescriptorPool* pool, const DescriptorSetLayoutDesc& layout) override;
```
## 作用
从指定 descriptor pool 分配一个 descriptor set。
## 当前实现行为
- 当前只是做一层转发
- 如果 `pool != nullptr`,直接调用 `pool->AllocateSet(layout)`
- 如果 `pool == nullptr`,返回 `nullptr`
## 返回值
- `RHIDescriptorSet*` - 实际对象通常是 `VulkanDescriptorSet`
- `nullptr` - pool 为空或分配失败
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanDescriptorPool](../VulkanDescriptorPool/VulkanDescriptorPool.md)
- [VulkanDescriptorSet](../VulkanDescriptorSet/VulkanDescriptorSet.md)

View File

@@ -0,0 +1,19 @@
# VulkanDevice::CreateFence
```cpp
RHIFence* CreateFence(const FenceDesc& desc) override;
```
## 作用
创建 Vulkan 后端的抽象 fence 对象。
## 当前实现行为
- 当前直接返回 `new VulkanFence(desc.initialValue)`
- 不会创建原生 `VkFence`
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanFence](../VulkanFence/VulkanFence.md)

View File

@@ -0,0 +1,21 @@
# VulkanDevice::CreateFramebuffer
```cpp
RHIFramebuffer* CreateFramebuffer(RHIRenderPass* renderPass, uint32_t width, uint32_t height, uint32_t colorAttachmentCount, RHIResourceView** colorAttachments, RHIResourceView* depthStencilAttachment) override;
```
## 作用
创建 Vulkan framebuffer。
## 当前实现行为
- 实际对象是 `VulkanFramebuffer`
- 调用 `Initialize(m_device, renderPass, width, height, colorAttachmentCount, colorAttachments, depthStencilAttachment)`
- 失败时返回 `nullptr`
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanFramebuffer](../VulkanFramebuffer/VulkanFramebuffer.md)
- [CreateRenderPass](CreateRenderPass.md)

View File

@@ -0,0 +1,21 @@
# VulkanDevice::CreateIndexBufferView
```cpp
RHIResourceView* CreateIndexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) override;
```
## 作用
为指定 buffer 创建索引缓冲视图。
## 当前实现行为
- 实际对象是 `VulkanResourceView`
- 会调用 `InitializeAsIndexBuffer(...)`
- 视图里会保存索引格式和 buffer 偏移等元数据
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [CreateBuffer](CreateBuffer.md)
- [VulkanResourceView](../VulkanResourceView/VulkanResourceView.md)

View File

@@ -0,0 +1,25 @@
# VulkanDevice::CreatePipelineLayout
```cpp
RHIPipelineLayout* CreatePipelineLayout(const RHIPipelineLayoutDesc& desc) override;
```
## 作用
创建 Vulkan pipeline layout把抽象 descriptor 布局翻译为原生 `VkPipelineLayout`
## 当前实现行为
- 实际对象类型是 `VulkanPipelineLayout`
- 当前调用的是 `Initialize(m_device, desc)` 这条真实可用路径
- 初始化失败返回 `nullptr`
## 返回值
- `RHIPipelineLayout*` - 实际对象是 `VulkanPipelineLayout`
- `nullptr` - 创建失败
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanPipelineLayout](../VulkanPipelineLayout/VulkanPipelineLayout.md)

View File

@@ -0,0 +1,26 @@
# VulkanDevice::CreatePipelineState
```cpp
RHIPipelineState* CreatePipelineState(const GraphicsPipelineDesc& desc) override;
```
## 作用
创建 Vulkan pipeline state 对象。
## 当前实现行为
- 实际对象类型是 `VulkanPipelineState`
- 内部立即调用 `VulkanPipelineState::Initialize(this, desc)`
- graphics 路径会在初始化阶段尝试建立图形管线
- 失败时返回 `nullptr`
## 返回值
- `RHIPipelineState*` - 实际对象是 `VulkanPipelineState`
- `nullptr` - 创建失败
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanPipelineState](../VulkanPipelineState/VulkanPipelineState.md)

View File

@@ -0,0 +1,21 @@
# VulkanDevice::CreateRenderPass
```cpp
RHIRenderPass* CreateRenderPass(uint32_t colorAttachmentCount, const AttachmentDesc* colorAttachments, const AttachmentDesc* depthStencilAttachment) override;
```
## 作用
创建 Vulkan render pass。
## 当前实现行为
- 实际对象是 `VulkanRenderPass`
- 调用 `Initialize(m_device, colorAttachmentCount, colorAttachments, depthStencilAttachment)`
- 失败时返回 `nullptr`
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanRenderPass](../VulkanRenderPass/VulkanRenderPass.md)
- [CreateFramebuffer](CreateFramebuffer.md)

View File

@@ -0,0 +1,21 @@
# VulkanDevice::CreateRenderTargetView
```cpp
RHIResourceView* CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) override;
```
## 作用
为纹理创建 render target view。
## 当前实现行为
- 实际对象是 `VulkanResourceView`
- 会调用 `InitializeAsRenderTarget(...)`
- 成功后持有一个 `VkImageView`
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [CreateTexture](CreateTexture.md)
- [VulkanResourceView](../VulkanResourceView/VulkanResourceView.md)

View File

@@ -0,0 +1,20 @@
# VulkanDevice::CreateSampler
```cpp
RHISampler* CreateSampler(const SamplerDesc& desc) override;
```
## 作用
创建 Vulkan sampler。
## 当前实现行为
- 实际对象是 `VulkanSampler`
- 调用 `Initialize(m_device, desc)`
- 失败时返回 `nullptr`
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanSampler](../VulkanSampler/VulkanSampler.md)

View File

@@ -0,0 +1,32 @@
# VulkanDevice::CreateShader
```cpp
RHIShader* CreateShader(const ShaderCompileDesc& desc) override;
```
## 作用
创建 Vulkan shader如果输入不是原始 SPIR-V会先经过 Vulkan shader 编译辅助流程。
## 当前实现行为
- 先构造 `VulkanShader`
- 调用 `CompileVulkanShader(desc, compiledShader, nullptr)`
- 再把编译产物中的 SPIR-V 交给 `VulkanShader::Compile(...)`
- 任一步失败都会删除对象并返回 `nullptr`
## 返回值
- `RHIShader*` - 实际对象是 `VulkanShader`
- `nullptr` - 编译或 module 创建失败
## 注意事项
- 当前 Vulkan 后端支持 `GLSL``SPIR-V`
- 当前不支持直接用 HLSL 源码走 Vulkan 路径
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanShader](../VulkanShader/VulkanShader.md)
- [VulkanShaderCompiler](../VulkanShaderCompiler/VulkanShaderCompiler.md)

View File

@@ -0,0 +1,21 @@
# VulkanDevice::CreateShaderResourceView
```cpp
RHIResourceView* CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) override;
```
## 作用
为纹理创建 shader resource view。
## 当前实现行为
- 实际对象是 `VulkanResourceView`
- 会调用 `InitializeAsShaderResource(...)`
- 当前 image view 路径主要覆盖单 mip、单 layer 的基础使用方式
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [CreateTexture](CreateTexture.md)
- [VulkanResourceView](../VulkanResourceView/VulkanResourceView.md)

View File

@@ -0,0 +1,32 @@
# VulkanDevice::CreateSwapChain
```cpp
RHISwapChain* CreateSwapChain(const SwapChainDesc& desc, RHICommandQueue* presentQueue) override;
```
## 作用
创建 `VulkanSwapChain`,把窗口句柄和呈现队列接入 Vulkan 呈现链路。
## 当前实现行为
- 会把 `presentQueue` 向下转型为 `VulkanCommandQueue`
- 会把 `desc.windowHandle` 解释为 `HWND__*`
- 内部调用 `VulkanSwapChain::Initialize(...)`
- 失败时返回 `nullptr`
## 参数
- `desc` - 交换链描述,当前主要使用窗口句柄、宽高和 buffer count 语义
- `presentQueue` - 呈现用命令队列;当前 Vulkan 后端仍然基于 graphics queue
## 返回值
- `RHISwapChain*` - 实际对象是 `VulkanSwapChain`
- `nullptr` - 创建失败
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanSwapChain](../VulkanSwapChain/VulkanSwapChain.md)
- [CreateCommandQueue](CreateCommandQueue.md)

View File

@@ -0,0 +1,46 @@
# VulkanDevice::CreateTexture
```cpp
RHITexture* CreateTexture(const TextureDesc& desc) override;
RHITexture* CreateTexture(const TextureDesc& desc, const void* initialData, size_t initialDataSize, uint32_t rowPitch = 0) override;
```
## 作用
创建 Vulkan 纹理;重载版本还会在创建后通过 staging buffer 上传初始数据。
## 前置条件
- `desc.width``desc.height` 必须非零
- `desc.format` 必须能映射到有效 `VkFormat`
## 当前实现行为
### 纯创建版本
- 创建自有 `VkImage`
- 会为图像附加当前后端需要的 usage 标志,例如 sampled、transfer、color/depth 或 storage
- 成功后返回 `VulkanTexture`
### 带初始数据版本
- 先调用无初始数据版本创建纹理
-`initialData` 非空且 `initialDataSize > 0`,会创建 staging buffer 并执行上传
- 上传成功后会把软件侧状态设置为 `ResourceStates::PixelShaderResource`
## 返回值
- `RHITexture*` - 实际对象是 `VulkanTexture`
- `nullptr` - 创建或上传失败
## 注意事项
- 当前上传路径是同步录制并提交单次使用命令,不是异步资源导入系统
- 纹理 view 需要后续再通过 `CreateRenderTargetView()``CreateShaderResourceView()` 等接口创建
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [VulkanTexture](../VulkanTexture/VulkanTexture.md)
- [CreateRenderTargetView](CreateRenderTargetView.md)
- [CreateShaderResourceView](CreateShaderResourceView.md)

View File

@@ -0,0 +1,21 @@
# VulkanDevice::CreateUnorderedAccessView
```cpp
RHIResourceView* CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) override;
```
## 作用
为纹理创建 unordered access view。
## 当前实现行为
- 实际对象是 `VulkanResourceView`
- 会调用 `InitializeAsUnorderedAccess(...)`
- 当前主要服务 compute shader 对 storage image 的访问
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [CreateTexture](CreateTexture.md)
- [VulkanResourceView](../VulkanResourceView/VulkanResourceView.md)

View File

@@ -0,0 +1,21 @@
# VulkanDevice::CreateVertexBufferView
```cpp
RHIResourceView* CreateVertexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) override;
```
## 作用
为指定 buffer 创建顶点缓冲视图。
## 当前实现行为
- 实际对象是 `VulkanResourceView`
- 会调用 `InitializeAsVertexBuffer(...)`
- 当前不会创建 `VkBufferView`,而是保存 buffer 指针、偏移、大小和步长信息
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [CreateBuffer](CreateBuffer.md)
- [VulkanResourceView](../VulkanResourceView/VulkanResourceView.md)

View File

@@ -0,0 +1,33 @@
# VulkanDevice::FindMemoryType
```cpp
uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) const;
```
## 作用
在当前物理设备的内存类型表里查找满足条件的内存类型索引。
## 当前实现行为
- 会遍历物理设备暴露的 memory types
- 要求索引命中 `typeFilter`
- 同时要求该类型包含调用方请求的 `properties`
- 找不到时返回 `UINT32_MAX`
## 使用场景
- `VulkanBuffer` 分配内存
- 纹理与 staging 资源分配内存
- 截图 readback staging buffer 分配
## 返回值
- 有效索引 - 可直接写入 `VkMemoryAllocateInfo::memoryTypeIndex`
- `UINT32_MAX` - 没找到匹配项
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [CreateBuffer](CreateBuffer.md)
- [CreateTexture](CreateTexture.md)

View File

@@ -0,0 +1,41 @@
# VulkanDevice::Initialize
```cpp
bool Initialize(const RHIDeviceDesc& desc) override;
```
## 作用
初始化 Vulkan 后端设备根对象,建立 `VkInstance`、选择物理设备、创建逻辑设备和图形队列,并填充设备信息缓存。
## 前置条件
- 进程环境里能加载 Vulkan runtime
- 当前平台路径需要支持 Win32 surface 扩展
- 如果对象已经初始化过,当前实现会直接返回 `true`
## 当前实现行为
- 会先保存 `desc`
- 按顺序调用 `CreateInstance()``PickPhysicalDevice()``CreateLogicalDevice()``QueryDeviceInfo()`
- 任一步失败都会调用 `Shutdown()` 回滚,并返回 `false`
- `desc.enableDebugLayer``desc.enableGPUValidation` 当前不会真正启用 Vulkan validation layers
## 参数
- `desc` - 设备初始化描述;当前会被保存,但其中调试层相关开关尚未真正接线
## 返回值
- `true` - 初始化完成,后续可以创建队列、命令列表、交换链与资源
- `false` - 初始化失败,设备对象会尽量回滚到未初始化状态
## 线程语义
应在渲染初始化阶段由单线程调用,不要并发初始化同一个设备对象。
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [Shutdown](Shutdown.md)
- [FindMemoryType](FindMemoryType.md)

View File

@@ -0,0 +1,29 @@
# VulkanDevice::Shutdown
```cpp
void Shutdown() override;
```
## 作用
释放 `VulkanDevice` 持有的 Vulkan 根对象,并把内部状态重置为未初始化。
## 当前实现行为
- 会销毁逻辑设备、实例以及相关缓存状态
- 会清空图形队列句柄、队列族索引、设备信息和 capability 缓存
- 析构函数也会调用 `Shutdown()`
## 使用建议
- 在调用它之前,先销毁由该设备创建出来的队列、交换链、命令列表、资源和状态对象
- 不要把它当成“热重载设备”的轻量操作,它属于完整关停路径
## 线程语义
应由调用方保证没有其他线程仍在使用该设备创建的对象。
## 相关文档
- [VulkanDevice](VulkanDevice.md)
- [Initialize](Initialize.md)

View File

@@ -0,0 +1,153 @@
# VulkanDevice
**命名空间**: `XCEngine::RHI`
**类型**: `class`
**头文件**: `XCEngine/RHI/Vulkan/VulkanDevice.h`
**描述**: Vulkan 后端里的 `RHIDevice` 实现,负责创建 `VkInstance`、选择物理设备、创建逻辑设备,并作为 Vulkan 资源工厂的根入口。
## 概览
如果把抽象层里的 [RHIDevice](../../RHIDevice/RHIDevice.md) 理解成“跨后端的设备契约”,那么 `VulkanDevice` 就是这份契约在 Vulkan 上的具体落地。
它承担三类职责:
- 建立 Vulkan 运行时根对象:`VkInstance``VkPhysicalDevice``VkDevice`、图形队列与队列族索引。
- 把 RHI 抽象描述翻译为 Vulkan 对象buffer、texture、swap chain、pipeline、descriptor、render pass、framebuffer 等都从这里创建。
- 提供 Vulkan 专属查询能力:例如 `GetInstance()``GetPhysicalDevice()``GetDevice()``GetGraphicsQueue()``FindMemoryType()`
这类设计和商业引擎里的 backend device 很接近。上层系统不应直接依赖 Vulkan 原生对象做资源管理,而应把 `VulkanDevice` 当成 Vulkan 后端的“实现根”。
## 生命周期
推荐顺序是:
1. 默认构造 `VulkanDevice`
2. 调用 `Initialize(const RHIDeviceDesc&)`
3. 通过设备创建队列、命令列表、交换链、资源与状态对象
4. 先释放由设备创建出来的对象
5. 最后调用 `Shutdown()`
`engine/src/RHI/Vulkan/VulkanDevice.cpp` 的当前实现,`Initialize()` 的内部顺序固定为:
1. `CreateInstance()`
2. `PickPhysicalDevice()`
3. `CreateLogicalDevice()`
4. `QueryDeviceInfo()`
如果中途失败,会调用 `Shutdown()` 做回滚。析构函数也会调用 `Shutdown()`,因此“忘记显式关停”通常不会泄漏根对象,但工程上仍应把显式 `Shutdown()` 视为标准用法。
## 当前实现的真实行为
### 设备初始化与平台边界
- `CreateInstance()` 当前固定请求 `VK_KHR_SURFACE_EXTENSION_NAME``VK_KHR_WIN32_SURFACE_EXTENSION_NAME`
- 这说明当前公开实现是 Win32 路径,不是跨平台 surface 创建实现。
- `RHIDeviceDesc` 里的 `enableDebugLayer``enableGPUValidation` 会被保存,但当前 `CreateInstance()` / `CreateLogicalDevice()` 并没有据此启用 Vulkan validation layers。
这点文档上必须讲清楚,因为接口形状看起来像“支持调试层开关”,但当前 Vulkan 实现并没有真正接线。
### 物理设备与队列选择
- `PickPhysicalDevice()` 会遍历物理设备,选择第一个拥有 `VK_QUEUE_GRAPHICS_BIT` 的设备。
- 当前只记录一个 `m_graphicsQueueFamilyIndex`
- `CreateLogicalDevice()` 只创建一个队列,并从这个队列族取出一个图形队列。
因此当前实现更接近“单图形队列 backend”而不是已经完整建好的多队列模型。即使 RHI 抽象里存在 `Direct / Compute / Copy` 等概念Vulkan 后端目前也没有拆成独立 native queue。
### 资源工厂行为
- `CreateBuffer()` 默认使用 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
- 这意味着当前 buffer 创建策略偏向“先易用、先可写”,而不是默认走更激进的 device-local 显存路径。
- `CreateTexture(const TextureDesc&)` 创建的是自有 `VkImage`,默认带 `TRANSFER_SRC``TRANSFER_DST``SAMPLED` 等 usage并根据格式决定是否附加 color/depth/storage 用途。
- `CreateTexture(..., initialData, ...)` 会先建纹理,再通过 staging buffer 上传,成功后把软件侧状态设为 `ResourceStates::PixelShaderResource`
- `CreateShader()` 不是直接从文本生成 `VkShaderModule`,而是先调用 [VulkanShaderCompiler](../VulkanShaderCompiler/VulkanShaderCompiler.md) 的编译流程得到 SPIR-V再交给 [VulkanShader](../VulkanShader/VulkanShader.md) 创建 module。
## 设备信息与能力
- `GetCapabilities()` 返回 `RHICapabilities`
- `GetDeviceInfo()` 返回 `RHIDeviceInfo`
- `GetNativeDevice()` 返回 `VkDevice`
- `GetInstance()` / `GetPhysicalDevice()` / `GetGraphicsQueue()` 暴露 Vulkan 原生根对象
`QueryDeviceInfo()` 的实现看,设备名称、厂商和适配器信息会在初始化阶段写入 `m_deviceInfo`,供上层调试、日志和测试使用。
## 线程语义
从当前实现看,`VulkanDevice` 内部没有显式锁,也没有跨线程资源所有权协调逻辑。更稳妥的工程约束是:
- 初始化与关停由单线程串行完成
- 资源创建与销毁由调用方自行保证时序
- 不要假设 `Create*()` 系列天然线程安全
这和商业引擎常见做法一致:设备对象通常是渲染基础设施的中心,但线程模型往往由更高层的 render graph、render thread 或 job system 决定,而不是由 backend device 自己兜底。
## 所有权与资源管理
- `Create*()` 系列大多返回裸指针,调用方负责后续 `Shutdown()``delete`
- `VulkanDevice` 不负责统一托管所有已创建对象
- 交换链 back buffer、render pass、descriptor pool 等对象也遵循同样模式
这属于“轻量工厂 + 调用方负责生命周期”的设计,而不是 Unity 那种高度托管的资源对象模型。好处是 backend 更直白,代价是使用者必须严格控制销毁顺序。
## 设计取向
`VulkanDevice` 的设计明显偏向“先把抽象层打通,再逐步补强后端特性”:
- 好处是 RHI 测试和跨后端样例可以较早跑起来
- 代价是 Vulkan 专属能力目前只覆盖了最核心路径
- 一些接口已经预留了成熟引擎会有的扩展点例如调试层、GPU validation、多队列但当前实现还没有全部兑现
如果你熟悉 Unity可以把它类比成更靠近引擎内部 `GfxDevice` 的那层,而不是脚本层直接操作的 `Graphics` API。
## 当前限制
- 当前 surface 创建路径是 Win32 专属
- 调试层与 GPU validation 开关尚未真正启用
- 只选择第一个可用 graphics queue family
- 没有单独的 present / compute / copy native queue
- buffer 默认分配策略更偏 host-visible而不是按资源用途做精细化显存分类
这些限制不会影响当前测试覆盖的基本图形与计算路径,但会影响后续性能调优和平台扩展。
## 主要公开方法
- `bool Initialize(const RHIDeviceDesc& desc)`
- `void Shutdown()`
- `RHIBuffer* CreateBuffer(const BufferDesc& desc)`
- `RHITexture* CreateTexture(const TextureDesc& desc)`
- `RHITexture* CreateTexture(const TextureDesc& desc, const void* initialData, size_t initialDataSize, uint32_t rowPitch = 0)`
- `RHISwapChain* CreateSwapChain(const SwapChainDesc& desc, RHICommandQueue* presentQueue)`
- `RHICommandList* CreateCommandList(const CommandListDesc& desc)`
- `RHICommandQueue* CreateCommandQueue(const CommandQueueDesc& desc)`
- `RHIShader* CreateShader(const ShaderCompileDesc& desc)`
- `RHIPipelineState* CreatePipelineState(const GraphicsPipelineDesc& desc)`
- `RHIPipelineLayout* CreatePipelineLayout(const RHIPipelineLayoutDesc& desc)`
- `RHIFence* CreateFence(const FenceDesc& desc)`
- `RHISampler* CreateSampler(const SamplerDesc& desc)`
- `RHIRenderPass* CreateRenderPass(...)`
- `RHIFramebuffer* CreateFramebuffer(...)`
- `RHIDescriptorPool* CreateDescriptorPool(const DescriptorPoolDesc& desc)`
- `RHIDescriptorSet* CreateDescriptorSet(RHIDescriptorPool* pool, const DescriptorSetLayoutDesc& layout)`
## 相关测试与使用线索
- `tests/RHI/unit/test_factory.cpp` 覆盖了 Vulkan 设备创建入口
- `tests/RHI/unit/test_device.cpp` 会通过抽象设备接口验证 `CreateCommandQueue()``CreateCommandList()`、资源与视图创建
- `tests/RHI/unit/test_vulkan_graphics.cpp` 直接走 Vulkan 后端验证 shader、pipeline、texture copy 与 compute 路径
- `tests/RHI/integration/fixtures/RHIIntegrationFixture.cpp` 展示了真实的设备、队列、交换链、命令列表初始化流程
## 相关指南
- [Devices, Queues, Command Lists, And Resource Creation](../../../../_guides/RHI/Devices-Queues-CommandLists-And-Resource-Creation.md)
## 相关文档
- [Vulkan](../Vulkan.md)
- [RHIDevice](../../RHIDevice/RHIDevice.md)
- [VulkanCommandQueue](../VulkanCommandQueue/VulkanCommandQueue.md)
- [VulkanCommandList](../VulkanCommandList/VulkanCommandList.md)
- [VulkanSwapChain](../VulkanSwapChain/VulkanSwapChain.md)
- [VulkanShader](../VulkanShader/VulkanShader.md)

View File

@@ -0,0 +1,65 @@
# VulkanFence
**命名空间**: `XCEngine::RHI`
**类型**: `class`
**头文件**: `XCEngine/RHI/Vulkan/VulkanFence.h`
**描述**: Vulkan 后端里的 `RHIFence` 轻量实现,当前本质上是一个软件计数器,而不是原生 `VkFence` 封装。
## 概览
`VulkanFence` 的接口形状继承自抽象层 fence 契约,但当前实现非常轻:
- 内部只保存 `uint64_t m_value`
- 不创建 `VkFence`
- 不等待 GPU 真正完成某条提交
## 当前实现的真实行为
- 构造时可以指定初始值
- `Signal()` 会把值加一
- `Signal(uint64_t value)` 会直接把值改成传入值
- `Wait(uint64_t value)` 如果当前值更小,就把它提升到目标值
- `GetCompletedValue()` 返回当前计数
- `GetNativeHandle()` 返回 `&m_value`
- `Shutdown()` 是空实现
因此这里的 `Wait()` 不是阻塞等待 GPU 信号,而只是一个软件层面的“把值推进到至少等于目标值”。
## 与命令队列的关系
[VulkanCommandQueue](../VulkanCommandQueue/VulkanCommandQueue.md) 的 `Signal()` / `Wait()` 当前只是把调用转发到 `RHIFence`,并没有接入 Vulkan 的 native queue synchronization。
这意味着当前 fence 语义更接近“测试友好的抽象完成值”,而不是商业级引擎里用于 CPU/GPU 精确同步的 timeline fence。
## 线程语义
源码里没有原子操作和锁。不要把它当成高并发同步原语使用。
## 设计取向
这个实现显然是为了先把抽象层 fence 接口接通,方便测试与统一 API而不是为了提供完整 Vulkan 同步系统。
## 当前限制
- 不是 `VkFence`
- 不代表 GPU 真正完成状态
- `Wait()` 不阻塞
- `GetNativeHandle()` 只是 `uint64_t` 地址
## 主要公开方法
- `void Shutdown()`
- `void Signal()`
- `void Signal(uint64_t value)`
- `void Wait(uint64_t value)`
- `uint64_t GetCompletedValue() const`
- `void* GetNativeHandle()`
## 相关文档
- [Vulkan](../Vulkan.md)
- [VulkanCommandQueue](../VulkanCommandQueue/VulkanCommandQueue.md)
- [RHIFence](../../RHIFence/RHIFence.md)

View File

@@ -0,0 +1,14 @@
# VulkanFramebuffer::GetColorAttachmentCount
```cpp
uint32_t GetColorAttachmentCount() const;
```
## 作用
返回当前缓存的颜色附件 view 数量。
## 相关文档
- [VulkanFramebuffer](VulkanFramebuffer.md)
- [GetColorAttachmentView](GetColorAttachmentView.md)

View File

@@ -0,0 +1,19 @@
# VulkanFramebuffer::GetColorAttachmentTexture
```cpp
VulkanTexture* GetColorAttachmentTexture(uint32_t index) const;
```
## 作用
按索引返回颜色附件 view 对应的纹理对象。
## 当前实现行为
- 会先通过 `GetColorAttachmentView(index)` 取 view
- 若 view 无效则返回 `nullptr`
## 相关文档
- [VulkanFramebuffer](VulkanFramebuffer.md)
- [GetColorAttachmentView](GetColorAttachmentView.md)

View File

@@ -0,0 +1,18 @@
# VulkanFramebuffer::GetColorAttachmentView
```cpp
VulkanResourceView* GetColorAttachmentView(uint32_t index) const;
```
## 作用
按索引返回颜色附件 view。
## 当前实现行为
- 索引越界时返回 `nullptr`
## 相关文档
- [VulkanFramebuffer](VulkanFramebuffer.md)
- [GetColorAttachmentTexture](GetColorAttachmentTexture.md)

View File

@@ -0,0 +1,18 @@
# VulkanFramebuffer::GetDepthStencilTexture
```cpp
VulkanTexture* GetDepthStencilTexture() const;
```
## 作用
返回深度模板 view 对应的纹理对象。
## 当前实现行为
- 若没有深度模板 view返回 `nullptr`
## 相关文档
- [VulkanFramebuffer](VulkanFramebuffer.md)
- [GetDepthStencilView](GetDepthStencilView.md)

View File

@@ -0,0 +1,14 @@
# VulkanFramebuffer::GetDepthStencilView
```cpp
VulkanResourceView* GetDepthStencilView() const;
```
## 作用
返回当前缓存的深度模板 view。
## 相关文档
- [VulkanFramebuffer](VulkanFramebuffer.md)
- [GetDepthStencilTexture](GetDepthStencilTexture.md)

View File

@@ -0,0 +1,14 @@
# VulkanFramebuffer::GetFramebuffer
```cpp
VkFramebuffer GetFramebuffer() const;
```
## 作用
返回真实的 native `VkFramebuffer`
## 相关文档
- [VulkanFramebuffer](VulkanFramebuffer.md)
- [GetNativeHandle](GetNativeHandle.md)

View File

@@ -0,0 +1,14 @@
# VulkanFramebuffer::GetHeight
```cpp
uint32_t GetHeight() const override;
```
## 作用
返回 framebuffer 高度。
## 相关文档
- [VulkanFramebuffer](VulkanFramebuffer.md)
- [GetWidth](GetWidth.md)

View File

@@ -0,0 +1,19 @@
# VulkanFramebuffer::GetNativeHandle
```cpp
void* GetNativeHandle() override;
```
## 作用
返回 native framebuffer 句柄。
## 当前实现行为
- 直接返回 `m_framebuffer`
- 实际类型是 `VkFramebuffer`
## 相关文档
- [VulkanFramebuffer](VulkanFramebuffer.md)
- [GetFramebuffer](GetFramebuffer.md)

Some files were not shown because too many files have changed in this diff Show More