From 00ce503762e478e8efd26ecb1dde64feb45f966b Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Thu, 2 Apr 2026 21:29:08 +0800 Subject: [PATCH] docs: update API docs for rendering and script editors --- .../ComponentEditorRegistry.md | 50 +++++--- .../ComponentEditors/ComponentEditors.md | 41 ++++-- .../ScriptComponentEditor.md | 105 ++++++++++++++++ .../ScriptComponentEditorUtils.md | 83 +++++++++++++ .../CameraRenderRequest.md | 84 +++++++++---- .../Rendering/CameraRenderRequest/IsValid.md | 27 ++-- .../CameraRenderer/CameraRenderer.md | 76 +++++++----- .../Rendering/CameraRenderer/Constructor.md | 44 +++++-- .../Rendering/CameraRenderer/Destructor.md | 21 ++-- .../CameraRenderer/GetObjectIdPass.md | 31 +++++ .../Rendering/CameraRenderer/GetPipeline.md | 16 +-- .../CameraRenderer/GetPipelineAsset.md | 32 +++++ .../Rendering/CameraRenderer/Render.md | 67 ++++++---- .../CameraRenderer/SetObjectIdPass.md | 34 +++++ .../Rendering/CameraRenderer/SetPipeline.md | 31 +++-- .../CameraRenderer/SetPipelineAsset.md | 35 ++++++ .../BuiltinInfiniteGridPass.md | 48 +++++++ .../BuiltinObjectIdOutlinePass.md | 102 +++++++++++++++ .../BuiltinObjectIdOutlinePass/Constructor.md | 50 ++++++++ .../BuiltinObjectIdOutlinePass/Destructor.md | 34 +++++ .../BuiltinObjectIdOutlinePass/Render.md | 76 ++++++++++++ .../BuiltinObjectIdOutlinePass/Shutdown.md | 51 ++++++++ .../BuiltinObjectIdPass.md | 54 ++++++++ .../BuiltinPostProcessPassPlan.md | 45 +++++++ .../BuiltinPostProcessPassSequenceBuilder.md | 57 +++++++++ .../ObjectIdOutlineStyle.md | 56 +++++++++ docs/api/XCEngine/Rendering/Passes/Passes.md | 61 +++++++++ .../ApplyMaterialRenderState.md | 19 ++- .../MatchesBuiltinPass.md | 44 +++++-- .../RenderMaterialUtility.md | 84 +++++++------ .../RenderMaterialUtility/ResolveMaterial.md | 43 ++++--- docs/api/XCEngine/Rendering/Rendering.md | 63 ++++++---- docs/plan/API文档并行更新任务池_2026-04-02.md | 117 ++++++++++++++++++ 33 files changed, 1519 insertions(+), 262 deletions(-) create mode 100644 docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditor/ScriptComponentEditor.md create mode 100644 docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditorUtils/ScriptComponentEditorUtils.md create mode 100644 docs/api/XCEngine/Rendering/CameraRenderer/GetObjectIdPass.md create mode 100644 docs/api/XCEngine/Rendering/CameraRenderer/GetPipelineAsset.md create mode 100644 docs/api/XCEngine/Rendering/CameraRenderer/SetObjectIdPass.md create mode 100644 docs/api/XCEngine/Rendering/CameraRenderer/SetPipelineAsset.md create mode 100644 docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/BuiltinInfiniteGridPass.md create mode 100644 docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md create mode 100644 docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Constructor.md create mode 100644 docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Destructor.md create mode 100644 docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Render.md create mode 100644 docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Shutdown.md create mode 100644 docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuiltinObjectIdPass.md create mode 100644 docs/api/XCEngine/Rendering/Passes/BuiltinPostProcessPassPlan/BuiltinPostProcessPassPlan.md create mode 100644 docs/api/XCEngine/Rendering/Passes/BuiltinPostProcessPassSequenceBuilder/BuiltinPostProcessPassSequenceBuilder.md create mode 100644 docs/api/XCEngine/Rendering/Passes/ObjectIdOutlineStyle/ObjectIdOutlineStyle.md create mode 100644 docs/api/XCEngine/Rendering/Passes/Passes.md create mode 100644 docs/plan/API文档并行更新任务池_2026-04-02.md diff --git a/docs/api/XCEngine/Editor/ComponentEditors/ComponentEditorRegistry/ComponentEditorRegistry.md b/docs/api/XCEngine/Editor/ComponentEditors/ComponentEditorRegistry/ComponentEditorRegistry.md index ec6ffa3e..41e25a2f 100644 --- a/docs/api/XCEngine/Editor/ComponentEditors/ComponentEditorRegistry/ComponentEditorRegistry.md +++ b/docs/api/XCEngine/Editor/ComponentEditors/ComponentEditorRegistry/ComponentEditorRegistry.md @@ -6,36 +6,54 @@ **源文件**: `editor/src/ComponentEditors/ComponentEditorRegistry.h` -**描述**: 持有并注册所有组件编辑器实例,按组件类型名为 Inspector 提供查询入口。 +**描述**: 持有并注册所有组件编辑器实例,按组件类型名为 Inspector 提供统一查询入口。 ## 概述 -`ComponentEditorRegistry` 是当前组件编辑器系统的注册中心。 +`ComponentEditorRegistry` 是当前 Inspector 组件编辑器系统的注册中心。它负责: -它负责: +- 拥有 `IComponentEditor` 实例 +- 按组件实例查找 editor +- 按组件类型名查找 editor +- 暴露所有已注册 editor,供 Inspector 侧遍历和构建“添加组件”入口 -- 注册 `IComponentEditor` -- 根据组件实例查找对应 editor -- 根据组件类型名查找 editor -- 暴露全部 editor 列表 +## 当前注册列表 -## 当前实现说明 +按 `editor/src/ComponentEditors/ComponentEditorRegistry.cpp`,构造函数当前会注册: -- 采用单例 `Get()`。 -- 构造函数里会自动注册: - - `TransformComponentEditor` - - `CameraComponentEditor` - - `LightComponentEditor` -- `m_editors` 负责拥有这些 editor 对象。 -- `m_editorsByType` 提供按组件类型名的快速查找。 +1. `TransformComponentEditor` +2. `CameraComponentEditor` +3. `LightComponentEditor` +4. `MeshFilterComponentEditor` +5. `MeshRendererComponentEditor` +6. [ScriptComponentEditor](../ScriptComponentEditor/ScriptComponentEditor.md) + +这说明脚本组件编辑器已经是 registry 里的正式一员,而不是 Inspector 特判分支。 + +## 数据结构 + +- `m_editors`:`std::vector>`,负责真正拥有 editor 对象 +- `m_editorsByType`:按 `GetComponentTypeName()` 建立的快速索引 + +`RegisterEditor()` 当前会先把裸指针写入 `m_editorsByType`,再把 `unique_ptr` 推入 `m_editors`。 ## 当前实现边界 -- 当前注册列表写死在构造函数中,不是插件式发现机制。 +- 注册列表写死在构造函数里,不是插件式发现机制。 - 如果后注册 editor 与已有类型名重复,`m_editorsByType` 会被覆盖,但 `m_editors` 里仍会保留多个对象。 +- `FindEditor(component)` 依赖组件的 `GetName()` 返回值与 editor 的 `GetComponentTypeName()` 完整对齐。 + +## 与 ScriptComponentEditor 的关系 + +`ScriptComponentEditor` 通过这里注册后,InspectorPanel 就能像处理普通组件一样处理脚本组件: + +- 按 `ScriptComponent` 类型查找到 editor +- 调用其 `Render()` 获取脚本类选择器和脚本字段 UI +- 继续复用统一的撤销与 Inspector 布局体系 ## 相关文档 - [ComponentEditors](../ComponentEditors.md) - [IComponentEditor](../IComponentEditor/IComponentEditor.md) +- [ScriptComponentEditor](../ScriptComponentEditor/ScriptComponentEditor.md) - [InspectorPanel](../../panels/InspectorPanel/InspectorPanel.md) diff --git a/docs/api/XCEngine/Editor/ComponentEditors/ComponentEditors.md b/docs/api/XCEngine/Editor/ComponentEditors/ComponentEditors.md index 234c9979..101432c2 100644 --- a/docs/api/XCEngine/Editor/ComponentEditors/ComponentEditors.md +++ b/docs/api/XCEngine/Editor/ComponentEditors/ComponentEditors.md @@ -4,30 +4,47 @@ **类型**: `submodule` -**描述**: 组件属性编辑器层,负责把运行时组件映射到 Inspector 中的具体编辑 UI。 +**描述**: Inspector 组件编辑器层,负责把运行时组件映射成可撤销、可交互的专用编辑 UI。 ## 概述 -当前目录包含: +当前组件编辑器系统走的是典型的“按组件类型注册专用 editor”路线: + +- [IComponentEditor](IComponentEditor/IComponentEditor.md) 定义统一契约 +- [ComponentEditorRegistry](ComponentEditorRegistry/ComponentEditorRegistry.md) 负责注册、拥有和查找 editor +- 各个具体 editor 只处理自己的组件 UI 与交互规则 + +这比把所有组件字段塞进一个通用 Inspector 渲染器更容易扩展,也更方便为特殊组件接入脚本元数据、引用选择器和撤销逻辑。 + +## 当前已记录的 editor -- [IComponentEditor](IComponentEditor/IComponentEditor.md) -- [ComponentEditorRegistry](ComponentEditorRegistry/ComponentEditorRegistry.md) - [TransformComponentEditor](TransformComponentEditor/TransformComponentEditor.md) - [CameraComponentEditor](CameraComponentEditor/CameraComponentEditor.md) - [LightComponentEditor](LightComponentEditor/LightComponentEditor.md) +- [ScriptComponentEditor](ScriptComponentEditor/ScriptComponentEditor.md) -这说明 Inspector 当前走的是“按组件类型注册专用 editor”的路线。这和商业编辑器里常见的 custom inspector / property drawer 思路是一致的。 +## 当前注册但尚未单独补页的 editor -当前这组 editor 也已经体现出比较明确的分层: +按 `ComponentEditorRegistry.cpp` 当前实现,registry 还会注册: -- `IComponentEditor` 定义统一契约 -- `ComponentEditorRegistry` 负责注册和查找 -- 具体 editor 只处理本组件的 Inspector 体验 +- `MeshFilterComponentEditor` +- `MeshRendererComponentEditor` -这比把所有组件字段硬塞进一个大而全的 Inspector 渲染器更容易扩展。 +## ScriptComponentEditor 在这层里的位置 + +[ScriptComponentEditor](ScriptComponentEditor/ScriptComponentEditor.md) 是这套系统里最依赖运行时元数据的一个 editor。它不是直接读取 `ScriptComponent` 自身的字段列表,而是通过 `ScriptEngine::TryGetScriptFieldModel()` 获取: + +- 当前脚本类是否已分配 / 可用 / 缺失 +- 字段元数据 +- 默认值、stored override 和 live managed value 的合成结果 +- stored-only / type mismatch 一类问题状态 + +因此它也是 `ComponentEditors` 子模块和 `Scripting` 子模块的主要交汇点。 ## 相关文档 -- [Editor 模块](../Editor.md) -- [panels](../panels/panels.md) +- [ComponentEditorRegistry](ComponentEditorRegistry/ComponentEditorRegistry.md) +- [ScriptComponentEditor](ScriptComponentEditor/ScriptComponentEditor.md) +- [ScriptComponentEditorUtils](ScriptComponentEditorUtils/ScriptComponentEditorUtils.md) +- [InspectorPanel](../panels/InspectorPanel/InspectorPanel.md) - [Components](../../Components/Components.md) diff --git a/docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditor/ScriptComponentEditor.md b/docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditor/ScriptComponentEditor.md new file mode 100644 index 00000000..98de11b1 --- /dev/null +++ b/docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditor/ScriptComponentEditor.md @@ -0,0 +1,105 @@ +# ScriptComponentEditor + +**命名空间**: `XCEngine::Editor` + +**类型**: `class` + +**源文件**: `editor/src/ComponentEditors/ScriptComponentEditor.h` + +**描述**: `ScriptComponent` 的专用 Inspector 编辑器,负责脚本类选择、脚本字段绘制、override 写回和运行时不可用提示。 + +## 概述 + +`ScriptComponentEditor` 是当前 Inspector 里最动态的一类组件编辑器。它实现了 [IComponentEditor](../IComponentEditor/IComponentEditor.md),但和普通“直接编辑组件成员”的 editor 不同,它要先经过 [ScriptEngine](../../../Scripting/ScriptEngine/ScriptEngine.md) 查询脚本字段模型,再决定能显示什么、能编辑什么。 + +当前 `Render()` 的主流程是: + +1. 把传入组件 `dynamic_cast` 成 [ScriptComponent](../../../Scripting/ScriptComponent/ScriptComponent.md) +2. 绘制脚本类选择器 +3. 调用 `ScriptEngine::TryGetScriptFieldModel()` 获取当前脚本类状态和字段快照 +4. 根据类状态显示 hint、缺失信息或字段列表 +5. 对每个字段决定走可编辑控件还是只读显示 +6. 必要时提供 `Reset` 按钮清除 stored override + +## 脚本类选择器 + +脚本类下拉框的数据来自 `ScriptEngine::TryGetAvailableScriptClasses()`。 + +- 已分配脚本类且能在当前 runtime 中找到时,显示规范化类名 +- 已分配但当前加载的脚本程序集里找不到时,显示 `"(Missing)"` +- 未分配时显示 `None` + +如果当前没有加载到脚本程序集,editor 还会读取 `Application::Get().GetScriptRuntimeStatus()`,并按状态决定是否显示: + +- `Rebuild Scripts` +- `Reload Scripts` + +这意味着脚本组件编辑器不仅是字段编辑 UI,也是脚本运行时状态的主要反馈入口之一。 + +## 字段元数据来源 + +字段列表不是直接从 `ScriptComponent` 本地成员生成的,而是来自 `ScriptEngine::TryGetScriptFieldModel()` 合成的 `ScriptFieldModel`: + +- 脚本类元数据来自 `IScriptRuntime::TryGetClassFieldMetadata()` +- 默认值来自运行时侧的类默认值采集 +- live managed value 优先来自 `TryGetManagedFieldValue()` +- 如果没有 live instance,则回退到 `ScriptComponent::GetFieldStorage()` 中的 stored override +- 如果 storage 里存在脚本类已不再声明的字段,会以 `StoredOnly` 形式补到模型里 + +这也是 editor 能区分“可编辑字段”“只剩 stored override 的遗留字段”“类型不匹配字段”的原因。 + +## 当前支持的字段类型 + +按 `RenderScriptFieldEditor()` 当前实现,editor 支持这些字段控件: + +- `Float` +- `Double` +- `Bool` +- `Int32` +- `UInt64` +- `String` +- `Vector2` +- `Vector3` +- `Vector4` +- `GameObject` + +其中: + +- `String` 使用内部 `StringFieldEditState` 做编辑缓存,避免外部同步打断输入 +- `GameObject` 通过 `UI::DrawReferencePickerControl()` 和场景对象 UUID / ID 映射完成引用选择 + +## 可编辑性与 reset 规则 + +当前规则由 [ScriptComponentEditorUtils](../ScriptComponentEditorUtils/ScriptComponentEditorUtils.md) 决定: + +- `classStatus == Available` 且字段确实由当前类声明时,字段可编辑 +- 类缺失或未分配时,stored-only 字段也允许继续编辑 / 保留 +- 当字段存在 stored value,或当前值来自 managed runtime 时,显示 `Reset` + +写回路径则统一走: + +- `ScriptEngine::ApplyScriptFieldWrites()` +- `ScriptEngine::ClearScriptFieldOverrides()` + +并在 Inspector 层接入 `IUndoManager::BeginInteractiveChange(...)`。 + +## 当前实现边界 + +- 当前不支持多对象联合编辑。 +- `StringFieldEditState` 以 `scriptComponentUUID + fieldName` 为键缓存,没有额外清理失效键的机制。 +- 对不支持的字段类型会退回只读文本显示,而不是显示通用编辑器。 +- `CanAddTo()` 当前对任意非空 `GameObject` 都返回 `true`,没有做“每对象只能有一个 ScriptComponent”之类的额外限制。 + +## 测试与真实调用点 + +- `ComponentEditorRegistry` 当前会自动注册这个 editor。 +- `tests/Editor/test_script_component_editor_utils.cpp` 覆盖了它依赖的显示名、字段可编辑性、issue 文本和 runtime 状态提示规则。 +- Inspector 真实字段模型和写回语义依赖 `ScriptEngine.cpp` 当前实现。 + +## 相关文档 + +- [ComponentEditors](../ComponentEditors.md) +- [ComponentEditorRegistry](../ComponentEditorRegistry/ComponentEditorRegistry.md) +- [ScriptComponentEditorUtils](../ScriptComponentEditorUtils/ScriptComponentEditorUtils.md) +- [ScriptEngine](../../../Scripting/ScriptEngine/ScriptEngine.md) +- [ScriptComponent](../../../Scripting/ScriptComponent/ScriptComponent.md) diff --git a/docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditorUtils/ScriptComponentEditorUtils.md b/docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditorUtils/ScriptComponentEditorUtils.md new file mode 100644 index 00000000..bf9ad4d7 --- /dev/null +++ b/docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditorUtils/ScriptComponentEditorUtils.md @@ -0,0 +1,83 @@ +# ScriptComponentEditorUtils + +**命名空间**: `XCEngine::Editor::ScriptComponentEditorUtils` + +**类型**: `utility namespace` + +**源文件**: `editor/src/ComponentEditors/ScriptComponentEditorUtils.h` + +**描述**: 为 `ScriptComponentEditor` 提供显示名格式化、场景对象 UUID 查找、字段可编辑性判断和脚本运行时状态提示的内联辅助函数集合。 + +## 概述 + +`ScriptComponentEditorUtils.h` 不是一个运行时对象,而是一组专门为 [ScriptComponentEditor](../ScriptComponentEditor/ScriptComponentEditor.md) 服务的 helper。它把那些“不是 UI 控件本身,但决定 UI 怎么显示”的规则集中到了一个地方,避免这些判断散落在 editor 主流程里。 + +## 当前承担的工作 + +### 脚本类显示名 + +- `BuildScriptClassDisplayName(descriptor)` +- `BuildScriptClassDisplayName(component)` + +当前规则是: + +- 如果程序集名为空,或程序集名为 `GameScripts`,只显示完整类名 +- 否则显示 `FullName (AssemblyName)` + +这让项目主脚本程序集保持简洁显示,而工具程序集仍能区分来源。 + +### 场景对象 UUID 到 Inspector 可选目标的映射 + +- `FindGameObjectIdByUuidRecursive(...)` +- `FindGameObjectIdByUuid(...)` + +`GameObjectReference` 在脚本字段里存的是 UUID,但 Inspector 引用选择器用的是当前 scene object 的运行时 ID。这组函数负责在场景树里做桥接查找。 + +### 字段可编辑性与问题提示 + +- `CanEditScriptField(...)` +- `CanClearScriptFieldOverride(...)` +- `BuildScriptFieldIssueText(...)` + +当前规则里最关键的一点是: + +- 当脚本类状态是 `Available` 时,只有 `declaredInClass == true` 的字段可编辑 +- 当类缺失或未分配时,遗留 stored 字段仍可继续显示和编辑 + +issue 文本当前主要覆盖: + +- `StoredOnly` +- `TypeMismatch` + +### 脚本运行时不可用提示 + +- `BuildScriptRuntimeUnavailableHint(...)` +- `CanReloadScriptRuntime(...)` +- `CanRebuildScriptAssemblies(...)` + +这些函数把 `EditorScriptRuntimeStatus` 转成 Inspector 上可直接显示的 hint 与按钮启用条件。 + +## 为什么这层有价值 + +`ScriptComponentEditor` 同时依赖: + +- 脚本运行时状态 +- 场景对象层级 +- 脚本字段问题状态 + +把这些判断抽到 utils 后,editor 主类就更聚焦于“如何渲染 UI”和“什么时候写回 ScriptEngine”,而不是维护大量显示策略分支。 + +## 测试覆盖 + +`tests/Editor/test_script_component_editor_utils.cpp` 当前验证了: + +- 脚本类显示名格式化 +- 场景层级里的 UUID 查找 +- 字段可编辑性、可清除性和 issue 文本 +- runtime unavailable hint 与 reload / rebuild 可用性 + +## 相关文档 + +- [ScriptComponentEditor](../ScriptComponentEditor/ScriptComponentEditor.md) +- [ScriptEngine](../../../Scripting/ScriptEngine/ScriptEngine.md) +- [ScriptComponent](../../../Scripting/ScriptComponent/ScriptComponent.md) diff --git a/docs/api/XCEngine/Rendering/CameraRenderRequest/CameraRenderRequest.md b/docs/api/XCEngine/Rendering/CameraRenderRequest/CameraRenderRequest.md index 8bccbe93..2816147d 100644 --- a/docs/api/XCEngine/Rendering/CameraRenderRequest/CameraRenderRequest.md +++ b/docs/api/XCEngine/Rendering/CameraRenderRequest/CameraRenderRequest.md @@ -6,47 +6,81 @@ **头文件**: `XCEngine/Rendering/CameraRenderRequest.h` -**描述**: 把一次“用哪台相机、在什么上下文、向哪个目标表面渲染”的请求打包成显式数据对象。 +**描述**: 把一次单相机渲染提交所需的主场景、目标表面、object-id 输出和 builtin 后处理配置打包成显式请求对象。 ## 概览 -`CameraRenderRequest` 是 `CameraRenderer` 和 `SceneRenderer` 之间的请求载体。它不负责自己执行渲染,而是把这一帧需要的最小输入集中起来: +`CameraRenderRequest` 是 [SceneRenderer](../SceneRenderer/SceneRenderer.md) 和 [CameraRenderer](../CameraRenderer/CameraRenderer.md) 之间的主要数据契约。它不负责自己执行绘制,而是把这次提交要用到的输入集中起来: -- `scene` 指向要提取可见物体的场景。 -- `camera` 指向本次渲染使用的相机组件。 -- `context` 持有当前帧要用的 RHI 设备、命令队列和命令列表。 -- `surface` 描述渲染目标尺寸、附件和状态切换要求。 -- `cameraDepth` 保存请求排序时要用的深度值。 -- `clearFlags` 指定这台相机希望如何清理目标表面。 +- 主场景渲染的 `scene`、`camera`、`context` 和 `surface` +- 可选的 object-id 辅助输出 `objectId` +- 可选的 builtin 后处理 `builtinPostProcess` +- 多相机排序与清屏控制字段 +- 在主场景前后插入自定义 pass 的钩子 -这种“显式请求对象”设计和商业引擎里常见的 render request / render view 思路一致。好处是相机选择、排序、目标表面分配和真正 draw call 执行可以解耦,而不是把所有逻辑都塞进一个大函数里。 +相比把这些参数散落在多个函数签名里,显式请求对象更适合表达“这次 camera submission 的完整意图”,也更利于测试和编辑器视口复用。 -## 数据契约 +## 核心字段 -| 字段 | 说明 | +| 字段 | 角色 | |------|------| -| `scene` | 不能为 `nullptr`,否则当前请求无效。 | -| `camera` | 不能为 `nullptr`,通常由 `SceneRenderer` 自动挑选,或由调用方显式覆盖。 | -| `context` | 必须通过 [RenderContext::IsValid](../RenderContext/IsValid.md) 校验。 | -| `surface` | 保存目标尺寸与附件描述;当前 `IsValid()` 不检查它是否完整。 | -| `cameraDepth` | 主要用于多相机请求排序,`SceneRenderer` 会按它从小到大排序。 | -| `clearFlags` | 当前会在提交前直接写入 `RenderSceneData::cameraData.clearFlags`。 | +| `scene` | 本次要提取可见物体的场景。 | +| `camera` | 本次渲染使用的相机组件。 | +| `context` | 当前帧的 RHI 上下文。 | +| `surface` | 主颜色输出目标及其 render area。 | +| `cameraDepth` | 多相机排序辅助值。 | +| `cameraStackOrder` | base / overlay camera stack 的附加排序键。 | +| `clearFlags` | 写回 `RenderSceneData::cameraData.clearFlags` 的覆盖值。 | +| `hasClearColorOverride` / `clearColorOverride` | 是否覆盖提取到的相机清屏色。 | +| `preScenePasses` | 主管线执行前的可选 pass 序列。 | +| `postScenePasses` | 主管线与 builtin post-process 之间的可选 pass 序列。 | +| `overlayPasses` | builtin post-process 之后的可选叠加 pass 序列。 | -## 当前实现边界 +## object-id 与 builtin 后处理 -- `IsValid()` 只检查 `scene`、`camera` 和 `context`,不会验证 `surface` 是否真正挂了颜色或深度附件。 -- `cameraDepth` 只是排序辅助值,当前 `CameraRenderer` 自身并不会拿它做额外逻辑。 -- `clearFlags` 会覆盖提取阶段得到的 `cameraData.clearFlags`,因此请求对象是当前这条链路里更高优先级的输入。 +### `objectId` -## 公开成员 +`objectId` 是一个 `ObjectIdRenderRequest`,用于描述“是否要额外渲染一张 object-id 辅助纹理”。 -| 成员 | 说明 | -|------|------| -| [IsValid](IsValid.md) | 检查这份请求是否满足最基本的提交流程前提。 | +- `IsRequested()` 只检查 `surface` 是否挂了颜色附件。 +- `IsValid()` 进一步要求: + - 第一个颜色附件非空 + - 深度附件非空 + - render area 宽高大于 `0` + +`CameraRenderer::Render()` 当前会在主场景之后、`postScenePasses` 之前执行 object-id pass。 + +### `builtinPostProcess` + +`builtinPostProcess` 是一个 `BuiltinPostProcessRequest`,用于描述 scene viewport 一类内建附加效果: + +- `gridPassData` 控制无限网格 +- `objectIdTextureView` 把 object-id 纹理作为 SRV 传给后处理 +- `selectedObjectIds` 决定是否需要 selection outline / mask +- `outlineStyle` 控制轮廓颜色、宽度与调试模式 + +它的 `IsRequested()` 规则很宽松:只要 `gridPassData.valid` 为真,或者 `selectedObjectIds` 非空,就会被视为请求了 builtin post-process。 + +## 当前数据契约 + +- [IsValid](IsValid.md) 只检查 `scene`、`camera` 和 `context`,不会验证 `surface`、`objectId` 或 `builtinPostProcess` 是否完整。 +- `CameraRenderer::Render()` 会额外拒绝: + - `surface` 的 render area 宽高为 `0` + - `objectId.IsRequested()` 但 `objectId.IsValid()` 为假 + - `builtinPostProcess.objectIdTextureView` 非空,但没有请求 `objectId` 输出 +- builtin post-process 更细的回退逻辑由 `BuiltinPostProcessPassSequenceBuilder` 决定,而不是在 `CameraRenderRequest` 自身完成。 + +## 真实使用位置 + +- `SceneRenderer` 负责构建和排序多个 `CameraRenderRequest`。 +- 编辑器 scene viewport 会在 `ViewportHostRenderFlowUtils.h` 里填充 `objectId` 与 `builtinPostProcess`。 +- `tests/Rendering/unit/test_camera_scene_renderer.cpp` 验证了 render area、clear override、object-id pass 与 builtin post-process 的真实接入时序。 ## 相关文档 +- [IsValid](IsValid.md) - [CameraRenderer](../CameraRenderer/CameraRenderer.md) - [SceneRenderer](../SceneRenderer/SceneRenderer.md) - [RenderContext](../RenderContext/RenderContext.md) - [RenderSurface](../RenderSurface/RenderSurface.md) +- [Passes](../Passes/Passes.md) diff --git a/docs/api/XCEngine/Rendering/CameraRenderRequest/IsValid.md b/docs/api/XCEngine/Rendering/CameraRenderRequest/IsValid.md index 95294524..fe86a142 100644 --- a/docs/api/XCEngine/Rendering/CameraRenderRequest/IsValid.md +++ b/docs/api/XCEngine/Rendering/CameraRenderRequest/IsValid.md @@ -14,7 +14,7 @@ bool IsValid() const ## 作用 -检查当前请求是否至少具备“可以进入相机渲染流程”的基本条件。 +检查当前请求是否满足进入 `CameraRenderer` 主流程的最小前提。 ## 当前实现行为 @@ -26,21 +26,26 @@ return scene != nullptr && context.IsValid(); ``` -这意味着: +## 这意味着 -- `surface` 不参与校验。 -- `cameraDepth` 和 `clearFlags` 也不参与校验。 -- 只要 `RenderContext` 无效,整个请求就会被 `CameraRenderer::Render()` 直接拒绝。 +- `surface` 不参与这里的校验。 +- `objectId` 和 `builtinPostProcess` 的完整性也不在这里检查。 +- `cameraDepth`、`cameraStackOrder`、`clearFlags` 和 clear color override 都不会影响这里的结果。 + +因此它只是“最小可进入渲染器”的校验,而不是“这次请求一定能完整跑通”的保证。 + +## 与 `CameraRenderer::Render()` 的关系 + +`CameraRenderer::Render()` 在调用这个方法之后,还会继续检查: + +- `surface.GetRenderAreaWidth()` / `GetRenderAreaHeight()` 是否大于 `0` +- `objectId.IsRequested()` 时 `objectId.IsValid()` 是否为真 +- `builtinPostProcess.objectIdTextureView` 非空时,是否同时请求了 `objectId` ## 返回值 - 返回 `true`:`scene`、`camera` 非空,且 `context` 通过校验。 -- 返回 `false`:至少有一个基础条件不满足。 - -## 使用建议 - -- 这只是“最小可提交”校验,不等价于“渲染一定成功”。 -- 如果调用方自己构造请求,仍然应该保证 `surface` 尺寸、附件和状态切换配置合理。 +- 返回 `false`:至少有一个最小前提不满足。 ## 相关文档 diff --git a/docs/api/XCEngine/Rendering/CameraRenderer/CameraRenderer.md b/docs/api/XCEngine/Rendering/CameraRenderer/CameraRenderer.md index e4d665a5..94ebc8a9 100644 --- a/docs/api/XCEngine/Rendering/CameraRenderer/CameraRenderer.md +++ b/docs/api/XCEngine/Rendering/CameraRenderer/CameraRenderer.md @@ -6,60 +6,80 @@ **头文件**: `XCEngine/Rendering/CameraRenderer.h` -**描述**: 面向单个相机请求的轻量渲染执行器,负责提取该相机视角下的场景数据并交给当前管线。 +**描述**: 面向单个 `CameraRenderRequest` 的渲染执行器,负责场景提取、主管线提交、object-id 输出和 builtin post-process 串接。 ## 概览 -`CameraRenderer` 可以理解成当前渲染层里的“单相机提交器”。它不负责决定要渲染哪些相机,也不负责管理 swap chain,而是聚焦在一件事上: +`CameraRenderer` 是当前渲染模块里的“单相机执行层”。它不负责从场景里挑哪些相机,也不负责对多个请求排序;这些工作由 [SceneRenderer](../SceneRenderer/SceneRenderer.md) 处理。它关心的是如何把一份 [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md) 真正跑完: -1. 校验 [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md)。 -2. 用 `RenderSceneExtractor` 从场景中抽取该相机看到的数据。 -3. 把结果交给当前 `RenderPipeline`。 +1. 校验请求和 render area。 +2. 用 `RenderSceneExtractor` 提取该相机视角下的 `RenderSceneData`。 +3. 执行主 `RenderPipeline`。 +4. 按需执行 `ObjectIdPass`。 +5. 执行调用方注入的 pre / post / overlay pass。 +6. 构建并执行 builtin post-process 序列。 -这和很多商业引擎里“scene renderer 负责组织 camera list,camera renderer 负责执行单 camera pass”的分层很接近。这样做的好处是未来要扩展多相机、离屏目标、编辑器预览窗口时,不必把所有逻辑堆在同一个类里。 +因此它已经不只是“单纯把场景交给 pipeline”,而是当前 camera submission 的主要编排器。 -## 生命周期 +## 持有的运行时对象 -- 默认构造会自动创建 `Pipelines::BuiltinForwardPipeline`。 -- 传入自定义管线构造时,如果传的是空 `unique_ptr`,当前实现也会回退到默认前向管线。 -- 析构时如果当前管线存在,会显式调用 `Shutdown()`。 -- [SetPipeline](SetPipeline.md) 在替换管线前也会先对旧管线执行 `Shutdown()`。 +- `m_sceneExtractor`:负责按相机和 render area 抽取可见物体。 +- `m_pipelineAsset`:可选,表示当前管线来自哪份资产工厂。 +- `m_pipeline`:当前实际执行主场景绘制的管线实例。 +- `m_objectIdPass`:主场景之后的 object-id 输出 pass,默认是 `Passes::BuiltinObjectIdPass`。 +- `m_builtinPostProcessBuilder`:把无限网格、selection outline、debug mask 组装成 `RenderPassSequence`。 -## 所有权 +## 生命周期与所有权 -- `CameraRenderer` 通过 `std::unique_ptr` 独占持有当前管线。 -- 调用方一旦把管线传给构造函数或 [SetPipeline](SetPipeline.md),所有权就转移给 `CameraRenderer`。 +- 默认构造会回退到内建前向管线资产,并默认创建 builtin object-id pass。 +- 手动注入 `RenderPipeline` 时,`CameraRenderer` 通过 `std::unique_ptr` 接管所有权。 +- 通过 [SetPipelineAsset](SetPipelineAsset.md) 走的是“资产创建实例”路径;通过 [SetPipeline](SetPipeline.md) 走的是“手动注入实例”路径。 +- 析构时会依次 `Shutdown()` 当前管线、当前 object-id pass,以及 builtin post-process builder。 -## 线程语义 +## 当前执行顺序 -- 当前实现没有内部锁。 -- `Render()`、`SetPipeline()` 和析构都假定由渲染主线程或拥有明确时序保证的调用点执行。 -- 不应在一个线程渲染的同时,另一个线程替换或销毁管线。 +对一份有效请求,`Render()` 的顺序是: + +1. `preScenePasses` +2. 主 `m_pipeline->Render(...)` +3. `m_objectIdPass->Render(...)`,如果请求了 object-id 输出 +4. `postScenePasses` +5. builtin post-process 序列 +6. `overlayPasses` + +这条顺序在 `tests/Rendering/unit/test_camera_scene_renderer.cpp` 里被明确验证过。 ## 当前实现边界 -- `Render()` 内部使用 `ExtractForCamera()`,因此它只处理单相机场景提取。 -- 如果提取后 `sceneData.HasCamera()` 为假,即使请求对象本身有效,也会返回 `false`。 -- 当前不会在 `Render()` 里调用 `Initialize()`;默认前向管线如何初始化,取决于具体管线自身实现。 +- 只处理单个相机请求,多相机排序和 camera stack 语义在更上层完成。 +- 主场景提取尺寸取自 `surface` 的 render area,而不是整张 surface 的宽高。 +- builtin post-process 当前依赖 object-id SRV、scene viewport grid 数据和 `Passes` 子模块,不是通用 render graph。 +- 没有内部锁;替换管线、替换 object-id pass 和执行渲染都假定由拥有明确时序的线程调用。 ## 公开方法 | 方法 | 说明 | |------|------| -| [Constructor](Constructor.md) | 创建相机渲染器,并建立默认或注入的管线。 | -| [Destructor](Destructor.md) | 关闭当前管线。 | -| [SetPipeline](SetPipeline.md) | 替换当前渲染管线。 | -| [GetPipeline](GetPipeline.md) | 读取当前持有的管线裸指针。 | -| [Render](Render.md) | 执行一次单相机渲染请求。 | +| [Constructor](Constructor.md) | 创建渲染器,并建立默认或注入的主管线 / object-id pass。 | +| [Destructor](Destructor.md) | 关闭当前管线、object-id pass 和 builtin post-process builder。 | +| [SetPipeline](SetPipeline.md) | 手动替换当前主管线实例,并清空当前 pipeline asset 来源。 | +| [SetPipelineAsset](SetPipelineAsset.md) | 通过 `RenderPipelineAsset` 重建主管线实例。 | +| [SetObjectIdPass](SetObjectIdPass.md) | 替换当前 object-id pass。 | +| [GetPipeline](GetPipeline.md) | 读取当前主管线裸指针。 | +| [GetPipelineAsset](GetPipelineAsset.md) | 读取当前 pipeline asset 裸指针。 | +| [GetObjectIdPass](GetObjectIdPass.md) | 读取当前 object-id pass 裸指针。 | +| [Render](Render.md) | 执行一次完整的单相机渲染提交流程。 | ## 真实使用位置 -- `SceneRenderer` 当前把多相机流程拆成多个 `CameraRenderRequest` 后,逐个交给 `CameraRenderer`。 -- `tests/Rendering/unit/test_camera_scene_renderer.cpp` 验证了相机覆盖、清屏标志传递、管线替换和析构关停行为。 +- `SceneRenderer::Render(...)` 会把排序后的 `CameraRenderRequest` 逐个交给它。 +- 编辑器 scene viewport 通过 `CameraRenderRequest.objectId` 与 `builtinPostProcess` 接到这里。 +- `tests/Rendering/unit/test_camera_scene_renderer.cpp` 覆盖了 clear override、render area、注入 pass 顺序、pipeline asset 替换和 object-id 失败回退等关键行为。 ## 相关文档 - [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md) - [SceneRenderer](../SceneRenderer/SceneRenderer.md) - [RenderPipeline](../RenderPipeline/RenderPipeline.md) +- [Passes](../Passes/Passes.md) - [Scene Extraction And Builtin Forward Pipeline](../../../_guides/Rendering/Scene-Extraction-And-Builtin-Forward-Pipeline.md) diff --git a/docs/api/XCEngine/Rendering/CameraRenderer/Constructor.md b/docs/api/XCEngine/Rendering/CameraRenderer/Constructor.md index 7e6ffebe..87dcc672 100644 --- a/docs/api/XCEngine/Rendering/CameraRenderer/Constructor.md +++ b/docs/api/XCEngine/Rendering/CameraRenderer/Constructor.md @@ -11,29 +11,49 @@ ```cpp CameraRenderer(); explicit CameraRenderer(std::unique_ptr pipeline); +explicit CameraRenderer(std::shared_ptr pipelineAsset); +CameraRenderer( + std::unique_ptr pipeline, + std::unique_ptr objectIdPass); ``` ## 作用 -创建一个可执行单相机渲染的 `CameraRenderer`,并为它准备当前要使用的渲染管线。 +创建一个能够执行单相机请求的 `CameraRenderer`,并为它准备主管线来源与 object-id pass。 ## 当前实现行为 -- 默认构造:直接创建 `Pipelines::BuiltinForwardPipeline`。 -- 注入构造:优先接管调用方传入的管线。 -- 如果注入构造拿到的是空 `unique_ptr`,当前实现会回退成默认的 `BuiltinForwardPipeline`,而不是让对象进入“无管线”状态。 +### 默认构造 + +- 通过内部静态 `CreateDefaultPipelineAsset()` 取得 `Pipelines::BuiltinForwardPipelineAsset`。 +- 再走 pipeline asset 构造路径创建默认主管线。 +- 同时默认创建 `Passes::BuiltinObjectIdPass`。 + +### `std::unique_ptr` 重载 + +- 接管调用方给出的主管线实例。 +- 如果 `objectIdPass` 没显式传入,则默认补一个 `Passes::BuiltinObjectIdPass`。 + +### `std::shared_ptr` 重载 + +- 保存 asset 指针。 +- 通过 `SetPipelineAsset()` 立即从 asset 创建一个新的主管线实例。 +- 同时默认创建 `Passes::BuiltinObjectIdPass`。 + +### `(pipeline, objectIdPass)` 重载 + +- 同时接管主管线和 object-id pass。 +- 如果传入的 `objectIdPass` 为空,会自动回退成 `Passes::BuiltinObjectIdPass`。 +- 如果传入的 `pipeline` 为空,`ResetPipeline()` 会进一步回退到默认 pipeline asset 创建的内建前向管线。 ## 所有权 -- 第二个重载会接管 `pipeline` 的所有权。 -- 调用结束后,原来的 `unique_ptr` 会被移动为空。 - -## 使用建议 - -- 如果你只是想沿用引擎当前默认渲染路径,直接用默认构造。 -- 如果你要插入测试管线、调试管线或自定义前向/延迟管线,用注入构造更合适。 +- `RenderPipeline` 与 `ObjectIdPass` 都由 `CameraRenderer` 独占持有。 +- `RenderPipelineAsset` 以 `shared_ptr` 形式共享持有,仅作为“如何创建管线实例”的来源。 ## 相关文档 - [CameraRenderer](CameraRenderer.md) -- [CameraRenderer::SetPipeline](SetPipeline.md) +- [SetPipeline](SetPipeline.md) +- [SetPipelineAsset](SetPipelineAsset.md) +- [SetObjectIdPass](SetObjectIdPass.md) diff --git a/docs/api/XCEngine/Rendering/CameraRenderer/Destructor.md b/docs/api/XCEngine/Rendering/CameraRenderer/Destructor.md index 0822a7e0..ee271a2f 100644 --- a/docs/api/XCEngine/Rendering/CameraRenderer/Destructor.md +++ b/docs/api/XCEngine/Rendering/CameraRenderer/Destructor.md @@ -14,23 +14,30 @@ ## 作用 -在对象销毁前关闭当前持有的渲染管线。 +在对象销毁前关闭当前持有的主管线、object-id pass 和 builtin post-process builder。 ## 当前实现行为 -- 如果 `m_pipeline` 非空,析构函数会调用一次 `m_pipeline->Shutdown()`。 -- 之后由 `std::unique_ptr` 自身负责释放管线对象内存。 +析构函数当前按以下顺序执行清理: + +1. 如果 `m_pipeline` 非空,调用 `m_pipeline->Shutdown()`。 +2. 如果 `m_objectIdPass` 非空,调用 `m_objectIdPass->Shutdown()`。 +3. 调用 `m_builtinPostProcessBuilder.Shutdown()`。 + +随后由 `unique_ptr` 和成员对象自身析构负责释放内存。 ## 设计含义 -这说明当前渲染层把“释放 GPU 侧状态”视为管线对象自己的职责,而不是单纯依赖 C++ 析构。这样能让 `RenderPipeline` 把资源回收逻辑集中在 `Shutdown()` 中,也便于替换管线时复用同一条关停路径。 +- 主管线和 object-id pass 的 GPU 资源回收路径是显式 `Shutdown()`,而不是只依赖 C++ 析构。 +- builtin post-process builder 会代为清理它内部缓存的 builtin grid / outline pass。 ## 注意事项 -- 由于没有加锁,不应在别的线程仍然使用当前管线时销毁 `CameraRenderer`。 -- 如果你在生命周期中途就要切换管线,优先调用 [SetPipeline](SetPipeline.md),不要等到析构时再被动释放。 +- 当前没有内部同步;不应在其它线程仍然可能访问管线或命令对象时销毁 `CameraRenderer`。 +- 如果你只是想切换主管线或 object-id pass,不要等到析构,应该优先使用 [SetPipeline](SetPipeline.md)、[SetPipelineAsset](SetPipelineAsset.md) 或 [SetObjectIdPass](SetObjectIdPass.md)。 ## 相关文档 - [CameraRenderer](CameraRenderer.md) -- [CameraRenderer::SetPipeline](SetPipeline.md) +- [SetPipeline](SetPipeline.md) +- [SetObjectIdPass](SetObjectIdPass.md) diff --git a/docs/api/XCEngine/Rendering/CameraRenderer/GetObjectIdPass.md b/docs/api/XCEngine/Rendering/CameraRenderer/GetObjectIdPass.md new file mode 100644 index 00000000..68af8c98 --- /dev/null +++ b/docs/api/XCEngine/Rendering/CameraRenderer/GetObjectIdPass.md @@ -0,0 +1,31 @@ +# CameraRenderer::GetObjectIdPass + +**命名空间**: `XCEngine::Rendering` + +**类型**: `method` + +**头文件**: `XCEngine/Rendering/CameraRenderer.h` + +## 签名 + +```cpp +ObjectIdPass* GetObjectIdPass() const; +``` + +## 作用 + +返回当前 object-id pass 的裸指针。 + +## 当前实现行为 + +- 这是头文件内联访问器,直接返回 `m_objectIdPass.get()`。 +- 不转移所有权。 + +## 注意事项 + +- 调用 [SetObjectIdPass](SetObjectIdPass.md) 后,之前读取到的裸指针可能立即失效。 + +## 相关文档 + +- [SetObjectIdPass](SetObjectIdPass.md) +- [Render](Render.md) diff --git a/docs/api/XCEngine/Rendering/CameraRenderer/GetPipeline.md b/docs/api/XCEngine/Rendering/CameraRenderer/GetPipeline.md index 54d2afb5..44d14f04 100644 --- a/docs/api/XCEngine/Rendering/CameraRenderer/GetPipeline.md +++ b/docs/api/XCEngine/Rendering/CameraRenderer/GetPipeline.md @@ -14,24 +14,24 @@ RenderPipeline* GetPipeline() const; ## 作用 -返回当前持有的渲染管线裸指针,便于外部查询当前绑定的是哪条管线。 +返回当前主管线实例的裸指针。 ## 当前实现行为 - 这是头文件内联访问器,直接返回 `m_pipeline.get()`。 -- 不转移所有权,也不增加生命周期保证。 +- 不转移所有权,也不提供额外生命周期保证。 ## 返回值 -- 返回当前管线的裸指针。 -- 理论上可能为 `nullptr`,但按当前构造和 `SetPipeline()` 的策略,正常路径下通常总会有一条默认管线。 +- 正常路径下通常会返回一个有效指针,因为构造和 `SetPipeline()` / `SetPipelineAsset()` 都会回退到默认管线。 +- 理论上如果对象正处于构造中的异常路径或未来实现改变,也不能把“永不为空”当成稳定契约。 ## 注意事项 -- 不要在外部缓存这个指针并假设它长期有效;`SetPipeline()` 后它可能立刻失效。 -- 如果只是要替换管线,应使用 [SetPipeline](SetPipeline.md),不要手动管理这个裸指针。 +- 不要长期缓存这个裸指针;调用 [SetPipeline](SetPipeline.md) 或 [SetPipelineAsset](SetPipelineAsset.md) 后它可能立即失效。 ## 相关文档 -- [CameraRenderer](CameraRenderer.md) -- [CameraRenderer::SetPipeline](SetPipeline.md) +- [SetPipeline](SetPipeline.md) +- [SetPipelineAsset](SetPipelineAsset.md) +- [GetPipelineAsset](GetPipelineAsset.md) diff --git a/docs/api/XCEngine/Rendering/CameraRenderer/GetPipelineAsset.md b/docs/api/XCEngine/Rendering/CameraRenderer/GetPipelineAsset.md new file mode 100644 index 00000000..8996a112 --- /dev/null +++ b/docs/api/XCEngine/Rendering/CameraRenderer/GetPipelineAsset.md @@ -0,0 +1,32 @@ +# CameraRenderer::GetPipelineAsset + +**命名空间**: `XCEngine::Rendering` + +**类型**: `method` + +**头文件**: `XCEngine/Rendering/CameraRenderer.h` + +## 签名 + +```cpp +const RenderPipelineAsset* GetPipelineAsset() const; +``` + +## 作用 + +返回当前绑定的 pipeline asset 裸指针。 + +## 当前实现行为 + +- 这是头文件内联访问器,直接返回 `m_pipelineAsset.get()`。 +- 如果最近一次切换是通过 [SetPipeline](SetPipeline.md) 手动注入实例完成的,这里可能返回 `nullptr`。 + +## 返回值 + +- 返回当前 asset 裸指针,或空指针。 + +## 相关文档 + +- [SetPipelineAsset](SetPipelineAsset.md) +- [SetPipeline](SetPipeline.md) +- [GetPipeline](GetPipeline.md) diff --git a/docs/api/XCEngine/Rendering/CameraRenderer/Render.md b/docs/api/XCEngine/Rendering/CameraRenderer/Render.md index 391d898c..6c68a8bd 100644 --- a/docs/api/XCEngine/Rendering/CameraRenderer/Render.md +++ b/docs/api/XCEngine/Rendering/CameraRenderer/Render.md @@ -14,52 +14,69 @@ bool Render(const CameraRenderRequest& request); ## 作用 -执行一次单相机渲染请求。 +执行一次完整的单相机渲染提交,包括主场景、object-id 输出、调用方注入 pass 和 builtin 后处理。 ## 当前实现流程 -按 `engine/src/Rendering/CameraRenderer.cpp`,当前流程固定为: +按 `engine/src/Rendering/CameraRenderer.cpp`,当前流程是: -1. 如果 `request.IsValid()` 为假,返回 `false`。 -2. 如果当前没有管线,返回 `false`。 -3. 调用 `m_sceneExtractor.ExtractForCamera(*request.scene, *request.camera, request.surface.GetWidth(), request.surface.GetHeight())` 提取场景数据。 -4. 如果提取结果 `sceneData.HasCamera()` 为假,返回 `false`。 -5. 用 `request.clearFlags` 覆盖 `sceneData.cameraData.clearFlags`。 -6. 调用 `m_pipeline->Render(request.context, request.surface, sceneData)`,并把结果直接返回。 +1. 如果 `request.IsValid()` 为假,或 `m_pipeline == nullptr`,返回 `false`。 +2. 如果 `request.surface` 的 render area 宽高为 `0`,返回 `false`。 +3. 如果请求了 `objectId` 但 `objectId.IsValid()` 为假,返回 `false`。 +4. 如果 builtin post-process 里提供了 `objectIdTextureView`,但这次请求并没有真正请求 `objectId` 输出,返回 `false`。 +5. 用 `request.surface.GetRenderAreaWidth()` / `GetRenderAreaHeight()` 调用 `m_sceneExtractor.ExtractForCamera(...)`。 +6. 如果提取结果 `sceneData.HasCamera()` 为假,返回 `false`。 +7. 用 `request.clearFlags` 覆盖 `sceneData.cameraData.clearFlags`;如有需要,再用 `clearColorOverride` 覆盖 `sceneData.cameraData.clearColor`。 +8. 依次执行: + - `preScenePasses` + - 主 `m_pipeline->Render(...)` + - `m_objectIdPass->Render(...)`,如果请求了 object-id + - `postScenePasses` + - builtin post-process 序列 + - `overlayPasses` +9. 每个阶段失败时,按已经初始化过的 pass 序列倒序 `Shutdown()` 并返回 `false`。 + +## builtin post-process 接入点 + +如果 `request.builtinPostProcess.IsRequested()` 为真,`Render()` 会: + +1. 调用 `m_builtinPostProcessBuilder.Build(...)` 生成一个临时 `RenderPassSequence`。 +2. 对这个序列执行 `Initialize(context)`。 +3. 在 `postScenePasses` 之后、`overlayPasses` 之前执行它。 + +这意味着 scene viewport 的无限网格、selection outline 和 debug mask 都是在主场景绘制完成之后叠加的。 ## 参数 | 参数 | 说明 | |------|------| -| `request` | 本次相机渲染的完整输入。 | +| `request` | 本次单相机渲染的完整输入。 | ## 返回值 -- 返回 `true`:请求通过校验,提取到有效相机数据,且当前管线 `Render()` 返回成功。 -- 返回 `false`:请求无效、管线为空、提取失败,或管线执行失败。 +- 返回 `true`:主流程和所有已请求的附加阶段都成功执行。 +- 返回 `false`:请求校验失败、场景提取失败、主管线失败,或任意一个附加 pass 阶段失败。 ## 关键语义 -- 当前实现会信任请求里的 `camera`,不再重新挑选最高深度主相机。 -- `surface` 的宽高会直接传给场景提取器,用来生成相机视口数据。 -- `clearFlags` 以请求对象为准,因此外部调度器可以统一覆盖某次渲染的清理策略。 +- 相机视口尺寸来自 `surface` 的 render area,而不是整张 surface 的宽高。 +- object-id pass 位于主场景之后、`postScenePasses` 之前。 +- builtin post-process 位于 `postScenePasses` 之后、`overlayPasses` 之前。 +- builder 负责决定 selection outline / debug mask 的具体回退策略,`CameraRenderer` 只负责执行 builder 给出的结果。 -## 最小可置信示例 +## 测试覆盖 -```cpp -CameraRenderRequest request; -request.scene = &scene; -request.camera = camera; -request.context = context; -request.surface = RenderSurface(1280, 720); -request.clearFlags = RenderClearFlags::All; +`tests/Rendering/unit/test_camera_scene_renderer.cpp` 当前验证了: -CameraRenderer renderer; -const bool ok = renderer.Render(request); -``` +- render area 会影响提取到的相机视口尺寸 +- `clearFlags` 和 clear color override 会写回 `sceneData.cameraData` +- pre / post / object-id pass 的执行顺序 +- object-id pass 失败和 post-pass 初始化失败时的清理路径 +- builtin post-process 在缺少 fresh object-id 数据时会被拒绝 ## 相关文档 - [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md) - [RenderSceneExtractor](../RenderSceneExtractor/RenderSceneExtractor.md) - [RenderPipeline::Render](../RenderPipeline/Render.md) +- [Passes](../Passes/Passes.md) diff --git a/docs/api/XCEngine/Rendering/CameraRenderer/SetObjectIdPass.md b/docs/api/XCEngine/Rendering/CameraRenderer/SetObjectIdPass.md new file mode 100644 index 00000000..a8822073 --- /dev/null +++ b/docs/api/XCEngine/Rendering/CameraRenderer/SetObjectIdPass.md @@ -0,0 +1,34 @@ +# CameraRenderer::SetObjectIdPass + +**命名空间**: `XCEngine::Rendering` + +**类型**: `method` + +**头文件**: `XCEngine/Rendering/CameraRenderer.h` + +## 签名 + +```cpp +void SetObjectIdPass(std::unique_ptr objectIdPass); +``` + +## 作用 + +替换主场景之后使用的 object-id 输出 pass。 + +## 当前实现行为 + +1. 如果旧 `m_objectIdPass` 非空,先调用它的 `Shutdown()`。 +2. 接管新的 `objectIdPass`。 +3. 如果新指针为空,则自动回退到 `Passes::BuiltinObjectIdPass`。 + +## 关键语义 + +- 这是 object-id 渲染链路的主要注入点,测试里可以通过它替换 mock pass。 +- `CameraRenderer::Render()` 只在 `request.objectId.IsRequested()` 时才会真正调用这里持有的 pass。 + +## 相关文档 + +- [GetObjectIdPass](GetObjectIdPass.md) +- [Render](Render.md) +- [Passes](../Passes/Passes.md) diff --git a/docs/api/XCEngine/Rendering/CameraRenderer/SetPipeline.md b/docs/api/XCEngine/Rendering/CameraRenderer/SetPipeline.md index a3426294..84b9a152 100644 --- a/docs/api/XCEngine/Rendering/CameraRenderer/SetPipeline.md +++ b/docs/api/XCEngine/Rendering/CameraRenderer/SetPipeline.md @@ -14,33 +14,30 @@ void SetPipeline(std::unique_ptr pipeline); ## 作用 -替换 `CameraRenderer` 当前持有的渲染管线。 +手动替换当前主管线实例,并切断与 `RenderPipelineAsset` 的绑定关系。 ## 当前实现行为 -按 `engine/src/Rendering/CameraRenderer.cpp` 的实现,替换流程是: - -1. 如果旧管线存在,先调用旧管线的 `Shutdown()`。 -2. 用传入的 `unique_ptr` 覆盖当前管线。 -3. 如果新指针为空,再自动回退成 `Pipelines::BuiltinForwardPipeline`。 +1. 先把 `m_pipelineAsset.reset()`,表示后续不再认为当前管线来自某个 asset。 +2. 调用内部 `ResetPipeline(std::move(pipeline))`。 +3. `ResetPipeline()` 会: + - 对旧 `m_pipeline` 调用 `Shutdown()` + - 接管新的 `pipeline` + - 如果新指针为空,则回退到默认 pipeline asset 创建的内建前向管线 ## 参数 | 参数 | 说明 | |------|------| -| `pipeline` | 新的渲染管线所有权。可以为空;为空时会自动回退到默认前向管线。 | +| `pipeline` | 新的主管线实例所有权。可以为空;为空时会自动回退到默认内建前向管线。 | -## 副作用 +## 关键语义 -- 会主动关闭旧管线,而不是简单替换指针。 -- 传入空指针并不会让渲染器失效;当前实现选择“总有一条默认管线可用”。 - -## 使用建议 - -- 如果你在测试里注入 mock pipeline,这个接口是最合适的切换点。 -- 如果新旧管线有共享的 GPU 资源管理需求,当前实现没有做迁移与热切换,只是单纯关旧建新。 +- 这是“直接注入实例”的入口,不会保留原来的 pipeline asset。 +- 替换时走的是显式关停旧管线的路径,而不是简单换指针。 ## 相关文档 -- [CameraRenderer](CameraRenderer.md) -- [CameraRenderer::GetPipeline](GetPipeline.md) +- [SetPipelineAsset](SetPipelineAsset.md) +- [GetPipeline](GetPipeline.md) +- [GetPipelineAsset](GetPipelineAsset.md) diff --git a/docs/api/XCEngine/Rendering/CameraRenderer/SetPipelineAsset.md b/docs/api/XCEngine/Rendering/CameraRenderer/SetPipelineAsset.md new file mode 100644 index 00000000..7def2c60 --- /dev/null +++ b/docs/api/XCEngine/Rendering/CameraRenderer/SetPipelineAsset.md @@ -0,0 +1,35 @@ +# CameraRenderer::SetPipelineAsset + +**命名空间**: `XCEngine::Rendering` + +**类型**: `method` + +**头文件**: `XCEngine/Rendering/CameraRenderer.h` + +## 签名 + +```cpp +void SetPipelineAsset(std::shared_ptr pipelineAsset); +``` + +## 作用 + +通过一份 `RenderPipelineAsset` 重建当前主管线实例。 + +## 当前实现行为 + +1. 如果传入的 `pipelineAsset` 非空,则保存它。 +2. 如果传入为空,则回退到内部静态默认 asset。 +3. 调用 `CreatePipelineFromAsset(m_pipelineAsset)` 创建新的管线实例。 +4. 再通过 `ResetPipeline(...)` 关停旧管线并接管新实例。 + +## 关键语义 + +- 这是“从 asset 创建实例”的入口,和 [SetPipeline](SetPipeline.md) 的手动注入路径不同。 +- 即使 asset 无法返回有效实例,`CreatePipelineFromAsset()` 也会继续回退到内建前向管线。 + +## 相关文档 + +- [SetPipeline](SetPipeline.md) +- [GetPipelineAsset](GetPipelineAsset.md) +- [Constructor](Constructor.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/BuiltinInfiniteGridPass.md b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/BuiltinInfiniteGridPass.md new file mode 100644 index 00000000..ce52d484 --- /dev/null +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/BuiltinInfiniteGridPass.md @@ -0,0 +1,48 @@ +# BuiltinInfiniteGridPass + +**命名空间**: `XCEngine::Rendering::Passes` + +**类型**: `class` + +**头文件**: `XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h` + +**描述**: 在场景颜色目标上叠加编辑器无限地面网格的内建全屏 pass。 + +## 概述 + +`BuiltinInfiniteGridPass` 是当前 scene viewport 里“地面参考网格”效果的实际执行者。 + +它消费一份相机姿态数据 `InfiniteGridPassData`,在运行时先推导网格尺度和过渡参数,再用一张 builtin shader 把网格直接混合到目标颜色附件上。 + +## 关键输入 + +- `InfiniteGridPassData` 决定相机位置、朝向、FOV、裁剪面和轨道距离。 +- `BuildInfiniteGridParameters()` 会把这些数据折算成 `baseScale`、`transitionBlend` 和 `fadeDistance`。 +- `RenderSurface` 需要同时提供颜色附件和深度附件;当前 pass 会开启深度测试但关闭深度写入。 + +## 当前实现流程 + +1. 先校验 `data.valid`、`RenderContext::IsValid()` 和 `backendType == D3D12`。 +2. 通过 `EnsureInitialized()` 惰性创建 pipeline layout、pipeline state 和常量描述符。 +3. 从相机姿态推导网格参数,并构建 view-projection 常量。 +4. 把第一个颜色附件和深度附件绑定为渲染目标。 +5. 以全屏三角形方式发出一次 `Draw(3, 1, 0, 0)`。 + +## 当前实现边界 + +- 当前只支持 `D3D12` builtin shader variant。 +- 只写入 `surface` 的第一个颜色附件。 +- 视口和裁剪矩形使用 `surface.GetWidth()` / `GetHeight()`,不是自定义 render area。 +- 网格参数完全由当前相机高度与朝向启发式推导,还没有暴露成更完整的编辑器样式配置。 + +## 真实使用位置 + +- `BuiltinPostProcessPassSequenceBuilder::AddInfiniteGridPass()` 会把它包装进 builtin post-process 序列。 +- `CameraRenderer` 在执行 `builtinPostProcess` 请求时,最终通过 builder 间接调用这里。 +- `tests/Rendering/unit/test_builtin_post_process_pass_sequence_builder.cpp` 覆盖了 grid-only 和 grid+selection 两类序列构建分支。 + +## 相关文档 + +- [Passes](../Passes.md) +- [BuiltinPostProcessPassSequenceBuilder](../BuiltinPostProcessPassSequenceBuilder/BuiltinPostProcessPassSequenceBuilder.md) +- [CameraRenderer](../../CameraRenderer/CameraRenderer.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md new file mode 100644 index 00000000..71c23f79 --- /dev/null +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md @@ -0,0 +1,102 @@ +# BuiltinObjectIdOutlinePass + +**命名空间**: `XCEngine::Rendering::Passes` + +**类型**: `class` + +**头文件**: `XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h` + +**描述**: 内建的全屏后处理 pass,读取 object-id 纹理并把选中对象轮廓或调试 selection mask 合成到当前颜色目标。 + +## 概览 + +`BuiltinObjectIdOutlinePass` 处在“object-id 已经生成,但主场景也已经画完”的阶段。它本身不负责提取可见物体,也不负责生成 object-id 纹理,而是消费这些上游结果: + +- object-id 纹理通常来自 `BuiltinObjectIdPass`。 +- 选中对象列表来自 [CameraRenderRequest](../../CameraRenderRequest/CameraRenderRequest.md) 的 `builtinPostProcess.selectedObjectIds`。 +- 调用时机由 `BuiltinPostProcessPassSequenceBuilder` 决定,并最终由 [CameraRenderer](../../CameraRenderer/CameraRenderer.md) 在主场景之后提交。 + +因此它更接近一个“builtin selection feedback compositor”,而不是一个通用 post-process 框架。 + +## 输入与输出契约 + +| 输入 / 输出 | 当前要求 | +|------|------| +| `renderContext` | 必须有效,且后端类型必须是 `D3D12`。 | +| `surface` | 至少要有一个有效的颜色附件;pass 只写 `colorAttachments[0]`。 | +| `objectIdTextureView` | 不能为空,并且调用方要保证它已经处于可作为 SRV 读取的状态。 | +| `selectedObjectIds` | 不能为空;超过 `256` 个时只会取前 `256` 个。 | +| `style` | 决定轮廓颜色、宽度和是否改为输出 debug mask。 | +| 输出 | 在主颜色目标上 alpha 混合轮廓,或在 debug 模式下输出黑白 selection mask。 | + +## 当前渲染算法 + +按 `BuiltinObjectIdOutlinePass.cpp` 与 builtin shader 的当前实现,流程是: + +1. 校验上下文、后端、object-id SRV 和选中对象列表。 +2. 懒初始化内部资源,必要时重新创建 pipeline layout、pipeline state 和两个 descriptor set。 +3. 从 `surface.GetWidth()` / `GetHeight()` 计算视口尺寸与 texel size。 +4. 用 `EncodeObjectIdToColor()` 把选中对象 ID 编码进常量缓冲,最多写入 `kMaxSelectedObjectCount = 256` 项。 +5. 绑定一个 CBV set 和一个 SRV set,设置目标为 `surface` 的第一个颜色附件。 +6. 以全屏三角形方式执行 `Draw(3, 1, 0, 0)`。 + +shader 侧的行为是: + +- 正常模式下,如果当前像素本身属于选中对象,则直接 `discard`,只保留“选中物体边缘外侧”的轮廓。 +- outline 强度来自周围邻域是否出现选中 object-id,并按像素距离做一次简单衰减。 +- debug 模式下,不做轮廓混合,而是把选中像素直接输出为白色,未选中输出为黑色。 + +## 依赖资源 + +- builtin shader 资源路径固定为 `builtin://shaders/object-id-outline`。 +- pass 优先查找 shader pass 名称 `ObjectIdOutline`,其次是 `EditorObjectIdOutline`,再退回到第一个可用的 graphics pass。 +- 内部维护: + - 一个 pipeline layout + - 一个 pipeline state + - 一个常量 descriptor pool / set + - 一个纹理 descriptor pool / set + +这些资源都按 device/backend 缓存,直到 [Shutdown](Shutdown.md) 或下次 [Render](Render.md) 发现上下文不匹配时重建。 + +## 生命周期 + +- [Constructor](Constructor.md) 只做状态清零,不会立刻创建 GPU 资源。 +- 第一次 [Render](Render.md) 成功进入初始化路径时,才会真正创建 descriptor 和 PSO。 +- [Shutdown](Shutdown.md) 会显式销毁所有内部缓存对象。 +- [Destructor](Destructor.md) 当前是默认析构,因此如果脱离 `BuiltinPostProcessPassSequenceBuilder` 单独使用,调用方需要自己保证在销毁前执行 `Shutdown()`。 + +## 当前实现边界 + +- 只接受 `D3D12`,其它后端直接返回 `false`。 +- 图形管线输出格式固定为 `R8G8B8A8_UNorm`,没有暴露可配置的 render target format。 +- 不使用深度测试和深度写入,也不读取 `surface` 的深度附件。 +- 不处理资源状态切换,也不负责清屏;这些由 builder 或更外层调用方保证。 +- 当前完全忽略 `surface.GetRenderArea()`,viewport 和 scissor 都直接取整张 surface 的宽高。 +- builtin shader 只采样 `[-2, 2]` 邻域,因此 `outlineWidthPixels` 大于约 `2` 像素时不会继续带来更宽的轮廓。 +- 只使用第一个颜色附件;多 render target 场景不会同步写其它附件。 +- object-id 颜色编码只保留 `uint64_t` 对象 ID 的低 `32` 位。 + +## 公开方法 + +| 方法 | 说明 | +|------|------| +| [Constructor](Constructor.md) | 创建一个空的 outline pass 对象。 | +| [Destructor](Destructor.md) | 默认析构;不会自动释放内部 RHI 资源。 | +| [Shutdown](Shutdown.md) | 销毁已创建的 pipeline、descriptor 和 shader handle。 | +| [Render](Render.md) | 读取 object-id SRV,并把轮廓或 debug mask 写入颜色目标。 | + +## 真实使用位置 + +- `BuiltinPostProcessPassSequenceBuilder::AddSelectionOutlinePass()` 会把它包装成 `SceneSelectionOutline` render pass。 +- `BuiltinPostProcessPassSequenceBuilder::AddSelectionMaskDebugPass()` 会在清空目标后复用同一个 pass 输出 debug mask。 +- [CameraRenderer](../../CameraRenderer/CameraRenderer.md) 在主场景与 object-id pass 之后执行 builtin post-process 序列。 +- `editor/src/Viewport/ViewportHostRenderFlowUtils.h` 当前为 scene viewport 组装默认橙色、`2px` 的 outline style。 +- `tests/Rendering/unit/test_builtin_post_process_pass_sequence_builder.cpp`、`tests/Rendering/unit/test_camera_scene_renderer.cpp` 与 `tests/Editor/test_viewport_render_flow_utils.cpp` 覆盖了它的主要接入链路。 + +## 相关文档 + +- [Passes](../Passes.md) +- [ObjectIdOutlineStyle](../ObjectIdOutlineStyle/ObjectIdOutlineStyle.md) +- [CameraRenderRequest](../../CameraRenderRequest/CameraRenderRequest.md) +- [CameraRenderer](../../CameraRenderer/CameraRenderer.md) +- [RenderSurface](../../RenderSurface/RenderSurface.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Constructor.md b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Constructor.md new file mode 100644 index 00000000..c2636dc9 --- /dev/null +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Constructor.md @@ -0,0 +1,50 @@ +# BuiltinObjectIdOutlinePass::Constructor + +**命名空间**: `XCEngine::Rendering::Passes` + +**类型**: `constructor` + +**头文件**: `XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h` + +## 签名 + +```cpp +BuiltinObjectIdOutlinePass(); +``` + +## 作用 + +创建一个尚未分配 GPU 资源的 outline pass 对象。 + +## 当前实现行为 + +当前构造函数只做一件事: + +```cpp +ResetState(); +``` + +这意味着: + +- 不会在构造阶段加载 builtin shader。 +- 不会提前创建 pipeline layout、pipeline state 或 descriptor set。 +- 所有真实的 GPU 资源准备都延迟到第一次 [Render](Render.md) 成功进入初始化路径时完成。 + +## 初始化后状态 + +构造后对象处于“空缓存”状态: + +- `m_device == nullptr` +- `m_pipelineLayout == nullptr` +- `m_pipelineState == nullptr` +- `m_constantPool == nullptr` +- `m_constantSet == nullptr` +- `m_texturePool == nullptr` +- `m_textureSet == nullptr` +- `m_builtinObjectIdOutlineShader` 为空 + +## 相关文档 + +- [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass.md) +- [Render](Render.md) +- [Shutdown](Shutdown.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Destructor.md b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Destructor.md new file mode 100644 index 00000000..cbdf8adf --- /dev/null +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Destructor.md @@ -0,0 +1,34 @@ +# BuiltinObjectIdOutlinePass::~BuiltinObjectIdOutlinePass + +**命名空间**: `XCEngine::Rendering::Passes` + +**类型**: `destructor` + +**头文件**: `XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h` + +## 签名 + +```cpp +~BuiltinObjectIdOutlinePass() = default; +``` + +## 作用 + +销毁 outline pass 对象本身。 + +## 当前实现行为 + +- 当前析构函数没有自定义逻辑。 +- 它不会自动调用 [Shutdown](Shutdown.md)。 +- 如果对象销毁前仍持有已创建的 pipeline、descriptor pool 或 descriptor set,这些 raw pointer 不会在析构函数里被显式 `Shutdown()` 和 `delete`。 + +## 注意事项 + +- 正常生命周期应在销毁前先调用 [Shutdown](Shutdown.md)。 +- 这也是 `BuiltinPostProcessPassSequenceBuilder::Shutdown()` 会主动转调 `m_outlinePass.Shutdown()` 的原因。 +- 如果把这个类单独作为局部对象或成员对象使用,而不是放在已有的 builder 生命周期里,析构前漏掉 `Shutdown()` 会造成资源回收路径缺失。 + +## 相关文档 + +- [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass.md) +- [Shutdown](Shutdown.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Render.md b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Render.md new file mode 100644 index 00000000..6b75e3ca --- /dev/null +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Render.md @@ -0,0 +1,76 @@ +# BuiltinObjectIdOutlinePass::Render + +**命名空间**: `XCEngine::Rendering::Passes` + +**类型**: `method` + +**头文件**: `XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h` + +## 签名 + +```cpp +bool Render( + const RenderContext& renderContext, + const RenderSurface& surface, + RHI::RHIResourceView* objectIdTextureView, + const std::vector& selectedObjectIds, + const ObjectIdOutlineStyle& style = {}); +``` + +## 作用 + +读取 object-id 纹理,并把选中对象的轮廓或调试 selection mask 输出到颜色目标。 + +## 当前实现流程 + +按 `engine/src/Rendering/Passes/BuiltinObjectIdOutlinePass.cpp`,当前执行顺序是: + +1. 如果 `renderContext` 无效、后端不是 `D3D12`、`objectIdTextureView == nullptr` 或 `selectedObjectIds` 为空,直接返回 `false`。 +2. 调用 `EnsureInitialized(renderContext)`;如果当前 device/backend 变化,会先销毁旧资源再重建。 +3. 检查 `surface.GetColorAttachments()`,要求至少存在一个有效颜色附件。 +4. 构建 `OutlineConstants`: + - `viewportSizeAndTexelSize` 来自 `surface.GetWidth()` / `GetHeight()` + - `outlineColor` 来自 `style.outlineColor` + - `selectedInfo.x` 写入实际使用的选中对象数量 + - `selectedInfo.y` 表示是否启用 debug mask + - `selectedInfo.z` 写入 `style.outlineWidthPixels` + - `selectedObjectColors` 写入最多 `256` 个编码后的 object-id 颜色 +5. 把常量缓冲写到 `m_constantSet`,把 `objectIdTextureView` 绑定到 `m_textureSet`。 +6. 设置渲染目标为 `surface` 的第一个颜色附件。 +7. 设置全屏 viewport / scissor、三角形图元、pipeline state 和两个 graphics descriptor set。 +8. 提交一次 `Draw(3, 1, 0, 0)`。 + +## 参数 + +| 参数 | 说明 | +|------|------| +| `renderContext` | 当前帧的 RHI 设备、命令队列和命令列表。 | +| `surface` | 要写回轮廓结果的目标表面。 | +| `objectIdTextureView` | object-id 纹理的 SRV。 | +| `selectedObjectIds` | 需要高亮的对象 ID 列表。 | +| `style` | 轮廓颜色、宽度和 debug mask 开关。 | + +## 返回值 + +- 返回 `true`:校验通过、资源初始化成功,并且 draw call 已经提交到命令列表。 +- 返回 `false`:输入无效、资源创建失败,或目标颜色附件不可用。 + +## 关键语义 + +- 当前只使用 `surface` 的第一个颜色附件,其它附件会被忽略。 +- pass 自己不会做 render target 清空;如果要输出 debug mask 这种“整张替换”的结果,需要由调用方先清空目标。 +- pass 自己也不负责资源状态切换;调用前应保证颜色目标已经在 `RenderTarget` 状态、object-id SRV 已经可读。 +- 当前 viewport 和 scissor 始终覆盖整张 surface,而不是 `surface.GetRenderArea()`。 +- 选中对象列表超过 `BuiltinObjectIdOutlinePass::kMaxSelectedObjectCount` 时,后面的对象会被静默截断。 + +## 典型使用位置 + +- `BuiltinPostProcessPassSequenceBuilder::AddSelectionOutlinePass()`:正常轮廓模式。 +- `BuiltinPostProcessPassSequenceBuilder::AddSelectionMaskDebugPass()`:debug mask 模式,调用前会先清空颜色目标。 + +## 相关文档 + +- [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass.md) +- [ObjectIdOutlineStyle](../ObjectIdOutlineStyle/ObjectIdOutlineStyle.md) +- [RenderContext](../../RenderContext/RenderContext.md) +- [RenderSurface](../../RenderSurface/RenderSurface.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Shutdown.md b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Shutdown.md new file mode 100644 index 00000000..fddb93f1 --- /dev/null +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass/Shutdown.md @@ -0,0 +1,51 @@ +# BuiltinObjectIdOutlinePass::Shutdown + +**命名空间**: `XCEngine::Rendering::Passes` + +**类型**: `method` + +**头文件**: `XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h` + +## 签名 + +```cpp +void Shutdown(); +``` + +## 作用 + +释放这个 outline pass 已经创建的所有内部 GPU 资源和 shader handle。 + +## 当前实现行为 + +当前实现非常直接: + +```cpp +void BuiltinObjectIdOutlinePass::Shutdown() { + DestroyResources(); +} +``` + +`DestroyResources()` 会按顺序销毁: + +1. `m_pipelineState` +2. `m_textureSet` +3. `m_texturePool` +4. `m_constantSet` +5. `m_constantPool` +6. `m_pipelineLayout` +7. `m_builtinObjectIdOutlineShader` + +每个 RHI 对象在释放前都会先调用自身的 `Shutdown()`,随后再 `delete` 并把成员清空。最后会通过 `ResetState()` 回到未初始化状态。 + +## 关键语义 + +- 对尚未初始化过的对象调用 `Shutdown()` 是安全的。 +- 资源释放后,后续仍可重新 [Render](Render.md) 并按新的 `RenderContext` 懒创建资源。 +- 这个类当前不是完全 RAII 的:析构函数是默认析构,并不会自动代替 `Shutdown()` 执行清理。 + +## 相关文档 + +- [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass.md) +- [Render](Render.md) +- [Destructor](Destructor.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuiltinObjectIdPass.md b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuiltinObjectIdPass.md new file mode 100644 index 00000000..954d6322 --- /dev/null +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuiltinObjectIdPass.md @@ -0,0 +1,54 @@ +# BuiltinObjectIdPass + +**命名空间**: `XCEngine::Rendering::Passes` + +**类型**: `class` + +**头文件**: `XCEngine/Rendering/Passes/BuiltinObjectIdPass.h` + +**描述**: `ObjectIdPass` 的当前内建实现,把可见物体渲染到 object-id 颜色目标,供拾取和后续选择描边使用。 + +## 概述 + +`BuiltinObjectIdPass` 可以理解成主场景渲染旁边的一条辅助输出路径: + +- 主颜色目标仍由当前 `RenderPipeline` 负责。 +- object-id 目标由这条 pass 用单独 shader 再画一遍可见物体。 +- 每个物体的 `GameObject::GetID()` 会被编码成 RGBA 颜色写入目标纹理。 + +这样后续只要采样这张 object-id 纹理,就能做选中描边、拾取或调试显示,而不必回读主颜色缓冲。 + +## 当前实现流程 + +1. 校验 `RenderContext`、颜色附件、深度附件和 `RenderSurface` 的 render area。 +2. 惰性创建 pipeline layout、pipeline state 和 builtin object-id shader。 +3. 如果 `RenderSurface` 开启自动状态切换,先把颜色附件切到 `RenderTarget`。 +4. 清空 object-id 颜色目标为全零。 +5. 遍历 `RenderSceneData::visibleItems`,按物体 ID 建立或复用常量缓冲并发出 draw call。 +6. 若开启自动状态切换,再把颜色附件切回 `surface.GetColorStateAfter()`。 + +## 关键实现细节 + +- 当前 input layout 只声明了 `POSITION`,依赖 mesh 顶点位置即可完成 object-id 绘制。 +- 每个 object ID 会缓存一套独立 descriptor set,避免重复分配常量绑定。 +- `RenderResourceCache` 负责把 `Mesh` 上传或复用成 GPU 资源。 +- shader pass 解析优先匹配 builtin object-id pass tag,其次回退到 `ObjectId` / `EditorObjectId` 命名,最后才退到首个 pass。 + +## 当前实现边界 + +- 这条 pass 依赖调用方先准备好 `RenderSceneData`,自己不做 scene extraction。 +- 只处理第一个颜色附件和一个深度附件。 +- 当前 object-id 绘制仍是逐物体提交,没有更高级的 batching。 +- 是否支持某个后端,取决于 builtin object-id shader 是否提供对应 graphics variant;现有 shader loader 测试覆盖了 D3D12、OpenGL 和 Vulkan 三种 builtin variant。 + +## 真实使用位置 + +- `CameraRenderer::Render()` 在主场景渲染后、builtin post-process 前调用 `m_objectIdPass->Render(...)`。 +- `tests/Editor/test_viewport_render_flow_utils.cpp` 验证了 scene viewport 会为 `CameraRenderRequest::objectId` 正确挂接颜色/深度附件和 shader view。 +- `tests/Resources/Shader/test_shader_loader.cpp` 验证了 builtin object-id shader 的 pass tag 和多后端 variant 是否存在。 + +## 相关文档 + +- [Passes](../Passes.md) +- [BuiltinObjectIdOutlinePass](../BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md) +- [CameraRenderer](../../CameraRenderer/CameraRenderer.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinPostProcessPassPlan/BuiltinPostProcessPassPlan.md b/docs/api/XCEngine/Rendering/Passes/BuiltinPostProcessPassPlan/BuiltinPostProcessPassPlan.md new file mode 100644 index 00000000..5a37e7b2 --- /dev/null +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinPostProcessPassPlan/BuiltinPostProcessPassPlan.md @@ -0,0 +1,45 @@ +# BuiltinPostProcessPassPlan + +**命名空间**: `XCEngine::Rendering::Passes` + +**类型**: `struct` + +**头文件**: `XCEngine/Rendering/Passes/BuiltinPostProcessPassPlan.h` + +**描述**: 根据 grid、selection 和 debug mask 请求,决定 builtin post-process 应执行哪些步骤以及顺序。 + +## 概述 + +`BuiltinPostProcessPassPlan` 本身只是一个轻量数据块: + +- `valid` 表示当前输入组合是否能生成可执行序列。 +- `steps` 保存真正要执行的 `BuiltinPostProcessPassStep` 顺序。 + +真正的规则写在同一个头文件里的 `BuildBuiltinPostProcessPassPlan()` 内联函数里,`BuiltinPostProcessPassSequenceBuilder` 只是把这个计划翻译成 `RenderPassSequence`。 + +## 当前步骤规则 + +| 输入组合 | 当前 steps | +|---------|-----------| +| `hasGridOverlay && hasSelection && hasObjectIdShaderView` | `ColorToRenderTarget -> InfiniteGrid -> SelectionOutline -> ColorToShaderResource` | +| `hasGridOverlay` | `ColorToRenderTarget -> InfiniteGrid -> ColorToShaderResource` | +| `hasSelection && hasObjectIdShaderView` | `ColorToRenderTarget -> SelectionOutline -> ColorToShaderResource` | +| `debugSelectionMask && hasSelection && hasObjectIdShaderView` | `ColorToRenderTarget -> SelectionMaskDebug -> ColorToShaderResource` | + +## 当前实现边界 + +- 没有任何 builtin 效果请求时,计划为无效。 +- 只有 selection 但没有 object-id shader view 时,计划也会无效。 +- debug mask 模式是排他路径,不会和正常 outline 混用。 +- 当前一个比较明显的实现特征是:如果 `debugSelectionMask == true` 且只有 grid,没有 selection,计划虽然返回有效,但只会做颜色状态切换,不会真正绘制网格。 + +## 真实使用位置 + +- `BuiltinPostProcessPassSequenceBuilder::Build()` 先构造这个 plan,再逐步转换为 lambda render pass。 +- `tests/Rendering/unit/test_builtin_post_process_pass_sequence_builder.cpp` 覆盖了 plan 最终影响到的主要输入组合。 + +## 相关文档 + +- [BuiltinPostProcessPassSequenceBuilder](../BuiltinPostProcessPassSequenceBuilder/BuiltinPostProcessPassSequenceBuilder.md) +- [BuiltinInfiniteGridPass](../BuiltinInfiniteGridPass/BuiltinInfiniteGridPass.md) +- [BuiltinObjectIdOutlinePass](../BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinPostProcessPassSequenceBuilder/BuiltinPostProcessPassSequenceBuilder.md b/docs/api/XCEngine/Rendering/Passes/BuiltinPostProcessPassSequenceBuilder/BuiltinPostProcessPassSequenceBuilder.md new file mode 100644 index 00000000..f5bbd01b --- /dev/null +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinPostProcessPassSequenceBuilder/BuiltinPostProcessPassSequenceBuilder.md @@ -0,0 +1,57 @@ +# BuiltinPostProcessPassSequenceBuilder + +**命名空间**: `XCEngine::Rendering::Passes` + +**类型**: `class` + +**头文件**: `XCEngine/Rendering/Passes/BuiltinPostProcessPassSequenceBuilder.h` + +**描述**: 把 builtin grid、selection outline 和 debug mask 请求翻译成一条可执行的 `RenderPassSequence`。 + +## 概述 + +`BuiltinPostProcessPassSequenceBuilder` 处在一个很实用的中间层位置: + +- 上游给它的是一份数据请求:grid 要不要画、有没有 object-id 纹理、当前选中了哪些对象、是否进入 debug mask。 +- 下游 `CameraRenderer` 需要的是一条真正可执行的 `RenderPassSequence`。 + +builder 的作用,就是把“想要什么效果”转成“要按什么顺序执行哪些 pass”。 + +## 当前实现流程 + +1. 先检查当前是否存在 selection,但缺少 object-id shader view;如果是,会在结果里把 `missingObjectIdTextureViewForSelection` 置为 `true`。 +2. 调用 [BuiltinPostProcessPassPlan](../BuiltinPostProcessPassPlan/BuiltinPostProcessPassPlan.md) 决定步骤顺序。 +3. 按步骤向 `RenderPassSequence` 追加 lambda pass: + - `ColorToRenderTarget` + - `InfiniteGrid` + - `SelectionOutline` + - `ColorToShaderResource` + - `SelectionMaskDebug` +4. 这些 lambda pass 会复用 builder 内部持有的 [BuiltinInfiniteGridPass](../BuiltinInfiniteGridPass/BuiltinInfiniteGridPass.md) 和 [BuiltinObjectIdOutlinePass](../BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md)。 + +## 关键行为 + +- `Build()` 不直接执行任何 GPU 命令,只是组装序列。 +- `Shutdown()` 会显式关闭内部复用的 grid/outline pass 资源。 +- debug mask 路径会先把主颜色目标清成黑色,再调用 outline pass 的掩码模式。 +- 无论是 grid 还是 outline,builder 都会在前后插入颜色目标到 `RenderTarget` / `ShaderResource` 的状态切换 pass。 + +## 当前实现边界 + +- builder 只处理 builtin post-process,不处理主场景渲染、object-id 预通道或 overlay pass。 +- sequence 里的每一步当前都是 lambda render pass,没有独立类型名和更细的生命周期控制。 +- 如果 selection 请求缺少 object-id shader view,builder 只负责报告问题;最终是否退化成 grid-only 或直接失败,取决于输入组合和 plan 结果。 + +## 真实使用位置 + +- `CameraRenderer::Render()` 在 `request.builtinPostProcess.IsRequested()` 为真时创建临时 `RenderPassSequence`,然后调用这里的 `Build()`。 +- `tests/Rendering/unit/test_builtin_post_process_pass_sequence_builder.cpp` 是当前最直接的行为覆盖来源。 +- `tests/Editor/test_viewport_render_flow_utils.cpp` 验证了编辑器 scene viewport 如何构造能被这里消费的 builtin post-process 请求。 + +## 相关文档 + +- [Passes](../Passes.md) +- [BuiltinPostProcessPassPlan](../BuiltinPostProcessPassPlan/BuiltinPostProcessPassPlan.md) +- [BuiltinInfiniteGridPass](../BuiltinInfiniteGridPass/BuiltinInfiniteGridPass.md) +- [BuiltinObjectIdOutlinePass](../BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md) +- [CameraRenderer](../../CameraRenderer/CameraRenderer.md) diff --git a/docs/api/XCEngine/Rendering/Passes/ObjectIdOutlineStyle/ObjectIdOutlineStyle.md b/docs/api/XCEngine/Rendering/Passes/ObjectIdOutlineStyle/ObjectIdOutlineStyle.md new file mode 100644 index 00000000..56ca53f6 --- /dev/null +++ b/docs/api/XCEngine/Rendering/Passes/ObjectIdOutlineStyle/ObjectIdOutlineStyle.md @@ -0,0 +1,56 @@ +# ObjectIdOutlineStyle + +**命名空间**: `XCEngine::Rendering::Passes` + +**类型**: `struct` + +**头文件**: `XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h` + +**描述**: 描述 builtin object-id 轮廓 pass 的可调参数,包括颜色、像素宽度和调试 mask 模式。 + +## 概览 + +`ObjectIdOutlineStyle` 是 [BuiltinObjectIdOutlinePass](../BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md) 的轻量配置结构。它不拥有 GPU 资源,也不参与生命周期管理,只负责把“这次要画什么样的轮廓”显式传给 pass。 + +当前它也被 [CameraRenderRequest](../../CameraRenderRequest/CameraRenderRequest.md) 的 `builtinPostProcess.outlineStyle` 直接持有,因此调用方通常在构建渲染请求时就把选中反馈样式一并确定下来。 + +## 默认值 + +按头文件默认成员初始化,当前默认样式是: + +| 字段 | 默认值 | 说明 | +|------|------|------| +| `outlineColor` | `Math::Color(1.0f, 0.4f, 0.0f, 1.0f)` | 橙色,不透明。 | +| `outlineWidthPixels` | `2.0f` | 轮廓宽度,单位是像素。 | +| `debugSelectionMask` | `false` | 默认输出正常轮廓,而不是调试 mask。 | + +编辑器 scene viewport 当前也沿用了这组默认值。 + +## 字段语义 + +### `outlineColor` + +- 直接写进 outline 常量缓冲。 +- 正常模式下会参与 alpha 混合,最终叠加到主颜色目标。 + +### `outlineWidthPixels` + +- 传给 shader,用于决定要在 object-id 纹理周围搜索多大的邻域。 +- 当前 builtin shader 只枚举 `[-2, 2]` 范围内的像素偏移,因此即使把这个值设得更大,实际可见宽度也不会继续明显增加。 + +### `debugSelectionMask` + +- 为 `true` 时,pass 不再输出橙色轮廓,而是把“当前像素是否属于被选中对象”可视化为白底/黑底 mask。 +- 这个模式通常配合 `BuiltinPostProcessPassSequenceBuilder::AddSelectionMaskDebugPass()` 使用;builder 会先清空目标,再调用 outline pass 进行覆盖。 + +## 当前实现边界 + +- 当前样式只覆盖颜色、宽度和调试开关,没有羽化曲线、内描边、虚线、深度遮挡等更高阶参数。 +- 匹配选中对象依赖 object-id 颜色编码,而不是额外的 stencil 或 instance mask。 +- 样式对象本身不做合法性校验,调用方仍需对宽度和颜色取值负责。 + +## 相关文档 + +- [BuiltinObjectIdOutlinePass](../BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md) +- [CameraRenderRequest](../../CameraRenderRequest/CameraRenderRequest.md) +- [Passes](../Passes.md) diff --git a/docs/api/XCEngine/Rendering/Passes/Passes.md b/docs/api/XCEngine/Rendering/Passes/Passes.md new file mode 100644 index 00000000..3cd109cc --- /dev/null +++ b/docs/api/XCEngine/Rendering/Passes/Passes.md @@ -0,0 +1,61 @@ +# Passes + +**命名空间**: `XCEngine::Rendering::Passes` + +**类型**: `submodule` + +**描述**: 承载 builtin object-id、outline、infinite grid 等补充型渲染 pass,以及把这些 pass 串成后处理序列的辅助类型。 + +## 概述 + +`Rendering::Passes` 不是用来替代 [RenderPipeline](../RenderPipeline/RenderPipeline.md) 的第二套主渲染框架,而是承载“主场景已经画完之后,还需要补一层额外结果”的 builtin pass: + +- `BuiltinObjectIdPass` 把可见物体编码成 object-id 颜色,写到辅助渲染目标,供拾取和选中反馈使用。 +- [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md) 读取 object-id 纹理和选中对象列表,把轮廓或调试 mask 叠加回场景颜色。 +- `BuiltinInfiniteGridPass` 给 Scene View 一类编辑器视口叠加无限网格。 +- `BuiltinPostProcessPassSequenceBuilder` 与 `BuiltinPostProcessPassPlan` 负责把这些 pass 排成一个可执行的 `RenderPassSequence`,并补上前后状态切换。 + +这层设计让 [CameraRenderer](../CameraRenderer/CameraRenderer.md) 仍然只关心“先画主场景,再按请求插入补充 pass”,而不必直接知道每个 editor-only 效果的 shader、descriptor 和执行顺序细节。 + +## 典型链路 + +当前 object-id 轮廓相关链路大致是: + +1. [CameraRenderer](../CameraRenderer/CameraRenderer.md) 先执行主 `RenderPipeline`。 +2. 如果 [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md) 里请求了 `objectId.surface`,则额外执行 `BuiltinObjectIdPass`,把每个可见物体的 `GameObject` ID 编码进辅助纹理。 +3. 如果 `builtinPostProcess` 请求了网格、选中轮廓或调试 mask,`BuiltinPostProcessPassSequenceBuilder` 会构建一个后处理 pass 序列。 +4. [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md) 在这个序列里读取 object-id SRV,并把结果写回主颜色目标。 + +编辑器场景视口当前正是沿着这条链路工作:`ViewportHostRenderFlowUtils.h` 先组装 `BuiltinPostProcessRequest`,再由 `CameraRenderer::Render()` 真正提交。 + +## 当前公开概念 + +| 类型 / 头文件 | 角色 | +|------|------| +| `BuiltinObjectIdPass` | 生成 object-id 颜色缓冲,给拾取与 outline 作为输入。 | +| [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md) | 读取 object-id 纹理,在主颜色目标上合成选中轮廓或调试 mask。 | +| [ObjectIdOutlineStyle](ObjectIdOutlineStyle/ObjectIdOutlineStyle.md) | 轮廓颜色、像素宽度、调试模式的配置载体。 | +| `BuiltinInfiniteGridPass` | Scene View 网格覆盖层。 | +| `BuiltinPostProcessPassSequenceBuilder` | 按请求条件拼出 grid / outline / debug mask 所需的 pass 序列。 | +| `BuiltinPostProcessPassPlan` | 一个纯规划 helper,决定后处理步骤顺序与回退策略。 | + +## 测试与真实调用点 + +- `tests/Rendering/unit/test_builtin_post_process_pass_plan.cpp` 固定了 outline、debug mask 和 grid 的排序规则。 +- `tests/Rendering/unit/test_builtin_post_process_pass_sequence_builder.cpp` 验证了 object-id SRV 缺失时的回退和报错行为。 +- `tests/Rendering/unit/test_camera_scene_renderer.cpp` 验证了 `CameraRenderer` 中 object-id pass 与 builtin post-process 的接入时机。 +- `tests/Editor/test_viewport_render_flow_utils.cpp` 验证了 scene viewport 如何组装 `BuiltinPostProcessRequest`,以及 object-id 目标和 SRV 缺失时的降级逻辑。 + +## 当前实现边界 + +- 这一层目前是 builtin、轻量、偏 editor/工具链导向的 pass 集合,还不是通用 render graph。 +- object-id 相关流程依赖单独的辅助 render target / shader resource view,而不是直接从主颜色结果反推。 +- 具体 pass 的资源状态切换大多由外层 builder 或调用方保证,单个 pass 本身往往只做最小提交逻辑。 + +## 相关文档 + +- [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md) +- [ObjectIdOutlineStyle](ObjectIdOutlineStyle/ObjectIdOutlineStyle.md) +- [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md) +- [CameraRenderer](../CameraRenderer/CameraRenderer.md) +- [当前模块](../Rendering.md) diff --git a/docs/api/XCEngine/Rendering/RenderMaterialUtility/ApplyMaterialRenderState.md b/docs/api/XCEngine/Rendering/RenderMaterialUtility/ApplyMaterialRenderState.md index 77fc0867..0cf0e3ad 100644 --- a/docs/api/XCEngine/Rendering/RenderMaterialUtility/ApplyMaterialRenderState.md +++ b/docs/api/XCEngine/Rendering/RenderMaterialUtility/ApplyMaterialRenderState.md @@ -16,11 +16,11 @@ void ApplyMaterialRenderState( ## 作用 -把材质对应的栅格化、混合和深度状态一次性写入 `GraphicsPipelineDesc`。 +把材质的栅格化、混合和深度状态一次性写入 `GraphicsPipelineDesc`。 ## 当前实现行为 -当前实现非常直接: +当前实现本身不加额外策略,只是统一调用三组状态构建函数: ```cpp pipelineDesc.rasterizerState = BuildRasterizerState(material); @@ -28,19 +28,16 @@ pipelineDesc.blendState = BuildBlendState(material); pipelineDesc.depthStencilState = BuildDepthStencilState(material); ``` -也就是说,这个函数本身不加任何额外策略,只是统一调用三组状态构建函数。 +## 关键语义 -## 参数 - -| 参数 | 说明 | -|------|------| -| `material` | 材质,可为空。 | -| `pipelineDesc` | 待写入的图形管线描述。 | +- 这里不会处理 render queue,也不会决定材质属于哪个 builtin pass。 +- 它只关心 `MaterialRenderState` 如何影响最终图形管线状态。 +- 这些 `MaterialRenderState` 字段通常来自 `MaterialLoader` 解析的 `renderState` JSON / artifact 数据。 ## 适用场景 -- 在 builtin forward pipeline 组装 draw call 或 PSO 描述时,把材质 render state 映射成后端无关结构。 -- 在未来要做状态缓存时,也适合作为统一的预处理入口。 +- builtin forward pipeline 组装 draw call 或 PSO 描述时 +- 需要用统一规则把材质状态映射到后端无关 RHI 结构时 ## 相关文档 diff --git a/docs/api/XCEngine/Rendering/RenderMaterialUtility/MatchesBuiltinPass.md b/docs/api/XCEngine/Rendering/RenderMaterialUtility/MatchesBuiltinPass.md index 064740f4..1983dbf4 100644 --- a/docs/api/XCEngine/Rendering/RenderMaterialUtility/MatchesBuiltinPass.md +++ b/docs/api/XCEngine/Rendering/RenderMaterialUtility/MatchesBuiltinPass.md @@ -14,26 +14,46 @@ bool MatchesBuiltinPass(const Resources::Material* material, BuiltinMaterialPass ## 作用 -判断材质是否匹配当前内建渲染通道。 +判断一份材质是否应当进入某个 builtin pass。 ## 当前实现行为 -当前只对 `BuiltinMaterialPass::Forward` 做了明确逻辑: +当前判断顺序是: -- 如果 `material == nullptr`,直接返回 `true`。 -- 先检查 `material->GetShaderPass()`。 -- 再检查 `material->GetTag("LightMode")`。 -- 这两个字段只要非空且不属于 `forward / forwardbase / forwardlit / forwardonly`,就返回 `false`。 -- 两边都通过时返回 `true`。 +1. 如果 `material == nullptr`,只有 `pass == BuiltinMaterialPass::ForwardLit` 时返回 `true`。 +2. 读取材质自身的 `GetShaderPass()` 和 `GetTag("LightMode")`。 +3. 只要材质自己写了显式 pass 元数据,就要求这些元数据都匹配目标 builtin pass。 +4. 如果材质自己没写显式元数据,再检查材质引用的 shader: + - 逐个遍历 shader pass + - 只要某个 shader pass 的名称或 `LightMode` 能匹配目标 builtin pass,就返回 `true` + - 如果 shader 里存在显式 builtin metadata,但没有任何一个匹配,则返回 `false` +5. 如果材质和 shader 都没有显式 builtin metadata,则把它隐式视为 `ForwardLit`。 -## 设计含义 +## 当前识别的 builtin pass 名称 -这是一套偏宽松的前向通道匹配策略。它允许: +按 `MatchesBuiltinPassName()` 当前规则: -- 完全没写 pass 元数据的简单材质先跑起来。 -- 使用不同命名习惯但语义接近的前向 pass 被统一接纳。 +- `ForwardLit` / `Forward`:接受空字符串、`forward`、`forwardbase`、`forwardlit`、`forwardonly` +- `Unlit`:接受 `unlit`、`forwardunlit`、`srpdefaultunlit` +- `DepthOnly`:接受 `depthonly`、`depth` +- `ShadowCaster`:接受 `shadowcaster`、`shadow` +- `ObjectId`:接受 `objectid`、`editorobjectid` -代价是当前还谈不上完整的多 pass 调度系统,更接近“builtin forward pipeline 的白名单过滤器”。 +所有比较都会先做 `Trim().ToLower()` 归一化。 + +## 真实语义 + +- 材质级元数据优先级高于 shader 级元数据。 +- shader 里如果显式声明了其它 builtin pass,就会关闭“隐式 ForwardLit 回退”。 +- 这使得 object-id、unlit、depth-only 一类材质可以通过 `shaderPass` 或 `LightMode` 明确进入对应路径,而不是被默认前向通道误收。 + +## 测试覆盖 + +`tests/Rendering/unit/test_render_scene_extractor.cpp` 当前验证了: + +- `ForwardLit`、`Unlit`、`DepthOnly`、`ObjectId` 的匹配结果 +- shader pass 名称也能驱动 builtin pass 匹配 +- 显式 shader metadata 会关闭隐式 forward fallback ## 相关文档 diff --git a/docs/api/XCEngine/Rendering/RenderMaterialUtility/RenderMaterialUtility.md b/docs/api/XCEngine/Rendering/RenderMaterialUtility/RenderMaterialUtility.md index 4c64c57d..7ea728b5 100644 --- a/docs/api/XCEngine/Rendering/RenderMaterialUtility/RenderMaterialUtility.md +++ b/docs/api/XCEngine/Rendering/RenderMaterialUtility/RenderMaterialUtility.md @@ -6,71 +6,81 @@ **头文件**: `XCEngine/Rendering/RenderMaterialUtility.h` -**描述**: 把 `Material` 的渲染语义翻译成当前渲染层和 RHI 能直接消费的选择规则与状态描述。 +**描述**: 把 `Material` 的 pass 元数据、render queue 和 render state 解释成渲染提取与图形管线可直接使用的规则。 ## 概览 -`RenderMaterialUtility.h` 不是一个运行时对象,而是一组内联工具函数与辅助枚举。它解决的是“材质资源层”和“实际绘制层”之间的翻译问题: +`RenderMaterialUtility.h` 不是运行时对象,而是一组内联规则函数。它当前承担三类工作: -- 先决定这次绘制到底用哪份 `Material`。 -- 再判断这份材质是否属于当前内建 pass。 -- 然后把材质里的 render state 翻译成 RHI 描述结构。 +1. 解析“这次绘制真正要用哪份材质”。 +2. 判断该材质是否匹配某个 builtin pass。 +3. 把材质里的 `MaterialRenderState` 翻译成 `RHI::GraphicsPipelineDesc` 需要的状态结构。 -这类 utility header 在商业引擎里很常见,因为材质解析、render queue 判定、RHI 状态映射本质上都是无状态规则,不值得再包成有生命周期的服务对象。 +这组函数位于 [RenderSceneExtractor](../RenderSceneExtractor/RenderSceneExtractor.md)、builtin forward pipeline 和 object-id 相关 pass 之间,是当前材质语义进入渲染路径的关键翻译层。 -## 公开概念 +## BuiltinMaterialPass -### BuiltinMaterialPass - -当前只定义了一个内建 pass: +当前公开的 builtin pass 枚举包括: | 枚举值 | 说明 | |------|------| -| `BuiltinMaterialPass::Forward` | 表示默认前向绘制通道。 | +| `ForwardLit` | 默认前向绘制路径。 | +| `Unlit` | 非受光绘制路径。 | +| `DepthOnly` | 深度预写 / depth-only 类通道。 | +| `ShadowCaster` | 阴影投射类通道。 | +| `ObjectId` | object-id 输出通道。 | +| `Forward` | `ForwardLit` 的别名。 | -### 当前前向 pass 识别规则 +## 当前 pass 匹配策略 -按 `MatchesBuiltinPass()` 当前实现,以下值会被视为前向 pass: +[MatchesBuiltinPass](MatchesBuiltinPass.md) 的判断顺序是: -- 空字符串 -- `forward` -- `forwardbase` -- `forwardlit` -- `forwardonly` +1. 先看材质自身的 `shaderPass` 和 `LightMode` tag。 +2. 如果材质没写显式 pass 元数据,再看材质引用 shader 里的 pass 名称和 `LightMode` tag。 +3. 如果材质和 shader 都没写显式 builtin metadata,则把它隐式视为 `ForwardLit`。 -比较前会先做 `Trim().ToLower()` 归一化,并同时检查 `GetShaderPass()` 与 `GetTag("LightMode")`。 +因此 `RenderMaterialUtility` 实际上把 [MaterialLoader](../../Resources/Material/MaterialLoader/MaterialLoader.md) 解析出来的: -## 设计要点 +- `shaderPass` +- `tags.LightMode` +- `renderQueue` +- `renderState` -- 材质选择逻辑单独抽出来,可以让 `RenderSceneExtractor` 和具体管线共用同一套规则。 -- render queue 和透明度判定放在这里,可以保证排序语义不散落在多个调用点。 -- RHI 状态构建函数把资源层枚举翻译成后端无关描述,这比直接在管线里读 `Material` 细节更干净。 +全部接进了渲染路径。 + +## render state 映射 + +`BuildRasterizerState()`、`BuildBlendState()` 和 `BuildDepthStencilState()` 会把 `MaterialRenderState` 映射成 RHI 状态,再由 [ApplyMaterialRenderState](ApplyMaterialRenderState.md) 一次性写入 `GraphicsPipelineDesc`。 + +当前影响渲染结果的核心状态包括: + +- 剔除模式 +- 混合开关与颜色 / alpha 混合因子 +- 颜色写掩码 +- 深度测试 / 深度写入 / 深度比较函数 ## 当前实现边界 -- 只覆盖当前引擎已有的 `MaterialRenderState` 字段,不负责 shader variant、keyword 或多 pass 编排。 -- 默认值是“可用但保守”的最小实现,例如栅格化默认 `CullMode::None`、深度默认开启且比较函数为 `Less`。 -- `MatchesBuiltinPass(nullptr, ...)` 当前返回 `true`,这意味着“没有材质”在 pass 过滤阶段不会被拦掉。 +- 它只处理 pass 归类、render queue 和固定渲染状态,不负责 shader variant、keyword 或多 pass 编排。 +- 没有显式元数据的材质会默认进入 `ForwardLit`,这是一种“先让基础路径可用”的保守回退。 +- `MatchesBuiltinPass(nullptr, pass)` 只有在 `pass == ForwardLit` 时才返回 `true`,而不是对所有 pass 都放行。 ## 公开函数 | 函数 | 说明 | |------|------| -| [ResolveMaterial](ResolveMaterial.md) | 按当前约定解析可见物体实际使用的材质。 | +| [ResolveMaterial](ResolveMaterial.md) | 解析当前可见物体最终应使用的材质。 | | [ResolveMaterialRenderQueue](ResolveMaterialRenderQueue.md) | 读取材质 render queue,空材质时回退到 `Geometry`。 | -| [IsTransparentRenderQueue](IsTransparentRenderQueue.md) | 判断一个 queue 是否属于透明区间。 | -| [MatchesBuiltinPass](MatchesBuiltinPass.md) | 判断材质是否匹配当前内建 pass。 | -| [BuildRasterizerState](BuildRasterizerState.md) | 从材质构建栅格化状态。 | -| [BuildBlendState](BuildBlendState.md) | 从材质构建混合状态。 | -| [BuildDepthStencilState](BuildDepthStencilState.md) | 从材质构建深度模板状态。 | -| [ApplyMaterialRenderState](ApplyMaterialRenderState.md) | 一次性把材质状态写入 `GraphicsPipelineDesc`。 | - -## 其他辅助函数 - -头文件里还提供了 `ToRHICullMode`、`ToRHIComparisonFunc`、`ToRHIBlendFactor`、`ToRHIBlendOp`、`MaterialRenderStateHash` 等内联辅助工具。它们主要服务于状态映射与缓存键构建,当前更适合被视为实现支撑件,而不是高层调用入口。 +| [IsTransparentRenderQueue](IsTransparentRenderQueue.md) | 判断 queue 是否处于透明区间。 | +| [MatchesBuiltinPass](MatchesBuiltinPass.md) | 判断材质是否匹配某个 builtin pass。 | +| [BuildRasterizerState](BuildRasterizerState.md) | 构建栅格化状态。 | +| [BuildBlendState](BuildBlendState.md) | 构建混合状态。 | +| [BuildDepthStencilState](BuildDepthStencilState.md) | 构建深度状态。 | +| [ApplyMaterialRenderState](ApplyMaterialRenderState.md) | 一次性写入图形管线状态。 | ## 相关文档 +- [CameraRenderer](../CameraRenderer/CameraRenderer.md) - [RenderSceneExtractor](../RenderSceneExtractor/RenderSceneExtractor.md) - [VisibleRenderObject](../VisibleRenderObject/VisibleRenderObject.md) - [BuiltinForwardPipeline](../Pipelines/BuiltinForwardPipeline/BuiltinForwardPipeline.md) diff --git a/docs/api/XCEngine/Rendering/RenderMaterialUtility/ResolveMaterial.md b/docs/api/XCEngine/Rendering/RenderMaterialUtility/ResolveMaterial.md index 00f1f772..702cbb84 100644 --- a/docs/api/XCEngine/Rendering/RenderMaterialUtility/ResolveMaterial.md +++ b/docs/api/XCEngine/Rendering/RenderMaterialUtility/ResolveMaterial.md @@ -19,39 +19,42 @@ const Resources::Material* ResolveMaterial(const VisibleRenderItem& visibleItem) ## 作用 -根据 `MeshRendererComponent`、`Mesh` 和 section/material 索引,解析本次绘制真正应当使用的材质。 +按当前实例覆盖优先、资源默认值兜底的规则,解析这次绘制真正应使用的材质。 ## 当前实现行为 -三参数重载当前按以下顺序回退: +### 三参数重载 -1. `meshRenderer` 的 `materialIndex` 槽位。 -2. `mesh` 自带材质数组的 `materialIndex` 槽位。 -3. `meshRenderer` 的第 0 个材质。 -4. `mesh` 的第 0 个材质。 -5. 全部都没有则返回 `nullptr`。 +当前按以下顺序回退: -`VisibleRenderItem` 重载会先看 `visibleItem.material`;只有它为空时,才走上面的回退链。 +1. `meshRenderer` 的 `materialIndex` 槽位 +2. `mesh` 自带材质数组的 `materialIndex` 槽位 +3. `meshRenderer` 的第 `0` 个材质 +4. `mesh` 的第 `0` 个材质 +5. 都没有时返回 `nullptr` + +### `VisibleRenderItem` 重载 + +- 如果 `visibleItem.material` 已经非空,直接返回它。 +- 否则再回退到上面的三参数逻辑。 ## 设计含义 -这是一种偏商业引擎的“组件覆盖优先,资源默认值兜底”策略: +这条规则体现的是: -- `MeshRendererComponent` 代表场景实例级覆盖。 -- `Mesh` 自带材质更像资源默认值。 -- `VisibleRenderItem.material` 则是提取阶段已经确认好的最终覆盖结果。 +- `visibleItem.material` 是提取阶段已经确认好的最终覆盖结果 +- `MeshRendererComponent` 材质代表场景实例级覆盖 +- `Mesh` 自带材质是资源默认值 + +这也解释了为什么 `RenderSceneExtractor` 和具体管线都可以共享同一套材质解析逻辑。 ## 返回值 -- 返回解析到的 `Material*`。 -- 如果所有来源都没有材质,返回 `nullptr`。 - -## 注意事项 - -- 返回空材质不一定会导致后续流程中断;当前很多工具函数会对空材质提供默认行为。 -- 这套规则是当前实现契约,未来如果引擎引入 submesh override stack,解析优先级可能扩展。 +- 返回解析到的 `Material*` +- 所有来源都没有材质时返回 `nullptr` ## 相关文档 -- [RenderMaterialUtility](RenderMaterialUtility.md) +- [ResolveMaterialRenderQueue](ResolveMaterialRenderQueue.md) - [MatchesBuiltinPass](MatchesBuiltinPass.md) +- [RenderMaterialUtility](RenderMaterialUtility.md) diff --git a/docs/api/XCEngine/Rendering/Rendering.md b/docs/api/XCEngine/Rendering/Rendering.md index 12164186..4cecee87 100644 --- a/docs/api/XCEngine/Rendering/Rendering.md +++ b/docs/api/XCEngine/Rendering/Rendering.md @@ -4,40 +4,62 @@ **类型**: `module` -**描述**: 提供场景提取、相机渲染请求、渲染目标表面、渲染管线抽象和内建前向渲染实现。 +**描述**: 提供场景提取、相机渲染请求、主管线执行、object-id 辅助输出和 builtin post-process 叠加的当前渲染主链路。 ## 概览 -当前 `XCEngine::Rendering` 可以按一条比较清楚的链路理解: +当前 `XCEngine::Rendering` 可以按一条更完整的链路理解: -1. [SceneRenderer](SceneRenderer/SceneRenderer.md) 负责多相机请求组织与排序。 -2. [CameraRenderRequest](CameraRenderRequest/CameraRenderRequest.md) 把单次相机渲染的输入打包成显式请求。 -3. [CameraRenderer](CameraRenderer/CameraRenderer.md) 负责执行单相机渲染。 -4. [RenderSceneExtractor](RenderSceneExtractor/RenderSceneExtractor.md) 从场景中抽取当前相机可见的数据。 -5. [RenderSurface](RenderSurface/RenderSurface.md) 描述渲染目标、附件与状态切换。 -6. [RenderPipeline](RenderPipeline/RenderPipeline.md) 定义真正执行绘制的后端无关接口。 -7. [Pipelines::BuiltinForwardPipeline](Pipelines/BuiltinForwardPipeline/BuiltinForwardPipeline.md) 是当前默认管线实现。 +1. [SceneRenderer](SceneRenderer/SceneRenderer.md) 负责组织和排序多个相机请求。 +2. [CameraRenderRequest](CameraRenderRequest/CameraRenderRequest.md) 把单次提交的主表面、object-id 输出和 builtin 后处理需求打包起来。 +3. [CameraRenderer](CameraRenderer/CameraRenderer.md) 执行单个请求。 +4. [RenderSceneExtractor](RenderSceneExtractor/RenderSceneExtractor.md) 依据相机和 render area 提取 `RenderSceneData`。 +5. [RenderPipeline](RenderPipeline/RenderPipeline.md) 负责主场景绘制。 +6. `ObjectIdPass` 与 [Passes](Passes/Passes.md) 子模块在主场景之后补上 object-id、grid 和 selection outline 等额外结果。 +7. [RenderMaterialUtility](RenderMaterialUtility/RenderMaterialUtility.md) 把材质元数据与 render state 翻译成这条链路能消费的规则。 -这类“请求组织层 -> 提取层 -> 管线执行层”的拆分,与商业引擎里常见的 camera rendering architecture 很接近。它的优势是未来要扩展更多相机类型、离屏渲染目标或替换整个管线时,不必把所有逻辑塞进一个大类里。 +这意味着当前渲染模块已经不只是“scene -> pipeline”两段式结构,而是包含了主场景、辅助缓冲和 builtin post-process 的完整相机提交流程。 -## 设计要点 +## 当前主流程 -- 把多相机调度和单相机执行拆开,便于排序和后续扩展。 -- 场景提取与真正绘制分层,给可见性、排序和批处理预留演进空间。 -- `RenderMaterialUtility` 统一承担材质解析、render queue 判定和 RHI 状态翻译,避免这些规则散落在多个调用点。 -- `RenderSurface` 把目标尺寸、附件和状态切换要求显式化,减少管线对具体平台窗口细节的直接依赖。 +对一台相机,当前主流程大致是: + +1. `SceneRenderer` 生成并排序 `CameraRenderRequest` +2. `CameraRenderer` 校验请求 +3. `RenderSceneExtractor` 提取可见物体与相机数据 +4. 主 `RenderPipeline` 绘制主颜色目标 +5. 如有需要,执行 object-id pass,生成辅助 object-id 纹理 +6. 执行调用方注入的 post pass、builtin post-process 和 overlay pass + +scene viewport 的无限网格和选中轮廓,就是通过这条 object-id + builtin post-process 链路接入的。 + +## 材质语义如何进入渲染路径 + +[RenderMaterialUtility](RenderMaterialUtility/RenderMaterialUtility.md) 当前负责消费材质里的: + +- `shaderPass` +- `LightMode` +- `renderQueue` +- `renderState` + +这些元数据可能来自普通材质文件,也可能来自 `MaterialLoader` 读取的 `.xcmat` artifact。它们最终会影响: + +- 某个可见物体属于哪个 builtin pass +- 不透明 / 透明排序语义 +- 剔除、混合和深度状态如何写进图形管线描述 ## 当前实现边界 -- 当前默认路径仍然是轻量级内建前向渲染,而不是完整 SRP 或延迟渲染框架。 -- `CameraRenderer` 当前只负责单相机;多相机排序仍由 `SceneRenderer` 组织。 -- `RenderSceneExtractor` 已支持基本的相机选择、可见项收集和 render queue 排序,但还没有完整视锥裁剪、遮挡裁剪和高级批处理。 -- 材质状态映射目前足够支撑基础前向绘制,但还谈不上完整商业引擎材质系统。 +- 当前默认路径仍然是内建前向渲染,而不是完整 SRP / render graph。 +- object-id 和 builtin post-process 主要服务于编辑器视口与调试链路。 +- builtin selection outline 当前依赖单独的 object-id SRV,而不是从主颜色结果反推。 +- 多相机排序由 `SceneRenderer` 处理,`CameraRenderer` 本身只执行单请求。 ## 头文件 - [CameraRenderRequest](CameraRenderRequest/CameraRenderRequest.md) - `CameraRenderRequest.h` - [CameraRenderer](CameraRenderer/CameraRenderer.md) - `CameraRenderer.h` +- [Passes](Passes/Passes.md) - `Passes/` - [RenderCameraData](RenderCameraData/RenderCameraData.md) - `RenderCameraData.h` - [RenderContext](RenderContext/RenderContext.md) - `RenderContext.h` - [RenderMaterialUtility](RenderMaterialUtility/RenderMaterialUtility.md) - `RenderMaterialUtility.h` @@ -52,11 +74,10 @@ ## 相关指南 -- [Scene Extraction And Builtin Forward Pipeline](../../_guides/Rendering/Scene-Extraction-And-Builtin-Forward-Pipeline.md) - 解释场景提取、相机请求和内建前向管线之间的当前协作关系。 +- [Scene Extraction And Builtin Forward Pipeline](../../_guides/Rendering/Scene-Extraction-And-Builtin-Forward-Pipeline.md) ## 相关文档 -- [Scene](../Scene/Scene.md) - [Components](../Components/Components.md) - [RHI](../RHI/RHI.md) - [上级目录](../XCEngine.md) diff --git a/docs/plan/API文档并行更新任务池_2026-04-02.md b/docs/plan/API文档并行更新任务池_2026-04-02.md new file mode 100644 index 00000000..da298653 --- /dev/null +++ b/docs/plan/API文档并行更新任务池_2026-04-02.md @@ -0,0 +1,117 @@ +# API 文档并行更新任务池(2026-04-02) + +## 目的 + +基于 `2026-04-02` 当前工作树,这份清单用于把 API 文档更新任务拆成可并行认领的独立块,供多个会话同时推进。 + +## 认领规则 + +- 一次只认领 `1` 个任务块,先改 `状态` 和 `认领人`。 +- 只修改自己任务块的 `写入范围`,不要跨任务顺手改别的模块页。 +- 除 `T09` 之外,其他任务不要更新 `docs/api/_meta/rebuild-status.md`,避免多人冲突。 +- 每个任务都要以源码、实现、测试、真实调用点为依据,不允许只按命名猜测行为。 +- 如果任务执行中发现需要新增 guide,统一放到 `docs/api/_guides//` 下。 + +## 首批并行推荐 + +- 优先并行启动: `T01`、`T03`、`T04`、`T05`、`T07` +- 第二批跟进: `T02`、`T06`、`T08` +- 收尾整合: `T09` + +## 任务池 + +## T01 Editor / Viewport 子模块补齐与重写 + +- 状态: `IN_PROGRESS` +- 认领人: `Codex-Viewport` +- 优先级: `P0` +- 写入范围: `docs/api/XCEngine/Editor/Viewport/**`、`docs/api/XCEngine/Editor/panels/SceneViewPanel/**`、`docs/api/XCEngine/Editor/panels/ViewportPanelContent/**` +- 主要源码依据: `editor/src/Viewport/**`、`editor/src/panels/SceneViewPanel.*`、`editor/src/panels/ViewportPanelContent.h`、`tests/editor/test_scene_viewport_camera_controller.cpp` +- 当前缺口: `Viewport` 整个 canonical 树尚未建立;以下页面当前缺失: `SceneViewportCameraController`、`SceneViewportMoveGizmo`、`SceneViewportRotateGizmo`、`SceneViewportScaleGizmo`、`SceneViewportOverlayRenderer`、`ViewportHostService`、`ViewportHostRenderFlowUtils`、`SceneViewportEditorOverlayData`、`SceneViewportOverlayBuilder`、`ViewportPanelContent` +- 完成标准: 补齐 `Viewport/Viewport.md` 与所有类型页;`SceneViewPanel` 文档重写到当前 gizmo / overlay / host flow 实现;写清楚生命周期、交互链路、渲染路径和测试覆盖 + +## T02 Editor / ScriptComponentEditor 补齐 + +- 状态: `DONE` +- 认领人: `Codex` +- 优先级: `P0` +- 写入范围: `docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditor/**`、`docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditorUtils/**`、`docs/api/XCEngine/Editor/ComponentEditors/ComponentEditors.md`、`docs/api/XCEngine/Editor/ComponentEditors/ComponentEditorRegistry/**` +- 主要源码依据: `editor/src/ComponentEditors/ScriptComponentEditor.h`、`editor/src/ComponentEditors/ScriptComponentEditorUtils.h`、`editor/src/ComponentEditors/ComponentEditorRegistry.cpp` +- 当前缺口: `ScriptComponentEditor` 与 `ScriptComponentEditorUtils` 还没有 canonical 页面;组件编辑器总览也需要纳入脚本组件编辑器 +- 完成标准: 补齐缺页;说明 Inspector 侧脚本字段绘制、字段元数据来源、与 `ScriptEngine` / `ScriptComponent` 的关系 + +## T03 Core / AssetDatabase 新建与资产数据库链路说明 + +- 状态: `IN_PROGRESS` +- 认领人: `Codex-Asset` +- 优先级: `P0` +- 写入范围: `docs/api/XCEngine/Core/Asset/AssetDatabase/**`、`docs/api/XCEngine/Core/Asset/Asset.md` +- 主要源码依据: `engine/include/XCEngine/Core/Asset/AssetDatabase.h`、相关 `.cpp` 实现、项目目录下新增的 `.meta` 与 `Library` 资产缓存变化 +- 当前缺口: `AssetDatabase` 对应的 canonical 类型页完全缺失;`Core/Asset` 模块总览需要反映新的数据库/导入缓存方向 +- 完成标准: 建立 `AssetDatabase` 页面,明确 GUID、path、meta、导入缓存、查询职责,以及它和 `ProjectPanel` / `ResourceManager` / 资源导入流程的关系 + +## T04 Rendering / Passes 子模块与 BuiltinObjectIdOutlinePass 补齐 + +- 状态: `DONE` +- 认领人: `Codex` +- 优先级: `P0` +- 写入范围: `docs/api/XCEngine/Rendering/Passes/**` +- 主要源码依据: `engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h`、`engine/src/Rendering/Passes/BuiltinObjectIdOutlinePass.cpp` +- 当前缺口: `Rendering/Passes` 目录当前没有 canonical 文档树;`BuiltinObjectIdOutlinePass` 页面缺失 +- 完成标准: 新建 `Passes/Passes.md` 和 `BuiltinObjectIdOutlinePass` 类型页;写清楚对象 ID / 轮廓高亮的输入输出、依赖资源、典型使用位置和当前限制 + +## T05 Scripting 模块内容重构 + +- 状态: `TODO` +- 认领人: `未认领` +- 优先级: `P1` +- 写入范围: `docs/api/XCEngine/Scripting/**`、`docs/api/_guides/Scripting/**` +- 主要源码依据: `engine/include/XCEngine/Scripting/IScriptRuntime.h`、`Mono/MonoScriptRuntime.h`、`NullScriptRuntime.h`、`ScriptComponent.h`、`ScriptEngine.h` 及对应 `.cpp`;`tests/scripting/**` +- 当前缺口: 结构存在,但脚本运行时、字段同步、项目脚本程序集、空运行时回退等内容需要按当前实现重写 +- 完成标准: 明确运行时抽象、Mono 后端、Null 后端、字段存储与组件生命周期;必要时补一篇项目脚本程序集 / 字段同步 guide + +## T06 Editor 运行时胶水层与面板内容更新 + +- 状态: `TODO` +- 认领人: `未认领` +- 优先级: `P1` +- 写入范围: `docs/api/XCEngine/Editor/Application/**`、`docs/api/XCEngine/Editor/Core/EventBus/**`、`docs/api/XCEngine/Editor/panels/InspectorPanel/**`、`docs/api/XCEngine/Editor/panels/ProjectPanel/**`、`docs/api/XCEngine/Editor/UI/Widgets/**`、`docs/api/XCEngine/Editor/Actions/HierarchyActionRouter/**`、`docs/api/XCEngine/Editor/Commands/EntityCommands/**` +- 主要源码依据: `editor/src/Application.*`、`editor/src/Core/EventBus.h`、`editor/src/panels/InspectorPanel.*`、`editor/src/panels/ProjectPanel.*`、`editor/src/UI/Widgets.h`、`editor/src/Actions/HierarchyActionRouter.h`、`editor/src/Commands/EntityCommands.h` +- 当前缺口: 这些页面虽然大多存在,但内容容易落后于当前交互链路;`ProjectPanel` 虽已较新,仍要根据这轮源码变化做二次核对 +- 完成标准: 把“Editor 主循环 -> EventBus -> 面板 -> Action/Command”这条链路写清楚;Inspector/Project/Hierarchy 相关页内容与当前实现严格对齐 + +## T07 Rendering 相机请求与对象 ID 渲染链路更新 + +- 状态: `DONE` +- 认领人: `Codex` +- 优先级: `P1` +- 写入范围: `docs/api/XCEngine/Rendering/CameraRenderRequest/**`、`docs/api/XCEngine/Rendering/CameraRenderer/**`、`docs/api/XCEngine/Rendering/RenderMaterialUtility/**`、`docs/api/XCEngine/Rendering/Rendering.md` +- 主要源码依据: `engine/include/XCEngine/Rendering/CameraRenderRequest.h`、`engine/src/Rendering/CameraRenderer.cpp`、`engine/src/Resources/Material/MaterialLoader.cpp`、`engine/src/Rendering/Passes/BuiltinObjectIdOutlinePass.cpp` +- 当前缺口: 文档需要反映这轮 renderer 里对象 ID、outline、camera request、材质 render state 的新关系 +- 完成标准: 写清楚 camera request 的职责边界、camera renderer 的主流程、object-id/outline 的接入点,以及材质 render state 对渲染路径的影响 + +## T08 Components / MeshFilterComponent 与资源绑定链路更新 + +- 状态: `IN_PROGRESS` +- 认领人: `Codex` +- 优先级: `P1` +- 写入范围: `docs/api/XCEngine/Components/MeshFilterComponent/**`、`docs/api/XCEngine/Components/Components.md` +- 主要源码依据: `engine/include/XCEngine/Components/MeshFilterComponent.h`、相关 `.cpp`、`tests/Resources/Mesh/test_mesh_loader.cpp`、`tests/Resources/Material/test_material_loader.cpp` +- 当前缺口: `MeshFilterComponent` 页面存在,但需要重新核对 mesh handle / path / 资源解析链路;模块总览也应补充 MeshFilter 在渲染和资产导入链路中的定位 +- 完成标准: 说明 `MeshFilterComponent` 如何保存 mesh 引用、如何与资源系统和渲染提取流程衔接,以及当前限制 + +## T09 根总览与最终审计 + +- 状态: `TODO` +- 认领人: `未认领` +- 优先级: `P2` +- 写入范围: `docs/api/XCEngine/XCEngine.md`、受影响的模块总览页、`docs/api/_meta/rebuild-status.md` +- 主要源码依据: 前面所有任务的完成结果 +- 当前缺口: 根总览和模块总览需要在前面任务落地后统一收口;审计状态文件只能由一个会话最终更新 +- 完成标准: 统一调整总览页导航;执行 `audit_api_docs.py`、覆盖校验与链接校验;写回新的 `rebuild-status.md` + +## 备注 + +- 如果只有 `2-3` 个会话,优先拿 `T01`、`T05`、`T07` +- 如果有 `4-6` 个会话,推荐并行拿 `T01`、`T03`、`T04`、`T05`、`T06`、`T07` +- `T09` 必须最后做