6.7 KiB
Scene View Render Plan And Failure Flow
先建立正确心智模型
当前 Scene View 不是“把活动场景直接交给 SceneRenderer”这么简单。
它真实的工作方式更接近商业引擎编辑器常见的两层结构:
- 运行时渲染层
- 负责把场景和相机变成基础
CameraRenderRequest
- 负责把场景和相机变成基础
- 编辑器附加层
- 负责网格、selection outline、editor overlay、object-id 与状态文案
SceneViewportRenderPlan 正是第二层里的一个关键中间件,而当前 ViewportHostService 与它之间还多了一层 SceneViewportRenderPassBundle,专门负责持有 grid / outline / overlay renderer 并提供 factory。
当前主调用链
按 ViewportHostService.h 当前实现,Scene View 一帧渲染大致是:
RenderRequestedViewports(...)遍历本帧请求过的视口。RenderViewportEntry(...)检查colorView/depthView是否存在。- 如果是 Scene 视口,进入
RenderSceneViewportEntry(...)。 - 检查隐藏编辑器 Scene View 相机是否可用。
- 检查活动场景是否存在。
BuildSceneViewportRenderState(...):- 读取
SceneViewportOverlayData - 读取当前选中对象列表
- 读取
SceneViewportOverlayFrameData - 调用
m_sceneViewportRenderPassBundle.BuildRenderPlan(...) - bundle 内部再调用
BuildSceneViewportRenderPlan(...)
- 读取
SceneRenderer::BuildRenderRequests(...)生成基础 request。ApplySceneViewportRenderPlan(...)把 editor 附加项写回 request。SceneRenderer::Render(requests)执行真正的场景渲染。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 的全部流程,只是“把 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 写回混成一个函数,短期看代码会少一些,但长期会带来三个问题:
- 很难单独测试规划逻辑
- 很容易在复用
CameraRenderRequest时留下脏指针 ViewportHostService会重新变成一大段条件拼装代码
当前拆成:
BuildSceneViewportRenderPlan(...)ApplySceneViewportRenderPlan(...)
本质上是在把“决策”和“接线”分离。这种拆法在商业引擎里很常见,因为它更容易维护,也更适合持续往 Scene View 里加新能力。
Game View 为什么不用这套 plan
Game View 的目标是尽量忠实呈现运行时相机结果,所以它没有:
- editor 私有背景
- 网格
- selection outline
- gizmo overlay
因此它直接走 SceneRenderer,成功后只做最基本的颜色状态回写与 object-id 失效处理。
当前最重要的设计收益
现在这套链路真正带来的收益不是“代码更优雅”,而是三个更现实的好处:
- Scene View 可以在局部能力缺失时尽量继续工作
- 编辑器附加渲染和运行时场景渲染保持分层
- 每一层都可以被单独测试和文档化
这正是商业级编辑器最需要的特征:稳定、可诊断、可扩展。
建议阅读顺序
- ViewportHostService
- ViewportHostService::RenderRequestedViewports
- SceneViewportRenderPassBundle
- SceneViewportRenderPlan
- BuildSceneViewportRenderPlan
- ApplySceneViewportRenderPlan
- ViewportHostRenderFlowUtils
- ApplySceneViewportRenderRequestSetup
- MarkSceneViewportRenderSuccess