docs: expand render request planning docs

This commit is contained in:
2026-04-03 14:09:36 +08:00
parent e4a4e90592
commit ed95aa1dc5
10 changed files with 405 additions and 2 deletions

View File

@@ -0,0 +1,40 @@
# 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)

View File

@@ -0,0 +1,32 @@
# 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 必须显式提供选中对象 IDoutline / debug mask 路径才会进入 builder。
## 相关文档
- [返回类型总览](BuiltinPostProcessRequest.md)
- [CameraRenderRequest](../CameraRenderRequest.md)
- [BuiltinPostProcessPassSequenceBuilder](../../Passes/BuiltinPostProcessPassSequenceBuilder/BuiltinPostProcessPassSequenceBuilder.md)

View File

@@ -40,7 +40,7 @@
### `objectId` ### `objectId`
`objectId` 是一个 `ObjectIdRenderRequest`,用于描述“是否要额外渲染一张 object-id 辅助纹理”。 `objectId` 是一个 [ObjectIdRenderRequest](ObjectIdRenderRequest/ObjectIdRenderRequest.md),用于描述“是否要额外渲染一张 object-id 辅助纹理”。
- `IsRequested()` 只检查 `surface` 是否挂了颜色附件。 - `IsRequested()` 只检查 `surface` 是否挂了颜色附件。
- `IsValid()` 进一步要求: - `IsValid()` 进一步要求:
@@ -52,7 +52,7 @@
### `builtinPostProcess` ### `builtinPostProcess`
`builtinPostProcess` 是一个 `BuiltinPostProcessRequest`,用于描述 scene viewport 一类内建附加效果: `builtinPostProcess` 是一个 [BuiltinPostProcessRequest](BuiltinPostProcessRequest/BuiltinPostProcessRequest.md),用于描述 scene viewport 一类内建附加效果:
- `gridPassData` 控制无限网格 - `gridPassData` 控制无限网格
- `objectIdTextureView` 把 object-id 纹理作为 SRV 传给后处理 - `objectIdTextureView` 把 object-id 纹理作为 SRV 传给后处理
@@ -79,6 +79,8 @@
## 相关文档 ## 相关文档
- [IsValid](IsValid.md) - [IsValid](IsValid.md)
- [ObjectIdRenderRequest](ObjectIdRenderRequest/ObjectIdRenderRequest.md)
- [BuiltinPostProcessRequest](BuiltinPostProcessRequest/BuiltinPostProcessRequest.md)
- [CameraRenderer](../CameraRenderer/CameraRenderer.md) - [CameraRenderer](../CameraRenderer/CameraRenderer.md)
- [SceneRenderer](../SceneRenderer/SceneRenderer.md) - [SceneRenderer](../SceneRenderer/SceneRenderer.md)
- [RenderContext](../RenderContext/RenderContext.md) - [RenderContext](../RenderContext/RenderContext.md)

View File

@@ -0,0 +1,31 @@
# ObjectIdRenderRequest::IsRequested
检查当前是否请求了 object-id 辅助输出。
```cpp
bool IsRequested() const;
```
## 行为说明
当前头文件内联实现只有一条判断:
```cpp
return !surface.GetColorAttachments().empty();
```
## 返回值
- 只要 `surface` 上至少挂了一个颜色附件,就返回 `true`
- 否则返回 `false`
## 注意事项
- 这不是完整性校验;颜色附件数组非空并不代表第一个附件非空,也不代表深度附件和 render area 合法。
- 真正更严格的检查在 [IsValid](IsValid.md)。
## 相关文档
- [返回类型总览](ObjectIdRenderRequest.md)
- [IsValid](IsValid.md)
- [CameraRenderRequest](../CameraRenderRequest.md)

View File

@@ -0,0 +1,36 @@
# ObjectIdRenderRequest::IsValid
检查当前 object-id 输出目标是否满足执行 pass 的最小条件。
```cpp
bool IsValid() const;
```
## 行为说明
当前头文件内联实现要求同时满足:
```cpp
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
return !colorAttachments.empty() &&
colorAttachments[0] != nullptr &&
surface.GetDepthAttachment() != nullptr &&
surface.GetRenderAreaWidth() > 0 &&
surface.GetRenderAreaHeight() > 0;
```
## 返回值
- 第一个颜色附件非空、深度附件非空且 render area 宽高都大于 `0` 时返回 `true`
- 否则返回 `false`
## 与 `CameraRenderer::Render()` 的关系
- `CameraRenderer::Render()` 会先看 [IsRequested](IsRequested.md)。
- 如果请求了 object-id 输出,但这里返回 `false`,整次相机渲染会直接失败。
## 相关文档
- [返回类型总览](ObjectIdRenderRequest.md)
- [IsRequested](IsRequested.md)
- [CameraRenderer::Render](../../CameraRenderer/Render.md)

View File

@@ -0,0 +1,37 @@
# ObjectIdRenderRequest
**命名空间**: `XCEngine::Rendering`
**类型**: `struct`
**头文件**: `XCEngine/Rendering/CameraRenderRequest.h`
**描述**: 描述一份 camera request 是否需要额外输出 object-id 纹理,以及这份输出目标是否完整可用。
## 概述
`ObjectIdRenderRequest` 是 [CameraRenderRequest](../CameraRenderRequest.md) 里的辅助子请求用来把“主场景颜色输出”和“object-id 辅助输出”拆开描述。
它当前只持有一个字段:
| 字段 | 说明 |
|------|------|
| `surface` | object-id pass 要写入的目标表面,通常是单独的颜色附件 + 深度附件组合。 |
## 关键语义
- [IsRequested](IsRequested.md) 只回答“调用方有没有挂一个颜色附件过来”。
- [IsValid](IsValid.md) 才回答“这份 object-id 输出目标是否足够完整,可以真正执行 pass”。
- `CameraRenderer::Render()` 会在主场景之后检查并消费这个子请求。
## 真实使用位置
- 编辑器 scene viewport 会准备一张单独的 object-id render target并把它装进 `request.objectId.surface`
- `CameraRenderer::Render()``request.objectId.IsRequested()` 为真时,调用当前的 `ObjectIdPass` 执行辅助输出。
## 相关文档
- [CameraRenderRequest](../CameraRenderRequest.md)
- [IsRequested](IsRequested.md)
- [IsValid](IsValid.md)
- [CameraRenderer](../../CameraRenderer/CameraRenderer.md)

View File

@@ -0,0 +1,38 @@
# SceneRenderRequestPlanner::BuildRequests
```cpp
std::vector<CameraRenderRequest> BuildRequests(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera,
const RenderContext& context,
const RenderSurface& surface) const;
```
## 行为说明
当前实现先调用 [CollectCameras](CollectCameras.md) 取得这次需要处理的相机列表,然后逐个调用 `SceneRenderRequestUtils::BuildCameraRenderRequest(...)` 生成请求。
构建过程中会维护两类计数:
- `renderedBaseCameraCount`
- 已成功加入结果数组的 request 数量
这两个计数会传给 `ResolveClearFlags()`,用于推导 `Auto` clear mode 的最终清屏行为。
## 当前过滤规则
- 如果某台相机最终生成的 request render area 宽高为 `0`,该请求会被跳过。
- 只有真正成功加入结果数组的 base camera才会推进 `renderedBaseCameraCount`
- 方法不会抛出失败原因;调用方只会拿到最终保留下来的请求数组。
## 返回值
- 一组已经完成基础字段填充的 [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md)。
- 结果可能为空,例如没有可用相机,或所有相机的 render area 都被裁成了零尺寸。
## 相关文档
- [SceneRenderRequestPlanner](SceneRenderRequestPlanner.md)
- [CollectCameras](CollectCameras.md)
- [SceneRenderRequestUtils](../SceneRenderRequestUtils/SceneRenderRequestUtils.md)
- [SceneRenderer::BuildRenderRequests](../SceneRenderer/BuildRenderRequests.md)

View File

@@ -0,0 +1,33 @@
# SceneRenderRequestPlanner::CollectCameras
```cpp
std::vector<Components::CameraComponent*> CollectCameras(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera) const;
```
## 行为说明
当前实现按下面的顺序决定这次真正参与渲染规划的相机列表:
1. 如果 `overrideCamera` 通过 [SceneRenderRequestUtils](../SceneRenderRequestUtils/SceneRenderRequestUtils.md) 的 `IsUsableCamera()` 检查,则只返回这一台相机。
2. 否则从 `scene` 里收集全部 `CameraComponent`
3. 过滤掉不可用相机。
4. 调用 `SceneRenderRequestUtils::SortSceneCamerasForRendering(...)` 做稳定排序。
## 返回值
- 一个已经过滤并排序好的相机数组。
- 如果没有任何可用相机,返回空数组。
## 当前排序语义
- `base` 相机会排在 `overlay` 前面。
- 同一 stack 类型内部再按 `depth` 升序。
- 排序使用 `std::stable_sort()`,所以完全同键时仍保留场景原始枚举顺序。
## 相关文档
- [SceneRenderRequestPlanner](SceneRenderRequestPlanner.md)
- [BuildRequests](BuildRequests.md)
- [SceneRenderRequestUtils](../SceneRenderRequestUtils/SceneRenderRequestUtils.md)

View File

@@ -0,0 +1,65 @@
# SceneRenderRequestPlanner
**命名空间**: `XCEngine::Rendering`
**类型**: `class`
**头文件**: `XCEngine/Rendering/SceneRenderRequestPlanner.h`
**描述**: 负责从场景和可选 override camera 生成实际要提交给 `CameraRenderer` 的相机请求列表。
## 概览
`SceneRenderRequestPlanner` 处在 [SceneRenderer](../SceneRenderer/SceneRenderer.md) 和 [SceneRenderRequestUtils](../SceneRenderRequestUtils/SceneRenderRequestUtils.md) 之间。
它不亲自渲染任何东西,而是把“应该渲染哪些相机、按什么顺序、哪些请求应该被丢弃”整理成一组 [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md)。
## 公开方法
| 方法 | 说明 |
|------|------|
| [CollectCameras](CollectCameras.md) | 收集本次应参与渲染的相机列表。 |
| [BuildRequests](BuildRequests.md) | 把相机列表展开成可执行的 `CameraRenderRequest` 数组。 |
## 当前规划规则
### `CollectCameras()`
当前规则是:
1. 如果 `overrideCamera` 可用,则只渲染它自己。
2. 否则从场景里查找全部 `CameraComponent`
3. 过滤掉不可用相机。
4. 按 [SceneRenderRequestUtils](../SceneRenderRequestUtils/SceneRenderRequestUtils.md) 里的稳定排序规则整理顺序。
### `BuildRequests()`
它会按相机顺序逐个调用 `BuildCameraRenderRequest()`,并维护:
- `renderedBaseCameraCount`
- 已成功生成的 request 数量
这两个计数会影响自动 clear flag 的推导。
如果某个相机最终解析出的 render area 宽高为 `0`,该请求会被直接丢弃,而且不会错误地推进 base camera 计数。
## 真实使用位置
- `engine/include/XCEngine/Rendering/SceneRenderer.h` 持有一个 `SceneRenderRequestPlanner` 实例。
- `engine/src/Rendering/SceneRenderRequestPlanner.cpp` 实现具体收集与构建逻辑。
- `tests/Rendering/unit/test_scene_render_request_planner.cpp` 覆盖了 override camera、稳定排序和零尺寸 viewport 的行为。
## 当前实现边界
- 它只规划相机请求,不处理 object-id 或 builtin post-process 的具体填充;那部分通常由更上层调用方补充。
- 当前没有 camera stacking 的更复杂依赖分析,只有 base / overlay 与 depth 的线性排序。
- `BuildRequests()` 只返回成功构建的请求,不单独报告被过滤掉的相机原因。
## 相关文档
- [当前模块](../Rendering.md)
- [CollectCameras](CollectCameras.md)
- [BuildRequests](BuildRequests.md)
- [SceneRenderer](../SceneRenderer/SceneRenderer.md)
- [SceneRenderRequestUtils](../SceneRenderRequestUtils/SceneRenderRequestUtils.md)
- [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md)

View File

@@ -0,0 +1,89 @@
# SceneRenderRequestUtils
**命名空间**: `XCEngine::Rendering::SceneRenderRequestUtils`
**类型**: `utility header`
**头文件**: `XCEngine/Rendering/SceneRenderRequestUtils.h`
**描述**: 提供相机可用性判断、稳定排序、clear flag 推导、render area 计算和 request 组装等内联 helper。
## 概览
`SceneRenderRequestUtils.h` 是当前相机请求规划链路的规则库。
和 [SceneRenderRequestPlanner](../SceneRenderRequestPlanner/SceneRenderRequestPlanner.md) 的职责分工是:
- planner 决定“要处理哪些相机”
- utils 决定“这些相机和请求该怎样排序、怎样计算 clear、怎样落成具体 request”
## 公开规则
### 相机可用性
`IsUsableCamera()` 只接受:
- 非空相机
- 组件启用
- 挂载 `GameObject` 非空
- 对象处于 `IsActiveInHierarchy()`
### 排序
`SortSceneCamerasForRendering()``SortCameraRenderRequests()` 都使用 `std::stable_sort()`,因此在排序键完全相同的情况下,会保留原始相对顺序。
当前排序键分别是:
- 场景相机: `stackType` -> `depth`
- 渲染请求: `cameraStackOrder` -> `cameraDepth`
### clear flag 推导
`ResolveClearFlags()` 的规则是:
- `ColorAndDepth` -> `RenderClearFlags::All`
- `DepthOnly` -> `RenderClearFlags::Depth`
- `None` -> `RenderClearFlags::None`
- `Auto`
- overlay camera: 本次若还是首个已渲染 request则清 `All`,否则只清 `Depth`
- base camera: 若还没有任何成功渲染的 base camera则清 `All`,否则只清 `Depth`
### render area 计算
`ResolveCameraRenderArea()` 会把相机 `viewportRect` 解释为父 `RenderSurface` 当前 render area 的归一化子矩形:
- 左上边界使用 `floor`
- 右下边界使用 `ceil`
### request 组装
`BuildCameraRenderRequest()` 当前会:
- 清空输出 request
- 绑定 `scene``camera``context`
- 复制输入 `surface`,再把 render area 改成相机子区域
- 写入 `cameraDepth``cameraStackOrder``clearFlags`
- 只有 render area 宽高都大于 `0` 时才返回 `true`
## 测试验证的真实行为
`tests/Rendering/unit/test_scene_render_request_utils.cpp` 已覆盖:
- 空指针 / 禁用 / 非激活相机都会被拒绝
- 场景相机与 request 排序都保持稳定 tie 顺序
- `Auto` clear mode 在 base / overlay 下的回退行为
- 嵌套 render area 的矩形组合规则
- 零尺寸 viewport 会导致 request 构建失败
## 当前实现边界
- 这些 helper 是纯局部规则,不处理多表面、多窗口或跨帧状态。
- clear 推导只依赖当前计数,不关心更复杂的 camera stack 依赖关系。
- request 组装阶段只填基础字段object-id 与 builtin post-process 仍需上层补全。
## 相关文档
- [当前模块](../Rendering.md)
- [SceneRenderRequestPlanner](../SceneRenderRequestPlanner/SceneRenderRequestPlanner.md)
- [CameraRenderRequest](../CameraRenderRequest/CameraRenderRequest.md)
- [SceneRenderer](../SceneRenderer/SceneRenderer.md)