From b9cde699cce08f5a6bc23592060f7e6b8ab86243 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sat, 4 Apr 2026 16:51:39 +0800 Subject: [PATCH] docs: sync viewport render flow and builtin pass docs --- .../CreateSceneViewportEditorOverlayPass.md | 27 ++-- .../SceneViewportEditorOverlayPass.md | 57 ++++----- .../SceneViewportGridPass.md | 2 +- .../SceneViewportSelectionOutlinePass.md | 2 +- .../BuildSceneViewportRenderPlan.md | 111 ++++++++++++---- .../SceneViewportRenderPlan.md | 119 +++++++++++++----- .../BeginFrame-And-RequestViewport.md | 2 +- .../ViewportHostService.md | 11 +- .../BuildInfiniteGridParameters.md | 27 ++-- .../BuiltinInfiniteGridPass/Destructor.md | 24 ++-- .../BuiltinInfiniteGridPass/GetShaderPath.md | 26 ++-- .../InfiniteGridParameters.md | 10 +- .../InfiniteGridPassData.md | 16 ++- .../BuiltinInfiniteGridPass/SetShaderPath.md | 34 +++-- .../BuiltinInfiniteGridPass/Shutdown.md | 30 ++--- .../BuiltinObjectIdPass/BuildInputLayout.md | 35 ++++-- .../BuiltinObjectIdPass.md | 3 +- .../Passes/BuiltinObjectIdPass/Destructor.md | 16 ++- .../Passes/BuiltinObjectIdPass/Shutdown.md | 30 +++-- 19 files changed, 377 insertions(+), 205 deletions(-) diff --git a/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportEditorOverlayPass/CreateSceneViewportEditorOverlayPass.md b/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportEditorOverlayPass/CreateSceneViewportEditorOverlayPass.md index 77b7faff..da6288a5 100644 --- a/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportEditorOverlayPass/CreateSceneViewportEditorOverlayPass.md +++ b/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportEditorOverlayPass/CreateSceneViewportEditorOverlayPass.md @@ -23,29 +23,32 @@ std::unique_ptr CreateSceneViewportEditorOverlayPass( 当前工厂函数会返回一个内部 `SceneViewportEditorOverlayPass` 实例。这个 pass: - 持有对 `renderer` 的引用 -- 持有一份按值拷贝/移动进来的 `frameData` +- 按值拷贝一份 `frameData` - `GetName()` 固定返回 `"SceneViewportEditorOverlay"` -- `Execute(...)` 时调用 `renderer.Render(context.renderContext, context.surface, m_frameData)` +- `Execute(...)` 时调用: -因此它本质上是一个很薄的 `RenderPass` 适配层,用来把 Editor overlay renderer 接到 `SceneRenderer` 的 overlay pass 序列里。 +```cpp +renderer.Render(context.renderContext, context.surface, m_frameData) +``` ## 所有权与生命周期 -- 返回值拥有 pass 对象本身 -- `renderer` 不被 pass 拥有,调用方必须保证它在 pass 执行期间仍然有效 -- `frameData` 会进入 pass 对象内部,因此可以安全跨过当前调用栈,等到真正渲染时再消费 +- 返回值拥有 pass 对象本身。 +- `renderer` 不被 pass 拥有;调用方必须保证其在 pass 执行期内有效。 +- `frameData` 会被拷入 pass 内部,因此可以安全跨过当前调用栈。 -## 当前使用位置 +## 当前调用位置 典型调用方是 [ViewportHostService](../../ViewportHostService/ViewportHostService.md): -1. 先合成 cached editor overlay 和 transient gizmo overlay -2. 再调用 `CreateSceneViewportEditorOverlayPass(...)` -3. 把结果挂进 `requests[0].overlayPasses` +1. 先通过 `GetSceneViewEditorOverlayFrameData(...)` 取得当前 canonical frame data。 +2. 再调用 `CreateSceneViewportEditorOverlayPass(...)`。 +3. 把结果挂进 `requests[0].overlayPasses`。 + +当前不会在这里再做“cached overlay + transient gizmo overlay”的二次合成;合成已经发生在 canonical frame data 构建阶段。 ## 相关文档 - [SceneViewportEditorOverlayPass](SceneViewportEditorOverlayPass.md) -- [Render](Render.md) -- [Shutdown](Shutdown.md) - [ViewportHostService](../../ViewportHostService/ViewportHostService.md) +- [SceneViewportEditorOverlayData](../../SceneViewportEditorOverlayData/SceneViewportEditorOverlayData.md) diff --git a/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportEditorOverlayPass/SceneViewportEditorOverlayPass.md b/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportEditorOverlayPass/SceneViewportEditorOverlayPass.md index a7dd5e06..d3a94388 100644 --- a/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportEditorOverlayPass/SceneViewportEditorOverlayPass.md +++ b/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportEditorOverlayPass/SceneViewportEditorOverlayPass.md @@ -6,13 +6,13 @@ **源文件**: `editor/src/Viewport/Passes/SceneViewportEditorOverlayPass.h` -**描述**: Scene View 世界 overlay 的 GPU 渲染器与 pass 工厂,负责把 overlay 线段和图标批次画进场景颜色目标。 +**描述**: Scene View 世界 overlay 的 GPU 渲染器与 pass 工厂,负责把 line、screen triangle 与 sprite 画进 Scene 视口颜色目标。 -## 概述 +## 概览 -`SceneViewportEditorOverlayPass` 这套代码解决的是“如何把 Scene View 的世界辅助元素真正画进 render target”。和 [SceneViewportOverlayRenderer](../../SceneViewportOverlayRenderer/SceneViewportOverlayRenderer.md) 不同,它不是 ImGui 前端 helper,而是 `RenderPass` 级别能力。 +`SceneViewportEditorOverlayPass` 解决的是“如何把 canonical `SceneViewportOverlayFrameData` 真正画到 Scene View render target”。 -当前入口包括两部分: +它不是 ImGui HUD helper,而是 `RenderPass` 级别能力。当前入口包括两部分: - `SceneViewportEditorOverlayPassRenderer` - `CreateSceneViewportEditorOverlayPass(...)` @@ -20,23 +20,26 @@ ## 生命周期与公开入口 - [Render](Render.md) - 把当前帧 `worldLines / screenTriangles / worldSprites` 转成 GPU draw call,并画进 Scene 视口颜色目标。 + 把当前帧 `worldLines / screenTriangles / worldSprites` 转成 GPU draw call。 - [Shutdown](Shutdown.md) - 释放 pipeline、descriptor set、动态图元 buffer 和内置 icon 纹理。 + 释放 pipeline、descriptor、buffer 与内置 icon 纹理。 - [CreateSceneViewportEditorOverlayPass](CreateSceneViewportEditorOverlayPass.md) - 生成一帧使用的 `RenderPass` 包装对象,把 `frameData` 绑定到当前 renderer。 + 生成一帧使用的 `RenderPass` 包装对象。 ## 当前实现行为 按 `SceneViewportEditorOverlayPass.cpp` 的实现: -- `Render(...)` 只在有效 `RenderContext` 且后端类型为 `D3D12` 时可成功初始化。 -- 会分别维护: +- `Render(...)` 只在有效 `RenderContext` 且后端为 `D3D12` 时真正执行。 +- 渲染器内部维护: - line pipeline - sprite pipeline - depth-tested / always-on-top 两套状态 -- 会按需扩容动态 line vertex buffer 与 sprite vertex buffer。 -- sprite 贴图当前只加载两张内置 PNG: +- 会按需扩容: + - line vertex buffer + - screen-triangle vertex buffer + - sprite vertex buffer +- 内置 sprite 纹理当前只有: - `resources/Icons/camera_gizmo.png` - `resources/Icons/main_light_gizmo.png` @@ -44,39 +47,31 @@ 这个 pass 消费的是 [SceneViewportOverlayFrameData](../../SceneViewportEditorOverlayData/SceneViewportEditorOverlayData.md): -- `worldLines` -> 线段批次 -- `worldSprites` -> 图标 sprite 批次 +- `worldLines` -> 世界线段批次 +- `screenTriangles` -> transform gizmo 等屏幕空间三角形批次 +- `worldSprites` -> camera / light 图标批次 -通常由 [ViewportHostService](../../ViewportHostService/ViewportHostService.md) 在 Scene View 渲染请求里注入。 +`handleRecords` 不参与渲染;它们只供 hit tester / resolver 使用。 + +通常由 [ViewportHostService](../../ViewportHostService/ViewportHostService.md) 在 Scene View render plan 中注入。 ## 设计说明 -为什么这部分要走 GPU pass,而不是和 gizmo 一样直接画在 ImGui 上? +当前这层仍然坚持 GPU pass,而不是把所有 overlay 都挪到 ImGui,原因是: - 世界 overlay 需要和场景深度关系一致。 -- 这些内容本质上属于“场景的一部分辅助可视化”,不是纯 UI chrome。 -- 这样能更自然地支持 `DepthTested` 和 `AlwaysOnTop` 两种模式。 - -这是商业引擎常见的分工方式:真正属于 3D 空间的信息放在 render pass,编辑器控件壳子放在 UI 层。 - -## 生命周期与资源管理 - -- `SceneViewportEditorOverlayPassRenderer` 内部持有 pipeline、descriptor pool、sampler、buffer 和 icon texture 资源。 -- [Shutdown](Shutdown.md) / `DestroyResources()` 会释放这些 GPU 资源。 -- [CreateSceneViewportEditorOverlayPass](CreateSceneViewportEditorOverlayPass.md) 只是生成一帧使用的 `RenderPass` 包装对象,不拥有长期资源。 +- transform gizmo 的正式出图也已经并入 canonical frame,可直接在同一 pass 中消费 `screenTriangles`。 +- HUD orientation gizmo 与世界 overlay 职责不同,仍应留在 UI 层。 ## 当前限制 - 当前只支持 `D3D12`。 -- sprite 贴图种类是固定内置集合,不支持开放式资源扩展。 -- 当前实现专门服务于 Editor Scene View,不是通用渲染管线 pass。 +- sprite 种类仍是固定的内置集合。 +- 这是专门服务 Editor Scene View 的 pass,不是通用渲染管线组件。 ## 相关文档 -- [Passes](../Passes.md) -- [Render](Render.md) -- [Shutdown](Shutdown.md) - [CreateSceneViewportEditorOverlayPass](CreateSceneViewportEditorOverlayPass.md) - [SceneViewportEditorOverlayData](../../SceneViewportEditorOverlayData/SceneViewportEditorOverlayData.md) -- [SceneViewportOverlayBuilder](../../SceneViewportOverlayBuilder/SceneViewportOverlayBuilder.md) +- [SceneViewportHudOverlay](../../SceneViewportHudOverlay/SceneViewportHudOverlay.md) - [ViewportHostService](../../ViewportHostService/ViewportHostService.md) diff --git a/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportGridPass/SceneViewportGridPass.md b/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportGridPass/SceneViewportGridPass.md index 00689330..2014840b 100644 --- a/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportGridPass/SceneViewportGridPass.md +++ b/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportGridPass/SceneViewportGridPass.md @@ -32,7 +32,7 @@ - [SceneViewportRenderPlan](../../SceneViewportRenderPlan/SceneViewportRenderPlan.md) 里的 `SceneViewportGridPassFactory` 负责把 `BuildSceneViewportGridPassData(...)` 的结果转成 `RenderPass` - [ViewportHostService](../../ViewportHostService/ViewportHostService.md) 当前把这个 factory 绑定到 `CreateSceneViewportGridPass(m_sceneViewportGridRenderer, data)` -- `tests/editor/test_viewport_render_flow_utils.cpp` 的 `BuildSceneViewportRenderPlanCollectsPostSceneAndOverlayPasses` 覆盖了 grid factory 被调用的路径 +- `tests/Editor/test_viewport_render_flow_utils.cpp` 的 `BuildSceneViewportRenderPlanCollectsPostSceneAndOverlayPasses` 覆盖了 grid factory 被调用的路径 ## 设计含义 diff --git a/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportSelectionOutlinePass/SceneViewportSelectionOutlinePass.md b/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportSelectionOutlinePass/SceneViewportSelectionOutlinePass.md index 035960fc..533d3b9e 100644 --- a/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportSelectionOutlinePass/SceneViewportSelectionOutlinePass.md +++ b/docs/api/XCEngine/Editor/Viewport/Passes/SceneViewportSelectionOutlinePass/SceneViewportSelectionOutlinePass.md @@ -39,7 +39,7 @@ - [SceneViewportRenderPlan](../../SceneViewportRenderPlan/SceneViewportRenderPlan.md) 的 `SceneViewportSelectionOutlinePassFactory` 会在“有选中对象且 `objectIdShaderView` 可用”时创建这个 pass - [ViewportHostService](../../ViewportHostService/ViewportHostService.md) 当前把该 factory 绑定到 `CreateSceneViewportSelectionOutlinePass(m_sceneViewportSelectionOutlineRenderer, ...)` -- `tests/editor/test_viewport_render_flow_utils.cpp` 覆盖了: +- `tests/Editor/test_viewport_render_flow_utils.cpp` 覆盖了: - 选中对象存在时 factory 被调用 - `outlineWidthPixels == 2.0f` - object-id 纹理缺失时返回警告文案 diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/BuildSceneViewportRenderPlan.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/BuildSceneViewportRenderPlan.md index dcea8fe5..ee566078 100644 --- a/docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/BuildSceneViewportRenderPlan.md +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/BuildSceneViewportRenderPlan.md @@ -14,7 +14,6 @@ SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan( const SceneViewportOverlayData& overlay, const std::vector& selectedObjectIds, const SceneViewportOverlayFrameData& editorOverlayFrameData, - const SceneViewportOverlayFrameData& transientOverlayFrameData, const SceneViewportGridPassFactory& gridPassFactory, const SceneViewportSelectionOutlinePassFactory& selectionOutlinePassFactory, const SceneViewportOverlayPassFactory& overlayPassFactory, @@ -23,29 +22,87 @@ SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan( ## 作用 -根据当前 Scene View 的 overlay、选中对象和视口 render targets,构建一份可直接应用到 `CameraRenderRequest` 的 render plan。 +根据当前 Scene View 的相机 overlay 状态、选中对象、render target 可用性以及当前帧 editor overlay 数据,构建一份可直接应用到 `CameraRenderRequest` 的 render plan。 + +## 输入分别承担什么职责 + +| 参数 | 作用 | +|------|------| +| `targets` | 提供 object-id SRV 等 render target 能力信息,决定 selection outline 能否被规划。 | +| `overlay` | 提供 Scene View 相机姿态,供 grid 规划与有效性判断使用。 | +| `selectedObjectIds` | 决定是否需要 selection outline。 | +| `editorOverlayFrameData` | 决定是否需要 editor overlay pass。 | +| `gridPassFactory` | 真正创建 grid pass 的工厂。 | +| `selectionOutlinePassFactory` | 真正创建 selection outline pass 的工厂。 | +| `overlayPassFactory` | 真正创建 editor overlay pass 的工厂。 | +| `debugSelectionMask` | 控制 selection outline 样式中的 debug mask 行为,并影响缺失 object-id SRV 时是否返回 warning。 | ## 当前实现行为 -当前函数会按下面顺序执行: +当前函数按下面顺序执行: -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`。 +1. 初始化 `SceneViewportRenderPlanBuildResult result = {}`。 +2. 若 `overlay.valid == false`,直接返回默认结果。 +3. 调用 [BuildSceneViewportGridPassData](../ViewportHostRenderFlowUtils/BuildSceneViewportGridPassData.md)。 +4. 若 `gridPassData.valid == true` 且 `gridPassFactory` 非空,则创建 grid pass 并加入 `result.plan.postScenePasses`。 +5. 若 `selectedObjectIds` 非空: + - 当 `targets.objectIdShaderView != nullptr` 且 `selectionOutlinePassFactory` 非空时,创建 selection outline pass 并加入 `result.plan.postScenePasses` + - 否则若 `debugSelectionMask == false`,写入 warning `Scene object id shader view is unavailable` +6. 若 `editorOverlayFrameData.HasOverlayPrimitives()` 且 `overlayPassFactory` 非空,则创建 overlay pass。 +7. 若 overlay 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 缺口。 +### `result.plan` + +当前 plan 可能同时包含三种附加语义: + +- 默认 clear-color override +- `postScenePasses` + - grid + - selection outline +- `overlayPasses` + - editor overlay + +### `result.warningStatusText` + +这是“可降级但值得提示”的状态,不代表 Scene View 整体失败。 + +当前最典型的来源是: + +- 选中了对象 +- 需要 selection outline +- 但 `targets.objectIdShaderView == nullptr` +- 且当前不是 debug mask 模式 + +此时 Scene View 仍然可以继续渲染,只是失去 selection outline 能力。 + +## 为什么要把三类 pass 分开规划 + +### Grid + +网格本质上是 Scene View 的空间参考层。它应该紧跟主场景之后,但仍属于“场景空间辅助信息”,所以进入 `postScenePasses`。 + +### Selection Outline + +选中轮廓依赖 object-id 结果,也属于“主场景之后的空间增强”,因此同样进入 `postScenePasses`。 + +### Editor Overlay + +相机 / 灯光图标、gizmo、屏幕辅助图形更接近“最终 HUD / overlay 叠加层”,所以进入 `overlayPasses`。 + +这种划分并不是为了形式好看,而是为了让未来不同 pass 类别可以有不同的执行约束,而不会全部挤在一个线性列表里。 + +## 为什么 `overlay.valid == false` 时直接返回 + +因为当前 grid 规划依赖 Scene View 相机姿态,而 Scene View overlay 的有效性本身就代表“当前 editor camera 数学状态是否成立”。 + +当它无效时: + +- grid 不应硬拼 +- selection outline 和 overlay 也不再值得继续按正常 Scene View 语义规划 + +但这里返回的仍是默认 plan,而不是失败状态;失败处理属于 [ViewportHostRenderFlowUtils](../ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md) 与 `ViewportHostService` 的职责。 ## 测试锚点 @@ -54,9 +111,18 @@ SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan( 这些测试确认了: -- grid、selection outline 和 overlay pass 的规划入口已经拆开 -- overlay frame data 会先合并,再交给 overlay factory -- object-id SRV 缺失时会返回警告而不是直接崩掉整条 Scene View 组装链 +- grid、selection outline、overlay 三条规划入口已经拆开 +- overlay pass 直接消费合成后的 `SceneViewportOverlayFrameData` +- object-id SRV 缺失时会返回 warning,而不是把整个 Scene View 规划链直接打断 + +## 设计说明 + +把工厂回调作为参数传入,而不是在这里直接 new 具体 pass,有两个重要收益: + +- 规划逻辑不依赖具体 renderer 类型,测试更轻 +- `ViewportHostService` 可以自由决定 pass renderer 的生命周期与实现细节 + +这是一种非常实用的商业引擎式写法:数据规划层只描述“需要什么”,资源与执行层决定“具体怎么创建”。 ## 相关文档 @@ -65,4 +131,7 @@ SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan( - [SceneViewportGridPassFactory](SceneViewportGridPassFactory.md) - [SceneViewportSelectionOutlinePassFactory](SceneViewportSelectionOutlinePassFactory.md) - [SceneViewportOverlayPassFactory](SceneViewportOverlayPassFactory.md) -- [ApplySceneViewportRenderPlan](ApplySceneViewportRenderPlan.md) +- [BuildSceneViewportGridPassData](../ViewportHostRenderFlowUtils/BuildSceneViewportGridPassData.md) +- [BuildSceneViewportSelectionOutlineStyle](../ViewportHostRenderFlowUtils/BuildSceneViewportSelectionOutlineStyle.md) +- [ViewportHostRenderFlowUtils](../ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md) +- [Scene View Render Plan And Failure Flow](../../../../_guides/Editor/Scene-Viewport-Render-Plan-And-Failure-Flow.md) diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md index 49a91350..92e6384e 100644 --- a/docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/SceneViewportRenderPlan.md @@ -6,19 +6,26 @@ **源文件**: `editor/src/Viewport/SceneViewportRenderPlan.h` -**描述**: 把 Scene View 一次渲染提交所需的 post-scene pass、overlay pass 和清屏色覆盖收口成可复用的轻量 request plan。 +**描述**: 为 Scene View 这一帧额外规划 post-scene pass、overlay pass 和 clear-color override,并把这些编辑器专属附加项与基础 `CameraRenderRequest` 装配解耦。 ## 概览 -`SceneViewportRenderPlan.h` 处在 [ViewportHostRenderFlowUtils](../ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md) 和 [ViewportHostService](../ViewportHostService/ViewportHostService.md) 之间。 +`SceneViewportRenderPlan.h` 处在 [ViewportHostService](../ViewportHostService/ViewportHostService.md) 的 Scene View 主路径中段。 -它解决的不是“如何渲染 Scene View”,而是“如何把这一帧 Scene View 额外挂到 `CameraRenderRequest` 上的东西组织好”: +它不负责决定“场景里有哪些相机需要渲染”,那是运行时 [SceneRenderer](../../../Rendering/SceneRenderer/SceneRenderer.md) 与 [SceneRenderRequestPlanner](../../../Rendering/SceneRenderRequestPlanner/SceneRenderRequestPlanner.md) 的职责。 -- post-scene pass 序列 -- overlay pass 序列 -- 默认 clear-color override +它负责的是 Scene View 这一层独有的编辑器附加项: -这样 `ViewportHostService` 的 Scene View 路径可以更像“构建 request -> 执行 request”,而不是把所有额外 pass 拼装细节都塞在一个方法里。 +- 在主场景之后还要不要画无限网格 +- 是否需要给当前选中对象做 selection outline +- 是否需要把相机图标、灯光辅助几何、transform gizmo 等 editor overlay 叠上去 +- 是否要用统一的 Scene View 背景清屏色覆盖相机自身 clear 结果 + +这类逻辑如果直接写死在 `ViewportHostService::RenderSceneViewportEntry(...)` 里,会让 service 变成“又要懂主流程,又要懂每种 pass 细节”的大杂糅。当前拆成 render plan 之后,调用链会更清晰: + +1. `ViewportHostService` 负责总调度 +2. `SceneViewportRenderPlan` 负责“本帧额外挂什么” +3. [ViewportHostRenderFlowUtils](../ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md) 负责失败回退、基础 request 接线和成功收尾 ## 公开类型与函数 @@ -26,44 +33,98 @@ |------|------| | [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 与可选警告文案。 | +| [SceneViewportOverlayPassFactory](SceneViewportOverlayPassFactory.md) | 根据合成后的 overlay frame data 生成 editor overlay `RenderPass` 的工厂回调。 | +| [SceneViewportRenderPlanBuildResult](SceneViewportRenderPlanBuildResult.md) | 返回 render plan 与可选 warning 文案。 | | [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`。 | +| [BuildSceneViewportRenderPlan](BuildSceneViewportRenderPlan.md) | 按当前 Scene View 状态生成 render plan。 | +| [ApplySceneViewportRenderPlan](ApplySceneViewportRenderPlan.md) | 把 render plan 应用到 `CameraRenderRequest`。 | + +## `SceneViewportRenderPlan` 结构本身 + +当前结构包含: + +- `postScenePasses` + - 存放 grid、selection outline 这类“仍属于场景空间,但要附加在主场景之后”的 pass +- `overlayPasses` + - 存放 editor overlay 这类 2D/屏幕叠加语义更强的 pass +- `hasClearColorOverride` + - 默认值是 `true` +- `clearColorOverride` + - 默认值是 `(0.27f, 0.27f, 0.27f, 1.0f)` + +这里最容易被忽略的一点是:**默认 plan 就带有 Scene View 背景色覆盖**。这意味着即便本帧没有 grid、没有 selection outline、没有 overlay primitive,Scene View 仍然会维持统一的编辑器背景基调,而不是完全依赖场景相机自己的 clear 配置。 + +这种设计很符合商业引擎编辑器的常见做法:Scene View 是编辑工作台,不是直接照搬运行时相机输出的 Game View。 ## 当前实现行为 -按 `SceneViewportRenderPlan.h` 和 `tests/editor/test_viewport_render_flow_utils.cpp` 的当前实现: +按 `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。 +- grid pass 由 [BuildSceneViewportGridPassData](../ViewportHostRenderFlowUtils/BuildSceneViewportGridPassData.md) + [SceneViewportGridPassFactory](SceneViewportGridPassFactory.md) 决定是否加入 `plan.postScenePasses`。 +- selection outline pass 由 `targets.objectIdShaderView`、`selectedObjectIds` 和 [SceneViewportSelectionOutlinePassFactory](SceneViewportSelectionOutlinePassFactory.md) 决定是否加入 `plan.postScenePasses`。 +- 若选中了对象但缺少 `objectIdShaderView`,且不是 debug mask 模式,则会返回 warning `Scene object id shader view is unavailable`,但不会终止整个 Scene View 渲染。 +- overlay pass 只在 `editorOverlayFrameData.HasOverlayPrimitives()` 为真、factory 非空且 factory 返回非空 pass 时才追加到 `overlayPasses`。 +- [ApplySceneViewportRenderPlan](ApplySceneViewportRenderPlan.md) 会先复用 [ApplySceneViewportRenderRequestSetup](../ViewportHostRenderFlowUtils/ApplySceneViewportRenderRequestSetup.md) 处理 object-id surface 与 `postScenePasses`,再补充 `overlayPasses` 和 clear-color override。 -## 测试锚点 +## 真实调用链 -当前至少有两条直接测试覆盖: +在 `ViewportHostService::RenderSceneViewportEntry(...)` 中,当前真实顺序大致是: -- `BuildSceneViewportRenderPlanCollectsPostSceneAndOverlayPasses` -- `ApplySceneViewportRenderPlanAttachesPlannedPassesAndClearState` +1. `BuildSceneViewportRenderState(...)` +2. `BuildSceneViewportRenderPlan(...)` +3. 若 `warningStatusText != nullptr`,用 `SetViewportStatusIfEmpty(...)` 尝试保留 warning +4. `SceneRenderer::BuildRenderRequests(...)` +5. `ApplySceneViewportRenderPlan(...)` +6. `SceneRenderer::Render(requests)` +7. 成功后 `MarkSceneViewportRenderSuccess(...)` + +这条链很重要,因为它说明 render plan 既不是最上层入口,也不是最终执行器,而是 Scene View 专属附加内容的中间规划层。 + +## 为什么只有 Scene View 需要它 + +Game View 的目标是尽量接近运行时相机结果,所以它直接走 `SceneRenderer`,不进入这条 editor pass 规划链。 + +Scene View 则完全不同: + +- 它需要编辑器私有背景 +- 需要无限网格辅助空间感 +- 需要 object-id 驱动的选中轮廓 +- 需要 world icon / gizmo / overlay 叠加 + +因此 Scene View 需要一份独立 render plan,而 Game View 不需要。 ## 设计说明 -把 Scene View request 组装拆成“构建计划”和“应用计划”两段,有两个现实收益: +### 为什么要拆成“Build + Apply” -- 可以单独测试 plan 的拼装逻辑,而不必把整个 `ViewportHostService` 拉起来。 -- 后续如果 Scene View 再增加 post-scene pass 或 overlay pass,扩写点会集中在这一层 helper,而不是继续膨胀 `RenderSceneViewportEntry(...)`。 +商业引擎里常见的一个成熟做法,是把“决定要画什么”和“把这些东西接到一次具体渲染请求上”拆开。 + +当前这样拆的直接收益有三个: + +- `BuildSceneViewportRenderPlan(...)` 可以被单独测试,不必真的跑 `SceneRenderer` +- `ApplySceneViewportRenderPlan(...)` 可以专注于 request 接线,不混入逻辑判断 +- `ViewportHostService` 的 Scene View 主路径可以保持接近“收集状态 -> 生成 plan -> 生成 request -> 执行 request”的稳定结构 + +### 为什么 warning 不直接中断渲染 + +例如 object-id SRV 缺失时,selection outline 画不出来,但网格、主场景和 overlay 仍然可能完全正常。 + +这里返回 warning 而不是直接失败,体现的是编辑器设计里很重要的“尽量降级、不轻易黑屏”原则。这比把所有问题都当成致命错误更适合日常编辑工作流。 + +## 测试锚点 + +- `BuildSceneViewportRenderPlanCollectsPostSceneAndOverlayPasses` +- `BuildSceneViewportRenderPlanWarnsWhenSelectionOutlineCannotAccessObjectIdTexture` +- `ApplySceneViewportRenderPlanAttachesPlannedPassesAndClearState` ## 相关文档 - [Viewport](../Viewport.md) -- [ViewportHostRenderFlowUtils](../ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md) - [ViewportHostService](../ViewportHostService/ViewportHostService.md) -- [SceneViewportGridPassFactory](SceneViewportGridPassFactory.md) -- [SceneViewportSelectionOutlinePassFactory](SceneViewportSelectionOutlinePassFactory.md) -- [SceneViewportEditorOverlayPass](../Passes/SceneViewportEditorOverlayPass/SceneViewportEditorOverlayPass.md) +- [ViewportHostRenderFlowUtils](../ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md) +- [ApplySceneViewportRenderRequestSetup](../ViewportHostRenderFlowUtils/ApplySceneViewportRenderRequestSetup.md) +- [BuildSceneViewportGridPassData](../ViewportHostRenderFlowUtils/BuildSceneViewportGridPassData.md) +- [BuildSceneViewportSelectionOutlineStyle](../ViewportHostRenderFlowUtils/BuildSceneViewportSelectionOutlineStyle.md) +- [Scene View Render Plan And Failure Flow](../../../../_guides/Editor/Scene-Viewport-Render-Plan-And-Failure-Flow.md) diff --git a/docs/api/XCEngine/Editor/Viewport/ViewportHostService/BeginFrame-And-RequestViewport.md b/docs/api/XCEngine/Editor/Viewport/ViewportHostService/BeginFrame-And-RequestViewport.md index 81b6731a..cca2756e 100644 --- a/docs/api/XCEngine/Editor/Viewport/ViewportHostService/BeginFrame-And-RequestViewport.md +++ b/docs/api/XCEngine/Editor/Viewport/ViewportHostService/BeginFrame-And-RequestViewport.md @@ -27,7 +27,7 @@ EditorViewportFrame RequestViewport( - `requestedThisFrame` - `requestedWidth` - `requestedHeight` -- 同时清空当前帧 Scene View transient transform gizmo overlay 缓存。 +- 同时清空当前帧 Scene View 的 `SceneViewportTransformGizmoOverlayState`,并标记 editor overlay cache 需要按新状态重建。 ### `RequestViewport(...)` diff --git a/docs/api/XCEngine/Editor/Viewport/ViewportHostService/ViewportHostService.md b/docs/api/XCEngine/Editor/Viewport/ViewportHostService/ViewportHostService.md index f9f14874..34fb42a2 100644 --- a/docs/api/XCEngine/Editor/Viewport/ViewportHostService/ViewportHostService.md +++ b/docs/api/XCEngine/Editor/Viewport/ViewportHostService/ViewportHostService.md @@ -81,7 +81,7 @@ 按当前实现,Scene View 的主路径大致是: 1. `RequestViewport(Scene, size)` 标记本帧需要 Scene 视口,并尽量复用或重建 render target。 -2. `SceneViewPanel` 根据当前 selection、工具模式和鼠标状态刷新 gizmo,并先调用 `SetSceneViewTransformGizmoOverlayState(...)`。 +2. `SceneViewPanel` 根据当前 selection、工具模式和鼠标状态刷新 gizmo,并先经由 coordinator 提交 gizmo overlay submission。 3. 命中测试前,面板通过 `GetSceneViewEditorOverlayFrameData(...)` 取到一份已经合成好 scene icon、相机/灯光辅助几何和 gizmo handle 的 frame data。 4. `RenderRequestedViewports(...)` 中为 Scene entry 构建 `SceneViewportRenderPlan`: - object-id surface @@ -134,7 +134,7 @@ ### 当前帧 gizmo state -- `SetSceneViewTransformGizmoOverlayState(...)` 只负责写入当前帧 gizmo state。 +- `SetSceneViewTransformGizmoOverlayState(...)` 只负责写入当前帧 gizmo state;当前 `SceneViewPanel` 是通过 coordinator helper 间接调用它。 - `BeginFrame()` 会清空这份 state,并把 overlay cache 标记为 dirty。 - 因此 transform gizmo overlay 当前是逐帧输入,而不是长期持久缓存。 @@ -154,9 +154,9 @@ m_sceneViewportOverlayBuilder.Build(..., &m_sceneViewTransformGizmoOverlayState) 当前面板和宿主服务的配合顺序大致是: -1. 面板先刷新 gizmo draw data,并调用 `SetSceneViewTransformGizmoOverlayState(...)`。 +1. 面板先刷新 gizmo draw data,并通过 coordinator 提交 gizmo overlay submission。 2. 面板再通过 `GetSceneViewEditorOverlayFrameData(...)` 拿到 hit-test 需要的合成 frame data。 -3. 处理完点击、拖拽和导航后,面板会再次刷新 gizmo,并再次调用 `SetSceneViewTransformGizmoOverlayState(...)`。 +3. 处理完点击、拖拽和导航后,面板会再次刷新 gizmo,并再次通过 coordinator 提交 submission。 4. 后续真正执行 `RenderRequestedViewports(...)` 时,宿主服务会因为 gizmo state 再次变脏而重建 overlay frame data,并把它交给 `SceneViewportEditorOverlayPass`。 这种顺序有两个直接收益: @@ -211,6 +211,8 @@ Game View 更接近运行时逻辑: ## 相关文档 - [IViewportHostService](../IViewportHostService/IViewportHostService.md) +- [ViewportHostRenderFlowUtils](../ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md) +- [SceneViewportRenderPlan](../SceneViewportRenderPlan/SceneViewportRenderPlan.md) - [SceneViewportCameraController](../SceneViewportCameraController/SceneViewportCameraController.md) - [SceneViewportOverlayBuilder](../SceneViewportOverlayBuilder/SceneViewportOverlayBuilder.md) - [SceneViewportOverlayProviders](../SceneViewportOverlayProviders/SceneViewportOverlayProviders.md) @@ -218,5 +220,6 @@ Game View 更接近运行时逻辑: - [SceneViewportGridPass](../Passes/SceneViewportGridPass/SceneViewportGridPass.md) - [SceneViewportSelectionOutlinePass](../Passes/SceneViewportSelectionOutlinePass/SceneViewportSelectionOutlinePass.md) - [SceneViewportEditorOverlayPass](../Passes/SceneViewportEditorOverlayPass/SceneViewportEditorOverlayPass.md) +- [Scene View Render Plan And Failure Flow](../../../../_guides/Editor/Scene-Viewport-Render-Plan-And-Failure-Flow.md) - [SceneView Interaction And Gizmo Model](../../../../_guides/Editor/SceneView-Interaction-And-Gizmo-Model.md) - [SceneViewPanel](../../panels/SceneViewPanel/SceneViewPanel.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/BuildInfiniteGridParameters.md b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/BuildInfiniteGridParameters.md index 40aabba6..625b4f53 100644 --- a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/BuildInfiniteGridParameters.md +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/BuildInfiniteGridParameters.md @@ -1,5 +1,6 @@ # BuildInfiniteGridParameters +根据当前相机姿态推导无限网格的尺度与淡出参数。 ```cpp InfiniteGridParameters BuildInfiniteGridParameters(const InfiniteGridPassData& data); ``` @@ -9,29 +10,31 @@ InfiniteGridParameters BuildInfiniteGridParameters(const InfiniteGridPassData& d 当前实现会: 1. 拒绝 `data.valid == false` 的输入。 -2. 以相机高度为主,计算目标网格间距。 -3. 把目标间距吸附到最近的十进制尺度 `10^n`。 -4. 计算向下一档网格过渡时的 `transitionBlend`。 -5. 结合网格尺度和视线到地面距离,生成 `fadeDistance`。 +2. 基于相机高度与朝向估算视线到地面平面的有效距离。 +3. 以相机高度的一半作为目标 spacing,并把它吸附到最近的十进制尺度 `10^n`。 +4. 计算朝下一档网格密度过渡时的 `transitionBlend`。 +5. 结合基础网格尺度和视线距离生成 `fadeDistance`。 ## 当前特征 -- 水平平移不会改变结果。 -- 相机高度跨过阈值时,`baseScale` 会从 `1 -> 10 -> 100` 这类十进制级别跳变。 -- `orbitDistance` 当前不参与参数推导。 +- `baseScale` 始终落在 `1, 10, 100, ...` 这类十进制尺度上。 +- `transitionBlend` 范围是 `[0, 1]`,并使用平滑插值而不是硬切换。 +- 水平方向平移不会影响结果;主导参数的是相机高度和观察方向。 +- `orbitDistance` 当前不参与推导,即使编辑器控制器的轨道距离过期,也不会改变这份结果。 ## 测试覆盖 `tests/Editor/test_scene_viewport_overlay_renderer.cpp` 当前验证了: -- 间距始终落在十进制尺度上。 -- 水平平移不影响参数。 -- 相机高度升高会扩大网格尺度和淡出距离。 -- `transitionBlend` 在阈值前后平滑过渡。 -- `orbitDistance` 变化不会影响结果。 +- 网格间距始终是十进制尺度。 +- 水平平移不会改变参数。 +- 相机升高会扩大 `baseScale` 和 `fadeDistance`。 +- `transitionBlend` 会在切换阈值前平滑逼近下一档。 +- `orbitDistance` 变化不会影响同一视角下的结果。 ## 相关文档 - [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md) - [InfiniteGridPassData](InfiniteGridPassData.md) - [InfiniteGridParameters](InfiniteGridParameters.md) +- [Render](Render.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/Destructor.md b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/Destructor.md index fbba34fd..04227846 100644 --- a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/Destructor.md +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/Destructor.md @@ -1,28 +1,24 @@ # BuiltinInfiniteGridPass::Destructor -**命名空间**: `XCEngine::Rendering::Passes` - -**类型**: `destructor` - -**头文件**: `XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h` - -## 签名 +销毁 builtin infinite-grid pass 对象。 ```cpp ~BuiltinInfiniteGridPass() = default; ``` -## 作用 - -销毁 `BuiltinInfiniteGridPass` 对象本身。 - ## 当前语义 -- 当前是默认析构函数,不会自动代替 [Shutdown](Shutdown.md) 释放内部 RHI 资源。 -- 如果这个类被长期持有为 renderer 成员,调用方应在销毁前显式执行 `Shutdown()`。 -- 析构不会清空或验证 `m_shaderPath`;路径只影响后续初始化,而不是对象生命期终结。 +- 当前析构函数是默认析构,不会自动代替 [Shutdown](Shutdown.md) 释放内部 GPU 资源。 +- 如果这个对象在销毁前已经初始化过 pipeline layout、pipeline state、constant pool 或 shader handle,调用方应在销毁前显式执行 `Shutdown()`。 +- 析构本身不会清空 `m_shaderPath`;该路径字符串只是普通成员状态,不参与 GPU 资源回收。 + +## 与其他内建路径的差异 + +- 这点与 [BuiltinObjectIdPass](../BuiltinObjectIdPass/Destructor.md) 和 [BuiltinForwardPipeline](../../Pipelines/BuiltinForwardPipeline/Destructor.md) 不同。 +- 后两者的析构函数都会主动兜底调用 `Shutdown()`;`BuiltinInfiniteGridPass` 当前没有这层保护。 ## 相关文档 - [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md) - [Shutdown](Shutdown.md) +- [SetShaderPath](SetShaderPath.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/GetShaderPath.md b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/GetShaderPath.md index ff95e2e2..f1b446c2 100644 --- a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/GetShaderPath.md +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/GetShaderPath.md @@ -1,28 +1,30 @@ # BuiltinInfiniteGridPass::GetShaderPath -**命名空间**: `XCEngine::Rendering::Passes` - -**类型**: `method` - -**头文件**: `XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h` - -## 签名 +返回当前记录的 infinite-grid shader 路径。 ```cpp const Containers::String& GetShaderPath() const; ``` -## 作用 +## 返回值 -返回这个 grid pass 当前记录的 shader 路径。 +- 返回内部 `m_shaderPath` 的常量引用。 ## 当前语义 -- 返回的是内部 `m_shaderPath` 的常量引用。 -- 构造后或调用 [SetShaderPath](SetShaderPath.md) 后,这里反映的就是下一次初始化将使用的路径。 -- 当前值允许为空;空路径本身不是非法状态,但后续资源创建会失败。 +- 这里反映的是“下一次资源初始化会尝试加载哪份 shader”,而不是“当前 GPU 上已经绑定的是哪份 shader”。 +- 构造后或调用 [SetShaderPath](SetShaderPath.md) 之后,这里返回的就是当前配置值。 +- 调用 [Shutdown](Shutdown.md) 不会清空这个路径;shutdown 只销毁已创建的 GPU 资源。 + +## 当前实现边界 + +- 允许返回空路径;空路径本身不会立刻报错。 +- 但一旦后续进入 `Render()` 的资源创建路径,`CreateResources()` 会因为缺少注入 shader path 而失败。 +- 如果 [SetShaderPath](SetShaderPath.md) 改成新路径,当前已创建的资源会先被销毁,后续再按这里返回的新路径重新初始化。 ## 相关文档 +- [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md) - [SetShaderPath](SetShaderPath.md) - [Render](Render.md) +- [Shutdown](Shutdown.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/InfiniteGridParameters.md b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/InfiniteGridParameters.md index 1b9e09e4..0ba23bbe 100644 --- a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/InfiniteGridParameters.md +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/InfiniteGridParameters.md @@ -12,16 +12,18 @@ |------|------| | `valid` | 当前参数是否可用于真正绘制网格。 | | `baseScale` | 当前十进制主网格间距。 | -| `transitionBlend` | 向下一档网格密度过渡的平滑插值。 | +| `transitionBlend` | 向下一档网格密度平滑过渡的插值值。 | | `fadeDistance` | 当前网格淡出距离。 | ## 当前语义 -- `baseScale` 始终是 `10^n` 这一类十进制尺度。 -- `transitionBlend` 范围在 `[0, 1]`。 -- `fadeDistance` 会同时参考网格尺度和视线到地面平面的距离。 +- `baseScale` 始终是 `10^n` 这类十进制尺度,而不是连续浮点间距。 +- `transitionBlend` 设计成 `[0, 1]` 范围内的平滑权重,用于在切换阈值前后混合主/次网格层级。 +- `fadeDistance` 同时考虑网格尺度和观察距离,避免高空观察时网格过早消失。 ## 相关文档 - [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md) +- [InfiniteGridPassData](InfiniteGridPassData.md) - [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md) +- [Render](Render.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/InfiniteGridPassData.md b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/InfiniteGridPassData.md index 60687f00..7792f49b 100644 --- a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/InfiniteGridPassData.md +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/InfiniteGridPassData.md @@ -18,14 +18,22 @@ | `verticalFovDegrees` | 垂直视场角。 | | `nearClipPlane` | 近裁剪面。 | | `farClipPlane` | 远裁剪面。 | -| `orbitDistance` | 当前 orbit 距离;现实现里不参与参数推导。 | +| `orbitDistance` | 当前 orbit 距离;当前实现里不参与网格参数推导。 | -## 当前使用位置 +## 当前语义 -- `editor/src/Viewport/ViewportHostRenderFlowUtils.h` 里的 `BuildSceneViewportGridPassData(...)` 会从 overlay 数据组装它。 -- [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md) 和 [Render](Render.md) 都消费它。 +- 这是 `BuiltinInfiniteGridPass` 的纯输入结构,不拥有任何 GPU 资源。 +- [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md) 和 [Render](Render.md) 都直接消费它。 +- 当前 editor 侧会在 `BuildSceneViewportGridPassData(...)` 路径里把 scene viewport overlay 数据翻译成这份结构。 + +## 当前实现边界 + +- `valid` 是总开关;为 `false` 时,参数推导和渲染都会直接失效。 +- 相机三个基向量默认提供了一组合理初值,但调用方仍应传入当前真实相机姿态,而不是依赖默认值。 ## 相关文档 - [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md) +- [InfiniteGridParameters](InfiniteGridParameters.md) - [BuildInfiniteGridParameters](BuildInfiniteGridParameters.md) +- [Render](Render.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/SetShaderPath.md b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/SetShaderPath.md index 464b5a21..07ca35c7 100644 --- a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/SetShaderPath.md +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/SetShaderPath.md @@ -1,37 +1,33 @@ # BuiltinInfiniteGridPass::SetShaderPath -**命名空间**: `XCEngine::Rendering::Passes` - -**类型**: `method` - -**头文件**: `XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h` - -## 签名 +更新后续初始化要加载的 infinite-grid shader 路径。 ```cpp void SetShaderPath(const Containers::String& shaderPath); ``` -## 作用 +## 参数 -更新这个 grid pass 后续初始化要加载的 shader 路径。 +- `shaderPath` - 新的 shader 资源路径;允许传入空路径。 -## 当前实现行为 +## 当前语义 -当前实现按下面顺序处理: +- 如果新旧路径完全相同,函数会直接返回,不做任何资源操作。 +- 如果路径发生变化,当前实现会先调用内部 `DestroyResources()`,释放当前缓存的 pipeline、descriptor 和 shader handle,再把 `m_shaderPath` 更新为新值。 +- 这意味着改路径会立刻丢弃现有 GPU 资源,但不会在调用点同步重新加载;真正的重新初始化仍要等到下一次 [Render](Render.md)。 -1. 如果新旧路径完全相同,直接返回。 -2. 调用 `DestroyResources()`,释放当前缓存的 pipeline、descriptor 和 shader handle。 -3. 把 `m_shaderPath` 更新为传入值。 +## 当前实现边界 -## 关键语义 - -- 改路径会立刻丢弃当前已创建的 GPU 资源,等待下一次 [Render](Render.md) 重新初始化。 -- 允许传入空路径;但之后第一次创建资源会因为缺少注入路径而失败。 -- 这个方法只改“后续要加载哪份 shader”,不立即触发加载。 +- 传入空路径是允许的,但后续首次进入资源创建路径时会因为缺少注入 shader path 而失败并记录日志。 +- [Shutdown](Shutdown.md) 不会清空这里设置的路径;shutdown 只销毁资源,不撤销配置。 +- 因此这组接口的语义是: + - `SetShaderPath()` 改“未来要用哪份 shader” + - `Render()` 在需要时按当前路径懒加载资源 + - `Shutdown()` 只释放已创建资源 ## 相关文档 +- [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md) - [GetShaderPath](GetShaderPath.md) - [Render](Render.md) - [Shutdown](Shutdown.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/Shutdown.md b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/Shutdown.md index e10d8d52..e1492985 100644 --- a/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/Shutdown.md +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass/Shutdown.md @@ -1,40 +1,34 @@ # BuiltinInfiniteGridPass::Shutdown -**命名空间**: `XCEngine::Rendering::Passes` - -**类型**: `method` - -**头文件**: `XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h` - -## 签名 - +主动释放当前 grid pass 已创建的 GPU 资源。 ```cpp void Shutdown(); ``` -## 作用 +## 行为说明 -释放这个 grid pass 已创建的内部 GPU 资源和当前配置路径对应的 shader handle。 - -## 当前实现行为 - -当前实现直接调用 `DestroyResources()`,并按顺序销毁: +当前实现直接调用内部 `DestroyResources()`,并按顺序销毁: 1. `m_pipelineState` 2. `m_constantSet` 3. `m_constantPool` 4. `m_pipelineLayout` 5. `m_builtinInfiniteGridShader` -6. `m_device` / `m_backendType` 等缓存状态 -## 关键语义 +之后还会把: + +- `m_device` 置空 +- `m_backendType` 复位到默认值 + +## 当前语义 - 对尚未初始化过的对象调用 `Shutdown()` 是安全的。 -- `Shutdown()` 不会清空 `m_shaderPath`;后续仍可重新 [Render](Render.md) 并按当前路径懒创建资源。 -- 默认析构函数不会自动代替它执行清理。 +- `Shutdown()` 不会清空 `m_shaderPath`,因此后续仍可在相同路径上重新进入 [Render](Render.md) 并懒初始化。 +- 当前默认析构函数不会自动替你调用这里,所以长期持有该 pass 的 renderer 需要显式转发 `Shutdown()`。 ## 相关文档 - [BuiltinInfiniteGridPass](BuiltinInfiniteGridPass.md) - [Destructor](Destructor.md) - [Render](Render.md) +- [SetShaderPath](SetShaderPath.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuildInputLayout.md b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuildInputLayout.md index fc404c63..75e45d07 100644 --- a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuildInputLayout.md +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuildInputLayout.md @@ -1,24 +1,43 @@ # BuiltinObjectIdPass::BuildInputLayout +返回 builtin object-id pass 当前使用的静态顶点布局。 + ```cpp static RHI::InputLayoutDesc BuildInputLayout(); ``` -## 行为说明 +## 当前实现返回值 -当前实现只声明一个顶点元素: +当前实现会按 `Resources::StaticMeshVertex` 的字段顺序构造 3 个输入元素: -- `POSITION` -- `semanticIndex = 0` -- `Format::R32G32B32_Float` -- 偏移取自 `Resources::StaticMeshVertex::position` +1. `POSITION` + - `semanticIndex = 0` + - `format = R32G32B32_Float` + - `inputSlot = 0` + - `alignedByteOffset = offsetof(Resources::StaticMeshVertex, position)` +2. `NORMAL` + - `semanticIndex = 0` + - `format = R32G32B32_Float` + - `inputSlot = 0` + - `alignedByteOffset = offsetof(Resources::StaticMeshVertex, normal)` +3. `TEXCOORD` + - `semanticIndex = 0` + - `format = R32G32_Float` + - `inputSlot = 0` + - `alignedByteOffset = offsetof(Resources::StaticMeshVertex, uv0)` ## 当前语义 -- object-id pass 只依赖顶点位置,不读取法线、UV、切线或颜色。 -- 这和当前 shader 语义一致,因为它只需要做几何覆盖并输出 object-id 颜色。 +- 这不是“只包含位置”的极简布局;当前 object-id pass 仍然沿用静态网格常用的 `POSITION / NORMAL / TEXCOORD0` 输入契约。 +- 这样构建出来的 pipeline 能直接消费 `RenderResourceCache` 上传的 `StaticMeshVertex` 顶点流,而不需要为 object-id 路径单独准备另一套顶点缓冲描述。 +- 资源绑定层面上,object-id pass 只要求 `PerObject` 常量缓冲;这和顶点布局是两个独立维度。 + +## 测试覆盖 + +- `tests/Rendering/unit/test_builtin_forward_pipeline.cpp` 显式验证了这里返回的 3 个输入元素及其 offset。 ## 相关文档 - [BuiltinObjectIdPass](BuiltinObjectIdPass.md) - [Render](Render.md) +- [ObjectIdEncoding](../../ObjectIdEncoding/ObjectIdEncoding.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuiltinObjectIdPass.md b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuiltinObjectIdPass.md index 09d522d0..37927262 100644 --- a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuiltinObjectIdPass.md +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/BuiltinObjectIdPass.md @@ -29,9 +29,10 @@ ## 关键实现细节 -- 当前 input layout 只声明了 `POSITION`,依赖 mesh 顶点位置即可完成 object-id 绘制。 +- 当前 input layout 沿用 `Resources::StaticMeshVertex` 的 `POSITION / NORMAL / TEXCOORD0` 三元素布局,而不是单独定义一套 object-id 专用顶点流。 - 每个 object ID 会缓存一套独立 descriptor set,避免重复分配常量绑定。 - `RenderResourceCache` 负责把 `Mesh` 上传或复用成 GPU 资源。 +- 资源声明会优先读取 shader pass 自己的 `resources`,若为空则回退到 [BuildLegacyBuiltinObjectIdPassResourceBindings](../../RenderMaterialUtility/BuildLegacyBuiltinObjectIdPassResourceBindings.md),再统一交给 [TryBuildBuiltinPassResourceBindingPlan](../../RenderMaterialUtility/TryBuildBuiltinPassResourceBindingPlan.md) 解析。 - shader pass 解析优先匹配 builtin object-id pass tag,其次回退到 `ObjectId` / `EditorObjectId` 命名,最后才退到首个 pass。 ## 当前实现边界 diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/Destructor.md b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/Destructor.md index 7f12ceab..c4ea8e1e 100644 --- a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/Destructor.md +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/Destructor.md @@ -1,20 +1,24 @@ # BuiltinObjectIdPass::~BuiltinObjectIdPass +销毁 builtin object-id pass。 + ```cpp ~BuiltinObjectIdPass() override; ``` -## 行为说明 +## 当前语义 -当前析构函数只有一个动作: +- 当前析构函数会直接调用 [Shutdown](Shutdown.md)。 +- 因此对象销毁前会释放 pipeline state、pipeline layout、per-object descriptor set/pool、builtin shader handle,以及内部 [RenderResourceCache](../../RenderResourceCache/RenderResourceCache.md)。 +- 之前按 object id 缓存的 descriptor set 也会在这一步一起清掉。 -```cpp -Shutdown(); -``` +## 调用方影响 -因此它会在对象销毁前显式释放当前缓存的 pipeline state、pipeline layout、每对象 descriptor set、shader handle 和 `RenderResourceCache`。 +- 即使调用方忘记先手动 `Shutdown()`,object-id pass 在析构时也会补做资源回收。 +- 任何此前观察到的内部 descriptor set、pipeline state 或缓存 mesh 指针,在析构后都不再有效。 ## 相关文档 - [BuiltinObjectIdPass](BuiltinObjectIdPass.md) - [Shutdown](Shutdown.md) +- [Render](Render.md) diff --git a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/Shutdown.md b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/Shutdown.md index bb068d6d..ffeac087 100644 --- a/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/Shutdown.md +++ b/docs/api/XCEngine/Rendering/Passes/BuiltinObjectIdPass/Shutdown.md @@ -1,20 +1,36 @@ # BuiltinObjectIdPass::Shutdown +主动销毁当前缓存的 object-id pass GPU 资源。 + ```cpp void Shutdown() override; ``` -## 行为说明 +## 当前语义 -当前实现直接转调内部 `DestroyResources()`,并释放: +- 当前实现直接转发到内部 `DestroyResources()`。 +- 它会依次清理: + - 内部 [RenderResourceCache](../../RenderResourceCache/RenderResourceCache.md) + - `m_perObjectSets` 中按 object ID 缓存的 descriptor pool / descriptor set + - `m_pipelineState` + - `m_pipelineLayout` + - `m_builtinObjectIdShader` +- 同时还会把 `m_device`、`m_backendType`、`m_perObjectBinding`、`m_firstDescriptorSet` 等运行时状态复位。 -- `RenderResourceCache` -- 每对象 descriptor pool / set -- pipeline state -- pipeline layout -- builtin object-id shader handle +## 调用后效果 + +- 这是一次“回到未初始化状态”的 teardown。 +- 下一次 [Render](Render.md) 再进入时,会重新走 `EnsureInitialized(...) -> CreateResources(...)` 路径。 +- 当前实现支持重复调用;资源已经清空后,后续 `Shutdown()` 只会处理空状态。 + +## 调用方影响 + +- 任何此前由该 pass 间接缓存的 mesh GPU 副本、pipeline state 或 per-object descriptor set,都会在这里失效。 +- 这不会销毁 `RenderSurface`、`RenderSceneData` 或调用方持有的 scene / mesh / material 对象本体。 ## 相关文档 - [BuiltinObjectIdPass](BuiltinObjectIdPass.md) - [Destructor](Destructor.md) +- [Render](Render.md) +- [RenderResourceCache](../../RenderResourceCache/RenderResourceCache.md)