docs: sync rendering pass execution docs
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# ApplySceneViewportRenderPlan
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `function`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
void ApplySceneViewportRenderPlan(
|
||||
const ViewportRenderTargets& targets,
|
||||
SceneViewportRenderPlan& plan,
|
||||
Rendering::CameraRenderRequest& request);
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
把已经构建好的 `SceneViewportRenderPlan` 写入目标 `CameraRenderRequest`。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
当前函数会先调用:
|
||||
|
||||
```cpp
|
||||
ApplySceneViewportRenderRequestSetup(
|
||||
targets,
|
||||
&plan.postScenePasses,
|
||||
request);
|
||||
```
|
||||
|
||||
然后继续做两件事:
|
||||
|
||||
1. 如果 `plan.HasOverlayPasses()` 为真,把 `request.overlayPasses` 指向 `plan.overlayPasses`
|
||||
2. 无条件把 `plan.hasClearColorOverride` 和 `plan.clearColorOverride` 复制到 request
|
||||
|
||||
## 当前语义边界
|
||||
|
||||
- object-id surface 和 `postScenePasses` 的挂接规则,仍由 `ApplySceneViewportRenderRequestSetup(...)` 定义。
|
||||
- 该函数不负责生成 `request.surface`,也不负责构建 plan,只负责“把 plan 应用到 request”。
|
||||
- `overlayPasses` 只有在非空时才会挂到 request;空序列不会暴露出去。
|
||||
|
||||
## 测试锚点
|
||||
|
||||
- `ApplySceneViewportRenderPlanAttachesPlannedPassesAndClearState`
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportRenderPlan](SceneViewportRenderPlan.md)
|
||||
- [HasOverlayPasses](HasOverlayPasses.md)
|
||||
- [BuildSceneViewportRenderPlan](BuildSceneViewportRenderPlan.md)
|
||||
- [ViewportHostRenderFlowUtils](../ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md)
|
||||
@@ -0,0 +1,68 @@
|
||||
# BuildSceneViewportRenderPlan
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `function`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan(
|
||||
const ViewportRenderTargets& targets,
|
||||
const SceneViewportOverlayData& overlay,
|
||||
const std::vector<uint64_t>& selectedObjectIds,
|
||||
const SceneViewportOverlayFrameData& editorOverlayFrameData,
|
||||
const SceneViewportOverlayFrameData& transientOverlayFrameData,
|
||||
const SceneViewportGridPassFactory& gridPassFactory,
|
||||
const SceneViewportSelectionOutlinePassFactory& selectionOutlinePassFactory,
|
||||
const SceneViewportOverlayPassFactory& overlayPassFactory,
|
||||
bool debugSelectionMask = false);
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
根据当前 Scene View 的 overlay、选中对象和视口 render targets,构建一份可直接应用到 `CameraRenderRequest` 的 render plan。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
当前函数会按下面顺序执行:
|
||||
|
||||
1. 若 `overlay.valid == false`,直接返回默认结果。
|
||||
2. 调用 `BuildSceneViewportGridPassData(...)` 生成 grid 参数。
|
||||
3. 若 `gridPassData.valid == true` 且 `gridPassFactory` 非空,则创建 grid pass 并加入 `result.plan.postScenePasses`。
|
||||
4. 若 `selectedObjectIds` 非空,并且 `targets.objectIdShaderView` 与 `selectionOutlinePassFactory` 都可用,则创建 selection outline pass 并加入 `result.plan.postScenePasses`。
|
||||
5. 若选中了对象,但缺少 `objectIdShaderView`,且 `debugSelectionMask == false`,则写入警告文案 `Scene object id shader view is unavailable`。
|
||||
6. 把 `editorOverlayFrameData` 和 `transientOverlayFrameData` 合并成一份最终 overlay frame data。
|
||||
7. 若合成后的 frame data 含有 overlay primitive,且 `overlayPassFactory` 非空,则调用 factory 创建 overlay pass。
|
||||
8. 若 factory 返回非空 pass,则把它加入 `result.plan.overlayPasses`。
|
||||
|
||||
## 当前返回值语义
|
||||
|
||||
- `result.plan.postScenePasses`
|
||||
- 当前承载 Scene View 的 grid 和 selection outline 两类附加 pass。
|
||||
- `result.plan.overlayPasses`
|
||||
- 承载 editor overlay / transient overlay 合并后的最终叠加层。
|
||||
- `result.warningStatusText`
|
||||
- 当前主要用于提示“Scene object id shader view is unavailable”这类可降级的 Scene View 缺口。
|
||||
|
||||
## 测试锚点
|
||||
|
||||
- `BuildSceneViewportRenderPlanCollectsPostSceneAndOverlayPasses`
|
||||
- `BuildSceneViewportRenderPlanWarnsWhenSelectionOutlineCannotAccessObjectIdTexture`
|
||||
|
||||
这些测试确认了:
|
||||
|
||||
- grid、selection outline 和 overlay pass 的规划入口已经拆开
|
||||
- overlay frame data 会先合并,再交给 overlay factory
|
||||
- object-id SRV 缺失时会返回警告而不是直接崩掉整条 Scene View 组装链
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportRenderPlan](SceneViewportRenderPlan.md)
|
||||
- [SceneViewportRenderPlanBuildResult](SceneViewportRenderPlanBuildResult.md)
|
||||
- [SceneViewportGridPassFactory](SceneViewportGridPassFactory.md)
|
||||
- [SceneViewportSelectionOutlinePassFactory](SceneViewportSelectionOutlinePassFactory.md)
|
||||
- [SceneViewportOverlayPassFactory](SceneViewportOverlayPassFactory.md)
|
||||
- [ApplySceneViewportRenderPlan](ApplySceneViewportRenderPlan.md)
|
||||
@@ -0,0 +1,33 @@
|
||||
# SceneViewportRenderPlan::HasOverlayPasses
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `method`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
bool HasOverlayPasses() const;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
判断当前 render plan 是否真的持有 overlay pass。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
当前只是返回:
|
||||
|
||||
```cpp
|
||||
overlayPasses.GetPassCount() > 0
|
||||
```
|
||||
|
||||
这也是 [ApplySceneViewportRenderPlan](ApplySceneViewportRenderPlan.md) 决定是否把 `request.overlayPasses` 指向 `plan.overlayPasses` 的直接依据。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportRenderPlan](SceneViewportRenderPlan.md)
|
||||
- [HasPostScenePasses](HasPostScenePasses.md)
|
||||
- [ApplySceneViewportRenderPlan](ApplySceneViewportRenderPlan.md)
|
||||
@@ -0,0 +1,33 @@
|
||||
# SceneViewportRenderPlan::HasPostScenePasses
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `method`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
bool HasPostScenePasses() const;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
判断当前 render plan 是否真的持有 post-scene pass。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
当前只是返回:
|
||||
|
||||
```cpp
|
||||
postScenePasses.GetPassCount() > 0
|
||||
```
|
||||
|
||||
因此空的 `RenderPassSequence` 不会被视为“已配置 post-scene pass”。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportRenderPlan](SceneViewportRenderPlan.md)
|
||||
- [HasOverlayPasses](HasOverlayPasses.md)
|
||||
- [ApplySceneViewportRenderPlan](ApplySceneViewportRenderPlan.md)
|
||||
@@ -0,0 +1,29 @@
|
||||
# SceneViewportGridPassFactory
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `type alias`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
|
||||
## 定义
|
||||
|
||||
```cpp
|
||||
using SceneViewportGridPassFactory =
|
||||
std::function<std::unique_ptr<Rendering::RenderPass>(const Rendering::Passes::InfiniteGridPassData&)>;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
描述一个“根据 Scene View 相机数据创建网格 pass”的工厂回调。
|
||||
|
||||
## 当前语义
|
||||
|
||||
- 输入是一份已经由 `BuildSceneViewportGridPassData(...)` 组装好的 `InfiniteGridPassData`。
|
||||
- 输出是要追加到 `SceneViewportRenderPlan::postScenePasses` 的一个 `RenderPass`。
|
||||
- 返回 `nullptr` 时,本帧不会追加网格 pass。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportRenderPlan](SceneViewportRenderPlan.md)
|
||||
- [BuildSceneViewportRenderPlan](BuildSceneViewportRenderPlan.md)
|
||||
@@ -0,0 +1,31 @@
|
||||
# SceneViewportOverlayPassFactory
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `type alias`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
|
||||
## 定义
|
||||
|
||||
```cpp
|
||||
using SceneViewportOverlayPassFactory =
|
||||
std::function<std::unique_ptr<Rendering::RenderPass>(const SceneViewportOverlayFrameData&)>;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
描述一个“由合成后的 overlay frame data 生成 GPU overlay pass”的工厂回调。
|
||||
|
||||
## 当前语义
|
||||
|
||||
- 输入是已经合并好的 `SceneViewportOverlayFrameData`
|
||||
- 输出是要追加到 `SceneViewportRenderPlan::overlayPasses` 的一个 `RenderPass`
|
||||
- 返回 `nullptr` 时,当前帧不会追加 overlay pass
|
||||
|
||||
这让 [BuildSceneViewportRenderPlan](BuildSceneViewportRenderPlan.md) 自己不依赖具体 pass 类型,只负责决定“何时创建 overlay pass、何时跳过”。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportRenderPlan](SceneViewportRenderPlan.md)
|
||||
- [BuildSceneViewportRenderPlan](BuildSceneViewportRenderPlan.md)
|
||||
@@ -0,0 +1,69 @@
|
||||
# SceneViewportRenderPlan
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `structs + aliases + free functions`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
|
||||
**描述**: 把 Scene View 一次渲染提交所需的 post-scene pass、overlay pass 和清屏色覆盖收口成可复用的轻量 request plan。
|
||||
|
||||
## 概览
|
||||
|
||||
`SceneViewportRenderPlan.h` 处在 [ViewportHostRenderFlowUtils](../ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md) 和 [ViewportHostService](../ViewportHostService/ViewportHostService.md) 之间。
|
||||
|
||||
它解决的不是“如何渲染 Scene View”,而是“如何把这一帧 Scene View 额外挂到 `CameraRenderRequest` 上的东西组织好”:
|
||||
|
||||
- post-scene pass 序列
|
||||
- overlay pass 序列
|
||||
- 默认 clear-color override
|
||||
|
||||
这样 `ViewportHostService` 的 Scene View 路径可以更像“构建 request -> 执行 request”,而不是把所有额外 pass 拼装细节都塞在一个方法里。
|
||||
|
||||
## 公开类型与函数
|
||||
|
||||
| 成员 | 说明 |
|
||||
|------|------|
|
||||
| [SceneViewportGridPassFactory](SceneViewportGridPassFactory.md) | 根据 `InfiniteGridPassData` 生成 grid `RenderPass` 的工厂回调。 |
|
||||
| [SceneViewportSelectionOutlinePassFactory](SceneViewportSelectionOutlinePassFactory.md) | 根据 object-id SRV、选中对象和样式生成 selection outline `RenderPass` 的工厂回调。 |
|
||||
| [SceneViewportOverlayPassFactory](SceneViewportOverlayPassFactory.md) | 根据合成后的 overlay frame data 生成 GPU overlay pass 的工厂回调。 |
|
||||
| [SceneViewportRenderPlanBuildResult](SceneViewportRenderPlanBuildResult.md) | 返回 render plan 与可选警告文案。 |
|
||||
| [HasPostScenePasses](HasPostScenePasses.md) | 判断当前计划是否包含 post-scene pass。 |
|
||||
| [HasOverlayPasses](HasOverlayPasses.md) | 判断当前计划是否包含 overlay pass。 |
|
||||
| [BuildSceneViewportRenderPlan](BuildSceneViewportRenderPlan.md) | 根据 targets、overlay 和当前帧 overlay 数据构建 render plan。 |
|
||||
| [ApplySceneViewportRenderPlan](ApplySceneViewportRenderPlan.md) | 把 render plan 写回 `CameraRenderRequest`。 |
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
按 `SceneViewportRenderPlan.h` 和 `tests/editor/test_viewport_render_flow_utils.cpp` 的当前实现:
|
||||
|
||||
- `BuildSceneViewportRenderPlan(...)` 在 `overlay.valid == false` 时直接返回默认结果。
|
||||
- grid pass 由 `BuildSceneViewportGridPassData(...)` + `SceneViewportGridPassFactory` 决定是否加入 `plan.postScenePasses`。
|
||||
- selection outline pass 由 `targets.objectIdShaderView`、`selectedObjectIds` 和 `SceneViewportSelectionOutlinePassFactory` 决定是否加入 `plan.postScenePasses`。
|
||||
- 如果选中了对象但缺少 `objectIdShaderView`,且不是 debug mask 模式,则会返回警告文案 `Scene object id shader view is unavailable`。
|
||||
- overlay pass 的输入不是单独一份 editor overlay,而是 `editorOverlayFrameData + transientOverlayFrameData` 合并后的结果。
|
||||
- 只有合成后的 overlay frame data 确实含有 primitive,且 factory 非空、factory 返回值也非空时,才会向 `overlayPasses` 里追加 pass。
|
||||
- `ApplySceneViewportRenderPlan(...)` 会继续复用 `ApplySceneViewportRenderRequestSetup(...)` 处理 object-id surface 与 `postScenePasses`,然后再补上 `overlayPasses` 和 clear-color override。
|
||||
|
||||
## 测试锚点
|
||||
|
||||
当前至少有两条直接测试覆盖:
|
||||
|
||||
- `BuildSceneViewportRenderPlanCollectsPostSceneAndOverlayPasses`
|
||||
- `ApplySceneViewportRenderPlanAttachesPlannedPassesAndClearState`
|
||||
|
||||
## 设计说明
|
||||
|
||||
把 Scene View request 组装拆成“构建计划”和“应用计划”两段,有两个现实收益:
|
||||
|
||||
- 可以单独测试 plan 的拼装逻辑,而不必把整个 `ViewportHostService` 拉起来。
|
||||
- 后续如果 Scene View 再增加 post-scene pass 或 overlay pass,扩写点会集中在这一层 helper,而不是继续膨胀 `RenderSceneViewportEntry(...)`。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Viewport](../Viewport.md)
|
||||
- [ViewportHostRenderFlowUtils](../ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md)
|
||||
- [ViewportHostService](../ViewportHostService/ViewportHostService.md)
|
||||
- [SceneViewportGridPassFactory](SceneViewportGridPassFactory.md)
|
||||
- [SceneViewportSelectionOutlinePassFactory](SceneViewportSelectionOutlinePassFactory.md)
|
||||
- [SceneViewportEditorOverlayPass](../Passes/SceneViewportEditorOverlayPass/SceneViewportEditorOverlayPass.md)
|
||||
@@ -0,0 +1,34 @@
|
||||
# SceneViewportRenderPlanBuildResult
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `struct`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
|
||||
## 定义
|
||||
|
||||
```cpp
|
||||
struct SceneViewportRenderPlanBuildResult {
|
||||
SceneViewportRenderPlan plan = {};
|
||||
const char* warningStatusText = nullptr;
|
||||
};
|
||||
```
|
||||
|
||||
## 字段
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `plan` | `SceneViewportRenderPlan` | 当前帧构建出的渲染计划。 |
|
||||
| `warningStatusText` | `const char*` | 可选警告文案,用于提示可降级但不必直接终止 Scene View 渲染的缺口。 |
|
||||
|
||||
## 当前实现语义
|
||||
|
||||
- 即使存在 `warningStatusText`,`plan` 也仍可能是可执行的有效计划。
|
||||
- 当前最典型的警告来源,是 Scene View 有选中对象、需要 selection outline,但 `objectIdShaderView` 不可用时返回的 `Scene object id shader view is unavailable`。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportRenderPlan](SceneViewportRenderPlan.md)
|
||||
- [BuildSceneViewportRenderPlan](BuildSceneViewportRenderPlan.md)
|
||||
- [ViewportHostRenderFlowUtils](../ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md)
|
||||
@@ -0,0 +1,31 @@
|
||||
# SceneViewportSelectionOutlinePassFactory
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `type alias`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
|
||||
## 定义
|
||||
|
||||
```cpp
|
||||
using SceneViewportSelectionOutlinePassFactory = std::function<std::unique_ptr<Rendering::RenderPass>(
|
||||
RHI::RHIResourceView*,
|
||||
const std::vector<uint64_t>&,
|
||||
const Rendering::Passes::ObjectIdOutlineStyle&)>;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
描述一个“根据 object-id SRV、选中对象列表和 style 创建 selection outline pass”的工厂回调。
|
||||
|
||||
## 当前语义
|
||||
|
||||
- 输入 object-id SRV 必须由上层 render target 流程准备好。
|
||||
- 输出 pass 会追加到 `SceneViewportRenderPlan::postScenePasses`。
|
||||
- 返回 `nullptr` 时,本帧不会追加 selection outline pass。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportRenderPlan](SceneViewportRenderPlan.md)
|
||||
- [BuildSceneViewportRenderPlan](BuildSceneViewportRenderPlan.md)
|
||||
@@ -1,40 +0,0 @@
|
||||
# BuiltinPostProcessRequest
|
||||
|
||||
**命名空间**: `XCEngine::Rendering`
|
||||
|
||||
**类型**: `struct`
|
||||
|
||||
**头文件**: `XCEngine/Rendering/CameraRenderRequest.h`
|
||||
|
||||
**描述**: 描述 scene viewport 一类内建后处理请求,包括无限网格、选中轮廓和调试遮罩所需输入。
|
||||
|
||||
## 概述
|
||||
|
||||
`BuiltinPostProcessRequest` 是 [CameraRenderRequest](../CameraRenderRequest.md) 里的内建后处理子请求。它不直接决定执行哪些 GPU 步骤,而是把原始输入交给 `BuiltinPostProcessPassSequenceBuilder` 去规划。
|
||||
|
||||
## 字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `gridPassData` | 控制是否绘制 scene viewport 的无限网格。 |
|
||||
| `objectIdTextureView` | 供 outline / debug mask 读取的 object-id 纹理 SRV。 |
|
||||
| `selectedObjectIds` | 当前需要描边或调试的对象 ID 集合。 |
|
||||
| `outlineStyle` | 轮廓颜色、宽度和调试遮罩开关。 |
|
||||
|
||||
## 关键语义
|
||||
|
||||
- [IsRequested](IsRequested.md) 当前只看 `gridPassData.valid` 和 `selectedObjectIds`,不会因为单独设置了 `objectIdTextureView` 或 `outlineStyle.debugSelectionMask` 就自动变成“已请求”。
|
||||
- 这意味着 `objectIdTextureView` 更像是 outline / debug 路径的依赖输入,而不是请求开关本身。
|
||||
- 更细的回退策略和步骤顺序由 `BuiltinPostProcessPassSequenceBuilder` 与 `BuiltinPostProcessPassPlan` 决定。
|
||||
|
||||
## 真实使用位置
|
||||
|
||||
- 编辑器 scene viewport 会构造这份请求,把无限网格、选中实体 ID 和 outline style 传进 `CameraRenderer`。
|
||||
- `CameraRenderer::Render()` 在主场景和 `postScenePasses` 之后,调用 builtin post-process builder 处理这里的内容。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [CameraRenderRequest](../CameraRenderRequest.md)
|
||||
- [IsRequested](IsRequested.md)
|
||||
- [Passes](../../Passes/Passes.md)
|
||||
- [BuiltinPostProcessPassSequenceBuilder](../../Passes/BuiltinPostProcessPassSequenceBuilder/BuiltinPostProcessPassSequenceBuilder.md)
|
||||
@@ -1,32 +0,0 @@
|
||||
# BuiltinPostProcessRequest::IsRequested
|
||||
|
||||
检查当前是否请求了 builtin 后处理。
|
||||
|
||||
```cpp
|
||||
bool IsRequested() const;
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
|
||||
当前头文件内联实现只有一条规则:
|
||||
|
||||
```cpp
|
||||
return gridPassData.valid || !selectedObjectIds.empty();
|
||||
```
|
||||
|
||||
## 返回值
|
||||
|
||||
- 当 `gridPassData.valid` 为真,或 `selectedObjectIds` 非空时,返回 `true`。
|
||||
- 否则返回 `false`。
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 单独设置 `objectIdTextureView` 不会让请求生效。
|
||||
- 单独把 `outlineStyle.debugSelectionMask` 设成 `true` 也不会让请求生效;仍然需要 grid 或 selection 至少有一个真正开启。
|
||||
- 这也是为什么 scene viewport 必须显式提供选中对象 ID,outline / debug mask 路径才会进入 builder。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](BuiltinPostProcessRequest.md)
|
||||
- [CameraRenderRequest](../CameraRenderRequest.md)
|
||||
- [BuiltinPostProcessPassSequenceBuilder](../../Passes/BuiltinPostProcessPassSequenceBuilder/BuiltinPostProcessPassSequenceBuilder.md)
|
||||
@@ -6,19 +6,18 @@
|
||||
|
||||
**头文件**: `XCEngine/Rendering/CameraRenderRequest.h`
|
||||
|
||||
**描述**: 把一次单相机渲染提交所需的主场景、目标表面、object-id 输出和 builtin 后处理配置打包成显式请求对象。
|
||||
**描述**: 把一次单相机提交所需的场景、目标表面、object-id 输出、清屏覆盖和可选 pass 序列打包成显式请求对象。
|
||||
|
||||
## 概览
|
||||
|
||||
`CameraRenderRequest` 是 [SceneRenderer](../SceneRenderer/SceneRenderer.md) 和 [CameraRenderer](../CameraRenderer/CameraRenderer.md) 之间的主要数据契约。它不负责自己执行绘制,而是把这次提交要用到的输入集中起来:
|
||||
`CameraRenderRequest` 是 [SceneRenderer](../SceneRenderer/SceneRenderer.md) 和 [CameraRenderer](../CameraRenderer/CameraRenderer.md) 之间的主要数据契约。它本身不执行渲染,只负责描述“这一台相机这次要怎么提交”:
|
||||
|
||||
- 主场景渲染的 `scene`、`camera`、`context` 和 `surface`
|
||||
- 主场景渲染所需的 `scene`、`camera`、`context` 和 `surface`
|
||||
- 可选的 object-id 辅助输出 `objectId`
|
||||
- 可选的 builtin 后处理 `builtinPostProcess`
|
||||
- 多相机排序与清屏控制字段
|
||||
- 在主场景前后插入自定义 pass 的钩子
|
||||
- 多相机排序和清屏覆盖字段
|
||||
- 主场景前后与最终叠加阶段要执行的可选 pass 序列
|
||||
|
||||
相比把这些参数散落在多个函数签名里,显式请求对象更适合表达“这次 camera submission 的完整意图”,也更利于测试和编辑器视口复用。
|
||||
Editor Scene View 的网格和选中轮廓,当前由 editor 侧先组装进 `postScenePasses` / `overlayPasses`,再随 request 一起交给 [CameraRenderer](../CameraRenderer/CameraRenderer.md) 执行。
|
||||
|
||||
## 核心字段
|
||||
|
||||
@@ -28,59 +27,62 @@
|
||||
| `camera` | 本次渲染使用的相机组件。 |
|
||||
| `context` | 当前帧的 RHI 上下文。 |
|
||||
| `surface` | 主颜色输出目标及其 render area。 |
|
||||
| `objectId` | 可选的 object-id 辅助输出。 |
|
||||
| `cameraDepth` | 多相机排序辅助值。 |
|
||||
| `cameraStackOrder` | base / overlay camera stack 的附加排序键。 |
|
||||
| `clearFlags` | 写回 `RenderSceneData::cameraData.clearFlags` 的覆盖值。 |
|
||||
| `hasClearColorOverride` / `clearColorOverride` | 是否覆盖提取到的相机清屏色。 |
|
||||
| `preScenePasses` | 主管线执行前的可选 pass 序列。 |
|
||||
| `postScenePasses` | 主管线与 builtin post-process 之间的可选 pass 序列。 |
|
||||
| `overlayPasses` | builtin post-process 之后的可选叠加 pass 序列。 |
|
||||
| `cameraStackOrder` | `Base` / `Overlay` camera stack 的附加排序键。 |
|
||||
| `clearFlags` | 回写到 `RenderSceneData::cameraData.clearFlags` 的覆盖值。 |
|
||||
| `hasClearColorOverride` / `clearColorOverride` | 是否覆盖提取出的相机清屏色。 |
|
||||
| `preScenePasses` | 主管线执行前的可选 `RenderPassSequence*`。 |
|
||||
| `postScenePasses` | 主场景和 object-id 之后、overlay 之前执行的可选 `RenderPassSequence*`。 |
|
||||
| `overlayPasses` | 最后叠加到主颜色目标上的可选 `RenderPassSequence*`。 |
|
||||
|
||||
## object-id 与 builtin 后处理
|
||||
## `objectId`
|
||||
|
||||
### `objectId`
|
||||
`objectId` 是一个 [ObjectIdRenderRequest](ObjectIdRenderRequest/ObjectIdRenderRequest.md),用于描述“是否要额外输出一张 object-id 辅助表面”。
|
||||
|
||||
`objectId` 是一个 [ObjectIdRenderRequest](ObjectIdRenderRequest/ObjectIdRenderRequest.md),用于描述“是否要额外渲染一张 object-id 辅助纹理”。
|
||||
|
||||
- `IsRequested()` 只检查 `surface` 是否挂了颜色附件。
|
||||
- `IsRequested()` 只检查 `surface` 是否真的挂了颜色附件。
|
||||
- `IsValid()` 进一步要求:
|
||||
- 第一个颜色附件非空
|
||||
- 第一张颜色附件非空
|
||||
- 深度附件非空
|
||||
- render area 宽高大于 `0`
|
||||
|
||||
`CameraRenderer::Render()` 当前会在主场景之后、`postScenePasses` 之前执行 object-id pass。
|
||||
按 `engine/src/Rendering/CameraRenderer.cpp` 的当前实现,`CameraRenderer::Render()` 会在主场景主管线之后、`postScenePasses` 之前执行当前的 [ObjectIdPass](../ObjectIdPass/ObjectIdPass.md)。
|
||||
|
||||
### `builtinPostProcess`
|
||||
## pass 序列字段
|
||||
|
||||
`builtinPostProcess` 是一个 [BuiltinPostProcessRequest](BuiltinPostProcessRequest/BuiltinPostProcessRequest.md),用于描述 scene viewport 一类内建附加效果:
|
||||
`preScenePasses`、`postScenePasses` 和 `overlayPasses` 都是非拥有指针。调用方需要保证这些序列在本次 `Render()` 完成前保持有效。
|
||||
|
||||
- `gridPassData` 控制无限网格
|
||||
- `objectIdTextureView` 把 object-id 纹理作为 SRV 传给后处理
|
||||
- `selectedObjectIds` 决定是否需要 selection outline / mask
|
||||
- `outlineStyle` 控制轮廓颜色、宽度与调试模式
|
||||
- `preScenePasses` 适合放主场景之前的准备阶段。
|
||||
- `postScenePasses` 适合放主场景和 object-id 之后的附加绘制。
|
||||
- `overlayPasses` 适合放最后叠加到主颜色目标上的编辑器覆盖层。
|
||||
|
||||
它的 `IsRequested()` 规则很宽松:只要 `gridPassData.valid` 为真,或者 `selectedObjectIds` 非空,就会被视为请求了 builtin post-process。
|
||||
Editor Scene View 当前会先通过 [SceneViewportRenderPlan](../../Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md) 组装一份 plan,再由 [ApplySceneViewportRenderPlan](../../Editor/Viewport/SceneViewportRenderPlan/ApplySceneViewportRenderPlan.md) 和 [ViewportHostRenderFlowUtils](../../Editor/Viewport/ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md) 把:
|
||||
|
||||
- `objectId`
|
||||
- `postScenePasses`
|
||||
- `overlayPasses`
|
||||
- clear-color override
|
||||
|
||||
写回到这份 request。
|
||||
|
||||
## 当前数据契约
|
||||
|
||||
- [IsValid](IsValid.md) 只检查 `scene`、`camera` 和 `context`,不会验证 `surface`、`objectId` 或 `builtinPostProcess` 是否完整。
|
||||
- [IsValid](IsValid.md) 只检查 `scene`、`camera` 和 `context`,不会验证 `surface` 或 `objectId` 是否完整。
|
||||
- `CameraRenderer::Render()` 会额外拒绝:
|
||||
- `surface` 的 render area 宽高为 `0`
|
||||
- `objectId.IsRequested()` 但 `objectId.IsValid()` 为假
|
||||
- `builtinPostProcess.objectIdTextureView` 非空,但没有请求 `objectId` 输出
|
||||
- builtin post-process 更细的回退逻辑由 `BuiltinPostProcessPassSequenceBuilder` 决定,而不是在 `CameraRenderRequest` 自身完成。
|
||||
- `SceneRenderer::Render(const std::vector<CameraRenderRequest>&)` 会先检查每条 request 的 `IsValid()`,再按 `cameraStackOrder` 和 `cameraDepth` 排序后逐条提交。
|
||||
|
||||
## 真实使用位置
|
||||
|
||||
- `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 的真实接入时序。
|
||||
- Editor Scene View 当前通过 [SceneViewportRenderPlan](../../Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md) 组装 `postScenePasses`、`overlayPasses` 与 clear override,并通过 [ViewportHostRenderFlowUtils](../../Editor/Viewport/ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md) 挂入 object-id surface。
|
||||
- `tests/Rendering/unit/test_camera_scene_renderer.cpp` 明确验证了 `preScenePasses -> pipeline -> objectId -> postScenePasses` 的真实执行顺序,以及 request clear override / 排序规则。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [IsValid](IsValid.md)
|
||||
- [ObjectIdRenderRequest](ObjectIdRenderRequest/ObjectIdRenderRequest.md)
|
||||
- [BuiltinPostProcessRequest](BuiltinPostProcessRequest/BuiltinPostProcessRequest.md)
|
||||
- [CameraRenderer](../CameraRenderer/CameraRenderer.md)
|
||||
- [SceneRenderer](../SceneRenderer/SceneRenderer.md)
|
||||
- [RenderContext](../RenderContext/RenderContext.md)
|
||||
|
||||
@@ -29,7 +29,7 @@ return scene != nullptr &&
|
||||
## 这意味着
|
||||
|
||||
- `surface` 不参与这里的校验。
|
||||
- `objectId` 和 `builtinPostProcess` 的完整性也不在这里检查。
|
||||
- `objectId` 的完整性也不在这里检查。
|
||||
- `cameraDepth`、`cameraStackOrder`、`clearFlags` 和 clear color override 都不会影响这里的结果。
|
||||
|
||||
因此它只是“最小可进入渲染器”的校验,而不是“这次请求一定能完整跑通”的保证。
|
||||
@@ -40,7 +40,6 @@ return scene != nullptr &&
|
||||
|
||||
- `surface.GetRenderAreaWidth()` / `GetRenderAreaHeight()` 是否大于 `0`
|
||||
- `objectId.IsRequested()` 时 `objectId.IsValid()` 是否为真
|
||||
- `builtinPostProcess.objectIdTextureView` 非空时,是否同时请求了 `objectId`
|
||||
|
||||
## 返回值
|
||||
|
||||
|
||||
@@ -6,44 +6,43 @@
|
||||
|
||||
**头文件**: `XCEngine/Rendering/CameraRenderer.h`
|
||||
|
||||
**描述**: 单个 `CameraRenderRequest` 的执行器。它负责在一次相机提交里完成场景提取、主管线渲染、object-id 输出,以及 builtin post-process / overlay pass 的串接。
|
||||
**描述**: 单个 `CameraRenderRequest` 的执行器。它负责在一次相机提交里完成场景提取、主管线渲染、object-id 输出,以及调用方注入的 post / overlay pass 串接。
|
||||
|
||||
## 概览
|
||||
|
||||
`CameraRenderer` 是当前渲染模块里的“单请求执行层”。和 [SceneRenderer](../SceneRenderer/SceneRenderer.md) 的分工是:
|
||||
|
||||
- `SceneRenderer` 负责规划和排序 `CameraRenderRequest`。
|
||||
- `CameraRenderer` 负责把一份 request 真正跑完。
|
||||
- `SceneRenderer` 负责规划和排序 `CameraRenderRequest`
|
||||
- `CameraRenderer` 负责把一份 request 真正跑完
|
||||
|
||||
因此这里关心的不是“该渲染哪些相机”,而是“这一个相机请求如何被执行”。
|
||||
因此这里关心的不是“该渲染哪些相机”,而是“这一份相机请求如何被执行”。
|
||||
|
||||
## 当前持有的运行时对象
|
||||
|
||||
- `m_sceneExtractor`
|
||||
使用 `request.surface` 的 render-area 尺寸调用 `ExtractForCamera(...)`,生成这次提交要用的 `RenderSceneData`。
|
||||
使用 `request.surface` 的 render area 尺寸调用 `ExtractForCamera(...)`,生成这次提交要用的 `RenderSceneData`。
|
||||
- `m_pipelineAsset`
|
||||
当前主管线的来源工厂;可能为空。
|
||||
- `m_pipeline`
|
||||
当前实际执行主场景绘制的 runtime [RenderPipeline](../RenderPipeline/RenderPipeline.md)。
|
||||
- `m_objectIdPass`
|
||||
主场景之后的 object-id pass;默认是 [Passes::BuiltinObjectIdPass](../Passes/BuiltinObjectIdPass/BuiltinObjectIdPass.md)。
|
||||
- `m_builtinPostProcessBuilder`
|
||||
把 builtin grid、selection outline、debug mask 请求翻译成临时 `RenderPassSequence`。
|
||||
|
||||
这意味着 `CameraRenderer` 本身不负责替调用方生成 Scene View 专用效果;它只按 request 已给出的 `postScenePasses` / `overlayPasses` 顺序执行。
|
||||
|
||||
## 当前执行顺序
|
||||
|
||||
对一份有效 request,当前 `Render()` 的主顺序是:
|
||||
|
||||
1. 校验 request、自身主管线、render-area、object-id 请求和 builtin post-process 前置条件。
|
||||
1. 校验 request、自身主管线、render-area 和 object-id 请求前置条件。
|
||||
2. 用 `m_sceneExtractor.ExtractForCamera(...)` 生成 `RenderSceneData`。
|
||||
3. 用 request 覆盖 `sceneData.cameraData.clearFlags`,必要时再覆盖 `clearColor`。
|
||||
4. 执行 `preScenePasses`。
|
||||
5. 执行 `m_pipeline->Render(...)`。
|
||||
6. 如请求了 object-id,执行 `m_objectIdPass->Render(...)`。
|
||||
7. 执行 `postScenePasses`。
|
||||
8. 如请求了 builtin post-process,先由 builder 生成临时 sequence,再执行它。
|
||||
9. 执行 `overlayPasses`。
|
||||
10. 按已初始化的 sequence 逆向 `Shutdown()`。
|
||||
8. 执行 `overlayPasses`。
|
||||
9. 按已初始化的 sequence 逆向 `Shutdown()`。
|
||||
|
||||
这条顺序被 `tests/Rendering/unit/test_camera_scene_renderer.cpp` 明确覆盖。
|
||||
|
||||
@@ -59,24 +58,23 @@
|
||||
|
||||
`Render()` 目前会显式拒绝:
|
||||
|
||||
- `request.IsValid()` 为假。
|
||||
- 当前 `m_pipeline == nullptr`。
|
||||
- `request.surface` 的 render-area 宽或高为 `0`。
|
||||
- 请求了 object-id,但 `request.objectId.IsValid()` 为假。
|
||||
- 请求了 builtin post-process,且提供了 `objectIdTextureView`,但这次 request 并没有真正请求 fresh object-id 输出。
|
||||
- `request.IsValid()` 为假
|
||||
- 当前 `m_pipeline == nullptr`
|
||||
- `request.surface` 的 render-area 宽或高为 `0`
|
||||
- 请求了 object-id,但 `request.objectId.IsValid()` 为假
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
- 只执行单个 request;多相机排序、stack 顺序和 clear 规则在 `SceneRenderer` / `SceneRenderRequestPlanner` 层完成。
|
||||
- 不直接决定“默认主管线是什么”,而是通过 pipeline asset seam 解决。
|
||||
- builtin post-process 仍然是顺序执行的 pass sequence,不是 render graph。
|
||||
- post / overlay 阶段只是顺序执行的 `RenderPassSequence`,不是 render graph。
|
||||
|
||||
## 公开方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| [Constructor](Constructor.md) | 构造 `CameraRenderer`,并建立默认或注入的主管线 / object-id pass。 |
|
||||
| [Destructor](Destructor.md) | 关闭当前主管线、object-id pass 和 builtin post-process builder。 |
|
||||
| [Destructor](Destructor.md) | 关闭当前主管线与 object-id pass。 |
|
||||
| [SetPipeline](SetPipeline.md) | 手动替换当前 runtime pipeline。 |
|
||||
| [SetPipelineAsset](SetPipelineAsset.md) | 通过 `RenderPipelineAsset` 重建当前 runtime pipeline。 |
|
||||
| [SetObjectIdPass](SetObjectIdPass.md) | 替换当前 object-id pass。 |
|
||||
@@ -92,4 +90,5 @@
|
||||
- [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md)
|
||||
- [RenderSceneExtractor](../RenderSceneExtractor/RenderSceneExtractor.md)
|
||||
- [RenderPipeline](../RenderPipeline/RenderPipeline.md)
|
||||
- [ObjectIdPass](../ObjectIdPass/ObjectIdPass.md)
|
||||
- [Scene Extraction And Builtin Forward Pipeline](../../../_guides/Rendering/Scene-Extraction-And-Builtin-Forward-Pipeline.md)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
## 作用
|
||||
|
||||
在对象销毁前关闭当前持有的主管线、object-id pass 和 builtin post-process builder。
|
||||
在对象销毁前关闭当前持有的主管线和 object-id pass。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
@@ -22,14 +22,13 @@
|
||||
|
||||
1. 如果 `m_pipeline` 非空,调用 `m_pipeline->Shutdown()`。
|
||||
2. 如果 `m_objectIdPass` 非空,调用 `m_objectIdPass->Shutdown()`。
|
||||
3. 调用 `m_builtinPostProcessBuilder.Shutdown()`。
|
||||
|
||||
随后由 `unique_ptr` 和成员对象自身析构负责释放内存。
|
||||
|
||||
## 设计含义
|
||||
|
||||
- 主管线和 object-id pass 的 GPU 资源回收路径是显式 `Shutdown()`,而不是只依赖 C++ 析构。
|
||||
- builtin post-process builder 会代为清理它内部缓存的 builtin grid / outline pass。
|
||||
- 调用方注入到 `CameraRenderRequest` 里的 `RenderPassSequence` 并不由这里持有,因此也不在这里做统一回收。
|
||||
|
||||
## 注意事项
|
||||
|
||||
|
||||
@@ -6,41 +6,39 @@ bool Render(const CameraRenderRequest& request);
|
||||
|
||||
## 行为说明
|
||||
|
||||
执行一次完整的单相机提交。当前实现会把主场景、object-id、注入 pass 和 builtin post-process 串成一条固定顺序的执行链。
|
||||
执行一次完整的单相机提交。当前实现会把主场景、object-id、调用方注入的 post passes 和 overlay passes 串成一条固定顺序的执行链。
|
||||
|
||||
## 当前流程
|
||||
|
||||
1. 校验 `request.IsValid()`,并确认 `m_pipeline` 非空。
|
||||
2. 拒绝 render-area 宽高为 `0` 的 surface。
|
||||
2. 拒绝 render-area 宽或高为 `0` 的 `surface`。
|
||||
3. 如果请求了 object-id,要求 `request.objectId.IsValid()` 为真。
|
||||
4. 如果 builtin post-process 依赖 `objectIdTextureView`,则要求本次 request 真的请求了 fresh object-id 输出。
|
||||
5. 用 `request.surface.GetRenderAreaWidth()` / `GetRenderAreaHeight()` 调用 `m_sceneExtractor.ExtractForCamera(...)`。
|
||||
6. 若 `sceneData.HasCamera()` 为假,则返回 `false`。
|
||||
7. 用 request 覆盖 `sceneData.cameraData.clearFlags`;如果 request 提供了 clear-color override,则继续覆盖 `sceneData.cameraData.clearColor`。
|
||||
8. 依次初始化并执行:
|
||||
4. 用 `request.surface.GetRenderAreaWidth()` / `GetRenderAreaHeight()` 调用 `m_sceneExtractor.ExtractForCamera(...)`。
|
||||
5. 若 `sceneData.HasCamera()` 为假,则返回 `false`。
|
||||
6. 用 request 覆盖 `sceneData.cameraData.clearFlags`;如果 request 提供了 clear-color override,则继续覆盖 `sceneData.cameraData.clearColor`。
|
||||
7. 依次初始化并执行:
|
||||
- `preScenePasses`
|
||||
- `m_pipeline->Render(...)`
|
||||
- `m_objectIdPass->Render(...)`
|
||||
- `postScenePasses`
|
||||
- builtin post-process 临时 sequence
|
||||
- `overlayPasses`
|
||||
9. 按已经成功初始化的 sequence 做逆向 `Shutdown()`。
|
||||
8. 按已经成功初始化的 sequence 做逆向 `Shutdown()`。
|
||||
|
||||
## builtin post-process 接入点
|
||||
## `postScenePasses` / `overlayPasses` 接入点
|
||||
|
||||
当 `request.builtinPostProcess.IsRequested()` 为真时,当前实现会:
|
||||
`CameraRenderer` 只消费调用方传入的 pass 序列,不再额外构建内部后处理层。
|
||||
|
||||
1. 调用 `m_builtinPostProcessBuilder.Build(...)` 生成一条临时 `RenderPassSequence`。
|
||||
2. 对这条临时 sequence 调用 `Initialize(context)`。
|
||||
3. 在 `postScenePasses` 之后、`overlayPasses` 之前执行它。
|
||||
- `postScenePasses` 直接来自调用方提供的 `RenderPassSequence`
|
||||
- `overlayPasses` 也是同样的注入点
|
||||
- 编辑器 Scene View 当前会先通过 [SceneViewportRenderPlan](../../Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md) 组装这些序列,再调用 `ApplySceneViewportRenderPlan(...)` 挂到 request
|
||||
|
||||
这意味着 scene viewport 的 builtin grid、selection outline 和 debug mask 都是主场景之后叠加的。
|
||||
这意味着 Scene View 的无限网格、选中轮廓和编辑器 overlay,已经被上移到 request 组装层,不再由 `CameraRenderer` 内部再做一次翻译。
|
||||
|
||||
## 失败与清理语义
|
||||
|
||||
- 任一验证失败会直接返回 `false`。
|
||||
- 任一 sequence 初始化失败会立即执行对应 `Shutdown()` 回滚。
|
||||
- 主主管线、object-id pass、post-process 或 overlay 任一阶段失败,都会按已经初始化过的 sequence 做清理再返回 `false`。
|
||||
- 任一 sequence 初始化失败会立刻执行对应 `Shutdown()` 回滚。
|
||||
- 主主管线、object-id pass、post 或 overlay 任一阶段失败,都会按已经初始化过的 sequence 做清理再返回 `false`。
|
||||
|
||||
## 参数
|
||||
|
||||
@@ -56,9 +54,9 @@ bool Render(const CameraRenderRequest& request);
|
||||
`tests/Rendering/unit/test_camera_scene_renderer.cpp` 当前验证了:
|
||||
|
||||
- render-area 会影响提取到的相机 viewport 尺寸。
|
||||
- clear-flags 与 clear-color override 会写回 `sceneData.cameraData`。
|
||||
- pre / pipeline / object-id / post / builtin / overlay 的执行顺序。
|
||||
- object-id 失败、post-pass 初始化失败和 builtin post-process 验证失败时的回滚路径。
|
||||
- clear-flags 和 clear-color override 会写回 `sceneData.cameraData`。
|
||||
- `pre / pipeline / object-id / post / overlay` 的执行顺序。
|
||||
- object-id 失败和 post-pass 初始化失败时的回滚路径。
|
||||
|
||||
## 相关文档
|
||||
|
||||
|
||||
71
docs/api/XCEngine/Rendering/ObjectIdPass/ObjectIdPass.md
Normal file
71
docs/api/XCEngine/Rendering/ObjectIdPass/ObjectIdPass.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# ObjectIdPass
|
||||
|
||||
**命名空间**: `XCEngine::Rendering`
|
||||
|
||||
**类型**: `interface`
|
||||
|
||||
**头文件**: `XCEngine/Rendering/ObjectIdPass.h`
|
||||
|
||||
**描述**: 定义把 `RenderSceneData` 渲染到 object-id 目标表面的抽象接口,供 `CameraRenderer` 注入具体实现。
|
||||
|
||||
## 概览
|
||||
|
||||
`ObjectIdPass` 是 `CameraRenderer` 主链路里的一个可替换插槽。
|
||||
|
||||
它的职责很明确:
|
||||
|
||||
- 输入已经提取好的 `RenderSceneData`
|
||||
- 输出一张 object-id 颜色目标
|
||||
- 不参与主管线本身的颜色绘制
|
||||
|
||||
当前默认实现是 [BuiltinObjectIdPass](../Passes/BuiltinObjectIdPass/BuiltinObjectIdPass.md),但 `CameraRenderer` 允许调用方替换成别的实现,测试里也正是通过 mock pass 验证执行时序和失败回滚。
|
||||
|
||||
## 接口契约
|
||||
|
||||
### `Render()`
|
||||
|
||||
调用方会把:
|
||||
|
||||
- 当前帧的 `RenderContext`
|
||||
- object-id 输出目标 `RenderSurface`
|
||||
- 已经提取完成的 `RenderSceneData`
|
||||
|
||||
一起传进来。返回 `false` 表示本次 object-id 渲染失败,`CameraRenderer::Render()` 会直接终止本次相机提交。
|
||||
|
||||
### `Shutdown()`
|
||||
|
||||
默认实现为空,用于给具体 pass 释放缓存的 GPU 资源或内部状态。
|
||||
|
||||
`CameraRenderer` 会在以下时机调用它:
|
||||
|
||||
- 替换 object-id pass 时
|
||||
- `CameraRenderer` 自身析构时
|
||||
|
||||
## 当前接入位置
|
||||
|
||||
在 [CameraRenderer](../CameraRenderer/CameraRenderer.md) 里,object-id pass 的执行顺序是:
|
||||
|
||||
1. 执行 pre-scene pass
|
||||
2. 绘制主 `RenderPipeline`
|
||||
3. 如果 `request.objectId.IsRequested()`,执行 `ObjectIdPass`
|
||||
4. 再执行 `postScenePasses`
|
||||
5. 最后执行 `overlayPasses`
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
- `ObjectIdPass` 只定义单次 object-id 绘制接口,不负责请求是否合法;这部分先由 `CameraRenderRequest` 和 `CameraRenderer` 做基础校验。
|
||||
- 接口没有显式的 `Initialize()` 阶段,具体实现通常在第一次 `Render()` 时懒初始化,或在构造期准备资源。
|
||||
- 当前引擎默认只维护一个 object-id pass 实例,由 `CameraRenderer` 持有。
|
||||
|
||||
## 真实使用位置
|
||||
|
||||
- `engine/src/Rendering/CameraRenderer.cpp`
|
||||
- `engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp`
|
||||
- `tests/Rendering/unit/test_camera_scene_renderer.cpp`
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [当前模块](../Rendering.md)
|
||||
- [CameraRenderer](../CameraRenderer/CameraRenderer.md)
|
||||
- [ObjectIdRenderRequest](../CameraRenderRequest/ObjectIdRenderRequest/ObjectIdRenderRequest.md)
|
||||
- [BuiltinObjectIdPass](../Passes/BuiltinObjectIdPass/BuiltinObjectIdPass.md)
|
||||
@@ -0,0 +1,37 @@
|
||||
# BuildInfiniteGridParameters
|
||||
|
||||
```cpp
|
||||
InfiniteGridParameters BuildInfiniteGridParameters(const InfiniteGridPassData& data);
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
|
||||
当前实现会:
|
||||
|
||||
1. 拒绝 `data.valid == false` 的输入。
|
||||
2. 以相机高度为主,计算目标网格间距。
|
||||
3. 把目标间距吸附到最近的十进制尺度 `10^n`。
|
||||
4. 计算向下一档网格过渡时的 `transitionBlend`。
|
||||
5. 结合网格尺度和视线到地面距离,生成 `fadeDistance`。
|
||||
|
||||
## 当前特征
|
||||
|
||||
- 水平平移不会改变结果。
|
||||
- 相机高度跨过阈值时,`baseScale` 会从 `1 -> 10 -> 100` 这类十进制级别跳变。
|
||||
- `orbitDistance` 当前不参与参数推导。
|
||||
|
||||
## 测试覆盖
|
||||
|
||||
`tests/Editor/test_scene_viewport_overlay_renderer.cpp` 当前验证了:
|
||||
|
||||
- 间距始终落在十进制尺度上。
|
||||
- 水平平移不影响参数。
|
||||
- 相机高度升高会扩大网格尺度和淡出距离。
|
||||
- `transitionBlend` 在阈值前后平滑过渡。
|
||||
- `orbitDistance` 变化不会影响结果。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md)
|
||||
- [InfiniteGridPassData](InfiniteGridPassData.md)
|
||||
- [InfiniteGridParameters](InfiniteGridParameters.md)
|
||||
@@ -10,14 +10,14 @@
|
||||
|
||||
## 概述
|
||||
|
||||
`BuiltinInfiniteGridPass` 是当前 scene viewport 里“地面参考网格”效果的实际执行者。
|
||||
`BuiltinInfiniteGridPass` 是当前 Scene View 里“地面参考网格”效果的实际执行者。
|
||||
|
||||
它消费一份相机姿态数据 `InfiniteGridPassData`,在运行时先推导网格尺度和过渡参数,再用一张 builtin shader 把网格直接混合到目标颜色附件上。
|
||||
它消费一份 [InfiniteGridPassData](InfiniteGridPassData.md),在运行时先推导 [InfiniteGridParameters](InfiniteGridParameters.md),再用 builtin shader 把网格直接混合到目标颜色附件上。
|
||||
|
||||
## 关键输入
|
||||
|
||||
- `InfiniteGridPassData` 决定相机位置、朝向、FOV、裁剪面和轨道距离。
|
||||
- `BuildInfiniteGridParameters()` 会把这些数据折算成 `baseScale`、`transitionBlend` 和 `fadeDistance`。
|
||||
- [InfiniteGridPassData](InfiniteGridPassData.md) 决定相机位置、朝向、FOV、裁剪面和轨道距离。
|
||||
- [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md) 会把这些数据折算成 `baseScale`、`transitionBlend` 和 `fadeDistance`。
|
||||
- `RenderSurface` 需要同时提供颜色附件和深度附件;当前 pass 会开启深度测试但关闭深度写入。
|
||||
|
||||
## 当前实现流程
|
||||
@@ -35,14 +35,26 @@
|
||||
- 视口和裁剪矩形使用 `surface.GetWidth()` / `GetHeight()`,不是自定义 render area。
|
||||
- 网格参数完全由当前相机高度与朝向启发式推导,还没有暴露成更完整的编辑器样式配置。
|
||||
|
||||
## 公开方法与相关类型
|
||||
|
||||
| 成员 | 说明 |
|
||||
|------|------|
|
||||
| [InfiniteGridPassData](InfiniteGridPassData.md) | 输入相机数据。 |
|
||||
| [InfiniteGridParameters](InfiniteGridParameters.md) | 推导后的网格尺度参数。 |
|
||||
| [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md) | 从输入相机数据生成当前网格参数。 |
|
||||
| [Render](Render.md) | 在当前颜色目标上提交网格覆盖层。 |
|
||||
| [Shutdown](Shutdown.md) | 销毁内部 pipeline、descriptor 和 shader handle。 |
|
||||
|
||||
## 真实使用位置
|
||||
|
||||
- `BuiltinPostProcessPassSequenceBuilder::AddInfiniteGridPass()` 会把它包装进 builtin post-process 序列。
|
||||
- `CameraRenderer` 在执行 `builtinPostProcess` 请求时,最终通过 builder 间接调用这里。
|
||||
- `tests/Rendering/unit/test_builtin_post_process_pass_sequence_builder.cpp` 覆盖了 grid-only 和 grid+selection 两类序列构建分支。
|
||||
- `editor/src/Viewport/Passes/SceneViewportGridPass.cpp` 用 `SceneViewportGridPassRenderer` 包装它,并把它挂进 Scene View 的 `postScenePasses`。
|
||||
- [SceneViewportRenderPlan](../../../Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md) 决定当前帧是否需要创建这条 pass。
|
||||
- `tests/Editor/test_scene_viewport_overlay_renderer.cpp` 固定了网格参数推导规则。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Passes](../Passes.md)
|
||||
- [BuiltinPostProcessPassSequenceBuilder](../BuiltinPostProcessPassSequenceBuilder/BuiltinPostProcessPassSequenceBuilder.md)
|
||||
- [CameraRenderer](../../CameraRenderer/CameraRenderer.md)
|
||||
- [InfiniteGridPassData](InfiniteGridPassData.md)
|
||||
- [InfiniteGridParameters](InfiniteGridParameters.md)
|
||||
- [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md)
|
||||
- [SceneViewportRenderPlan](../../../Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# InfiniteGridParameters
|
||||
|
||||
**命名空间**: `XCEngine::Rendering::Passes`
|
||||
|
||||
**类型**: `struct`
|
||||
|
||||
**头文件**: `XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h`
|
||||
|
||||
## 字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `valid` | 当前参数是否可用于真正绘制网格。 |
|
||||
| `baseScale` | 当前十进制主网格间距。 |
|
||||
| `transitionBlend` | 向下一档网格密度过渡的平滑插值。 |
|
||||
| `fadeDistance` | 当前网格淡出距离。 |
|
||||
|
||||
## 当前语义
|
||||
|
||||
- `baseScale` 始终是 `10^n` 这一类十进制尺度。
|
||||
- `transitionBlend` 范围在 `[0, 1]`。
|
||||
- `fadeDistance` 会同时参考网格尺度和视线到地面平面的距离。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md)
|
||||
- [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md)
|
||||
@@ -0,0 +1,31 @@
|
||||
# InfiniteGridPassData
|
||||
|
||||
**命名空间**: `XCEngine::Rendering::Passes`
|
||||
|
||||
**类型**: `struct`
|
||||
|
||||
**头文件**: `XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h`
|
||||
|
||||
## 字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `valid` | 当前输入是否可用于绘制网格。 |
|
||||
| `cameraPosition` | 当前 Scene View 相机世界坐标。 |
|
||||
| `cameraForward` | 相机 forward 方向。 |
|
||||
| `cameraRight` | 相机 right 方向。 |
|
||||
| `cameraUp` | 相机 up 方向。 |
|
||||
| `verticalFovDegrees` | 垂直视场角。 |
|
||||
| `nearClipPlane` | 近裁剪面。 |
|
||||
| `farClipPlane` | 远裁剪面。 |
|
||||
| `orbitDistance` | 当前 orbit 距离;现实现里不参与参数推导。 |
|
||||
|
||||
## 当前使用位置
|
||||
|
||||
- `editor/src/Viewport/ViewportHostRenderFlowUtils.h` 里的 `BuildSceneViewportGridPassData(...)` 会从 overlay 数据组装它。
|
||||
- [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md) 和 [Render](Render.md) 都消费它。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md)
|
||||
- [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md)
|
||||
@@ -0,0 +1,32 @@
|
||||
# BuiltinInfiniteGridPass::Render
|
||||
|
||||
```cpp
|
||||
bool Render(
|
||||
const RenderContext& renderContext,
|
||||
const RenderSurface& surface,
|
||||
const InfiniteGridPassData& data);
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
|
||||
当前实现会:
|
||||
|
||||
1. 要求 `data.valid`、`renderContext.IsValid()`,且后端必须是 `D3D12`。
|
||||
2. 惰性初始化 shader、pipeline layout、pipeline state 和常量 descriptor。
|
||||
3. 校验颜色附件与深度附件。
|
||||
4. 调用 [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md) 生成网格参数。
|
||||
5. 构建 view-projection 常量并写入 descriptor set。
|
||||
6. 绑定第一个颜色附件和深度附件。
|
||||
7. 以全屏三角形方式执行一次 `Draw(3, 1, 0, 0)`。
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 视口与 scissor 总是覆盖整张 surface,不读取 `surface.GetRenderArea()`。
|
||||
- pass 本身不做资源状态切换。
|
||||
- 只写第一个颜色附件。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md)
|
||||
- [InfiniteGridPassData](InfiniteGridPassData.md)
|
||||
- [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md)
|
||||
@@ -0,0 +1,19 @@
|
||||
# BuiltinInfiniteGridPass::Shutdown
|
||||
|
||||
```cpp
|
||||
void Shutdown();
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
|
||||
当前实现直接销毁内部缓存的:
|
||||
|
||||
- pipeline state
|
||||
- 常量 descriptor pool / set
|
||||
- pipeline layout
|
||||
- builtin infinite-grid shader handle
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md)
|
||||
- [Render](Render.md)
|
||||
@@ -13,8 +13,8 @@
|
||||
`BuiltinObjectIdOutlinePass` 处在“object-id 已经生成,但主场景也已经画完”的阶段。它本身不负责提取可见物体,也不负责生成 object-id 纹理,而是消费这些上游结果:
|
||||
|
||||
- object-id 纹理通常来自 `BuiltinObjectIdPass`。
|
||||
- 选中对象列表来自 [CameraRenderRequest](../../CameraRenderRequest/CameraRenderRequest.md) 的 `builtinPostProcess.selectedObjectIds`。
|
||||
- 调用时机由 `BuiltinPostProcessPassSequenceBuilder` 决定,并最终由 [CameraRenderer](../../CameraRenderer/CameraRenderer.md) 在主场景之后提交。
|
||||
- 选中对象列表和 style 当前通常由 editor 侧的 Scene View render plan 提供。
|
||||
- 调用时机当前由 `editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp` 这类包装 pass 决定,并最终通过 [CameraRenderer](../../CameraRenderer/CameraRenderer.md) 的 `postScenePasses` 提交。
|
||||
|
||||
因此它更接近一个“builtin selection feedback compositor”,而不是一个通用 post-process 框架。
|
||||
|
||||
@@ -40,41 +40,19 @@
|
||||
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()`。
|
||||
- [Destructor](Destructor.md) 当前是默认析构,因此如果把它作为长期 renderer 成员使用,调用方仍需在销毁前显式执行 `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` 位。
|
||||
|
||||
## 公开方法
|
||||
|
||||
@@ -87,16 +65,14 @@ shader 侧的行为是:
|
||||
|
||||
## 真实使用位置
|
||||
|
||||
- `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` 覆盖了它的主要接入链路。
|
||||
- `editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp` 用 `SceneViewportSelectionOutlinePassRenderer` 包装它。
|
||||
- [SceneViewportRenderPlan](../../../Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md) 决定当前帧是否要创建 selection outline pass。
|
||||
- [CameraRenderer](../../CameraRenderer/CameraRenderer.md) 在主场景与 object-id pass 之后执行这些 `postScenePasses`。
|
||||
- `editor/src/Viewport/ViewportHostRenderFlowUtils.h` 当前为 Scene View 组装默认橙色、`2px` 的 outline style。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Passes](../Passes.md)
|
||||
- [ObjectIdOutlineStyle](../ObjectIdOutlineStyle/ObjectIdOutlineStyle.md)
|
||||
- [CameraRenderRequest](../../CameraRenderRequest/CameraRenderRequest.md)
|
||||
- [CameraRenderer](../../CameraRenderer/CameraRenderer.md)
|
||||
- [RenderSurface](../../RenderSurface/RenderSurface.md)
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
## 注意事项
|
||||
|
||||
- 正常生命周期应在销毁前先调用 [Shutdown](Shutdown.md)。
|
||||
- 这也是 `BuiltinPostProcessPassSequenceBuilder::Shutdown()` 会主动转调 `m_outlinePass.Shutdown()` 的原因。
|
||||
- 如果把这个类单独作为局部对象或成员对象使用,而不是放在已有的 builder 生命周期里,析构前漏掉 `Shutdown()` 会造成资源回收路径缺失。
|
||||
- `editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.h` 里的 renderer 包装器也会把 `Shutdown()` 责任显式留给上层 host service。
|
||||
- 如果把这个类单独作为局部对象或成员对象使用,析构前漏掉 `Shutdown()` 会造成资源回收路径缺失。
|
||||
|
||||
## 相关文档
|
||||
|
||||
|
||||
@@ -23,54 +23,27 @@ bool Render(
|
||||
|
||||
## 当前实现流程
|
||||
|
||||
按 `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 颜色
|
||||
4. 构建 `OutlineConstants`,写入 viewport 尺寸、轮廓颜色、调试开关、像素宽度和最多 `256` 个选中对象编码颜色。
|
||||
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`:输入无效、资源创建失败,或目标颜色附件不可用。
|
||||
6. 设置渲染目标为 `surface` 的第一个颜色附件,并提交一次全屏三角形 `Draw(3, 1, 0, 0)`。
|
||||
|
||||
## 关键语义
|
||||
|
||||
- 当前只使用 `surface` 的第一个颜色附件,其它附件会被忽略。
|
||||
- 当前只使用 `surface` 的第一个颜色附件。
|
||||
- pass 自己不会做 render target 清空;如果要输出 debug mask 这种“整张替换”的结果,需要由调用方先清空目标。
|
||||
- pass 自己也不负责资源状态切换;调用前应保证颜色目标已经在 `RenderTarget` 状态、object-id SRV 已经可读。
|
||||
- 当前 viewport 和 scissor 始终覆盖整张 surface,而不是 `surface.GetRenderArea()`。
|
||||
- 选中对象列表超过 `BuiltinObjectIdOutlinePass::kMaxSelectedObjectCount` 时,后面的对象会被静默截断。
|
||||
|
||||
## 典型使用位置
|
||||
|
||||
- `BuiltinPostProcessPassSequenceBuilder::AddSelectionOutlinePass()`:正常轮廓模式。
|
||||
- `BuiltinPostProcessPassSequenceBuilder::AddSelectionMaskDebugPass()`:debug mask 模式,调用前会先清空颜色目标。
|
||||
- `editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp`:正常 Scene View 轮廓模式。
|
||||
- debug mask 仍然通过同一条 `Render(...)` 路径完成,只是 `style.debugSelectionMask = true`。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass.md)
|
||||
- [ObjectIdOutlineStyle](../ObjectIdOutlineStyle/ObjectIdOutlineStyle.md)
|
||||
- [RenderContext](../../RenderContext/RenderContext.md)
|
||||
- [RenderSurface](../../RenderSurface/RenderSurface.md)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# BuiltinObjectIdPass::BuildInputLayout
|
||||
|
||||
```cpp
|
||||
static RHI::InputLayoutDesc BuildInputLayout();
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
|
||||
当前实现只声明一个顶点元素:
|
||||
|
||||
- `POSITION`
|
||||
- `semanticIndex = 0`
|
||||
- `Format::R32G32B32_Float`
|
||||
- 偏移取自 `Resources::StaticMeshVertex::position`
|
||||
|
||||
## 当前语义
|
||||
|
||||
- object-id pass 只依赖顶点位置,不读取法线、UV、切线或颜色。
|
||||
- 这和当前 shader 语义一致,因为它只需要做几何覆盖并输出 object-id 颜色。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinObjectIdPass](BuiltinObjectIdPass.md)
|
||||
- [Render](Render.md)
|
||||
@@ -39,16 +39,29 @@
|
||||
- 这条 pass 依赖调用方先准备好 `RenderSceneData`,自己不做 scene extraction。
|
||||
- 只处理第一个颜色附件和一个深度附件。
|
||||
- 当前 object-id 绘制仍是逐物体提交,没有更高级的 batching。
|
||||
- viewport 与 scissor 使用 `surface.GetRenderArea()`,因此 object-id 输出会跟随当前 request 的子视口。
|
||||
- 是否支持某个后端,取决于 builtin object-id shader 是否提供对应 graphics variant;现有 shader loader 测试覆盖了 D3D12、OpenGL 和 Vulkan 三种 builtin variant。
|
||||
|
||||
## 公开方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| [Destructor](Destructor.md) | 析构时关闭内部缓存的 pipeline、descriptor set 和 shader handle。 |
|
||||
| [BuildInputLayout](BuildInputLayout.md) | 生成 object-id pass 当前使用的顶点布局。 |
|
||||
| [Render](Render.md) | 把 `visibleItems` 再绘制一遍到 object-id 目标。 |
|
||||
| [Shutdown](Shutdown.md) | 主动销毁当前缓存的 GPU 资源。 |
|
||||
|
||||
## 真实使用位置
|
||||
|
||||
- `CameraRenderer::Render()` 在主场景渲染后、builtin post-process 前调用 `m_objectIdPass->Render(...)`。
|
||||
- `CameraRenderer::Render()` 在主场景渲染后、`postScenePasses` 与 `overlayPasses` 前调用 `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)
|
||||
- [BuildInputLayout](BuildInputLayout.md)
|
||||
- [Render](Render.md)
|
||||
- [Shutdown](Shutdown.md)
|
||||
- [BuiltinObjectIdOutlinePass](../BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md)
|
||||
- [CameraRenderer](../../CameraRenderer/CameraRenderer.md)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# BuiltinObjectIdPass::~BuiltinObjectIdPass
|
||||
|
||||
```cpp
|
||||
~BuiltinObjectIdPass() override;
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
|
||||
当前析构函数只有一个动作:
|
||||
|
||||
```cpp
|
||||
Shutdown();
|
||||
```
|
||||
|
||||
因此它会在对象销毁前显式释放当前缓存的 pipeline state、pipeline layout、每对象 descriptor set、shader handle 和 `RenderResourceCache`。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinObjectIdPass](BuiltinObjectIdPass.md)
|
||||
- [Shutdown](Shutdown.md)
|
||||
@@ -0,0 +1,37 @@
|
||||
# BuiltinObjectIdPass::Render
|
||||
|
||||
```cpp
|
||||
bool Render(
|
||||
const RenderContext& context,
|
||||
const RenderSurface& surface,
|
||||
const RenderSceneData& sceneData) override;
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
|
||||
当前实现会:
|
||||
|
||||
1. 校验 `context`、颜色附件、深度附件和 `surface.GetRenderArea()`。
|
||||
2. 调用惰性初始化,必要时创建 shader、pipeline layout 和 pipeline state。
|
||||
3. 如果 `surface` 开启自动状态切换,把第一个颜色附件切到 `RenderTarget`。
|
||||
4. 把当前 render area 清成全零 object-id 颜色。
|
||||
5. 遍历 `sceneData.visibleItems`,为每个 `gameObject` 编码 object-id 并发出 draw call。
|
||||
6. 如果开启自动状态切换,再把颜色附件切回 `surface.GetColorStateAfter()`。
|
||||
|
||||
## 当前细节
|
||||
|
||||
- viewport 与 scissor 使用 `surface.GetRenderArea()`,不是整张 surface。
|
||||
- 每个 object ID 会缓存一套独立常量 descriptor set。
|
||||
- section mesh 会按 `VisibleRenderItem::sectionIndex` 绘制对应 section;否则退回整 mesh。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 成功完成本次 object-id 提交时返回 `true`。
|
||||
- 输入无效、资源初始化失败,或某个可见项缺少必要 mesh/buffer 时返回 `false`。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinObjectIdPass](BuiltinObjectIdPass.md)
|
||||
- [BuildInputLayout](BuildInputLayout.md)
|
||||
- [ObjectIdEncoding](../../ObjectIdEncoding/ObjectIdEncoding.md)
|
||||
- [CameraRenderer::Render](../../CameraRenderer/Render.md)
|
||||
@@ -0,0 +1,20 @@
|
||||
# BuiltinObjectIdPass::Shutdown
|
||||
|
||||
```cpp
|
||||
void Shutdown() override;
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
|
||||
当前实现直接转调内部 `DestroyResources()`,并释放:
|
||||
|
||||
- `RenderResourceCache`
|
||||
- 每对象 descriptor pool / set
|
||||
- pipeline state
|
||||
- pipeline layout
|
||||
- builtin object-id shader handle
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinObjectIdPass](BuiltinObjectIdPass.md)
|
||||
- [Destructor](Destructor.md)
|
||||
@@ -1,45 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,57 +0,0 @@
|
||||
# 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)
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
`ObjectIdOutlineStyle` 是 [BuiltinObjectIdOutlinePass](../BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md) 的轻量配置结构。它不拥有 GPU 资源,也不参与生命周期管理,只负责把“这次要画什么样的轮廓”显式传给 pass。
|
||||
|
||||
当前它也被 [CameraRenderRequest](../../CameraRenderRequest/CameraRenderRequest.md) 的 `builtinPostProcess.outlineStyle` 直接持有,因此调用方通常在构建渲染请求时就把选中反馈样式一并确定下来。
|
||||
当前它主要由 editor 侧的 Scene View 组装流程创建,再传给 `SceneViewportSelectionOutlinePassRenderer`,最终落到 `BuiltinObjectIdOutlinePass::Render(...)`。
|
||||
|
||||
## 默认值
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
| `outlineWidthPixels` | `2.0f` | 轮廓宽度,单位是像素。 |
|
||||
| `debugSelectionMask` | `false` | 默认输出正常轮廓,而不是调试 mask。 |
|
||||
|
||||
编辑器 scene viewport 当前也沿用了这组默认值。
|
||||
|
||||
## 字段语义
|
||||
|
||||
### `outlineColor`
|
||||
@@ -41,7 +39,7 @@
|
||||
### `debugSelectionMask`
|
||||
|
||||
- 为 `true` 时,pass 不再输出橙色轮廓,而是把“当前像素是否属于被选中对象”可视化为白底/黑底 mask。
|
||||
- 这个模式通常配合 `BuiltinPostProcessPassSequenceBuilder::AddSelectionMaskDebugPass()` 使用;builder 会先清空目标,再调用 outline pass 进行覆盖。
|
||||
- Scene View 当前也复用同一组 style 字段控制这个模式,只是由 editor 侧决定是否创建对应 pass。
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
@@ -52,5 +50,5 @@
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinObjectIdOutlinePass](../BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md)
|
||||
- [CameraRenderRequest](../../CameraRenderRequest/CameraRenderRequest.md)
|
||||
- [SceneViewportRenderPlan](../../../Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md)
|
||||
- [Passes](../Passes.md)
|
||||
|
||||
@@ -4,58 +4,58 @@
|
||||
|
||||
**类型**: `submodule`
|
||||
|
||||
**描述**: 承载 builtin object-id、outline、infinite grid 等补充型渲染 pass,以及把这些 pass 串成后处理序列的辅助类型。
|
||||
**描述**: 承载 builtin object-id、selection outline、infinite grid 等补充型渲染 pass,以及这些 pass 复用的轻量样式/参数类型。
|
||||
|
||||
## 概述
|
||||
|
||||
`Rendering::Passes` 不是用来替代 [RenderPipeline](../RenderPipeline/RenderPipeline.md) 的第二套主渲染框架,而是承载“主场景已经画完之后,还需要补一层额外结果”的 builtin pass:
|
||||
|
||||
- `BuiltinObjectIdPass` 把可见物体编码成 object-id 颜色,写到辅助渲染目标,供拾取和选中反馈使用。
|
||||
- [BuiltinObjectIdPass](BuiltinObjectIdPass/BuiltinObjectIdPass.md) 把可见物体编码成 object-id 颜色,写到辅助渲染目标,供拾取和选中反馈使用。
|
||||
- [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md) 读取 object-id 纹理和选中对象列表,把轮廓或调试 mask 叠加回场景颜色。
|
||||
- `BuiltinInfiniteGridPass` 给 Scene View 一类编辑器视口叠加无限网格。
|
||||
- `BuiltinPostProcessPassSequenceBuilder` 与 `BuiltinPostProcessPassPlan` 负责把这些 pass 排成一个可执行的 `RenderPassSequence`,并补上前后状态切换。
|
||||
- [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass/BuiltinInfiniteGridPass.md) 给 Scene View 一类编辑器视口叠加无限网格。
|
||||
|
||||
这层设计让 [CameraRenderer](../CameraRenderer/CameraRenderer.md) 仍然只关心“先画主场景,再按请求插入补充 pass”,而不必直接知道每个 editor-only 效果的 shader、descriptor 和执行顺序细节。
|
||||
当前这些 builtin pass 更多是“底层执行单元”。真正决定 Scene View 要不要画网格、要不要叠选中轮廓、这些效果要挂到 `postScenePasses` 还是 `overlayPasses` 上的逻辑,已经迁移到 editor 侧的 [SceneViewportRenderPlan](../../Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md)。
|
||||
|
||||
## 典型链路
|
||||
|
||||
当前 object-id 轮廓相关链路大致是:
|
||||
当前 Scene View 相关链路大致是:
|
||||
|
||||
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()` 真正提交。
|
||||
2. 如果 [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md) 里请求了 `objectId.surface`,则额外执行 [BuiltinObjectIdPass](BuiltinObjectIdPass/BuiltinObjectIdPass.md),把每个可见物体的 `GameObject` ID 编码进辅助纹理。
|
||||
3. editor 侧的 [SceneViewportRenderPlan](../../Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md) 会根据 overlay、选中对象和 render targets 同时构建 `postScenePasses` 与 `overlayPasses`。
|
||||
4. 其中 `postScenePasses` 里的 `SceneViewportGridPass` / `SceneViewportSelectionOutlinePass` 包装器,最终分别调用 [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass/BuiltinInfiniteGridPass.md) 与 [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md);`overlayPasses` 则保留给 editor overlay 的最终叠加层。
|
||||
|
||||
## 当前公开概念
|
||||
|
||||
| 类型 / 头文件 | 角色 |
|
||||
|------|------|
|
||||
| `BuiltinObjectIdPass` | 生成 object-id 颜色缓冲,给拾取与 outline 作为输入。 |
|
||||
| [BuiltinObjectIdPass](BuiltinObjectIdPass/BuiltinObjectIdPass.md) | 生成 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,决定后处理步骤顺序与回退策略。 |
|
||||
| [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass/BuiltinInfiniteGridPass.md) | Scene View 网格覆盖层的底层执行 pass。 |
|
||||
| [InfiniteGridPassData](BuiltinInfiniteGridPass/InfiniteGridPassData.md) | 网格 pass 使用的相机姿态与裁剪参数。 |
|
||||
| [InfiniteGridParameters](BuiltinInfiniteGridPass/InfiniteGridParameters.md) | 由相机状态推导出的网格尺度、过渡和淡出参数。 |
|
||||
| [BuildInfiniteGridParameters](BuiltinInfiniteGridPass/BuildInfiniteGridParameters.md) | 把 Scene View 相机数据折算成稳定的十进制网格参数。 |
|
||||
|
||||
## 测试与真实调用点
|
||||
|
||||
- `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 缺失时的降级逻辑。
|
||||
- `tests/Rendering/unit/test_camera_scene_renderer.cpp` 验证了 `CameraRenderer` 中 object-id pass 与可选 pass sequence 的接入时机。
|
||||
- `tests/Editor/test_scene_viewport_overlay_renderer.cpp` 固定了 `BuildInfiniteGridParameters(...)` 的十进制尺度、过渡和淡出语义。
|
||||
- `tests/Editor/test_viewport_render_flow_utils.cpp` 验证了 Scene View render plan 如何组装 grid / selection outline / overlay pass,以及 object-id SRV 缺失时的警告路径。
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
- 这一层目前是 builtin、轻量、偏 editor/工具链导向的 pass 集合,还不是通用 render graph。
|
||||
- object-id 相关流程依赖单独的辅助 render target / shader resource view,而不是直接从主颜色结果反推。
|
||||
- 具体 pass 的资源状态切换大多由外层 builder 或调用方保证,单个 pass 本身往往只做最小提交逻辑。
|
||||
- 具体 pass 的资源状态切换大多由调用方保证,单个 pass 本身往往只做最小提交逻辑。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [BuiltinObjectIdPass](BuiltinObjectIdPass/BuiltinObjectIdPass.md)
|
||||
- [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass/BuiltinInfiniteGridPass.md)
|
||||
- [BuiltinObjectIdOutlinePass](BuiltinObjectIdOutlinePass/BuiltinObjectIdOutlinePass.md)
|
||||
- [ObjectIdOutlineStyle](ObjectIdOutlineStyle/ObjectIdOutlineStyle.md)
|
||||
- [SceneViewportRenderPlan](../../Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md)
|
||||
- [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md)
|
||||
- [CameraRenderer](../CameraRenderer/CameraRenderer.md)
|
||||
- [当前模块](../Rendering.md)
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
# RenderPipeline::~RenderPipeline
|
||||
|
||||
销毁渲染管线对象。
|
||||
销毁渲染管线基类对象。
|
||||
|
||||
```cpp
|
||||
virtual ~RenderPipeline() = default;
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
## 当前语义
|
||||
|
||||
这是一个虚析构函数,确保通过 `RenderPipeline*` 删除派生对象时行为正确。
|
||||
- 这是一个虚析构函数,保证通过 `RenderPipeline*` 删除派生对象时行为正确。
|
||||
- 析构函数本身没有定义额外 shutdown 流程;资源释放语义仍应由 [Shutdown](Shutdown.md) 和派生类自己的析构实现共同承担。
|
||||
|
||||
## 当前所有权模型
|
||||
|
||||
按当前渲染主链路,runtime pipeline 通常由 [CameraRenderer](../CameraRenderer/CameraRenderer.md) 以 `std::unique_ptr<RenderPipeline>` 独占持有。
|
||||
这也是为什么基类必须提供虚析构,而不是让调用方了解每个 concrete pipeline 的真实类型。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](RenderPipeline.md)
|
||||
- [RenderPipeline::Shutdown](Shutdown.md)
|
||||
- [BuiltinForwardPipeline](../Pipelines/BuiltinForwardPipeline/BuiltinForwardPipeline.md)
|
||||
|
||||
@@ -1,26 +1,34 @@
|
||||
# RenderPipeline::Initialize
|
||||
|
||||
初始化渲染管线资源。
|
||||
初始化这条主场景 runtime 管线所需的底层资源。
|
||||
|
||||
```cpp
|
||||
virtual bool Initialize(const RenderContext& context) = 0;
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
## 当前语义
|
||||
|
||||
派生类应在这里创建 shader、descriptor set、pipeline state、sampler 等与具体管线相关的资源。
|
||||
- 这是一个“显式预热”入口,用来让具体管线提前准备 shader、pipeline state、descriptor set、采样器等资源。
|
||||
- 上层当前并不保证一定会在首次 [Render](Render.md) 前先调用这里。按 `engine/src/Rendering/CameraRenderer.cpp` 的现状,`CameraRenderer` 会直接进入 `m_pipeline->Render(...)`。
|
||||
- 因此调用方只能依赖“成功后这条管线进入可渲染状态”这一契约,不能依赖具体初始化时机。
|
||||
|
||||
## 当前实现弹性
|
||||
|
||||
- 具体实现可以把初始化全部放在这里一次性完成。
|
||||
- 也可以像 [BuiltinForwardPipeline](../Pipelines/BuiltinForwardPipeline/BuiltinForwardPipeline.md) 一样,在 [Render](Render.md) 里按需触发或复用内部初始化逻辑。
|
||||
|
||||
## 参数
|
||||
|
||||
- `context` - 当前渲染上下文。
|
||||
- `context` - 当前帧的底层渲染上下文。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 初始化成功返回 `true`。
|
||||
- 否则返回 `false`。
|
||||
- 返回 `true`:初始化成功,后续可进入 [Render](Render.md)。
|
||||
- 返回 `false`:初始化失败;调用方不应假定这条管线已经进入可用状态。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](RenderPipeline.md)
|
||||
- [RenderPipeline::Render](Render.md)
|
||||
- [RenderContext](../RenderContext/RenderContext.md)
|
||||
- [BuiltinForwardPipeline::Initialize](../Pipelines/BuiltinForwardPipeline/Initialize.md)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# RenderPipeline::Render
|
||||
|
||||
执行一次渲染。
|
||||
执行一次主场景 runtime 绘制。
|
||||
|
||||
```cpp
|
||||
virtual bool Render(
|
||||
@@ -9,23 +9,53 @@ virtual bool Render(
|
||||
const RenderSceneData& sceneData) = 0;
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
## 当前职责边界
|
||||
|
||||
派生类应在这里把已经提取好的 `sceneData` 绘制到 `surface`。
|
||||
这个接口只负责把已经提取好的主场景数据画到目标 [RenderSurface](../RenderSurface/RenderSurface.md):
|
||||
|
||||
- 消费 `sceneData.cameraData`
|
||||
- 遍历 `sceneData.visibleItems`
|
||||
- 读写 `surface` 的颜色/深度附件与 render-area
|
||||
|
||||
它不负责:
|
||||
|
||||
- 多相机 request 规划与排序
|
||||
- object-id pass
|
||||
- `preScenePasses` / `postScenePasses` / `overlayPasses` 编排
|
||||
|
||||
这些外层编排由 [CameraRenderer](../CameraRenderer/CameraRenderer.md) 负责。
|
||||
|
||||
## 当前调用位置
|
||||
|
||||
按 `engine/src/Rendering/CameraRenderer.cpp`,当前主链路顺序是:
|
||||
|
||||
1. `preScenePasses`
|
||||
2. `m_pipeline->Render(...)`
|
||||
3. `ObjectIdPass`
|
||||
4. `postScenePasses`
|
||||
5. `overlayPasses`
|
||||
|
||||
因此这里代表的是“主场景绘制槽位”,不是整次相机提交的全部流程。
|
||||
|
||||
## 当前实现弹性
|
||||
|
||||
- 具体实现可以假定 `sceneData` 已经由 [RenderSceneExtractor](../RenderSceneExtractor/RenderSceneExtractor.md) 产出。
|
||||
- 具体实现也可以在这里内部做惰性初始化,只要对调用方维持一致的成败语义。
|
||||
|
||||
## 参数
|
||||
|
||||
- `context` - 当前渲染上下文。
|
||||
- `surface` - 本次渲染的目标表面。
|
||||
- `sceneData` - 已经提取好的相机和可见对象数据。
|
||||
- `context` - 当前帧的底层渲染上下文。
|
||||
- `surface` - 本次主场景绘制的目标表面与 render-area。
|
||||
- `sceneData` - 已提取好的相机、光照与可绘制项数据。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 成功返回 `true`。
|
||||
- 失败返回 `false`。
|
||||
- 返回 `true`:主场景绘制成功。
|
||||
- 返回 `false`:主场景绘制失败;外层 `CameraRenderer` 会终止这次提交并按已初始化阶段做清理。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](RenderPipeline.md)
|
||||
- [RenderSurface](../RenderSurface/RenderSurface.md)
|
||||
- [CameraRenderer::Render](../CameraRenderer/Render.md)
|
||||
- [RenderSceneExtractor](../RenderSceneExtractor/RenderSceneExtractor.md)
|
||||
- [RenderSurface](../RenderSurface/RenderSurface.md)
|
||||
|
||||
@@ -6,47 +6,72 @@
|
||||
|
||||
**头文件**: `XCEngine/Rendering/RenderPipeline.h`
|
||||
|
||||
**描述**: 定义渲染管线的统一接口,负责初始化、关闭和把 `RenderSceneData` 绘制到 `RenderSurface`。
|
||||
**描述**: 主场景 runtime 管线的统一接口,负责初始化、关闭,并把一份 `RenderSceneData` 绘制到目标 `RenderSurface`。
|
||||
|
||||
## 概述
|
||||
## 概览
|
||||
|
||||
`RenderPipeline` 是当前渲染模块最关键的扩展点之一。它把真正的绘制实现抽象成三个阶段:
|
||||
`RenderPipeline` 是当前渲染模块里最核心的 runtime 扩展点之一。它把“真正的主场景绘制”抽象成三个阶段:
|
||||
|
||||
- 初始化管线资源
|
||||
- 关闭并释放资源
|
||||
- 执行一次渲染
|
||||
- `Initialize(const RenderContext&)`
|
||||
- `Shutdown()`
|
||||
- `Render(const RenderContext&, const RenderSurface&, const RenderSceneData&)`
|
||||
|
||||
这类接口和商业引擎里常见的 render pipeline abstraction 很接近,因为上层的 scene renderer 不应该知道具体是前向、延迟还是自定义渲染路径。
|
||||
上游不会直接依赖某个具体 builtin 管线类型,而是只依赖这个抽象接口。
|
||||
|
||||
## 当前实现
|
||||
## 当前接入方式
|
||||
|
||||
当前公开的具体实现是:
|
||||
默认情况下,具体 runtime pipeline 不是由 `SceneRenderer` 直接构造,而是由 `CameraRenderer` 通过 [RenderPipelineAsset](../RenderPipelineAsset/RenderPipelineAsset.md) 创建并持有。
|
||||
|
||||
当前公开的默认实现是:
|
||||
|
||||
- [BuiltinForwardPipeline](../Pipelines/BuiltinForwardPipeline/BuiltinForwardPipeline.md)
|
||||
|
||||
`SceneRenderer` 默认也会创建这个内建前向管线。
|
||||
默认工厂则是:
|
||||
|
||||
- [BuiltinForwardPipelineAsset](../Pipelines/BuiltinForwardPipelineAsset/BuiltinForwardPipelineAsset.md)
|
||||
|
||||
## 当前职责边界
|
||||
|
||||
`RenderPipeline` 只负责主场景绘制本身,不负责:
|
||||
|
||||
- 多相机 request 规划和排序
|
||||
- object-id pass 与 object-id 输出目标
|
||||
- `preScenePasses` / `postScenePasses` / `overlayPasses` 的外层编排
|
||||
|
||||
这些都由 [CameraRenderer](../CameraRenderer/CameraRenderer.md) 在外层编排。
|
||||
|
||||
## 当前实现弹性
|
||||
|
||||
接口本身并不强制初始化策略。具体实现可以:
|
||||
|
||||
- 在 `Initialize()` 中一次性准备资源。
|
||||
- 或者像 `BuiltinForwardPipeline` 一样,在 `Render()` 中按需触发或复用初始化逻辑。
|
||||
|
||||
因此调用方只依赖语义契约,而不依赖某个具体实现的资源建立时机。
|
||||
|
||||
## 公开方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| [Destructor](Destructor.md) | 虚析构函数。 |
|
||||
| [Initialize](Initialize.md) | 初始化这条渲染管线需要的底层资源。 |
|
||||
| [Shutdown](Shutdown.md) | 释放管线资源。 |
|
||||
| [Render](Render.md) | 把场景数据绘制到指定表面。 |
|
||||
| [Initialize](Initialize.md) | 初始化这条主管线所需的底层资源。 |
|
||||
| [Shutdown](Shutdown.md) | 释放主管线资源。 |
|
||||
| [Render](Render.md) | 把一份 `RenderSceneData` 绘制到指定 `RenderSurface`。 |
|
||||
|
||||
## 设计说明
|
||||
|
||||
把 `RenderContext`、`RenderSurface` 和 `RenderSceneData` 分开传入,是一个很好的职责拆分:
|
||||
把 `RenderContext`、`RenderSurface` 和 `RenderSceneData` 分开传入,是当前很关键的职责拆分:
|
||||
|
||||
- `RenderContext` 代表底层执行环境。
|
||||
- `RenderSurface` 代表当前目标表面。
|
||||
- `RenderSceneData` 代表已经提取好的高层场景数据。
|
||||
- `RenderContext` 表示底层设备与命令环境。
|
||||
- `RenderSurface` 表示这次提交的目标与 render-area。
|
||||
- `RenderSceneData` 表示已经提取好的相机、光照和可绘制项。
|
||||
|
||||
这样做能让同一条管线更容易在不同目标和不同场景上复用。
|
||||
这样同一条 runtime pipeline 才能在不同 surface、不同 request 和不同场景提取结果之间复用。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [当前模块](../Rendering.md)
|
||||
- [Rendering](../Rendering.md)
|
||||
- [CameraRenderer](../CameraRenderer/CameraRenderer.md)
|
||||
- [RenderPipelineAsset](../RenderPipelineAsset/RenderPipelineAsset.md)
|
||||
- [BuiltinForwardPipelineAsset](../Pipelines/BuiltinForwardPipelineAsset/BuiltinForwardPipelineAsset.md)
|
||||
- [SceneRenderer](../SceneRenderer/SceneRenderer.md)
|
||||
|
||||
@@ -1,16 +1,29 @@
|
||||
# RenderPipeline::Shutdown
|
||||
|
||||
关闭渲染管线并释放其资源。
|
||||
关闭这条主场景 runtime 管线并释放其持有资源。
|
||||
|
||||
```cpp
|
||||
virtual void Shutdown() = 0;
|
||||
```
|
||||
|
||||
## 行为说明
|
||||
## 当前调用语义
|
||||
|
||||
派生类应在这里释放这条管线持有的 RHI 资源、缓存和临时对象。
|
||||
从 `engine/src/Rendering/CameraRenderer.cpp` 可以看到,当前调用方会在这些时机直接调用它:
|
||||
|
||||
- `CameraRenderer` 析构时
|
||||
- `CameraRenderer::ResetPipeline()` 替换旧主管线时
|
||||
|
||||
调用方不会额外区分“这条管线是否做过显式 [Initialize](Initialize.md)”。
|
||||
|
||||
## 对实现的要求
|
||||
|
||||
- 应释放这条 runtime pipeline 自己持有的 RHI 资源、缓存和临时对象。
|
||||
- 最好能容忍“尚未显式初始化就被关闭”或“曾经在 [Render](Render.md) 内部惰性初始化过”的调用路径。
|
||||
- 不应把 object-id pass 或外层 request / `RenderPassSequence` 编排状态混进这里。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](RenderPipeline.md)
|
||||
- [RenderPipeline::Initialize](Initialize.md)
|
||||
- [CameraRenderer](../CameraRenderer/CameraRenderer.md)
|
||||
- [BuiltinForwardPipeline::Shutdown](../Pipelines/BuiltinForwardPipeline/Shutdown.md)
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
- 它只规划相机请求,不处理 object-id 或 builtin post-process 的具体填充;那部分通常由更上层调用方补充。
|
||||
- 它只规划相机请求,不处理 `objectId` 或各类 `RenderPassSequence` 的具体填充;那部分通常由更上层调用方补充。
|
||||
- 当前没有 camera stacking 的更复杂依赖分析,只有 base / overlay 与 depth 的线性排序。
|
||||
- `BuildRequests()` 只返回成功构建的请求,不单独报告被过滤掉的相机原因。
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
|
||||
- 这些 helper 是纯局部规则,不处理多表面、多窗口或跨帧状态。
|
||||
- clear 推导只依赖当前计数,不关心更复杂的 camera stack 依赖关系。
|
||||
- request 组装阶段只填基础字段,object-id 与 builtin post-process 仍需上层补全。
|
||||
- request 组装阶段只填基础字段;`objectId`、`preScenePasses`、`postScenePasses` 与 `overlayPasses` 仍可由上层继续补全。
|
||||
|
||||
## 相关文档
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
`SceneRenderer` 自身没有自定义析构逻辑。真正的资源关闭发生在成员析构阶段:
|
||||
|
||||
- `m_cameraRenderer` 会在自身析构里关闭当前主管线、object-id pass 和 builtin post-process builder。
|
||||
- `m_cameraRenderer` 会在自身析构里关闭当前主管线和 object-id pass;request 级 `RenderPassSequence` 的生命周期则由每次 `Render()` 调用自行初始化和回收。
|
||||
- `m_requestPlanner` 按普通值成员销毁。
|
||||
|
||||
因此当前析构语义是“把 teardown 责任完全交给内部 `CameraRenderer`”,而不是在 `SceneRenderer` 层重复写一套 shutdown 流程。
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
`SceneRenderer` 当前不再自己持有 `RenderSceneExtractor` 或直接驱动主管线。它只维护两块运行时对象:
|
||||
|
||||
- `m_requestPlanner`:负责从 `Scene` 和可选 override camera 生成 `CameraRenderRequest` 列表。
|
||||
- `m_cameraRenderer`:负责真正执行单个相机请求,包括场景提取、主管线提交、object-id pass 和 builtin post-process。
|
||||
- `m_cameraRenderer`:负责真正执行单个相机请求,包括场景提取、主管线提交、object-id pass,以及 request 上挂接的 pre/post/overlay pass sequence。
|
||||
|
||||
这意味着当前的主链路已经拆成两层:
|
||||
|
||||
@@ -48,6 +48,8 @@ return Render(BuildRenderRequests(scene, overrideCamera, context, surface));
|
||||
4. 依次调用 `m_cameraRenderer.Render(request)`。
|
||||
5. 任一请求失败就立即返回 `false`。
|
||||
|
||||
这里不会额外改写 request 里的 `objectId` 或各类 pass sequence;如果上层已经补好了这些字段,`SceneRenderer` 只负责排序后转交执行。
|
||||
|
||||
### `Render(const CameraRenderRequest&)`
|
||||
|
||||
这是最薄的一层,直接转发到 `m_cameraRenderer.Render(request)`。
|
||||
@@ -64,6 +66,7 @@ return Render(BuildRenderRequests(scene, overrideCamera, context, surface));
|
||||
|
||||
- 不直接做 scene extraction;真正的 `RenderSceneExtractor` 调用发生在 `CameraRenderer` 里。
|
||||
- 不直接操作 `RenderPipeline` 的初始化或渲染细节;它只负责请求规划和排序。
|
||||
- 不负责替 request 自动补 `objectId` 或各类 `RenderPassSequence`;这类附加阶段通常由 Editor 或其它上层调用方在提交前写入。
|
||||
- 对手工提交的请求数组,会再次按相机优先级稳定排序,而不是按调用方原始顺序盲目执行。
|
||||
|
||||
## 测试覆盖
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
同时,这里也统一收纳编辑器侧 API 文档入口 [Editor](Editor/Editor.md),用于描述与引擎并行协作的编辑器应用层接口。
|
||||
|
||||
本轮收口之后,`Core/Asset`、`Rendering`、`Resources` 和 `Scene` 这几个模块已经把基础 helper / 协议层 header 也纳入了 canonical 文档,而不只覆盖高层类和模块页。
|
||||
|
||||
## 设计原则
|
||||
|
||||
- 目录结构与源码平行,避免“文档是一套世界,源码又是一套世界”。
|
||||
@@ -38,6 +40,13 @@
|
||||
- [Scripting](Scripting/Scripting.md)
|
||||
- [Threading](Threading/Threading.md)
|
||||
|
||||
## 重点入口
|
||||
|
||||
- [Core / Asset](Core/Asset/Asset.md) - 项目资产身份、artifact 缓存与运行时资源加载基础设施。
|
||||
- [Rendering](Rendering/Rendering.md) - 相机请求规划、场景提取、主管线、object-id 与显式 `RenderPassSequence` 主链路。
|
||||
- [Resources](Resources/Resources.md) - 具体资源类型以及 `builtin://` 内置资源入口。
|
||||
- [Scene](Scene/Scene.md) - 场景容器、运行态执行器与时间推进控制。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [API 总索引](../main.md)
|
||||
|
||||
@@ -1,27 +1,35 @@
|
||||
# API 文档重构状态
|
||||
|
||||
**生成时间**: `2026-03-28 15:13:19`
|
||||
**生成时间**: `2026-04-03 15:07:08`
|
||||
|
||||
**来源**: `docs/api/_tools/audit_api_docs.py`
|
||||
|
||||
## 摘要
|
||||
|
||||
- Markdown 页面数(全部): `2881`
|
||||
- Markdown 页面数(canonical): `2860`
|
||||
- Public headers 数: `227`
|
||||
- 有效头文件引用数(全部): `227`
|
||||
- 有效头文件引用数(canonical): `227`
|
||||
- Markdown 页面数(全部): `3327`
|
||||
- Markdown 页面数(canonical): `3298`
|
||||
- Public headers 数: `244`
|
||||
- Editor source headers 数: `124`
|
||||
- 有效头文件引用数(全部): `244`
|
||||
- 有效头文件引用数(canonical): `244`
|
||||
- 无效头文件引用数: `0`
|
||||
- 有效源文件引用数(全部): `124`
|
||||
- 有效源文件引用数(Editor canonical): `124`
|
||||
- 无效源文件引用数: `0`
|
||||
- 失效 `.md` 链接数: `0`
|
||||
- 非 `.md` 相对链接数: `0`
|
||||
- 旧模板页面数: `0`
|
||||
- 扁平 header 页面数: `0`
|
||||
- Canonical 显式过期符号残留数: `0`
|
||||
- Editor 显式过期符号残留数: `0`
|
||||
- Editor 残留 canonical 旧页面数: `0`
|
||||
- Editor 高风险单页目录数: `0`
|
||||
|
||||
## 平行目录
|
||||
|
||||
- Canonical 根目录: `XCEngine`
|
||||
- 源码目录节点数: `29`
|
||||
- 已生成目录总览页节点数: `29`
|
||||
- 源码目录节点数: `30`
|
||||
- 已生成目录总览页节点数: `30`
|
||||
- 缺失目录总览页节点数: `0`
|
||||
- 支撑目录: `_meta, _tools`
|
||||
|
||||
@@ -31,23 +39,43 @@
|
||||
|------|----------------|--------|--------|
|
||||
| `Audio` | `11` | `11` | `0` |
|
||||
| `Components` | `10` | `10` | `0` |
|
||||
| `Core` | `42` | `42` | `0` |
|
||||
| `Core` | `48` | `48` | `0` |
|
||||
| `Debug` | `10` | `10` | `0` |
|
||||
| `Input` | `5` | `5` | `0` |
|
||||
| `Memory` | `5` | `5` | `0` |
|
||||
| `Platform` | `11` | `11` | `0` |
|
||||
| `RHI` | `87` | `87` | `0` |
|
||||
| `Rendering` | `13` | `13` | `0` |
|
||||
| `Resources` | `13` | `13` | `0` |
|
||||
| `Scene` | `3` | `3` | `0` |
|
||||
| `Rendering` | `22` | `22` | `0` |
|
||||
| `Resources` | `14` | `14` | `0` |
|
||||
| `Scene` | `4` | `4` | `0` |
|
||||
| `Scripting` | `7` | `7` | `0` |
|
||||
| `Threading` | `10` | `10` | `0` |
|
||||
|
||||
## Editor 源文件页覆盖
|
||||
|
||||
| 模块 | Source headers | 已覆盖 | 未覆盖 |
|
||||
|------|----------------|--------|--------|
|
||||
| `(root)` | `3` | `3` | `0` |
|
||||
| `Actions` | `9` | `9` | `0` |
|
||||
| `Commands` | `4` | `4` | `0` |
|
||||
| `ComponentEditors` | `10` | `10` | `0` |
|
||||
| `Core` | `20` | `20` | `0` |
|
||||
| `Layers` | `1` | `1` | `0` |
|
||||
| `Layout` | `1` | `1` | `0` |
|
||||
| `Managers` | `3` | `3` | `0` |
|
||||
| `Platform` | `4` | `4` | `0` |
|
||||
| `Scripting` | `3` | `3` | `0` |
|
||||
| `UI` | `29` | `29` | `0` |
|
||||
| `Utils` | `4` | `4` | `0` |
|
||||
| `Viewport` | `23` | `23` | `0` |
|
||||
| `panels` | `10` | `10` | `0` |
|
||||
|
||||
## 元信息覆盖
|
||||
|
||||
| 字段 | 页面数 |
|
||||
|------|--------|
|
||||
| `命名空间` | `449` |
|
||||
| `类型` | `449` |
|
||||
| `描述` | `344` |
|
||||
| `头文件` | `332` |
|
||||
| `命名空间` | `891` |
|
||||
| `类型` | `891` |
|
||||
| `描述` | `417` |
|
||||
| `头文件` | `502` |
|
||||
| `源文件` | `348` |
|
||||
|
||||
248
docs/plan/API文档实时同步任务池_2026-04-03.md
Normal file
248
docs/plan/API文档实时同步任务池_2026-04-03.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# API 文档实时同步任务池(2026-04-03,第二轮)
|
||||
|
||||
## 文档定位
|
||||
|
||||
这份任务池接替已归档的第一轮计划:
|
||||
|
||||
- `docs/plan/used/API文档实时同步任务池_2026-04-03_第一轮归档.md`
|
||||
|
||||
第一轮已经解决的重点是:
|
||||
|
||||
- canonical 目录结构收口
|
||||
- 历史缺页补齐
|
||||
- 第一轮大规模内容重写
|
||||
|
||||
本轮重点不再是“补结构”,而是:
|
||||
|
||||
- 重新对照当前工作树源码与测试
|
||||
- 清理最近重构后重新出现的内容级失配
|
||||
- 继续维护一份适合多人并行认领的增量同步清单
|
||||
|
||||
## 当前复核快照
|
||||
|
||||
- 最近一次结构审计时间:`2026-04-03 15:07:08`
|
||||
- 审计命令:`python -B docs/api/_tools/audit_api_docs.py`
|
||||
- 当前结果:
|
||||
- `Public headers: 244`
|
||||
- `Editor source headers: 124`
|
||||
- `Invalid header refs: 0`
|
||||
- `Invalid source refs: 0`
|
||||
- `Broken .md links: 0`
|
||||
- `Stale canonical doc tokens: 0`
|
||||
- `Stale editor doc tokens: 0`
|
||||
- `Stale editor canonical pages: 0`
|
||||
|
||||
这说明当前结构层面已经恢复全绿;本轮后续工作以内容级持续同步为主。
|
||||
|
||||
## 认领规则
|
||||
|
||||
- 一次只认领 `1` 个任务块。
|
||||
- 先把 `状态` 改成 `DOING`,再写 `认领人`。
|
||||
- 只能改自己任务块的 `写入范围`。
|
||||
- 所有改动都必须以“当前源码 + 当前测试 + 当前真实调用链”为依据,不允许按旧文档续写旧行为。
|
||||
- 如果清理了过期 API 页面,必须同时清理交叉链接。
|
||||
|
||||
## 任务池
|
||||
|
||||
## T01 Editor / Viewport 渲染计划与宿主流程内容同步
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P0`
|
||||
- 写入范围:
|
||||
- `docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/**`
|
||||
- `docs/api/XCEngine/Editor/Viewport/ViewportHostRenderFlowUtils/**`
|
||||
- `docs/api/XCEngine/Editor/Viewport/ViewportHostService/**`
|
||||
- 必要时 `docs/api/XCEngine/Editor/Viewport/IViewportHostService/**`
|
||||
- 主要源码依据:
|
||||
- `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
- `editor/src/Viewport/ViewportHostRenderFlowUtils.h`
|
||||
- `editor/src/Viewport/ViewportHostService.h`
|
||||
- `tests/editor/test_viewport_render_flow_utils.cpp`
|
||||
- 已关闭问题:
|
||||
- 旧文档仍把 Scene View 当前主路径写成 builtin post-process 主导
|
||||
- 没写清 grid / selection outline 现在已转为显式 `postScenePasses`
|
||||
- 没写清 overlay pass 是 `editorOverlayFrameData + transientOverlayFrameData` 合并后再创建
|
||||
- 没写清 `Scene object id shader view is unavailable` 是局部降级警告,不是整帧失败
|
||||
- 完成记录:
|
||||
- 已重写 `SceneViewportRenderPlan.md`、`BuildSceneViewportRenderPlan.md`、`ApplySceneViewportRenderPlan.md`
|
||||
- 已同步 `ViewportHostRenderFlowUtils.md`、`ViewportHostService.md`、`RenderRequestedViewports.md`
|
||||
- 已清理 `IViewportHostService` 中残留的旧表述
|
||||
|
||||
## T02 Core / Asset 缓存接口改名与语义重构同步
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P0`
|
||||
- 写入范围:
|
||||
- `docs/api/XCEngine/Core/Asset/ResourceManager/**`
|
||||
- `docs/api/XCEngine/Core/Asset/AssetImportService/**`
|
||||
- `docs/api/XCEngine/Core/Asset/ProjectAssetIndex/**`
|
||||
- 必要时 `docs/api/XCEngine/Core/Asset/Asset.md`
|
||||
- 必要时 `docs/api/XCEngine/Core/Asset/ArtifactFormats/**`
|
||||
- 主要源码依据:
|
||||
- `engine/include/XCEngine/Core/Asset/ResourceManager.h`
|
||||
- `engine/src/Core/Asset/ResourceManager.cpp`
|
||||
- `engine/include/XCEngine/Core/Asset/AssetImportService.h`
|
||||
- `engine/src/Core/Asset/AssetImportService.cpp`
|
||||
- `engine/include/XCEngine/Core/Asset/ProjectAssetIndex.h`
|
||||
- `engine/src/Core/Asset/ProjectAssetIndex.cpp`
|
||||
- `tests/core/Asset/test_resource_manager.cpp`
|
||||
- 已关闭问题:
|
||||
- `RefreshAssetDatabase` 已经不存在,但旧文档仍在沿用
|
||||
- `RefreshProjectAssets` / `RebuildProjectAssetCache` / `GetProjectLibraryRoot` 缺页或未写清
|
||||
- `AssetImportService::EnsureArtifact()` 旧文档仍把输出写成 `ResolvedAsset`
|
||||
- `BuildLookupSnapshot()` 旧文档仍按“双 map 出参”描述,而不是 `LookupSnapshot`
|
||||
- `ImportedAsset::runtimeLoadPath` 语义未同步到上层文档
|
||||
- 完成记录:
|
||||
- 已删除过期页 `ResourceManager/RefreshAssetDatabase.md`
|
||||
- 已补齐 `RefreshProjectAssets.md`、`RebuildProjectAssetCache.md`、`GetProjectLibraryRoot.md`
|
||||
- 已补齐 `LookupSnapshot.md`、`ImportedAsset.md`、`GetLibraryRoot.md`、`RebuildLibraryCache.md`
|
||||
- 已同步 `Asset.md`、`ArtifactFormats.md`、`ResourceManager/Load.md` 的 `runtimeLoadPath` 口径
|
||||
|
||||
## T03 Scripting / Mono 托管销毁入口同步
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P1`
|
||||
- 写入范围:
|
||||
- `docs/api/XCEngine/Scripting/Mono/MonoScriptRuntime/**`
|
||||
- 主要源码依据:
|
||||
- `engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h`
|
||||
- `engine/src/Scripting/Mono/MonoScriptRuntime.cpp`
|
||||
- `tests/scripting/test_mono_script_runtime.cpp`
|
||||
- 已关闭问题:
|
||||
- `DestroyManagedObject(MonoObject*)` 已进入头文件与测试,但文档树没有对应页面
|
||||
- `MonoScriptRuntime.md` 没写清 `Object.Destroy(...)` 会回落到原生对象 / 组件销毁
|
||||
- 完成记录:
|
||||
- 已新增 `DestroyManagedObject.md`
|
||||
- 已更新 `MonoScriptRuntime.md` 的 internal call 说明与方法总表
|
||||
|
||||
## T04 Cross-Module / 教程层与模块总览口径持续复核
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P2`
|
||||
- 写入范围:
|
||||
- `docs/api/_guides/**`
|
||||
- `docs/api/XCEngine/*/*.md`
|
||||
- 仅限与本轮已确认 API 变更直接相关的总览页
|
||||
- 任务目标:
|
||||
- 继续检查教程页、模块总览页是否仍在传播旧心智模型
|
||||
- 尤其关注:
|
||||
- Scene View 仍被写成 builtin post-process 主导
|
||||
- 资源导入链仍被写成 `artifactMainPath` / `RefreshAssetDatabase` 时代的口径
|
||||
- 托管对象销毁路径未在教程层被解释
|
||||
- 产出要求:
|
||||
- 只修正已确认失配的 guide / overview 页面
|
||||
- 不做与当前源码无关的泛化扩写
|
||||
|
||||
## T05 增量变更监控 / 新一轮差异发现
|
||||
|
||||
- 状态: `OPEN`
|
||||
- 认领人: ``
|
||||
- 优先级: `P2`
|
||||
- 写入范围:
|
||||
- 只读检查 `engine/include/**`、`engine/src/**`、`editor/src/**`、`tests/**`
|
||||
- 必要时只向本任务池追加新任务块
|
||||
- 任务目标:
|
||||
- 继续结合工作树最新改动,找出新的“源码已变但文档还没跟上”的内容级失配
|
||||
- 优先检查:
|
||||
- 最近改动过的 public headers
|
||||
- 最近改动过的 Editor source headers
|
||||
- 最近新增或更新过的测试
|
||||
- 产出要求:
|
||||
- 只记录已确认的问题
|
||||
- 每条新任务都要写明:
|
||||
- 受影响文档
|
||||
- 主要源码依据
|
||||
- 真实失配点
|
||||
- 建议写入范围
|
||||
|
||||
## T06 Rendering / Camera request、Passes 与执行链旧口径清理
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P1`
|
||||
- 写入范围:
|
||||
- `docs/api/XCEngine/Rendering/CameraRenderRequest/**`
|
||||
- `docs/api/XCEngine/Rendering/CameraRenderer/**`
|
||||
- `docs/api/XCEngine/Rendering/Passes/**`
|
||||
- `docs/api/XCEngine/Rendering/ObjectIdPass/**`
|
||||
- `docs/api/XCEngine/Rendering/RenderPipeline/**`
|
||||
- `docs/api/XCEngine/Rendering/SceneRenderer/**`
|
||||
- `docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/**`
|
||||
- 必要时 `docs/api/XCEngine/XCEngine.md`
|
||||
- 必要时 `docs/api/_tools/audit_api_docs.py`
|
||||
- 主要源码依据:
|
||||
- `engine/include/XCEngine/Rendering/CameraRenderRequest.h`
|
||||
- `engine/include/XCEngine/Rendering/CameraRenderer.h`
|
||||
- `engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h`
|
||||
- `engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h`
|
||||
- `engine/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h`
|
||||
- `engine/include/XCEngine/Rendering/ObjectIdPass.h`
|
||||
- `engine/include/XCEngine/Rendering/RenderPipeline.h`
|
||||
- `engine/include/XCEngine/Rendering/SceneRenderer.h`
|
||||
- `engine/src/Rendering/CameraRenderer.cpp`
|
||||
- `engine/src/Rendering/SceneRenderer.cpp`
|
||||
- `tests/Rendering/unit/test_camera_scene_renderer.cpp`
|
||||
- `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
- `editor/src/Viewport/Passes/SceneViewportGridPass.cpp`
|
||||
- `editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp`
|
||||
- `editor/src/Viewport/ViewportHostRenderFlowUtils.h`
|
||||
- `tests/Editor/test_viewport_render_flow_utils.cpp`
|
||||
- `tests/Editor/test_scene_viewport_overlay_renderer.cpp`
|
||||
- 已关闭问题:
|
||||
- `CameraRenderRequest.md` 仍描述已删除的 `builtinPostProcess` 子请求
|
||||
- 页面仍链接到不存在的 `BuiltinPostProcessRequest/BuiltinPostProcessRequest.md`
|
||||
- Scene View 已改为通过 `postScenePasses` / `overlayPasses` 写回 request,但该页口径未同步
|
||||
- `CameraRenderer.md` 仍保留已删除的 `m_builtinPostProcessBuilder` 历史口径
|
||||
- `Passes.md` 的典型链路只写了 `postScenePasses`,遗漏当前 `overlayPasses` 组装路径
|
||||
- `Rendering/Passes` 下仍残留已删除的 `BuiltinPostProcessPassPlan` / `BuiltinPostProcessPassSequenceBuilder` 页面
|
||||
- `BuiltinObjectIdPass`、`BuiltinInfiniteGridPass` 多个公开入口缺页
|
||||
- `ObjectIdPass.md`、`RenderPipeline.md`、`SceneRenderer.md` 与顶层 `XCEngine.md` 仍在传播 builtin-post-process 心智模型
|
||||
- 完成记录:
|
||||
- 已重写 `CameraRenderRequest.md`
|
||||
- 已清理 `2` 个失效 `.md` 链接
|
||||
- 已同步 `CameraRenderer.md` 与 `Passes.md` 的当前执行链路表述
|
||||
- 已删除 `BuiltinPostProcessPassPlan.md`、`BuiltinPostProcessPassSequenceBuilder.md`
|
||||
- 已补齐 `BuiltinObjectIdPass` / `BuiltinInfiniteGridPass` 缺失页面,并补充 `SceneViewportRenderPlan` 下 grid / selection outline pass factory 页面
|
||||
- 已同步 `ObjectIdPass.md`、`RenderPipeline.md`、`SceneRenderer.md` 与 `XCEngine.md` 的当前口径
|
||||
- 已补充 Rendering guide、`CameraRenderer::Render` 与 `ViewportHostService::RenderRequestedViewports` 的 request 级 pass 注入说明
|
||||
- 已为 `builtinPostProcess` / `BuiltinPostProcessRequest` / `m_builtinPostProcessBuilder` 增加 canonical 过期符号审计
|
||||
- 已复跑结构审计并确认 `Broken .md links: 0`
|
||||
|
||||
## T07 Editor / Core `EditorConsoleSink` 生命周期说明同步
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P1`
|
||||
- 写入范围:
|
||||
- `docs/api/XCEngine/Editor/Core/EditorConsoleSink/**`
|
||||
- 必要时 `docs/api/XCEngine/Editor/Managers/Managers.md`
|
||||
- 必要时 `docs/api/_tools/audit_api_docs.py`
|
||||
- 主要源码依据:
|
||||
- `editor/src/Core/EditorConsoleSink.h`
|
||||
- `editor/src/Core/EditorConsoleSink.cpp`
|
||||
- `editor/src/panels/ConsolePanel.cpp`
|
||||
- `editor/src/Core/EditorLoggingSetup.h`
|
||||
- `tests/Editor/test_editor_console_sink.cpp`
|
||||
- 已关闭问题:
|
||||
- `EditorConsoleSink::GetInstance()` 旧文档仍把它写成会返回 fallback 实例
|
||||
- 相关 overview 页面没有写清活动 sink 销毁后会返回 `nullptr`
|
||||
- 审计脚本此前无法自动拦截这类旧生命周期表述
|
||||
- 完成记录:
|
||||
- 已同步 `EditorConsoleSink.md`、`GetInstance.md`、`Destructor.md`
|
||||
- 已同步 `Managers.md` 中对控制台 sink 生命周期的引用口径
|
||||
- 已为 `fallback 实例` 与“不会返回空指针”旧表述增加定向审计
|
||||
|
||||
## 当前结论
|
||||
|
||||
- 本轮已经关掉五组明确的内容级失配:
|
||||
- `Viewport` 渲染计划与宿主流程
|
||||
- `Core/Asset` 缓存接口与导入服务语义
|
||||
- `MonoScriptRuntime` 托管销毁入口
|
||||
- `Rendering` 相机请求、Passes 与单相机执行链旧口径
|
||||
- `EditorConsoleSink` 生命周期与空指针返回语义
|
||||
- 当前结构审计为全绿。
|
||||
- 当前仍建议保留 `T04` 与 `T05` 作为持续性并行入口,用来承接你后续重构带来的新文档漂移。
|
||||
@@ -259,7 +259,10 @@ C# 脚本模块已经完成了第一阶段的核心闭环,不再是“从 0
|
||||
- 已新增运行时回归用例,验证层级查找、自身命中、组件销毁、GameObject 销毁。
|
||||
- 已通过 `MonoScriptRuntimeTest.*` 与 `ProjectScriptAssemblyTest.*` 相关整组验证。
|
||||
|
||||
- 下一步建议继续做第二批 Unity API 对齐:
|
||||
- 已完成第二步第一小项:`GetComponents<T>()`
|
||||
- 下一步候选:`Object.Instantiate`
|
||||
- 下一步候选:`tag / CompareTag / layer`
|
||||
- 已完成第二步第二小项:`tag / CompareTag / layer`
|
||||
- `GameObject`、`Component` 与 `MonoBehaviour` 已提供 Unity 风格 `tag`、`layer`、`CompareTag(string)` 入口。
|
||||
- native `GameObject`、场景查找、序列化与反序列化已同步收口,`FindGameObjectWithTag` / `FindGameObjectsWithTag` 已改为基于 tag。
|
||||
- 已新增 `TagLayerProbe` 与对应 C++ / Mono 回归测试,覆盖默认 tag、layer 裁剪、脚本侧读写与场景 roundtrip。
|
||||
- 相关定向测试全部通过;完整 `MonoScriptRuntimeTest.*:ProjectScriptAssemblyTest.*` 输出全绿,当前仍存在历史性的 `exit code 3` 异常,需后续单独跟踪。
|
||||
- 下一步建议继续做第三小项:`Object.Instantiate`
|
||||
|
||||
@@ -30,6 +30,9 @@ public:
|
||||
uint64_t GetUUID() const { return m_uuid; }
|
||||
const std::string& GetName() const { return m_name; }
|
||||
void SetName(const std::string& name) { m_name = name; }
|
||||
const std::string& GetTag() const { return m_tag; }
|
||||
void SetTag(const std::string& tag) { m_tag = tag.empty() ? std::string("Untagged") : tag; }
|
||||
bool CompareTag(const std::string& tag) const { return m_tag == tag; }
|
||||
uint8_t GetLayer() const { return m_layer; }
|
||||
void SetLayer(uint8_t layer) { m_layer = std::min<uint8_t>(layer, 31u); }
|
||||
|
||||
@@ -217,6 +220,7 @@ private:
|
||||
ID m_id = INVALID_ID;
|
||||
uint64_t m_uuid = 0;
|
||||
std::string m_name;
|
||||
std::string m_tag = "Untagged";
|
||||
bool m_activeSelf = true;
|
||||
bool m_started = false;
|
||||
uint8_t m_layer = 0;
|
||||
|
||||
@@ -8,7 +8,14 @@ namespace Components {
|
||||
GameObject::ID GameObject::s_nextID = 1;
|
||||
|
||||
GameObject::GameObject()
|
||||
: m_name("GameObject") {
|
||||
: m_name("GameObject")
|
||||
, m_tag("Untagged")
|
||||
, m_activeSelf(true)
|
||||
, m_started(false)
|
||||
, m_layer(0)
|
||||
, m_parent(nullptr)
|
||||
, m_scene(nullptr)
|
||||
, m_transform(nullptr) {
|
||||
m_id = s_nextID++;
|
||||
static std::random_device rd;
|
||||
static std::mt19937_64 gen(rd());
|
||||
@@ -19,7 +26,14 @@ GameObject::GameObject()
|
||||
}
|
||||
|
||||
GameObject::GameObject(const std::string& name)
|
||||
: m_name(name) {
|
||||
: m_name(name)
|
||||
, m_tag("Untagged")
|
||||
, m_activeSelf(true)
|
||||
, m_started(false)
|
||||
, m_layer(0)
|
||||
, m_parent(nullptr)
|
||||
, m_scene(nullptr)
|
||||
, m_transform(nullptr) {
|
||||
m_id = s_nextID++;
|
||||
static std::random_device rd;
|
||||
static std::mt19937_64 gen(rd());
|
||||
@@ -212,7 +226,7 @@ std::vector<GameObject*> GameObject::FindGameObjectsWithTag(const std::string& t
|
||||
auto& registry = GetGlobalRegistry();
|
||||
std::vector<GameObject*> result;
|
||||
for (auto& pair : registry) {
|
||||
if (pair.second->GetName() == tag) {
|
||||
if (pair.second->CompareTag(tag)) {
|
||||
result.push_back(pair.second);
|
||||
}
|
||||
}
|
||||
@@ -316,6 +330,7 @@ void GameObject::Destroy() {
|
||||
|
||||
void GameObject::Serialize(std::ostream& os) const {
|
||||
os << "name=" << m_name << ";";
|
||||
os << "tag=" << m_tag << ";";
|
||||
os << "active=" << (m_activeSelf ? "1" : "0") << ";";
|
||||
os << "layer=" << static_cast<uint32_t>(m_layer) << ";";
|
||||
os << "id=" << m_id << ";";
|
||||
@@ -342,6 +357,10 @@ void GameObject::Deserialize(std::istream& is) {
|
||||
|
||||
if (strcmp(key, "name") == 0) {
|
||||
std::getline(is, m_name, ';');
|
||||
} else if (strcmp(key, "tag") == 0) {
|
||||
std::string tag;
|
||||
std::getline(is, tag, ';');
|
||||
SetTag(tag);
|
||||
} else if (strcmp(key, "active") == 0) {
|
||||
char val;
|
||||
is.get(val);
|
||||
|
||||
@@ -31,7 +31,9 @@ struct PendingGameObjectData {
|
||||
GameObject::ID id = GameObject::INVALID_ID;
|
||||
uint64_t uuid = 0;
|
||||
std::string name = "GameObject";
|
||||
std::string tag = "Untagged";
|
||||
bool active = true;
|
||||
uint8_t layer = 0;
|
||||
GameObject::ID parentId = GameObject::INVALID_ID;
|
||||
std::string transformPayload;
|
||||
std::vector<PendingComponentData> components;
|
||||
@@ -75,6 +77,28 @@ std::string UnescapeString(const std::string& value) {
|
||||
return unescaped;
|
||||
}
|
||||
|
||||
GameObject* FindInChildrenByTag(GameObject* parent, const std::string& tag) {
|
||||
if (!parent) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (GameObject* child : parent->GetChildren()) {
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child->CompareTag(tag)) {
|
||||
return child;
|
||||
}
|
||||
|
||||
if (GameObject* found = FindInChildrenByTag(child, tag)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SerializeGameObjectRecursive(std::ostream& os, GameObject* gameObject) {
|
||||
if (!gameObject) {
|
||||
return;
|
||||
@@ -84,7 +108,9 @@ void SerializeGameObjectRecursive(std::ostream& os, GameObject* gameObject) {
|
||||
os << "id=" << gameObject->GetID() << "\n";
|
||||
os << "uuid=" << gameObject->GetUUID() << "\n";
|
||||
os << "name=" << EscapeString(gameObject->GetName()) << "\n";
|
||||
os << "tag=" << EscapeString(gameObject->GetTag()) << "\n";
|
||||
os << "active=" << (gameObject->IsActive() ? 1 : 0) << "\n";
|
||||
os << "layer=" << static_cast<uint32_t>(gameObject->GetLayer()) << "\n";
|
||||
os << "parent=" << (gameObject->GetParent() ? gameObject->GetParent()->GetID() : GameObject::INVALID_ID) << "\n";
|
||||
os << "transform=";
|
||||
gameObject->GetTransform()->Serialize(os);
|
||||
@@ -212,11 +238,11 @@ GameObject* Scene::FindInChildren(GameObject* parent, const std::string& name) c
|
||||
|
||||
GameObject* Scene::FindGameObjectWithTag(const std::string& tag) const {
|
||||
for (auto* go : GetRootGameObjects()) {
|
||||
if (go->GetName() == tag) {
|
||||
if (go->CompareTag(tag)) {
|
||||
return go;
|
||||
}
|
||||
GameObject* found = FindInChildren(go, tag);
|
||||
if (found) {
|
||||
|
||||
if (GameObject* found = FindInChildrenByTag(go, tag)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
@@ -312,8 +338,12 @@ void Scene::DeserializeFromString(const std::string& data) {
|
||||
currentObject->uuid = std::stoull(value);
|
||||
} else if (key == "name") {
|
||||
currentObject->name = UnescapeString(value);
|
||||
} else if (key == "tag") {
|
||||
currentObject->tag = UnescapeString(value);
|
||||
} else if (key == "active") {
|
||||
currentObject->active = (value == "1");
|
||||
} else if (key == "layer") {
|
||||
currentObject->layer = static_cast<uint8_t>(std::min<uint32_t>(std::stoul(value), 31u));
|
||||
} else if (key == "parent") {
|
||||
currentObject->parentId = static_cast<GameObject::ID>(std::stoull(value));
|
||||
} else if (key == "transform") {
|
||||
@@ -340,7 +370,9 @@ void Scene::DeserializeFromString(const std::string& data) {
|
||||
if (pending.uuid != 0) {
|
||||
go->m_uuid = pending.uuid;
|
||||
}
|
||||
go->SetTag(pending.tag);
|
||||
go->m_activeSelf = pending.active;
|
||||
go->SetLayer(pending.layer);
|
||||
go->m_scene = this;
|
||||
|
||||
if (!pending.transformPayload.empty()) {
|
||||
|
||||
@@ -572,6 +572,41 @@ void InternalCall_GameObject_SetName(uint64_t gameObjectUUID, MonoString* name)
|
||||
gameObject->SetName(MonoStringToUtf8(name));
|
||||
}
|
||||
|
||||
MonoString* InternalCall_GameObject_GetTag(uint64_t gameObjectUUID) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
return mono_string_new(
|
||||
mono_domain_get(),
|
||||
gameObject ? gameObject->GetTag().c_str() : "");
|
||||
}
|
||||
|
||||
void InternalCall_GameObject_SetTag(uint64_t gameObjectUUID, MonoString* tag) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
if (!gameObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
gameObject->SetTag(MonoStringToUtf8(tag));
|
||||
}
|
||||
|
||||
mono_bool InternalCall_GameObject_CompareTag(uint64_t gameObjectUUID, MonoString* tag) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
return (gameObject && gameObject->CompareTag(MonoStringToUtf8(tag))) ? 1 : 0;
|
||||
}
|
||||
|
||||
int32_t InternalCall_GameObject_GetLayer(uint64_t gameObjectUUID) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
return gameObject ? static_cast<int32_t>(gameObject->GetLayer()) : 0;
|
||||
}
|
||||
|
||||
void InternalCall_GameObject_SetLayer(uint64_t gameObjectUUID, int32_t layer) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
if (!gameObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
gameObject->SetLayer(static_cast<uint8_t>(std::clamp(layer, 0, 31)));
|
||||
}
|
||||
|
||||
mono_bool InternalCall_GameObject_GetActiveSelf(uint64_t gameObjectUUID) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
return (gameObject && gameObject->IsActive()) ? 1 : 0;
|
||||
@@ -1492,6 +1527,11 @@ void RegisterInternalCalls() {
|
||||
mono_add_internal_call("XCEngine.InternalCalls::Input_GetMouseScrollDelta", reinterpret_cast<const void*>(&InternalCall_Input_GetMouseScrollDelta));
|
||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetName", reinterpret_cast<const void*>(&InternalCall_GameObject_GetName));
|
||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetName", reinterpret_cast<const void*>(&InternalCall_GameObject_SetName));
|
||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetTag", reinterpret_cast<const void*>(&InternalCall_GameObject_GetTag));
|
||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetTag", reinterpret_cast<const void*>(&InternalCall_GameObject_SetTag));
|
||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_CompareTag", reinterpret_cast<const void*>(&InternalCall_GameObject_CompareTag));
|
||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetLayer", reinterpret_cast<const void*>(&InternalCall_GameObject_GetLayer));
|
||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetLayer", reinterpret_cast<const void*>(&InternalCall_GameObject_SetLayer));
|
||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetActiveSelf", reinterpret_cast<const void*>(&InternalCall_GameObject_GetActiveSelf));
|
||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetActiveInHierarchy", reinterpret_cast<const void*>(&InternalCall_GameObject_GetActiveInHierarchy));
|
||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetActive", reinterpret_cast<const void*>(&InternalCall_GameObject_SetActive));
|
||||
|
||||
@@ -110,6 +110,7 @@ set(XCENGINE_GAME_SCRIPT_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/MeshComponentProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/MeshRendererEdgeCaseProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/ObjectApiProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/TagLayerProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/TickLogProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/TransformConversionProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/TransformMotionProbe.cs
|
||||
|
||||
35
managed/GameScripts/TagLayerProbe.cs
Normal file
35
managed/GameScripts/TagLayerProbe.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using XCEngine;
|
||||
|
||||
namespace Gameplay
|
||||
{
|
||||
public sealed class TagLayerProbe : MonoBehaviour
|
||||
{
|
||||
public string ObservedInitialTag = string.Empty;
|
||||
public int ObservedInitialLayer = -1;
|
||||
public bool ObservedInitialCompareTag;
|
||||
public bool ObservedGameObjectRouteMatches;
|
||||
public string ObservedUpdatedTag = string.Empty;
|
||||
public int ObservedUpdatedLayer = -1;
|
||||
public bool ObservedUpdatedCompareTag;
|
||||
public bool ObservedOriginalTagRejected;
|
||||
public bool ObservedEmptyTagRejected;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
ObservedInitialTag = tag;
|
||||
ObservedInitialLayer = layer;
|
||||
ObservedInitialCompareTag = CompareTag("Enemy");
|
||||
ObservedGameObjectRouteMatches = gameObject.tag == tag
|
||||
&& gameObject.layer == layer;
|
||||
|
||||
tag = "Player";
|
||||
layer = 33;
|
||||
|
||||
ObservedUpdatedTag = gameObject.tag;
|
||||
ObservedUpdatedLayer = gameObject.layer;
|
||||
ObservedUpdatedCompareTag = gameObject.CompareTag("Player");
|
||||
ObservedOriginalTagRejected = !CompareTag("Enemy");
|
||||
ObservedEmptyTagRejected = !CompareTag(string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,30 @@ namespace XCEngine
|
||||
public Transform Transform => GameObject.Transform;
|
||||
public Transform transform => Transform;
|
||||
|
||||
public string Tag
|
||||
{
|
||||
get => GameObject.tag;
|
||||
set => GameObject.tag = value;
|
||||
}
|
||||
|
||||
public string tag
|
||||
{
|
||||
get => Tag;
|
||||
set => Tag = value;
|
||||
}
|
||||
|
||||
public int Layer
|
||||
{
|
||||
get => GameObject.layer;
|
||||
set => GameObject.layer = value;
|
||||
}
|
||||
|
||||
public int layer
|
||||
{
|
||||
get => Layer;
|
||||
set => Layer = value;
|
||||
}
|
||||
|
||||
public bool HasComponent<T>() where T : Component
|
||||
{
|
||||
return GameObject.HasComponent<T>();
|
||||
@@ -60,6 +84,11 @@ namespace XCEngine
|
||||
return component != null;
|
||||
}
|
||||
|
||||
public bool CompareTag(string tag)
|
||||
{
|
||||
return GameObject.CompareTag(tag);
|
||||
}
|
||||
|
||||
internal static T Create<T>(ulong gameObjectUUID) where T : Component
|
||||
{
|
||||
return Create(typeof(T), gameObjectUUID) as T;
|
||||
|
||||
@@ -40,6 +40,30 @@ namespace XCEngine
|
||||
set => Name = value;
|
||||
}
|
||||
|
||||
public string Tag
|
||||
{
|
||||
get => InternalCalls.GameObject_GetTag(UUID) ?? string.Empty;
|
||||
set => InternalCalls.GameObject_SetTag(UUID, value ?? string.Empty);
|
||||
}
|
||||
|
||||
public string tag
|
||||
{
|
||||
get => Tag;
|
||||
set => Tag = value;
|
||||
}
|
||||
|
||||
public int Layer
|
||||
{
|
||||
get => InternalCalls.GameObject_GetLayer(UUID);
|
||||
set => InternalCalls.GameObject_SetLayer(UUID, value);
|
||||
}
|
||||
|
||||
public int layer
|
||||
{
|
||||
get => Layer;
|
||||
set => Layer = value;
|
||||
}
|
||||
|
||||
public bool activeSelf => InternalCalls.GameObject_GetActiveSelf(UUID);
|
||||
|
||||
public bool activeInHierarchy => InternalCalls.GameObject_GetActiveInHierarchy(UUID);
|
||||
@@ -54,6 +78,11 @@ namespace XCEngine
|
||||
InternalCalls.GameObject_Destroy(UUID);
|
||||
}
|
||||
|
||||
public bool CompareTag(string tag)
|
||||
{
|
||||
return InternalCalls.GameObject_CompareTag(UUID, tag ?? string.Empty);
|
||||
}
|
||||
|
||||
public Transform Transform => GetComponent<Transform>();
|
||||
public Transform transform => Transform;
|
||||
|
||||
|
||||
@@ -71,6 +71,21 @@ namespace XCEngine
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern void GameObject_SetName(ulong gameObjectUUID, string name);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern string GameObject_GetTag(ulong gameObjectUUID);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern void GameObject_SetTag(ulong gameObjectUUID, string tag);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern bool GameObject_CompareTag(ulong gameObjectUUID, string tag);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern int GameObject_GetLayer(ulong gameObjectUUID);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern void GameObject_SetLayer(ulong gameObjectUUID, int layer);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern bool GameObject_GetActiveSelf(ulong gameObjectUUID);
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ TEST(GameObject_Test, DefaultConstructor_DefaultValues) {
|
||||
GameObject go;
|
||||
|
||||
EXPECT_EQ(go.GetName(), "GameObject");
|
||||
EXPECT_EQ(go.GetTag(), "Untagged");
|
||||
EXPECT_TRUE(go.CompareTag("Untagged"));
|
||||
EXPECT_TRUE(go.IsActive());
|
||||
EXPECT_NE(go.GetID(), GameObject::INVALID_ID);
|
||||
}
|
||||
@@ -68,6 +70,7 @@ TEST(GameObject_Test, NamedConstructor) {
|
||||
|
||||
TEST(GameObject_Test, Layer_GetSetAndSerializeRoundTrip) {
|
||||
GameObject source("LayeredObject");
|
||||
source.SetTag("Player");
|
||||
source.SetLayer(7);
|
||||
|
||||
std::stringstream stream;
|
||||
@@ -76,7 +79,9 @@ TEST(GameObject_Test, Layer_GetSetAndSerializeRoundTrip) {
|
||||
GameObject target;
|
||||
target.Deserialize(stream);
|
||||
|
||||
EXPECT_EQ(source.GetTag(), "Player");
|
||||
EXPECT_EQ(source.GetLayer(), 7u);
|
||||
EXPECT_EQ(target.GetTag(), "Player");
|
||||
EXPECT_EQ(target.GetLayer(), 7u);
|
||||
}
|
||||
|
||||
|
||||
@@ -1828,6 +1828,53 @@ TEST_F(MonoScriptRuntimeTest, GameObjectRuntimeApiCreatesFindsAndDestroysSceneOb
|
||||
EXPECT_EQ(meshRenderer->GetRenderLayer(), 4u);
|
||||
}
|
||||
|
||||
TEST_F(MonoScriptRuntimeTest, GameObjectTagAndLayerApiExposeUnityStylePropertiesAndCompareTag) {
|
||||
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||
host->SetTag("Enemy");
|
||||
host->SetLayer(7);
|
||||
|
||||
ScriptComponent* component = AddScript(host, "Gameplay", "TagLayerProbe");
|
||||
|
||||
engine->OnRuntimeStart(runtimeScene);
|
||||
engine->OnUpdate(0.016f);
|
||||
|
||||
std::string observedInitialTag;
|
||||
int32_t observedInitialLayer = -1;
|
||||
bool observedInitialCompareTag = false;
|
||||
bool observedGameObjectRouteMatches = false;
|
||||
std::string observedUpdatedTag;
|
||||
int32_t observedUpdatedLayer = -1;
|
||||
bool observedUpdatedCompareTag = false;
|
||||
bool observedOriginalTagRejected = false;
|
||||
bool observedEmptyTagRejected = false;
|
||||
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialTag", observedInitialTag));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialLayer", observedInitialLayer));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialCompareTag", observedInitialCompareTag));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedGameObjectRouteMatches", observedGameObjectRouteMatches));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedTag", observedUpdatedTag));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedLayer", observedUpdatedLayer));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedCompareTag", observedUpdatedCompareTag));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedOriginalTagRejected", observedOriginalTagRejected));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEmptyTagRejected", observedEmptyTagRejected));
|
||||
|
||||
EXPECT_EQ(observedInitialTag, "Enemy");
|
||||
EXPECT_EQ(observedInitialLayer, 7);
|
||||
EXPECT_TRUE(observedInitialCompareTag);
|
||||
EXPECT_TRUE(observedGameObjectRouteMatches);
|
||||
EXPECT_EQ(observedUpdatedTag, "Player");
|
||||
EXPECT_EQ(observedUpdatedLayer, 31);
|
||||
EXPECT_TRUE(observedUpdatedCompareTag);
|
||||
EXPECT_TRUE(observedOriginalTagRejected);
|
||||
EXPECT_TRUE(observedEmptyTagRejected);
|
||||
|
||||
EXPECT_EQ(host->GetTag(), "Player");
|
||||
EXPECT_EQ(host->GetLayer(), 31u);
|
||||
EXPECT_EQ(runtimeScene->FindGameObjectWithTag("Player"), host);
|
||||
EXPECT_EQ(runtimeScene->FindGameObjectWithTag("Enemy"), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(MonoScriptRuntimeTest, RuntimeCreatedScriptComponentCreatesManagedInstanceAfterClassAssignment) {
|
||||
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user