chore: sync workspace state
This commit is contained in:
@@ -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 headers;Editor 这组文档更依赖目录并行约束、链接校验和人工核对源码行为。
|
||||
|
||||
## 目录
|
||||
|
||||
- [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)
|
||||
|
||||
107
docs/api/XCEngine/Editor/UI/BuiltInIcons/BuiltInIcons.md
Normal file
107
docs/api/XCEngine/Editor/UI/BuiltInIcons/BuiltInIcons.md
Normal 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)
|
||||
73
docs/api/XCEngine/Editor/UI/DividerChrome/DividerChrome.md
Normal file
73
docs/api/XCEngine/Editor/UI/DividerChrome/DividerChrome.md
Normal 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)
|
||||
103
docs/api/XCEngine/Editor/UI/DockTabBarChrome/DockTabBarChrome.md
Normal file
103
docs/api/XCEngine/Editor/UI/DockTabBarChrome/DockTabBarChrome.md
Normal 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)
|
||||
105
docs/api/XCEngine/Editor/UI/PropertyLayout/PropertyLayout.md
Normal file
105
docs/api/XCEngine/Editor/UI/PropertyLayout/PropertyLayout.md
Normal 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)
|
||||
88
docs/api/XCEngine/Editor/UI/SplitterChrome/SplitterChrome.md
Normal file
88
docs/api/XCEngine/Editor/UI/SplitterChrome/SplitterChrome.md
Normal 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)
|
||||
@@ -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)
|
||||
|
||||
122
docs/api/XCEngine/Editor/UI/TreeView/TreeView.md
Normal file
122
docs/api/XCEngine/Editor/UI/TreeView/TreeView.md
Normal 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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 layout,OpenGL 会按资源类别分别分配 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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
81
docs/api/XCEngine/RHI/Vulkan/Vulkan.md
Normal file
81
docs/api/XCEngine/RHI/Vulkan/Vulkan.md
Normal 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)
|
||||
94
docs/api/XCEngine/RHI/Vulkan/VulkanBuffer/VulkanBuffer.md
Normal file
94
docs/api/XCEngine/RHI/Vulkan/VulkanBuffer/VulkanBuffer.md
Normal 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)
|
||||
@@ -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)
|
||||
26
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Clear.md
Normal file
26
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Clear.md
Normal 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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
25
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Close.md
Normal file
25
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Close.md
Normal 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)
|
||||
@@ -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)
|
||||
29
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Dispatch.md
Normal file
29
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Dispatch.md
Normal 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)
|
||||
27
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Draw.md
Normal file
27
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Draw.md
Normal 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)
|
||||
@@ -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)
|
||||
27
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Initialize.md
Normal file
27
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Initialize.md
Normal 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)
|
||||
26
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Reset.md
Normal file
26
docs/api/XCEngine/RHI/Vulkan/VulkanCommandList/Reset.md
Normal 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)
|
||||
@@ -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 推导 layout;compute 版本当前要求调用方明确传入 pipeline layout。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [VulkanCommandList](VulkanCommandList.md)
|
||||
- [SetGraphicsDescriptorSets](SetGraphicsDescriptorSets.md)
|
||||
- [VulkanPipelineLayout](../VulkanPipelineLayout/VulkanPipelineLayout.md)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -0,0 +1,19 @@
|
||||
# VulkanCommandList::SetScissorRect
|
||||
|
||||
```cpp
|
||||
void SetScissorRect(const Rect& rect) override;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
设置当前剪裁矩形。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 会更新内部 `VkRect2D`
|
||||
- 同时把 scissor 直接写入当前 command buffer
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [VulkanCommandList](VulkanCommandList.md)
|
||||
- [SetViewport](SetViewport.md)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -0,0 +1,23 @@
|
||||
# VulkanCommandQueue::GetCompletedValue
|
||||
|
||||
```cpp
|
||||
uint64_t GetCompletedValue() override;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
返回当前队列记录的完成值。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 当前直接返回 `m_currentFrame`
|
||||
- 这个值在 `ExecuteCommandLists()` 提交并 `WaitIdle()` 之后自增
|
||||
|
||||
## 解释
|
||||
|
||||
它表示的是“本地已完成的提交计数”,不是 Vulkan 驱动侧某个原生同步对象的完成值。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [VulkanCommandQueue](VulkanCommandQueue.md)
|
||||
- [ExecuteCommandLists](ExecuteCommandLists.md)
|
||||
@@ -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)
|
||||
20
docs/api/XCEngine/RHI/Vulkan/VulkanCommandQueue/Signal.md
Normal file
20
docs/api/XCEngine/RHI/Vulkan/VulkanCommandQueue/Signal.md
Normal 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)
|
||||
@@ -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)
|
||||
24
docs/api/XCEngine/RHI/Vulkan/VulkanCommandQueue/Wait.md
Normal file
24
docs/api/XCEngine/RHI/Vulkan/VulkanCommandQueue/Wait.md
Normal 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)
|
||||
@@ -0,0 +1,24 @@
|
||||
# VulkanCommandQueue::WaitForIdle
|
||||
|
||||
```cpp
|
||||
void WaitForIdle() override;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
阻塞直到当前 `VkQueue` 空闲。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 如果 `m_queue` 有效,直接调用 `vkQueueWaitIdle(m_queue)`
|
||||
|
||||
## 使用场景
|
||||
|
||||
- 交换链重建前
|
||||
- 截图或调试路径需要强制同步时
|
||||
- 关停设备或销毁依赖资源前
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [VulkanCommandQueue](VulkanCommandQueue.md)
|
||||
- [ExecuteCommandLists](ExecuteCommandLists.md)
|
||||
83
docs/api/XCEngine/RHI/Vulkan/VulkanCommon/VulkanCommon.md
Normal file
83
docs/api/XCEngine/RHI/Vulkan/VulkanCommon/VulkanCommon.md
Normal 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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
36
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateBuffer.md
Normal file
36
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateBuffer.md
Normal 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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
19
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateFence.md
Normal file
19
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateFence.md
Normal 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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
20
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateSampler.md
Normal file
20
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateSampler.md
Normal 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)
|
||||
32
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateShader.md
Normal file
32
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateShader.md
Normal 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)
|
||||
@@ -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)
|
||||
32
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateSwapChain.md
Normal file
32
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateSwapChain.md
Normal 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)
|
||||
46
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateTexture.md
Normal file
46
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/CreateTexture.md
Normal 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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
33
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/FindMemoryType.md
Normal file
33
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/FindMemoryType.md
Normal 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)
|
||||
41
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/Initialize.md
Normal file
41
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/Initialize.md
Normal 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)
|
||||
29
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/Shutdown.md
Normal file
29
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/Shutdown.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# VulkanDevice::Shutdown
|
||||
|
||||
```cpp
|
||||
void Shutdown() override;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
释放 `VulkanDevice` 持有的 Vulkan 根对象,并把内部状态重置为未初始化。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 会销毁逻辑设备、实例以及相关缓存状态
|
||||
- 会清空图形队列句柄、队列族索引、设备信息和 capability 缓存
|
||||
- 析构函数也会调用 `Shutdown()`
|
||||
|
||||
## 使用建议
|
||||
|
||||
- 在调用它之前,先销毁由该设备创建出来的队列、交换链、命令列表、资源和状态对象
|
||||
- 不要把它当成“热重载设备”的轻量操作,它属于完整关停路径
|
||||
|
||||
## 线程语义
|
||||
|
||||
应由调用方保证没有其他线程仍在使用该设备创建的对象。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [VulkanDevice](VulkanDevice.md)
|
||||
- [Initialize](Initialize.md)
|
||||
153
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/VulkanDevice.md
Normal file
153
docs/api/XCEngine/RHI/Vulkan/VulkanDevice/VulkanDevice.md
Normal 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)
|
||||
65
docs/api/XCEngine/RHI/Vulkan/VulkanFence/VulkanFence.md
Normal file
65
docs/api/XCEngine/RHI/Vulkan/VulkanFence/VulkanFence.md
Normal 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)
|
||||
@@ -0,0 +1,14 @@
|
||||
# VulkanFramebuffer::GetColorAttachmentCount
|
||||
|
||||
```cpp
|
||||
uint32_t GetColorAttachmentCount() const;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
返回当前缓存的颜色附件 view 数量。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [VulkanFramebuffer](VulkanFramebuffer.md)
|
||||
- [GetColorAttachmentView](GetColorAttachmentView.md)
|
||||
@@ -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)
|
||||
@@ -0,0 +1,18 @@
|
||||
# VulkanFramebuffer::GetColorAttachmentView
|
||||
|
||||
```cpp
|
||||
VulkanResourceView* GetColorAttachmentView(uint32_t index) const;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
按索引返回颜色附件 view。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 索引越界时返回 `nullptr`
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [VulkanFramebuffer](VulkanFramebuffer.md)
|
||||
- [GetColorAttachmentTexture](GetColorAttachmentTexture.md)
|
||||
@@ -0,0 +1,18 @@
|
||||
# VulkanFramebuffer::GetDepthStencilTexture
|
||||
|
||||
```cpp
|
||||
VulkanTexture* GetDepthStencilTexture() const;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
返回深度模板 view 对应的纹理对象。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 若没有深度模板 view,返回 `nullptr`
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [VulkanFramebuffer](VulkanFramebuffer.md)
|
||||
- [GetDepthStencilView](GetDepthStencilView.md)
|
||||
@@ -0,0 +1,14 @@
|
||||
# VulkanFramebuffer::GetDepthStencilView
|
||||
|
||||
```cpp
|
||||
VulkanResourceView* GetDepthStencilView() const;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
返回当前缓存的深度模板 view。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [VulkanFramebuffer](VulkanFramebuffer.md)
|
||||
- [GetDepthStencilTexture](GetDepthStencilTexture.md)
|
||||
@@ -0,0 +1,14 @@
|
||||
# VulkanFramebuffer::GetFramebuffer
|
||||
|
||||
```cpp
|
||||
VkFramebuffer GetFramebuffer() const;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
返回真实的 native `VkFramebuffer`。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [VulkanFramebuffer](VulkanFramebuffer.md)
|
||||
- [GetNativeHandle](GetNativeHandle.md)
|
||||
14
docs/api/XCEngine/RHI/Vulkan/VulkanFramebuffer/GetHeight.md
Normal file
14
docs/api/XCEngine/RHI/Vulkan/VulkanFramebuffer/GetHeight.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# VulkanFramebuffer::GetHeight
|
||||
|
||||
```cpp
|
||||
uint32_t GetHeight() const override;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
返回 framebuffer 高度。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [VulkanFramebuffer](VulkanFramebuffer.md)
|
||||
- [GetWidth](GetWidth.md)
|
||||
@@ -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
Reference in New Issue
Block a user