Refactor scene viewport render planning
This commit is contained in:
793
docs/plan/Unity式SceneView_Gizmo系统完整审查与正式化重构方案.md
Normal file
793
docs/plan/Unity式SceneView_Gizmo系统完整审查与正式化重构方案.md
Normal file
@@ -0,0 +1,793 @@
|
||||
# Unity式 SceneView Gizmo 系统完整审查与正式化重构方案
|
||||
|
||||
日期:`2026-04-03`
|
||||
|
||||
相关旧文档:
|
||||
- `docs/plan/SceneViewport_Overlay_Gizmo_Rework_Plan.md`
|
||||
- `docs/plan/SceneViewport_Overlay_Gizmo_Rework_Checkpoint_2026-04-02.md`
|
||||
- `docs/plan/Unity SRP API参考文档.md`
|
||||
|
||||
本文档以当前代码真实状态为准,覆盖:
|
||||
- `editor` 中 SceneView gizmo / grid / outline / overlay 的现状审查
|
||||
- `engine` 与 `editor` 的职责边界重划
|
||||
- 一套更接近 Unity SceneView / Gizmos / Handles 思路的正式化重构方案
|
||||
- 可分阶段落地、可测试、可验收的迁移计划
|
||||
|
||||
## 0. 执行结论
|
||||
|
||||
当前 SceneView 可视化链路已经完成了第一轮“从 ImGui 临时绘制向正式渲染 pass 迁移”的关键基础设施建设,但还没有真正收口。
|
||||
|
||||
当前最重要的结论有四条:
|
||||
|
||||
1. `engine` 里现有的通用渲染扩展点是对的,应该保留。
|
||||
- `RenderPass`
|
||||
- `RenderPassSequence`
|
||||
- `CameraRenderRequest::preScenePasses`
|
||||
- `CameraRenderRequest::postScenePasses`
|
||||
- `CameraRenderRequest::overlayPasses`
|
||||
|
||||
2. `editor` 里现有的 `SceneViewportEditorOverlayData` 和 `SceneViewportEditorOverlayPass` 方向是对的,应该继续扩展,而不是回退到 ImGui world draw。
|
||||
|
||||
3. 目前真正的架构问题,不是“某个 gizmo 画得还不够像 Unity”,而是 **SceneView 的 editor 语义仍然泄漏进了 engine 的 builtin postprocess**。
|
||||
- `infinite grid`
|
||||
- `selection outline`
|
||||
- `outline debug mask`
|
||||
- 与之绑定的 shader / resource 注册
|
||||
|
||||
4. 最终正确边界应当是:
|
||||
- `engine/runtime` 负责“能画什么、怎样插 pass、怎样跑后端”
|
||||
- `editor/SceneView` 负责“为什么要画、何时画、画哪些 gizmo、哪些对象参与、交互规则是什么”
|
||||
|
||||
换句话说,**Unity-like 的正确做法不是把所有 gizmo 都塞进 runtime engine,而是 runtime 提供绘制能力,editor 拥有 SceneView gizmo 的语义和调度权。**
|
||||
|
||||
---
|
||||
|
||||
## 1. 当前实现完整审查
|
||||
|
||||
## 1.1 已经正式化、应保留的部分
|
||||
|
||||
### A. CameraRenderer 的可扩展 pass 时序已经成立
|
||||
|
||||
当前 `CameraRenderer` 的执行顺序已经具备正规的扩展缝:
|
||||
|
||||
```text
|
||||
preScenePasses
|
||||
-> scene geometry
|
||||
-> object id
|
||||
-> postScenePasses
|
||||
-> builtin postprocess
|
||||
-> overlayPasses
|
||||
```
|
||||
|
||||
这条缝本身是正确的,说明现在的 renderer 已经不是一个只能硬编码单条流水线的结构,后续 SceneView 的正式化工作不需要推翻它。
|
||||
|
||||
### B. Editor world overlay 已经有 canonical frame data
|
||||
|
||||
`SceneViewportEditorOverlayData.h` 已经提供了一份比较正确的中间表示:
|
||||
- `worldLines`
|
||||
- `worldSprites`
|
||||
- `screenTriangles`
|
||||
- `handleRecords`
|
||||
|
||||
这意味着当前系统已经不必再依赖“某个 gizmo 组件自己画、自己 hit test、自己决定屏幕图元”的散乱做法,而是可以朝一份单帧 canonical overlay 数据前进。
|
||||
|
||||
### C. Editor overlay 已经具备独立 GPU pass
|
||||
|
||||
`SceneViewportEditorOverlayPass` 已经能够在 renderer pass 中绘制:
|
||||
- world line
|
||||
- sprite billboard
|
||||
- screen-space triangle
|
||||
|
||||
这一步非常关键。它说明 camera/light icon、camera frustum、light shape、transform gizmo 等内容已经不需要再走 ImGui world draw 这条临时路径。
|
||||
|
||||
### D. Scene object picking 已经走 object-id GPU 路线
|
||||
|
||||
当前对象选中本身已经不是纯 CPU 射线猜测,而是建立在 object id buffer 之上的正式读回流程。这个方向是对的,后续无需推翻。
|
||||
|
||||
结论:
|
||||
**当前系统最有价值的资产不是若干单独功能,而是已经出现了一套可继续扩张的“SceneView render extension seam + canonical overlay data + editor overlay pass”。**
|
||||
|
||||
---
|
||||
|
||||
## 1.2 已经进入正式化,但还没有彻底收口的部分
|
||||
|
||||
### A. Camera / Light icon、Camera frustum、Directional Light gizmo
|
||||
|
||||
这部分现在已经通过 `SceneViewportOverlayBuilder -> SceneViewportEditorOverlayPass` 进入 GPU overlay 链路,已经不再是纯 ImGui hack。
|
||||
|
||||
这是正确方向,但当前还存在几个明显问题:
|
||||
- `SceneViewportOverlayBuilder` 仍然是一个偏“堆逻辑”的单体 builder
|
||||
- gizmo provider 还没有独立注册体系
|
||||
- 灯光 gizmo 的覆盖范围还不完整
|
||||
- `PointLight` / `SpotLight` 还没有形成完整正式方案
|
||||
|
||||
### B. Transform gizmo 的绘制链路已经正规化,但来源还偏临时
|
||||
|
||||
当前 transform gizmo 的 solver、状态机、屏幕几何、handle records 已经大量进入 canonical overlay 路线,这是好的。
|
||||
|
||||
但当前仍然有两个收口问题:
|
||||
- `SceneViewPanel` 仍然参与了 transform gizmo 输入组装与 transient overlay 提交
|
||||
- `ViewportHostService` 仍然通过 `SetSceneViewTransientTransformGizmoOverlayData(...)` 注入一份“临时 overlay”
|
||||
|
||||
这说明 transform gizmo 还没有真正变成一套由 SceneView gizmo system 自己统一调度的 provider,而是“正规链路 + 临时桥接层”并存。
|
||||
|
||||
### C. Orientation gizmo 仍然走 ImGui / HUD 路线
|
||||
|
||||
这本身不算错误,因为 orientation gizmo 本质上就是 HUD,而不是 world overlay。
|
||||
|
||||
真正的问题在于:
|
||||
- 当前 HUD overlay 与 world overlay 的系统边界还没有被正式命名
|
||||
- `SceneViewportOverlayRenderer.cpp` 还承担着一个“历史遗留收容层”的角色
|
||||
|
||||
结论:
|
||||
**不是所有 gizmo 都必须迁入 GPU world pass。SceneView 右上角 orientation gizmo 属于 HUD,可以继续保留在 editor UI 层,但应该被正式归类为 HUD overlay,而不是继续以“杂项 overlay”存在。**
|
||||
|
||||
---
|
||||
|
||||
## 1.3 当前最核心的架构问题:SceneView 语义泄漏进了 engine
|
||||
|
||||
### A. grid / outline 不应该继续作为 engine 的 builtin scene semantics 存在
|
||||
|
||||
当前 `CameraRenderRequest::BuiltinPostProcessRequest` 直接携带:
|
||||
- `InfiniteGridPassData`
|
||||
- `selectedObjectIds`
|
||||
- `ObjectIdOutlineStyle`
|
||||
|
||||
这在工程上能跑,但从架构边界看是不对的。原因很简单:
|
||||
|
||||
- SceneView infinite grid 是编辑器语义,不是 runtime camera 的通用语义
|
||||
- Scene selection outline 是编辑器语义,不是 runtime scene 的通用语义
|
||||
- `debugSelectionMask` 更是纯 editor 调试语义
|
||||
|
||||
如果这些字段挂在 engine 的公共 camera request 上,等价于让 runtime camera API 默认理解 SceneView 的编辑器概念。
|
||||
|
||||
这与 Unity 风格不一致。
|
||||
|
||||
### B. 具体 editor 效果被注册成 engine builtin shader / builtin resource
|
||||
|
||||
当前 engine builtin resource 中已经出现:
|
||||
- `builtin://shaders/object-id-outline`
|
||||
- `builtin://shaders/infinite-grid`
|
||||
|
||||
这意味着:
|
||||
- runtime 资源表里已经掺入 editor SceneView 专用效果
|
||||
- 后续 player/package 很容易继续被这些 editor-only 资源污染
|
||||
- engine API 会逐步被 SceneView 需求反向牵着走
|
||||
|
||||
### C. SceneView 的 post scene policy 目前仍然依赖 engine 内建的固定流程
|
||||
|
||||
现在 `SceneView` 的 grid / outline 虽然是从 editor 发起,但执行上还是通过 engine builtin postprocess builder 被硬编码规划:
|
||||
- `SceneInfiniteGrid`
|
||||
- `SceneSelectionOutline`
|
||||
- `SceneSelectionMaskDebug`
|
||||
|
||||
这相当于 SceneView 的一部分渲染语义并不真正属于 editor,而是“借用 engine builtin 实现”。
|
||||
|
||||
短期可用,长期会越来越难维护。
|
||||
|
||||
结论:
|
||||
**当前系统最大的结构性缺陷,不是 gizmo 样式,而是 editor 语义与 runtime 语义没有彻底分层。**
|
||||
|
||||
---
|
||||
|
||||
## 1.4 仍然偏临时的实现点
|
||||
|
||||
### A. icon 资源加载仍然是 ad hoc 文件路径解析
|
||||
|
||||
`SceneViewportEditorOverlayPass` 当前直接解析:
|
||||
- `editor/resources/Icons/camera_gizmo.png`
|
||||
- `editor/resources/Icons/main_light_gizmo.png`
|
||||
|
||||
问题在于:
|
||||
- 没有 editor 内建资源注册表
|
||||
- 没有生命周期与缓存抽象
|
||||
- 没有 icon atlas / versioning / fallback 策略
|
||||
- 这条链仍然像“功能先跑起来”的工程写法
|
||||
|
||||
### B. gizmo provider 没有正式注册点
|
||||
|
||||
当前 camera/light gizmo 是 `SceneViewportOverlayBuilder` 直接扫描 scene 后构建。
|
||||
|
||||
这在功能上可行,但有明显扩展隐患:
|
||||
- 新增 gizmo 类型时会继续堆进单个 builder
|
||||
- provider 的职责无法单测
|
||||
- 组件与 gizmo 的映射关系不清晰
|
||||
- 后续 collider / reflection probe / audio source / particle / custom component gizmo 很难优雅接入
|
||||
|
||||
### C. 视觉图元与交互图元虽然已经接近统一,但规范还不完整
|
||||
|
||||
现在 `handleRecords` 已经是一个很好的方向,但还缺少更明确的约束:
|
||||
- 哪些图元负责可见渲染
|
||||
- 哪些记录负责 hit test
|
||||
- 同一 handle 是否允许多份可视 primitive
|
||||
- 优先级、遮挡、深度模式、拾取扩张半径等是否有统一规则
|
||||
|
||||
当前这些规则大多“隐含在代码里”,还没有真正上升为系统规范。
|
||||
|
||||
---
|
||||
|
||||
## 1.5 测试体系现状
|
||||
|
||||
当前测试并不差,但仍然没有覆盖到最关键的“正式化边界”。
|
||||
|
||||
已有较好覆盖:
|
||||
- grid 数学与相机投影相关测试
|
||||
- render flow utils 测试
|
||||
- object id picker 测试
|
||||
- move / rotate / scale gizmo solver 测试
|
||||
|
||||
当前缺口:
|
||||
- `SceneViewportOverlayBuilder` 的 contract 测试不足
|
||||
- `SceneViewportEditorOverlayPass` 的资源契约与 primitive 分发测试不足
|
||||
- camera / light gizmo provider 行为测试不足
|
||||
- SceneView pass 顺序与装配测试不足
|
||||
- “editor-only 资源不污染 runtime” 的构建层测试缺失
|
||||
|
||||
结论:
|
||||
**当前最大测试缺口不是数学公式,而是架构边界、provider 契约、pass 组合关系。**
|
||||
|
||||
---
|
||||
|
||||
## 2. Unity 风格的目标架构
|
||||
|
||||
## 2.1 总体原则
|
||||
|
||||
参考 Unity 的思路,应当把 SceneView 可视化拆成三层:
|
||||
|
||||
### Layer A:Runtime Render Capability(属于 `engine`)
|
||||
|
||||
负责“能画什么”:
|
||||
- scene geometry 渲染
|
||||
- object id 渲染
|
||||
- render pass 扩展点
|
||||
- 通用 full-screen / line / sprite / mesh / debug primitive 渲染能力
|
||||
- 后端抽象、资源绑定、shader 编译与执行
|
||||
|
||||
### Layer B:SceneView Gizmo Orchestration(属于 `editor`)
|
||||
|
||||
负责“为什么画、何时画、画哪些”:
|
||||
- SceneView grid
|
||||
- selection outline
|
||||
- camera / light / helper gizmo
|
||||
- transform gizmo
|
||||
- Scene icon
|
||||
- gizmo subset 调度
|
||||
- 交互 handle 组织与命中规则
|
||||
|
||||
### Layer C:HUD Overlay(属于 `editor UI`)
|
||||
|
||||
负责固定在面板屏幕空间的内容:
|
||||
- orientation gizmo
|
||||
- toolbar
|
||||
- 状态提示
|
||||
- 调试开关
|
||||
- 2D 操作提示与 hover 标签
|
||||
|
||||
这三层之间的关系应该是:
|
||||
|
||||
```text
|
||||
engine 提供绘制能力
|
||||
editor SceneView 决定 world gizmo / editor pass 计划
|
||||
editor UI 决定 HUD 展示
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2.2 Unity-like 的关键不是“都放 engine”,而是“runtime 与 editor 的语义边界清楚”
|
||||
|
||||
Unity 的本质做法并不是让 player runtime 永远背着 SceneView gizmo 语义。
|
||||
|
||||
更接近本项目的正确映射应当是:
|
||||
|
||||
- `engine` 对应 Unity 中底层 render context / renderer capability
|
||||
- `editor` 的 SceneView 系统对应 Unity 中 SceneView 对 gizmos / wire overlay / handles 的调度
|
||||
- `HUD` 对应 SceneView 窗口自己的 UI overlay
|
||||
|
||||
因此本项目的最终方案应当遵循:
|
||||
|
||||
1. `engine` 暴露“generic render extension seam”
|
||||
2. `editor` 组装 SceneView 专属 render plan
|
||||
3. SceneView 的 gizmo/grid/outline 作为 editor-only feature 实现
|
||||
4. runtime/player 构建不再默认携带 SceneView 语义资产
|
||||
|
||||
---
|
||||
|
||||
## 2.3 正式目标流水线
|
||||
|
||||
推荐的 SceneView 渲染时序如下:
|
||||
|
||||
```text
|
||||
PreScenePasses
|
||||
-> Scene Geometry
|
||||
-> ObjectId Pass
|
||||
-> SceneView Editor PostScene Passes
|
||||
- Grid
|
||||
- Selection Outline
|
||||
- Future: bounds / selection mask visualize / editor-only debug fullscreen pass
|
||||
-> SceneView World Overlay Pass
|
||||
- Scene icons
|
||||
- Camera frustum
|
||||
- Light gizmos
|
||||
- Transform gizmos
|
||||
- Future: collider / audio / volume / probe gizmos
|
||||
-> SceneView HUD Overlay
|
||||
- Orientation gizmo
|
||||
- Toolbar
|
||||
- Labels / hints / debug text
|
||||
```
|
||||
|
||||
这里面最关键的一点是:
|
||||
|
||||
- `grid` 和 `selection outline` 仍然属于 world-space / scene-view editor pass
|
||||
- 但它们不再属于 engine 的 builtin camera semantics
|
||||
- 它们应该变成 editor 自己构建的 post-scene pass chain
|
||||
|
||||
---
|
||||
|
||||
## 2.4 Gizmo 系统内部应再拆三类数据
|
||||
|
||||
最终系统不应只有“画什么”,还要清楚地区分三种数据:
|
||||
|
||||
### A. Visual Primitive Data
|
||||
|
||||
纯视觉数据:
|
||||
- line
|
||||
- billboard sprite
|
||||
- mesh / wire mesh
|
||||
- screen triangle
|
||||
|
||||
### B. Handle Interaction Data
|
||||
|
||||
纯交互数据:
|
||||
- handle id
|
||||
- entity id
|
||||
- pick shape
|
||||
- priority
|
||||
- depth / sort rule
|
||||
- hit thickness / pick expansion
|
||||
|
||||
### C. Gizmo Provider Output
|
||||
|
||||
语义层输出:
|
||||
- Camera gizmo provider 输出 camera icon + frustum + camera specific handles
|
||||
- Light gizmo provider 输出 light icon + light shape
|
||||
- Transform gizmo provider 输出 axis / plane / ring / center handles
|
||||
|
||||
这样做的好处是:
|
||||
- 视觉可以演进
|
||||
- 交互可以演进
|
||||
- provider 可以独立测试
|
||||
- 不会再把 SceneViewPanel 变成绘制器 + 命中器 + 业务调度器的混合体
|
||||
|
||||
---
|
||||
|
||||
## 3. 最终职责边界
|
||||
|
||||
## 3.1 `engine` 应保留的内容
|
||||
|
||||
以下内容应明确保留在 `engine`:
|
||||
|
||||
- `RenderPass` / `RenderPassSequence`
|
||||
- `CameraRenderer` 的 pass 执行骨架
|
||||
- scene geometry 渲染
|
||||
- object id pass
|
||||
- render target / state transition / descriptor / backend shader compilation
|
||||
- 如果未来可复用,则保留“无 editor 语义”的底层 helper
|
||||
- full-screen quad/blit helper
|
||||
- line primitive renderer helper
|
||||
- sprite billboard renderer helper
|
||||
- debug mesh renderer helper
|
||||
|
||||
核心要求:
|
||||
**这些能力必须是 generic 的,不能带 SceneView / Gizmo / Editor 的具体业务语义。**
|
||||
|
||||
---
|
||||
|
||||
## 3.2 `editor` 应拥有的内容
|
||||
|
||||
以下内容应明确归 `editor`:
|
||||
|
||||
- SceneView grid
|
||||
- selection outline 及其颜色、宽度、debug mask 策略
|
||||
- camera / light scene icon
|
||||
- camera frustum
|
||||
- directional / point / spot light gizmo
|
||||
- transform gizmo
|
||||
- orientation gizmo
|
||||
- 组件到 gizmo provider 的注册关系
|
||||
- editor-only gizmo shader / icon / material / texture 资源
|
||||
|
||||
换句话说:
|
||||
**凡是只有 SceneView 才关心的“显示语义”,最终都必须回收到 `editor`。**
|
||||
|
||||
---
|
||||
|
||||
## 3.3 可以下沉到 `engine` 的只有“无语义 helper”,不是 SceneView 语义本身
|
||||
|
||||
例如:
|
||||
- 如果 full-screen outline 的底层执行流程是通用的,可以提炼成 helper
|
||||
- 如果 world line / billboard / wire mesh 的 GPU 提交代码是通用的,可以提炼成 helper
|
||||
|
||||
但是不能继续暴露成这样的 engine 公共语义:
|
||||
- `SceneInfiniteGrid`
|
||||
- `SceneSelectionOutline`
|
||||
- `debugSelectionMask`
|
||||
|
||||
原则非常简单:
|
||||
|
||||
**实现可以共享,语义不能泄漏。**
|
||||
|
||||
---
|
||||
|
||||
## 4. 正式重构方案
|
||||
|
||||
## 4.1 Phase 1:先把 SceneView pass 规划权完全收回 editor
|
||||
|
||||
### 目标
|
||||
|
||||
让 `editor` 自己决定 SceneView 需要哪些 pass,而不是继续把 grid / outline 塞进 `engine::BuiltinPostProcessRequest`。
|
||||
|
||||
### 方案
|
||||
|
||||
新增 editor 侧的 SceneView render planning 层,例如:
|
||||
- `SceneViewportRenderPlan`
|
||||
- `SceneViewportPassChainBuilder`
|
||||
|
||||
由它负责:
|
||||
- 生成 SceneView 的 `postScenePasses`
|
||||
- 生成 SceneView 的 `overlayPasses`
|
||||
- 规划 object id 是否需要
|
||||
- 规划 grid / outline / gizmo 的具体顺序
|
||||
|
||||
### 结果
|
||||
|
||||
`CameraRenderRequest` 仍然保留:
|
||||
- `preScenePasses`
|
||||
- `postScenePasses`
|
||||
- `overlayPasses`
|
||||
|
||||
但 `BuiltinPostProcessRequest` 不再继续承载 SceneView 专属语义。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- `SceneView` 渲染路径不再依赖 `BuiltinPostProcessRequest` 中的 grid / outline 字段
|
||||
- SceneView pass 顺序完全由 editor 侧 builder 决定
|
||||
- engine 只负责执行 pass,不再理解 SceneView 概念
|
||||
|
||||
---
|
||||
|
||||
## 4.2 Phase 2:把 Grid 与 Selection Outline 从 engine builtin 中迁出
|
||||
|
||||
### 目标
|
||||
|
||||
完成这次重构中最关键的一刀:**切断 SceneView editor 语义对 engine builtin postprocess 的依赖。**
|
||||
|
||||
### 方案
|
||||
|
||||
在 `editor/src/Viewport/Passes` 新建 editor-owned passes:
|
||||
- `SceneViewportGridPass`
|
||||
- `SceneViewportSelectionOutlinePass`
|
||||
|
||||
如果当前实现代码中有通用部分,可以这样拆:
|
||||
|
||||
- `engine`
|
||||
- 保留无语义的 GPU helper
|
||||
- `editor`
|
||||
- 维护 `SceneViewportGridPassData`
|
||||
- 维护 `SceneViewportSelectionOutlineSettings`
|
||||
- 维护 SceneView 下的调用时机与参数策略
|
||||
|
||||
### 需要同步迁移的内容
|
||||
|
||||
- `InfiniteGridPassData` 从 engine 公共 camera request 中移除
|
||||
- `ObjectIdOutlineStyle` 不再作为 engine camera request 的 editor 语义字段暴露
|
||||
- `builtin://shaders/infinite-grid`
|
||||
- `builtin://shaders/object-id-outline`
|
||||
|
||||
上述资源应迁移为 editor-owned builtin 资源,或至少从注册语义上变为 editor 专属。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- runtime build 不再默认依赖 SceneView grid / outline shader
|
||||
- `CameraRenderRequest` 不再携带 SceneView grid / outline 语义
|
||||
- SceneView grid / outline 仍能正常工作,并维持现有视觉效果
|
||||
|
||||
---
|
||||
|
||||
## 4.3 Phase 3:建立正式的 Gizmo Provider Registry
|
||||
|
||||
### 目标
|
||||
|
||||
把当前“一个大 builder 扫全场”的模式,升级为“SceneView 系统调度多个 provider”。
|
||||
|
||||
### 建议接口
|
||||
|
||||
```text
|
||||
ISceneViewportGizmoProvider
|
||||
Gather(const SceneViewportGizmoBuildContext&, SceneViewportOverlayFrameData&)
|
||||
```
|
||||
|
||||
### 建议首批 provider
|
||||
|
||||
- `SceneCameraGizmoProvider`
|
||||
- `SceneLightGizmoProvider`
|
||||
- `SceneTransformHandleProvider`
|
||||
- `SceneSelectionHelperProvider`
|
||||
|
||||
后续可继续扩展:
|
||||
- collider gizmo provider
|
||||
- audio source gizmo provider
|
||||
- reflection probe gizmo provider
|
||||
- custom editor component gizmo provider
|
||||
|
||||
### 为什么必须这么做
|
||||
|
||||
因为 Unity-like 的 SceneView 不是“一个巨大 builder 拼所有形状”,而是“editor 根据对象类型和选择状态,调度多种 gizmo provider / handle provider / wire provider”。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- `SceneViewportOverlayBuilder` 从单体 builder 退化为 orchestration layer
|
||||
- 新增 gizmo 类型时,不需要继续往一个 cpp 文件里堆逻辑
|
||||
- provider 可以独立单测
|
||||
|
||||
---
|
||||
|
||||
## 4.4 Phase 4:把 World Overlay 与 HUD Overlay 正式分家
|
||||
|
||||
### 目标
|
||||
|
||||
让系统命名和责任与实际表现一致。
|
||||
|
||||
### 最终划分
|
||||
|
||||
#### World Overlay
|
||||
|
||||
使用 renderer pass:
|
||||
- scene icons
|
||||
- frustum
|
||||
- light shapes
|
||||
- transform gizmos
|
||||
- future helper volumes
|
||||
|
||||
#### HUD Overlay
|
||||
|
||||
使用 editor UI / ImGui:
|
||||
- orientation gizmo
|
||||
- toolbar
|
||||
- screen label
|
||||
- 操作提示
|
||||
|
||||
### 注意
|
||||
|
||||
这一步不是要求 orientation gizmo 也上 GPU。
|
||||
|
||||
正确目标是:
|
||||
- world-space 的内容不再走 ImGui
|
||||
- HUD 的内容明确归 UI
|
||||
|
||||
### 验收标准
|
||||
|
||||
- `SceneViewportOverlayRenderer.cpp` 不再承担历史兼容杂项职责
|
||||
- 所有 world-anchored overlay 都从统一 world overlay frame 进入
|
||||
- HUD overlay 有独立职责命名
|
||||
|
||||
---
|
||||
|
||||
## 4.5 Phase 5:建立 editor gizmo 资源体系
|
||||
|
||||
### 目标
|
||||
|
||||
消除当前 ad hoc 图标与 shader 资源加载方式。
|
||||
|
||||
### 方案
|
||||
|
||||
新增 editor 资源注册层,例如:
|
||||
- `EditorBuiltinResourceRegistry`
|
||||
- `EditorGizmoResourceRegistry`
|
||||
|
||||
负责:
|
||||
- icon texture 注册
|
||||
- gizmo shader 注册
|
||||
- gizmo material / pipeline preset 注册
|
||||
- future atlas / hot reload / fallback
|
||||
|
||||
### 具体要求
|
||||
|
||||
- 不再在 pass 内部直接写文件路径解析作为长期方案
|
||||
- camera / light / future icons 统一纳入 editor builtin 资源
|
||||
- gizmo pass 通过资源 key 查询,而不是硬编码本地 png 路径
|
||||
|
||||
### 验收标准
|
||||
|
||||
- gizmo 资源加载路径统一
|
||||
- 资源生命周期与缓存不再散落在单个 pass 里
|
||||
- future 新增 icon / shader 不需要继续复制粘贴路径解析代码
|
||||
|
||||
---
|
||||
|
||||
## 4.6 Phase 6:测试体系重构
|
||||
|
||||
### 目标
|
||||
|
||||
让测试覆盖系统边界,而不只是覆盖若干数学细节。
|
||||
|
||||
### 建议补齐的测试类别
|
||||
|
||||
#### A. Provider contract tests
|
||||
|
||||
例如:
|
||||
- camera provider 在 camera enabled 时输出 icon + frustum
|
||||
- disabled camera 不输出 gizmo
|
||||
- directional / point / spot light 分别输出正确图元组合
|
||||
|
||||
#### B. Overlay builder orchestration tests
|
||||
|
||||
例如:
|
||||
- 多 provider 输出能正确合并到单帧 overlay data
|
||||
- selected / unselected subset 行为正确
|
||||
- provider 顺序不影响 hit test 规则
|
||||
|
||||
#### C. Pass assembly tests
|
||||
|
||||
例如:
|
||||
- SceneView render plan 能生成正确的 `postScenePasses`
|
||||
- SceneView world overlay pass 顺序正确
|
||||
- object id / outline / gizmo 的依赖关系正确
|
||||
|
||||
#### D. Editor resource contract tests
|
||||
|
||||
例如:
|
||||
- icon key 能解析到合法资源
|
||||
- 缺失资源时能给出可诊断 fallback
|
||||
|
||||
#### E. Packaging / build boundary tests
|
||||
|
||||
例如:
|
||||
- player runtime target 不依赖 editor gizmo shader / icon 资源
|
||||
- editor target 才链接或注册 SceneView gizmo 资源
|
||||
|
||||
### 验收标准
|
||||
|
||||
- 新增 gizmo 类型时,至少有 provider contract test
|
||||
- 修改 SceneView pass 顺序时,至少有 render plan test 可以兜底
|
||||
- editor/runtime 边界错误能通过构建或测试尽早暴露
|
||||
|
||||
---
|
||||
|
||||
## 5. 关于 Picking 与 Handle 命中的最终建议
|
||||
|
||||
## 5.1 Scene object picking
|
||||
|
||||
当前对象选择已经基于 object-id buffer,这条路线应保留。
|
||||
|
||||
原因:
|
||||
- 这是正规的 GPU picking
|
||||
- 它与场景真实渲染结果一致性更好
|
||||
- 更适合 editor 里对 mesh / model / future submesh 的对象级选中
|
||||
|
||||
## 5.2 Gizmo handle picking
|
||||
|
||||
当前 gizmo handle 的命中是基于 canonical `handleRecords` 的 CPU hit test。
|
||||
|
||||
这不是临时方案,反而是合理的正式方案之一。
|
||||
|
||||
原因:
|
||||
- gizmo handle 本身就是高度人工设计的 screen-space / mixed-space 交互图元
|
||||
- CPU 命中更容易做 priority、命中扩张、前后景优先级、hover 宽容度
|
||||
- 没必要为了“全 GPU 化”把 handles 也做成 object-id pass
|
||||
|
||||
最终建议:
|
||||
- Scene object picking:继续 GPU object-id
|
||||
- Gizmo handle picking:继续 CPU hit test,但必须建立在统一 canonical handle 数据之上
|
||||
|
||||
这与 Unity 的使用体验和工程组织都更接近。
|
||||
|
||||
---
|
||||
|
||||
## 6. 对当前代码的具体重构建议
|
||||
|
||||
## 6.1 立即保留、不要动错的部分
|
||||
|
||||
- 保留 `CameraRenderer` 的 pass 时序骨架
|
||||
- 保留 `overlayPasses`
|
||||
- 保留 `SceneViewportEditorOverlayData`
|
||||
- 保留 `SceneViewportEditorOverlayPass`
|
||||
- 保留 object-id picking
|
||||
|
||||
这些是已经验证正确的基础设施,不应推翻重做。
|
||||
|
||||
## 6.2 应优先迁出的部分
|
||||
|
||||
- `BuiltinPostProcessRequest` 中的 SceneView 语义字段
|
||||
- `BuiltinInfiniteGridPass`
|
||||
- `BuiltinObjectIdOutlinePass`
|
||||
- SceneView 专属 shader 的 engine builtin 注册
|
||||
|
||||
## 6.3 应逐步拆分的部分
|
||||
|
||||
- `SceneViewportOverlayBuilder`
|
||||
- `ViewportHostService` 中 SceneView overlay / transient gizmo 的编排逻辑
|
||||
- `SceneViewPanel` 中仍然残留的 gizmo orchestration 代码
|
||||
|
||||
---
|
||||
|
||||
## 7. 推荐落地顺序
|
||||
|
||||
为避免大爆炸式重写,建议严格按下面顺序推进:
|
||||
|
||||
1. 先做 Phase 1
|
||||
- editor 拿回 SceneView pass 规划权
|
||||
- 不要先拆 gizmo provider
|
||||
|
||||
2. 再做 Phase 2
|
||||
- grid / outline 脱离 engine builtin
|
||||
- 先保证边界正确
|
||||
|
||||
3. 再做 Phase 3
|
||||
- provider registry
|
||||
- camera / light / transform 分 provider
|
||||
|
||||
4. 再做 Phase 4
|
||||
- 正式命名 HUD 与 world overlay 的分层
|
||||
|
||||
5. 然后做 Phase 5
|
||||
- 资源体系
|
||||
|
||||
6. 最后做 Phase 6
|
||||
- 测试与 packaging 收口
|
||||
|
||||
核心原则:
|
||||
**不要先推翻正在工作的 overlay pass;应当先纠正语义归属,再做 provider 化和资源化。**
|
||||
|
||||
---
|
||||
|
||||
## 8. 非目标
|
||||
|
||||
本轮重构不应顺手扩展成以下主题:
|
||||
|
||||
- render graph 改造
|
||||
- gameplay runtime debug draw 系统
|
||||
- 把所有 ImGui HUD 都迁到 GPU
|
||||
- 把 gizmo handle picking 也改成 GPU object-id
|
||||
- 重写整个 editor UI 框架
|
||||
|
||||
这些都不是本轮的主目标。
|
||||
|
||||
本轮的唯一主目标是:
|
||||
**把 SceneView gizmo / grid / outline 的系统边界、调度方式、资源归属、测试契约一次性做正规。**
|
||||
|
||||
---
|
||||
|
||||
## 9. 完成态判定标准
|
||||
|
||||
当以下条件全部满足时,可以认为本轮正式化重构收口:
|
||||
|
||||
1. `engine` 不再公开 SceneView grid / outline 的具体 camera 语义
|
||||
2. `editor` 自己构建 SceneView post-scene passes 与 world overlay passes
|
||||
3. camera / light / transform gizmo 由正式 provider 输出
|
||||
4. orientation gizmo 被正式归类为 HUD overlay
|
||||
5. gizmo 图标和 shader 拥有 editor 侧资源注册体系
|
||||
6. player runtime 构建不再依赖 SceneView gizmo 资源
|
||||
7. 测试覆盖 provider、render plan、resource boundary 三类关键契约
|
||||
|
||||
达到这一步后,当前 SceneView gizmo 系统才算真正进入“可持续扩展”的阶段。
|
||||
|
||||
---
|
||||
|
||||
## 10. 本文档对应的直接行动建议
|
||||
|
||||
下一阶段的实施建议如下:
|
||||
|
||||
1. 先开一个“SceneView pass ownership 回收”阶段
|
||||
- 目标只做 Phase 1 + Phase 2
|
||||
- 不在同一阶段内继续大规模重写 gizmo 样式
|
||||
|
||||
2. 该阶段完成后,再开“Gizmo Provider Registry”阶段
|
||||
- 先把 camera / light provider 正式拆出
|
||||
- transform gizmo provider 最后并入
|
||||
|
||||
3. 再开“Editor Gizmo Resources + Tests 收口”阶段
|
||||
- 把图标、shader、测试边界一次性清干净
|
||||
|
||||
这会比“看到哪改哪、边做边塞进现有 builder”稳定得多。
|
||||
|
||||
96
editor/src/Viewport/SceneViewportRenderPlan.h
Normal file
96
editor/src/Viewport/SceneViewportRenderPlan.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include "Passes/SceneViewportEditorOverlayPass.h"
|
||||
#include "SceneViewportEditorOverlayData.h"
|
||||
#include "ViewportHostRenderFlowUtils.h"
|
||||
|
||||
#include <XCEngine/Core/Math/Color.h>
|
||||
#include <XCEngine/Rendering/RenderPass.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
struct SceneViewportRenderPlan {
|
||||
Rendering::BuiltinPostProcessRequest builtinPostProcess = {};
|
||||
Rendering::RenderPassSequence postScenePasses = {};
|
||||
Rendering::RenderPassSequence overlayPasses = {};
|
||||
bool hasClearColorOverride = true;
|
||||
Math::Color clearColorOverride = Math::Color(0.27f, 0.27f, 0.27f, 1.0f);
|
||||
|
||||
bool HasPostScenePasses() const {
|
||||
return postScenePasses.GetPassCount() > 0;
|
||||
}
|
||||
|
||||
bool HasOverlayPasses() const {
|
||||
return overlayPasses.GetPassCount() > 0;
|
||||
}
|
||||
};
|
||||
|
||||
using SceneViewportOverlayPassFactory =
|
||||
std::function<std::unique_ptr<Rendering::RenderPass>(const SceneViewportOverlayFrameData&)>;
|
||||
|
||||
struct SceneViewportRenderPlanBuildResult {
|
||||
SceneViewportRenderPlan plan = {};
|
||||
const char* warningStatusText = nullptr;
|
||||
};
|
||||
|
||||
inline SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan(
|
||||
const ViewportRenderTargets& targets,
|
||||
const SceneViewportOverlayData& overlay,
|
||||
const std::vector<uint64_t>& selectedObjectIds,
|
||||
const SceneViewportOverlayFrameData& editorOverlayFrameData,
|
||||
const SceneViewportOverlayFrameData& transientOverlayFrameData,
|
||||
const SceneViewportOverlayPassFactory& overlayPassFactory,
|
||||
bool debugSelectionMask = false) {
|
||||
SceneViewportRenderPlanBuildResult result = {};
|
||||
if (!overlay.valid) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const SceneViewportBuiltinPostProcessBuildResult builtinPostProcess =
|
||||
BuildSceneViewportBuiltinPostProcess(
|
||||
overlay,
|
||||
selectedObjectIds,
|
||||
targets.objectIdShaderView != nullptr,
|
||||
debugSelectionMask);
|
||||
result.plan.builtinPostProcess = builtinPostProcess.request;
|
||||
result.warningStatusText = builtinPostProcess.warningStatusText;
|
||||
|
||||
SceneViewportOverlayFrameData renderOverlayFrameData = editorOverlayFrameData;
|
||||
AppendSceneViewportOverlayFrameData(renderOverlayFrameData, transientOverlayFrameData);
|
||||
if (renderOverlayFrameData.HasOverlayPrimitives() &&
|
||||
overlayPassFactory != nullptr) {
|
||||
std::unique_ptr<Rendering::RenderPass> overlayPass = overlayPassFactory(renderOverlayFrameData);
|
||||
if (overlayPass != nullptr) {
|
||||
result.plan.overlayPasses.AddPass(std::move(overlayPass));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void ApplySceneViewportRenderPlan(
|
||||
const ViewportRenderTargets& targets,
|
||||
SceneViewportRenderPlan& plan,
|
||||
Rendering::CameraRenderRequest& request) {
|
||||
ApplySceneViewportRenderRequestSetup(
|
||||
targets,
|
||||
&plan.builtinPostProcess,
|
||||
&plan.postScenePasses,
|
||||
request);
|
||||
|
||||
if (plan.HasOverlayPasses()) {
|
||||
request.overlayPasses = &plan.overlayPasses;
|
||||
}
|
||||
|
||||
request.hasClearColorOverride = plan.hasClearColorOverride;
|
||||
request.clearColorOverride = plan.clearColorOverride;
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "SceneViewportEditorOverlayData.h"
|
||||
#include "SceneViewportOverlayHandleBuilder.h"
|
||||
#include "SceneViewportOverlayBuilder.h"
|
||||
#include "SceneViewportRenderPlan.h"
|
||||
#include "ViewportHostRenderFlowUtils.h"
|
||||
#include "ViewportHostRenderTargets.h"
|
||||
#include "ViewportObjectIdPicker.h"
|
||||
@@ -417,9 +418,7 @@ private:
|
||||
|
||||
struct SceneViewportRenderState {
|
||||
SceneViewportOverlayData overlay = {};
|
||||
Rendering::BuiltinPostProcessRequest builtinPostProcess = {};
|
||||
SceneViewportOverlayFrameData editorOverlayFrameData = {};
|
||||
std::vector<uint64_t> selectedObjectIds;
|
||||
SceneViewportRenderPlan renderPlan = {};
|
||||
};
|
||||
|
||||
ViewportEntry& GetEntry(EditorViewportKind kind) {
|
||||
@@ -641,17 +640,25 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
outState.selectedObjectIds = context.GetSelectionManager().GetSelectedEntities();
|
||||
outState.editorOverlayFrameData = GetSceneViewEditorOverlayFrameData(context);
|
||||
const SceneViewportBuiltinPostProcessBuildResult builtinPostProcess =
|
||||
BuildSceneViewportBuiltinPostProcess(
|
||||
const std::vector<uint64_t> selectedObjectIds = context.GetSelectionManager().GetSelectedEntities();
|
||||
const SceneViewportOverlayFrameData& editorOverlayFrameData =
|
||||
GetSceneViewEditorOverlayFrameData(context);
|
||||
SceneViewportRenderPlanBuildResult renderPlan =
|
||||
BuildSceneViewportRenderPlan(
|
||||
entry.renderTargets,
|
||||
outState.overlay,
|
||||
outState.selectedObjectIds,
|
||||
entry.renderTargets.objectIdShaderView != nullptr,
|
||||
selectedObjectIds,
|
||||
editorOverlayFrameData,
|
||||
BuildSceneViewTransientTransformGizmoOverlayFrameData(),
|
||||
[this](const SceneViewportOverlayFrameData& frameData) {
|
||||
return CreateSceneViewportEditorOverlayPass(
|
||||
m_sceneViewportEditorOverlayRenderer,
|
||||
frameData);
|
||||
},
|
||||
kDebugSceneSelectionMask);
|
||||
outState.builtinPostProcess = builtinPostProcess.request;
|
||||
if (builtinPostProcess.warningStatusText != nullptr) {
|
||||
SetViewportStatusIfEmpty(entry.statusText, builtinPostProcess.warningStatusText);
|
||||
outState.renderPlan = std::move(renderPlan.plan);
|
||||
if (renderPlan.warningStatusText != nullptr) {
|
||||
SetViewportStatusIfEmpty(entry.statusText, renderPlan.warningStatusText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,25 +702,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
ApplySceneViewportRenderRequestSetup(
|
||||
entry.renderTargets,
|
||||
&sceneState.builtinPostProcess,
|
||||
nullptr,
|
||||
requests[0]);
|
||||
SceneViewportOverlayFrameData renderOverlayFrameData = sceneState.editorOverlayFrameData;
|
||||
AppendSceneViewportOverlayFrameData(
|
||||
renderOverlayFrameData,
|
||||
BuildSceneViewTransientTransformGizmoOverlayFrameData());
|
||||
Rendering::RenderPassSequence overlayPassSequence = {};
|
||||
if (renderOverlayFrameData.HasOverlayPrimitives()) {
|
||||
overlayPassSequence.AddPass(
|
||||
CreateSceneViewportEditorOverlayPass(
|
||||
m_sceneViewportEditorOverlayRenderer,
|
||||
renderOverlayFrameData));
|
||||
requests[0].overlayPasses = &overlayPassSequence;
|
||||
}
|
||||
requests[0].hasClearColorOverride = true;
|
||||
requests[0].clearColorOverride = Math::Color(0.27f, 0.27f, 0.27f, 1.0f);
|
||||
ApplySceneViewportRenderPlan(entry.renderTargets, sceneState.renderPlan, requests[0]);
|
||||
|
||||
if (!m_sceneRenderer->Render(requests)) {
|
||||
ApplyViewportRenderFailure(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Viewport/SceneViewportEditorOverlayData.h"
|
||||
#include "Viewport/SceneViewportRenderPlan.h"
|
||||
#include "Viewport/ViewportHostRenderFlowUtils.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -8,16 +10,21 @@
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::ApplySceneViewportRenderRequestSetup;
|
||||
using XCEngine::Editor::ApplySceneViewportRenderPlan;
|
||||
using XCEngine::Editor::ApplyViewportFailureStatus;
|
||||
using XCEngine::Editor::BuildGameViewportRenderFailurePolicy;
|
||||
using XCEngine::Editor::BuildSceneViewportBuiltinPostProcess;
|
||||
using XCEngine::Editor::BuildSceneViewportRenderPlan;
|
||||
using XCEngine::Editor::BuildSceneViewportRenderFailurePolicy;
|
||||
using XCEngine::Editor::BuildViewportRenderTargetUnavailablePolicy;
|
||||
using XCEngine::Editor::GameViewportRenderFailure;
|
||||
using XCEngine::Editor::MarkGameViewportRenderSuccess;
|
||||
using XCEngine::Editor::MarkSceneViewportRenderSuccess;
|
||||
using XCEngine::Editor::SceneViewportOverlayFrameData;
|
||||
using XCEngine::Editor::SceneViewportOverlayLinePrimitive;
|
||||
using XCEngine::Editor::SceneViewportRenderFailure;
|
||||
using XCEngine::Editor::SceneViewportOverlayData;
|
||||
using XCEngine::Editor::SceneViewportRenderPlan;
|
||||
using XCEngine::Editor::ViewportRenderTargets;
|
||||
using XCEngine::RHI::Format;
|
||||
using XCEngine::RHI::RHIResourceView;
|
||||
@@ -92,6 +99,20 @@ SceneViewportOverlayData CreateValidOverlay() {
|
||||
return overlay;
|
||||
}
|
||||
|
||||
SceneViewportOverlayFrameData CreateOverlayFrameDataWithLine(
|
||||
const SceneViewportOverlayData& overlay,
|
||||
const XCEngine::Math::Vector3& start,
|
||||
const XCEngine::Math::Vector3& end) {
|
||||
SceneViewportOverlayFrameData frameData = {};
|
||||
frameData.overlay = overlay;
|
||||
|
||||
SceneViewportOverlayLinePrimitive& line = frameData.worldLines.emplace_back();
|
||||
line.startWorld = start;
|
||||
line.endWorld = end;
|
||||
line.color = XCEngine::Math::Color::White();
|
||||
return frameData;
|
||||
}
|
||||
|
||||
TEST(ViewportRenderFlowUtilsTest, BuildFailurePoliciesExposeExpectedStatusAndClearBehavior) {
|
||||
const auto targetUnavailable = BuildViewportRenderTargetUnavailablePolicy();
|
||||
EXPECT_STREQ(targetUnavailable.statusText, "Viewport render target is unavailable");
|
||||
@@ -293,6 +314,80 @@ TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupPreservesBuiltinGr
|
||||
EXPECT_TRUE(request.objectId.IsRequested());
|
||||
}
|
||||
|
||||
TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsBuiltinAndOverlayPasses) {
|
||||
DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource);
|
||||
|
||||
ViewportRenderTargets targets = {};
|
||||
targets.objectIdShaderView = &objectIdShaderView;
|
||||
|
||||
const SceneViewportOverlayData overlay = CreateValidOverlay();
|
||||
const SceneViewportOverlayFrameData editorOverlayFrameData =
|
||||
CreateOverlayFrameDataWithLine(
|
||||
overlay,
|
||||
XCEngine::Math::Vector3::Zero(),
|
||||
XCEngine::Math::Vector3::Right());
|
||||
const SceneViewportOverlayFrameData transientOverlayFrameData =
|
||||
CreateOverlayFrameDataWithLine(
|
||||
overlay,
|
||||
XCEngine::Math::Vector3::Zero(),
|
||||
XCEngine::Math::Vector3::Up());
|
||||
|
||||
size_t factoryCallCount = 0u;
|
||||
size_t combinedWorldLineCount = 0u;
|
||||
const auto result = BuildSceneViewportRenderPlan(
|
||||
targets,
|
||||
overlay,
|
||||
{ 7u, 11u },
|
||||
editorOverlayFrameData,
|
||||
transientOverlayFrameData,
|
||||
[&factoryCallCount, &combinedWorldLineCount](const SceneViewportOverlayFrameData& frameData) {
|
||||
++factoryCallCount;
|
||||
combinedWorldLineCount = frameData.worldLines.size();
|
||||
return std::make_unique<NoopRenderPass>();
|
||||
},
|
||||
false);
|
||||
|
||||
EXPECT_TRUE(result.plan.builtinPostProcess.IsRequested());
|
||||
EXPECT_EQ(result.plan.overlayPasses.GetPassCount(), 1u);
|
||||
EXPECT_EQ(factoryCallCount, 1u);
|
||||
EXPECT_EQ(combinedWorldLineCount, 2u);
|
||||
EXPECT_EQ(result.warningStatusText, nullptr);
|
||||
}
|
||||
|
||||
TEST(ViewportRenderFlowUtilsTest, ApplySceneViewportRenderPlanAttachesPlannedPassesAndClearState) {
|
||||
DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
|
||||
DummyResourceView objectIdView(ResourceViewType::RenderTarget);
|
||||
DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource);
|
||||
|
||||
ViewportRenderTargets targets = {};
|
||||
targets.width = 800;
|
||||
targets.height = 600;
|
||||
targets.depthView = &depthView;
|
||||
targets.objectIdView = &objectIdView;
|
||||
targets.objectIdShaderView = &objectIdShaderView;
|
||||
|
||||
SceneViewportRenderPlan plan = {};
|
||||
plan.builtinPostProcess.gridPassData.valid = true;
|
||||
plan.postScenePasses.AddPass(std::make_unique<NoopRenderPass>());
|
||||
plan.overlayPasses.AddPass(std::make_unique<NoopRenderPass>());
|
||||
plan.clearColorOverride = XCEngine::Math::Color(0.1f, 0.2f, 0.3f, 1.0f);
|
||||
|
||||
XCEngine::Rendering::CameraRenderRequest request = {};
|
||||
request.surface = RenderSurface(800, 600);
|
||||
request.surface.SetRenderArea(XCEngine::Math::RectInt(10, 20, 300, 200));
|
||||
|
||||
ApplySceneViewportRenderPlan(targets, plan, request);
|
||||
|
||||
EXPECT_EQ(request.postScenePasses, &plan.postScenePasses);
|
||||
EXPECT_EQ(request.overlayPasses, &plan.overlayPasses);
|
||||
EXPECT_TRUE(request.objectId.IsRequested());
|
||||
EXPECT_TRUE(request.builtinPostProcess.IsRequested());
|
||||
EXPECT_TRUE(request.hasClearColorOverride);
|
||||
EXPECT_FLOAT_EQ(request.clearColorOverride.r, 0.1f);
|
||||
EXPECT_FLOAT_EQ(request.clearColorOverride.g, 0.2f);
|
||||
EXPECT_FLOAT_EQ(request.clearColorOverride.b, 0.3f);
|
||||
}
|
||||
|
||||
TEST(ViewportRenderFlowUtilsTest, MarkSceneRenderSuccessMovesTargetsToShaderResourceState) {
|
||||
DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
|
||||
DummyResourceView objectIdView(ResourceViewType::RenderTarget);
|
||||
|
||||
Reference in New Issue
Block a user