Files
XCEngine/docs/api/_guides/Editor/Scene-Viewport-Render-Plan-And-Failure-Flow.md

6.7 KiB
Raw Blame History

Scene View Render Plan And Failure Flow

先建立正确心智模型

当前 Scene View 不是“把活动场景直接交给 SceneRenderer”这么简单。

它真实的工作方式更接近商业引擎编辑器常见的两层结构:

  1. 运行时渲染层
    • 负责把场景和相机变成基础 CameraRenderRequest
  2. 编辑器附加层
    • 负责网格、selection outline、editor overlay、object-id 与状态文案

SceneViewportRenderPlan 正是第二层里的一个关键中间件,而当前 ViewportHostService 与它之间还多了一层 SceneViewportRenderPassBundle,专门负责持有 grid / outline / overlay renderer 并提供 factory。

当前主调用链

ViewportHostService.h 当前实现Scene View 一帧渲染大致是:

  1. RenderRequestedViewports(...) 遍历本帧请求过的视口。
  2. RenderViewportEntry(...) 检查 colorView / depthView 是否存在。
  3. 如果是 Scene 视口,进入 RenderSceneViewportEntry(...)
  4. 检查隐藏编辑器 Scene View 相机是否可用。
  5. 检查活动场景是否存在。
  6. BuildSceneViewportRenderState(...)
    • 读取 SceneViewportOverlayData
    • 读取当前选中对象列表
    • 读取 SceneViewportOverlayFrameData
    • 调用 m_sceneViewportRenderPassBundle.BuildRenderPlan(...)
    • bundle 内部再调用 BuildSceneViewportRenderPlan(...)
  7. SceneRenderer::BuildRenderRequests(...) 生成基础 request。
  8. ApplySceneViewportRenderPlan(...) 把 editor 附加项写回 request。
  9. SceneRenderer::Render(requests) 执行真正的场景渲染。
  10. MarkSceneViewportRenderSuccess(...) 回写颜色状态、object-id 状态和有效标记。

SceneViewportRenderPlan 到底负责什么

它当前只负责三类附加项:

  • postScenePasses
    • 无限网格
    • selection outline
  • overlayPasses
    • editor overlay
  • clear-color override
    • 默认 Scene View 背景色是 (0.27, 0.27, 0.27, 1.0)

这意味着它不是 Scene View 的全部流程,只是“把 Scene View 专属附加能力组织成一个可应用的 plan”。

为什么要有默认 clear-color override

因为 Scene View 的定位不是游戏运行结果,而是编辑工作台。

如果完全复用场景相机自己的 clear 语义,编辑体验会出现两个问题:

  • 背景风格可能随场景内容波动,不稳定
  • 编辑器 overlay、gizmo、网格失去统一视觉基底

当前默认灰色背景,是典型的编辑器视图设计:优先保证工作环境一致性,再叠加运行时内容。

为什么 warning 不直接终止渲染

一个很典型的例子是:

  • 选中了对象
  • 需要 selection outline
  • objectIdShaderView 不可用

这时当前实现不会直接让 Scene View 失败,而是:

  • 返回 warning Scene object id shader view is unavailable
  • 继续保留网格、主场景和 overlay 的正常渲染

这是编辑器设计里非常关键的原则:局部能力缺口优先降级,而不是整页黑屏

失败回退是怎么接进来的

Scene View 的失败回退不在 SceneViewportRenderPlan 自己内部处理,而是交给:

其中最关键的策略包括:

  • BuildSceneViewportRenderFailurePolicy(...)
  • ApplyViewportFailureStatus(...)
  • InvalidateViewportObjectIdFrame(...)
  • MarkSceneViewportRenderSuccess(...)

也就是说:

  • render plan 负责“本帧附加画什么”
  • flow utils 负责“失败时怎么退、成功后怎么收尾”

这样的分工会让 Scene View 主链更稳。

为什么要把 Build 和 Apply 分开

如果把 plan 构建和 request 写回混成一个函数,短期看代码会少一些,但长期会带来三个问题:

  1. 很难单独测试规划逻辑
  2. 很容易在复用 CameraRenderRequest 时留下脏指针
  3. ViewportHostService 会重新变成一大段条件拼装代码

当前拆成:

  • BuildSceneViewportRenderPlan(...)
  • ApplySceneViewportRenderPlan(...)

本质上是在把“决策”和“接线”分离。这种拆法在商业引擎里很常见,因为它更容易维护,也更适合持续往 Scene View 里加新能力。

Game View 为什么不用这套 plan

Game View 的目标是尽量忠实呈现运行时相机结果,所以它没有:

  • editor 私有背景
  • 网格
  • selection outline
  • gizmo overlay

因此它直接走 SceneRenderer,成功后只做最基本的颜色状态回写与 object-id 失效处理。

当前最重要的设计收益

现在这套链路真正带来的收益不是“代码更优雅”,而是三个更现实的好处:

  • Scene View 可以在局部能力缺失时尽量继续工作
  • 编辑器附加渲染和运行时场景渲染保持分层
  • 每一层都可以被单独测试和文档化

这正是商业级编辑器最需要的特征:稳定、可诊断、可扩展。

建议阅读顺序

  1. ViewportHostService
  2. ViewportHostService::RenderRequestedViewports
  3. SceneViewportRenderPassBundle
  4. SceneViewportRenderPlan
  5. BuildSceneViewportRenderPlan
  6. ApplySceneViewportRenderPlan
  7. ViewportHostRenderFlowUtils
  8. ApplySceneViewportRenderRequestSetup
  9. MarkSceneViewportRenderSuccess

相关文档