docs: update API docs for rendering and script editors

This commit is contained in:
2026-04-02 21:29:08 +08:00
parent f7d7d08d99
commit 00ce503762
33 changed files with 1519 additions and 262 deletions

View File

@@ -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<std::unique_ptr<IComponentEditor>>`,负责真正拥有 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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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`:至少有一个最小前提不满足。
## 相关文档

View File

@@ -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 listcamera 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<RenderPipeline>` 独占持有当前管线
- 调用方一旦把管线传给构造函数或 [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)

View File

@@ -11,29 +11,49 @@
```cpp
CameraRenderer();
explicit CameraRenderer(std::unique_ptr<RenderPipeline> pipeline);
explicit CameraRenderer(std::shared_ptr<const RenderPipelineAsset> pipelineAsset);
CameraRenderer(
std::unique_ptr<RenderPipeline> pipeline,
std::unique_ptr<ObjectIdPass> objectIdPass);
```
## 作用
创建一个执行单相机渲染`CameraRenderer`,并为它准备当前要使用的渲染管线
创建一个能够执行单相机请求`CameraRenderer`,并为它准备主管线来源与 object-id pass
## 当前实现行为
- 默认构造:直接创建 `Pipelines::BuiltinForwardPipeline`
- 注入构造:优先接管调用方传入的管线。
- 如果注入构造拿到的是空 `unique_ptr`,当前实现会回退成默认的 `BuiltinForwardPipeline`,而不是让对象进入“无管线”状态
### 默认构造
- 通过内部静态 `CreateDefaultPipelineAsset()` 取得 `Pipelines::BuiltinForwardPipelineAsset`
- 再走 pipeline asset 构造路径创建默认主管线。
- 同时默认创建 `Passes::BuiltinObjectIdPass`
### `std::unique_ptr<RenderPipeline>` 重载
- 接管调用方给出的主管线实例。
- 如果 `objectIdPass` 没显式传入,则默认补一个 `Passes::BuiltinObjectIdPass`
### `std::shared_ptr<const RenderPipelineAsset>` 重载
- 保存 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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -0,0 +1,34 @@
# CameraRenderer::SetObjectIdPass
**命名空间**: `XCEngine::Rendering`
**类型**: `method`
**头文件**: `XCEngine/Rendering/CameraRenderer.h`
## 签名
```cpp
void SetObjectIdPass(std::unique_ptr<ObjectIdPass> 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)

View File

@@ -14,33 +14,30 @@ void SetPipeline(std::unique_ptr<RenderPipeline> 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)

View File

@@ -0,0 +1,35 @@
# CameraRenderer::SetPipelineAsset
**命名空间**: `XCEngine::Rendering`
**类型**: `method`
**头文件**: `XCEngine/Rendering/CameraRenderer.h`
## 签名
```cpp
void SetPipelineAsset(std::shared_ptr<const RenderPipelineAsset> 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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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<uint64_t>& 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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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 还是 outlinebuilder 都会在前后插入颜色目标到 `RenderTarget` / `ShaderResource` 的状态切换 pass。
## 当前实现边界
- builder 只处理 builtin post-process不处理主场景渲染、object-id 预通道或 overlay pass。
- sequence 里的每一步当前都是 lambda render pass没有独立类型名和更细的生命周期控制。
- 如果 selection 请求缺少 object-id shader viewbuilder 只负责报告问题;最终是否退化成 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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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 结构时
## 相关文档

View File

@@ -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
## 相关文档

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -0,0 +1,117 @@
# API 文档并行更新任务池2026-04-02
## 目的
基于 `2026-04-02` 当前工作树,这份清单用于把 API 文档更新任务拆成可并行认领的独立块,供多个会话同时推进。
## 认领规则
- 一次只认领 `1` 个任务块,先改 `状态``认领人`
- 只修改自己任务块的 `写入范围`,不要跨任务顺手改别的模块页。
-`T09` 之外,其他任务不要更新 `docs/api/_meta/rebuild-status.md`,避免多人冲突。
- 每个任务都要以源码、实现、测试、真实调用点为依据,不允许只按命名猜测行为。
- 如果任务执行中发现需要新增 guide统一放到 `docs/api/_guides/<Module>/` 下。
## 首批并行推荐
- 优先并行启动: `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` 必须最后做