docs: sync api and planning docs
This commit is contained in:
@@ -1,248 +0,0 @@
|
||||
# API 文档实时同步任务池(2026-04-03,第二轮)
|
||||
|
||||
## 文档定位
|
||||
|
||||
这份任务池接替已归档的第一轮计划:
|
||||
|
||||
- `docs/plan/used/API文档实时同步任务池_2026-04-03_第一轮归档.md`
|
||||
|
||||
第一轮已经解决的重点是:
|
||||
|
||||
- canonical 目录结构收口
|
||||
- 历史缺页补齐
|
||||
- 第一轮大规模内容重写
|
||||
|
||||
本轮重点不再是“补结构”,而是:
|
||||
|
||||
- 重新对照当前工作树源码与测试
|
||||
- 清理最近重构后重新出现的内容级失配
|
||||
- 继续维护一份适合多人并行认领的增量同步清单
|
||||
|
||||
## 当前复核快照
|
||||
|
||||
- 最近一次结构审计时间:`2026-04-03 15:07:08`
|
||||
- 审计命令:`python -B docs/api/_tools/audit_api_docs.py`
|
||||
- 当前结果:
|
||||
- `Public headers: 244`
|
||||
- `Editor source headers: 124`
|
||||
- `Invalid header refs: 0`
|
||||
- `Invalid source refs: 0`
|
||||
- `Broken .md links: 0`
|
||||
- `Stale canonical doc tokens: 0`
|
||||
- `Stale editor doc tokens: 0`
|
||||
- `Stale editor canonical pages: 0`
|
||||
|
||||
这说明当前结构层面已经恢复全绿;本轮后续工作以内容级持续同步为主。
|
||||
|
||||
## 认领规则
|
||||
|
||||
- 一次只认领 `1` 个任务块。
|
||||
- 先把 `状态` 改成 `DOING`,再写 `认领人`。
|
||||
- 只能改自己任务块的 `写入范围`。
|
||||
- 所有改动都必须以“当前源码 + 当前测试 + 当前真实调用链”为依据,不允许按旧文档续写旧行为。
|
||||
- 如果清理了过期 API 页面,必须同时清理交叉链接。
|
||||
|
||||
## 任务池
|
||||
|
||||
## T01 Editor / Viewport 渲染计划与宿主流程内容同步
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P0`
|
||||
- 写入范围:
|
||||
- `docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/**`
|
||||
- `docs/api/XCEngine/Editor/Viewport/ViewportHostRenderFlowUtils/**`
|
||||
- `docs/api/XCEngine/Editor/Viewport/ViewportHostService/**`
|
||||
- 必要时 `docs/api/XCEngine/Editor/Viewport/IViewportHostService/**`
|
||||
- 主要源码依据:
|
||||
- `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
- `editor/src/Viewport/ViewportHostRenderFlowUtils.h`
|
||||
- `editor/src/Viewport/ViewportHostService.h`
|
||||
- `tests/editor/test_viewport_render_flow_utils.cpp`
|
||||
- 已关闭问题:
|
||||
- 旧文档仍把 Scene View 当前主路径写成 builtin post-process 主导
|
||||
- 没写清 grid / selection outline 现在已转为显式 `postScenePasses`
|
||||
- 没写清 overlay pass 是 `editorOverlayFrameData + transientOverlayFrameData` 合并后再创建
|
||||
- 没写清 `Scene object id shader view is unavailable` 是局部降级警告,不是整帧失败
|
||||
- 完成记录:
|
||||
- 已重写 `SceneViewportRenderPlan.md`、`BuildSceneViewportRenderPlan.md`、`ApplySceneViewportRenderPlan.md`
|
||||
- 已同步 `ViewportHostRenderFlowUtils.md`、`ViewportHostService.md`、`RenderRequestedViewports.md`
|
||||
- 已清理 `IViewportHostService` 中残留的旧表述
|
||||
|
||||
## T02 Core / Asset 缓存接口改名与语义重构同步
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P0`
|
||||
- 写入范围:
|
||||
- `docs/api/XCEngine/Core/Asset/ResourceManager/**`
|
||||
- `docs/api/XCEngine/Core/Asset/AssetImportService/**`
|
||||
- `docs/api/XCEngine/Core/Asset/ProjectAssetIndex/**`
|
||||
- 必要时 `docs/api/XCEngine/Core/Asset/Asset.md`
|
||||
- 必要时 `docs/api/XCEngine/Core/Asset/ArtifactFormats/**`
|
||||
- 主要源码依据:
|
||||
- `engine/include/XCEngine/Core/Asset/ResourceManager.h`
|
||||
- `engine/src/Core/Asset/ResourceManager.cpp`
|
||||
- `engine/include/XCEngine/Core/Asset/AssetImportService.h`
|
||||
- `engine/src/Core/Asset/AssetImportService.cpp`
|
||||
- `engine/include/XCEngine/Core/Asset/ProjectAssetIndex.h`
|
||||
- `engine/src/Core/Asset/ProjectAssetIndex.cpp`
|
||||
- `tests/core/Asset/test_resource_manager.cpp`
|
||||
- 已关闭问题:
|
||||
- `RefreshAssetDatabase` 已经不存在,但旧文档仍在沿用
|
||||
- `RefreshProjectAssets` / `RebuildProjectAssetCache` / `GetProjectLibraryRoot` 缺页或未写清
|
||||
- `AssetImportService::EnsureArtifact()` 旧文档仍把输出写成 `ResolvedAsset`
|
||||
- `BuildLookupSnapshot()` 旧文档仍按“双 map 出参”描述,而不是 `LookupSnapshot`
|
||||
- `ImportedAsset::runtimeLoadPath` 语义未同步到上层文档
|
||||
- 完成记录:
|
||||
- 已删除过期页 `ResourceManager/RefreshAssetDatabase.md`
|
||||
- 已补齐 `RefreshProjectAssets.md`、`RebuildProjectAssetCache.md`、`GetProjectLibraryRoot.md`
|
||||
- 已补齐 `LookupSnapshot.md`、`ImportedAsset.md`、`GetLibraryRoot.md`、`RebuildLibraryCache.md`
|
||||
- 已同步 `Asset.md`、`ArtifactFormats.md`、`ResourceManager/Load.md` 的 `runtimeLoadPath` 口径
|
||||
|
||||
## T03 Scripting / Mono 托管销毁入口同步
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P1`
|
||||
- 写入范围:
|
||||
- `docs/api/XCEngine/Scripting/Mono/MonoScriptRuntime/**`
|
||||
- 主要源码依据:
|
||||
- `engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h`
|
||||
- `engine/src/Scripting/Mono/MonoScriptRuntime.cpp`
|
||||
- `tests/scripting/test_mono_script_runtime.cpp`
|
||||
- 已关闭问题:
|
||||
- `DestroyManagedObject(MonoObject*)` 已进入头文件与测试,但文档树没有对应页面
|
||||
- `MonoScriptRuntime.md` 没写清 `Object.Destroy(...)` 会回落到原生对象 / 组件销毁
|
||||
- 完成记录:
|
||||
- 已新增 `DestroyManagedObject.md`
|
||||
- 已更新 `MonoScriptRuntime.md` 的 internal call 说明与方法总表
|
||||
|
||||
## T04 Cross-Module / 教程层与模块总览口径持续复核
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P2`
|
||||
- 写入范围:
|
||||
- `docs/api/_guides/**`
|
||||
- `docs/api/XCEngine/*/*.md`
|
||||
- 仅限与本轮已确认 API 变更直接相关的总览页
|
||||
- 任务目标:
|
||||
- 继续检查教程页、模块总览页是否仍在传播旧心智模型
|
||||
- 尤其关注:
|
||||
- Scene View 仍被写成 builtin post-process 主导
|
||||
- 资源导入链仍被写成 `artifactMainPath` / `RefreshAssetDatabase` 时代的口径
|
||||
- 托管对象销毁路径未在教程层被解释
|
||||
- 产出要求:
|
||||
- 只修正已确认失配的 guide / overview 页面
|
||||
- 不做与当前源码无关的泛化扩写
|
||||
|
||||
## T05 增量变更监控 / 新一轮差异发现
|
||||
|
||||
- 状态: `OPEN`
|
||||
- 认领人: ``
|
||||
- 优先级: `P2`
|
||||
- 写入范围:
|
||||
- 只读检查 `engine/include/**`、`engine/src/**`、`editor/src/**`、`tests/**`
|
||||
- 必要时只向本任务池追加新任务块
|
||||
- 任务目标:
|
||||
- 继续结合工作树最新改动,找出新的“源码已变但文档还没跟上”的内容级失配
|
||||
- 优先检查:
|
||||
- 最近改动过的 public headers
|
||||
- 最近改动过的 Editor source headers
|
||||
- 最近新增或更新过的测试
|
||||
- 产出要求:
|
||||
- 只记录已确认的问题
|
||||
- 每条新任务都要写明:
|
||||
- 受影响文档
|
||||
- 主要源码依据
|
||||
- 真实失配点
|
||||
- 建议写入范围
|
||||
|
||||
## T06 Rendering / Camera request、Passes 与执行链旧口径清理
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P1`
|
||||
- 写入范围:
|
||||
- `docs/api/XCEngine/Rendering/CameraRenderRequest/**`
|
||||
- `docs/api/XCEngine/Rendering/CameraRenderer/**`
|
||||
- `docs/api/XCEngine/Rendering/Passes/**`
|
||||
- `docs/api/XCEngine/Rendering/ObjectIdPass/**`
|
||||
- `docs/api/XCEngine/Rendering/RenderPipeline/**`
|
||||
- `docs/api/XCEngine/Rendering/SceneRenderer/**`
|
||||
- `docs/api/XCEngine/Editor/Viewport/SceneViewportRenderPlan/**`
|
||||
- 必要时 `docs/api/XCEngine/XCEngine.md`
|
||||
- 必要时 `docs/api/_tools/audit_api_docs.py`
|
||||
- 主要源码依据:
|
||||
- `engine/include/XCEngine/Rendering/CameraRenderRequest.h`
|
||||
- `engine/include/XCEngine/Rendering/CameraRenderer.h`
|
||||
- `engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h`
|
||||
- `engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h`
|
||||
- `engine/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h`
|
||||
- `engine/include/XCEngine/Rendering/ObjectIdPass.h`
|
||||
- `engine/include/XCEngine/Rendering/RenderPipeline.h`
|
||||
- `engine/include/XCEngine/Rendering/SceneRenderer.h`
|
||||
- `engine/src/Rendering/CameraRenderer.cpp`
|
||||
- `engine/src/Rendering/SceneRenderer.cpp`
|
||||
- `tests/Rendering/unit/test_camera_scene_renderer.cpp`
|
||||
- `editor/src/Viewport/SceneViewportRenderPlan.h`
|
||||
- `editor/src/Viewport/Passes/SceneViewportGridPass.cpp`
|
||||
- `editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp`
|
||||
- `editor/src/Viewport/ViewportHostRenderFlowUtils.h`
|
||||
- `tests/Editor/test_viewport_render_flow_utils.cpp`
|
||||
- `tests/Editor/test_scene_viewport_overlay_renderer.cpp`
|
||||
- 已关闭问题:
|
||||
- `CameraRenderRequest.md` 仍描述已删除的 `builtinPostProcess` 子请求
|
||||
- 页面仍链接到不存在的 `BuiltinPostProcessRequest/BuiltinPostProcessRequest.md`
|
||||
- Scene View 已改为通过 `postScenePasses` / `overlayPasses` 写回 request,但该页口径未同步
|
||||
- `CameraRenderer.md` 仍保留已删除的 `m_builtinPostProcessBuilder` 历史口径
|
||||
- `Passes.md` 的典型链路只写了 `postScenePasses`,遗漏当前 `overlayPasses` 组装路径
|
||||
- `Rendering/Passes` 下仍残留已删除的 `BuiltinPostProcessPassPlan` / `BuiltinPostProcessPassSequenceBuilder` 页面
|
||||
- `BuiltinObjectIdPass`、`BuiltinInfiniteGridPass` 多个公开入口缺页
|
||||
- `ObjectIdPass.md`、`RenderPipeline.md`、`SceneRenderer.md` 与顶层 `XCEngine.md` 仍在传播 builtin-post-process 心智模型
|
||||
- 完成记录:
|
||||
- 已重写 `CameraRenderRequest.md`
|
||||
- 已清理 `2` 个失效 `.md` 链接
|
||||
- 已同步 `CameraRenderer.md` 与 `Passes.md` 的当前执行链路表述
|
||||
- 已删除 `BuiltinPostProcessPassPlan.md`、`BuiltinPostProcessPassSequenceBuilder.md`
|
||||
- 已补齐 `BuiltinObjectIdPass` / `BuiltinInfiniteGridPass` 缺失页面,并补充 `SceneViewportRenderPlan` 下 grid / selection outline pass factory 页面
|
||||
- 已同步 `ObjectIdPass.md`、`RenderPipeline.md`、`SceneRenderer.md` 与 `XCEngine.md` 的当前口径
|
||||
- 已补充 Rendering guide、`CameraRenderer::Render` 与 `ViewportHostService::RenderRequestedViewports` 的 request 级 pass 注入说明
|
||||
- 已为 `builtinPostProcess` / `BuiltinPostProcessRequest` / `m_builtinPostProcessBuilder` 增加 canonical 过期符号审计
|
||||
- 已复跑结构审计并确认 `Broken .md links: 0`
|
||||
|
||||
## T07 Editor / Core `EditorConsoleSink` 生命周期说明同步
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P1`
|
||||
- 写入范围:
|
||||
- `docs/api/XCEngine/Editor/Core/EditorConsoleSink/**`
|
||||
- 必要时 `docs/api/XCEngine/Editor/Managers/Managers.md`
|
||||
- 必要时 `docs/api/_tools/audit_api_docs.py`
|
||||
- 主要源码依据:
|
||||
- `editor/src/Core/EditorConsoleSink.h`
|
||||
- `editor/src/Core/EditorConsoleSink.cpp`
|
||||
- `editor/src/panels/ConsolePanel.cpp`
|
||||
- `editor/src/Core/EditorLoggingSetup.h`
|
||||
- `tests/Editor/test_editor_console_sink.cpp`
|
||||
- 已关闭问题:
|
||||
- `EditorConsoleSink::GetInstance()` 旧文档仍把它写成会返回 fallback 实例
|
||||
- 相关 overview 页面没有写清活动 sink 销毁后会返回 `nullptr`
|
||||
- 审计脚本此前无法自动拦截这类旧生命周期表述
|
||||
- 完成记录:
|
||||
- 已同步 `EditorConsoleSink.md`、`GetInstance.md`、`Destructor.md`
|
||||
- 已同步 `Managers.md` 中对控制台 sink 生命周期的引用口径
|
||||
- 已为 `fallback 实例` 与“不会返回空指针”旧表述增加定向审计
|
||||
|
||||
## 当前结论
|
||||
|
||||
- 本轮已经关掉五组明确的内容级失配:
|
||||
- `Viewport` 渲染计划与宿主流程
|
||||
- `Core/Asset` 缓存接口与导入服务语义
|
||||
- `MonoScriptRuntime` 托管销毁入口
|
||||
- `Rendering` 相机请求、Passes 与单相机执行链旧口径
|
||||
- `EditorConsoleSink` 生命周期与空指针返回语义
|
||||
- 当前结构审计为全绿。
|
||||
- 当前仍建议保留 `T04` 与 `T05` 作为持续性并行入口,用来承接你后续重构带来的新文档漂移。
|
||||
@@ -1,117 +0,0 @@
|
||||
# API 文档并行更新任务池(2026-04-02)
|
||||
|
||||
## 目的
|
||||
|
||||
基于 `2026-04-02` 当前工作树,这份清单用于把 API 文档更新任务拆成可并行认领的独立块,供多个会话同时推进。
|
||||
|
||||
## 认领规则
|
||||
|
||||
- 一次只认领 `1` 个任务块,先改 `状态` 和 `认领人`。
|
||||
- 只修改自己任务块的 `写入范围`,不要跨任务顺手改别的模块页。
|
||||
- 除 `T09` 之外,其他任务不要更新 `docs/api/_meta/rebuild-status.md`,避免多人冲突。
|
||||
- 每个任务都要以源码、实现、测试、真实调用点为依据,不允许只按命名猜测行为。
|
||||
- 如果任务执行中发现需要新增 guide,统一放到 `docs/api/_guides/<Module>/` 下。
|
||||
|
||||
## 首批并行推荐
|
||||
|
||||
- 优先并行启动: `T01`、`T03`、`T04`、`T05`、`T07`
|
||||
- 第二批跟进: `T02`、`T06`、`T08`
|
||||
- 收尾整合: `T09`
|
||||
|
||||
## 任务池
|
||||
|
||||
## T01 Editor / Viewport 子模块补齐与重写
|
||||
|
||||
- 状态: `IN_PROGRESS`
|
||||
- 认领人: `Codex-Viewport`
|
||||
- 优先级: `P0`
|
||||
- 写入范围: `docs/api/XCEngine/Editor/Viewport/**`、`docs/api/XCEngine/Editor/panels/SceneViewPanel/**`、`docs/api/XCEngine/Editor/panels/ViewportPanelContent/**`
|
||||
- 主要源码依据: `editor/src/Viewport/**`、`editor/src/panels/SceneViewPanel.*`、`editor/src/panels/ViewportPanelContent.h`、`tests/editor/test_scene_viewport_camera_controller.cpp`
|
||||
- 当前缺口: `Viewport` 整个 canonical 树尚未建立;以下页面当前缺失: `SceneViewportCameraController`、`SceneViewportMoveGizmo`、`SceneViewportRotateGizmo`、`SceneViewportScaleGizmo`、`SceneViewportOverlayRenderer`、`ViewportHostService`、`ViewportHostRenderFlowUtils`、`SceneViewportEditorOverlayData`、`SceneViewportOverlayBuilder`、`ViewportPanelContent`
|
||||
- 完成标准: 补齐 `Viewport/Viewport.md` 与所有类型页;`SceneViewPanel` 文档重写到当前 gizmo / overlay / host flow 实现;写清楚生命周期、交互链路、渲染路径和测试覆盖
|
||||
|
||||
## T02 Editor / ScriptComponentEditor 补齐
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P0`
|
||||
- 写入范围: `docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditor/**`、`docs/api/XCEngine/Editor/ComponentEditors/ScriptComponentEditorUtils/**`、`docs/api/XCEngine/Editor/ComponentEditors/ComponentEditors.md`、`docs/api/XCEngine/Editor/ComponentEditors/ComponentEditorRegistry/**`
|
||||
- 主要源码依据: `editor/src/ComponentEditors/ScriptComponentEditor.h`、`editor/src/ComponentEditors/ScriptComponentEditorUtils.h`、`editor/src/ComponentEditors/ComponentEditorRegistry.cpp`
|
||||
- 当前缺口: `ScriptComponentEditor` 与 `ScriptComponentEditorUtils` 还没有 canonical 页面;组件编辑器总览也需要纳入脚本组件编辑器
|
||||
- 完成标准: 补齐缺页;说明 Inspector 侧脚本字段绘制、字段元数据来源、与 `ScriptEngine` / `ScriptComponent` 的关系
|
||||
|
||||
## T03 Core / AssetDatabase 新建与资产数据库链路说明
|
||||
|
||||
- 状态: `IN_PROGRESS`
|
||||
- 认领人: `Codex-Asset`
|
||||
- 优先级: `P0`
|
||||
- 写入范围: `docs/api/XCEngine/Core/Asset/AssetDatabase/**`、`docs/api/XCEngine/Core/Asset/Asset.md`
|
||||
- 主要源码依据: `engine/include/XCEngine/Core/Asset/AssetDatabase.h`、相关 `.cpp` 实现、项目目录下新增的 `.meta` 与 `Library` 资产缓存变化
|
||||
- 当前缺口: `AssetDatabase` 对应的 canonical 类型页完全缺失;`Core/Asset` 模块总览需要反映新的数据库/导入缓存方向
|
||||
- 完成标准: 建立 `AssetDatabase` 页面,明确 GUID、path、meta、导入缓存、查询职责,以及它和 `ProjectPanel` / `ResourceManager` / 资源导入流程的关系
|
||||
|
||||
## T04 Rendering / Passes 子模块与 BuiltinObjectIdOutlinePass 补齐
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P0`
|
||||
- 写入范围: `docs/api/XCEngine/Rendering/Passes/**`
|
||||
- 主要源码依据: `engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h`、`engine/src/Rendering/Passes/BuiltinObjectIdOutlinePass.cpp`
|
||||
- 当前缺口: `Rendering/Passes` 目录当前没有 canonical 文档树;`BuiltinObjectIdOutlinePass` 页面缺失
|
||||
- 完成标准: 新建 `Passes/Passes.md` 和 `BuiltinObjectIdOutlinePass` 类型页;写清楚对象 ID / 轮廓高亮的输入输出、依赖资源、典型使用位置和当前限制
|
||||
|
||||
## T05 Scripting 模块内容重构
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P1`
|
||||
- 写入范围: `docs/api/XCEngine/Scripting/**`、`docs/api/_guides/Scripting/**`
|
||||
- 主要源码依据: `engine/include/XCEngine/Scripting/IScriptRuntime.h`、`Mono/MonoScriptRuntime.h`、`NullScriptRuntime.h`、`ScriptComponent.h`、`ScriptEngine.h` 及对应 `.cpp`;`tests/scripting/**`
|
||||
- 当前缺口: 结构存在,但脚本运行时、字段同步、项目脚本程序集、空运行时回退等内容需要按当前实现重写
|
||||
- 完成标准: 明确运行时抽象、Mono 后端、Null 后端、字段存储与组件生命周期;必要时补一篇项目脚本程序集 / 字段同步 guide
|
||||
|
||||
## T06 Editor 运行时胶水层与面板内容更新
|
||||
|
||||
- 状态: `TODO`
|
||||
- 认领人: `未认领`
|
||||
- 优先级: `P1`
|
||||
- 写入范围: `docs/api/XCEngine/Editor/Application/**`、`docs/api/XCEngine/Editor/Core/EventBus/**`、`docs/api/XCEngine/Editor/panels/InspectorPanel/**`、`docs/api/XCEngine/Editor/panels/ProjectPanel/**`、`docs/api/XCEngine/Editor/UI/Widgets/**`、`docs/api/XCEngine/Editor/Actions/HierarchyActionRouter/**`、`docs/api/XCEngine/Editor/Commands/EntityCommands/**`
|
||||
- 主要源码依据: `editor/src/Application.*`、`editor/src/Core/EventBus.h`、`editor/src/panels/InspectorPanel.*`、`editor/src/panels/ProjectPanel.*`、`editor/src/UI/Widgets.h`、`editor/src/Actions/HierarchyActionRouter.h`、`editor/src/Commands/EntityCommands.h`
|
||||
- 当前缺口: 这些页面虽然大多存在,但内容容易落后于当前交互链路;`ProjectPanel` 虽已较新,仍要根据这轮源码变化做二次核对
|
||||
- 完成标准: 把“Editor 主循环 -> EventBus -> 面板 -> Action/Command”这条链路写清楚;Inspector/Project/Hierarchy 相关页内容与当前实现严格对齐
|
||||
|
||||
## T07 Rendering 相机请求与对象 ID 渲染链路更新
|
||||
|
||||
- 状态: `DONE`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P1`
|
||||
- 写入范围: `docs/api/XCEngine/Rendering/CameraRenderRequest/**`、`docs/api/XCEngine/Rendering/CameraRenderer/**`、`docs/api/XCEngine/Rendering/RenderMaterialUtility/**`、`docs/api/XCEngine/Rendering/Rendering.md`
|
||||
- 主要源码依据: `engine/include/XCEngine/Rendering/CameraRenderRequest.h`、`engine/src/Rendering/CameraRenderer.cpp`、`engine/src/Resources/Material/MaterialLoader.cpp`、`engine/src/Rendering/Passes/BuiltinObjectIdOutlinePass.cpp`
|
||||
- 当前缺口: 文档需要反映这轮 renderer 里对象 ID、outline、camera request、材质 render state 的新关系
|
||||
- 完成标准: 写清楚 camera request 的职责边界、camera renderer 的主流程、object-id/outline 的接入点,以及材质 render state 对渲染路径的影响
|
||||
|
||||
## T08 Components / MeshFilterComponent 与资源绑定链路更新
|
||||
|
||||
- 状态: `IN_PROGRESS`
|
||||
- 认领人: `Codex`
|
||||
- 优先级: `P1`
|
||||
- 写入范围: `docs/api/XCEngine/Components/MeshFilterComponent/**`、`docs/api/XCEngine/Components/Components.md`
|
||||
- 主要源码依据: `engine/include/XCEngine/Components/MeshFilterComponent.h`、相关 `.cpp`、`tests/Resources/Mesh/test_mesh_loader.cpp`、`tests/Resources/Material/test_material_loader.cpp`
|
||||
- 当前缺口: `MeshFilterComponent` 页面存在,但需要重新核对 mesh handle / path / 资源解析链路;模块总览也应补充 MeshFilter 在渲染和资产导入链路中的定位
|
||||
- 完成标准: 说明 `MeshFilterComponent` 如何保存 mesh 引用、如何与资源系统和渲染提取流程衔接,以及当前限制
|
||||
|
||||
## T09 根总览与最终审计
|
||||
|
||||
- 状态: `TODO`
|
||||
- 认领人: `未认领`
|
||||
- 优先级: `P2`
|
||||
- 写入范围: `docs/api/XCEngine/XCEngine.md`、受影响的模块总览页、`docs/api/_meta/rebuild-status.md`
|
||||
- 主要源码依据: 前面所有任务的完成结果
|
||||
- 当前缺口: 根总览和模块总览需要在前面任务落地后统一收口;审计状态文件只能由一个会话最终更新
|
||||
- 完成标准: 统一调整总览页导航;执行 `audit_api_docs.py`、覆盖校验与链接校验;写回新的 `rebuild-status.md`
|
||||
|
||||
## 备注
|
||||
|
||||
- 如果只有 `2-3` 个会话,优先拿 `T01`、`T05`、`T07`
|
||||
- 如果有 `4-6` 个会话,推荐并行拿 `T01`、`T03`、`T04`、`T05`、`T06`、`T07`
|
||||
- `T09` 必须最后做
|
||||
@@ -1,756 +0,0 @@
|
||||
# C#脚本模块的设计与实现
|
||||
|
||||
## 1. 背景
|
||||
|
||||
XCEngine 的整体方向是模仿传统 Unity 引擎架构,而不是 DOTS/ECS-first 路线。
|
||||
在这一目标下,脚本系统应当满足以下基本预期:
|
||||
|
||||
- 脚本语言使用 `C#`
|
||||
- 脚本以“挂载到 `GameObject` 上的组件”形式工作
|
||||
- 脚本与引擎核心解耦,支持独立编译和运行时加载
|
||||
- 脚本可以逐步扩展到 Inspector、场景序列化、Play/Simulate 工作流
|
||||
|
||||
当前 `editor` 仍处于基础阶段,因此脚本系统第一阶段不应依赖 editor 完整落地。
|
||||
第一阶段的目标应当收敛为:
|
||||
|
||||
- 先完成原生运行时与托管运行时之间的桥接
|
||||
- 先完成 `ScriptComponent + C# MonoBehaviour` 的基本执行链路
|
||||
- 先完成单元测试和最小场景级验证
|
||||
- 将 editor 集成需求单独列为 issue,后续补齐
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计目标
|
||||
|
||||
### 2.1 总体目标
|
||||
|
||||
脚本系统应当提供一条接近 Unity 的开发路径:
|
||||
|
||||
1. 用户在独立的 C# 项目中编写脚本
|
||||
2. C# 脚本编译为程序集
|
||||
3. Engine Core 在运行时加载程序集
|
||||
4. `GameObject` 上挂载 `ScriptComponent`
|
||||
5. `ScriptComponent` 驱动一个对应的 C# `MonoBehaviour` 实例
|
||||
6. 脚本通过引擎暴露的 API 调用原生功能
|
||||
|
||||
### 2.2 第一阶段目标
|
||||
|
||||
第一阶段只覆盖以下内容:
|
||||
|
||||
- `engine` 内部的脚本运行时抽象
|
||||
- 第一套 C# 运行时实现
|
||||
- `ScriptComponent` 原生组件
|
||||
- `ScriptCore` 托管基础库
|
||||
- 最小可用的 `InternalCall` 绑定
|
||||
- 单元测试与最小运行时测试
|
||||
|
||||
### 2.3 第一阶段非目标
|
||||
|
||||
第一阶段明确不做以下内容:
|
||||
|
||||
- editor Inspector 脚本字段编辑
|
||||
- editor 中的脚本类选择器
|
||||
- editor 的 Play/Simulate 集成
|
||||
- 自动编译、文件监听、热重载
|
||||
- 调试器接入
|
||||
- 发布态 AOT / IL2CPP
|
||||
- 大而全的引擎 API 暴露
|
||||
|
||||
---
|
||||
|
||||
## 3. 三方对比结论
|
||||
|
||||
### 3.1 Unity
|
||||
|
||||
Unity 的典型脚本模型有几个关键特征:
|
||||
|
||||
- 用户脚本通常继承 `MonoBehaviour`
|
||||
- 一段脚本本质上就是一个组件实例
|
||||
- 脚本字段可序列化、可在 Inspector 中编辑
|
||||
- 生命周期完整,且与 `GameObject active` / `Component enabled` 语义一致
|
||||
- Play 模式运行的是运行时场景副本,而不是直接修改编辑场景
|
||||
|
||||
### 3.2 参考项目 Fermion
|
||||
|
||||
参考项目已经实现了一套 C# 脚本模块,优点主要在于:
|
||||
|
||||
- 已经证明了 `Mono embedding + InternalCall + C# 程序集加载` 这条路线可行
|
||||
- 已经具备脚本类发现、脚本实例创建、字段反射、运行时调用
|
||||
- 已经有托管侧 API 包装层和原生侧 `ScriptGlue`
|
||||
|
||||
但它的对象模型并不是 Unity 风格:
|
||||
|
||||
- 托管脚本类继承的是 `Entity`
|
||||
- 原生挂载结构是 `ScriptContainerComponent`
|
||||
- 更像“一个实体挂多个脚本类名”,而不是“每个脚本本身就是一个组件”
|
||||
- 生命周期目前主要聚焦 `OnCreate/OnUpdate`
|
||||
|
||||
结论:
|
||||
|
||||
- Fermion 适合借鉴运行时技术路线
|
||||
- Fermion 不适合作为最终 API 形态的直接模板
|
||||
|
||||
### 3.3 XCEngine 当前情况
|
||||
|
||||
XCEngine 当前已经具备以下基础:
|
||||
|
||||
- 已有 `GameObject + Component + Scene` 模型
|
||||
- `Component` 已定义 Unity 风格生命周期接口
|
||||
- `Scene` 和 `GameObject` 已有序列化入口
|
||||
- `ComponentFactoryRegistry` 已支持按类型名恢复组件
|
||||
|
||||
但当前也存在会直接影响脚本系统设计的现实约束:
|
||||
|
||||
- `Scene::Update/FixedUpdate/LateUpdate` 目前只遍历根对象
|
||||
- `GameObject::Update/FixedUpdate/LateUpdate` 目前不递归子对象
|
||||
- `Start` 的场景级一次性调度路径还未完整建立
|
||||
- `SetActive` 尚未真正驱动 `OnEnable/OnDisable`
|
||||
- editor 的 Play/Simulate 工作流仍未落地
|
||||
- `GameObject UUID` 当前未进入场景序列化主路径
|
||||
|
||||
结论:
|
||||
|
||||
- XCEngine 适合走 Unity 风格脚本模型
|
||||
- 但脚本系统第一阶段必须连同一部分运行时地基一起建设
|
||||
|
||||
---
|
||||
|
||||
## 4. 总体设计结论
|
||||
|
||||
XCEngine 的 C# 脚本模块采用以下路线:
|
||||
|
||||
- **脚本语言**:C#
|
||||
- **脚本挂载模型**:原生 `ScriptComponent` 对应托管 `MonoBehaviour`
|
||||
- **程序集模型**:`ScriptCore` 与 `GameScripts` 分离
|
||||
- **运行时加载模式**:独立编译,运行时加载
|
||||
- **桥接方式**:`InternalCall`
|
||||
- **运行时抽象策略**:先抽象接口,再优先落 Mono 实现
|
||||
- **第一阶段验证方式**:单元测试优先,不依赖 editor
|
||||
|
||||
这条路线有两个核心原则:
|
||||
|
||||
1. API 形态尽量接近 Unity
|
||||
2. 第一阶段严格控制范围,只做运行时闭环和测试闭环
|
||||
|
||||
---
|
||||
|
||||
## 5. 运行时选型
|
||||
|
||||
### 5.1 第一阶段选型:Mono
|
||||
|
||||
第一阶段建议使用 `Mono` 作为第一套 C# 运行时实现,原因如下:
|
||||
|
||||
- 与现有规划文档保持一致
|
||||
- 参考项目已经证明这条技术路线可落地
|
||||
- `InternalCall` 路线成熟,适合快速建立最小可用系统
|
||||
- 便于在 Windows 环境中先做出可运行结果
|
||||
|
||||
### 5.2 选型边界
|
||||
|
||||
本设计不把 `Mono` 写死为脚本系统唯一实现,而是将其作为第一实现:
|
||||
|
||||
- 对外暴露 `IScriptRuntime` / `ScriptEngine` 抽象
|
||||
- `MonoScriptRuntime` 作为第一套后端
|
||||
- 后续如有需要,可以演进到 `CoreCLR` 或其他运行时
|
||||
|
||||
### 5.3 第一阶段不做的运行时能力
|
||||
|
||||
Mono 相关的以下复杂能力不进入第一阶段:
|
||||
|
||||
- 域热重载
|
||||
- 编辑器内自动重编译后重载
|
||||
- 托管调试器接入
|
||||
- 发布态 AOT
|
||||
|
||||
第一阶段仅要求:
|
||||
|
||||
- 初始化运行时
|
||||
- 加载核心程序集
|
||||
- 加载用户程序集
|
||||
- 发现脚本类
|
||||
- 实例化对象
|
||||
- 调用生命周期
|
||||
- 读写托管字段
|
||||
|
||||
---
|
||||
|
||||
## 6. 模块划分
|
||||
|
||||
### 6.1 原生侧模块
|
||||
|
||||
建议在 `engine` 内新增 `Scripting` 模块:
|
||||
|
||||
```text
|
||||
engine/
|
||||
├── include/XCEngine/Scripting/
|
||||
│ ├── ScriptEngine.h
|
||||
│ ├── IScriptRuntime.h
|
||||
│ ├── ScriptAssembly.h
|
||||
│ ├── ScriptClass.h
|
||||
│ ├── ScriptInstance.h
|
||||
│ ├── ScriptField.h
|
||||
│ ├── ScriptFieldStorage.h
|
||||
│ ├── ScriptGlue.h
|
||||
│ └── ScriptComponent.h
|
||||
└── src/Scripting/
|
||||
├── ScriptEngine.cpp
|
||||
├── ScriptGlue.cpp
|
||||
├── ScriptComponent.cpp
|
||||
└── Mono/
|
||||
├── MonoScriptRuntime.cpp
|
||||
├── MonoScriptClass.cpp
|
||||
└── MonoScriptAssembly.cpp
|
||||
```
|
||||
|
||||
### 6.2 托管侧模块
|
||||
|
||||
建议新增托管核心库 `ScriptCore`:
|
||||
|
||||
```text
|
||||
managed/
|
||||
├── XCEngine.ScriptCore/
|
||||
│ ├── XCEngine.ScriptCore.csproj
|
||||
│ ├── Object.cs
|
||||
│ ├── Component.cs
|
||||
│ ├── Behaviour.cs
|
||||
│ ├── MonoBehaviour.cs
|
||||
│ ├── GameObject.cs
|
||||
│ ├── Transform.cs
|
||||
│ ├── Debug.cs
|
||||
│ ├── Time.cs
|
||||
│ └── InternalCalls.cs
|
||||
└── GameScripts/
|
||||
├── GameScripts.csproj
|
||||
└── Scripts/*.cs
|
||||
```
|
||||
|
||||
### 6.3 程序集分层
|
||||
|
||||
程序集分为两层:
|
||||
|
||||
- `XCEngine.ScriptCore.dll`
|
||||
- 由引擎维护
|
||||
- 提供托管基类和引擎 API 包装
|
||||
- `GameScripts.dll`
|
||||
- 由项目侧维护
|
||||
- 编写具体游戏脚本
|
||||
|
||||
关系如下:
|
||||
|
||||
```text
|
||||
GameScripts.dll
|
||||
└── 引用 XCEngine.ScriptCore.dll
|
||||
|
||||
Engine Core
|
||||
├── 先加载 XCEngine.ScriptCore.dll
|
||||
└── 再加载 GameScripts.dll
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 对象模型
|
||||
|
||||
### 7.1 托管侧模型
|
||||
|
||||
托管侧应当采用接近 Unity 的对象层次:
|
||||
|
||||
```text
|
||||
Object
|
||||
└── Component
|
||||
└── Behaviour
|
||||
└── MonoBehaviour
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- `Object`:基础托管对象
|
||||
- `Component`:挂载到 `GameObject` 上的托管组件基类
|
||||
- `Behaviour`:带 `enabled` 语义的组件
|
||||
- `MonoBehaviour`:用户脚本直接继承的基类
|
||||
|
||||
### 7.2 原生挂载模型
|
||||
|
||||
原生侧脚本挂载使用 `ScriptComponent`,而不是 `ScriptContainerComponent`。
|
||||
|
||||
原因如下:
|
||||
|
||||
- 更符合 Unity 认知模型
|
||||
- 更容易与现有 `GameObject::AddComponent<T>` 思路对齐
|
||||
- 更容易在未来做 Inspector 级脚本组件显示
|
||||
- 更容易把字段序列化与组件实例对应起来
|
||||
|
||||
建议 `ScriptComponent` 至少包含:
|
||||
|
||||
- `scriptComponentUUID`
|
||||
- `assemblyName`
|
||||
- `namespaceName`
|
||||
- `className`
|
||||
- `enabled`
|
||||
- `fieldStorage`
|
||||
|
||||
推荐接口示意:
|
||||
|
||||
```cpp
|
||||
class ScriptComponent : public Component {
|
||||
public:
|
||||
std::string GetName() const override { return "Script"; }
|
||||
|
||||
const std::string& GetAssemblyName() const;
|
||||
const std::string& GetNamespaceName() const;
|
||||
const std::string& GetClassName() const;
|
||||
std::string GetFullClassName() const;
|
||||
|
||||
uint64_t GetScriptComponentUUID() const;
|
||||
bool IsRuntimeValid() const;
|
||||
|
||||
void Serialize(std::ostream& os) const override;
|
||||
void Deserialize(std::istream& is) override;
|
||||
|
||||
private:
|
||||
uint64_t m_scriptComponentUUID = 0;
|
||||
std::string m_assemblyName = "GameScripts";
|
||||
std::string m_namespaceName;
|
||||
std::string m_className;
|
||||
ScriptFieldStorage m_fieldStorage;
|
||||
};
|
||||
```
|
||||
|
||||
### 7.3 多脚本挂载
|
||||
|
||||
一个 `GameObject` 应允许挂载多个 `ScriptComponent`。
|
||||
|
||||
这与 Unity 保持一致:
|
||||
|
||||
- 同一个对象可以挂多个不同脚本
|
||||
- 同一个脚本是否允许重复挂载,由后续属性或规则控制
|
||||
- 第一阶段不做“禁止重复挂载”的复杂策略
|
||||
|
||||
---
|
||||
|
||||
## 8. 身份模型与序列化要求
|
||||
|
||||
### 8.1 必须使用 UUID,而不是运行时 ID
|
||||
|
||||
脚本系统中,托管实例与原生对象的稳定绑定必须建立在 `UUID` 之上,而不是当前的自增 `ID`。
|
||||
|
||||
原因如下:
|
||||
|
||||
- 运行时场景复制不能依赖自增 ID 稳定
|
||||
- 脚本字段里的对象引用必须有稳定键
|
||||
- editor 与 runtime 之间的对象映射必须有稳定键
|
||||
- 单元测试和场景恢复也需要稳定身份
|
||||
|
||||
因此需要补齐以下要求:
|
||||
|
||||
- `GameObject UUID` 进入场景序列化
|
||||
- 场景反序列化时恢复 `UUID`
|
||||
- `ScriptComponent` 自身也应有持久化 UUID
|
||||
|
||||
### 8.2 第一阶段字段序列化策略
|
||||
|
||||
第一阶段的脚本字段序列化原则如下:
|
||||
|
||||
- 只序列化 `ScriptComponent` 的字段缓存
|
||||
- 只序列化脚本作者显式设置的字段值
|
||||
- 运行时脚本执行过程中修改的值,不自动回写场景
|
||||
|
||||
这与 Unity 的 Play 模式行为一致:
|
||||
|
||||
- Play 中的运行时改动不应直接污染编辑数据
|
||||
|
||||
### 8.3 第一阶段字段支持范围
|
||||
|
||||
建议第一阶段先支持:
|
||||
|
||||
- `float`
|
||||
- `double`
|
||||
- `bool`
|
||||
- `int32`
|
||||
- `uint64`
|
||||
- `string`
|
||||
- `Vector2`
|
||||
- `Vector3`
|
||||
- `Vector4`
|
||||
- `GameObject` 引用
|
||||
|
||||
第一阶段不要求支持:
|
||||
|
||||
- `List<T>`
|
||||
- 自定义托管结构体
|
||||
- 嵌套对象图
|
||||
- 泛型容器
|
||||
- 资源引用对象选择器
|
||||
|
||||
---
|
||||
|
||||
## 9. 生命周期设计
|
||||
|
||||
### 9.1 目标生命周期
|
||||
|
||||
脚本系统最终应支持以下 Unity 风格生命周期:
|
||||
|
||||
- `Awake`
|
||||
- `OnEnable`
|
||||
- `Start`
|
||||
- `FixedUpdate`
|
||||
- `Update`
|
||||
- `LateUpdate`
|
||||
- `OnDisable`
|
||||
- `OnDestroy`
|
||||
|
||||
### 9.2 第一阶段生命周期闭环
|
||||
|
||||
第一阶段就应当把上述生命周期的原生调度链路设计好,哪怕 editor 尚未接入。
|
||||
|
||||
建议运行时流程如下:
|
||||
|
||||
#### `Scene` 运行时启动
|
||||
|
||||
1. `ScriptEngine::OnRuntimeStart(scene)`
|
||||
2. 遍历场景中全部激活对象
|
||||
3. 找到所有 `ScriptComponent`
|
||||
4. 为每个组件创建托管 `MonoBehaviour` 实例
|
||||
5. 写入原生对象 UUID / 组件 UUID / 基础上下文
|
||||
6. 应用序列化字段缓存
|
||||
7. 调用 `Awake`
|
||||
8. 若对象激活且组件启用,调用 `OnEnable`
|
||||
9. 标记“等待 Start”
|
||||
|
||||
#### 每帧执行
|
||||
|
||||
- 物理阶段:`FixedUpdate`
|
||||
- 普通阶段:`Update`
|
||||
- 后处理阶段:`LateUpdate`
|
||||
- 对于尚未执行 `Start` 的脚本,在第一次普通帧前先调用 `Start`
|
||||
|
||||
#### 运行时停止
|
||||
|
||||
1. 对仍处于启用状态的脚本调用 `OnDisable`
|
||||
2. 对所有脚本调用 `OnDestroy`
|
||||
3. 清理托管实例表
|
||||
4. 清理运行时场景上下文
|
||||
|
||||
### 9.3 对现有引擎的前置要求
|
||||
|
||||
为了让脚本生命周期符合预期,现有引擎需要补齐以下地基:
|
||||
|
||||
- `Scene` 更新必须递归整个层级,而不是只更新根对象
|
||||
- `Start` 必须具备“一次且仅一次”语义
|
||||
- `SetActive` 必须驱动 `OnEnable/OnDisable`
|
||||
- 运行时场景启动与停止必须显式化
|
||||
- 创建对象时不应直接把“编辑态创建”与“运行态 Awake”混为一谈
|
||||
|
||||
这些工作虽然不都属于脚本模块,但它们是脚本模块的直接运行前提。
|
||||
|
||||
---
|
||||
|
||||
## 10. 原生与托管之间的桥接
|
||||
|
||||
### 10.1 桥接方式
|
||||
|
||||
第一阶段使用 `InternalCall`:
|
||||
|
||||
- 托管侧通过 `MethodImplOptions.InternalCall` 声明方法
|
||||
- 原生侧通过 `mono_add_internal_call` 注册
|
||||
|
||||
### 10.2 第一阶段最小 API 集
|
||||
|
||||
第一阶段建议只暴露最小必需 API:
|
||||
|
||||
- `Debug.Log / LogWarning / LogError`
|
||||
- `Time.deltaTime`
|
||||
- `GameObject.GetName / SetName`
|
||||
- `GameObject.GetTransform`
|
||||
- `Component.GetGameObject`
|
||||
- `GameObject.HasComponent<T>`
|
||||
- `GameObject.GetComponent<T>`
|
||||
- `Transform` 的本地位置 / 旋转 / 缩放
|
||||
|
||||
这套 API 足够覆盖以下测试与最小演示:
|
||||
|
||||
- 变换脚本
|
||||
- 旋转/移动脚本
|
||||
- 生命周期日志验证
|
||||
- 组件访问验证
|
||||
|
||||
第一阶段不建议优先暴露:
|
||||
|
||||
- 物理 API
|
||||
- 渲染 API
|
||||
- 音频 API
|
||||
- 输入系统
|
||||
- 资源系统
|
||||
|
||||
原因很简单:
|
||||
|
||||
- 第一阶段以单元测试闭环为主
|
||||
- 暴露面越大,绑定维护成本越高
|
||||
- 当前这些系统本身仍在演进
|
||||
|
||||
---
|
||||
|
||||
## 11. 类发现与实例管理
|
||||
|
||||
### 11.1 脚本类发现规则
|
||||
|
||||
用户脚本类应满足以下条件才被视为可挂载脚本:
|
||||
|
||||
- 定义在 `GameScripts.dll`
|
||||
- 非抽象类
|
||||
- 继承 `XCEngine.MonoBehaviour`
|
||||
|
||||
### 11.2 缓存结构
|
||||
|
||||
原生运行时需要缓存以下信息:
|
||||
|
||||
- 程序集表
|
||||
- 脚本类表
|
||||
- 方法句柄表
|
||||
- 字段元数据表
|
||||
- 运行时实例表
|
||||
|
||||
建议实例表键使用:
|
||||
|
||||
- `GameObjectUUID + ScriptComponentUUID`
|
||||
|
||||
而不是:
|
||||
|
||||
- 内存地址
|
||||
- 组件在容器中的索引
|
||||
- 自增 ID
|
||||
|
||||
### 11.3 方法缓存
|
||||
|
||||
每个脚本类应缓存常用生命周期方法句柄:
|
||||
|
||||
- `Awake`
|
||||
- `OnEnable`
|
||||
- `Start`
|
||||
- `FixedUpdate`
|
||||
- `Update`
|
||||
- `LateUpdate`
|
||||
- `OnDisable`
|
||||
- `OnDestroy`
|
||||
|
||||
这样可以避免每帧按字符串查找方法。
|
||||
|
||||
---
|
||||
|
||||
## 12. 单元测试优先策略
|
||||
|
||||
### 12.1 原则
|
||||
|
||||
第一阶段脚本模块不依赖 editor,因此验证策略以单元测试和最小运行时测试为主。
|
||||
|
||||
### 12.2 测试目录建议
|
||||
|
||||
```text
|
||||
tests/
|
||||
└── Scripting/
|
||||
├── unit/
|
||||
│ ├── test_script_runtime.cpp
|
||||
│ ├── test_script_metadata.cpp
|
||||
│ ├── test_script_component.cpp
|
||||
│ ├── test_script_fields.cpp
|
||||
│ └── CMakeLists.txt
|
||||
└── managed/
|
||||
├── XCEngine.ScriptCore/
|
||||
└── TestScripts/
|
||||
```
|
||||
|
||||
### 12.3 第一阶段必须覆盖的测试
|
||||
|
||||
#### 运行时初始化
|
||||
|
||||
- 能初始化脚本运行时
|
||||
- 能加载 `ScriptCore`
|
||||
- 能加载测试脚本程序集
|
||||
|
||||
#### 类发现
|
||||
|
||||
- 只发现继承 `MonoBehaviour` 的类
|
||||
- 忽略抽象类
|
||||
- 忽略普通工具类
|
||||
|
||||
#### 生命周期
|
||||
|
||||
- 能创建托管实例
|
||||
- 能按顺序触发 `Awake -> OnEnable -> Start -> Update`
|
||||
- 能在停止时触发 `OnDisable -> OnDestroy`
|
||||
|
||||
#### 组件桥接
|
||||
|
||||
- 脚本能访问 `GameObject`
|
||||
- 脚本能访问 `Transform`
|
||||
- 脚本能访问最小组件 API
|
||||
|
||||
#### 字段系统
|
||||
|
||||
- 能发现公共字段
|
||||
- 能读写字段
|
||||
- 字段缓存可回填到托管实例
|
||||
- 场景序列化后字段值不丢失
|
||||
|
||||
#### 多脚本对象
|
||||
|
||||
- 同一 `GameObject` 上多个 `ScriptComponent` 都可实例化
|
||||
- 不同脚本实例之间不会串字段
|
||||
|
||||
#### UUID 绑定
|
||||
|
||||
- 运行时复制或反序列化后仍可稳定恢复脚本绑定键
|
||||
|
||||
### 12.4 第一阶段不要求的测试
|
||||
|
||||
- editor Inspector UI
|
||||
- 热重载
|
||||
- 编译器输出面板
|
||||
- 文件监听
|
||||
- 资源拖拽
|
||||
|
||||
---
|
||||
|
||||
## 13. 建议的实现顺序
|
||||
|
||||
### 阶段 A:补运行时地基
|
||||
|
||||
先补以下基础能力:
|
||||
|
||||
- `GameObject UUID` 序列化
|
||||
- 层级递归更新
|
||||
- `Start` 一次性语义
|
||||
- `SetActive` 对生命周期的影响
|
||||
- `Scene` 运行时启动/停止接口
|
||||
|
||||
### 阶段 B:脚本最小闭环
|
||||
|
||||
完成:
|
||||
|
||||
- `Scripting` 模块骨架
|
||||
- `ScriptComponent`
|
||||
- `MonoScriptRuntime`
|
||||
- `XCEngine.ScriptCore.dll`
|
||||
- `GameScripts.dll`
|
||||
- 最小 `InternalCall`
|
||||
|
||||
### 阶段 C:字段与序列化
|
||||
|
||||
完成:
|
||||
|
||||
- 脚本字段元数据
|
||||
- 字段缓存
|
||||
- `ScriptComponent` 序列化/反序列化
|
||||
- 字段相关单元测试
|
||||
|
||||
### 阶段 D:最小运行时演示
|
||||
|
||||
在不依赖 editor 的前提下完成:
|
||||
|
||||
- 纯运行时测试场景
|
||||
- 一个或两个最小脚本示例
|
||||
- Play 级别运行验证
|
||||
|
||||
### 阶段 E:editor 集成
|
||||
|
||||
后续再做:
|
||||
|
||||
- 脚本组件 Inspector
|
||||
- 类选择器
|
||||
- 编译按钮
|
||||
- 错误输出
|
||||
- 重载流程
|
||||
|
||||
---
|
||||
|
||||
## 14. 与 editor 的关系
|
||||
|
||||
第一阶段文档明确规定:
|
||||
|
||||
- C# 脚本模块**不依赖 editor 完整落地**
|
||||
- editor 相关工作全部后置
|
||||
- editor 相关缺口统一记录在 `docs/issues`
|
||||
|
||||
第一阶段唯一允许与 editor 共用的内容是:
|
||||
|
||||
- 场景序列化格式
|
||||
- `GameObject UUID` 语义
|
||||
- 运行时场景副本的总体设计方向
|
||||
|
||||
除此之外,不应把脚本模块第一阶段实现建立在 editor 已具备以下能力的假设上:
|
||||
|
||||
- Play/Simulate 控制条
|
||||
- Inspector 自定义字段绘制
|
||||
- 项目内脚本自动编译
|
||||
- 脚本异常面板
|
||||
|
||||
---
|
||||
|
||||
## 15. 风险与权衡
|
||||
|
||||
### 15.1 Mono 依赖
|
||||
|
||||
风险:
|
||||
|
||||
- Windows 环境需要显式安装或打包 Mono
|
||||
- CMake 配置与发布路径管理会变复杂
|
||||
|
||||
权衡:
|
||||
|
||||
- 第一阶段优先解决“能跑起来”的问题
|
||||
- 运行时抽象保留未来替换空间
|
||||
|
||||
### 15.2 生命周期与现有引擎实现的偏差
|
||||
|
||||
风险:
|
||||
|
||||
- 如果继续保留当前“创建对象就立即 `Awake`”的行为,脚本生命周期会混乱
|
||||
|
||||
权衡:
|
||||
|
||||
- 脚本系统建设必须带动运行时生命周期整理
|
||||
|
||||
### 15.3 字段系统范围
|
||||
|
||||
风险:
|
||||
|
||||
- 一开始就追求完整序列化会让范围失控
|
||||
|
||||
权衡:
|
||||
|
||||
- 第一阶段只做基础类型与少量引用类型
|
||||
- 复杂容器和高级序列化后置
|
||||
|
||||
### 15.4 editor 后置
|
||||
|
||||
风险:
|
||||
|
||||
- 第一阶段用户体验不完整
|
||||
|
||||
权衡:
|
||||
|
||||
- 可以显著降低实现风险
|
||||
- 可以先通过单测和运行时样例把底层做稳
|
||||
|
||||
---
|
||||
|
||||
## 16. 最终结论
|
||||
|
||||
XCEngine 的 C# 脚本系统应当采用:
|
||||
|
||||
- `ScriptComponent + MonoBehaviour`
|
||||
- `ScriptCore + GameScripts` 双程序集结构
|
||||
- `InternalCall` 桥接
|
||||
- `Mono` 作为第一套运行时实现
|
||||
- `UUID` 作为绑定与序列化的稳定身份
|
||||
- 第一阶段只做运行时与单元测试,不依赖 editor
|
||||
|
||||
这条路线既保留了 Unity 风格的一致性,也能适配当前工程实际进度。
|
||||
|
||||
第一阶段的成功标准不是“Inspector 能编辑脚本字段”,而是:
|
||||
|
||||
- 能加载脚本程序集
|
||||
- 能发现脚本类
|
||||
- 能在场景运行时驱动 `MonoBehaviour`
|
||||
- 能通过单元测试验证生命周期、字段和绑定语义
|
||||
|
||||
达到这一点后,再进入 editor 集成阶段,工程风险会显著更低。
|
||||
@@ -1,128 +0,0 @@
|
||||
# Editor Viewport 宿主渲染收口总结 4.1
|
||||
|
||||
## 当前判断
|
||||
|
||||
截至 2026-04-01,这一阶段的主线应视为:
|
||||
|
||||
- 把 `Editor -> ViewportHost -> Renderer -> RHI` 这条链路接通
|
||||
- 把 editor viewport 的宿主层从 panel 内部逻辑中收出来
|
||||
- 把这层宿主代码压到“可继续演进,但先停止扩张”的状态
|
||||
|
||||
这一阶段的目标不是继续往 editor viewport 里塞更多功能,也不是提前做完未来 renderer 的全部能力。
|
||||
|
||||
如果继续在本阶段里混入 gizmo 完整体系、GPU picking 正式方案、多 pass 大改、后处理、光照系统等内容,只会让阶段边界再次失控。
|
||||
|
||||
## 已完成的收口结果
|
||||
|
||||
### 1. editor viewport 已经不再是空壳
|
||||
|
||||
当前 `SceneView` 与 `GameView` 已经能通过统一的 viewport host 路径请求离屏 render target,并把 renderer 输出展示到 editor 面板中。
|
||||
|
||||
当前已实际接通:
|
||||
|
||||
- Scene viewport 渲染
|
||||
- Game viewport 渲染
|
||||
- viewport resize 资源重建
|
||||
- backpack 等真实模型内容在 editor 中显示
|
||||
- grid / 选中 / 描边 / object id 读取链路
|
||||
|
||||
### 2. viewport host 的职责边界已经成型
|
||||
|
||||
当前 viewport 宿主层已经被拆成几块相对明确的职责:
|
||||
|
||||
- [editor/src/Viewport/ViewportHostSurfaceUtils.h](D:/Xuanchi/Main/XCEngine/editor/src/Viewport/ViewportHostSurfaceUtils.h)
|
||||
- viewport surface / texture / reuse 相关纯工具
|
||||
- [editor/src/Viewport/ViewportHostRenderTargets.h](D:/Xuanchi/Main/XCEngine/editor/src/Viewport/ViewportHostRenderTargets.h)
|
||||
- viewport render target 生命周期与创建销毁
|
||||
- [editor/src/Viewport/ViewportObjectIdPicker.h](D:/Xuanchi/Main/XCEngine/editor/src/Viewport/ViewportObjectIdPicker.h)
|
||||
- viewport object id 读取与像素映射
|
||||
- [editor/src/Viewport/ViewportHostRenderFlowUtils.h](D:/Xuanchi/Main/XCEngine/editor/src/Viewport/ViewportHostRenderFlowUtils.h)
|
||||
- scene/game viewport 的失败回退策略、request 组装、成功态迁移
|
||||
- [editor/src/Viewport/ViewportHostService.h](D:/Xuanchi/Main/XCEngine/editor/src/Viewport/ViewportHostService.h)
|
||||
- 保留真正的 orchestration、scene view camera、post pass 组装与 clear 执行
|
||||
|
||||
这意味着 viewport host 已经不再是“一个面板私有的大杂烩类”,而是一个有明确宿主边界的 editor 侧接入层。
|
||||
|
||||
### 3. 现阶段测试闭环已经补上
|
||||
|
||||
当前 editor 侧已经有明确的宿主层单测覆盖:
|
||||
|
||||
- [tests/editor/test_viewport_host_surface_utils.cpp](D:/Xuanchi/Main/XCEngine/tests/editor/test_viewport_host_surface_utils.cpp)
|
||||
- [tests/editor/test_viewport_render_targets.cpp](D:/Xuanchi/Main/XCEngine/tests/editor/test_viewport_render_targets.cpp)
|
||||
- [tests/editor/test_viewport_object_id_picker.cpp](D:/Xuanchi/Main/XCEngine/tests/editor/test_viewport_object_id_picker.cpp)
|
||||
- [tests/editor/test_viewport_render_flow_utils.cpp](D:/Xuanchi/Main/XCEngine/tests/editor/test_viewport_render_flow_utils.cpp)
|
||||
|
||||
本轮收口完成后,`editor_tests` 已覆盖 viewport host 中最容易回归的纯策略与资源管理逻辑。
|
||||
|
||||
## 为什么到这里应当停止继续拆
|
||||
|
||||
当前 [editor/src/Viewport/ViewportHostService.h](D:/Xuanchi/Main/XCEngine/editor/src/Viewport/ViewportHostService.h) 剩下的主要内容,是以下几类“真正属于宿主编排”的职责:
|
||||
|
||||
- scene view camera 的创建与驱动
|
||||
- focus / orientation axis 等 editor 专属视图控制
|
||||
- scene view post pass 的装配
|
||||
- scene/game viewport 的 render dispatch
|
||||
- clear 执行
|
||||
|
||||
这些逻辑如果继续硬拆,很容易为了“文件更小”而把真正依赖 editor 运行时状态的 orchestration 人为打散,收益已经明显下降。
|
||||
|
||||
所以这一阶段的明确结论是:
|
||||
|
||||
- `ViewportHostService` 继续保持为宿主编排入口是合理的
|
||||
- 不再以“继续拆文件”为本阶段目标
|
||||
- 下一阶段应把重点转移到 renderer 本身的演进,而不是继续挤压 editor host
|
||||
|
||||
## 本阶段的结束标准
|
||||
|
||||
这一阶段以以下标准视为完成:
|
||||
|
||||
1. `editor_tests` 全部通过
|
||||
2. `XCEditor` 能正常编译
|
||||
3. viewport host 的主要纯逻辑都有独立单测
|
||||
4. `ViewportHostService` 不再继续承载资源创建、object id 读取、失败策略等细碎职责
|
||||
5. 明确哪些问题属于后续 renderer 阶段,而不是继续留在当前阶段消耗
|
||||
|
||||
## 明确不属于本阶段的内容
|
||||
|
||||
以下内容不再计入“editor viewport 宿主渲染收口”阶段:
|
||||
|
||||
- GPU object id 正式拾取方案
|
||||
- renderer 内部通用多 pass / render graph 体系
|
||||
- 更正式的 editor outline 方案升级
|
||||
- gizmo 全量产品化
|
||||
- 光照、阴影、后处理
|
||||
- runtime 渲染管线的完整 SRP 式抽象
|
||||
|
||||
这些内容应进入 renderer 后续阶段,而不是继续塞回当前 viewport host 收口任务。
|
||||
|
||||
## 下一阶段建议
|
||||
|
||||
下一阶段主线应转为:
|
||||
|
||||
### 1. renderer 能力继续上移
|
||||
|
||||
把目前 editor 层为了接通 viewport 而保留的一些临时职责,逐步让 renderer 吸收为正式能力,例如:
|
||||
|
||||
- 多 pass 组织能力
|
||||
- 正式的 object id / editor helper pass 接口
|
||||
- editor 与 runtime 共享的 camera render path
|
||||
|
||||
### 2. editor 只消费 renderer 提供的正式输出
|
||||
|
||||
editor viewport 后续应更多扮演:
|
||||
|
||||
- render target 宿主
|
||||
- 输入转发
|
||||
- overlay / gizmo 宿主
|
||||
- editor 专属交互入口
|
||||
|
||||
而不是继续承载 renderer 内部演进本体。
|
||||
|
||||
## 阶段性结论
|
||||
|
||||
这一阶段现在可以正式收口:
|
||||
|
||||
- editor viewport 已经从“空面板”进入“真实宿主层”
|
||||
- renderer 与 RHI 已经能稳定把离屏结果送进 editor
|
||||
- viewport host 的边界已经明确
|
||||
- 后续不应继续在本阶段内部无限拆分,而应切换到 renderer 下一阶段
|
||||
@@ -6,15 +6,19 @@
|
||||
|
||||
- 把视觉样式、交互路由、编辑命令、dock 布局、面板壳层拆开。
|
||||
- 把 `Hierarchy / Project / Inspector / Console / MenuBar` 统一到同一套 shared UI 和 action 语义上。
|
||||
- 保持 `Scene / Game` 先作为空壳 panel,等待后续 `Viewport / RHI` 回归。
|
||||
- 把 `Scene / Game` viewport 保持在当前真实主链上:`ViewportHostService -> Rendering + RHI -> ImGui panel`。
|
||||
- 把 `Assets + .meta + Library` 项目工作流、脚本程序集构建与运行时状态纳入 editor 正式分层,而不是继续当外围脚本。
|
||||
|
||||
这意味着当前 editor 的重点是“编辑器外壳架构完整”,不是“运行时视口功能完整”。
|
||||
这意味着当前 editor 的重点已经不是“先把外壳搭出来”,而是:
|
||||
|
||||
- 在现有外壳分层上继续收口真实可运行的 viewport / project / scripting 主链。
|
||||
- 让 panel、viewport helper、commands、managers 和引擎侧 `Rendering / Resources / Scene / Scripting` 的边界继续清晰化。
|
||||
|
||||
## 2. 总体分层
|
||||
|
||||
当前 editor 推荐按下面的依赖方向理解:
|
||||
|
||||
`Application -> EditorLayer -> EditorWorkspace -> Panels -> Actions -> Commands -> Managers/Core`
|
||||
`Application -> EditorLayer -> EditorWorkspace -> Panels/Viewport -> Actions -> Commands -> Managers/Core`
|
||||
|
||||
同时还有一条横向的共享 UI 层:
|
||||
|
||||
@@ -24,6 +28,10 @@
|
||||
|
||||
`InspectorPanel -> ComponentEditorRegistry -> IComponentEditor`
|
||||
|
||||
以及一条已经落地的 editor 到引擎主链:
|
||||
|
||||
`Viewport / Managers / Scripting -> Rendering / Resources / Scene / Scripting / RHI`
|
||||
|
||||
允许依赖的基本原则:
|
||||
|
||||
- `UI` 只负责样式 token、共享控件、popup/property-grid 等表现层能力,不承担业务语义。
|
||||
@@ -32,6 +40,7 @@
|
||||
- `Panels` 只保留最小的渲染壳层和少量局部瞬时状态,不直接堆业务逻辑。
|
||||
- `Layout` 只负责 dockspace 和布局持久化,不处理 scene/project 业务。
|
||||
- `Core/Managers` 提供 editor 运行时共享状态与数据入口。
|
||||
- `Viewport` 是 editor 宿主和引擎渲染主链之间的正式桥接层,不是 panel 内随手拼的辅助代码。
|
||||
|
||||
不允许继续扩散的方向:
|
||||
|
||||
@@ -272,6 +281,60 @@
|
||||
3. 在 `ComponentEditorRegistry.cpp` 注册。
|
||||
4. 如果涉及复杂交互,优先复用 `UI::PropertyGrid` 和 undo interactive change 机制。
|
||||
|
||||
### 3.10 Viewport
|
||||
|
||||
关键文件:
|
||||
|
||||
- `editor/src/Viewport/ViewportHostService.h`
|
||||
- `editor/src/Viewport/IViewportHostService.h`
|
||||
- `editor/src/Viewport/SceneViewportChrome.h`
|
||||
- `editor/src/Viewport/SceneViewportInteractionFrame.h`
|
||||
- `editor/src/Viewport/SceneViewportNavigation.h`
|
||||
- `editor/src/Viewport/SceneViewportTransformGizmoCoordinator.h`
|
||||
- `editor/src/Viewport/SceneViewportOverlayBuilder.h`
|
||||
- `editor/src/Viewport/SceneViewportOverlayFrameCache.h`
|
||||
- `editor/src/Viewport/SceneViewportOverlaySpriteResources.h`
|
||||
- `editor/src/Viewport/SceneViewportResourcePaths.h`
|
||||
- `editor/src/Viewport/Passes/SceneViewportEditorOverlayPass.h`
|
||||
|
||||
职责:
|
||||
|
||||
- 作为 editor 宿主与引擎 `Rendering + RHI` 的桥接层,维护 Scene / Game viewport 的离屏渲染接线。
|
||||
- 维护当前 Scene View helper 主链:
|
||||
- `SceneViewportChrome`
|
||||
- `SceneViewportInteractionFrame`
|
||||
- `SceneViewportNavigation`
|
||||
- `SceneViewportTransformGizmoCoordinator`
|
||||
- `ViewportHostService`
|
||||
- 负责 overlay builder、overlay frame cache、sprite 资源准备、object-id picking、grid、outline 和 gizmo overlay state。
|
||||
|
||||
边界:
|
||||
|
||||
- `Viewport` 是正式子系统,不再是“等待未来回归”的空位。
|
||||
- panel 不应自己拼接一套独立渲染路径;新的 viewport 行为优先落到 helper、host service 或 overlay pass。
|
||||
- `SceneViewportShaderPaths.h` 当前主要是兼容 include;资源路径真实 owner 已经转到 `SceneViewportResourcePaths.h`。
|
||||
|
||||
### 3.11 Scripting / Project Workflow
|
||||
|
||||
关键文件:
|
||||
|
||||
- `editor/src/Scripting/EditorScriptAssemblyBuilder.h`
|
||||
- `editor/src/Scripting/EditorScriptRuntimeStatus.h`
|
||||
- `editor/src/Managers/ProjectManager.h`
|
||||
- `editor/src/Managers/SceneManager.h`
|
||||
- `editor/src/Utils/ProjectFileUtils.h`
|
||||
|
||||
职责:
|
||||
|
||||
- 解析项目根目录、读写 `Project.xcproject`,并把仓库内 `project/` 或 `--project <path>` 解析为当前工程。
|
||||
- 驱动 `Assets + .meta + Library` 风格项目 workflow,包括项目资源浏览、脚本程序集重建入口和运行时状态反馈。
|
||||
- 把项目脚本程序集与 editor/runtime 实际消费的 `Library/ScriptAssemblies/` 目录连接起来。
|
||||
|
||||
边界:
|
||||
|
||||
- 与项目资产、`.meta`、`Library`、脚本程序集相关的问题,不应再按“editor 仍处于无工程状态”理解。
|
||||
- `ProjectManager`、`SceneManager`、`EditorScriptAssemblyBuilder` 与引擎侧 `ResourceManager / AssetImportService / ScriptEngine` 是协作关系,不应在 panel 里各自复制一层状态机。
|
||||
|
||||
## 4. EventBus 使用规则
|
||||
|
||||
`EventBus` 现在主要承担两类职责:
|
||||
@@ -334,16 +397,19 @@
|
||||
- dock layout controller 与 editor workspace 装配。
|
||||
- inspector 的 component editor registry 接入。
|
||||
- editor 级回归测试基础框架与关键命令测试。
|
||||
- `Scene / Game` viewport 已经重新接回当前正式主链:引擎 `Rendering + RHI` 离屏输出 -> `ViewportHostService` -> ImGui panel。
|
||||
- `Assets + .meta + Library` 项目工作流、`Project.xcproject`、脚本程序集重建与脚本运行时状态已经进入 editor 正式工作流。
|
||||
- viewport helper 已按 `Chrome -> InteractionFrame -> Navigation -> TransformGizmoCoordinator -> ViewportHostService` 显式拆层。
|
||||
|
||||
当前明确暂缓:
|
||||
|
||||
- `Scene` panel 内容。
|
||||
- `Game` panel 内容。
|
||||
- `Viewport` 与 RHI 重新接入。
|
||||
- 把 editor 主线程上残留的同步资源兜底点继续清理到更严格的异步消费模型。
|
||||
- 把 `Library bootstrap / scene streaming / explicit import` 的状态模型继续正式化到 UI。
|
||||
- 继续减少 panel 内局部瞬时状态与 helper 间重复规则。
|
||||
|
||||
原因很明确:
|
||||
|
||||
- 这三部分依赖渲染壳层与后续 renderer/RHI 重构,暂时不适合和 editor UI 架构收尾混在一起。
|
||||
- 前三项已经不是“没接回来的未来工作”,而是已经落地、但还要继续收口的当前主线。
|
||||
|
||||
## 8. 后续新增功能时的落点原则
|
||||
|
||||
@@ -355,6 +421,7 @@
|
||||
- 新 inspector 组件面板:放 `ComponentEditors`
|
||||
- 新 dock/window 布局控制:放 `Layout`
|
||||
- 新 editor 全局状态:放 `Core/Managers`
|
||||
- 新 viewport host / overlay / interaction helper:放 `Viewport`
|
||||
- 只是把已有能力拼进某个窗口:放 `Panels`
|
||||
|
||||
如果一个功能不知道放哪,一般先问自己:
|
||||
@@ -371,8 +438,9 @@
|
||||
|
||||
- 继续压缩少量 panel 本地瞬时状态,能下沉的继续下沉。
|
||||
- 继续补命令/路由回归测试,尤其是 inspector interactive undo 边界。
|
||||
- 为将来的 viewport 回归预留稳定接入口,但暂不提前接入渲染逻辑。
|
||||
- 继续补 viewport helper、project workflow、script assembly builder 与 scene streaming 相关回归测试。
|
||||
- 把 `Library bootstrap`、显式导入、后台 scene asset streaming 这三类状态在 editor UI 上分开表达。
|
||||
|
||||
结论:
|
||||
|
||||
editor 当前已经形成稳定分层,后面再做功能迭代时,应坚持“先看边界,再落代码”,不要回到 panel 内部堆逻辑的旧路线。
|
||||
editor 当前已经形成稳定分层,而且这套分层已经承接了真实的 viewport / project / scripting 工作流。后面再做功能迭代时,应坚持“先看边界,再落代码”,不要回到 panel 内部堆逻辑的旧路线。
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
# Editor 重构 3.26
|
||||
|
||||
## 当前判断
|
||||
|
||||
截至 2026-03-27,如果只看 editor 的 UI 架构与编辑器壳层整理,不把 `Viewport / RHI` 和 `Scene / Game` 的真实内容算进去,这一轮重构大约已经完成 **96%**。
|
||||
|
||||
如果把“编辑器整体可用度”一起算进去,则仍然没有结束,因为:
|
||||
|
||||
- `Scene` panel 仍然是空壳。
|
||||
- `Game` panel 仍然是空壳。
|
||||
- `Viewport` 尚未跟随 RHI 重构完成回归。
|
||||
|
||||
所以现在的真实状态是:
|
||||
|
||||
- **UI 架构已经基本收稳**
|
||||
- **编辑器产品功能还没有做完**
|
||||
|
||||
## 已经完成的核心重构
|
||||
|
||||
### 1. 分层已经基本建立
|
||||
|
||||
当前 editor 已经形成比较稳定的职责划分:
|
||||
|
||||
- `UI` 负责主题、token、共享控件、popup、property-grid。
|
||||
- `Actions` 负责菜单、快捷键、右键菜单、按钮动作的共享路由。
|
||||
- `Commands` 负责 scene/project/entity/component 的编辑行为。
|
||||
- `Layout` 负责 dock host、默认布局、布局重置与持久化。
|
||||
- `Panels` 逐步退化为窗口壳层。
|
||||
- `Core / Managers` 负责 editor context、selection、undo、scene、project 等共享状态。
|
||||
- `ComponentEditors` 负责 inspector 中各组件的编辑器与注册体系。
|
||||
|
||||
这说明 editor 已经从“每个 panel 自己堆逻辑”的结构,切换到“共享层驱动”的结构。
|
||||
|
||||
### 2. 共享 UI 基础层已经落稳
|
||||
|
||||
已经把大量原先散落在 panel 内部的视觉和控件逻辑收口到 shared UI 层,包括:
|
||||
|
||||
- 主题和样式 token
|
||||
- panel chrome
|
||||
- toolbar/search/button
|
||||
- popup / modal state
|
||||
- empty state
|
||||
- asset tile
|
||||
- hierarchy tree node
|
||||
- component section
|
||||
- property grid / scalar / vector 编辑控件
|
||||
|
||||
这部分的意义是:后面再调 editor 外观,不应该回到 panel 内部逐个修补。
|
||||
|
||||
### 3. Action 路由已经成型
|
||||
|
||||
当前已形成两条主路由:
|
||||
|
||||
- `MainMenuActionRouter`
|
||||
- `EditActionRouter`
|
||||
|
||||
并且 `Hierarchy / Project / Inspector / Console / MenuBar` 的主要菜单和快捷键语义已经接入共享 action/router。
|
||||
|
||||
当前已经做到:
|
||||
|
||||
- `Edit` 菜单不再只是一套写死逻辑,而是跟随 active action route 切换。
|
||||
- menu / shortcut / context menu / toolbar 的动作开始共用同一套 action 定义。
|
||||
- rename、popup、about、exit、reset layout 等交互已不再零散写在 panel 内部。
|
||||
|
||||
### 4. Commands 层已经承担主要编辑语义
|
||||
|
||||
当前主要编辑行为已进入 command 层:
|
||||
|
||||
- 新建 / 打开 / 保存场景
|
||||
- 脏场景 fallback 保存
|
||||
- 创建 / 删除 / 复制 / 粘贴 / duplicate / rename / reparent entity
|
||||
- 创建文件夹 / 删除资源 / 打开资源 / 移动资源
|
||||
- 添加 / 删除组件
|
||||
|
||||
同时,undo / dirty / selection reset 等关键边界,已经尽量不再散落在 panel。
|
||||
|
||||
### 5. Dock / Workspace / Application 壳层已收口
|
||||
|
||||
当前已经完成:
|
||||
|
||||
- `EditorWorkspace` 统一 panel 装配与生命周期调度
|
||||
- `DockLayoutController` 统一 dockspace 和 layout reset
|
||||
- ImGui layout 持久化到项目 `.xceditor/imgui_layout.ini`
|
||||
- `Application.cpp` 中的窗口、renderer、ImGui session、layer attach/detach 已完成明显拆分
|
||||
|
||||
这说明 editor 顶层壳层已经不再像之前那样把 UI、layout、backend、panel 生命周期混在一起。
|
||||
|
||||
### 6. Inspector 的 ComponentEditor 注册体系已稳定
|
||||
|
||||
当前 inspector 不再直接硬编码全部组件逻辑,而是通过:
|
||||
|
||||
- `IComponentEditor`
|
||||
- `ComponentEditorRegistry`
|
||||
- 各具体 `Transform / Camera / Light` component editor
|
||||
|
||||
来统一:
|
||||
|
||||
- 组件显示
|
||||
- Add Component 菜单构建
|
||||
- 组件可添加性 / 可删除性判断
|
||||
|
||||
这部分已经是后续扩展自定义组件 inspector 的正确落点。
|
||||
|
||||
### 7. 回归测试基础已补齐
|
||||
|
||||
当前已新增 `editor_tests`,并已覆盖关键 editor 行为,包括:
|
||||
|
||||
- hierarchy edit route 的 copy / paste / duplicate / delete / rename request
|
||||
- project edit route 的 open / back / delete
|
||||
- scene dirty save + load 后的 selection / undo reset
|
||||
- reparent 时的 parent 切换、cycle 拦截与 world transform 保持
|
||||
- main menu 的 exit / reset layout / about popup request
|
||||
- hierarchy rename helper
|
||||
- project create-folder / move-asset / open-folder helper
|
||||
|
||||
本轮收尾继续补充后,测试会进一步覆盖:
|
||||
|
||||
- clean scene 下的新建场景重置行为
|
||||
- clean scene 下 fallback save 的 no-op 路径
|
||||
- project move-asset 的非法输入保护
|
||||
|
||||
## 当前仍然剩下什么
|
||||
|
||||
### 架构收尾项
|
||||
|
||||
1. 继续压缩少量 panel 本地瞬时状态
|
||||
目标是把还能共享的 popup / router / state 再往 shared 层收一点。
|
||||
|
||||
2. 继续补 editor 回归测试
|
||||
重点补 command/router 边界,而不是 UI 像素测试。
|
||||
|
||||
3. 为 viewport 回归保留 editor shell 接口
|
||||
但暂时不把 RHI/renderer 接进来。
|
||||
|
||||
### 暂缓项
|
||||
|
||||
以下内容不计入当前这一轮 UI 架构收尾:
|
||||
|
||||
- `Scene` panel 真正内容
|
||||
- `Game` panel 真正内容
|
||||
- `Viewport` 渲染接入
|
||||
|
||||
原因不是不做,而是这些工作和 renderer / RHI 重构直接绑定,应该放到后续阶段。
|
||||
|
||||
## 本轮新增文档
|
||||
|
||||
本次已补正式架构说明:
|
||||
|
||||
- `docs/plan/Editor架构说明.md`
|
||||
|
||||
这个文档用于明确:
|
||||
|
||||
- 各层职责边界
|
||||
- 允许依赖方向
|
||||
- panel / action / command / manager / component editor 的落点规则
|
||||
- event bus、undo、dirty、selection 的统一约定
|
||||
|
||||
## 阶段性结论
|
||||
|
||||
当前 editor 可以明确地说:
|
||||
|
||||
- **UI 架构层面已经基本重构完成**
|
||||
- **剩余主要是封口、验证和后续 viewport 接入准备**
|
||||
|
||||
也就是说,后面不应该再回到“看到一个 panel 问题就地补一段特殊逻辑”的方式,而应该继续沿着现有分层做增量完善。
|
||||
@@ -1,567 +0,0 @@
|
||||
# Material Inspector与Shader属性面板收口计划
|
||||
|
||||
日期:2026-04-07
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前 Rendering / Shader / Material 主线已经基本建立起正式架构:
|
||||
|
||||
- Shader 侧已经具备统一的 authoring/schema 能力。
|
||||
- Material 运行时与 artifact 路径已经逐步从旧兼容逻辑中收口。
|
||||
- Editor 中的 Material Inspector 仍然没有完全切换到“由 Shader schema 驱动”的正式工作流。
|
||||
|
||||
当前最突出的实际问题有两类:
|
||||
|
||||
1. Material 选择 Shader 后,Shader 中声明的属性没有被正式暴露到 Inspector 面板上。
|
||||
2. Material 面板上还残留了一些历史字段或临时 UI,用户无法清晰判断哪些是正式能力,哪些只是过渡产物。
|
||||
|
||||
此外,当前工程条件已经允许做更彻底的清理:
|
||||
|
||||
- `project/` 中旧材质资产可直接编辑。
|
||||
- 当前不存在必须长期兼容的历史材质资产包袱。
|
||||
|
||||
因此,这一阶段的目标不是“继续兼容旧面板”,而是把 Material 编辑链路彻底切换到正式路径,并为后续 Shader/Material 编辑器扩展打好基础。
|
||||
|
||||
## 2. 本阶段目标
|
||||
|
||||
本阶段要完成以下目标:
|
||||
|
||||
1. 让 Material Inspector 成为 Shader schema 驱动的正式编辑入口。
|
||||
2. 让材质面板只暴露当前正式架构中的概念,不再混入旧字段和误导性配置。
|
||||
3. 保证 Editor 面板、运行时 `Material`、源资产、artifact、reimport 之间的数据一致性。
|
||||
4. 用单测和必要的编辑器验证把这条链路收口,而不是停留在“能显示”层面。
|
||||
|
||||
## 3. 不在本阶段处理的内容
|
||||
|
||||
以下内容不属于本阶段主目标,避免范围失控:
|
||||
|
||||
- 自定义 Material Drawer 系统。
|
||||
- Shader Graph / 节点式材质编辑器。
|
||||
- 面向美术工作流的复杂 Material 预设库。
|
||||
- SRP 层级的材质检查器定制机制。
|
||||
- 完整的 Shader GUI 仿 Unity 高级扩展体系。
|
||||
|
||||
这些能力后续可以做,但必须建立在当前正式链路已经稳定收口的前提上。
|
||||
|
||||
## 4. 当前已确认的问题
|
||||
|
||||
### 4.1 Inspector 没有正式反射 Shader 属性
|
||||
|
||||
当前 Material Inspector 还没有完整读取 Shader schema 并按类型生成属性控件,导致:
|
||||
|
||||
- Shader 中声明的属性无法完整暴露。
|
||||
- 用户无法直接编辑当前材质真正生效的参数。
|
||||
- 材质默认值、覆盖值、纹理槽与资源引用状态之间关系不清晰。
|
||||
|
||||
### 4.2 材质面板仍存在历史路径残留
|
||||
|
||||
之前材质层面曾承担过一些本不应由材质承担的职责,例如:
|
||||
|
||||
- 材质自己选择 builtin pass。
|
||||
- Inspector 里出现与当前正式架构不一致的旧字段。
|
||||
|
||||
这类逻辑已经开始清理,但 Editor 面板仍需同步彻底收口。
|
||||
|
||||
### 4.3 Editor 与运行时 Material 状态可能脱节
|
||||
|
||||
如果面板的编辑状态不是直接围绕正式 `Material` 数据模型建立,就容易出现:
|
||||
|
||||
- Inspector 显示值与运行时实际值不一致。
|
||||
- 资源重载后 UI 状态失真。
|
||||
- artifact / reimport 后材质参数丢失或回退异常。
|
||||
|
||||
### 4.4 缺少围绕正式工作流的测试闭环
|
||||
|
||||
仅靠手工点 Inspector 验证不足以支撑后续迭代。必须补足以下覆盖:
|
||||
|
||||
- Shader 切换后属性集合重建。
|
||||
- 默认值与显式覆盖值的切换。
|
||||
- Texture 属性与资源引用链路。
|
||||
- Keyword 与 Render State 的持久化。
|
||||
- artifact/reimport 后数据一致性。
|
||||
|
||||
## 5. 设计原则
|
||||
|
||||
本阶段严格遵循以下原则:
|
||||
|
||||
### 5.1 Shader 定义什么,Material 就暴露什么
|
||||
|
||||
Material 不是独立定义属性结构的地方。属性结构必须由 Shader schema 决定,Inspector 只是将其可视化并允许编辑。
|
||||
|
||||
### 5.2 Material 只承载实例数据,不承载管线选择策略
|
||||
|
||||
材质应承载:
|
||||
|
||||
- Shader 引用
|
||||
- Shader schema 对应的属性值
|
||||
- Keyword 状态
|
||||
- 合法的 render state 覆盖
|
||||
|
||||
材质不应再承载:
|
||||
|
||||
- 独立指定 builtin pass 的旧路径
|
||||
- 与 Shader metadata 冲突的临时策略字段
|
||||
|
||||
### 5.3 Editor 不发明第二套数据模型
|
||||
|
||||
Inspector 的中间态必须服务于正式数据模型,而不是绕开 `Material` 自己维护一套平行逻辑。
|
||||
|
||||
### 5.4 面板必须可验证
|
||||
|
||||
每一步改动都必须能通过测试或明确的编辑器验证闭环证明正确,不接受只靠目测“看起来差不多”。
|
||||
|
||||
## 6. 分阶段执行计划
|
||||
|
||||
## Phase 1:现状审查与残留路径清点
|
||||
|
||||
### 目标
|
||||
|
||||
彻底梳理当前 Material Inspector、Material 资源、Shader schema、artifact 序列化之间的实际数据流。
|
||||
|
||||
### 任务
|
||||
|
||||
- 审查 `InspectorPanel` 当前 Material 面板的数据来源和写回路径。
|
||||
- 审查 `Material` 当前正式字段与历史遗留字段。
|
||||
- 审查 `MaterialLoader`、`AssetDatabase`、artifact schema 当前是否仍保留不必要兼容路径。
|
||||
- 识别 Material 面板中哪些字段是正式路径,哪些属于旧方案残留。
|
||||
|
||||
### 完成标准
|
||||
|
||||
- 列清楚当前正式数据流。
|
||||
- 列清楚必须删除的旧字段/旧 UI。
|
||||
- 列清楚缺失的 schema 反射入口。
|
||||
|
||||
## Phase 2:Material Inspector 数据模型收口
|
||||
|
||||
### 目标
|
||||
|
||||
让 Material 面板的数据模型只围绕正式架构组织。
|
||||
|
||||
### 任务
|
||||
|
||||
- 移除 Material 层面的旧 pass 选择 UI 与残余兼容路径。
|
||||
- 整理 Inspector 内部状态结构,只保留:
|
||||
- Shader 资源引用
|
||||
- Shader schema 对应属性
|
||||
- Keywords
|
||||
- Render Queue / Render State
|
||||
- Tags(仅保留当前正式允许暴露的部分)
|
||||
- 避免 Inspector 保存与运行时 `Material` 不一致的冗余字段。
|
||||
|
||||
### 完成标准
|
||||
|
||||
- 面板字段与正式 `Material` 模型一一对应。
|
||||
- 旧字段彻底不再出现在材质编辑界面和保存链路中。
|
||||
|
||||
## Phase 3:Shader schema 驱动的属性面板生成
|
||||
|
||||
### 目标
|
||||
|
||||
Material Inspector 能基于 Shader schema 动态生成属性编辑 UI。
|
||||
|
||||
### 任务
|
||||
|
||||
- 读取 Shader 声明的属性定义与默认值。
|
||||
- 按属性类型渲染控件:
|
||||
- `float`
|
||||
- `int`
|
||||
- `bool`
|
||||
- `float2/3/4`
|
||||
- `texture`
|
||||
- 正确显示每个属性的:
|
||||
- 属性名
|
||||
- 显示名
|
||||
- 默认值
|
||||
- 当前覆盖值
|
||||
- 纹理槽绑定状态
|
||||
- 资源引用路径/AssetRef 状态
|
||||
- 明确“未覆盖时使用 Shader 默认值”的表现形式。
|
||||
|
||||
### 完成标准
|
||||
|
||||
- 选定 Shader 后,Inspector 中能稳定暴露该 Shader 的正式属性集合。
|
||||
- 面板能正确编辑并持久化这些属性。
|
||||
|
||||
## Phase 4:Editor 与运行时一致性收口
|
||||
|
||||
### 目标
|
||||
|
||||
保证 Material Inspector 编辑结果与运行时 `Material`、artifact、reimport 行为一致。
|
||||
|
||||
### 任务
|
||||
|
||||
- 对齐 Inspector 写回逻辑与 `Material` 正式 API。
|
||||
- 验证 Shader 切换时属性重建、默认值回退、无效属性清理。
|
||||
- 验证纹理属性在源资产、artifact、异步资源加载中的一致性。
|
||||
- 验证关键词与 render state 在 reload/reimport 后不丢失。
|
||||
|
||||
### 完成标准
|
||||
|
||||
- Inspector 改动能被运行时正确读取。
|
||||
- Reload / Reimport / Artifact Round Trip 后材质数据不漂移。
|
||||
|
||||
## Phase 5:测试补齐与阶段验收
|
||||
|
||||
### 目标
|
||||
|
||||
为正式工作流建立可持续回归验证。
|
||||
|
||||
### 任务
|
||||
|
||||
- 补充或更新 `material_tests`。
|
||||
- 补充或更新 `asset_tests`。
|
||||
- 若材质解析或 builtin pass 匹配受影响,补充必要的 `rendering_unit_tests`。
|
||||
- 重新编译 `XCEditor`,执行人工冒烟验证。
|
||||
|
||||
### 验收项
|
||||
|
||||
- `material_tests` 全绿。
|
||||
- `asset_tests` 全绿。
|
||||
- 必要的 `rendering_unit_tests` 全绿。
|
||||
- `XCEditor` 编译通过。
|
||||
- Material Inspector 中 Shader 属性暴露、编辑、保存、重载行为正确。
|
||||
|
||||
## 7. 预期交付结果
|
||||
|
||||
本阶段完成后,工程应达到以下状态:
|
||||
|
||||
1. Material 面板正式切换为 Shader schema 驱动。
|
||||
2. 材质不再承担旧 pass 选择职责。
|
||||
3. Inspector 中只剩下当前正式架构允许存在的字段。
|
||||
4. 材质属性、纹理槽、关键词、render state 都能稳定编辑并正确持久化。
|
||||
5. Shader / Material 后续扩展拥有清晰入口,不再建立在旧兼容逻辑之上。
|
||||
|
||||
## 8. 风险与注意事项
|
||||
|
||||
### 8.1 Shader 切换会触发属性重建
|
||||
|
||||
必须定义清楚以下规则:
|
||||
|
||||
- 与新 Shader schema 匹配的属性如何保留。
|
||||
- 不匹配的旧属性如何移除。
|
||||
- 默认值何时回退,何时保留用户覆盖。
|
||||
|
||||
### 8.2 Texture 属性不仅是 UI 问题
|
||||
|
||||
Texture 属性同时涉及:
|
||||
|
||||
- Inspector 显示
|
||||
- AssetRef
|
||||
- 资源路径
|
||||
- artifact 存储
|
||||
- 延迟加载
|
||||
|
||||
因此不能只改面板显示,必须连同资源路径一起验证。
|
||||
|
||||
### 8.3 Render State 需要坚持“正式最小集”
|
||||
|
||||
Material 面板不应再次演化成随意堆字段的临时入口。所有 render state 暴露都必须建立在当前正式支持的能力之上。
|
||||
|
||||
## 9. 建议执行顺序
|
||||
|
||||
建议严格按以下顺序落地:
|
||||
|
||||
1. 先完成旧字段与旧路径清点。
|
||||
2. 再重构 Inspector 数据模型。
|
||||
3. 再接 Shader schema 驱动属性 UI。
|
||||
4. 再收口 Editor 与运行时一致性。
|
||||
5. 最后统一补测与验收。
|
||||
|
||||
不能反过来先堆 UI,再回头补数据模型,否则很容易再次生成新的临时方案。
|
||||
|
||||
## 10. 阶段结论
|
||||
|
||||
当前最应该推进的不是继续增加 Material 编辑功能花样,而是把 Material Inspector 这条正式链路做对、做稳、做干净。
|
||||
|
||||
只要这一阶段收口完成,后续无论是:
|
||||
|
||||
- 更完整的 Shader authoring
|
||||
- Material 默认面板
|
||||
- 针对 Renderer/SRP 的材质体系扩展
|
||||
- 更接近 Unity 的 Shader/Material 编辑工作流
|
||||
|
||||
都会建立在清晰、稳定、可验证的基础之上。
|
||||
|
||||
## 11. Phase 1 审查结果
|
||||
|
||||
状态:已完成
|
||||
|
||||
本阶段已对当前 Material Inspector、运行时 `Material`、Shader schema、material source/artifact 路径进行了实际代码审查,结论如下。
|
||||
|
||||
### 11.1 当前 Inspector 数据模型先天不完整
|
||||
|
||||
当前 `InspectorPanel::MaterialAssetState` 只维护了以下内容:
|
||||
|
||||
- `shaderPath`
|
||||
- `renderQueue`
|
||||
- `renderState`
|
||||
- `tags`
|
||||
|
||||
它没有正式承载以下关键数据:
|
||||
|
||||
- Shader schema 属性列表
|
||||
- 材质属性当前值
|
||||
- 纹理槽与贴图引用
|
||||
- 关键词状态
|
||||
- Render State Override 开关本身
|
||||
|
||||
这意味着当前 Inspector 不是“少画了几个控件”,而是其内部状态模型本身就无法表示正式的材质编辑数据。
|
||||
|
||||
### 11.2 当前 Inspector 保存链路会丢失材质的正式内容
|
||||
|
||||
当前 `BuildMaterialAssetFileText()` 仅写出:
|
||||
|
||||
- `shader`
|
||||
- `renderQueue`
|
||||
- `tags`
|
||||
- `renderState`
|
||||
|
||||
它不会写出:
|
||||
|
||||
- `properties`
|
||||
- `textures`
|
||||
- `keywords`
|
||||
|
||||
因此,只要一个材质已经拥有 Shader 属性、纹理槽或关键词,若用户通过当前 Inspector 保存一次,该材质源文件就可能被覆盖成一个严重简化后的版本,导致正式材质数据丢失。
|
||||
|
||||
### 11.3 当前 Inspector 的实时写回同样不完整
|
||||
|
||||
当前 `ApplyResolvedMaterialStateToResource()` 仅把面板状态写回到运行时 `Material` 的以下部分:
|
||||
|
||||
- `SetShader`
|
||||
- `SetRenderQueue`
|
||||
- `SetRenderState`
|
||||
- `ClearTags` / `SetTag`
|
||||
|
||||
它不会写回:
|
||||
|
||||
- Shader schema 属性值
|
||||
- 纹理绑定
|
||||
- 关键词
|
||||
|
||||
因此 Inspector 当前即使支持继续加控件,如果不先重构数据模型,运行时同步仍然会不完整。
|
||||
|
||||
### 11.4 Render State Override 当前没有被正式建模
|
||||
|
||||
运行时 `Material::SetRenderState()` 会自动把 `HasRenderStateOverride` 置为 `true`。
|
||||
|
||||
但当前 Inspector:
|
||||
|
||||
- 没有暴露 “是否启用 Render State Override”
|
||||
- 保存时始终写出完整 `renderState`
|
||||
- 应用时始终调用 `SetRenderState`
|
||||
|
||||
这意味着只要用户通过当前材质面板保存,材质就会被强制推进到显式 Render State Override 路径。这个行为不符合正式设计,必须在下一阶段一起修正。
|
||||
|
||||
### 11.5 运行时底层能力其实已经基本齐备
|
||||
|
||||
当前 runtime/resource 层已经具备正式所需的大部分能力:
|
||||
|
||||
- `ShaderPropertyDesc` 已包含 `name / displayName / type / defaultValue / semantic`
|
||||
- `Shader` 已提供 `GetProperties()` / `FindProperty()` / keyword declaration 能力
|
||||
- `Material` 已提供完整的 `SetFloat/SetInt/SetBool/SetTexture...`
|
||||
- `Material` 已支持 Shader schema 默认值同步
|
||||
- `MaterialLoader` 已支持解析 `properties / textures / keywords / tags / renderState`
|
||||
- material artifact 当前已支持这些正式数据的持久化
|
||||
|
||||
也就是说,当前问题的主要矛盾不在资源系统,而在 Editor 侧没有接入正式模型。
|
||||
|
||||
### 11.6 当前阶段的根本性结论
|
||||
|
||||
下一阶段不能直接在现有材质面板上继续堆控件。
|
||||
|
||||
必须先完成:
|
||||
|
||||
1. `MaterialAssetState` 的正式重构
|
||||
2. Save/Reload/Apply 链路对 `properties / textures / keywords / renderState override` 的建模
|
||||
3. Inspector 与运行时 `Material` 之间的一致性重建
|
||||
|
||||
只有先做完这些,后续的 Shader schema 驱动属性 UI 才不会继续建立在错误基础上。
|
||||
|
||||
### 11.7 Phase 2 的直接执行范围
|
||||
|
||||
基于本阶段审查结果,下一阶段将直接进入以下工作:
|
||||
|
||||
1. 扩展 `MaterialAssetState`,让其正式承载属性、纹理槽、关键词与 render state override。
|
||||
2. 重构 `Populate / Apply / Save / Reload` 这四条关键链路。
|
||||
3. 彻底消除当前“保存一次就丢属性”的结构性问题。
|
||||
|
||||
## 12. Phase 2 执行结果
|
||||
|
||||
状态:已完成
|
||||
|
||||
本阶段已经完成 Material Inspector 数据模型的第一轮正式收口,重点是先把状态模型与保存链路做正确,而不是提前堆属性 UI。
|
||||
|
||||
### 12.1 已完成内容
|
||||
|
||||
- `MaterialAssetState` 已扩展为正式承载:
|
||||
- `keywords`
|
||||
- `properties`
|
||||
- `texture bindings`
|
||||
- `renderState override`
|
||||
- `PopulateMaterialAssetStateFromResource()` 已从运行时 `Material` 同步:
|
||||
- Shader 路径
|
||||
- Render Queue
|
||||
- Render State
|
||||
- Render State Override 标志
|
||||
- Tags
|
||||
- Keywords
|
||||
- Properties / Texture Bindings
|
||||
- `ApplyResolvedMaterialStateToResource()` 已开始完整写回:
|
||||
- Shader
|
||||
- Render Queue
|
||||
- Render State
|
||||
- Render State Override
|
||||
- Tags
|
||||
- Keywords
|
||||
- Properties / Texture Bindings
|
||||
- `BuildMaterialAssetFileText()` 已开始正式写出:
|
||||
- `keywords`
|
||||
- `properties`
|
||||
- `textures`
|
||||
- 仅在启用 override 时才写 `renderState`
|
||||
- Inspector 已增加 `Render State Override` 开关,避免默认把材质强行推进到显式 override 路径。
|
||||
|
||||
### 12.2 本阶段解决的核心问题
|
||||
|
||||
本阶段已经解决了最危险的结构性问题:
|
||||
|
||||
- 当前 Inspector 再保存材质时,不会像之前那样天然丢掉 `properties / textures / keywords`。
|
||||
- Render State 是否为显式 override,已经不再是隐藏副作用,而成为正式状态的一部分。
|
||||
|
||||
### 12.3 本阶段仍未完成的部分
|
||||
|
||||
以下能力还没有在本阶段完成,这是下一阶段的工作重点:
|
||||
|
||||
- 基于 Shader schema 的属性 UI 自动生成
|
||||
- 各属性类型的可视化编辑控件
|
||||
- 默认值/覆盖值的明确表现
|
||||
- 纹理槽的正式资源选择 UI
|
||||
- 关键词与属性在 Inspector 中的可视化编辑
|
||||
|
||||
因此,Phase 2 的性质是“先把材质状态模型与保存链路做对”,而不是“材质面板功能已经完整”。
|
||||
|
||||
## 13. Phase 3 执行结果
|
||||
|
||||
状态:已完成
|
||||
|
||||
本阶段已经开始把正确的材质状态模型真正暴露到 Inspector 上,重点是基于 Shader schema 生成属性面板,并处理 Shader 切换时的状态重建。
|
||||
|
||||
### 13.1 已完成内容
|
||||
|
||||
- Inspector 已新增 `Properties` 区块。
|
||||
- 属性区会基于当前 Shader 的 schema 动态生成,而不是写死字段。
|
||||
- 当前已接入的属性类型包括:
|
||||
- `Float / Range`
|
||||
- `Int`
|
||||
- `Color`
|
||||
- `Vector`
|
||||
- `Texture2D / TextureCube`
|
||||
- Texture 类型已接入资源选择控件,不再只是文本占位。
|
||||
- 每个属性当前会直接显示 Shader 中声明的默认值文本,作为当前参数的基线提示。
|
||||
|
||||
### 13.2 Shader 切换行为已收口
|
||||
|
||||
本阶段同时处理了一个关键一致性问题:
|
||||
|
||||
- 当用户切换 Shader 时,Inspector 会先基于新 Shader schema 重建 `MaterialAssetState`
|
||||
- 能与新 Shader 对齐的同名属性会尽量保留原值
|
||||
- 已不被新 Shader 声明的旧属性不会继续残留在保存结果里
|
||||
- 与新 Shader 不匹配的旧关键词也会被清理
|
||||
|
||||
这样可以避免旧材质属性被继续写回到新 Shader 材质文件中,从而减少生成无效材质源文件的风险。
|
||||
|
||||
### 13.3 本阶段仍未完成的部分
|
||||
|
||||
以下内容还需要后续阶段继续收口:
|
||||
|
||||
- 属性默认值与显式覆盖值的正式“重置/回退”交互
|
||||
- 关键词的可视化编辑 UI
|
||||
- 更完整的属性类型与显示策略细化
|
||||
- 针对 Inspector 材质链路的专门自动化测试
|
||||
|
||||
因此,Phase 3 的完成标准是“Shader schema 驱动的属性面板已经建立起来”,但还不是最终形态。
|
||||
|
||||
## 14. Phase 4 执行结果
|
||||
|
||||
状态:已完成
|
||||
|
||||
本阶段重点处理的是 authoring-state 与 runtime-state 的一致性问题,避免 Inspector 因为单纯从运行时对象反推而破坏材质源文件的语义。
|
||||
|
||||
### 14.1 已完成内容
|
||||
|
||||
- Inspector 现在会回读材质源文件中实际 authored 的:
|
||||
- `properties`
|
||||
- `textures`
|
||||
- `keywords`
|
||||
- `renderState`
|
||||
- 材质状态中已加入“是否需要序列化回源文件”的 authored 标记。
|
||||
- 保存材质时,不再无条件把所有运行时属性都写回源文件。
|
||||
- 对于未在源文件中显式 authored 的属性,当前会继续保持“继承 Shader 默认值”的语义。
|
||||
- 当用户在 Inspector 中修改属性或贴图后,对应项才会被标记为显式 authored 并写回源文件。
|
||||
|
||||
### 14.2 本阶段解决的核心问题
|
||||
|
||||
本阶段解决的是一个架构层面的隐患:
|
||||
|
||||
- 如果只从运行时 `Material` 反推回材质源文件,打开并保存一次材质,就会把 Shader 默认值全部固化进 `.mat`。
|
||||
- 一旦默认值被固化,后续 Shader 默认值再调整,材质就不再继承新的默认值。
|
||||
|
||||
当前这条链路已经收口到更合理的状态:
|
||||
|
||||
- 只有显式 authored 的 override 才会写回
|
||||
- 默认值仍然可以继续作为 Shader 侧的基线被继承
|
||||
|
||||
### 14.3 本阶段仍未完成的部分
|
||||
|
||||
以下内容仍然需要下一阶段继续完成:
|
||||
|
||||
- 针对 Inspector 材质链路的专门自动化测试
|
||||
- 属性“重置到默认值”的正式交互
|
||||
- 关键词的可视化编辑 UI
|
||||
- 更完整的属性类型/显示策略覆盖
|
||||
|
||||
因此,Phase 4 的性质是“先把 authoring 语义做正确”,为最后的测试与收口创造条件。
|
||||
|
||||
## 15. Phase 5 执行结果
|
||||
|
||||
状态:已完成
|
||||
|
||||
本阶段重点不是继续扩材质面板功能,而是把已经形成的正式链路变成“可持续回归验证”的状态,并把测试面收口到 Inspector 实际依赖的核心逻辑上。
|
||||
|
||||
### 15.1 已完成内容
|
||||
|
||||
- 新增 `MaterialInspectorMaterialState` / `MaterialInspectorMaterialStateIO` 辅助模块,承载:
|
||||
- Material Inspector 状态结构
|
||||
- source authored presence 解析
|
||||
- Shader 切换时的属性/关键词重同步
|
||||
- 材质源文件序列化文本生成
|
||||
- 新增 `tests/editor/test_material_inspector_material_state_io.cpp`,覆盖:
|
||||
- authored 属性/纹理/关键词标记识别
|
||||
- Shader 切换时保留兼容 override、清理陈旧字段
|
||||
- 非 authored 默认值不写回源文件
|
||||
- 纹理 override 与 render state override 的正式序列化
|
||||
- `editor_tests` 构建链路已接入上述 helper 与新测试文件。
|
||||
|
||||
### 15.2 已执行验证
|
||||
|
||||
- `cmake --build build --config Debug --target XCEditor -j 8`
|
||||
- `cmake --build build --config Debug --target editor_tests -- /v:minimal`
|
||||
- `build/tests/Resources/Material/Debug/material_tests.exe`
|
||||
- `build/tests/Core/Asset/Debug/asset_tests.exe`
|
||||
- `build/tests/Editor/Debug/editor_tests.exe --gtest_filter=MaterialInspectorMaterialStateIOTest.*`
|
||||
|
||||
验证结果:
|
||||
|
||||
- `XCEditor` 编译通过
|
||||
- `material_tests`:61 / 61 通过
|
||||
- `asset_tests`:56 / 56 通过
|
||||
- `MaterialInspectorMaterialStateIOTest`:4 / 4 通过
|
||||
|
||||
### 15.3 当前剩余风险
|
||||
|
||||
- 全量 `editor_tests.exe` 在顺序执行 `EditorActionRoutingTest.*` 时仍存在既有的共享状态级别挂起现象。
|
||||
- 该挂起并非本次新增的材质面板测试本身触发:
|
||||
- 新增 `MaterialInspectorMaterialStateIOTest` 单独执行通过
|
||||
- `EditorActionRoutingTest.PlayModeAllowsRuntimeSceneUndoRedoButKeepsSceneDocumentCommandsBlocked` 单独执行通过
|
||||
- 因此,本阶段围绕 Material Inspector / Shader 属性面板的测试收口已经完成,但 Editor 其余历史测试链路仍需单独排查。
|
||||
765
docs/plan/NanoVDB稀疏体积渲染正式集成计划_2026-04-08.md
Normal file
765
docs/plan/NanoVDB稀疏体积渲染正式集成计划_2026-04-08.md
Normal file
@@ -0,0 +1,765 @@
|
||||
# NanoVDB 稀疏体积渲染正式集成计划(Unity 对齐版)
|
||||
|
||||
文档日期:2026-04-08
|
||||
|
||||
适用范围:当前 `XCEngine` 的 `Resources / Shader / RHI / Rendering / Components / Editor` 体系,以及未来要对齐 Unity 的 C#、Shader、SRP 工作流。
|
||||
|
||||
文档目标:把 `MVS/VolumeRenderer` 中已经验证过的 `NanoVDB + GPU Buffer + Ray Marching` 原型,整理成一套能正式进入引擎主线、且从一开始就严格兼容 Unity 风格 shader/C# 语义的落地方案。
|
||||
|
||||
---
|
||||
|
||||
## 1. 先给结论
|
||||
|
||||
这条主线的正式方向不是:
|
||||
|
||||
1. 把体积数据退化成 `Texture3D`
|
||||
2. 给当前引擎临时塞一套 volume 专用 shader 语法
|
||||
3. 只在某个后端里私下打通一条 demo 路径
|
||||
|
||||
这条主线的正式方向必须是:
|
||||
|
||||
1. 保留 `NanoVDB` 的稀疏体积本质
|
||||
2. 把 buffer 资源能力补齐到引擎正式架构里
|
||||
3. 让高层 authoring 语义严格对齐 Unity
|
||||
4. 让低层 RHI 分类只存在于引擎内部
|
||||
5. 让 volumetric 成为 renderer / future SRP 中的正式 pass,而不是特例后处理
|
||||
|
||||
因此,这份计划默认坚持一条红线:
|
||||
|
||||
`StructuredBuffer / RawBuffer` 可以作为引擎内部资源分类存在,但作者可见语义、未来 C# API 语义、shader 写法语义,都必须对齐 Unity 既有做法,而不是引擎自创。
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计总原则
|
||||
|
||||
### 2.1 Unity 对齐原则
|
||||
|
||||
后续所有设计都必须同时满足下面这些条件:
|
||||
|
||||
1. shader 文件继续朝 Unity 风格 `.shader + HLSLPROGRAM` 组织收口。
|
||||
2. 作者侧 buffer 语义必须沿用 HLSL/Unity 现成写法,例如:
|
||||
- `StructuredBuffer<T>`
|
||||
- `ByteAddressBuffer`
|
||||
- `RWStructuredBuffer<T>`
|
||||
- `RWByteAddressBuffer`
|
||||
3. 不允许为了体积渲染单独发明新的高层 buffer 关键字、声明块或材质 schema。
|
||||
4. `Properties` 块只用于可序列化作者参数,不用于声明或承载 GPU buffer。
|
||||
5. 未来 C# 层的 buffer 接口方向,必须对齐 Unity 的:
|
||||
- `ComputeBuffer`
|
||||
- `GraphicsBuffer`
|
||||
- `Material.SetBuffer`
|
||||
- `Shader.SetGlobalBuffer`
|
||||
- `CommandBuffer.SetGlobalBuffer`
|
||||
6. renderer 侧对 buffer 的真正绑定,必须是运行时 per-pass / per-object / global resource binding,而不是材质资产直接持有底层 view。
|
||||
7. `StructuredBuffer / RawBuffer` 在引擎里首先是底层编译和绑定分类,不是高层 authoring 概念。
|
||||
|
||||
### 2.2 稀疏体积原则
|
||||
|
||||
1. `NanoVDB` 必须按稀疏体积方案正式接入。
|
||||
2. 不允许把正式路线退化成 `Texture3D` 替代方案。
|
||||
3. `Texture3D` 可以作为引擎未来的独立能力预留,但不是这条主线的收口路径。
|
||||
|
||||
### 2.3 SRP 兼容原则
|
||||
|
||||
1. volumetric 必须被设计成正式 scene pass。
|
||||
2. 它的输入和生命周期要能自然映射到未来 SRP 的 renderer feature / render pass event。
|
||||
3. 不允许把它设计成只能在 builtin pipeline 中硬编码存在的特例逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 3. Unity 参考基线
|
||||
|
||||
`参考/Unity NanoVDB` 已经给出了一条对本引擎非常重要的参考方向:
|
||||
|
||||
1. C# 侧使用 `ComputeBuffer`
|
||||
2. 运行时通过 `Material.SetBuffer("buf", gpuBuffer)` 绑定
|
||||
3. shader 侧通过 `StructuredBuffer<uint>` 读取
|
||||
4. `ComputeBufferType.Default` 对应的正是通用 GPU buffer 资源路径,而不是材质属性系统
|
||||
|
||||
这说明后续正式集成时,高层语义应该对齐的是:
|
||||
|
||||
1. Unity 的 buffer authoring / binding 模式
|
||||
2. Unity 的 shader 资源声明模式
|
||||
3. Unity/HDRP 中把体积渲染插入 render pass 的方式
|
||||
|
||||
而不是:
|
||||
|
||||
1. 直接照抄 `MVS` 的 D3D12 私有代码
|
||||
2. 用引擎内部 `RawBuffer` 这个名词去污染高层 shader/C# 接口
|
||||
3. 为了先出图,临时开 volume 专用旁路
|
||||
|
||||
---
|
||||
|
||||
## 4. 本轮明确不做什么
|
||||
|
||||
为了防止架构被 demo 反向拖偏,本轮明确不做下面这些事:
|
||||
|
||||
1. 不把 `NanoVDB` 退化成正式 `Texture3D` 方案。
|
||||
2. 不把 `MVS/VolumeRenderer` 的 D3D12 私有 helper、私有绑定逻辑直接搬进引擎主线。
|
||||
3. 不先做全局体积雾、froxel fog、clustered volumetrics。
|
||||
4. 不先做复杂编辑器 authoring 工具,例如体积笔刷和节点编辑器。
|
||||
5. 不先做 DXR / path tracing 体渲染。
|
||||
6. 不为体积渲染另开一套与 Unity 不一致的 shader/material/C# 表层语法。
|
||||
|
||||
---
|
||||
|
||||
## 5. 当前真正缺的不是“算法”,而是正式能力
|
||||
|
||||
当前原型已经证明了下面几件事:
|
||||
|
||||
1. `NanoVDB` 数据可以读。
|
||||
2. `NanoVDB` 数据可以上传到 GPU buffer。
|
||||
3. shader 可以基于 `pnanovdb` 做树遍历、HDDA 跳空和 ray marching。
|
||||
4. D3D12 原型能完成从数据到画面的闭环。
|
||||
|
||||
但引擎主线真正还缺下面这些正式能力:
|
||||
|
||||
1. `.nvdb` 如何进入资产与 artifact 体系。
|
||||
2. shader 如何用 Unity 风格语义正式声明 buffer 资源。
|
||||
3. 材质系统如何和运行时 buffer 绑定做严格职责分离。
|
||||
4. RHI 三后端如何正式支持 buffer SRV/UAV。
|
||||
5. renderer 如何把体积渲染作为正式 pass 插入 frame。
|
||||
6. 未来 C# 层如何以 Unity 风格接口绑定 buffer。
|
||||
7. editor 和测试如何验证整条链路不会回归。
|
||||
|
||||
所以本计划的本质不是“移植 demo”,而是“先补正式能力,再把 demo 算法放到正式能力之上”。
|
||||
|
||||
---
|
||||
|
||||
## 6. 正式架构目标
|
||||
|
||||
### 6.1 作者可见层:严格对齐 Unity 风格
|
||||
|
||||
这一层是未来引擎用户、shader 作者、C# 脚本作者看到的语义表面。
|
||||
|
||||
要求如下:
|
||||
|
||||
1. `.shader` 文件语法继续按 Unity 风格推进。
|
||||
2. buffer 在 shader 中的声明使用 HLSL 现成资源类型,不使用自定义资源关键字。
|
||||
3. 材质面板暴露的是作者参数,而不是底层 GPU buffer 本体。
|
||||
4. 如果未来提供脚本绑定接口,脚本看到的应该是 `ComputeBuffer / GraphicsBuffer` 风格对象和 `SetBuffer` 风格 API。
|
||||
|
||||
这层明确禁止:
|
||||
|
||||
1. 在 `Properties` 中定义 `StructuredBuffer` 或 `RawBuffer`
|
||||
2. 在 `.shader` 外层再发明 `BufferResources { ... }` 之类自定义块
|
||||
3. 让作者直接接触 `RHIResourceView` 或后端 view descriptor
|
||||
|
||||
### 6.2 引擎中层:运行时绑定契约
|
||||
|
||||
这一层负责把“作者声明的 shader 资源”和“运行时真实绑定的 GPU 资源”接起来。
|
||||
|
||||
正式边界应该是:
|
||||
|
||||
1. `Material` 只持有:
|
||||
- shader 引用
|
||||
- keyword / render state
|
||||
- 序列化参数
|
||||
- 常规纹理/采样器类作者资源
|
||||
2. `VolumeField` 或其他 runtime producer 提供真实 GPU buffer 资源
|
||||
3. `Renderer` / `CommandBuffer` / future script binding API 在渲染阶段把 buffer 绑定进 pass
|
||||
|
||||
也就是说:
|
||||
|
||||
1. `NanoVDB` 主数据不是普通 material property
|
||||
2. 它是运行时资源绑定
|
||||
3. 材质只负责“怎么渲染”
|
||||
4. `VolumeField` 负责“渲染的数据是什么”
|
||||
|
||||
### 6.3 引擎底层:RHI 资源分类
|
||||
|
||||
在 RHI 与编译反射层,可以存在正式的内部分类,例如:
|
||||
|
||||
1. `StructuredBuffer`
|
||||
2. `RawBuffer`
|
||||
3. 后续可扩展:
|
||||
- `ByteAddressBuffer` 的内部映射
|
||||
- `RWStructuredBuffer`
|
||||
- `RWByteAddressBuffer`
|
||||
|
||||
但要强调:
|
||||
|
||||
1. 这些是内部分类,不是高层 authoring 语法。
|
||||
2. 高层写的是 Unity/HLSL 资源声明。
|
||||
3. 引擎负责把这些声明反射并落到底层分类。
|
||||
|
||||
### 6.4 资源层:正式 VolumeField 资产
|
||||
|
||||
新增正式资源抽象:
|
||||
|
||||
1. `ResourceType::VolumeField`
|
||||
2. `Resources::VolumeField`
|
||||
3. `VolumeFieldLoader`
|
||||
4. `NanoVDBVolumeImporter`
|
||||
|
||||
导入链路建议:
|
||||
|
||||
1. 源资产:`Assets/.../*.nvdb`
|
||||
2. artifact:`Library/Artifacts/.../main.xcvol`
|
||||
|
||||
`main.xcvol` 建议包含:
|
||||
|
||||
1. schema version
|
||||
2. storage kind
|
||||
3. grid type / grid class
|
||||
4. world bbox
|
||||
5. voxel size
|
||||
6. active voxel statistics
|
||||
7. payload byte size
|
||||
8. 原始 `NanoVDB` blob
|
||||
|
||||
原则:
|
||||
|
||||
1. 第一阶段不做有损转换。
|
||||
2. artifact 中保留原始稀疏表示。
|
||||
3. metadata 单独结构化,供 loader / renderer 快速读取。
|
||||
|
||||
### 6.5 渲染层:正式体积 pass
|
||||
|
||||
正式新增:
|
||||
|
||||
1. `VolumeRendererComponent`
|
||||
2. `VisibleVolumeItem`
|
||||
3. `RenderSceneData.visibleVolumes`
|
||||
4. `BuiltinVolumetricPass`
|
||||
|
||||
该 pass 的职责:
|
||||
|
||||
1. 消费 `VisibleVolumeItem`
|
||||
2. 绑定 camera / depth / lighting / shadow / volume buffer / material 参数
|
||||
3. 执行 ray marching
|
||||
4. 合成到 scene color
|
||||
|
||||
推荐阶段位置:
|
||||
|
||||
1. `ShadowCaster`
|
||||
2. `Depth / Opaque`
|
||||
3. `Skybox`
|
||||
4. `Volumetrics`
|
||||
5. `Transparent`
|
||||
6. `PostProcess`
|
||||
7. `FinalColor`
|
||||
|
||||
这保证它天然兼容未来 SRP 的 renderer feature / pass event 方向。
|
||||
|
||||
### 6.6 C# 层预留目标
|
||||
|
||||
即使当前 C# 模块还没完全落地,这条计划也必须先把接口方向冻结。
|
||||
|
||||
后续需要预留的高层语义包括:
|
||||
|
||||
1. `ComputeBuffer`/`GraphicsBuffer` 风格资源对象
|
||||
2. `Material.SetBuffer`
|
||||
3. `Shader.SetGlobalBuffer`
|
||||
4. `CommandBuffer.SetGlobalBuffer`
|
||||
|
||||
这一层的原则是:
|
||||
|
||||
1. 先冻结语义,再决定托管层具体封装细节
|
||||
2. 不允许等到以后 C# 接入时再发现底层 buffer 路径与 Unity 语义冲突
|
||||
|
||||
---
|
||||
|
||||
## 7. 关键命名与职责冻结
|
||||
|
||||
为了避免后续反复推翻命名,这一轮先冻结:
|
||||
|
||||
1. 正式资源抽象:`VolumeField`
|
||||
2. 存储种类:`VolumeStorageKind::NanoVDB`
|
||||
3. 导入器:`NanoVDBVolumeImporter`
|
||||
4. 运行时加载器:`VolumeFieldLoader`
|
||||
5. 组件:`VolumeRendererComponent`
|
||||
6. frame data:`VisibleVolumeItem`
|
||||
7. pass:`BuiltinVolumetricPass`
|
||||
|
||||
命名原则:
|
||||
|
||||
1. 引擎对外抽象是 `VolumeField`
|
||||
2. `NanoVDB` 是第一种正式存储后端
|
||||
3. 不把具体文件格式命名泄露到整个 renderer 表面
|
||||
|
||||
---
|
||||
|
||||
## 8. Shader 与 Buffer 的正式契约
|
||||
|
||||
这是整个计划最关键的部分。
|
||||
|
||||
### 8.1 shader 作者看到的语义
|
||||
|
||||
作者应该写的是:
|
||||
|
||||
1. `StructuredBuffer<uint>` 这类 Unity/HLSL 现成声明
|
||||
2. 未来必要时的 `ByteAddressBuffer`
|
||||
3. 如果进入写路径,再扩到 `RWStructuredBuffer<T>` / `RWByteAddressBuffer`
|
||||
|
||||
作者不应该写的是:
|
||||
|
||||
1. `RawBuffer`
|
||||
2. `StructuredRawBuffer`
|
||||
3. 任何引擎自创的高层 buffer 类型关键字
|
||||
|
||||
### 8.2 材质系统的职责边界
|
||||
|
||||
材质系统只负责:
|
||||
|
||||
1. 密度、吸收、散射、步长、相位函数参数
|
||||
2. tint / emission
|
||||
3. shader variant / keyword
|
||||
4. render state
|
||||
|
||||
材质系统不负责:
|
||||
|
||||
1. 序列化 `NanoVDB` payload
|
||||
2. 序列化 GPU buffer handle
|
||||
3. 序列化底层 SRV/UAV view
|
||||
|
||||
### 8.3 运行时绑定职责
|
||||
|
||||
运行时绑定应该由以下层级之一完成:
|
||||
|
||||
1. `Renderer`
|
||||
2. `CommandBuffer`
|
||||
3. future script API
|
||||
|
||||
典型语义应接近:
|
||||
|
||||
1. `material.SetBuffer("buf", volumeBuffer)`
|
||||
2. `commandBuffer.SetGlobalBuffer(name, buffer)`
|
||||
|
||||
### 8.4 内部映射规则
|
||||
|
||||
建议的内部映射是:
|
||||
|
||||
1. `StructuredBuffer<T>` -> `ShaderResourceType::StructuredBuffer`
|
||||
2. `ByteAddressBuffer` -> `ShaderResourceType::RawBuffer`
|
||||
3. `RWStructuredBuffer<T>` -> UAV + structured 分类
|
||||
4. `RWByteAddressBuffer` -> UAV + raw 分类
|
||||
|
||||
这里的重点不是名字,而是边界:
|
||||
|
||||
1. 高层保持 Unity/HLSL 语义
|
||||
2. 中层做反射和 metadata
|
||||
3. 低层做真正 view 创建与 descriptor 绑定
|
||||
|
||||
### 8.5 一个硬性前置条件
|
||||
|
||||
如果当前 shader authoring 体系还不能正式表达 Unity 风格 buffer 声明,那么这个缺口的优先级高于体积渲染本身。
|
||||
|
||||
也就是说:
|
||||
|
||||
1. 不允许为了先做 volumetric,单独给它走私有 shader 语法
|
||||
2. 必须先把正式 shader authoring / reflection / runtime binding 路径打通
|
||||
|
||||
---
|
||||
|
||||
## 9. RHI 正式能力目标
|
||||
|
||||
RHI 侧至少需要补齐:
|
||||
|
||||
1. `RHIDevice::CreateShaderResourceView(RHIBuffer*, const ResourceViewDesc&)`
|
||||
2. `RHIDevice::CreateUnorderedAccessView(RHIBuffer*, const ResourceViewDesc&)`
|
||||
3. `ResourceViewDesc` 的 buffer 字段:
|
||||
- firstElement
|
||||
- elementCount
|
||||
- structureByteStride
|
||||
- raw / structured / formatted 标记
|
||||
4. `RHIResourceView` 对 buffer SRV/UAV 的统一表达
|
||||
5. descriptor set / bind group 更新路径对 buffer view 的正式支持
|
||||
|
||||
三后端预期映射:
|
||||
|
||||
1. D3D12:
|
||||
- buffer SRV
|
||||
- buffer UAV
|
||||
2. Vulkan:
|
||||
- storage buffer
|
||||
- 需要时使用 texel buffer 路径
|
||||
3. OpenGL:
|
||||
- SSBO 优先
|
||||
- 明确 GLSL 转译和绑定规则
|
||||
|
||||
这里必须强调:
|
||||
|
||||
1. 这些是底层实现能力
|
||||
2. 它们服务于高层 Unity 风格语义
|
||||
3. 它们不能反过来决定高层作者接口长什么样
|
||||
|
||||
---
|
||||
|
||||
## 10. 正式实施阶段
|
||||
|
||||
## 阶段 0:冻结 Unity 对齐边界
|
||||
|
||||
### 目标
|
||||
|
||||
在改代码前,先冻结高层语义、中层契约、低层分类三层边界。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 冻结 `VolumeField` / `VolumeRendererComponent` / `VisibleVolumeItem` / `BuiltinVolumetricPass` 命名
|
||||
2. 冻结体积渲染在 scene pass 中的阶段位置
|
||||
3. 冻结高层 buffer 语义:
|
||||
- 只接受 Unity/HLSL 写法
|
||||
4. 冻结材质边界:
|
||||
- buffer 不进 `Properties`
|
||||
5. 冻结未来 C# 方向:
|
||||
- `ComputeBuffer / GraphicsBuffer + SetBuffer` 语义
|
||||
6. 冻结内部映射:
|
||||
- `StructuredBuffer<T>` -> internal structured
|
||||
- `ByteAddressBuffer` -> internal raw
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 后续实现阶段不再反复摇摆高层语义
|
||||
|
||||
---
|
||||
|
||||
## 阶段 1:先补底层 buffer 资源视图能力
|
||||
|
||||
### 目标
|
||||
|
||||
补齐 renderer 目前缺失的“buffer 作为正式 shader 资源”的底层能力。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 完成 `RHIBuffer` 的 SRV/UAV 创建接口
|
||||
2. 补齐 `ResourceViewDesc` 的 buffer 视图字段
|
||||
3. 让 descriptor set 更新路径正式支持 buffer 类 view
|
||||
4. D3D12 / Vulkan / OpenGL 三后端同时按正式接口设计
|
||||
5. 明确 backend capability 与错误回报,不允许 silent wrong image
|
||||
|
||||
### 测试
|
||||
|
||||
1. `tests/RHI/unit` 增加 buffer SRV/UAV 创建测试
|
||||
2. descriptor set buffer binding 测试
|
||||
3. 三后端分别验证 buffer view 不再被误当成 texture view
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 引擎能在不依赖 texture hack 的前提下,正式把 GPU buffer 绑定给 shader
|
||||
|
||||
---
|
||||
|
||||
## 阶段 2:打通 Unity 风格 shader buffer 语义
|
||||
|
||||
### 目标
|
||||
|
||||
让 shader authoring / parser / reflection / artifact / runtime binding 能正式表达 Unity/HLSL 风格 buffer 资源。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 扩展 shader 反射模型以支持 structured/raw buffer 资源
|
||||
2. 明确 `StructuredBuffer<T>` 与 `ByteAddressBuffer` 的内部映射
|
||||
3. 扩展 runtime build、pass layout、descriptor metadata 对新资源类型的支持
|
||||
4. 明确 OpenGL 的 GLSL/SSBO 转译规则
|
||||
5. 严格禁止 volume 专属私有 shader 语法旁路
|
||||
|
||||
### 强约束
|
||||
|
||||
1. 不新增高层 `RawBuffer` 关键字
|
||||
2. 不新增 `MaterialPropertyType::StructuredBuffer / RawBuffer`
|
||||
3. 如果 `Properties` 中尝试声明 buffer,必须报错或拒绝导入
|
||||
|
||||
### 测试
|
||||
|
||||
1. `tests/Resources/Shader`
|
||||
2. `tests/Resources/Material`
|
||||
3. `tests/Rendering/unit` 中的 pass layout / metadata 测试
|
||||
4. authoring 约束测试:
|
||||
- Unity/HLSL 风格 buffer 声明可以被正确反射
|
||||
- 非法把 buffer 塞进 `Properties` 会被拒绝
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. shader 资源描述层正式支持 buffer 资源
|
||||
2. 高层 authoring 语义仍保持 Unity 风格,不被底层分类污染
|
||||
|
||||
---
|
||||
|
||||
## 阶段 3:冻结 future C# buffer 绑定契约
|
||||
|
||||
### 目标
|
||||
|
||||
在 C# 模块尚未完全落地前,先把与本主线相关的 buffer 绑定语义冻结。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 定义 native 侧可对应 future C# 的 buffer 绑定入口
|
||||
2. 约束 API 形状向 Unity 对齐:
|
||||
- `Material.SetBuffer`
|
||||
- `Shader.SetGlobalBuffer`
|
||||
- `CommandBuffer.SetGlobalBuffer`
|
||||
3. 明确 `VolumeField` 与 runtime buffer producer 的关系
|
||||
4. 保证未来托管层不会直接看到 RHI view 对象
|
||||
|
||||
### 测试
|
||||
|
||||
1. native 侧 binding metadata 测试
|
||||
2. material/runtime/global 三类 buffer 绑定路径测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 当前实现方向已经为 future C# 留好 Unity 风格接口落点
|
||||
|
||||
---
|
||||
|
||||
## 阶段 4:接入正式 VolumeField 资产链路
|
||||
|
||||
### 目标
|
||||
|
||||
让 `.nvdb` 成为项目里的正式资源,而不是 demo 私有文件。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 新增 `ResourceType::VolumeField`
|
||||
2. 新增 `Resources::VolumeField`
|
||||
3. 新增 `VolumeFieldLoader`
|
||||
4. 新增 `NanoVDBVolumeImporter`
|
||||
5. `AssetDatabase` 识别 `.nvdb`
|
||||
6. 生成 `main.xcvol`
|
||||
7. 接入 reimport / dependency / cache / runtime load
|
||||
|
||||
### 测试
|
||||
|
||||
1. `tests/Resources/Volume`
|
||||
2. `tests/Core/Asset`
|
||||
3. `.nvdb` 改动后的 reimport 测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. `.nvdb` 可以像 mesh / material / shader 一样进入正式资产体系
|
||||
|
||||
---
|
||||
|
||||
## 阶段 5:接入组件、提取与 frame data
|
||||
|
||||
### 目标
|
||||
|
||||
让场景系统知道“什么是可渲染的体积对象”。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 新增 `VolumeRendererComponent`
|
||||
2. 组件持有:
|
||||
- `VolumeField`
|
||||
- `Material`
|
||||
- enable/disable
|
||||
- 局部参数 override
|
||||
- bounds / transform
|
||||
3. `RenderSceneExtractor` 增加体积对象提取
|
||||
4. 新增 `VisibleVolumeItem`
|
||||
5. `RenderSceneData.visibleVolumes` 接入
|
||||
|
||||
### 测试
|
||||
|
||||
1. 组件序列化 / 反序列化
|
||||
2. `VisibleVolumeItem` 提取测试
|
||||
3. frustum / bounds culling 与稳定顺序测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. renderer 已经能从正式场景数据中拿到体积对象输入
|
||||
|
||||
---
|
||||
|
||||
## 阶段 6:BuiltinVolumetricPass 首次正式点亮
|
||||
|
||||
### 目标
|
||||
|
||||
在当前 renderer 正式链路中点亮第一版 `NanoVDB` 稀疏体积渲染。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 新增 `BuiltinVolumetricPass`
|
||||
2. pass 输入包括:
|
||||
- camera
|
||||
- depth
|
||||
- scene color
|
||||
- light / shadow
|
||||
- volume buffer
|
||||
- material parameters
|
||||
3. D3D12 首次点亮
|
||||
4. 允许保留一条集成测试级别的 fullscreen debug path
|
||||
5. 正式运行时路径收口到 proxy box / unit cube volume renderer
|
||||
|
||||
### 强约束
|
||||
|
||||
1. fullscreen path 只能作为 bring-up / integration debug 路径
|
||||
2. 正式场景运行时不能停留在 fullscreen 方案
|
||||
3. 不得保留 `MVS` 风格直接文件加载或私有绑定路径
|
||||
|
||||
### 测试
|
||||
|
||||
1. `tests/Rendering/integration/volume_nanovdb_minimal`
|
||||
2. GT 对比
|
||||
3. 中间调试图输出:
|
||||
- `volume_mask_debug`
|
||||
- `depth_debug`
|
||||
- 必要时的 shadow debug
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 正式 renderer 已经能稳定渲染一个来自 `.nvdb` 的体积对象
|
||||
|
||||
---
|
||||
|
||||
## 阶段 7:深度、阴影、合成规则收口
|
||||
|
||||
### 目标
|
||||
|
||||
把“能出图”收口成“行为正确”。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 深度遮挡规则确定
|
||||
2. scene color 合成规则确定
|
||||
3. 主方向光与阴影采样接入
|
||||
4. transform 与 bbox 变换一致
|
||||
5. 步长、阴影采样步数、质量参数稳定化
|
||||
|
||||
### 测试
|
||||
|
||||
1. `volume_nanovdb_occlusion`
|
||||
2. `volume_nanovdb_transform`
|
||||
3. 光照方向变化的回归测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 画面行为能解释、能回归、能维护,不再只是 demo level
|
||||
|
||||
---
|
||||
|
||||
## 阶段 8:多后端 rollout
|
||||
|
||||
### 目标
|
||||
|
||||
把已在 D3D12 验证的正式能力推进到 Vulkan 与 OpenGL。
|
||||
|
||||
### 任务
|
||||
|
||||
1. Vulkan storage buffer 路线点亮
|
||||
2. OpenGL SSBO 路线点亮
|
||||
3. 明确 capability 检测与 fallback / disable 行为
|
||||
4. 补齐 backend-specific shader compile / runtime binding 回归
|
||||
|
||||
### 测试
|
||||
|
||||
1. 各后端统一 GT 对比
|
||||
2. backend-specific shader compile 测试
|
||||
3. buffer binding 回归测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 不是只有 D3D12 demo 可用,而是正式进入多后端 renderer 能力集合
|
||||
|
||||
---
|
||||
|
||||
## 阶段 9:Editor 最小工作流接入
|
||||
|
||||
### 目标
|
||||
|
||||
让这项能力进入项目工作流,而不是只能靠测试程序看图。
|
||||
|
||||
### 任务
|
||||
|
||||
1. `ProjectPanel` 识别 `.nvdb`
|
||||
2. `Inspector` 显示 `VolumeField` metadata
|
||||
3. `VolumeRendererComponent` inspector 编辑
|
||||
4. scene view / game view 中体积对象可见
|
||||
5. 资源缺失、编译失败、后端不支持时给出明确状态
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 用户可以从资产、组件、场景三个层面完整使用这项能力
|
||||
|
||||
---
|
||||
|
||||
## 11. 测试体系规划
|
||||
|
||||
### 11.1 单元测试
|
||||
|
||||
需要新增或扩展:
|
||||
|
||||
1. `tests/RHI/unit`
|
||||
- buffer SRV/UAV 创建
|
||||
- descriptor set buffer binding
|
||||
2. `tests/Resources/Shader`
|
||||
- Unity/HLSL 风格 buffer 声明解析
|
||||
- 非法 `Properties` buffer 声明拒绝
|
||||
3. `tests/Resources/Material`
|
||||
- 材质参数 schema 与 runtime buffer 绑定边界
|
||||
4. `tests/Resources/Volume`
|
||||
- `.nvdb -> .xcvol`
|
||||
- metadata roundtrip
|
||||
- reimport
|
||||
5. `tests/Rendering/unit`
|
||||
- `VisibleVolumeItem` 提取
|
||||
- `BuiltinVolumetricPass` 输入校验
|
||||
|
||||
### 11.2 集成测试
|
||||
|
||||
建议新增:
|
||||
|
||||
1. `tests/Rendering/integration/volume_nanovdb_minimal`
|
||||
2. `tests/Rendering/integration/volume_nanovdb_occlusion`
|
||||
3. `tests/Rendering/integration/volume_nanovdb_transform`
|
||||
|
||||
GT 规则:
|
||||
|
||||
1. 使用单一 `GT.ppm`
|
||||
2. 各后端输出都对同一张 `GT.ppm` 做对比
|
||||
3. 必要时保留中间调试图,但不把调试图当正式输出
|
||||
|
||||
### 11.3 性能与稳定性验收
|
||||
|
||||
至少记录:
|
||||
|
||||
1. 单体积对象 frame time
|
||||
2. 多体积对象 frame time
|
||||
3. `.nvdb` 首次导入时长
|
||||
4. artifact 命中后的加载时长
|
||||
|
||||
---
|
||||
|
||||
## 12. 关键风险
|
||||
|
||||
### 12.1 最大风险不是算法,而是语义边界被做脏
|
||||
|
||||
如果一开始把 `StructuredBuffer / RawBuffer` 直接暴露成高层 authoring 概念,后面整个 shader/material/C# 体系都会被污染。
|
||||
|
||||
### 12.2 OpenGL 路线风险最高
|
||||
|
||||
原因:
|
||||
|
||||
1. `StructuredBuffer` 到 GLSL/SSBO 的映射最容易出边角问题
|
||||
2. `PNanoVDB` 这种大 include 在 GLSL 转译路径上更容易暴露兼容性问题
|
||||
|
||||
### 12.3 fullscreen debug path 容易反客为主
|
||||
|
||||
它只能用于 bring-up 和集成测试,不能变成正式场景架构。
|
||||
|
||||
### 12.4 如果当前 shader 体系还没正式支持 Unity 风格 buffer 语义,这会成为主阻塞项
|
||||
|
||||
这不是额外问题,而是本主线的前置依赖。
|
||||
|
||||
---
|
||||
|
||||
## 13. 本阶段收口标准
|
||||
|
||||
当下面条件同时满足时,可以认为“稀疏体积渲染第一阶段正式完成”:
|
||||
|
||||
1. `.nvdb` 已成为正式项目资源,可导入、可 reimport、可 artifact 化。
|
||||
2. 引擎 shader / runtime binding / RHI 已正式支持 Unity 风格 buffer 资源链路。
|
||||
3. 高层 authoring 没有引入任何体积专用自定义 buffer 语法。
|
||||
4. `VolumeRendererComponent` 能把 `VolumeField` 放进场景。
|
||||
5. `BuiltinVolumetricPass` 已作为正式 scene pass 存在。
|
||||
6. D3D12 至少已经在正式链路中点亮。
|
||||
7. 集成测试能稳定出图并做 GT 对比。
|
||||
8. future C# 的 `SetBuffer` 风格接口已经有明确落点,不会和当前底层设计冲突。
|
||||
9. 代码中不再依赖 `MVS/VolumeRenderer` 那套 D3D12 私有 demo 实现。
|
||||
|
||||
---
|
||||
|
||||
## 14. 一句话结论
|
||||
|
||||
这条主线的正确做法不是“先做出体积渲染再说”,而是“从第一天开始就按 Unity 风格的 shader/C# 语义设计 buffer 路径,把 `NanoVDB` 作为正式稀疏体积能力接入 renderer”,这样后面无论是 C# 层还是 SRP,都不会被今天的底层实现反噬。
|
||||
@@ -1,127 +0,0 @@
|
||||
# Renderer 下一阶段: Camera PostProcess 描述层正式化计划
|
||||
|
||||
日期: `2026-04-06`
|
||||
|
||||
## 1. 阶段目标
|
||||
|
||||
在已经完成 runtime `SceneColor -> PostProcess -> FinalOutput` 主链收口的基础上,
|
||||
把相机上的后处理配置从临时的 `ColorScale` 专用字段,提升为正式的描述层。
|
||||
|
||||
这一阶段只解决一件事:
|
||||
|
||||
- `CameraComponent` 持有正式的 post-process pass 描述
|
||||
- `SceneRenderer` 通过工厂把描述翻译成 runtime pass sequence
|
||||
- 旧的 `ColorScale` 接口继续兼容,旧场景序列化继续可读
|
||||
|
||||
## 2. 已完成内容
|
||||
|
||||
### 2.1 相机后处理描述层
|
||||
|
||||
已新增:
|
||||
|
||||
- `CameraPostProcessPassType`
|
||||
- `CameraPostProcessPassDesc`
|
||||
- `CameraPostProcessStack`
|
||||
|
||||
当前首个正式 builtin effect 仍然是:
|
||||
|
||||
- `ColorScale`
|
||||
|
||||
但它已经不再通过 `CameraComponent -> vector<Vector4> -> SceneRenderer if/for` 这种硬编码链路传递。
|
||||
|
||||
### 2.2 CameraComponent 正式化
|
||||
|
||||
已完成:
|
||||
|
||||
- `CameraComponent` 内部存储迁移到 `CameraPostProcessStack`
|
||||
- 新增通用接口:
|
||||
- `GetPostProcessPasses`
|
||||
- `SetPostProcessPasses`
|
||||
- `AddPostProcessPass`
|
||||
- `ClearPostProcessPasses`
|
||||
- 旧接口继续保留:
|
||||
- `SetColorScalePostProcessEnabled`
|
||||
- `SetColorScalePostProcessScale`
|
||||
- `SetColorScalePostProcessPasses`
|
||||
- `GetColorScalePostProcessPasses`
|
||||
|
||||
兼容规则:
|
||||
|
||||
- 旧接口继续映射到 `ColorScale` 类型的描述
|
||||
- 新格式序列化写出正式 `postProcessPassN*` 字段
|
||||
- 反序列化同时兼容旧的 `colorScalePostProcess*` 字段
|
||||
|
||||
### 2.3 SceneRenderer 工厂翻译层
|
||||
|
||||
已完成:
|
||||
|
||||
- 从 `SceneRenderer` 中移除 `ColorScale` 专用硬编码构造
|
||||
- 新增 `BuildCameraPostProcessPassSequence(...)`
|
||||
- `SceneRenderer` 现在只关心:
|
||||
- 读取相机的 `CameraPostProcessStack`
|
||||
- 为 post-process 分配 source surface
|
||||
- 调用工厂生成 `RenderPassSequence`
|
||||
|
||||
这意味着后续增加新的 builtin camera post-process 时,
|
||||
修改点已经收缩到描述层和工厂,而不是继续把分支堆进 `SceneRenderer`。
|
||||
|
||||
## 3. 验证结果
|
||||
|
||||
### 3.1 构建
|
||||
|
||||
已通过:
|
||||
|
||||
- `cmake --build --preset debug --target components_tests rendering_unit_tests rendering_integration_camera_post_process_scene rendering_integration_post_process_scene -- /m:1`
|
||||
|
||||
### 3.2 单元测试
|
||||
|
||||
已通过:
|
||||
|
||||
- `components_tests --gtest_filter=CameraComponent_Test.*`
|
||||
- `rendering_unit_tests --gtest_filter=SceneRenderer_Test.*:CameraRenderer_Test.*`
|
||||
|
||||
覆盖点包括:
|
||||
|
||||
- 新描述层 round-trip
|
||||
- 旧 `ColorScale` 字段反序列化兼容
|
||||
- `SceneRenderer` 从 camera post-process stack 构建正式 request
|
||||
|
||||
### 3.3 集成测试
|
||||
|
||||
已通过:
|
||||
|
||||
- `rendering_integration_camera_post_process_scene`
|
||||
- D3D12
|
||||
- OpenGL
|
||||
- Vulkan
|
||||
- `rendering_integration_post_process_scene`
|
||||
- D3D12
|
||||
- OpenGL
|
||||
- Vulkan
|
||||
|
||||
说明:
|
||||
|
||||
- `camera_post_process_scene` 验证 formal scene path
|
||||
- `post_process_scene` 保留为 manual / low-level multi-pass 覆盖
|
||||
|
||||
## 4. 当前结论
|
||||
|
||||
这一阶段已经完成。
|
||||
|
||||
当前 renderer 在 camera post-process 这一层,已经从“单个效果的临时接线”升级为“正式描述 + 工厂翻译”的结构。
|
||||
|
||||
但现阶段仍然有两个边界:
|
||||
|
||||
- 描述层虽然正式化了,但当前 builtin effect 仍只有 `ColorScale`
|
||||
- editor / asset / material 侧还没有把 camera post-process 做成完整的资产化与配置面板体系
|
||||
|
||||
## 5. 下一步建议
|
||||
|
||||
下一步不要回头继续往 `SceneRenderer` 里塞特判,而应沿着下面的方向推进:
|
||||
|
||||
1. 继续扩展 `CameraPostProcessPassDesc`
|
||||
- 例如 tone mapping / exposure / gamma / final color transform
|
||||
2. 让新的 builtin camera effect 继续只经过“描述层 + 工厂”
|
||||
3. 等 shader/material 主线更稳定后,再决定 camera post-process 的资产化形式和 editor 配置入口
|
||||
|
||||
本阶段的核心收口标准已经满足,可以提交归档。
|
||||
@@ -1,841 +0,0 @@
|
||||
# Renderer 下一阶段:Unity 风格 Shader 体系正式化计划
|
||||
|
||||
日期:`2026-04-06`
|
||||
|
||||
## 1. 阶段结论
|
||||
|
||||
当前 renderer 主线已经可以阶段性收口,但 `Shader / Material / Pass` 体系还没有真正统一。
|
||||
|
||||
现在仓库里的 shader 体系处于一个过渡态:
|
||||
|
||||
- 逻辑上已经有 `Shader -> Pass -> Variant` 模型
|
||||
- authoring 外观上已经接近 Unity ShaderLab
|
||||
- 运行时已经能按 pass 和 backend variant 选择 shader
|
||||
- 三后端已经能稳定跑通 builtin shader
|
||||
|
||||
但它仍然不是 Unity 风格的正式体系,因为当前 authoring 仍然暴露了太多 backend 与 binding 细节:
|
||||
|
||||
- `.shader` 文件里仍然显式写 `#pragma backend D3D12/OpenGL/Vulkan`
|
||||
- `.shader` 文件里仍然显式写 `Resources { name(type, set, binding) }`
|
||||
- material 仍然可以显式指定 `shaderPass`
|
||||
- shader 关键字、变体、include、pass state、SubShader 选择还没有真正成体系
|
||||
|
||||
所以这一阶段的主线不是继续做新渲染效果,而是把 shader 体系升级成:
|
||||
|
||||
- **authoring 层完全按 Unity 风格书写**
|
||||
- **三后端编译路径统一收敛到 importer / compiler 层**
|
||||
- **runtime 只消费正式 shader artifact / material contract**
|
||||
|
||||
这一步不是 SRP 本身,但它是后续 SRP 能否成立的硬地基。
|
||||
|
||||
---
|
||||
|
||||
## 2. 阶段目标
|
||||
|
||||
本阶段要达成的核心目标只有一件事:
|
||||
|
||||
**把当前“伪 ShaderLab + 后端直连”的过渡体系,升级为“Unity 风格 authoring + 引擎内部统一 shader IR + 三后端编译产物”的正式体系。**
|
||||
|
||||
完成后应达到:
|
||||
|
||||
1. shader authoring 采用 Unity 风格 `.shader` 语法,不再要求作者显式写 backend variant 分发表。
|
||||
2. 新语法不再要求作者手写 `set/binding` 级资源绑定表。
|
||||
3. HLSL 成为 raster shader 的单一 authoring 语言源。
|
||||
4. D3D12 / Vulkan / OpenGL 的差异退到 importer / compiler / artifact 层。
|
||||
5. material 只管 property / keyword / texture / render state,不再负责点名 pass。
|
||||
6. renderer 按 `LightMode` / pass contract 选 pass,而不是靠 material 的临时字符串兜底。
|
||||
7. builtin shader 全部迁移到新体系并保持三后端回归稳定。
|
||||
8. 保留 legacy shader 兼容层,避免一次性炸掉现有工程内容。
|
||||
|
||||
---
|
||||
|
||||
## 3. 非目标
|
||||
|
||||
本阶段明确不做:
|
||||
|
||||
- SRP 本体
|
||||
- render graph
|
||||
- deferred / clustered
|
||||
- Shader Graph
|
||||
- Surface Shader
|
||||
- 完整 Unity ShaderLab 100% 全语法一次性覆盖
|
||||
- volume 系统
|
||||
- 更多新渲染效果
|
||||
|
||||
本阶段也不追求一口气把 Unity 的所有 authoring 特性补完,例如:
|
||||
|
||||
- `Fallback`
|
||||
- `UsePass`
|
||||
- `CustomEditor`
|
||||
- `GrabPass`
|
||||
- tessellation / geometry / ray tracing authoring
|
||||
|
||||
这些可以后续补,但不应阻塞当前阶段把“统一 shader 体系”先做正确。
|
||||
|
||||
---
|
||||
|
||||
## 4. 当前真实状态与根本问题
|
||||
|
||||
### 4.1 当前已经具备的能力
|
||||
|
||||
当前工程已经具备:
|
||||
|
||||
- `.shader` 文件解析能力
|
||||
- `Shader` 的 pass / property / resource / variant 数据模型
|
||||
- builtin shader 资产化
|
||||
- material property / texture / render state / tag 载体
|
||||
- renderer 中按 builtin pass metadata 选择可用 shader pass
|
||||
|
||||
这意味着当前不是从零开始设计。
|
||||
|
||||
### 4.2 当前最根本的问题
|
||||
|
||||
当前最大的问题不是“没有 shader 体系”,而是“authoring 层和 runtime 层的边界还没收干净”。
|
||||
|
||||
具体表现为:
|
||||
|
||||
1. **authoring 层暴露后端差异**
|
||||
- `.shader` 文件直接写 `#pragma backend D3D12/OpenGL/Vulkan`
|
||||
- shader 作者必须知道后端与源码文件映射
|
||||
|
||||
2. **authoring 层暴露 RHI binding 细节**
|
||||
- `.shader` 文件显式写 `Resources { ConstantBuffer / Texture2D / Sampler, set, binding }`
|
||||
- 这更像 Vulkan/D3D12 binding 清单,不是 Unity 风格 shader authoring
|
||||
|
||||
3. **material 还带着临时 pass 选择职责**
|
||||
- `Material::SetShaderPass()` 仍然存在
|
||||
- renderer 仍然优先吃 material 显式指定的 pass
|
||||
- 这会阻碍未来 RendererFeature / SRP 规范化
|
||||
|
||||
4. **关键字与变体体系缺失**
|
||||
- 没有正式的 `multi_compile / shader_feature`
|
||||
- 没有变体剥离与编译缓存策略
|
||||
|
||||
5. **include 与共享库体系缺失**
|
||||
- 没有正式的 shader include 搜索路径、预处理、公共库组织
|
||||
|
||||
6. **pass state 仍然不在 shader authoring 的统一语义内**
|
||||
- 诸如 `Cull / ZWrite / ZTest / Blend / ColorMask / Stencil` 还没有完整进入 shader authoring contract
|
||||
|
||||
7. **三后端仍然是物理三套源码直连**
|
||||
- 当前虽然“逻辑上一个 shader asset”
|
||||
- 但作者本质上还在维护三套 shader stage 文件
|
||||
|
||||
---
|
||||
|
||||
## 5. 核心设计结论
|
||||
|
||||
### 5.1 目标不是“看起来像 Unity”,而是“真正采用 Unity 风格 authoring 模型”
|
||||
|
||||
最终目标应当是:
|
||||
|
||||
- 一个 `.shader` 文件描述一个逻辑 shader
|
||||
- shader 内部有 `Properties / SubShader / Pass / Tags / State / Program`
|
||||
- renderer 消费的是 import 后的统一 IR / artifact
|
||||
- backend 差异不暴露给 shader 作者
|
||||
|
||||
### 5.2 HLSL 作为单一 authoring 语言源
|
||||
|
||||
本阶段必须明确:
|
||||
|
||||
- **新体系下 raster shader 统一使用 HLSL authoring**
|
||||
- D3D12 直接编 HLSL
|
||||
- Vulkan 由 HLSL 编到 SPIR-V
|
||||
- OpenGL 由 HLSL 编到 SPIR-V,再转 GLSL 430
|
||||
|
||||
原因:
|
||||
|
||||
- 如果 authoring 仍然保留 GLSL/HLSL 三套并行,永远不可能真正统一写法
|
||||
- 只有 single-source authoring,才能接近 Unity 的真实体验
|
||||
|
||||
### 5.3 backend 差异必须退到 importer / compiler 层
|
||||
|
||||
新 authoring 文件中不应再出现:
|
||||
|
||||
- `#pragma backend ...`
|
||||
- backend 专属 stage 文件路径表
|
||||
|
||||
这些内容应由 importer 根据 target backend 生成产物。
|
||||
|
||||
### 5.4 resource binding 不再由 shader 作者手写 `Resources(set,binding)`
|
||||
|
||||
Unity 风格 shader authoring 不要求作者手写 descriptor set / binding。
|
||||
|
||||
因此新体系下应改为:
|
||||
|
||||
- material 暴露属性来自 `Properties`
|
||||
- engine 内建 constant buffer / texture / sampler 来自约定与 reflection
|
||||
- importer 通过 HLSL reflection + 约定库推导 runtime resource layout
|
||||
|
||||
也就是说:
|
||||
|
||||
- authoring 层写“语义”
|
||||
- importer 层生成“绑定布局”
|
||||
- runtime 层消费“绑定布局”
|
||||
|
||||
### 5.5 pass 选择必须回归 renderer,而不是 material
|
||||
|
||||
新体系中:
|
||||
|
||||
- material 只绑定 shader 与 property / keyword / texture
|
||||
- renderer 按 `LightMode` 选 pass
|
||||
- `Material::shaderPass` 进入弃用与最终移除路径
|
||||
|
||||
这与 Unity 的 `ShaderTagId / LightMode` 思路对齐,也更利于未来 SRP。
|
||||
|
||||
### 5.6 必须保留 legacy 兼容层
|
||||
|
||||
当前仓库已经有一批 builtin shader 和测试资产。
|
||||
|
||||
因此不能激进地“一刀切重做”,而应:
|
||||
|
||||
- legacy `.shader` 继续可加载
|
||||
- 新 Unity 风格 `.shader` 进入新 importer 路径
|
||||
- builtin shader 分批迁移
|
||||
- runtime 统一落在同一套 `Shader` / artifact / variant 模型上
|
||||
|
||||
---
|
||||
|
||||
## 6. 目标架构
|
||||
|
||||
建议把 shader 体系正式分成 5 层。
|
||||
|
||||
### 6.1 Authoring 层
|
||||
|
||||
职责:
|
||||
|
||||
- 让开发者以 Unity 风格书写 shader
|
||||
|
||||
建议语法子集:
|
||||
|
||||
- `Shader`
|
||||
- `Properties`
|
||||
- `SubShader`
|
||||
- `Pass`
|
||||
- `Tags`
|
||||
- `LOD`
|
||||
- `HLSLINCLUDE`
|
||||
- `HLSLPROGRAM`
|
||||
- `ENDHLSL`
|
||||
- `#pragma vertex`
|
||||
- `#pragma fragment`
|
||||
- `#pragma target`
|
||||
- `#pragma multi_compile`
|
||||
- `#pragma shader_feature`
|
||||
- `#pragma shader_feature_local`
|
||||
- `Cull`
|
||||
- `ZWrite`
|
||||
- `ZTest`
|
||||
- `Blend`
|
||||
- `ColorMask`
|
||||
- `Stencil`
|
||||
- `Offset`
|
||||
|
||||
### 6.2 Importer / Parser 层
|
||||
|
||||
职责:
|
||||
|
||||
- 解析 Unity 风格 `.shader`
|
||||
- 生成统一的内部 `ShaderIR`
|
||||
|
||||
建议引入:
|
||||
|
||||
- `ShaderAuthoringParser`
|
||||
- `ShaderIR`
|
||||
- `ShaderSubShaderIR`
|
||||
- `ShaderPassIR`
|
||||
- `ShaderKeywordDecl`
|
||||
- `ShaderPassStateDesc`
|
||||
- `ShaderProgramIR`
|
||||
|
||||
### 6.3 Compiler / Reflection 层
|
||||
|
||||
职责:
|
||||
|
||||
- 编译 authoring 中的 HLSL
|
||||
- 为不同 backend 生成最终编译产物
|
||||
- 生成 resource layout / constant layout / keyword variant metadata
|
||||
|
||||
建议技术路径:
|
||||
|
||||
- D3D12:`DXC -> DXIL/DXBC`
|
||||
- Vulkan:`DXC -> SPIR-V`
|
||||
- OpenGL:`DXC -> SPIR-V -> SPIRV-Cross -> GLSL 430`
|
||||
|
||||
输出:
|
||||
|
||||
- 每个 pass / stage / keyword-set / backend 的编译产物
|
||||
- 反射出的 constant buffer / texture / sampler 布局
|
||||
|
||||
### 6.4 Artifact 层
|
||||
|
||||
职责:
|
||||
|
||||
- 保存运行时真正消费的 shader 产物
|
||||
|
||||
建议引入新版 artifact:
|
||||
|
||||
- `xcshader2` 或继续升级现有 `xcshader`
|
||||
|
||||
artifact 内容至少包含:
|
||||
|
||||
- shader 名称 / guid
|
||||
- properties
|
||||
- subshader / pass tags
|
||||
- pass state
|
||||
- keyword declarations
|
||||
- keyword variant table
|
||||
- backend binaries / backend source payload
|
||||
- reflected resource layout
|
||||
- include 依赖与 hash
|
||||
|
||||
### 6.5 Runtime 层
|
||||
|
||||
职责:
|
||||
|
||||
- renderer 根据 pass contract、keywords、backend 选择最终 variant
|
||||
- material 根据 property/texture 生成常量与资源绑定
|
||||
- pipeline cache 根据 shader variant + render state 建 key
|
||||
|
||||
---
|
||||
|
||||
## 7. Unity 风格 authoring 范围定义
|
||||
|
||||
### 7.1 第一阶段必须支持的语法
|
||||
|
||||
第一阶段建议正式支持:
|
||||
|
||||
```shaderlab
|
||||
Shader "XCEngine/Example/Lit"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_BaseColor ("Base Color", Color) = (1,1,1,1)
|
||||
_BaseMap ("Base Map", 2D) = "white" {}
|
||||
_Cutoff ("Alpha Cutoff", Range(0,1)) = 0.5
|
||||
}
|
||||
|
||||
HLSLINCLUDE
|
||||
#include "ShaderLibrary/Core.hlsl"
|
||||
ENDHLSL
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags { "RenderType"="Opaque" "Queue"="Geometry" }
|
||||
LOD 200
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "ForwardLit"
|
||||
Tags { "LightMode"="ForwardLit" }
|
||||
Cull Back
|
||||
ZWrite On
|
||||
ZTest LEqual
|
||||
Blend One Zero
|
||||
|
||||
HLSLPROGRAM
|
||||
#pragma target 4.5
|
||||
#pragma vertex Vert
|
||||
#pragma fragment Frag
|
||||
#pragma multi_compile _ XC_MAIN_LIGHT_SHADOWS
|
||||
#pragma shader_feature_local _ XC_ALPHA_TEST
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 第一阶段暂不支持的语法
|
||||
|
||||
第一阶段可暂缓:
|
||||
|
||||
- `Fallback`
|
||||
- `UsePass`
|
||||
- `GrabPass`
|
||||
- `CustomEditor`
|
||||
- `Category`
|
||||
- `Dependency`
|
||||
- Surface Shader
|
||||
- CG fixed-function 时代遗留语义
|
||||
|
||||
这些需要列入兼容性说明,但不应阻塞首版落地。
|
||||
|
||||
---
|
||||
|
||||
## 8. 统一的 shader library 与 include 体系
|
||||
|
||||
这一层是“写法统一”能否成立的关键。
|
||||
|
||||
### 8.1 必须引入正式的 include 库
|
||||
|
||||
建议新增:
|
||||
|
||||
- `engine/assets/shaderlib/ShaderLibrary/Core.hlsl`
|
||||
- `engine/assets/shaderlib/ShaderLibrary/Common.hlsl`
|
||||
- `engine/assets/shaderlib/ShaderLibrary/SpaceTransforms.hlsl`
|
||||
- `engine/assets/shaderlib/ShaderLibrary/Lighting.hlsl`
|
||||
- `engine/assets/shaderlib/ShaderLibrary/MaterialInput.hlsl`
|
||||
- `engine/assets/shaderlib/ShaderLibrary/Shadow.hlsl`
|
||||
|
||||
目标:
|
||||
|
||||
- builtin shader 共用统一宏与公共函数
|
||||
- authoring 层不再重复声明一堆 per-object / lighting 结构
|
||||
|
||||
### 8.2 统一的内建常量组
|
||||
|
||||
建议统一为接近 Unity 的内建分组:
|
||||
|
||||
- `UnityPerFrame` 或 `XCPerFrame`
|
||||
- `UnityPerCamera` 或 `XCPerCamera`
|
||||
- `UnityPerDraw` 或 `XCPerDraw`
|
||||
- `UnityPerMaterial` 或 `XCPerMaterial`
|
||||
|
||||
建议引擎内部最终保留 `XC*` 前缀实现名,但 authoring 宏层提供 Unity 风格别名。
|
||||
|
||||
### 8.3 内建纹理/采样器由 include 库与 reflection 管理
|
||||
|
||||
例如:
|
||||
|
||||
- 主纹理
|
||||
- 阴影图
|
||||
- 环境图
|
||||
- sampler state
|
||||
|
||||
这些都不再由 `.shader` 手填 `Resources(set,binding)`。
|
||||
|
||||
---
|
||||
|
||||
## 9. Material 体系同步改造
|
||||
|
||||
shader 统一如果不带 material 一起改,最后会停在半路。
|
||||
|
||||
### 9.1 material 的职责边界
|
||||
|
||||
新体系里 material 负责:
|
||||
|
||||
- 选择 shader
|
||||
- 保存 property override
|
||||
- 保存 texture override
|
||||
- 保存 keyword 开关
|
||||
- 保存材质级 render queue / tag override(如保留)
|
||||
|
||||
新体系里 material 不再负责:
|
||||
|
||||
- 指定 `shaderPass`
|
||||
- 硬编码 backend 资源名
|
||||
- 猜测 shader 内的 descriptor set / binding
|
||||
|
||||
### 9.2 material constant buffer 正式化
|
||||
|
||||
必须建立:
|
||||
|
||||
- `Properties` -> `UnityPerMaterial/XCPerMaterial` 布局
|
||||
- importer 生成 property layout
|
||||
- material 按 layout 打包 GPU 常量
|
||||
- layout/hash 进入 pipeline/material cache key
|
||||
|
||||
### 9.3 keyword 体系正式化
|
||||
|
||||
建议引入:
|
||||
|
||||
- `global keywords`
|
||||
- `local keywords`
|
||||
- material keyword set
|
||||
- variant lookup key
|
||||
|
||||
material 应持有:
|
||||
|
||||
- `ShaderKeywordSet`
|
||||
|
||||
renderer 运行时根据:
|
||||
|
||||
- shader
|
||||
- pass
|
||||
- keyword set
|
||||
- backend
|
||||
|
||||
选择最终 shader variant。
|
||||
|
||||
---
|
||||
|
||||
## 10. Renderer 运行时契约调整
|
||||
|
||||
### 10.1 pass 选择规则统一为 `LightMode`
|
||||
|
||||
renderer 选 pass 时只看:
|
||||
|
||||
- 当前 pipeline 阶段需要的 `LightMode`
|
||||
- shader/subshader/pass 是否匹配
|
||||
- backend 是否有有效 variant
|
||||
|
||||
例如:
|
||||
|
||||
- 主几何:`ForwardLit` / `Unlit`
|
||||
- 阴影:`ShadowCaster`
|
||||
- 深度:`DepthOnly`
|
||||
- ObjectId:`ObjectId`
|
||||
- FinalOutput:`FinalColor`
|
||||
|
||||
### 10.2 material 显式 `shaderPass` 进入弃用路径
|
||||
|
||||
建议执行顺序:
|
||||
|
||||
1. 第一阶段保留字段,但标记为 legacy
|
||||
2. runtime 优先按 pass contract / LightMode 选 pass
|
||||
3. 只有 legacy 资产才允许 fallback 到 `shaderPass`
|
||||
4. migration 完成后移除 `shaderPass` 主路径职责
|
||||
|
||||
### 10.3 builtin pass metadata 继续保留,但收进 importer/runtime
|
||||
|
||||
当前基于 semantic 的 builtin pass binding 解析仍然有价值,但应改成:
|
||||
|
||||
- authoring/IR 层表达语义
|
||||
- compiler/reflection 层生成 binding plan
|
||||
- runtime 只消费 binding plan
|
||||
|
||||
而不是继续靠散落的名字匹配和 fallback。
|
||||
|
||||
---
|
||||
|
||||
## 11. 三后端统一策略
|
||||
|
||||
### 11.1 D3D12
|
||||
|
||||
目标:
|
||||
|
||||
- 直接消费 HLSL 编译产物
|
||||
- 反射得到 root signature / resource layout 所需元数据
|
||||
|
||||
第一阶段可继续沿用 `ps_5_0 / vs_5_0`,但建议同时规划升级:
|
||||
|
||||
- 后续逐步转 `SM 6.x`
|
||||
|
||||
### 11.2 Vulkan
|
||||
|
||||
目标:
|
||||
|
||||
- 统一吃由 HLSL 编到 SPIR-V 的产物
|
||||
- 摆脱独立 `.vk.glsl` 长期维护
|
||||
|
||||
### 11.3 OpenGL
|
||||
|
||||
目标:
|
||||
|
||||
- 不再长期维护独立 `.glsl` authoring 文件
|
||||
- importer 自动生成 OpenGL 目标 GLSL 430
|
||||
|
||||
这一步会是整个阶段最大的工程风险之一,但它是“写法统一”绕不开的核心点。
|
||||
|
||||
### 11.4 迁移期间的策略
|
||||
|
||||
在新体系落稳前,允许:
|
||||
|
||||
- legacy backend-specific variant 继续存在
|
||||
- 新 Unity 风格 shader 走统一 HLSL single-source 路径
|
||||
|
||||
两套 importer 并行一段时间,最终再逐步淘汰 legacy path。
|
||||
|
||||
---
|
||||
|
||||
## 12. 分阶段实施计划
|
||||
|
||||
### Phase A:冻结目标与建立兼容边界
|
||||
|
||||
目标:
|
||||
|
||||
- 明确“什么叫 Unity 风格 shader”
|
||||
- 明确 legacy 与新 authoring 的兼容边界
|
||||
|
||||
工作项:
|
||||
|
||||
1. 写清 Unity 风格支持子集。
|
||||
2. 明确旧 `.shader` 的 legacy 模式规则。
|
||||
3. 明确新 authoring 中禁止出现:
|
||||
- `#pragma backend`
|
||||
- `Resources(set,binding)`
|
||||
4. 明确 material 中 `shaderPass` 的弃用策略。
|
||||
|
||||
完成标准:
|
||||
|
||||
- 文档、命名、兼容边界全部写死
|
||||
|
||||
### Phase B:建立新的 Shader Authoring Parser 与 IR
|
||||
|
||||
目标:
|
||||
|
||||
- 新 `.shader` authoring 能导入到统一 `ShaderIR`
|
||||
|
||||
工作项:
|
||||
|
||||
1. 新增 parser,支持:
|
||||
- `Shader / Properties / SubShader / Pass / Tags`
|
||||
- `HLSLINCLUDE / HLSLPROGRAM`
|
||||
- `#pragma vertex / fragment / target / multi_compile / shader_feature`
|
||||
- pass state DSL
|
||||
2. 生成 `ShaderIR`
|
||||
3. 支持 include 依赖收集
|
||||
4. 保留 legacy importer
|
||||
|
||||
完成标准:
|
||||
|
||||
- authoring parser 单测齐全
|
||||
- 可以把一个 Unity 风格 shader 解析成稳定 IR
|
||||
|
||||
### Phase C:建立单一 HLSL 编译链
|
||||
|
||||
目标:
|
||||
|
||||
- 打通 `HLSL -> D3D12/Vulkan/OpenGL` 编译管线
|
||||
|
||||
工作项:
|
||||
|
||||
1. 接入 DXC 编译 HLSL。
|
||||
2. Vulkan 产出 SPIR-V。
|
||||
3. OpenGL 产出 GLSL 430。
|
||||
4. 建立 reflection 数据抽取:
|
||||
- cbuffer
|
||||
- texture
|
||||
- sampler
|
||||
- entry point
|
||||
- keywords
|
||||
5. 缓存编译产物与依赖 hash。
|
||||
|
||||
完成标准:
|
||||
|
||||
- 一份 HLSL authoring 能生成三后端产物
|
||||
- OpenGL 不再依赖手写 `.glsl` 作为新体系长期主路径
|
||||
|
||||
### Phase D:Material 与 property/keyword/runtime binding 正式化
|
||||
|
||||
目标:
|
||||
|
||||
- material 能正式驱动新 shader artifact
|
||||
|
||||
工作项:
|
||||
|
||||
1. 引入正式 property layout。
|
||||
2. 引入 material keyword set。
|
||||
3. 生成 `PerMaterial` 常量缓冲布局。
|
||||
4. texture/sampler 绑定从 reflection/约定生成。
|
||||
5. 让 material 运行时不再关心 `set/binding`。
|
||||
|
||||
完成标准:
|
||||
|
||||
- material property / texture / keyword 真正接入 GPU 绑定链
|
||||
|
||||
### Phase E:renderer pass 选择与 pipeline cache 收口
|
||||
|
||||
目标:
|
||||
|
||||
- renderer 完全按 pass contract 驱动 shader
|
||||
|
||||
工作项:
|
||||
|
||||
1. 按 `LightMode` 选 pass。
|
||||
2. `shaderPass` 降级为 legacy fallback。
|
||||
3. pipeline cache key 引入:
|
||||
- shader artifact id
|
||||
- pass id
|
||||
- keyword variant id
|
||||
- render state
|
||||
4. builtin pass 的 runtime contract 全部切到新 artifact。
|
||||
|
||||
完成标准:
|
||||
|
||||
- renderer 主路径不再依赖 material 显式 pass 指定
|
||||
|
||||
### Phase F:分批迁移 builtin shader
|
||||
|
||||
建议迁移顺序:
|
||||
|
||||
1. `unlit`
|
||||
2. `forward-lit`
|
||||
3. `depth-only`
|
||||
4. `shadow-caster`
|
||||
5. `object-id`
|
||||
6. `skybox`
|
||||
7. `color-scale-post-process`
|
||||
8. `final-color`
|
||||
|
||||
完成标准:
|
||||
|
||||
- builtin shader 全部有新 authoring 版本
|
||||
- 旧版 backend 分发文件不再是长期主定义来源
|
||||
|
||||
### Phase G:文档、测试、旧路径收口
|
||||
|
||||
目标:
|
||||
|
||||
- 让新旧体系的边界最终收口
|
||||
|
||||
工作项:
|
||||
|
||||
1. 更新 `tests/TEST_SPEC.md` 中 shader/material 测试矩阵。
|
||||
2. 增加 authoring parser / compiler / runtime 回归测试。
|
||||
3. 更新开发文档。
|
||||
4. 标记 legacy 路径弃用阶段。
|
||||
|
||||
完成标准:
|
||||
|
||||
- 新体系文档、测试、builtin 迁移都完成
|
||||
|
||||
---
|
||||
|
||||
## 13. 测试策略
|
||||
|
||||
### 13.1 Parser / Importer 单测
|
||||
|
||||
必须覆盖:
|
||||
|
||||
- Properties 解析
|
||||
- SubShader / Pass / Tags 解析
|
||||
- HLSLINCLUDE / HLSLPROGRAM 解析
|
||||
- pragma 解析
|
||||
- pass state 解析
|
||||
- include 依赖收集
|
||||
- legacy / 新 authoring 双路径兼容
|
||||
|
||||
### 13.2 Compiler 单测
|
||||
|
||||
必须覆盖:
|
||||
|
||||
- 单一 HLSL 源能生成三后端产物
|
||||
- reflection 结果稳定
|
||||
- keyword variant 正确展开
|
||||
- 编译错误日志可读、可定位到 authoring 源文件
|
||||
|
||||
### 13.3 Material 单测
|
||||
|
||||
必须覆盖:
|
||||
|
||||
- property 默认值
|
||||
- property override
|
||||
- texture binding
|
||||
- keyword set
|
||||
- 常量缓冲布局打包
|
||||
|
||||
### 13.4 Rendering 单测
|
||||
|
||||
必须覆盖:
|
||||
|
||||
- renderer 按 `LightMode` 选 pass
|
||||
- legacy `shaderPass` fallback 行为
|
||||
- keyword variant 参与 pipeline cache key
|
||||
- final color / post-process / shadow / object-id 不回退
|
||||
|
||||
### 13.5 集成测试
|
||||
|
||||
至少回归:
|
||||
|
||||
- `material_state_scene`
|
||||
- `transparent_material_scene`
|
||||
- `camera_stack_scene`
|
||||
- `directional_shadow_scene`
|
||||
- `multi_light_scene`
|
||||
- `skybox_scene`
|
||||
- `post_process_scene`
|
||||
- `final_color_scene`
|
||||
- `object_id_scene`
|
||||
|
||||
要求:
|
||||
|
||||
- 三后端全部跑通
|
||||
- GT 不回退
|
||||
|
||||
---
|
||||
|
||||
## 14. 风险与控制策略
|
||||
|
||||
### 风险 1:OpenGL 是统一写法最难的一环
|
||||
|
||||
原因:
|
||||
|
||||
- OpenGL 当前直接吃 GLSL
|
||||
- 统一 authoring 要求它改为编译链生成目标 GLSL
|
||||
|
||||
控制策略:
|
||||
|
||||
- 先把 importer/IR 做好
|
||||
- OpenGL 先走“生成 GLSL 文本产物”路径
|
||||
- legacy OpenGL GLSL 文件在迁移期保留 fallback
|
||||
|
||||
### 风险 2:一次性追 full Unity 语法会把阶段拖爆
|
||||
|
||||
控制策略:
|
||||
|
||||
- 明确 first-class 子集
|
||||
- 先做 SRP 真正依赖的 authoring 基础
|
||||
- 非关键语法延后
|
||||
|
||||
### 风险 3:material / pass 迁移会破坏当前 builtin renderer
|
||||
|
||||
控制策略:
|
||||
|
||||
- legacy runtime path 保留一段时间
|
||||
- builtin shader 分批迁移
|
||||
- 每迁移一个 shader 就跑对应 integration
|
||||
|
||||
### 风险 4:编译错误如果不可读,会极大拖慢落地
|
||||
|
||||
控制策略:
|
||||
|
||||
- 必须做 authoring 源到 backend 编译日志的映射
|
||||
- 错误日志要带 shader 名、pass 名、stage、backend、源文件行号
|
||||
|
||||
---
|
||||
|
||||
## 15. 收口判定
|
||||
|
||||
满足下面条件时,本阶段可视为完成:
|
||||
|
||||
1. 新 `.shader` authoring 采用 Unity 风格子集。
|
||||
2. 新体系 shader authoring 中不再出现 `#pragma backend`。
|
||||
3. 新体系 shader authoring 中不再出现 `Resources(set,binding)`。
|
||||
4. HLSL single-source 能生成 D3D12 / Vulkan / OpenGL 三后端产物。
|
||||
5. material 已正式接入 property / keyword / texture binding runtime。
|
||||
6. renderer 按 `LightMode` 正式选择 pass。
|
||||
7. `shaderPass` 只剩 legacy fallback,不再是主路径。
|
||||
8. builtin shader 已完成新体系迁移。
|
||||
9. 三后端关键集成测试全部通过。
|
||||
|
||||
---
|
||||
|
||||
## 16. 与后续 SRP 的承接关系
|
||||
|
||||
这一阶段完成后,才能真正自然地承接:
|
||||
|
||||
- `RenderPipelineAsset`
|
||||
- `RenderPipeline`
|
||||
- `RendererFeature`
|
||||
- `ScriptableRenderPass`
|
||||
- C# 层对 shader/material/keyword 的控制
|
||||
|
||||
承接关系应当是:
|
||||
|
||||
```text
|
||||
Unity-style Shader Authoring
|
||||
-> Shader Importer / IR / Artifact
|
||||
-> Native Material & Pass Runtime
|
||||
-> Native Renderer Pass Contract
|
||||
-> C# SRP / RendererFeature
|
||||
```
|
||||
|
||||
也就是说:
|
||||
|
||||
- 这一阶段不是 SRP
|
||||
- 但这是 SRP 成立前必须先做完的最后一层底座
|
||||
|
||||
---
|
||||
|
||||
## 17. 一句话总结
|
||||
|
||||
当前 shader 体系不是没有,而是还停在“过渡态”。
|
||||
|
||||
下一阶段的正确方向不是继续堆更多 shader 功能,而是:
|
||||
|
||||
- **把 `.shader` 的 authoring 真正统一成 Unity 风格**
|
||||
- **把 backend 差异与 binding 细节收回 importer / compiler 层**
|
||||
- **把 material / pass / variant/runtime contract 一次性做正式**
|
||||
|
||||
只有这样,后面的 SRP 才不会建立在一层伪统一的 shader 体系上。
|
||||
@@ -1,556 +0,0 @@
|
||||
# Renderer模块设计与实现
|
||||
|
||||
## 1. 背景
|
||||
|
||||
XCEngine 当前已经完成了较为可用的 RHI 抽象层,且已经具备:
|
||||
|
||||
- `Scene + GameObject + Component` 基础场景模型
|
||||
- `CameraComponent` / `LightComponent` 等基础组件
|
||||
- `Mesh` / `Material` / `Texture` / `Shader` 等资源类型
|
||||
- D3D12 / OpenGL 双后端 RHI 抽象与测试体系
|
||||
|
||||
下一阶段不应该继续封闭式打磨 RHI,而应该在 RHI 之上正式建立 **Renderer 模块**。
|
||||
|
||||
这里的 Renderer 模块不是“最终形态的 SRP”,而是:
|
||||
|
||||
- 先建立一层 **原生渲染运行时**
|
||||
- 先让场景对象能够以正式渲染链路被绘制
|
||||
- 同时在设计上预留未来 **C# Scriptable Render Pipeline(SRP)** 的接入点
|
||||
|
||||
也就是说,当前阶段的正确目标不是直接实现 Unity 的 URP/HDRP,而是先建立一套 **与 Unity 渲染架构方向一致的原生基础层**,后续让 C# SRP 驱动它。
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计目标
|
||||
|
||||
Renderer 模块的目标分为两层:
|
||||
|
||||
### 2.1 当前阶段目标
|
||||
|
||||
先完成一套最小但完整的原生渲染链路:
|
||||
|
||||
- 从 `Scene` 中提取可渲染对象
|
||||
- 通过 `Camera` 构建视图与投影数据
|
||||
- 通过 `Material` / `Mesh` / `Texture` 构建 GPU 绘制数据
|
||||
- 在 RHI 之上完成正式的 frame 渲染
|
||||
- 支持 swapchain 输出与离屏输出
|
||||
- 建立独立于 editor 的渲染宿主模型
|
||||
|
||||
### 2.2 面向未来 C# SRP 的目标
|
||||
|
||||
当前阶段的实现必须为后续演进预留稳定边界:
|
||||
|
||||
- 未来允许用 C# 定义 `RenderPipelineAsset` / `RenderPipeline`
|
||||
- 未来允许用 C# 组织 render pass
|
||||
- 未来允许 editor `SceneView` / `GameView` 通过同一套 renderer 输出
|
||||
- 未来允许 C# 脚本控制 camera 渲染、pass 排序、目标输出与 command buffer
|
||||
|
||||
因此,当前阶段的原生 Renderer 不能做成一个写死的“大一统内建渲染函数”,而应该一开始就具备“可被上层 pipeline 驱动”的结构。
|
||||
|
||||
---
|
||||
|
||||
## 3. 与 Unity 渲染架构的对应关系
|
||||
|
||||
当前建议的路线与 Unity 的总体方向是对齐的,但要注意分层位置。
|
||||
|
||||
### 3.1 推荐分层
|
||||
|
||||
```text
|
||||
Scene / Components / Resources
|
||||
↓
|
||||
Renderer 模块(原生渲染运行时)
|
||||
↓
|
||||
未来 C# SRP 层(脚本化渲染管线)
|
||||
↓
|
||||
RHI 抽象层
|
||||
↓
|
||||
D3D12 / OpenGL / Vulkan 后端
|
||||
```
|
||||
|
||||
### 3.2 各层职责
|
||||
|
||||
#### Scene / Components / Resources
|
||||
|
||||
负责描述“要渲染什么”,例如:
|
||||
|
||||
- 场景对象
|
||||
- 相机
|
||||
- 灯光
|
||||
- 网格
|
||||
- 材质
|
||||
- 贴图
|
||||
|
||||
这一层不应该直接持有后端 API 对象。
|
||||
|
||||
#### Renderer 模块(本阶段核心)
|
||||
|
||||
负责描述“如何从场景变成 draw call”,例如:
|
||||
|
||||
- 渲染对象抽取
|
||||
- 可见性裁剪
|
||||
- GPU 资源缓存
|
||||
- camera frame 数据组织
|
||||
- render target / depth target 管理
|
||||
- render pass 调度
|
||||
- 内建前向管线
|
||||
|
||||
这一层是未来 C# SRP 的原生支撑层。
|
||||
|
||||
#### 未来 C# SRP 层
|
||||
|
||||
负责描述“以脚本方式控制渲染流程”,例如:
|
||||
|
||||
- `RenderPipelineAsset`
|
||||
- `RenderPipeline`
|
||||
- `ScriptableRenderContext`
|
||||
- `CommandBuffer`
|
||||
- `RenderPassEvent`
|
||||
- pass 注入与重排
|
||||
|
||||
这一层不应该直接绕过 Renderer 模块去操作后端 API。
|
||||
|
||||
#### RHI 抽象层
|
||||
|
||||
负责统一 GPU 接口与资源对象,是渲染系统的执行后端,而不是场景渲染逻辑本身。
|
||||
|
||||
### 3.3 与 Unity 的概念映射
|
||||
|
||||
| Unity 概念 | XCEngine 当前/规划对应 |
|
||||
|---|---|
|
||||
| `Camera` | `CameraComponent` |
|
||||
| `Light` | `LightComponent` |
|
||||
| `MeshFilter` | 计划新增 `MeshFilterComponent` |
|
||||
| `MeshRenderer` | 计划新增 `MeshRendererComponent` |
|
||||
| `RenderPipelineAsset` | 未来 Renderer 模块上的 pipeline asset 抽象 |
|
||||
| `RenderPipeline` | 未来 Renderer 模块上的 pipeline 实例抽象 |
|
||||
| `ScriptableRenderContext` | 未来 Renderer 模块对脚本暴露的原生 render context |
|
||||
| `CommandBuffer` | 未来 Renderer 模块对脚本暴露的命令缓冲抽象 |
|
||||
| `GraphicsDevice` / native render backend | 当前 RHI + 后端实现 |
|
||||
|
||||
结论是:
|
||||
|
||||
- **方向上符合 Unity 渲染架构**
|
||||
- **当前阶段实现的应是 Unity 渲染体系中的原生底座**
|
||||
- **而不是直接跳到最终的脚本化 SRP**
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心设计原则
|
||||
|
||||
### 4.1 先建立原生渲染运行时,再开放脚本化管线
|
||||
|
||||
如果现在直接做 C# SRP,而原生 Renderer 边界还不存在,后续会出现:
|
||||
|
||||
- C# API 直接耦合 RHI
|
||||
- editor viewport 与 runtime camera 逻辑混杂
|
||||
- 资源对象与 GPU 对象生命周期混乱
|
||||
|
||||
因此必须先收敛原生 Renderer 模块。
|
||||
|
||||
### 4.2 Scene 层只描述逻辑对象,不持有后端对象
|
||||
|
||||
`GameObject`、`Component`、`Mesh`、`Material` 等对象只能描述逻辑与资源,不应该直接持有 D3D12/OpenGL 私有对象。
|
||||
|
||||
GPU 对象应由 Renderer 内部缓存层负责创建和复用。
|
||||
|
||||
### 4.3 editor 只是渲染宿主,不是渲染逻辑本体
|
||||
|
||||
`GameView` / `SceneView` 最终只是 Renderer 的输出宿主。
|
||||
|
||||
Renderer 本身必须先支持:
|
||||
|
||||
- 输出到 swapchain
|
||||
- 输出到离屏纹理
|
||||
|
||||
然后 editor 再把离屏纹理接进 ImGui 面板。
|
||||
|
||||
### 4.4 为未来 SRP 预留 pipeline 抽象
|
||||
|
||||
即使第一阶段先做内建前向渲染,也不应该把逻辑写死成单一 `SceneRenderer::DrawEverything()`。
|
||||
|
||||
应该从一开始就保留:
|
||||
|
||||
- `RenderPipeline`
|
||||
- `RenderPipelineAsset`
|
||||
- `RenderContext`
|
||||
- camera 列表驱动
|
||||
- pass 分阶段执行
|
||||
|
||||
这样未来 C# 只是在这个原生结构上做绑定,而不是重做一遍架构。
|
||||
|
||||
### 4.5 测试体系与渲染层分离
|
||||
|
||||
`tests/RHI/` 继续只验证 RHI。
|
||||
|
||||
Renderer 模块应建立自己的测试体系:
|
||||
|
||||
- `tests/Rendering/unit/`
|
||||
- `tests/Rendering/integration/`
|
||||
|
||||
这样职责边界才清晰。
|
||||
|
||||
---
|
||||
|
||||
## 5. 模块划分建议
|
||||
|
||||
建议新增 `Rendering` 模块,作为场景与 RHI 之间的正式中间层。
|
||||
|
||||
### 5.1 推荐目录结构
|
||||
|
||||
```text
|
||||
engine/
|
||||
├── include/XCEngine/Rendering/
|
||||
│ ├── RenderSurface.h
|
||||
│ ├── RenderContext.h
|
||||
│ ├── RenderPipeline.h
|
||||
│ ├── RenderPipelineAsset.h
|
||||
│ ├── SceneRenderer.h
|
||||
│ ├── RenderSceneExtractor.h
|
||||
│ ├── RenderCameraData.h
|
||||
│ ├── VisibleRenderObject.h
|
||||
│ ├── RenderResourceCache.h
|
||||
│ └── Pipelines/
|
||||
│ └── BuiltinForwardPipeline.h
|
||||
└── src/Rendering/
|
||||
├── RenderSurface.cpp
|
||||
├── SceneRenderer.cpp
|
||||
├── RenderSceneExtractor.cpp
|
||||
├── RenderResourceCache.cpp
|
||||
└── Pipelines/
|
||||
└── BuiltinForwardPipeline.cpp
|
||||
```
|
||||
|
||||
### 5.2 组件层建议
|
||||
|
||||
为了尽可能对齐 Unity,而不是做一个临时过渡方案,建议直接采用:
|
||||
|
||||
- `MeshFilterComponent`
|
||||
- `MeshRendererComponent`
|
||||
|
||||
其中:
|
||||
|
||||
#### `MeshFilterComponent`
|
||||
|
||||
负责“这个对象使用哪一个 mesh”:
|
||||
|
||||
- `ResourceHandle<Mesh>`
|
||||
|
||||
#### `MeshRendererComponent`
|
||||
|
||||
负责“这个对象如何被渲染”:
|
||||
|
||||
- 材质数组
|
||||
- cast shadow / receive shadow
|
||||
- render queue / layer / enable 状态
|
||||
- 未来可扩展 light probe / motion vector / static batching 标记
|
||||
|
||||
这样做的好处是:
|
||||
|
||||
- 更贴近 Unity 的对象模型
|
||||
- 更容易映射未来 C# API
|
||||
- 更容易在 editor Inspector 中呈现
|
||||
- 更容易为 `SkinnedMeshRenderer`、`SpriteRenderer` 等后续组件扩展留位置
|
||||
|
||||
### 5.3 Renderer 内部运行时对象
|
||||
|
||||
#### `RenderSurface`
|
||||
|
||||
统一表示渲染输出目标:
|
||||
|
||||
- 交换链输出
|
||||
- 离屏 color/depth 输出
|
||||
- editor viewport 输出
|
||||
|
||||
#### `RenderSceneExtractor`
|
||||
|
||||
负责从 `Scene` 中提取本帧可渲染对象:
|
||||
|
||||
- mesh
|
||||
- material
|
||||
- transform
|
||||
- bounds
|
||||
- render state
|
||||
|
||||
#### `RenderResourceCache`
|
||||
|
||||
负责把资源模块对象转成 GPU 可用对象:
|
||||
|
||||
- mesh -> vertex/index buffer
|
||||
- texture -> RHI texture / resource view
|
||||
- material -> descriptor set / uniform buffer / pipeline key
|
||||
- shader pass -> pipeline state
|
||||
|
||||
#### `RenderContext`
|
||||
|
||||
作为原生渲染执行上下文,未来用于承接脚本化 pipeline 的调度。
|
||||
|
||||
它应封装:
|
||||
|
||||
- 当前 frame 的 command list
|
||||
- render target 设置
|
||||
- clear / draw / submit
|
||||
- camera 相关渲染上下文
|
||||
|
||||
#### `RenderPipeline`
|
||||
|
||||
用于抽象具体渲染流程。
|
||||
|
||||
第一阶段只有一个原生内建实现:
|
||||
|
||||
- `BuiltinForwardPipeline`
|
||||
|
||||
未来再开放:
|
||||
|
||||
- native 可切换 pipeline
|
||||
- C# 绑定的 scriptable pipeline
|
||||
|
||||
---
|
||||
|
||||
## 6. 第一阶段实现边界
|
||||
|
||||
第一阶段只做最小可用链路,不做“大而全”。
|
||||
|
||||
### 6.1 第一阶段要做
|
||||
|
||||
- `Rendering` 模块骨架
|
||||
- `MeshFilterComponent` / `MeshRendererComponent`
|
||||
- `RenderSurface`
|
||||
- `RenderSceneExtractor`
|
||||
- `RenderResourceCache`
|
||||
- `SceneRenderer`
|
||||
- `BuiltinForwardPipeline`
|
||||
- 单 camera
|
||||
- 单方向的 opaque forward 渲染
|
||||
- 深度测试与深度写入
|
||||
- 交换链输出
|
||||
- 离屏输出
|
||||
|
||||
### 6.2 第一阶段先不做
|
||||
|
||||
- 阴影
|
||||
- 后处理
|
||||
- 延迟渲染
|
||||
- 完整 PBR
|
||||
- render graph
|
||||
- C# SRP 真正落地
|
||||
- editor viewport 完整交互
|
||||
- 多 camera 叠加
|
||||
|
||||
### 6.3 第一阶段材质能力建议
|
||||
|
||||
建议先支持两档:
|
||||
|
||||
1. `UnlitTexture`
|
||||
2. `SimpleLit`
|
||||
|
||||
其中:
|
||||
|
||||
- `UnlitTexture` 用于先打通最小链路
|
||||
- `SimpleLit` 用于验证灯光、法线与材质基础通路
|
||||
|
||||
---
|
||||
|
||||
## 7. 面向未来 C# SRP 的预留设计
|
||||
|
||||
虽然第一阶段先做原生内建渲染,但必须提前约束下面这些方向。
|
||||
|
||||
### 7.1 先定义 pipeline 边界,再定义内建实现
|
||||
|
||||
正确顺序应当是:
|
||||
|
||||
1. 先定义 `RenderPipeline` 抽象
|
||||
2. 再实现 `BuiltinForwardPipeline`
|
||||
3. 后续 C# SRP 只是在这个边界上做脚本绑定
|
||||
|
||||
而不是:
|
||||
|
||||
1. 先写死一个 `SceneRenderer`
|
||||
2. 以后再强行拆成 pipeline
|
||||
|
||||
第二种方式后续返工会很大。
|
||||
|
||||
### 7.2 Renderer 模块应向未来脚本层暴露的概念
|
||||
|
||||
当前阶段不一定全部实现,但结构上要留位置:
|
||||
|
||||
- `RenderPipelineAsset`
|
||||
- `RenderPipeline`
|
||||
- `RenderContext`
|
||||
- `CullingResults`
|
||||
- `DrawingSettings`
|
||||
- `FilteringSettings`
|
||||
- `ShaderTag`
|
||||
- `CommandBuffer`
|
||||
- `RendererList`
|
||||
|
||||
这些概念不一定要立刻与 Unity 一字不差,但应该在职责上能对应上。
|
||||
|
||||
### 7.3 材质与 Shader 资产模型不能停留在“单 shader 文件 + 属性包”
|
||||
|
||||
未来做 SRP 时,shader pass 选择、render queue、tag、render state 都是必要能力。
|
||||
|
||||
因此当前阶段即使先不全量实现,也不能把资产模型彻底锁死在过于简单的结构上。
|
||||
|
||||
这一点单独列为 issue。
|
||||
|
||||
---
|
||||
|
||||
## 8. 分阶段推进建议
|
||||
|
||||
### 阶段 A:Renderer v0 骨架
|
||||
|
||||
目标:
|
||||
|
||||
- 建立 `Rendering` 模块
|
||||
- 建立 `MeshFilterComponent` / `MeshRendererComponent`
|
||||
- 建立 `RenderSurface`
|
||||
- 建立 `BuiltinForwardPipeline`
|
||||
|
||||
验收:
|
||||
|
||||
- 可以通过 Renderer 正式绘制一个 textured quad 场景
|
||||
- 输出到 swapchain
|
||||
- 输出到离屏 RT
|
||||
|
||||
### 阶段 B:真实资源场景接入
|
||||
|
||||
目标:
|
||||
|
||||
- 接入 mesh / texture / material 资源模块
|
||||
- 跑通 `backpack` 这样的真实模型场景
|
||||
|
||||
验收:
|
||||
|
||||
- 真实 obj 资源经资源模块导入后可通过 Renderer 正式绘制
|
||||
- D3D12 / OpenGL 双后端结果一致
|
||||
|
||||
### 阶段 C:基础光照
|
||||
|
||||
目标:
|
||||
|
||||
- 接入 `LightComponent`
|
||||
- 跑通最基础的单方向光前向渲染
|
||||
|
||||
验收:
|
||||
|
||||
- `sphere` / `backpack` 存在正确基础明暗
|
||||
- 材质参数与法线链路可验证
|
||||
|
||||
### 阶段 D:pipeline 抽象显式化
|
||||
|
||||
目标:
|
||||
|
||||
- 把内建前向渲染切到 `RenderPipeline` 抽象之下
|
||||
- 支持 camera 列表驱动
|
||||
- 为未来 C# SRP 绑定准备原生接口
|
||||
|
||||
验收:
|
||||
|
||||
- 原生内建 pipeline 通过统一接口驱动
|
||||
- renderer 不再依赖单一路径写死执行
|
||||
|
||||
### 阶段 E:editor viewport 接入
|
||||
|
||||
目标:
|
||||
|
||||
- `SceneView` / `GameView` 使用 Renderer 的离屏输出
|
||||
|
||||
验收:
|
||||
|
||||
- editor 面板只是渲染宿主
|
||||
- 不额外复制一套渲染逻辑
|
||||
|
||||
### 阶段 F:C# SRP 桥接
|
||||
|
||||
目标:
|
||||
|
||||
- 在既有 Renderer 模块基础上绑定脚本化 pipeline
|
||||
|
||||
验收:
|
||||
|
||||
- C# 可以控制 camera 渲染流程
|
||||
- 原生 Renderer 继续负责底层资源、上下文与执行
|
||||
|
||||
---
|
||||
|
||||
## 9. 测试体系建议
|
||||
|
||||
Renderer 模块需要独立测试体系。
|
||||
|
||||
### 9.1 单元测试
|
||||
|
||||
建议放在:
|
||||
|
||||
- `tests/Rendering/unit/`
|
||||
|
||||
测试内容:
|
||||
|
||||
- render object 抽取
|
||||
- material 参数打包
|
||||
- pipeline key 构建
|
||||
- GPU cache 命中与失效
|
||||
- render surface 创建与 resize
|
||||
|
||||
### 9.2 集成测试
|
||||
|
||||
建议放在:
|
||||
|
||||
- `tests/Rendering/integration/`
|
||||
|
||||
建议场景:
|
||||
|
||||
1. `textured_quad_scene`
|
||||
2. `backpack_scene`
|
||||
3. `lit_sphere_scene`
|
||||
|
||||
仍然维持当前 RHI 抽象测试的好习惯:
|
||||
|
||||
- 一场景一张 `GT.ppm`
|
||||
- D3D12 / OpenGL 都与同一张 GT 比对
|
||||
|
||||
### 9.3 与 RHI 测试的关系
|
||||
|
||||
`tests/RHI/` 继续用于验证:
|
||||
|
||||
- API 抽象正确性
|
||||
- 后端行为一致性
|
||||
- 资源 / 命令 /格式映射等底层问题
|
||||
|
||||
`tests/Rendering/` 则验证:
|
||||
|
||||
- 场景渲染链路
|
||||
- 组件与资源到渲染结果的闭环
|
||||
|
||||
---
|
||||
|
||||
## 10. 当前已识别的不适配问题
|
||||
|
||||
以下问题不适合直接塞进本设计文档正文实现里,而应该独立跟踪:
|
||||
|
||||
1. `Scene / Components` 层还没有 `MeshFilter / MeshRenderer` 抽象
|
||||
2. `Editor` 还没有 viewport 的离屏渲染宿主接入层
|
||||
3. `Material / Shader` 资产模型还不足以支撑未来 SRP 的 pass/tag 语义
|
||||
|
||||
对应 issue:
|
||||
|
||||
- `docs/issues/Renderer模块_Scene层缺少MeshFilter与MeshRenderer抽象.md`
|
||||
- `docs/issues/Renderer模块_EditorViewport缺少RenderSurface接入层.md`
|
||||
- `docs/issues/Renderer模块_Material与Shader资产模型暂不满足SRP演进需求.md`
|
||||
|
||||
---
|
||||
|
||||
## 11. 结论
|
||||
|
||||
当前 Renderer 阶段的正确方向是:
|
||||
|
||||
- 在 RHI 之上建立 **原生渲染运行时**
|
||||
- 用它先承接基础前向渲染
|
||||
- 同时提前为未来 **C# SRP** 留出清晰接口
|
||||
|
||||
因此,下一阶段的 Renderer 规划如果按本文执行,是与 Unity 渲染架构方向相容的,而且比“先做一个临时内建 renderer,后面再拆”更稳。
|
||||
|
||||
一句话概括:
|
||||
|
||||
- **现在做的是 Unity 式渲染体系的原生底座**
|
||||
- **以后在这个底座之上接 C# SRP**
|
||||
|
||||
@@ -1,415 +0,0 @@
|
||||
# Renderer 结构收口与代码正式化计划
|
||||
日期:`2026-04-05`
|
||||
|
||||
## 1. 阶段定位
|
||||
|
||||
当前 Rendering 主线在功能上已经完成了相当多闭环:
|
||||
|
||||
- 三后端统一的 runtime renderer 主链已经建立
|
||||
- directional shadow、multi-light、object-id、editor overlay 等能力都已接入
|
||||
- SceneView / GameView 基本共用了同一条 runtime 渲染路径
|
||||
|
||||
但从代码结构和职责边界上看,这一阶段还没有真正收口。现在的问题已经不再是“某个功能没接上”,而是:
|
||||
|
||||
**renderer 的核心模块里仍然混有明显的阶段性写法、特殊分支、职责堆叠和 editor/runtime 边界不清的问题。**
|
||||
|
||||
如果此时直接继续往上叠 skybox、环境、后处理、更多 renderer feature,后面会越来越难拆,最终重新把已经相对稳定的 renderer 主链拖回到“能跑但很难维护”的状态。
|
||||
|
||||
因此,本阶段的唯一目标不是加新功能,而是:
|
||||
|
||||
**把当前 renderer 这一阶段真正做成可持续演进的正式结构,为后续 Skybox / Environment / PostProcess / 更正式的 SRP 承接清掉结构债。**
|
||||
|
||||
## 2. 为什么现在必须先做结构收口
|
||||
|
||||
这不是“目录看着乱一点”的表面问题,而是已经影响后续演进的实质性架构问题。
|
||||
|
||||
### 2.1 `CameraRenderer` 仍然存在特殊通道
|
||||
|
||||
当前 `CameraRenderer` 虽然已经具备请求规划与多阶段执行能力,但 `object-id` 仍然是单独的一套特殊路径,而不是正式 frame composition 里的统一 pass 节点。
|
||||
|
||||
这带来的问题:
|
||||
|
||||
- 相机级执行顺序不是单一模型,而是“主链 + 特判”
|
||||
- 后续 skybox / post-process / capture / debug target 更难正规接入
|
||||
- 单元测试里被迫维护特殊 mock pass 类型,而不是统一的 pass contract
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/include/XCEngine/Rendering/ObjectIdPass.h`
|
||||
- `engine/include/XCEngine/Rendering/CameraRenderer.h`
|
||||
- `engine/include/XCEngine/Rendering/CameraRenderRequest.h`
|
||||
- `engine/src/Rendering/CameraRenderer.cpp`
|
||||
|
||||
### 2.2 `BuiltinForwardPipeline.cpp` 已经是典型 god file
|
||||
|
||||
这个文件里当前同时混着:
|
||||
|
||||
- pass wrapper
|
||||
- shader pass resolve
|
||||
- graphics pipeline 创建
|
||||
- descriptor set layout 规划
|
||||
- descriptor set 资源写入
|
||||
- lighting 常量打包
|
||||
- material fallback
|
||||
- draw submission
|
||||
|
||||
这已经不是“文件有点长”,而是职责拆分失败。后续任何修改都会把高层语义、资源绑定、RHI 细节、draw 级逻辑一起牵动,测试也只能做大颗粒回归,无法精准保护。
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp`
|
||||
|
||||
### 2.3 `RenderMaterialUtility.h` 混合了契约、兼容层和运行时解析
|
||||
|
||||
当前这个头里至少混了五类职责:
|
||||
|
||||
- builtin pass contract 定义
|
||||
- shader/property/binding 查询
|
||||
- descriptor layout 规划
|
||||
- legacy/material fallback 兼容
|
||||
- runtime material resolve 与绑定辅助
|
||||
|
||||
这会导致:
|
||||
|
||||
- “正式 contract” 与 “过渡兼容逻辑” 难以分开演进
|
||||
- 很多 renderer 代码只能依赖一个超大工具头
|
||||
- 头文件膨胀,职责不可读,接口边界不清
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/include/XCEngine/Rendering/RenderMaterialUtility.h`
|
||||
|
||||
### 2.4 editor / debug pass 仍然混在 runtime renderer 核心层里
|
||||
|
||||
grid、outline 这些能力现在已经走到了比较正式的 runtime host path,但它们在 engine 里的组织方式仍然更接近“把 editor 需求塞进 renderer 核心”。
|
||||
|
||||
风险在于:
|
||||
|
||||
- runtime 核心会继续被 editor 语义污染
|
||||
- 后续 scene/game/editor 三条宿主路径边界会再次变模糊
|
||||
- 玩家运行时和编辑器专用渲染能力的依赖关系难以长期维护
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h`
|
||||
- `engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h`
|
||||
|
||||
### 2.5 文件拆分层次仍然不干净
|
||||
|
||||
例如 `BuiltinDepthStylePassBase.cpp` 的底部还直接放着 `BuiltinDepthOnlyPass`、`BuiltinShadowCasterPass` 具体实现,这说明“抽象基类”和“具体 pass”仍然没有彻底分层。
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp`
|
||||
|
||||
### 2.6 少量稳定性问题仍然暴露出“临时写法”
|
||||
|
||||
比如 scene extractor 里 visible item 的稳定排序仍然用 raw pointer 作为 tie-breaker,而 additional light 已经升级成了稳定的 `GameObject::ID` 语义。
|
||||
|
||||
这类问题虽然不大,但非常能说明当前代码里仍混有阶段性临时写法,必须顺手清理干净。
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/src/Rendering/RenderSceneExtractor.cpp`
|
||||
|
||||
## 3. 本阶段的核心设计原则
|
||||
|
||||
本阶段继续严格遵循当前工程既定设计理念,并与 `RHI模块总览` 中的核心原则保持一致:
|
||||
|
||||
1. `RHI` 只负责 GPU 抽象,不感知 object-id、grid、outline、skybox、post-process 等高层语义。
|
||||
2. `Renderer` 负责 scene extraction、frame composition、material/shader contract、runtime pass orchestration。
|
||||
3. `Editor` 只作为 renderer 的宿主和扩展使用方,不把 editor 语义反向污染 RHI。
|
||||
4. 兼容层和正式 contract 必须拆开,不能继续把“临时兜底”混在正式主链里。
|
||||
5. 不引入 `render graph`。本阶段先把现有 renderer 结构做正式化,不跳级优化。
|
||||
6. 不做“修修补补式”文件搬家,必须同时修职责边界、执行路径和测试结构。
|
||||
|
||||
## 4. 阶段总目标
|
||||
|
||||
本阶段收口完成后,应达到以下状态:
|
||||
|
||||
1. `CameraRenderer` 形成单一、明确、可测试的 frame composition 模型。
|
||||
2. runtime pass 与 object-id / editor-debug pass 的边界清晰,接入点正式化。
|
||||
3. `BuiltinForwardPipeline` 不再由一个 god file 承担所有责任。
|
||||
4. `RenderMaterialUtility` 被拆分为“正式 contract 层”和“兼容/运行时辅助层”。
|
||||
5. renderer 文件结构与代码结构一致,抽象基类、具体 pass、绑定辅助、compat helper 各归其位。
|
||||
6. 当前所有 rendering / editor 相关测试继续通过,不破坏已闭环功能。
|
||||
|
||||
## 5. 非目标
|
||||
|
||||
本阶段明确不做:
|
||||
|
||||
- `render graph`
|
||||
- deferred / clustered / tiled lighting
|
||||
- 新一轮 editor 视觉特效堆叠
|
||||
- 大规模 shader authoring 体系重写
|
||||
- point / spot shadow
|
||||
- 重新设计 RHI
|
||||
|
||||
## 6. 分阶段执行方案
|
||||
|
||||
## 6.1 Phase A:Camera Frame Composition 正式化
|
||||
|
||||
### 目标
|
||||
|
||||
消灭 `CameraRenderer` 里 object-id 的特殊执行通道,把相机级执行模型统一成正式 frame composition。
|
||||
|
||||
### 要解决的根因
|
||||
|
||||
- 现在相机渲染顺序不是单一 contract
|
||||
- `ObjectIdPass` 是旁路抽象,不利于后续继续扩展 composition
|
||||
- 测试里存在针对 object-id 的特殊 mock pass 体系
|
||||
|
||||
### 具体工作
|
||||
|
||||
1. 重新审视 `CameraRenderRequest` 的阶段描述,明确:
|
||||
- pre-scene
|
||||
- shadow/depth
|
||||
- scene pipeline
|
||||
- auxiliary offscreen passes
|
||||
- post-scene
|
||||
- overlay
|
||||
2. 去掉 `ObjectIdPass` 作为并行特例抽象的地位。
|
||||
3. 把 object-id 统一纳入正式 pass 执行序列,必要时通过 pass category / target intent 标识语义,而不是再保留独立虚函数族。
|
||||
4. 简化 `CameraRenderer` 执行逻辑,让失败传播、目标准备、pass 顺序只走一套主链。
|
||||
5. 同步收敛相关单元测试,让测试验证“阶段顺序”和“失败传播”,而不是验证某个特判分支。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- `CameraRenderer` 不再对 object-id 走特判主逻辑
|
||||
- `test_camera_scene_renderer` 等单测仍覆盖 object-id 顺序与失败传播
|
||||
- editor viewport object-id picking 不回退
|
||||
|
||||
### 计划提交点
|
||||
|
||||
这一阶段完成后立即提交推送一次。
|
||||
|
||||
## 6.2 Phase B:BuiltinForwardPipeline 职责拆分
|
||||
|
||||
### 目标
|
||||
|
||||
把 `BuiltinForwardPipeline.cpp` 从 god file 拆成正式的职责层次,但不改变现有 forward runtime 的对外行为。
|
||||
|
||||
### 要解决的根因
|
||||
|
||||
- pipeline resolve、resource layout、descriptor write、material resolve、lighting packing、draw submission 全部耦合
|
||||
- 任何小修改都会波及整个文件
|
||||
- 难以为 skybox / post-process / future pipeline 承接建立稳定接口
|
||||
|
||||
### 具体工作
|
||||
|
||||
1. 先按职责切出独立模块,优先拆成以下几层:
|
||||
- shader/pass resolve
|
||||
- pipeline cache/build
|
||||
- resource binding layout / descriptor planning
|
||||
- frame-scoped lighting / pass constants upload
|
||||
- draw item submission
|
||||
2. 让 `BuiltinForwardPipeline` 保留 orchestration 职责,而不是继续承载全部细节。
|
||||
3. 清理与 `RenderMaterialUtility` 的交叉依赖,为下一阶段拆 contract 做准备。
|
||||
4. 保证 unlit / lit / object-id / depth-only / shadow-caster 的绑定逻辑不被混淆。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- `BuiltinForwardPipeline.cpp` 明显缩小,核心职责清晰
|
||||
- 新拆出的模块命名与职责稳定,不是单纯“工具类化”
|
||||
- forward 相关单测、集成测试全部不回退
|
||||
|
||||
### 计划提交点
|
||||
|
||||
这一阶段完成后立即提交推送一次。
|
||||
|
||||
## 6.3 Phase C:RenderMaterialUtility 正式拆层
|
||||
|
||||
### 目标
|
||||
|
||||
把 shader/material/pass 的正式 contract 与 legacy/compat/runtime helper 拆开。
|
||||
|
||||
### 要解决的根因
|
||||
|
||||
- 正式接口和过渡逻辑混在一起
|
||||
- 任何依赖 `RenderMaterialUtility.h` 的代码都会被迫包含大量不相干能力
|
||||
- 后续 shader/material 演进会被兼容逻辑长期绑死
|
||||
|
||||
### 具体工作
|
||||
|
||||
1. 明确拆成三层语义:
|
||||
- `contract`:builtin pass 名称、标准 binding 名称、正式解析规则
|
||||
- `runtime resolve`:材质/着色器运行时查询、pass 选择、绑定规划
|
||||
- `compat`:legacy property 名称、历史 fallback、过渡适配
|
||||
2. 避免再把大段实现继续塞在头文件里,能下沉到 `.cpp` 的尽量下沉。
|
||||
3. 对外只暴露最小且稳定的正式接口。
|
||||
4. 给 compat 层加清晰边界,避免以后继续被当作默认主路径使用。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- `RenderMaterialUtility.h` 体量显著下降,职责单一
|
||||
- renderer 主链依赖的是正式 contract / runtime resolve,而不是 compat 大杂烩
|
||||
- 现有 shader/material 行为与测试结果保持一致
|
||||
|
||||
### 计划提交点
|
||||
|
||||
这一阶段完成后立即提交推送一次。
|
||||
|
||||
## 6.4 Phase D:Runtime Pass 与 Editor/Debug Pass 边界重整
|
||||
|
||||
### 目标
|
||||
|
||||
明确 engine runtime rendering core 与 editor/debug-oriented rendering extension 的边界。
|
||||
|
||||
### 要解决的根因
|
||||
|
||||
- grid、outline 等语义虽然已经可用,但组织上仍偏临时
|
||||
- engine 核心层里混有 editor 专用概念
|
||||
- 后续 camera frame composition 扩展容易再次被 editor 需求污染
|
||||
|
||||
### 具体工作
|
||||
|
||||
1. 明确哪些是 runtime 正式能力,哪些是 editor/debug extension。
|
||||
2. 把 editor/debug pass 的注册与宿主接入方式整理成正式 extension seam。
|
||||
3. 保持 SceneView / GameView 继续复用 runtime renderer 主链,但 editor overlay / outline / grid 不侵入 runtime scene composition。
|
||||
4. 补足必要文档,说明 engine、renderer、editor 三者的责任边界。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- editor/debug pass 不再作为 runtime renderer 核心概念扩散
|
||||
- SceneView / GameView 显示、grid、outline、gizmo 宿主路径不回退
|
||||
- 新增代码结构能自然承接后续 icon/light gizmo/camera gizmo 等扩展
|
||||
|
||||
### 计划提交点
|
||||
|
||||
这一阶段完成后立即提交推送一次。
|
||||
|
||||
## 6.5 Phase E:稳定性清扫、文件收口与文档归档
|
||||
|
||||
### 目标
|
||||
|
||||
清掉这一阶段剩余的临时写法,让实现、测试、文档口径再次一致。
|
||||
|
||||
### 具体工作
|
||||
|
||||
1. 修正 `RenderSceneExtractor` 里仍然使用 raw pointer 的稳定排序 tie-breaker。
|
||||
2. 把 `BuiltinDepthStylePassBase.cpp` 中具体 pass 实现拆出到独立文件。
|
||||
3. 全面复查 renderer 相关文件命名、目录结构、头源分布是否仍有明显反模式。
|
||||
4. 更新 `tests/TEST_SPEC.md` 与相关 renderer / editor guide。
|
||||
5. 阶段完成后,把已过期 plan 归档到 `docs/used`。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- renderer 核心目录结构与职责边界基本一致
|
||||
- 没有明显残留的阶段性临时代码入口
|
||||
- 文档、测试矩阵、实现状态三者一致
|
||||
|
||||
### 计划提交点
|
||||
|
||||
这一阶段完成后立即提交推送一次。
|
||||
|
||||
## 7. 测试策略
|
||||
|
||||
本阶段的测试必须是“每阶段落地即验证”,不能到最后一次性回归。
|
||||
|
||||
### 7.1 Unit
|
||||
|
||||
重点保护:
|
||||
|
||||
- `CameraRenderer` 阶段顺序与失败传播
|
||||
- `RenderSceneExtractor` 的稳定输出
|
||||
- `BuiltinForwardPipeline` 的绑定与材质解析
|
||||
- material/shader contract 拆层后的接口行为
|
||||
|
||||
优先关注:
|
||||
|
||||
- `tests/Rendering/unit/test_camera_scene_renderer.cpp`
|
||||
- `tests/Rendering/unit/test_builtin_forward_pipeline.cpp`
|
||||
- 与 material utility / scene extractor 相关的 unit tests
|
||||
|
||||
### 7.2 Editor / Runtime Integration
|
||||
|
||||
重点回归:
|
||||
|
||||
- object-id picking
|
||||
- SceneView / GameView runtime 渲染链
|
||||
- overlay / outline / grid
|
||||
- backpack / shadow / multi-light / camera stack / offscreen 等现有场景
|
||||
|
||||
至少覆盖:
|
||||
|
||||
- `tests/editor/test_viewport_render_flow_utils.cpp`
|
||||
- `tests/editor/test_scene_viewport_overlay_renderer.cpp`
|
||||
- `tests/editor/test_viewport_object_id_picker.cpp`
|
||||
- 现有 rendering integration matrix 中与 lighting、object-id、camera flow 相关的场景
|
||||
|
||||
### 7.3 编译与宿主验证
|
||||
|
||||
每一阶段至少执行:
|
||||
|
||||
1. 相关 test target 编译
|
||||
2. 相关 unit / integration tests
|
||||
3. 必要时编译 `XCEditor`
|
||||
4. 对 editor 中 SceneView / GameView 做 smoke 验证
|
||||
|
||||
## 8. 风险与控制
|
||||
|
||||
### 风险 1:把“结构重构”做成单纯的文件搬家
|
||||
|
||||
后果:
|
||||
|
||||
- 文件名变了,职责没变
|
||||
- 代码仍然继续跨层互相依赖
|
||||
|
||||
控制策略:
|
||||
|
||||
- 每次拆分都要同时调整接口边界和测试保护点
|
||||
|
||||
### 风险 2:为了图省事继续保留 object-id 特判
|
||||
|
||||
后果:
|
||||
|
||||
- Camera frame composition 永远无法正式化
|
||||
- 后续 skybox / post-process 会继续引入更多特判
|
||||
|
||||
控制策略:
|
||||
|
||||
- 第一阶段必须先砍掉这类特殊旁路
|
||||
|
||||
### 风险 3:compat 逻辑继续侵入正式 contract
|
||||
|
||||
后果:
|
||||
|
||||
- shader/material 体系长期混乱
|
||||
- 之后 Unity 风格 shader authoring 很难落地
|
||||
|
||||
控制策略:
|
||||
|
||||
- compat 层必须显式命名、显式隔离、显式限定使用场景
|
||||
|
||||
### 风险 4:editor/debug pass 重整时破坏现有 editor 体验
|
||||
|
||||
后果:
|
||||
|
||||
- 影响当前 SceneView 主线
|
||||
- 把结构收口又变成功能回退
|
||||
|
||||
控制策略:
|
||||
|
||||
- 每一阶段都要做 editor smoke 和既有测试回归
|
||||
|
||||
## 9. 阶段完成判定
|
||||
|
||||
满足以下条件时,本阶段才算真正收口:
|
||||
|
||||
1. `CameraRenderer` 已统一成正式 frame composition 执行模型。
|
||||
2. `BuiltinForwardPipeline` 与 `RenderMaterialUtility` 已完成职责拆分,核心 god file 问题消除。
|
||||
3. runtime pass 与 editor/debug pass 边界清晰,不再混成一团。
|
||||
4. 现有 rendering / editor tests 继续稳定通过。
|
||||
5. `docs/plan` 与 `docs/used` 的 plan 入口重新清晰,不再保留已过期的执行入口。
|
||||
|
||||
## 10. 与当前主线的关系
|
||||
|
||||
这份计划不是替代“Skybox 环境与 Frame Composition 正式化”方向,而是它的前置收口。
|
||||
|
||||
顺序必须是:
|
||||
|
||||
1. 先做 renderer 结构收口与代码正式化
|
||||
2. 再做 skybox / environment / post-process 的正式接入
|
||||
3. 最后才考虑更高阶的 renderer feature 与未来 SRP 承接
|
||||
|
||||
否则就是在结构债未清的情况下继续加层,后面只会越收越难。
|
||||
@@ -1,523 +0,0 @@
|
||||
# Renderer阶段收口:旧兼容路径清理与正式化计划
|
||||
|
||||
日期:`2026-04-08`
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前 `Rendering` 模块的主执行架构已经基本成型:
|
||||
|
||||
- `RenderSceneExtractor`
|
||||
- `SceneRenderRequestPlanner`
|
||||
- `SceneRenderer / CameraRenderer`
|
||||
- built-in forward / shadow / object-id / outline / final-color / skybox
|
||||
|
||||
这些主链路已经能稳定支撑:
|
||||
|
||||
- runtime 场景渲染
|
||||
- editor scene/game viewport
|
||||
- 多光源、阴影、object-id、outline、skybox 等现有能力
|
||||
|
||||
因此,当前 Rendering 的主要问题已经不再是“能不能画出来”,而是:
|
||||
|
||||
- 还残留一些旧路线兼容代码
|
||||
- 一些 built-in 运行契约仍然依赖隐式推断
|
||||
- 少量路径仍然带有明显的过渡期实现痕迹
|
||||
|
||||
如果这些问题不在当前阶段彻底收口,后续继续推进:
|
||||
|
||||
- Renderer 模块扩展
|
||||
- Material / Shader editor
|
||||
- Unity 风格 SRP 底层承接
|
||||
|
||||
就会持续建立在一层“虽然能跑,但不是正式规则”的兼容逻辑之上。
|
||||
|
||||
这不符合当前阶段的目标。
|
||||
|
||||
当前阶段的正确方向不是新增更多渲染功能,而是:
|
||||
|
||||
- 清理旧兼容路径
|
||||
- 去掉运行时语义猜测
|
||||
- 把 built-in shader / material / pass contract 进一步正式化
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前已确认的问题
|
||||
|
||||
基于本轮对 `engine/include/XCEngine/Rendering`、`engine/src/Rendering`、`engine/src/Resources/Shader`、`engine/src/Resources/Mesh` 的代码审查,当前确认存在以下问题。
|
||||
|
||||
### 2.1 Mesh 导入仍可生成“无 shader / 无 schema”的旧材质路线
|
||||
|
||||
当前 `MeshLoader` 导入子材质时,仍然直接写入:
|
||||
|
||||
- `baseColor`
|
||||
- `baseColorTexture`
|
||||
- `opacity`
|
||||
- `twoSided`
|
||||
|
||||
而不是直接落到正式 shader schema 对应的属性名与纹理槽位。
|
||||
|
||||
这导致 runtime 渲染阶段仍然需要兜底兼容这些旧名字。
|
||||
|
||||
典型位置:
|
||||
|
||||
- `engine/src/Resources/Mesh/MeshLoader.cpp`
|
||||
- `engine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h`
|
||||
|
||||
### 2.2 Rendering 仍通过属性别名表推断 built-in 材质语义
|
||||
|
||||
当前 `RenderMaterialResolve.h` 中,仍然保留了大量 builtin 属性/纹理别名表,例如:
|
||||
|
||||
- `baseColor`
|
||||
- `_BaseColor`
|
||||
- `color`
|
||||
- `_Color`
|
||||
- `baseColorTexture`
|
||||
- `_BaseColorTexture`
|
||||
- `_MainTex`
|
||||
- `texture`
|
||||
|
||||
这意味着 runtime 当前并不是“按 shader schema 正式解析”,而是:
|
||||
|
||||
- 优先找 semantic
|
||||
- 找不到就继续按一批旧属性名字猜
|
||||
|
||||
这属于典型过渡兼容逻辑,不应成为正式长期实现。
|
||||
|
||||
### 2.3 BuiltinForward / Depth / Shadow 仍存在 per-material fallback constant 路线
|
||||
|
||||
当前如果材质没有正式 schema constant layout,管线仍会临时构造:
|
||||
|
||||
- `FallbackPerMaterialConstants`
|
||||
|
||||
并继续提交 draw。
|
||||
|
||||
这说明 runtime 仍允许“非正式材质实例”继续进入正式绘制链路。
|
||||
|
||||
这条路径虽然提高了兼容性,但本质上绕开了已经建立的 shader/material 正式模型。
|
||||
|
||||
### 2.4 Built-in pass resource binding 仍依赖隐式硬编码
|
||||
|
||||
当前 builtin shader pass 如果未显式声明 `resources`,运行时仍会通过:
|
||||
|
||||
- `TryBuildImplicitBuiltinPassResourceBindings`
|
||||
|
||||
自动补一套绑定布局。
|
||||
|
||||
这意味着资源绑定契约并不完全存在于 shader 资产中,而是仍有一部分硬编码在 C++ 中。
|
||||
|
||||
这会带来两个问题:
|
||||
|
||||
1. shader 资产与 runtime 存在双份真相
|
||||
2. 后续继续演进 shader/material/editor 时,容易再次产生隐式规则
|
||||
|
||||
### 2.5 HLSL register 重写仍保留 legacy alias
|
||||
|
||||
当前 `ShaderVariantUtils.h` 仍保留:
|
||||
|
||||
- `ResolveLegacyHlslBindingDeclarationAlias`
|
||||
|
||||
以及基于 `gBaseColorTexture` / `gLinearSampler` 一类旧命名的重写逻辑。
|
||||
|
||||
这说明 shader runtime 编译阶段仍在兼容旧命名风格。
|
||||
|
||||
这属于典型“过渡兼容层”,应在 built-in shader 显式资源契约完成后清掉。
|
||||
|
||||
### 2.6 Built-in pass 选择仍存在隐式默认规则
|
||||
|
||||
当前如果 shader 没有显式 builtin metadata,`MatchesBuiltinPass(...)` 仍会把它默认当成:
|
||||
|
||||
- `ForwardLit`
|
||||
|
||||
这意味着 shader 即使没有明确声明自己属于哪个 built-in pass,也有可能继续进入主几何管线。
|
||||
|
||||
这不利于长期正式化。
|
||||
|
||||
### 2.7 Shader artifact 仍兼容多代旧 schema
|
||||
|
||||
当前 shader artifact loader 仍兼容:
|
||||
|
||||
- `XCSHD01`
|
||||
- `XCSHD02`
|
||||
- `XCSHD03`
|
||||
- `XCSHD04`
|
||||
- 当前 schema
|
||||
|
||||
但 shader artifact 本质上是 `Library` 中的可重建中间产物,不属于必须长期 runtime 兼容的用户资产格式。
|
||||
|
||||
如果继续保留多代 schema 分支,会让 shader 资源链路长期背着历史包袱。
|
||||
|
||||
---
|
||||
|
||||
## 3. 本阶段设计原则
|
||||
|
||||
本计划执行时,必须严格遵守以下原则。
|
||||
|
||||
### 3.1 正式路径只能有一条
|
||||
|
||||
对 built-in shader / material / pass 来说,正式路径必须是:
|
||||
|
||||
`导入/authoring -> shader schema -> material instance -> explicit pass contract -> render pipeline`
|
||||
|
||||
不能继续允许 runtime 依赖旧命名、旧别名、旧格式去自动猜测。
|
||||
|
||||
### 3.2 兼容应尽量前移到导入/重建阶段,而不是留在 runtime
|
||||
|
||||
如果确实存在历史资产问题,应优先采用:
|
||||
|
||||
- 重新导入
|
||||
- 重新生成 artifact
|
||||
- 一次性迁移
|
||||
|
||||
而不是继续在 runtime loader / renderer 中保留长期兼容分支。
|
||||
|
||||
### 3.3 Built-in shader 契约必须显式写进 shader 资产
|
||||
|
||||
以下内容必须属于 shader/pass 资产本身,而不是 runtime 猜出来:
|
||||
|
||||
- pass 类型
|
||||
- pass metadata
|
||||
- resource binding
|
||||
- property semantic
|
||||
|
||||
### 3.4 Rendering 不再为“无正式 shader/schema 的材质”兜底渲染
|
||||
|
||||
当前阶段的目标是“收口”,不是“继续最大化兼容”。
|
||||
|
||||
因此:
|
||||
|
||||
- 非正式材质应尽快在导入层修正
|
||||
- runtime 应逐步拒绝无 schema 的正式绘制路径
|
||||
|
||||
### 3.5 每一步都必须可验证
|
||||
|
||||
每个阶段完成后必须配套:
|
||||
|
||||
- unit test
|
||||
- 必要的 integration test
|
||||
- editor 编译/回归
|
||||
|
||||
不能只凭画面“看起来没问题”判断完成。
|
||||
|
||||
---
|
||||
|
||||
## 4. 本阶段目标
|
||||
|
||||
本阶段完成后,Rendering 模块应达到以下状态:
|
||||
|
||||
1. Mesh 导入出来的材质直接走正式 shader/material 体系
|
||||
2. runtime 不再依赖 `baseColor` / `_MainTex` 等别名表去维持 built-in 主链
|
||||
3. built-in pass resource binding 由 shader 资产显式声明,不再依赖隐式硬编码补全
|
||||
4. built-in pass 分类必须显式声明,不再存在“默认 ForwardLit”
|
||||
5. shader artifact runtime loader 不再长期兼容多代旧 schema
|
||||
6. 对应测试体系同步升级,保证收口后功能不回退
|
||||
|
||||
---
|
||||
|
||||
## 5. 明确不在本阶段处理的内容
|
||||
|
||||
以下内容不属于本阶段目标:
|
||||
|
||||
- render graph
|
||||
- deferred renderer
|
||||
- 新一轮后处理功能扩展
|
||||
- C# SRP 脚本侧 API
|
||||
- ShaderGraph
|
||||
- 高级材质编辑器功能扩展
|
||||
|
||||
这些方向都依赖本阶段先把底层 contract 收紧。
|
||||
|
||||
---
|
||||
|
||||
## 6. 分阶段执行计划
|
||||
|
||||
## Phase 1:建立基线与目标测试
|
||||
|
||||
### 目标
|
||||
|
||||
先把当前遗留兼容路径的行为边界用测试钉住,并同步写出“目标行为”的新测试。
|
||||
|
||||
### 任务
|
||||
|
||||
- 审查并整理当前覆盖以下行为的测试:
|
||||
- `RenderMaterialResolve`
|
||||
- builtin forward pipeline resource binding
|
||||
- mesh material import
|
||||
- shader artifact load
|
||||
- 新增/调整测试,使其明确区分:
|
||||
- 当前历史兼容行为
|
||||
- 本阶段目标正式行为
|
||||
- 对以下目标先写失败测试或待切换测试:
|
||||
- imported mesh material 必须绑定正式 builtin shader
|
||||
- imported material property 必须落到正式 schema 名称
|
||||
- builtin pass 若无显式 metadata,不得进入主 pipeline
|
||||
- builtin shader 若无显式 resources,不得依赖隐式 binding 补全
|
||||
|
||||
### 验收标准
|
||||
|
||||
- 能清楚列出哪些测试在保护旧行为,哪些测试在保护目标行为
|
||||
- 后续每个阶段都能基于这些测试判断是否真正收口
|
||||
|
||||
---
|
||||
|
||||
## Phase 2:收口 Mesh 导入材质到正式 shader/material 路径
|
||||
|
||||
### 目标
|
||||
|
||||
彻底去掉 imported mesh material 的“无 shader / 裸属性名”旧路线。
|
||||
|
||||
### 任务
|
||||
|
||||
- 调整 `MeshLoader` 导入逻辑:
|
||||
- imported material 直接绑定正式 builtin shader
|
||||
- 默认按现有主线落到 builtin lit/forward 合同
|
||||
- 导入属性与纹理时,直接写正式 property name / texture slot:
|
||||
- 例如 `_BaseColor`
|
||||
- `_MainTex`
|
||||
- `_Cutoff`
|
||||
- 其他已正式声明的 builtin 属性
|
||||
- 不再向 imported material 写入仅靠 runtime 别名识别的裸字段:
|
||||
- `baseColor`
|
||||
- `baseColorTexture`
|
||||
- `color`
|
||||
- `texture`
|
||||
- 更新 mesh import 相关测试、render extractor 测试、相关 integration 资源测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
- mesh import 结果中的材质都带有正式 shader 引用
|
||||
- mesh import 结果中的属性/纹理绑定名称与 shader schema 对齐
|
||||
- 不再需要 runtime 靠旧别名才能让导入材质正常渲染
|
||||
|
||||
---
|
||||
|
||||
## Phase 3:移除 runtime builtin 材质语义别名与 fallback 常量路径
|
||||
|
||||
### 目标
|
||||
|
||||
让 built-in pipeline 只吃正式 schema 材质,不再继续兼容旧材质命名。
|
||||
|
||||
### 任务
|
||||
|
||||
- 清理 `RenderMaterialResolve.h` 中的旧别名解析表:
|
||||
- base color property alias
|
||||
- base texture alias
|
||||
- skybox texture alias
|
||||
- alpha cutoff alias
|
||||
- 保留并强化基于 `shader property semantic` 的正式解析路径
|
||||
- 移除 `FallbackPerMaterialConstants` 路线
|
||||
- 当材质未携带正式 schema constant layout 时:
|
||||
- 显式报错 / 记录诊断
|
||||
- 拒绝进入需要正式材质常量的绘制路径
|
||||
- 调整 forward / depth / shadow / skybox 相关单测
|
||||
|
||||
### 验收标准
|
||||
|
||||
- builtin pipeline 不再依赖属性别名表维持主链
|
||||
- builtin pipeline 不再手工构造 per-material fallback constant 继续绘制
|
||||
- runtime 只接受正式 shader/material 契约
|
||||
|
||||
---
|
||||
|
||||
## Phase 4:显式化 builtin pass resource binding contract
|
||||
|
||||
### 目标
|
||||
|
||||
让 builtin shader pass 的资源绑定契约完全存在于 shader 资产中,而不是藏在 runtime 硬编码里。
|
||||
|
||||
### 任务
|
||||
|
||||
- 为所有 builtin shader pass 补齐显式 `resources` 描述
|
||||
- 覆盖至少以下 shader:
|
||||
- `forward-lit.shader`
|
||||
- `depth-only.shader`
|
||||
- `shadow-caster.shader`
|
||||
- `object-id.shader`
|
||||
- `skybox.shader`
|
||||
- `final-color.shader`
|
||||
- 其他当前仍在主链中的 builtin shader
|
||||
- 清理 `TryBuildImplicitBuiltinPassResourceBindings`
|
||||
- 清理 `ShaderVariantUtils.h` 中围绕 implicit/legacy binding 的兼容逻辑:
|
||||
- legacy alias register rewrite
|
||||
- 依赖 `gXxx` 名称重写的分支
|
||||
- 调整 shader loader / rendering pipeline / builtin pass 单测
|
||||
|
||||
### 验收标准
|
||||
|
||||
- builtin shader pass 缺少显式资源绑定时,构建或运行应明确失败
|
||||
- runtime 不再替 shader 资产自动补 binding layout
|
||||
- HLSL runtime 编译不再依赖 legacy alias register 重写
|
||||
|
||||
---
|
||||
|
||||
## Phase 5:显式化 builtin pass metadata 与 pass 选择规则
|
||||
|
||||
### 目标
|
||||
|
||||
去掉“默认 ForwardLit”一类隐式 pass 归类规则。
|
||||
|
||||
### 任务
|
||||
|
||||
- 收紧 `BuiltinPassMetadataUtils`:
|
||||
- built-in pass 匹配必须依赖显式 pass name / tag
|
||||
- 删除“无 metadata 默认归 ForwardLit”的逻辑
|
||||
- 审查并统一 builtin shader 的 pass metadata:
|
||||
- `Name`
|
||||
- `LightMode`
|
||||
- 其它当前正式要求的 tag
|
||||
- 对进入 builtin 主线的 shader 建立硬约束:
|
||||
- 没有显式 builtin metadata 的 shader,不得继续被当作主几何 shader 使用
|
||||
- 更新 pass 匹配测试和 shader authoring 测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
- builtin pass 选择全部基于显式 metadata
|
||||
- 不存在 runtime 默认猜一个 pass 类型的行为
|
||||
|
||||
---
|
||||
|
||||
## Phase 6:清理旧 shader artifact schema 兼容
|
||||
|
||||
### 目标
|
||||
|
||||
让 shader artifact runtime loader 与 material artifact 一样,收口到 current schema。
|
||||
|
||||
### 任务
|
||||
|
||||
- 清理 `ShaderArtifactLoader.cpp` 中对旧 schema 的分支兼容:
|
||||
- `XCSHD01`
|
||||
- `XCSHD02`
|
||||
- `XCSHD03`
|
||||
- `XCSHD04`
|
||||
- 将旧 `Library` artifact 的处理方式改为:
|
||||
- 识别为过期
|
||||
- 触发重新导入 / 重新生成
|
||||
- 或直接报错要求重建 `Library`
|
||||
- 更新 asset database / shader load 相关测试
|
||||
- 明确记录此阶段会带来的影响:
|
||||
- 旧 `Library` 无法直接沿用
|
||||
- 需要一次性刷新或重建
|
||||
|
||||
### 验收标准
|
||||
|
||||
- shader artifact loader 只接受 current schema
|
||||
- 对旧 artifact 的处理边界清晰且可测试
|
||||
|
||||
---
|
||||
|
||||
## Phase 7:全量验证与阶段收口
|
||||
|
||||
### 目标
|
||||
|
||||
确认 Rendering 在去掉旧兼容层之后没有破坏现有功能。
|
||||
|
||||
### 任务
|
||||
|
||||
- 编译并运行:
|
||||
- `material_tests`
|
||||
- `rendering_unit_tests`
|
||||
- `asset_tests`
|
||||
- `editor_tests`
|
||||
- 受影响的 mesh/shader 资源测试
|
||||
- 重新编译 `XCEditor`
|
||||
- 重点回归:
|
||||
- scene viewport
|
||||
- game viewport
|
||||
- object-id picking
|
||||
- selection outline
|
||||
- skybox
|
||||
- 阴影
|
||||
- 多光源
|
||||
- backpack / sphere / quad 等 integration scene
|
||||
- 形成阶段收口报告
|
||||
|
||||
### 验收标准
|
||||
|
||||
- 所有直接相关测试通过
|
||||
- editor 编译通过
|
||||
- 关键 integration scene 渲染行为不回退
|
||||
- 能明确宣告 runtime 旧兼容路径已移除
|
||||
|
||||
---
|
||||
|
||||
## 7. 风险与注意事项
|
||||
|
||||
### 7.1 这是一次“切正式路径”的收口,不是小修小补
|
||||
|
||||
本计划一旦执行,就会主动删除一部分兼容逻辑。
|
||||
|
||||
因此不能以“尽量少改代码”为目标,而应以:
|
||||
|
||||
- 正式路径唯一
|
||||
- contract 清晰
|
||||
- 后续 SRP 可承接
|
||||
|
||||
为目标。
|
||||
|
||||
### 7.2 `Library` 重建属于预期影响
|
||||
|
||||
一旦收掉旧 shader artifact schema 兼容,旧 `Library` 里的 shader artifact 失效是正常现象。
|
||||
|
||||
这不应被视为回归,而应被视为阶段性收口的合理代价。
|
||||
|
||||
### 7.3 必须避免引入新的“临时兼容层”
|
||||
|
||||
执行过程中需要特别警惕以下错误做法:
|
||||
|
||||
- 新加一层 alias 表,试图“先兼容一下”
|
||||
- 把 runtime fallback 换个名字继续保留
|
||||
- 在 editor 或 import 层再次引入一套过渡数据模型
|
||||
|
||||
如果遇到结构性问题,正确做法是:
|
||||
|
||||
- 直接改到正式模型
|
||||
- 同步补测试
|
||||
|
||||
而不是再加一层短期兜底。
|
||||
|
||||
---
|
||||
|
||||
## 8. 建议执行顺序
|
||||
|
||||
建议严格按以下顺序推进:
|
||||
|
||||
1. `Phase 1` 测试基线整理
|
||||
2. `Phase 2` mesh 导入材质正式化
|
||||
3. `Phase 3` runtime 材质别名与 fallback 常量清理
|
||||
4. `Phase 4` builtin pass 显式资源绑定
|
||||
5. `Phase 5` builtin pass metadata 显式化
|
||||
6. `Phase 6` shader artifact schema 收口
|
||||
7. `Phase 7` 全量验证
|
||||
|
||||
原因是:
|
||||
|
||||
- 如果不先把 imported material 拉回正式路径
|
||||
- 后面的 runtime alias / fallback 清理就一定会打断现有资源链路
|
||||
|
||||
---
|
||||
|
||||
## 9. 本阶段完成后的预期状态
|
||||
|
||||
本计划完成后,Rendering 模块应达到以下状态:
|
||||
|
||||
1. built-in shader/material/pass contract 全部走正式显式路径
|
||||
2. runtime 不再依赖旧命名猜测材质语义
|
||||
3. runtime 不再替非正式材质拼接 fallback 常量布局
|
||||
4. builtin shader 资源绑定契约完全由 shader 资产声明
|
||||
5. builtin pass 类型选择完全依赖显式 metadata
|
||||
6. shader artifact runtime loader 不再背负旧 schema 包袱
|
||||
7. 整个 Rendering 模块更适合作为后续 Unity 风格 SRP 的底层承接
|
||||
|
||||
---
|
||||
|
||||
## 10. 一句话总结
|
||||
|
||||
当前 Rendering 真正需要的不是继续加功能,而是把残留的旧兼容路径彻底拔干净。
|
||||
|
||||
这一阶段的本质,是把:
|
||||
|
||||
- imported material
|
||||
- built-in shader binding
|
||||
- pass metadata
|
||||
- shader artifact
|
||||
|
||||
全部拉回到同一套正式 contract 上,为后续 Renderer / Material / Shader / SRP 的继续推进打地基。
|
||||
@@ -1,71 +0,0 @@
|
||||
# Renderer 阶段收口补充:Object ID Picking 正式化
|
||||
|
||||
日期:`2026-04-02`
|
||||
|
||||
## 1. 这次补充收口解决什么
|
||||
|
||||
本次补充只收一件事:
|
||||
|
||||
- `SceneView` 选中主链路正式切到 `GPU object-id`
|
||||
|
||||
本次明确不做:
|
||||
|
||||
- render graph
|
||||
- renderer 内更完整的多 pass 调度
|
||||
- game/runtime 通用 picking 服务
|
||||
|
||||
原因很简单:这些属于下一阶段架构演进,不应该继续污染当前阶段的收口边界。
|
||||
|
||||
## 2. 本次收口后的正式行为
|
||||
|
||||
当前 `SceneView` 选中行为统一定义为:
|
||||
|
||||
1. 场景渲染时生成 `object-id` 纹理
|
||||
2. 鼠标点击时读取对应像素
|
||||
3. 颜色解码为实体 ID
|
||||
4. `0` 视为“未选中任何对象”,但这仍然是一次成功的 GPU 采样
|
||||
|
||||
关键变化:
|
||||
|
||||
- editor 不再把 `CPU ray picking` 作为 `SceneView` 点击选中的静默回退主链路
|
||||
- `CPU ray picking` 继续保留为独立几何工具能力,不再承担当前正式选中流程
|
||||
- `object-id` 读取失败会被显式标记为 readback failure,而不是与“没有有效帧”混在一起
|
||||
|
||||
## 3. 为什么这才算收口
|
||||
|
||||
之前的问题不是没有 `object-id pass`,而是“主路径”和“兜底路径”的语义不够硬:
|
||||
|
||||
- 成功采样
|
||||
- 无有效 object-id 帧
|
||||
- GPU 读回失败
|
||||
|
||||
这三种状态以前没有被清晰区分。
|
||||
|
||||
现在已经收紧为显式结果类型:
|
||||
|
||||
- `Unavailable`
|
||||
- `Success`
|
||||
- `ReadbackFailed`
|
||||
|
||||
这意味着:
|
||||
|
||||
- renderer/editor 的 `object-id` 交互已经形成可测试契约
|
||||
- `0 id` 与“采样失败”不再混淆
|
||||
- 后续若要继续升级成异步 readback、共享 picking 服务,也有稳定边界可接
|
||||
|
||||
## 4. 当前阶段完成后的边界
|
||||
|
||||
到这里,当前阶段可以正式视为完成:
|
||||
|
||||
- editor viewport 宿主链路已打通
|
||||
- renderer 的 builtin post-process 已形成稳定接口
|
||||
- `SceneView` 选中正式以 GPU object-id 为主链路
|
||||
- 回归测试已覆盖 object-id 读回状态语义
|
||||
|
||||
下一阶段真正该做的是:
|
||||
|
||||
- renderer 内正式 render graph / pass graph
|
||||
- 更完整的 renderer-owned picking 服务
|
||||
- editor / runtime shared picking contract
|
||||
|
||||
而不是继续在这个阶段里反复修补 viewport host。
|
||||
@@ -1,164 +0,0 @@
|
||||
# Renderer阶段收口说明
|
||||
|
||||
## 1. 目标
|
||||
|
||||
本文用于正式收口当前 Renderer 阶段,明确:
|
||||
|
||||
- 本阶段已经完成什么
|
||||
- 哪些能力已经进入稳定边界
|
||||
- 哪些事项明确延期到下一阶段
|
||||
- 后续开发不应再继续把新功能塞回本阶段
|
||||
|
||||
当前收口日期:`2026-04-02`
|
||||
|
||||
---
|
||||
|
||||
## 2. 本阶段已完成能力
|
||||
|
||||
### 2.1 Renderer 主体边界
|
||||
|
||||
当前已经形成稳定分层:
|
||||
|
||||
- `RHI` 负责后端抽象与资源/命令执行
|
||||
- `Rendering` 负责场景提取、camera request、pipeline、builtin pass
|
||||
- `Editor` 负责 viewport 宿主、输入、overlay、编辑态请求装配
|
||||
|
||||
关键点:
|
||||
|
||||
- `CameraRenderer` 已经承担统一 camera 渲染执行职责
|
||||
- `SceneRenderer` 已经承担 scene -> camera request 的组织职责
|
||||
- editor scene viewport 不再自己拼装 renderer 执行逻辑
|
||||
|
||||
### 2.2 内建后处理边界
|
||||
|
||||
本阶段内建编辑态后处理已经收敛为 renderer 自己的通用请求能力:
|
||||
|
||||
- `BuiltinPostProcessRequest`
|
||||
- `BuiltinPostProcessPassPlan`
|
||||
- `BuiltinPostProcessPassSequenceBuilder`
|
||||
|
||||
这意味着:
|
||||
|
||||
- renderer 公共接口不再暴露 `SceneView` 专有命名
|
||||
- grid / selection outline / debug mask 已归入 renderer 侧 builtin post-process 能力
|
||||
- editor 只负责“是否启用、传什么数据、把哪些 render target 绑定进 request”
|
||||
|
||||
### 2.3 Editor Scene Viewport 接入
|
||||
|
||||
当前 editor scene viewport 已具备:
|
||||
|
||||
- renderer 离屏输出接入
|
||||
- object-id 帧输出接入
|
||||
- CPU picking 回退链路
|
||||
- selection outline
|
||||
- infinite grid
|
||||
- built-in post-process 请求装配
|
||||
|
||||
其中:
|
||||
|
||||
- grid 和 outline 仍然服务于 editor scene viewport
|
||||
- 但执行入口已经下沉到 renderer
|
||||
- editor 只保留宿主与编辑器语义
|
||||
|
||||
### 2.4 自动化测试体系
|
||||
|
||||
当前已经具备稳定回归闸门:
|
||||
|
||||
- `tests/Rendering/unit`
|
||||
- `tests/Rendering/integration`
|
||||
- `tests/Editor`
|
||||
- `rendering_phase_regression`
|
||||
|
||||
当前阶段收口依赖的关键验证包括:
|
||||
|
||||
- renderer unit tests
|
||||
- editor tests
|
||||
- 全 rendering integration 场景
|
||||
- `XCEditor` smoke launch
|
||||
|
||||
---
|
||||
|
||||
## 3. 本阶段稳定边界
|
||||
|
||||
以下内容从现在开始视为本阶段稳定边界:
|
||||
|
||||
1. renderer 公共请求以 `CameraRenderRequest` 为核心,而不是 editor 自定义执行入口。
|
||||
2. editor scene viewport 的内建后处理数据由 editor 组装,但 pass 执行由 renderer 负责。
|
||||
3. builtin post-process 的公共语义是 renderer 语义,不是 `SceneView` 语义。
|
||||
4. rendering regression 失败时,优先视为阶段回归,而不是“可接受的小问题”。
|
||||
|
||||
---
|
||||
|
||||
## 4. 本阶段明确延期项
|
||||
|
||||
以下事项明确不再继续塞入本阶段,转入下一阶段:
|
||||
|
||||
### 4.1 真正的多 pass / render graph 框架
|
||||
|
||||
当前已有 pass sequence 与 builtin post-process,但这还不是完整的 renderer 多 pass 架构。
|
||||
|
||||
延期内容:
|
||||
|
||||
- renderer 级 render graph
|
||||
- 更正式的 pass phase / event 模型
|
||||
- 更通用的资源读写依赖管理
|
||||
|
||||
### 4.2 GPU Object ID 正式方案
|
||||
|
||||
当前 editor selection 相关链路已经能工作,但还不是最终方案。
|
||||
|
||||
延期内容:
|
||||
|
||||
- renderer 内正式 object-id pass/attachment 规范化
|
||||
- editor picking 从 CPU fallback 继续向 GPU object-id 正式方案收敛
|
||||
- editor/game shared picking contract
|
||||
|
||||
### 4.3 Gizmo 最终渲染体系
|
||||
|
||||
当前 gizmo 与 scene viewport 已经能工作,但不属于本阶段 renderer 收口范围。
|
||||
|
||||
延期内容:
|
||||
|
||||
- 更成熟的 gizmo 渲染架构
|
||||
- 更统一的 gizmo draw pass / picking / overlay 体系
|
||||
- 与后续 renderer 多 pass 的正式对接
|
||||
|
||||
### 4.4 C# SRP 对接
|
||||
|
||||
当前 renderer 的职责边界已经为 SRP 预留好了方向,但本阶段不做真正脚本化 pipeline 落地。
|
||||
|
||||
延期内容:
|
||||
|
||||
- `RenderPipelineAsset` / `RenderPipeline` 脚本绑定
|
||||
- `ScriptableRenderContext`
|
||||
- `CommandBuffer`
|
||||
- renderer 与脚本侧的正式桥接层
|
||||
|
||||
---
|
||||
|
||||
## 5. 阶段退出标准
|
||||
|
||||
当前阶段只有在以下条件全部满足时才视为完成:
|
||||
|
||||
1. renderer/editor 边界中不再存在新的 `SceneView` 语义向 renderer 公共接口泄漏。
|
||||
2. scene viewport 的 builtin post-process 组合链路具备稳定自动化回归覆盖。
|
||||
3. `rendering_phase_regression` 保持通过。
|
||||
4. 新功能开发转入下一阶段,不再回头污染本阶段边界。
|
||||
|
||||
截至本文落地时,这些退出标准已经满足。
|
||||
|
||||
---
|
||||
|
||||
## 6. 下一阶段入口
|
||||
|
||||
Renderer 下一阶段应当正式转向:
|
||||
|
||||
- renderer 内更完整的多 pass / phase 模型
|
||||
- editor/game shared render feature 契约
|
||||
- object-id 正式化
|
||||
- 为后续 C# SRP 搭建真正可扩展的 renderer 接口
|
||||
|
||||
一句话总结:
|
||||
|
||||
- 当前阶段已经把“Renderer 从 RHI 之上独立出来,并接通 editor scene viewport”这件事做完
|
||||
- 下一阶段不该继续修补这一层,而应开始建设更正式的 renderer 扩展框架
|
||||
291
docs/plan/Scene选中描边彻底修复计划_2026-04-08.md
Normal file
291
docs/plan/Scene选中描边彻底修复计划_2026-04-08.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# Scene 选中描边彻底修复计划
|
||||
|
||||
日期:2026-04-08
|
||||
|
||||
## 1. 文档定位
|
||||
|
||||
本文档只解决一个问题:
|
||||
|
||||
- `Scene` 面板里,选中对象的描边效果需要达到稳定、可扩展、接近商业编辑器的质量。
|
||||
|
||||
本文档不讨论:
|
||||
|
||||
- 层级面板右键菜单
|
||||
- 灯光 gizmo 图标
|
||||
- 运行时高亮
|
||||
- 游戏内选中反馈
|
||||
|
||||
## 2. 当前问题结论
|
||||
|
||||
当前实现不是“可见外轮廓提取”,而是“基于 object-id 的屏幕空间膨胀”。
|
||||
|
||||
现状链路如下:
|
||||
|
||||
1. 主场景正常渲染。
|
||||
2. `BuiltinObjectIdPass` 把屏幕上最前面的物体 id 写入 `objectIdTexture`。
|
||||
3. `BuiltinObjectIdOutlinePass` 在全屏 shader 中检查当前像素周围是否存在“选中对象的 object-id”。
|
||||
4. 只要邻域中存在选中 object-id,当前像素就会被涂成 outline。
|
||||
|
||||
这个算法的根本缺陷是:
|
||||
|
||||
- 它只知道“邻域里有没有选中对象”。
|
||||
- 它不知道“当前像素前面是不是别的物体挡住了选中对象”。
|
||||
- 它也不知道“这条边是可见外轮廓,还是选中大平面与前景遮挡物之间的内部接触边界”。
|
||||
|
||||
所以当选中一个大平面时,平面上方的 cube、sphere、capsule 这些前景物体边缘也会被错误描边。
|
||||
|
||||
这不是数据污染,也不是拾取错误,而是算法层面的必然结果。
|
||||
|
||||
## 3. 根因
|
||||
|
||||
根因可以归纳为两条:
|
||||
|
||||
### 3.1 描边输入选错了
|
||||
|
||||
当前描边 pass 直接消费 `objectIdTexture`。
|
||||
|
||||
但 `objectIdTexture` 的职责本质上是:
|
||||
|
||||
- 告诉编辑器“当前屏幕像素最前面的对象是谁”,用于 picking。
|
||||
|
||||
它不适合作为“选中描边”的唯一依据,因为它不包含:
|
||||
|
||||
- 选中对象的可见 mask
|
||||
- 选中对象自己的深度
|
||||
- 当前场景深度与选中对象深度之间的遮挡关系
|
||||
|
||||
### 3.2 描边算法缺少深度裁决
|
||||
|
||||
当前 outline shader 的判断规则是:
|
||||
|
||||
- 当前像素不是选中对象
|
||||
- 但周围像素里存在选中对象
|
||||
|
||||
满足这两个条件就画 outline。
|
||||
|
||||
这套规则没有使用场景深度,也没有使用选中对象的可见性信息,因此无法区分:
|
||||
|
||||
- 真正应该画的外轮廓
|
||||
- 不应该画的前景遮挡边界
|
||||
|
||||
## 4. 修复目标
|
||||
|
||||
本次彻底修复后的目标是:
|
||||
|
||||
1. 选中大平面时,前景物体边缘不再被错误染成 outline。
|
||||
2. 选中普通 mesh 时,outline 仍然能稳定出现在可见外轮廓处。
|
||||
3. 被遮挡区域不画“伪外轮廓”。
|
||||
4. picking 与 selection outline 两套能力彻底解耦。
|
||||
5. 后续如果要做 Unity 风格的 x-ray 选中、被遮挡高亮、hover outline,可以在当前结构上继续扩展,而不是推倒重来。
|
||||
|
||||
## 5. 非目标
|
||||
|
||||
这次修复不做以下内容:
|
||||
|
||||
1. 不做运行时游戏内高亮系统。
|
||||
2. 不做完整的“透视遮挡时仍显示淡色轮廓”的 x-ray 选中风格。
|
||||
3. 不借这次机会重写整个 editor render pipeline。
|
||||
4. 不继续在现有 `object-id-outline.shader` 上做只针对单个 case 的临时补丁。
|
||||
|
||||
## 6. 总体方案
|
||||
|
||||
正确方案是把“拾取”和“描边”拆开,改成:
|
||||
|
||||
- `object id for picking`
|
||||
- `visible selection mask for outline`
|
||||
- `depth-aware outline composite`
|
||||
|
||||
### 6.1 picking 继续走 object-id
|
||||
|
||||
现有 `objectIdTexture` 继续只用于:
|
||||
|
||||
- 场景点击选中
|
||||
- object-id readback
|
||||
|
||||
它不再直接决定 outline 的视觉结果。
|
||||
|
||||
### 6.2 新增 selection mask pass
|
||||
|
||||
新增一张仅服务于 outline 的 `selectionMaskTexture`。
|
||||
|
||||
它的规则是:
|
||||
|
||||
1. 只渲染当前选中的对象。
|
||||
2. 渲染时绑定主场景已有的深度缓冲。
|
||||
3. 依赖深度测试,只把“真正可见的 selected fragment”写进 `selectionMaskTexture`。
|
||||
|
||||
这样得到的不是 picking id 图,而是“选中对象可见区域 mask”。
|
||||
|
||||
### 6.3 scene viewport 暴露深度 SRV
|
||||
|
||||
为了做真正的 depth-aware composite,`Scene` viewport 需要同时持有:
|
||||
|
||||
1. `depthView`
|
||||
2. `depthShaderView`
|
||||
|
||||
也就是说,Scene viewport 的深度纹理既要能作为 DSV 使用,也要能在全屏 composite shader 中作为 SRV 读取。
|
||||
|
||||
底层 RHI 已经具备这条能力,阴影缓存已有相同用法,因此这不是架构冒险,而是 viewport 资源层尚未接线。
|
||||
|
||||
### 6.4 重写 outline composite pass
|
||||
|
||||
新的 outline shader 不再直接从 `objectIdTexture` 做邻域膨胀,而是读取:
|
||||
|
||||
1. `selectionMaskTexture`
|
||||
2. `sceneDepthTexture`
|
||||
|
||||
必要时预留:
|
||||
|
||||
3. `selectionDepthTexture`
|
||||
|
||||
新的裁决规则应为:
|
||||
|
||||
1. 中心像素若属于 selected mask,本像素不直接着色。
|
||||
2. 若邻域存在 selected mask,则该像素可能位于 selected 的边界附近。
|
||||
3. 只有在深度关系证明“当前像素不是更近的前景遮挡物”时,才允许输出 outline。
|
||||
|
||||
这一步的本质是:
|
||||
|
||||
- 只画可见 selected 外轮廓。
|
||||
- 不把前景遮挡物边界误判为 selected outline。
|
||||
|
||||
## 7. 具体实施步骤
|
||||
|
||||
### Phase 1:补齐 viewport 资源
|
||||
|
||||
目标:
|
||||
|
||||
- Scene viewport 持有可采样深度。
|
||||
- Scene viewport 持有单独的 selection mask render target。
|
||||
|
||||
需要修改:
|
||||
|
||||
1. `editor/src/Viewport/ViewportHostRenderTargets.h`
|
||||
2. 相关 viewport resource reuse / destroy / create 流程
|
||||
|
||||
新增资源建议:
|
||||
|
||||
1. `depthShaderView`
|
||||
2. `selectionMaskTexture`
|
||||
3. `selectionMaskView`
|
||||
4. `selectionMaskShaderView`
|
||||
5. `selectionMaskState`
|
||||
|
||||
验收标准:
|
||||
|
||||
- Scene viewport 创建/销毁/复用逻辑能完整覆盖新资源。
|
||||
- 编译通过。
|
||||
- 不影响现有 Scene 视图展示。
|
||||
|
||||
### Phase 2:新增 selection mask pass
|
||||
|
||||
目标:
|
||||
|
||||
- 只把当前选中对象的可见区域写入 mask。
|
||||
|
||||
实现建议:
|
||||
|
||||
1. 新建 editor 专用 `SelectionMaskPass`。
|
||||
2. 输入为选中对象 id 列表。
|
||||
3. 输出到 `selectionMaskTexture`。
|
||||
4. 渲染时绑定主场景同一套深度。
|
||||
|
||||
实现关键点:
|
||||
|
||||
1. 这个 pass 的语义不是 picking。
|
||||
2. 它只关心“选中对象哪些像素当前可见”。
|
||||
3. 它不需要写 object id,只需要写统一 mask 值或选中组 id。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 选中对象时可以输出稳定的可见 mask。
|
||||
- 未选中时 pass 可被跳过。
|
||||
|
||||
### Phase 3:重写 outline composite
|
||||
|
||||
目标:
|
||||
|
||||
- outline 只出现在选中对象的可见外轮廓。
|
||||
|
||||
实现建议:
|
||||
|
||||
1. 保留全屏 pass 形式。
|
||||
2. 输入从 `objectIdTexture` 改为 `selectionMaskTexture + depthShaderView`。
|
||||
3. 邻域检查继续保留,但结果必须经过深度裁决。
|
||||
|
||||
最低正确规则:
|
||||
|
||||
1. 中心像素若是别的前景几何,且深度显著近于 selected 边界,不画 outline。
|
||||
2. 中心像素若是背景或更远表面,可以画 outline。
|
||||
|
||||
验收标准:
|
||||
|
||||
1. 选中大平面时,前景 cube/sphere/capsule 边缘不再被错误描边。
|
||||
2. 选中单个 mesh 时,外轮廓仍连续稳定。
|
||||
|
||||
### Phase 4:移除旧耦合
|
||||
|
||||
目标:
|
||||
|
||||
- 彻底消除 “outline 直接依赖 object-id picking 纹理” 这一旧逻辑。
|
||||
|
||||
需要完成:
|
||||
|
||||
1. 旧 `object-id-outline.shader` 逻辑下线或重命名。
|
||||
2. `SceneViewportSelectionOutlinePass` 的输入契约改为新资源。
|
||||
3. 保证 `objectIdTexture` 仅服务于 picking。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 代码层不再存在“outline 直接读 object id 邻域并膨胀”的核心路径。
|
||||
|
||||
## 8. 测试计划
|
||||
|
||||
至少补以下回归验证:
|
||||
|
||||
### 8.1 功能场景
|
||||
|
||||
1. 选中大平面,前面摆放 cube / sphere / capsule:
|
||||
- 前景物体边缘不能被错误描边。
|
||||
2. 选中单个 cube,背景是地面:
|
||||
- outline 能稳定显示在 cube 可见外轮廓上。
|
||||
3. 选中被部分遮挡的物体:
|
||||
- 只允许可见区域出现 outline。
|
||||
4. 多选相邻物体:
|
||||
- outline 不串边,不污染第三方前景物体。
|
||||
|
||||
### 8.2 回归类型
|
||||
|
||||
1. shader 资源加载测试
|
||||
2. render plan 接线测试
|
||||
3. pass 构建与资源存在性测试
|
||||
4. 视口级人工验证截图
|
||||
|
||||
## 9. 风险与边界
|
||||
|
||||
### 9.1 风险
|
||||
|
||||
1. 仅使用 `sceneDepth + selectionMask` 可能仍有少数边界 case 需要 `selectionDepth` 才能更稳。
|
||||
2. Scene viewport 新增 render target 后,资源管理和状态切换复杂度会上升。
|
||||
3. 若后续要兼容 Vulkan / OpenGL,需要同步确认深度 SRV 路径与资源状态语义。
|
||||
|
||||
### 9.2 边界
|
||||
|
||||
本计划的第一目标不是“做最炫的 outline”,而是:
|
||||
|
||||
- 先把语义做对
|
||||
- 再把视觉做稳
|
||||
|
||||
只要还在复用 picking 的 `objectIdTexture` 直接做 outline,问题就不可能彻底解决。
|
||||
|
||||
## 10. 最终结论
|
||||
|
||||
这次 bug 不能靠继续修补当前 `object-id-outline.shader` 解决。
|
||||
|
||||
彻底方案必须满足三点:
|
||||
|
||||
1. picking 与 outline 解耦
|
||||
2. 选中对象生成独立可见 mask
|
||||
3. outline composite 引入深度裁决
|
||||
|
||||
只有这样,Scene 视图选中描边才会从“屏幕空间膨胀特效”升级为“真正可用的编辑器轮廓系统”。
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
||||
# Unity式 Tick 系统与 Play Mode 运行时方案阶段进展
|
||||
|
||||
日期:2026-04-02
|
||||
|
||||
## 已完成
|
||||
|
||||
### 阶段 A
|
||||
|
||||
- 已接入 `RuntimeLoop`,统一承载 `FixedUpdate / Update / LateUpdate`
|
||||
- 已接入 `PlaySessionController`
|
||||
- 已实现 `Play / Stop`
|
||||
- Play 时运行 runtime scene clone
|
||||
- Stop 时恢复 editor scene snapshot
|
||||
- `Run` 菜单与 `F5` 已可切换 `Play / Stop`
|
||||
|
||||
### 阶段 B 当前收口
|
||||
|
||||
- 明确区分“文档级编辑”和“运行态场景对象编辑”
|
||||
- `New/Open/Save Scene` 与 `New/Open/Save Project` 仍只允许在 `Edit` 下执行
|
||||
- `Play / Paused` 下允许对 runtime scene 进行对象级编辑与 `Undo / Redo`
|
||||
- runtime scene 的对象改动默认不再污染场景文档 dirty 状态
|
||||
|
||||
### 阶段 C 当前收口
|
||||
|
||||
- 已补全 `Pause / Resume / Step` 的完整请求与状态切换
|
||||
- `Run` 菜单现在区分 `Play/Stop`、`Pause/Resume`、`Step`
|
||||
- `Error Pause` 已接入正式 Pause 请求通道
|
||||
- `Paused` 下维持 runtime world,不回退到 editor scene
|
||||
- `Step` 现在只在 `Paused` 下有效,并保持 `Paused` 状态不变
|
||||
|
||||
### 阶段 D 当前收口
|
||||
|
||||
- 已在软件顶部增加独立运行栏
|
||||
- 运行栏已接入 `Play / Pause / Step` 三个图标按钮
|
||||
- 顶部按钮直接复用现有 PlayMode 请求通道,不额外分叉状态机
|
||||
- `Play` 按钮在运行中会保持高亮,再次点击即 `Stop`
|
||||
- `Pause` 在 `Play / Paused` 下可用,并沿用现有 `F6`
|
||||
- `Step` 仍只在 `Paused` 下可用
|
||||
- 顶部栏已改为参与 dockspace 布局,不再覆盖 Scene / Hierarchy / Inspector 面板标题区
|
||||
|
||||
## 本轮验证
|
||||
|
||||
- 已重新执行 `cmake -S . -B build`
|
||||
- 已通过 `cmake --build build --config Debug --target scene_tests`
|
||||
- 已通过 `cmake --build build --config Debug --target editor_tests -- /m:1 /v:minimal`
|
||||
- 已通过 `cmake --build build --config Debug --target XCEditor -- /m:1 /v:minimal`
|
||||
- 已通过聚焦测试:
|
||||
`ctest --test-dir build -C Debug --output-on-failure -j1 -R "RuntimeLoopTest|PlaySessionControllerTest|EditorActionRoutingTest.*PlayMode|EditorActionRoutingTest.*MainMenuRouterRequestsPlayPauseResumeAndStepEvents"`
|
||||
|
||||
## 当前语义
|
||||
|
||||
- `editor tick` 负责托管运行时会话
|
||||
- `engine tick` 负责推进 runtime world
|
||||
- Play 时 `Hierarchy / Inspector / SceneView / GameView` 面对的是同一份 runtime world
|
||||
- Play 中对对象的改动默认是临时运行态改动,Stop 后回滚
|
||||
- Play 中禁止的是文档切换与文档保存,不是禁止观察或编辑 runtime clone
|
||||
|
||||
## 下一阶段建议
|
||||
|
||||
- 明确 `Paused` 下的 `Undo / Redo / Gizmo / Inspector` 更细粒度交互边界
|
||||
- 将 GameView 输入正式接入 runtime input 通道
|
||||
- 继续补 `Simulate` 与更完整的 Time 语义
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,631 +0,0 @@
|
||||
# Scene Viewport Overlay 与 Gizmo 正规化重构方案
|
||||
|
||||
日期:`2026-04-02`
|
||||
|
||||
## 0. 当前进度 Checkpoint
|
||||
|
||||
截至 `2026-04-02`,本方案已有以下落地结果:
|
||||
|
||||
- `Phase 1` 已完成:
|
||||
- `CameraRenderRequest` 已新增 `overlayPasses`
|
||||
- `CameraRenderer` 已在 builtin postprocess 之后执行 `overlayPasses`
|
||||
- `ViewportHostService` 已接入 editor overlay pass sequence
|
||||
- `Phase 2` 已完成首批迁移:
|
||||
- `camera frustum`
|
||||
- `directional light gizmo`
|
||||
- `camera/light scene icon`
|
||||
- 上述内容已不再走 ImGui world draw,而是走 renderer overlay pass
|
||||
- `scene icon` 的命中数据已开始收口:
|
||||
- `SceneViewPanel` 不再自己扫描 scene 构建 icon draw data
|
||||
- icon hit test 已改为消费 `SceneViewportOverlayBuilder::Build()` 产出的同类 frame data
|
||||
- `transform gizmo` 的统一命中已开始接线:
|
||||
- `SceneViewportEditorOverlayData.h` 已扩展为通用 `handleRecords`
|
||||
- `SceneViewportOverlayHandleBuilder.h` 已可把 move/rotate/scale gizmo draw data 转为 canonical handle records
|
||||
- `SceneViewPanel` 中 gizmo 的 hover / click-begin 已开始走统一 `HitTestSceneViewportOverlayHandles(...)`
|
||||
- `transform gizmo` 的绘制迁移已开始接线:
|
||||
- `SceneViewportEditorOverlayPass` 已支持 screen-space triangle primitive
|
||||
- `ViewportHostService` 已可在 host 侧根据 `SceneViewPanel` 提交的 overlay 与 gizmo handle build inputs 构建 transient transform overlay frame data
|
||||
- transform gizmo 的 handle build inputs 组装 helper 已开始从 `SceneViewPanel` 向 `SceneViewportOverlayHandleBuilder.h` 收口
|
||||
- transform gizmo 的 selection/context/refresh/cancel helper 已开始从 `SceneViewPanel` 向 `SceneViewportTransformGizmoFrameBuilder.h` 收口
|
||||
- move / rotate / scale gizmo 已不再直接依赖 `DrawSceneViewportOverlay()` 的 ImGui gizmo 绘制分支出图
|
||||
- `SceneViewportOverlayRenderer.cpp` 已收缩回 HUD/orientation 责任,不再承担 transform gizmo / scene icon / scene line 的 ImGui world draw
|
||||
- `SceneViewPanel` 内部交互前命中与交互后绘制的 gizmo 刷新链路已开始复用同一套 helper,重复的 context/update/submit 逻辑已明显收缩
|
||||
- interaction overlay frame 已改为 host 按传入的 transform gizmo inputs 现场组合,`SceneViewPanel` 不再为 hit test 预先写入 transient overlay 缓存
|
||||
- render 阶段使用的 transient transform gizmo frame data 也已改为 host 基于缓存的原始 overlay + inputs 现场构建
|
||||
|
||||
当前仍未完成的关键点:
|
||||
|
||||
- `transform gizmo` 的 drag solver / 变换求解仍然保留在各自 gizmo 类中
|
||||
- `SceneViewPanel` 里仍保留 transform gizmo 的 draw data 生成、交互仲裁与 transient overlay 提交逻辑
|
||||
- `SceneViewPanel` 仍直接控制 transform gizmo 的最终绘制 overlay 提交时机,host 尚未完全接管这一层 frame orchestration
|
||||
- `ViewportHostService` 的 canonical overlay frame data 仍未直接承载 transform gizmo,尚未收敛到单帧单份 canonical overlay 数据
|
||||
|
||||
当前阶段结论:
|
||||
|
||||
**方案方向已经验证正确,下一步不应该回头继续扩写 ImGui world overlay,而应该继续推进 canonical overlay data 与统一命中系统。**
|
||||
|
||||
## 1. 方案结论
|
||||
|
||||
当前 `Scene Viewport` 的问题,不是某一个 `Directional Light Gizmo` 画丑了,而是整条 editor overlay 链路本身没有收口:
|
||||
|
||||
- `grid` 已经是正规 renderer pass
|
||||
- `transform gizmo / camera icon / light icon / camera frustum / directional light gizmo` 仍然是 ImGui overlay
|
||||
- 输入命中和绘制几何不是同一份数据
|
||||
- `SceneViewPanel.cpp` 同时承担 panel UI、输入调度、世界转屏幕、overlay 构建、命中仲裁,职责已经失控
|
||||
|
||||
结论只有一个:
|
||||
|
||||
**不能再继续往 `SceneViewPanel.cpp` 和 ImGui world overlay 上堆功能。必须把场景中的 editor 可视化正式收口成一套 renderer 级 overlay pass 和统一 handle 数据。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前链路梳理
|
||||
|
||||
### 2.1 正规链路:Grid
|
||||
|
||||
当前 `grid` 的路径是正规的 renderer pass:
|
||||
|
||||
`ViewportHostService -> SceneRenderer -> CameraRenderer -> BuiltinPostProcessPassSequenceBuilder -> BuiltinInfiniteGridPass`
|
||||
|
||||
关键文件:
|
||||
|
||||
- `editor/src/Viewport/ViewportHostService.h`
|
||||
- `editor/src/Viewport/ViewportHostRenderFlowUtils.h`
|
||||
- `engine/src/Rendering/CameraRenderer.cpp`
|
||||
- `engine/src/Rendering/Passes/BuiltinPostProcessPassSequenceBuilder.cpp`
|
||||
- `engine/src/Rendering/Passes/BuiltinInfiniteGridPass.cpp`
|
||||
|
||||
这条链路的特征是:
|
||||
|
||||
- 在 scene 几何渲染完成后,由 GPU pass 正式叠加
|
||||
- 有明确的 render request 输入
|
||||
- 有独立 pass 边界
|
||||
- 不依赖 ImGui draw list
|
||||
|
||||
### 2.2 非正规链路:Editor World Overlay
|
||||
|
||||
当前绝大多数 editor 可视化不是 renderer pass,而是:
|
||||
|
||||
1. `ViewportHostService` 渲出 scene viewport 纹理
|
||||
2. `RenderViewportPanelContent()` 在 ImGui 面板中显示这张纹理
|
||||
3. `SceneViewPanel.cpp` 在这张纹理之上继续用 ImGui draw list 手搓 world overlay
|
||||
|
||||
关键文件:
|
||||
|
||||
- `editor/src/panels/ViewportPanelContent.h`
|
||||
- `editor/src/panels/SceneViewPanel.cpp`
|
||||
- `editor/src/Viewport/SceneViewportOverlayRenderer.cpp`
|
||||
|
||||
当前放在这条链上的内容包括:
|
||||
|
||||
- move gizmo
|
||||
- rotate gizmo
|
||||
- scale gizmo
|
||||
- camera icon / light icon
|
||||
- camera frustum
|
||||
- directional light gizmo
|
||||
- orientation gizmo
|
||||
|
||||
其中 `orientation gizmo` 本质上是固定在右上角的 HUD,放在 ImGui 问题不大。真正失控的是那些锚定在世界空间中的 overlay。
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前架构的核心问题
|
||||
|
||||
### 3.1 绘制职责放错层
|
||||
|
||||
`SceneViewPanel.cpp` 不应该知道:
|
||||
|
||||
- camera frustum 怎么构造几何
|
||||
- directional light gizmo 怎么构造几何
|
||||
- icon 如何转屏幕矩形
|
||||
- 各种 gizmo 如何排序、如何遮挡
|
||||
|
||||
这些本质上都属于 viewport overlay 系统,不属于 panel UI。
|
||||
|
||||
### 3.2 命中和绘制分裂
|
||||
|
||||
当前很多交互是:
|
||||
|
||||
- 一套代码负责画
|
||||
- 另一套代码负责 hover / click / drag
|
||||
|
||||
这会导致:
|
||||
|
||||
- 看见的和能点的不是同一个东西
|
||||
- 改样式时经常忘改命中
|
||||
- 优先级只能靠条件链硬拼
|
||||
|
||||
### 3.3 世界空间对象被当成 2D UI 处理
|
||||
|
||||
camera/light icon、frustum、light gizmo、transform gizmo 本质上都是世界空间 editor overlay。
|
||||
|
||||
但它们现在被塞进 ImGui draw list 后,就天然失去:
|
||||
|
||||
- 正规的渲染顺序语义
|
||||
- 稳定的深度/遮挡策略
|
||||
- 统一的 primitive 渲染方式
|
||||
- GPU 级别的扩展能力
|
||||
|
||||
### 3.4 `SceneViewPanel.cpp` 已经过胖
|
||||
|
||||
当前它同时负责:
|
||||
|
||||
- tools/top bar UI
|
||||
- tool mode 切换
|
||||
- gizmo context 组装
|
||||
- gizmo hover/click 仲裁
|
||||
- icon hit test
|
||||
- overlay 世界几何构建
|
||||
- scene picking
|
||||
- scene camera 输入
|
||||
|
||||
这已经不再是“面板”,而是一个巨型调度器加半个渲染系统。
|
||||
|
||||
### 3.5 视觉风格无法稳定收敛
|
||||
|
||||
Directional Light 这次暴露得最明显:
|
||||
|
||||
- 需求是“圆形底盘上的光线分布”
|
||||
- 当前实现却是在 panel 里临时拼几根线
|
||||
|
||||
这不是调几个参数能根治的问题,而是底层 primitive 表达和系统边界不对。
|
||||
|
||||
---
|
||||
|
||||
## 4. 重构目标
|
||||
|
||||
这次重构的目标不是“顺手把几个 gizmo 再修漂亮一点”,而是把 Scene Viewport overlay 彻底正规化。
|
||||
|
||||
最终目标如下:
|
||||
|
||||
### 4.1 分离两类 overlay
|
||||
|
||||
#### A. HUD 类 overlay
|
||||
|
||||
固定在面板坐标系的内容继续留在 ImGui:
|
||||
|
||||
- 顶部工具栏
|
||||
- 左侧 tools 按钮
|
||||
- 右上角 orientation gizmo
|
||||
- 状态提示文字
|
||||
|
||||
#### B. World Anchored Overlay
|
||||
|
||||
锚定在世界空间里的 editor 可视化统一进入 renderer overlay pass:
|
||||
|
||||
- move / rotate / scale gizmo
|
||||
- camera / light scene icon
|
||||
- camera frustum
|
||||
- directional light gizmo
|
||||
- 后续 collider bounds / helper shapes / volume gizmo
|
||||
|
||||
### 4.2 统一绘制数据与命中数据
|
||||
|
||||
所有可交互 gizmo handle 必须来自同一份 canonical data:
|
||||
|
||||
- 画什么
|
||||
- 颜色是什么
|
||||
- 层级优先级是什么
|
||||
- handle id 是什么
|
||||
- 哪里可以点
|
||||
|
||||
都不能再分散在不同类里各算一套。
|
||||
|
||||
### 4.3 建立 renderer 级 editor overlay pass
|
||||
|
||||
目标顺序应为:
|
||||
|
||||
`Scene Geometry -> ObjectId -> Builtin Post Process(Grid/Outline) -> Editor Overlay Pass -> ImGui HUD`
|
||||
|
||||
也就是说,editor 世界 overlay 必须成为正式 render stage,而不是纹理上的二次手绘。
|
||||
|
||||
### 4.4 让 gizmo 类回归“控制器/求解器”角色
|
||||
|
||||
`Move / Rotate / Scale Gizmo` 类应主要负责:
|
||||
|
||||
- drag 状态机
|
||||
- 轴向约束
|
||||
- plane 约束
|
||||
- 变换求解
|
||||
- 交互反馈求解
|
||||
|
||||
不再继续兼任:
|
||||
|
||||
- 实际几何绘制器
|
||||
- 实际命中主仲裁器
|
||||
|
||||
---
|
||||
|
||||
## 5. 推荐的目标架构
|
||||
|
||||
## 5.1 Render Request 层新增 `overlayPasses`
|
||||
|
||||
当前 `CameraRenderRequest` 里有:
|
||||
|
||||
- `preScenePasses`
|
||||
- `postScenePasses`
|
||||
- `builtinPostProcess`
|
||||
|
||||
但没有真正适合 editor world overlay 的最后一层。
|
||||
|
||||
建议新增:
|
||||
|
||||
- `overlayPasses`
|
||||
|
||||
执行顺序调整为:
|
||||
|
||||
1. `preScenePasses`
|
||||
2. scene geometry
|
||||
3. object id
|
||||
4. `postScenePasses`
|
||||
5. `builtinPostProcess`
|
||||
6. `overlayPasses`
|
||||
|
||||
这样 world overlay 才能稳定压在 grid 和 outline 之上,再由 ImGui 负责最后的 HUD。
|
||||
|
||||
### 5.2 新建 `SceneViewportOverlayFrameData`
|
||||
|
||||
建议新增一个独立的 frame data 结构,承载这一帧 Scene Viewport 的 editor overlay 数据。
|
||||
|
||||
建议字段:
|
||||
|
||||
- `linePrimitives`
|
||||
- `trianglePrimitives`
|
||||
- `billboardSprites`
|
||||
- `handleRecords`
|
||||
- `renderLayer`
|
||||
- `depthMode`
|
||||
- `screenSpaceThickness`
|
||||
|
||||
其中:
|
||||
|
||||
- primitive 用于绘制
|
||||
- handle record 用于命中
|
||||
- 两者共享相同的 `handleId`
|
||||
|
||||
### 5.3 新建 `SceneViewportOverlayBuilder`
|
||||
|
||||
职责:
|
||||
|
||||
- 接收 scene overlay context
|
||||
- 收集 selected objects
|
||||
- 构建 camera frustum
|
||||
- 构建 directional light 圆形底盘 gizmo
|
||||
- 构建 scene icons
|
||||
- 构建 transform gizmo handles
|
||||
- 输出统一的 `SceneViewportOverlayFrameData`
|
||||
|
||||
建议位置:
|
||||
|
||||
- `editor/src/Viewport/SceneViewportOverlayBuilder.h`
|
||||
- `editor/src/Viewport/SceneViewportOverlayBuilder.cpp`
|
||||
|
||||
### 5.4 新建 `SceneViewportOverlayHitTester`
|
||||
|
||||
职责:
|
||||
|
||||
- 基于 `SceneViewportOverlayFrameData::handleRecords` 做统一命中
|
||||
- 输出唯一的 hovered handle
|
||||
- 同一份数据同时服务 hover / click / drag begin
|
||||
|
||||
建议位置:
|
||||
|
||||
- `editor/src/Viewport/SceneViewportOverlayHitTester.h`
|
||||
- `editor/src/Viewport/SceneViewportOverlayHitTester.cpp`
|
||||
|
||||
### 5.5 新建 `SceneViewportEditorOverlayPass`
|
||||
|
||||
职责:
|
||||
|
||||
- 读取 `SceneViewportOverlayFrameData`
|
||||
- 用 GPU 绘制 line / fill / billboard
|
||||
- 负责世界空间 editor overlay 的正式渲染
|
||||
|
||||
建议位置:
|
||||
|
||||
- `editor/src/Viewport/Passes/SceneViewportEditorOverlayPass.h`
|
||||
- `editor/src/Viewport/Passes/SceneViewportEditorOverlayPass.cpp`
|
||||
|
||||
### 5.6 `SceneViewPanel` 的目标职责
|
||||
|
||||
重构后 `SceneViewPanel` 只负责:
|
||||
|
||||
- 顶部栏和 tools UI
|
||||
- tool mode / pivot / local-global 状态
|
||||
- 输入汇总
|
||||
- 与 viewport host service 交互
|
||||
- 显示固定 HUD
|
||||
|
||||
不再负责:
|
||||
|
||||
- world overlay 几何构建
|
||||
- icon 转屏幕
|
||||
- frustum / light gizmo 线框拼装
|
||||
- world overlay 主渲染
|
||||
|
||||
---
|
||||
|
||||
## 6. 模块职责重新划分
|
||||
|
||||
### 6.1 `SceneViewPanel`
|
||||
|
||||
保留:
|
||||
|
||||
- 面板 UI
|
||||
- 工具模式切换
|
||||
- 快捷键
|
||||
- 鼠标/键盘输入汇总
|
||||
- orientation gizmo
|
||||
|
||||
移出:
|
||||
|
||||
- world overlay primitive 构建
|
||||
- scene icon world/screen 几何生成
|
||||
- camera/light 线框绘制逻辑
|
||||
|
||||
### 6.2 `ViewportHostService`
|
||||
|
||||
新增职责:
|
||||
|
||||
- 组装 `SceneViewportOverlayFrameData`
|
||||
- 把 overlay pass 接到 render request
|
||||
|
||||
保留职责:
|
||||
|
||||
- scene view camera
|
||||
- viewport render target 管理
|
||||
- scene render request 提交
|
||||
- object id picking
|
||||
|
||||
### 6.3 Gizmo 求解器
|
||||
|
||||
`SceneViewportMoveGizmo / RotateGizmo / ScaleGizmo` 保留:
|
||||
|
||||
- 拖拽求解
|
||||
- 激活态
|
||||
- 交互反馈数据
|
||||
|
||||
逐步移除:
|
||||
|
||||
- 直接操作 ImGui draw data 的职责
|
||||
- 各自内部封闭的 hit test 决策权
|
||||
|
||||
### 6.4 Overlay Pass
|
||||
|
||||
只负责 GPU 绘制,不处理业务判断。
|
||||
|
||||
输入必须已经是“可直接画”的 primitive 数据,避免 pass 内部再知道 camera/light/gizmo 业务语义。
|
||||
|
||||
---
|
||||
|
||||
## 7. 推荐执行阶段
|
||||
|
||||
这次重构不能一次性大爆炸改完,但必须每一步都朝最终架构收敛,不能做过渡性屎层。
|
||||
|
||||
### Phase 0:冻结错误扩展方向
|
||||
|
||||
目标:
|
||||
|
||||
- 停止继续向 `SceneViewPanel.cpp` 添加新的 world overlay 绘制逻辑
|
||||
- 停止继续扩写 `SceneViewportOverlayRenderer.cpp` 为 ImGui world renderer
|
||||
|
||||
产出:
|
||||
|
||||
- 文档确认
|
||||
- 后续新增 world gizmo 一律走 overlay builder / pass 方向
|
||||
|
||||
### Phase 1:打通 `overlayPasses` 通道
|
||||
|
||||
目标:
|
||||
|
||||
- 给 `CameraRenderRequest` 新增 `overlayPasses`
|
||||
- 在 `CameraRenderer` 中调整执行顺序
|
||||
- 在 `ViewportHostService` 中接入 editor overlay pass sequence
|
||||
|
||||
产出:
|
||||
|
||||
- renderer 支持 scene 之后再画 editor overlay
|
||||
- 这一步允许先画空 pass,不做功能迁移
|
||||
|
||||
验收:
|
||||
|
||||
- 不影响现有 scene / grid / selection outline
|
||||
- `overlayPasses` 具备独立初始化、执行、释放边界
|
||||
|
||||
### Phase 2:迁移纯显示型 world overlay
|
||||
|
||||
优先迁移:
|
||||
|
||||
- camera frustum
|
||||
- camera/light scene icon
|
||||
- directional light gizmo
|
||||
|
||||
原因:
|
||||
|
||||
- 这些内容交互复杂度低
|
||||
- 最容易先把 `SceneViewPanel.cpp` 中世界几何拼装代码减掉
|
||||
|
||||
Directional Light 本阶段的目标形态:
|
||||
|
||||
- 圆形底盘
|
||||
- 光线分布在圆环或圆盘采样点上
|
||||
- 明确的世界朝向
|
||||
- 稳定屏幕线宽
|
||||
|
||||
验收:
|
||||
|
||||
- 这些 overlay 不再由 ImGui draw list 直接绘制
|
||||
- `SceneViewPanel.cpp` 不再负责生成对应线框
|
||||
|
||||
### Phase 3:迁移 Transform Gizmo 的绘制
|
||||
|
||||
目标:
|
||||
|
||||
- move / rotate / scale gizmo 的显示改为 overlay pass primitive
|
||||
- gizmo 类转为 handle builder + drag solver
|
||||
|
||||
当前进度:
|
||||
|
||||
- transform gizmo 已开始转成 transient overlay frame data
|
||||
- overlay pass 已可消费一批 screen-space triangle primitive 来绘制 gizmo 屏幕几何
|
||||
- 但 transient gizmo overlay 仍然由 `SceneViewPanel` 在帧末提交,尚未完全收口到 host / builder 的 canonical 路径
|
||||
|
||||
说明:
|
||||
|
||||
- 这一阶段只迁移“怎么画”
|
||||
- 可以暂时保留现有 drag 求解逻辑
|
||||
|
||||
验收:
|
||||
|
||||
- transform gizmo 已不依赖 ImGui world draw
|
||||
- gizmo 视觉反馈仍然可用
|
||||
|
||||
### Phase 4:统一命中系统
|
||||
|
||||
目标:
|
||||
|
||||
- 所有 world overlay 的 hover / click / drag begin 统一使用 `handleRecords`
|
||||
- 彻底删除 panel 里的多套 hit test 拼接逻辑
|
||||
|
||||
当前进度:
|
||||
|
||||
- `scene icon` 已完成
|
||||
- `transform gizmo` 的 hover / click-begin 已经开始接入统一 `handleRecords`
|
||||
- `drag begin` 之后的求解与 active 状态仍然保留在 gizmo 类与 `SceneViewPanel` 现有链路中
|
||||
|
||||
涵盖对象:
|
||||
|
||||
- transform gizmo
|
||||
- scene icon
|
||||
- 后续 camera/light/world helper handles
|
||||
|
||||
验收:
|
||||
|
||||
- 命中结果只由一份 canonical handle 数据决定
|
||||
- 不再依赖大量互相屏蔽的布尔条件
|
||||
|
||||
### Phase 5:删除旧世界 overlay 路径
|
||||
|
||||
目标:
|
||||
|
||||
- 删除 `SceneViewPanel.cpp` 中遗留的 world overlay 构建逻辑
|
||||
- 缩减 `SceneViewportOverlayRenderer.cpp` 到只保留 HUD 类渲染或直接拆分
|
||||
|
||||
最终保留:
|
||||
|
||||
- ImGui HUD
|
||||
- renderer world overlay
|
||||
|
||||
最终移除:
|
||||
|
||||
- ImGui world overlay
|
||||
|
||||
---
|
||||
|
||||
## 8. 第一阶段建议改动范围
|
||||
|
||||
如果按最小风险起步,第一轮只做下面这些:
|
||||
|
||||
- `engine/include/XCEngine/Rendering/CameraRenderRequest.h`
|
||||
- `engine/src/Rendering/CameraRenderer.cpp`
|
||||
- `editor/src/Viewport/ViewportHostService.h`
|
||||
- `editor/src/Viewport/ViewportHostRenderFlowUtils.h`
|
||||
- 新增 `editor/src/Viewport/Passes/SceneViewportEditorOverlayPass.*`
|
||||
|
||||
第一轮不碰:
|
||||
|
||||
- move/rotate/scale 的求解逻辑
|
||||
- Scene icon 的交互优先级逻辑
|
||||
- SceneViewPanel 的大面积交互重写
|
||||
|
||||
这样可以先把 renderer 通道立住,再分批迁移业务内容。
|
||||
|
||||
---
|
||||
|
||||
## 9. 方案边界
|
||||
|
||||
这次重构明确不做的内容:
|
||||
|
||||
- 不顺手做 local mode 新行为
|
||||
- 不顺手做 runtime 通用 debug draw 系统
|
||||
- 不顺手把所有 editor widget 一次性改成 pass
|
||||
- 不在本轮引入 render graph
|
||||
|
||||
这次只做一件事:
|
||||
|
||||
**把 Scene Viewport 中锚定世界空间的 editor overlay,从 ImGui 手搓模式收口为正式 renderer overlay 系统。**
|
||||
|
||||
---
|
||||
|
||||
## 10. 风险与控制
|
||||
|
||||
### 10.1 风险:一次性迁移过大
|
||||
|
||||
控制方式:
|
||||
|
||||
- 先打通 pass 通道
|
||||
- 再迁静态 overlay
|
||||
- 最后迁 transform gizmo 与命中
|
||||
|
||||
### 10.2 风险:命中系统重写影响交互稳定性
|
||||
|
||||
控制方式:
|
||||
|
||||
- handle record 和旧逻辑短期并存
|
||||
- 先让新系统服务 hover
|
||||
- 再切 click / drag begin
|
||||
|
||||
### 10.3 风险:D3D12-only pass 与后端扩展
|
||||
|
||||
控制方式:
|
||||
|
||||
- 第一阶段允许 editor overlay pass 先只支持 D3D12
|
||||
- 但数据结构和 pass 边界必须 backend-neutral
|
||||
|
||||
### 10.4 风险:继续出现“画的是一个,点的是另一个”
|
||||
|
||||
控制方式:
|
||||
|
||||
- 明确规定 overlay primitive 和 hit proxy 必须同源
|
||||
- 不能再允许绘制与命中分别各算一套几何
|
||||
|
||||
---
|
||||
|
||||
## 11. 最终验收标准
|
||||
|
||||
当以下条件全部满足时,说明收口完成:
|
||||
|
||||
- `SceneViewPanel.cpp` 不再承担 world overlay 几何构建职责
|
||||
- world anchored editor overlay 全部进入 renderer pass
|
||||
- transform gizmo / scene icon / camera frustum / light gizmo 使用统一 overlay frame data
|
||||
- hover / click / drag begin 使用统一 handle record
|
||||
- ImGui 只负责 HUD,不再负责世界空间 gizmo 主绘制
|
||||
- 新增一种 world gizmo 时,不需要再把世界转屏幕逻辑写回 panel
|
||||
|
||||
---
|
||||
|
||||
## 12. 对本次 Directional Light Gizmo 的直接指导
|
||||
|
||||
在该重构方案下,Directional Light Gizmo 的正确实现方式应是:
|
||||
|
||||
- 它属于 `World Anchored Overlay`
|
||||
- 它的几何由 `SceneViewportOverlayBuilder` 负责生成
|
||||
- 它的线段/圆环由 `SceneViewportEditorOverlayPass` 负责绘制
|
||||
- 它的样式使用“圆形底盘 + 圆周分布光线”,不再在 panel 中临时拼矩形列线
|
||||
|
||||
也就是说,Directional Light Gizmo 不应该被当成一个局部修补任务继续留在 `SceneViewPanel.cpp`。
|
||||
|
||||
---
|
||||
|
||||
## 13. 本文后的执行原则
|
||||
|
||||
在本方案审核通过之前:
|
||||
|
||||
- 允许修 bug
|
||||
- 不建议继续扩写新的 ImGui world overlay 逻辑
|
||||
|
||||
在本方案审核通过之后:
|
||||
|
||||
- 新增 world overlay 功能,优先接入 overlay builder / overlay pass
|
||||
- `SceneViewPanel.cpp` 只减不增
|
||||
@@ -1,506 +0,0 @@
|
||||
# Shader 与 Material 系统下一阶段计划
|
||||
|
||||
日期:`2026-04-03`
|
||||
|
||||
## 1. 阶段结论
|
||||
|
||||
当前 Renderer 这一轮主线已经完成收口,但完成的是“基础运行时闭环”,不是“最终的 Unity 风格 shader/material 体系”。
|
||||
|
||||
已经完成并应视为当前基线的内容:
|
||||
|
||||
- `Shader` 运行时契约已具备 `properties / passes / resources / backend variants`
|
||||
- builtin shader 资源已经外置,运行时不再依赖 C++ 内嵌 fallback
|
||||
- `Material` 对 builtin forward 的基础属性解析已经优先走 shader semantic
|
||||
- `BuiltinForwardPipeline` 已按 shader pass `resources` 合约生成 pipeline layout 与 descriptor binding
|
||||
- `Shader` 已接入 `AssetDatabase / Library` 流程,shader import 会生成 `main.xcshader`
|
||||
- `ShaderLoader` 已支持加载 `.xcshader` artifact
|
||||
- shader manifest 依赖与 `Material -> Shader` 依赖已进入资产追踪链
|
||||
- `rendering_unit_tests`
|
||||
- `rendering_integration_textured_quad_scene`
|
||||
- `rendering_integration_backpack_lit_scene`
|
||||
- `shader_tests`
|
||||
- `material_tests`
|
||||
|
||||
三后端验证当前是稳定的:
|
||||
|
||||
- D3D12:通过
|
||||
- OpenGL:通过
|
||||
- Vulkan:通过
|
||||
|
||||
但这仍然只是“Shader / Material Runtime 的第一层骨架”。
|
||||
|
||||
当前真正还没有完成的是:
|
||||
|
||||
- Unity 风格的 shader authoring 入口
|
||||
- 正式的 material schema / instance contract
|
||||
- 从 shader property layout 到 GPU material layout 的通用映射
|
||||
- 从 builtin forward 扩展到更通用 pass 的正式执行模型
|
||||
|
||||
## 2. 旧计划归档说明
|
||||
|
||||
以下两份计划文档已经完成历史使命,应归入 `docs/used/`:
|
||||
|
||||
- `Renderer模块设计与实现.md`
|
||||
- `Renderer下一阶段_ShaderMaterial与Pass体系设计.md`
|
||||
|
||||
原因不是它们“写错了”,而是:
|
||||
|
||||
- 第一份解决的是 Renderer 模块从无到有的问题,当前骨架已经落地
|
||||
- 第二份解决的是“为什么下一阶段先做 shader/material/pass contract,而不是 render graph”的阶段判断;这份判断已经部分兑现,整体已过期
|
||||
|
||||
本文件接手它们之后的主线,只保留对当前 checkout 仍然有效的目标。
|
||||
|
||||
## 3. 当前真实问题
|
||||
|
||||
### 3.1 Shader 运行时有了,但 authoring 还没有正式化
|
||||
|
||||
现在的运行时已经能消费:
|
||||
|
||||
- shader property
|
||||
- pass tag
|
||||
- pass resource binding
|
||||
- backend variant
|
||||
|
||||
但作者侧仍然不是最终形态。
|
||||
|
||||
当前还缺:
|
||||
|
||||
- 面向用户的 Unity 风格 `.shader` 语法入口
|
||||
- import 阶段把 authoring 语法转换为 runtime contract 的正式流程
|
||||
- shader 资产与 Library artifact 的稳定产物边界
|
||||
|
||||
### 3.2 Material 仍偏“资源容器”,还不是正式材质实例系统
|
||||
|
||||
当前 `Material` 已有:
|
||||
|
||||
- shader 引用
|
||||
- render state
|
||||
- property / texture 覆盖
|
||||
- tag / queue
|
||||
|
||||
但它还缺少真正用于 Renderer 执行的正式约束:
|
||||
|
||||
- 基于 shader property schema 的类型验证
|
||||
- property 默认值与 override 的统一解析
|
||||
- per-pass material constant layout
|
||||
- texture / sampler / buffer 到 pass resource 的正式映射
|
||||
- renderer 侧可缓存、可失效、可复用的 material binding plan
|
||||
|
||||
### 3.3 现有 pass contract 仍偏 builtin-forward 视角
|
||||
|
||||
当前 forward 主链已经能跑,但完整的 pass contract 还没有正式化为可扩展系统。
|
||||
|
||||
后续至少要能稳定承接:
|
||||
|
||||
- `ForwardLit`
|
||||
- `Unlit`
|
||||
- `DepthOnly`
|
||||
- `ShadowCaster`
|
||||
- `ObjectId`
|
||||
|
||||
否则后面一旦开始做阴影、深度预通道、更多 editor/runtime helper pass,就会重新退化回 pipeline 内部的条件分支拼装。
|
||||
|
||||
### 3.4 三后端问题的本质不是“语法不同”,而是“资产如何统一”
|
||||
|
||||
当前真正要解决的,不是简单回答“到底用 GLSL 还是 HLSL”,而是明确三层边界:
|
||||
|
||||
1. 对外 authoring 语法是什么
|
||||
2. import 后的内部运行时资产是什么
|
||||
3. 每个 backend 最终执行的 variant 是什么
|
||||
|
||||
这三层不分开,后面一定会把 authoring、runtime、backend 编译链搅在一起。
|
||||
|
||||
## 4. 下一阶段的目标
|
||||
|
||||
下一阶段只做一件事:把 `Shader` 和 `Material` 从“能支撑当前 builtin forward 的运行时拼装”升级为“能长期承接 Unity 风格渲染架构的正式系统”。
|
||||
|
||||
### 4.1 对外 authoring 语法目标:严格向 Unity 对齐
|
||||
|
||||
最终对外公开的 shader 语法目标,必须与 Unity 的使用方式保持一致。
|
||||
|
||||
目标形态应当是:
|
||||
|
||||
- 单个 `.shader` 文件作为逻辑 shader 入口
|
||||
- `Shader / Properties / SubShader / Pass` 的层级结构
|
||||
- pass 内通过 `HLSLPROGRAM ... ENDHLSL` 或等价块组织代码
|
||||
- 通过 `#pragma vertex` / `#pragma fragment` 指定 stage 入口
|
||||
|
||||
也就是说:
|
||||
|
||||
- 对外 authoring 视角应当是“Unity 风格的一体化 shader 文件”
|
||||
- 不是要求作者直接去维护一堆 runtime JSON manifest
|
||||
- 也不是让上层逻辑直接感知 D3D12/OpenGL/Vulkan 各自的底层差异
|
||||
|
||||
### 4.2 对内 runtime 资产目标:继续保留 contract 模型
|
||||
|
||||
虽然 authoring 目标要严格向 Unity 靠拢,但 runtime 不应直接拿 authoring AST 当执行数据。
|
||||
|
||||
运行时仍应落到清晰的 contract:
|
||||
|
||||
```text
|
||||
Shader Asset
|
||||
-> Properties
|
||||
-> Passes
|
||||
-> Tags
|
||||
-> Resource Bindings
|
||||
-> Backend Variants
|
||||
```
|
||||
|
||||
原因很直接:
|
||||
|
||||
- Renderer 需要稳定、扁平、可缓存的数据结构
|
||||
- 三后端最终执行的仍然是 backend variant
|
||||
- material schema 与 pass binding 都需要基于 import 结果,而不是原始文本
|
||||
|
||||
结论是:
|
||||
|
||||
- 外部写法要像 Unity
|
||||
- 内部执行模型继续使用现在这套 runtime contract,并进一步完善
|
||||
|
||||
### 4.3 Material 目标:从资源对象升级为正式材质实例
|
||||
|
||||
Material 下一阶段的核心不是“多支持几个 SetFloat”,而是建立正式实例语义。
|
||||
|
||||
Material 至少要明确:
|
||||
|
||||
- 引用哪个 shader
|
||||
- 使用哪个 pass 或 pass 策略
|
||||
- 每个 property 的默认值、覆盖值、类型与序列化规则
|
||||
- 每个 pass 对应的 material constant block 如何布局
|
||||
- 每个 texture / sampler / buffer 如何映射到 pass resource
|
||||
|
||||
Renderer 侧则必须能把这些内容稳定编译成:
|
||||
|
||||
- material binding key
|
||||
- material constant payload
|
||||
- descriptor update plan
|
||||
- pipeline compatibility key
|
||||
|
||||
## 5. 推荐架构
|
||||
|
||||
### 5.1 分成三层,不混写
|
||||
|
||||
推荐明确拆成三层:
|
||||
|
||||
```text
|
||||
Unity-like Shader Authoring (.shader)
|
||||
-> Shader Importer
|
||||
-> Runtime Shader Contract
|
||||
-> Backend Variants / Material Binding Plan
|
||||
```
|
||||
|
||||
三层职责分别是:
|
||||
|
||||
- authoring 层:给人写、给 editor 看
|
||||
- importer 层:把 authoring 语法转成稳定运行时资产
|
||||
- runtime 层:给 renderer 执行、缓存、绑定、测试
|
||||
|
||||
### 5.2 Shader 的 public contract 与 backend contract 分离
|
||||
|
||||
下一阶段不建议把“Unity 风格语法”直接等同于“单源码自动跨平台编译已完全成熟”。
|
||||
|
||||
更务实的路线是:
|
||||
|
||||
- public contract 先统一成 Unity 风格 `.shader`
|
||||
- importer 先产出统一 runtime contract
|
||||
- backend variant 暂时仍允许按 backend 持有各自的编译输入或中间产物
|
||||
|
||||
这意味着:
|
||||
|
||||
- 作者看到的是统一 shader
|
||||
- Renderer 消费的是统一 runtime contract
|
||||
- backend 最终执行的仍然可以是不同 variant
|
||||
|
||||
这个分层既不违背 Unity 风格目标,也不会过早把工程拖进复杂的全平台 shader 编译链泥潭。
|
||||
|
||||
### 5.3 Vertex / Fragment 的外部写法按 Unity 组织,内部可拆分
|
||||
|
||||
对外语义上,vertex / fragment 应当属于同一个 pass。
|
||||
|
||||
也就是说,public authoring 角度要符合 Unity:
|
||||
|
||||
- 一个 shader
|
||||
- 一个或多个 subshader
|
||||
- 一个或多个 pass
|
||||
- 每个 pass 里通过 pragma 指定 vertex / fragment
|
||||
|
||||
但内部 import/runtime 完全可以把它们拆成:
|
||||
|
||||
- pass descriptor
|
||||
- vertex stage variant
|
||||
- fragment stage variant
|
||||
|
||||
外部合一,内部拆开,这是最稳妥的做法。
|
||||
|
||||
## 6. 下一阶段实施顺序
|
||||
|
||||
### 阶段 D0:先打通 Shader Import / Artifact 基础设施(已完成)
|
||||
|
||||
这是当前 checkout 在 `2026-04-03` 已经完成的新增里程碑。
|
||||
|
||||
完成内容:
|
||||
|
||||
- `ShaderImporter` 已接管 `.shader / .hlsl / .glsl / .vert / .frag / .geom / .comp`
|
||||
- shader import 结果会写入 `Library/Artifacts/.../main.xcshader`
|
||||
- `ShaderLoader` 已支持直接读取 `.xcshader`
|
||||
- shader manifest 中声明的 stage 源文件会进入依赖追踪
|
||||
- `Material` 对引用 shader 的直接依赖也已进入依赖追踪
|
||||
- `shader_tests` 与 `material_tests` 已覆盖 shader artifact 生成、加载与重导场景
|
||||
|
||||
这一步的意义不是“最终方案已完成”,而是先把 shader 纳入和 texture/material/mesh 一致的资产闭环。
|
||||
|
||||
没有这一步,后续不管做 Unity 风格 frontend,还是做 material schema,都会一直建立在“运行时临时解析源码”的不稳定基础上。
|
||||
|
||||
### 阶段 0:当前基线确认
|
||||
|
||||
这部分已经完成,不再作为待办:
|
||||
|
||||
- runtime shader contract 已建立
|
||||
- builtin forward 已按 pass resources 驱动
|
||||
- 三后端渲染回归通过
|
||||
|
||||
### 阶段 A:建立 Unity 风格 Shader Authoring Frontend
|
||||
|
||||
目标:
|
||||
|
||||
- 新增 Unity 风格 `.shader` authoring 入口
|
||||
- importer 能解析最小闭环子集
|
||||
|
||||
第一批建议支持的子集:
|
||||
|
||||
- `Shader`
|
||||
- `Properties`
|
||||
- `SubShader`
|
||||
- `Tags`
|
||||
- `Pass`
|
||||
- `HLSLPROGRAM / ENDHLSL`
|
||||
- `#pragma vertex`
|
||||
- `#pragma fragment`
|
||||
|
||||
交付标准:
|
||||
|
||||
- builtin `forward-lit`
|
||||
- builtin `unlit`
|
||||
- builtin `object-id`
|
||||
- builtin `infinite-grid`
|
||||
|
||||
至少迁移其中一条主线并成功跑通三后端测试。
|
||||
|
||||
### 阶段 B:建立正式的 Material Schema 与 Instance Contract
|
||||
|
||||
目标:
|
||||
|
||||
- shader importer 输出可供 material 消费的 property schema
|
||||
- material 对 property override 做类型校验与默认值回退
|
||||
- 为 pass 生成 material constant layout 与 resource mapping
|
||||
|
||||
交付标准:
|
||||
|
||||
- material 不再只靠 builtin alias 名字兜底
|
||||
- shader property semantic 变成正式主路径,而不是兼容性补丁
|
||||
- renderer 能从 shader schema 生成 material binding payload
|
||||
|
||||
当前建议把这一阶段作为下一步主线。
|
||||
|
||||
原因:
|
||||
|
||||
- shader artifact 与依赖追踪已经到位,shader 现在可以作为稳定 schema 来源
|
||||
- material 仍然缺少基于 shader property 的正式类型校验、默认值回退和资源映射
|
||||
- renderer 目前虽然能消费 pass resources,但 material binding 仍偏 builtin-forward 特判
|
||||
|
||||
当前进展(`2026-04-03`):
|
||||
|
||||
- 已完成:shader schema 驱动的 property 类型校验与默认值回退
|
||||
- 已完成:source `.material` 的 `properties` authoring 入口
|
||||
- 已完成:material constant layout runtime contract
|
||||
- `Material` 现在会生成正式的 constant layout 元数据
|
||||
- layout 字段包含 `name / type / offset / size / alignedSize`
|
||||
- renderer 读取的已不再只是裸字节 payload,而是 `layout + payload` 组合
|
||||
- 已完成:builtin pass resource mapping / material binding plan runtime contract
|
||||
- `RenderMaterialUtility` 现在统一提供 `PassResourceBindingLocation / BuiltinPassResourceBindingPlan`
|
||||
- 显式 shader pass `resources` 与 legacy builtin forward fallback 已走同一套解析与校验路径
|
||||
- `BuiltinForwardPipeline` 已改为消费通用 binding plan,而不是继续内联 forward 特判绑定逻辑
|
||||
- 已验证:`rendering_unit_tests` 57/57,`material_tests` 51/51
|
||||
- 下一步:把这套 pass binding plan 继续推到 `Unlit / ObjectId` 等 pass,收敛成真正共享的 shader/material 执行边界
|
||||
|
||||
### 阶段 C:把 Pass Binding 扩展为正式材质执行链路
|
||||
|
||||
目标:
|
||||
|
||||
- 不只是 builtin forward 能吃 pass resources
|
||||
- `Unlit`、`ObjectId` 也逐步切到同一套 shader/material contract
|
||||
- pipeline state key 与 descriptor binding plan 从“按功能写逻辑”升级到“按 shader pass contract 解析”
|
||||
|
||||
交付标准:
|
||||
|
||||
- 至少 `ForwardLit + Unlit + ObjectId` 共用同一套 shader/material 执行边界
|
||||
- 新增 pass 不再默认要求先写一套新的硬编码 binding 路径
|
||||
|
||||
当前进展(`2026-04-04`):
|
||||
|
||||
- 已完成:builtin `ObjectId` pass 接入通用 pass binding plan
|
||||
- builtin object-id shader 已显式声明 `PerObject` 资源合约
|
||||
- `BuiltinObjectIdPass` 已改为消费通用 binding plan,不再硬编码 `set0/binding0` 常量布局
|
||||
- 显式 shader `resources` 与 legacy object-id fallback 现在走同一套解析与校验路径
|
||||
- 已完成:builtin `Unlit` shader / pipeline 主线接入共享执行边界
|
||||
- 新增 builtin `unlit` shader 资产与 `BuiltinResources` 入口
|
||||
- `BuiltinForwardPipeline` 现在会在 `ForwardLit + Unlit` 之间按 material/shader metadata 解析目标 pass
|
||||
- `Unlit` 与 `ForwardLit` 现在共用同一套 input layout、material schema、binding plan 与 descriptor 组装路径
|
||||
- 已完成:抽出 `ForwardLit / Unlit / ObjectId` 共用的 pass layout / descriptor set 组装骨架
|
||||
- `RenderMaterialUtility` 现在统一提供 `BuiltinPassSetLayoutMetadata` 与 `TryBuildBuiltinPassSetLayouts(...)`
|
||||
- `BuiltinForwardPipeline` 与 `BuiltinObjectIdPass` 现在都复用同一套 `binding plan -> set layout -> pipeline layout` 组装路径
|
||||
- forward 侧只保留 set0 compatibility fallback 与 draw/update 逻辑;object-id 侧只保留自身约束校验与 per-object 常量写入
|
||||
- 已完成:builtin `DepthOnly / ShadowCaster` shader 与独立 pass skeleton 落地
|
||||
- 新增 builtin `depth-only` 与 `shadow-caster` shader 资产,以及 `BuiltinResources` 对应入口
|
||||
- 新增 `BuiltinDepthOnlyPass / BuiltinShadowCasterPass`,作为独立 `RenderPass` 复用同一套 shared pass-layout skeleton
|
||||
- 两个 pass 当前先收敛到 `PerObject` 常量路径;opaque 物体走 builtin fallback,`ShadowCaster` 额外尊重 `MeshRenderer.castShadows`
|
||||
- 这一步解决的是“pass contract 与执行骨架缺失”,还没有把 shadow map / light-space request flow 一次性做完
|
||||
- 已完成:`CameraRenderRequest + CameraRenderer` 正式接入 `DepthOnly / ShadowCaster` request 流程
|
||||
- 新增 `depthOnly / shadowCaster` request,支持独立 surface、clear flags / clear color,以及可选 `RenderCameraData` override
|
||||
- `CameraRenderer` 现在按 `ShadowCaster -> DepthOnly -> Forward -> ObjectId` 的顺序执行这些请求,`BuiltinDepthStylePassBase` 也会按 request clear flags 清理目标
|
||||
- 已验证:`rendering_unit_tests` 73/73
|
||||
- 已验证:`rendering_integration_textured_quad_scene` 3/3(D3D12 / OpenGL / Vulkan)
|
||||
- 已验证:`rendering_integration_unlit_scene` 3/3(D3D12 / OpenGL / Vulkan)
|
||||
- 已验证:`rendering_integration_object_id_scene` 3/3(D3D12 / OpenGL / Vulkan)
|
||||
- 下一步:如果继续沿 renderer 主线收口,优先补 `DepthOnly / ShadowCaster` 的真实 cross-backend integration coverage,并决定是否把 shadow-map 结果继续接回 forward lighting 消费链;如果先控制范围,request 流程这一块已经收口
|
||||
|
||||
### 阶段 D:扩展 AssetDatabase / Library Artifact 能力
|
||||
|
||||
目标:
|
||||
|
||||
- 在已完成的 shader artifact 基础上,继续扩展 import 产物边界
|
||||
- backend variant 的编译输入、中间产物或缓存策略进入 `Library/Artifacts`
|
||||
- 为后续 Unity 风格 `.shader` frontend 预留稳定 importer 输出层
|
||||
|
||||
交付标准:
|
||||
|
||||
- 资源改动能够稳定触发 shader/material 重新导入
|
||||
- editor/runtime 读取的是 import 后资产,不是源码临时解析
|
||||
- shader importer 不再只服务当前 JSON manifest 兼容路径,也能承接下一步 authoring frontend
|
||||
|
||||
### 阶段 E:测试与回归收口
|
||||
|
||||
目标:
|
||||
|
||||
- 给 shader importer、material schema、binding plan 增加 unit tests
|
||||
- 对 forward/unlit/object-id 增加 integration coverage
|
||||
- 保持 D3D12 / OpenGL / Vulkan 一致回归
|
||||
|
||||
最低验证集:
|
||||
|
||||
- `shader_tests`
|
||||
- `material_tests`
|
||||
- `rendering_unit_tests`
|
||||
- `rendering_integration_textured_quad_scene`
|
||||
- `rendering_integration_backpack_lit_scene`
|
||||
|
||||
必要时新增:
|
||||
|
||||
- `rendering_integration_unlit_scene`
|
||||
- `rendering_integration_object_id_scene`
|
||||
|
||||
当前进展(`2026-04-04`):
|
||||
|
||||
- 已完成:`rendering_integration_unlit_scene`
|
||||
- 显式使用 builtin `unlit` shader + `Unlit` pass,覆盖 `BuiltinForwardPipeline` 的 `ForwardLit / Unlit` pass 选择路径
|
||||
- 复用 `textured_quad_scene` 构图做最小差异回归,验证 shader/material 新 contract 不改变既有画面输出
|
||||
- 已验证:`rendering_integration_unlit_scene`
|
||||
- D3D12:通过
|
||||
- OpenGL:通过
|
||||
- Vulkan:通过
|
||||
- 已完成:`rendering_integration_object_id_scene`
|
||||
- 新增独立的 object-id integration scene,直接验证 object-id 输出采样点,而不是再维护一张新的大尺寸 GT 图片
|
||||
- 覆盖 `CameraRenderer + BuiltinObjectIdPass` 路径,以及 `Forward -> ObjectId -> Copy/Screenshot` 的跨 pass 回归
|
||||
- 修正了 `BuiltinObjectIdPass` 在 Vulkan 下的顶点输入步长问题,并让 object-id pass 自己清理/写入 depth,避免依赖前一 pass 的 depth 复用状态
|
||||
- 已验证:`rendering_integration_object_id_scene`
|
||||
- D3D12:通过
|
||||
- OpenGL:通过
|
||||
- Vulkan:通过
|
||||
- 阶段 E 当前状态:`unlit_scene` 与 `object_id_scene` 均已完成并通过三后端验证,本阶段可以收口
|
||||
|
||||
## 7. 当前阶段明确不做
|
||||
|
||||
下一阶段不应把范围扩散到下面这些方向:
|
||||
|
||||
- render graph
|
||||
- shader graph
|
||||
- 全平台单源码自动转译一次性做完
|
||||
- 完整 SRP scripting API
|
||||
- 大规模后处理框架
|
||||
|
||||
这些都依赖 shader/material 正式体系先稳定下来。
|
||||
|
||||
## 8. 成功标准
|
||||
|
||||
这个阶段完成时,应该满足下面几个判断:
|
||||
|
||||
- 作者侧已经可以写 Unity 风格的 `.shader`
|
||||
- runtime 已不再依赖手写 JSON manifest 才能描述 pass contract
|
||||
- material 能基于 shader schema 做正式绑定,而不是 builtin 特判兜底
|
||||
- 至少 `ForwardLit / Unlit / ObjectId` 三类 pass 共用统一 shader/material 执行边界
|
||||
- 三后端回归测试仍稳定通过
|
||||
|
||||
## 9. 一句话总结
|
||||
|
||||
下一阶段不是继续给 builtin forward 打补丁,而是把 `Shader` 和 `Material` 正式提升为 Unity 风格渲染架构中的稳定中层资产与执行契约。
|
||||
|
||||
## 10. 快速收口策略(`2026-04-04`)
|
||||
|
||||
目标收窄为只处理 `shader / material` 核心主线,不继续扩散到完整阴影功能、render graph、shader graph 或 editor 外围能力。
|
||||
|
||||
按下面顺序收口:
|
||||
|
||||
1. 先完成 Unity-like `.shader` authoring MVP importer
|
||||
- 允许最小子集:`Shader / Properties / SubShader / Tags / Pass / HLSLPROGRAM / #pragma vertex / #pragma fragment`
|
||||
- importer 输出继续复用当前 runtime shader contract:`properties / passes / resources / backend variants`
|
||||
- 这一阶段不追求完整 Unity ShaderLab,只做 builtin 与主线材质系统需要的最小闭环
|
||||
|
||||
2. 再迁移 builtin shader 到新 authoring 入口
|
||||
- 优先 `ForwardLit / Unlit / ObjectId / DepthOnly / ShadowCaster`
|
||||
- 要求 importer 产出的 runtime contract 与当前 renderer 消费路径保持一致
|
||||
|
||||
3. 然后收紧 material 主路径
|
||||
- 以 imported shader schema 为主路径完成 property 类型校验、默认值回退、constant layout 与 resource mapping
|
||||
- builtin alias / canonical-name fallback 只保留兼容兜底,不再作为主执行路径
|
||||
|
||||
4. 最后做最小回归集收口
|
||||
- `shader_tests`
|
||||
- `material_tests`
|
||||
- `rendering_unit_tests`
|
||||
- 必要的 rendering integration smoke
|
||||
|
||||
当前进展(`2026-04-04`):
|
||||
|
||||
- 已完成:Step 1 `Unity-like .shader authoring MVP importer`
|
||||
- `ShaderLoader` 新增 Unity-like `.shader` authoring 识别与解析入口,但保留现有 JSON manifest `.shader` 兼容路径不动
|
||||
- importer 继续落到现有 runtime shader contract:`properties / passes / resources / backend variants`
|
||||
- `CollectSourceDependencies` 已覆盖新 authoring 路径,`AssetDatabase` 会继续追踪各 backend stage 文件依赖并参与重导入
|
||||
- 已完成:Step 2 `builtin shader 迁到新 authoring 入口`
|
||||
- `forward-lit / unlit / object-id / depth-only / shadow-caster` 五个 builtin `.shader` 入口已全部切到 Unity-like authoring
|
||||
- stage 源文件、builtin shader 路径与 renderer 消费 contract 保持不变,迁移只发生在 authoring 入口层
|
||||
- 补充 `DepthOnly / ShadowCaster` builtin shader loader 覆盖,确保五类 builtin pass 都经过新 authoring 路径验证
|
||||
- 已完成:Step 3 `material 主路径收紧到 imported shader schema`
|
||||
- `MaterialLoader` 在存在 shader schema 时,`properties / textures` 中的旧 key 会先解析到 shader property semantic,再落到 shader 正式属性名
|
||||
- 旧的 `baseColor / baseColorTexture` 等兼容 key 仍可导入,但只作为兼容输入存在,不再直接污染 material 主执行路径
|
||||
- 当 material 绑定 shader schema 后,未知 texture binding 现在会被显式拒绝,不再静默忽略
|
||||
- 已验证:`shader_tests` 中新增 authoring 直载与 artifact/reimport 覆盖
|
||||
- 已验证:`shader_tests` 31/31 通过,builtin `ForwardLit / Unlit / ObjectId / DepthOnly / ShadowCaster` 全部通过加载与 backend variant 覆盖
|
||||
- 已验证:`material_tests` 53/53 通过,schema 驱动的 property/texture 映射与未知 binding 拒绝路径都已覆盖
|
||||
- 已完成:Step 4 `最小回归集收口`
|
||||
- `rendering_unit_tests` 73/73 通过,builtin pass contract 与 renderer 侧消费路径在当前重构后保持稳定
|
||||
- 本轮最小回归集结果:`shader_tests` 31/31,`material_tests` 53/53,`rendering_unit_tests` 73/73
|
||||
- 以当前目标范围看,`shader / material` 核心主线已经可以阶段性收口
|
||||
- 补充验收(`2026-04-04`):
|
||||
- 补跑 integration smoke 时发现并修复了一处真实回归:Unity-like `.shader` authoring 将 HLSL 的 `MainVS / MainPS` 入口错误套到了 GLSL/Vulkan variant,上层表现为 Vulkan `BuiltinForwardPipeline` 建 pipeline 失败
|
||||
- 修复后补跑 `rendering_integration_textured_quad_scene` 与 `rendering_integration_unlit_scene`、`rendering_integration_object_id_scene`,三者在 `D3D12 / OpenGL / Vulkan` 下均通过
|
||||
- 额外补跑 `rendering_integration_backpack_lit_scene` 时,暴露出一条相邻但不同范围的问题:direct `MeshLoader` 导入的 mesh/material/texture 生命周期路径存在异常退出/不干净收尾,已超出本次 `shader / material contract` 收口范围
|
||||
- 因此本计划按“核心主线已收口,附带一条相邻 residual risk”归档;若后续继续补强,优先单独开一轮处理 `MeshLoader + imported subresource lifetime`
|
||||
- 下一步:从当前目标范围出发,本计划可归档;后续如继续推进,请以新计划承接 `backpack_lit_scene` 这条相邻问题
|
||||
|
||||
当前阶段明确不做:
|
||||
|
||||
- 完整阴影贴图消费链
|
||||
- render graph
|
||||
- shader graph
|
||||
- 完整 Unity ShaderLab 全语法
|
||||
- 与 shader/material 主线无关的 editor/ui 扩展
|
||||
@@ -1,140 +0,0 @@
|
||||
# XCUI Input / Focus / Shortcut Subplan 完成归档
|
||||
|
||||
日期:`2026-04-04`
|
||||
|
||||
## 1. 归档结论
|
||||
|
||||
`Subplan-04` 在其原始定义边界内已经完成,可以归档。
|
||||
|
||||
这里的“完成”指的是:
|
||||
|
||||
- 已建立 XCUI 输入事件基础模型
|
||||
- 已建立焦点、激活路径、指针捕获路径三套状态管理
|
||||
- 已建立 capture / target / bubble 三阶段输入路由
|
||||
- 已建立 shortcut scope 与命令匹配机制
|
||||
- 已建立统一的 `UIInputDispatcher`
|
||||
- 已补齐最小单元测试并完成通过验证
|
||||
|
||||
这里的“完成”不包括:
|
||||
|
||||
- Win32 原生消息采集
|
||||
- ImGui / Editor 侧的输入桥接
|
||||
- 文本输入控件级别的 IME 细节
|
||||
|
||||
这些内容本来就不在 `Subplan-04` 的负责边界里,后续应由 `Subplan-05`、`Subplan-08`、`Subplan-09` 等继续接手。
|
||||
|
||||
## 2. 本次落地内容
|
||||
|
||||
### 2.1 输入事件模型
|
||||
|
||||
已扩展 `UIInputEvent`:
|
||||
|
||||
- `PointerEnter`
|
||||
- `PointerLeave`
|
||||
- `pointerId`
|
||||
- `timestampNanoseconds`
|
||||
- `repeat`
|
||||
- `synthetic`
|
||||
|
||||
相关文件:
|
||||
|
||||
- `engine/include/XCEngine/UI/Types.h`
|
||||
|
||||
### 2.2 焦点与路径模型
|
||||
|
||||
已补齐:
|
||||
|
||||
- `UIElementId`
|
||||
- `UIInputPath`
|
||||
- `UIFocusController`
|
||||
- `UIFocusChange`
|
||||
|
||||
相关文件:
|
||||
|
||||
- `engine/include/XCEngine/UI/Input/UIInputPath.h`
|
||||
- `engine/include/XCEngine/UI/Input/UIFocusController.h`
|
||||
- `engine/src/UI/Input/UIInputPath.cpp`
|
||||
- `engine/src/UI/Input/UIFocusController.cpp`
|
||||
|
||||
### 2.3 Shortcut 系统
|
||||
|
||||
已补齐:
|
||||
|
||||
- `UIShortcutScope`
|
||||
- `UIShortcutChord`
|
||||
- `UIShortcutBinding`
|
||||
- `UIShortcutRegistry`
|
||||
- shortcut scope 优先级匹配规则
|
||||
|
||||
相关文件:
|
||||
|
||||
- `engine/include/XCEngine/UI/Input/UIShortcutRegistry.h`
|
||||
- `engine/src/UI/Input/UIShortcutRegistry.cpp`
|
||||
|
||||
### 2.4 输入路由与统一分发器
|
||||
|
||||
已补齐:
|
||||
|
||||
- `UIInputRouter`
|
||||
- `UIInputRoutingPlan`
|
||||
- `UIInputRoutingStep`
|
||||
- `UIInputDispatcher`
|
||||
|
||||
相关文件:
|
||||
|
||||
- `engine/include/XCEngine/UI/Input/UIInputRouter.h`
|
||||
- `engine/include/XCEngine/UI/Input/UIInputDispatcher.h`
|
||||
- `engine/src/UI/Input/UIInputRouter.cpp`
|
||||
- `engine/src/UI/Input/UIInputDispatcher.cpp`
|
||||
|
||||
## 3. 测试与验证
|
||||
|
||||
新增测试:
|
||||
|
||||
- `tests/Input/test_xcui_input_dispatcher.cpp`
|
||||
|
||||
已完成验证:
|
||||
|
||||
- `cmake --build build --config Debug --target input_tests`
|
||||
- `ctest -C Debug -R "XCUI.*" --output-on-failure`
|
||||
|
||||
验证结果:
|
||||
|
||||
- `6 / 6` 通过
|
||||
|
||||
覆盖点包括:
|
||||
|
||||
- focus path 切换
|
||||
- capture path 优先级
|
||||
- keyboard routed path 顺序
|
||||
- shortcut scope 匹配优先级
|
||||
- pointer down / pointer up 对 active path 的影响
|
||||
- shortcut 命中后优先消费、跳过普通 routing
|
||||
|
||||
## 4. 对后续 subplan 的可复用输出
|
||||
|
||||
当前已经可以被后续直接依赖的稳定入口:
|
||||
|
||||
- `XCEngine::UI::UIInputPath`
|
||||
- `XCEngine::UI::UIFocusController`
|
||||
- `XCEngine::UI::UIShortcutRegistry`
|
||||
- `XCEngine::UI::UIInputRouter`
|
||||
- `XCEngine::UI::UIInputDispatcher`
|
||||
|
||||
后续建议对接方式:
|
||||
|
||||
- `Subplan-05`:负责把 ImGui/平台输入桥接进这套 dispatcher
|
||||
- `Subplan-08`:负责把 menu / dock / panel shell 的 shortcut scope 接进 registry
|
||||
- `Subplan-09`:负责把 viewport shell 的 pointer / focus / capture 接进 routing
|
||||
|
||||
## 5. 原 subplan 文件
|
||||
|
||||
原始 subplan 文件保留在:
|
||||
|
||||
- `docs/plan/xcui-subplans/Subplan-04_XCUI-Input-Focus-Shortcut.md`
|
||||
|
||||
其状态应视为:
|
||||
|
||||
- 已完成
|
||||
- 已归档
|
||||
- 不再作为活跃开发计划继续扩写
|
||||
@@ -1,93 +0,0 @@
|
||||
# XCUI Subplan 01:Core Tree / State / Invalidation
|
||||
|
||||
归档日期:
|
||||
|
||||
- `2026-04-04`
|
||||
|
||||
状态:
|
||||
|
||||
- 已完成
|
||||
|
||||
本次实际完成内容:
|
||||
|
||||
- 新增 XCUI core 基础契约:`UIElementId`、`UIDirtyFlags`、`IUIViewModel`、`RevisionedViewModelBase`
|
||||
- 新增 retained-mode build 层:`UIBuildElementDesc`、`UIBuildList`、`UIBuildContext`
|
||||
- 新增 retained-mode tree 层:`UIElementTree`、`UIElementNode`、`UIElementTreeRebuildResult`
|
||||
- 新增统一入口:`UIContext`
|
||||
- 实现最小闭环:tree rebuild、dirty flag 标记、layout dirty 向祖先传播、dirty root 收集
|
||||
- 新增并验证 UI core 测试:tree 创建、unchanged rebuild、local state invalidation、view model invalidation、structure change、未闭合 build scope 失败
|
||||
|
||||
本次涉及文件:
|
||||
|
||||
- `engine/include/XCEngine/UI/Types.h`
|
||||
- `engine/include/XCEngine/UI/Core/UIInvalidation.h`
|
||||
- `engine/include/XCEngine/UI/Core/UIViewModel.h`
|
||||
- `engine/include/XCEngine/UI/Core/UIBuildContext.h`
|
||||
- `engine/include/XCEngine/UI/Core/UIElementTree.h`
|
||||
- `engine/include/XCEngine/UI/Core/UIContext.h`
|
||||
- `engine/src/UI/Core/UIBuildContext.cpp`
|
||||
- `engine/src/UI/Core/UIElementTree.cpp`
|
||||
- `tests/core/UI/CMakeLists.txt`
|
||||
- `tests/core/UI/test_ui_core.cpp`
|
||||
|
||||
验证结果:
|
||||
|
||||
- `cmake --build . --config Debug --target core_ui_tests`
|
||||
- `ctest -C Debug --output-on-failure -R UICoreTest --test-dir .`
|
||||
- 结果:`6/6` 通过
|
||||
|
||||
当前结论:
|
||||
|
||||
- `Subplan 01` 的最小 retained-mode core 已经可用
|
||||
- 后续 `Subplan 03/05/06/07/08/09` 可以基于这套 core 继续推进
|
||||
|
||||
原始 subplan 内容归档如下:
|
||||
|
||||
# Subplan 01:XCUI Core Tree / State / Invalidation
|
||||
|
||||
目标:
|
||||
|
||||
- 搭出 XCUI 的 retained-mode 核心骨架。
|
||||
- 明确 `ElementTree`、`NodeId`、`View`、`ViewModel`、`dirty flag`、`rebuild`、`lifecycle` 的最小闭环。
|
||||
|
||||
负责人边界:
|
||||
|
||||
- 负责 `engine/include/XCEngine/UI/` 与 `engine/src/UI/Core/` 的核心树模型。
|
||||
- 不负责具体布局算法。
|
||||
- 不负责 ImGui 适配绘制。
|
||||
|
||||
建议目录:
|
||||
|
||||
- `engine/include/XCEngine/UI/Core/`
|
||||
- `engine/src/UI/Core/`
|
||||
- `tests` 中对应 XCUI core 测试文件
|
||||
|
||||
前置依赖:
|
||||
|
||||
- 依赖主线完成 `Phase 0` 的基础类型和 UI 生命周期边界清理。
|
||||
|
||||
现在就可以先做的内容:
|
||||
|
||||
- 设计 `UIElementId` / `UIElement` / `UIContext` / `UIBuildContext`
|
||||
- 设计 dirty 标记与增量重建规则
|
||||
- 设计 ViewModel 读写边界和 command 回调入口
|
||||
- 写最小 tree rebuild 测试
|
||||
|
||||
明确不做:
|
||||
|
||||
- 不接入 `.xcui` 文件
|
||||
- 不接入 editor 面板
|
||||
- 不写具体 widget 大库
|
||||
|
||||
交付物:
|
||||
|
||||
- XCUI core 基础类与生命周期定义
|
||||
- tree rebuild / invalidation / state propagation 单元测试
|
||||
- 一个最小 demo:代码构建 UI tree 并触发一次增量更新
|
||||
|
||||
验收标准:
|
||||
|
||||
- 可以构建一棵稳定的 UI tree
|
||||
- 局部状态变化时只标脏必要节点
|
||||
- 重建逻辑与布局/渲染解耦
|
||||
- 其他 subplan 可以基于该模块定义控件树和状态更新
|
||||
@@ -1,50 +0,0 @@
|
||||
# XCUI Subplan 02:Layout Engine 完成归档
|
||||
|
||||
归档日期:
|
||||
|
||||
- `2026-04-04`
|
||||
|
||||
原始来源:
|
||||
|
||||
- [../XCUI完整架构设计与执行计划.md](../XCUI完整架构设计与执行计划.md)
|
||||
|
||||
本次完成范围:
|
||||
|
||||
- 落地 XCUI 纯算法布局基础类型:
|
||||
- `UILayoutLength`
|
||||
- `UILayoutConstraints`
|
||||
- `UILayoutThickness`
|
||||
- `UILayoutItem`
|
||||
- `UIStackLayoutOptions`
|
||||
- `UIOverlayLayoutOptions`
|
||||
- 落地 measure / arrange 双阶段布局算法
|
||||
- 实现 `Horizontal Stack` / `Vertical Stack` / `Overlay` 三类 MVP 容器
|
||||
- 支持 `px / content / stretch`
|
||||
- 支持 `padding / spacing / margin / min / max / alignment`
|
||||
- 建立独立 `ui_tests` 测试目标并通过验证
|
||||
|
||||
实际代码落点:
|
||||
|
||||
- [engine/include/XCEngine/UI/Types.h](D:/Xuanchi/Main/XCEngine/engine/include/XCEngine/UI/Types.h)
|
||||
- [engine/include/XCEngine/UI/Layout/LayoutTypes.h](D:/Xuanchi/Main/XCEngine/engine/include/XCEngine/UI/Layout/LayoutTypes.h)
|
||||
- [engine/include/XCEngine/UI/Layout/LayoutEngine.h](D:/Xuanchi/Main/XCEngine/engine/include/XCEngine/UI/Layout/LayoutEngine.h)
|
||||
- [tests/Core/Math/CMakeLists.txt](D:/Xuanchi/Main/XCEngine/tests/Core/Math/CMakeLists.txt)
|
||||
- [tests/Core/Math/test_ui_layout.cpp](D:/Xuanchi/Main/XCEngine/tests/Core/Math/test_ui_layout.cpp)
|
||||
|
||||
验证结果:
|
||||
|
||||
- `cmake --build . --config Debug --target math_tests -- /m:1`
|
||||
- `ctest -C Debug --test-dir D:\\Xuanchi\\Main\\XCEngine\\build\\tests\\Core\\Math --output-on-failure -R UI_Layout`
|
||||
- 结果:`5/5 UI_Layout tests passed`
|
||||
|
||||
与原子计划相比,当前仍未覆盖:
|
||||
|
||||
- `Scroll` 容器
|
||||
- 更复杂的主轴分布策略
|
||||
- 与 XCUI tree/state 的正式对接
|
||||
- 文本测量与真实控件树集成
|
||||
|
||||
建议后续承接:
|
||||
|
||||
- 由 `Subplan-01` 提供 tree / node / invalidation 契约后,把当前布局算法接入正式 UI tree
|
||||
- 后续再补 `Scroll`、更完整容器族、文本测量桥接
|
||||
@@ -1,83 +0,0 @@
|
||||
# XCUI Subplan 05: ImGui Transition Backend
|
||||
|
||||
归档日期:
|
||||
- `2026-04-04`
|
||||
|
||||
状态:
|
||||
|
||||
- 已完成
|
||||
|
||||
本次实际完成内容:
|
||||
- 新增 XCUI 绘制数据契约:`UIColor`、`UIDrawCommandType`、`UIDrawCommand`、`UIDrawList`、`UIDrawData`
|
||||
- 新增 `ImGuiTransitionBackend` 过渡后端,支持 `FilledRect`、`RectOutline`、`Text`、`Image`、`PushClipRect`、`PopClipRect`
|
||||
- 新增最小 editor 接入样例 `XCUIDemoPanel`,用于在现有编辑器壳层中演示 XCUI draw data 到 ImGui draw call 的过渡链路
|
||||
- 将 demo panel 接入 editor workspace,并补齐 editor/tests 构建入口
|
||||
- 新增 Subplan-05 配套测试,覆盖 draw data 聚合与 backend flush 行为
|
||||
|
||||
本次涉及文件:
|
||||
- `editor/CMakeLists.txt`
|
||||
- `editor/src/Core/EditorWorkspace.h`
|
||||
- `editor/src/XCUIBackend/ImGuiTransitionBackend.h`
|
||||
- `editor/src/panels/XCUIDemoPanel.cpp`
|
||||
- `editor/src/panels/XCUIDemoPanel.h`
|
||||
- `engine/include/XCEngine/UI/DrawData.h`
|
||||
- `tests/editor/CMakeLists.txt`
|
||||
- `tests/editor/test_xcui_draw_data.cpp`
|
||||
- `tests/editor/test_xcui_imgui_transition_backend.cpp`
|
||||
|
||||
验证结果:
|
||||
- `cmake --build . --config Debug --target editor_tests -- /m:1 /p:CL_MPCount=1`
|
||||
- `ctest -C Debug -R "XCUIDrawDataTest|XCUIImGuiTransitionBackendTest" --output-on-failure`
|
||||
- 结果:`4/4` 通过
|
||||
- `cmake --build . --config Debug --target XCEditor -- /m:1 /p:UseMultiToolTask=false /p:CL_MPCount=1`
|
||||
- 结果:通过
|
||||
|
||||
提交记录:
|
||||
- `75ded6f` `Add XCUI ImGui transition backend MVP`
|
||||
|
||||
当前结论:
|
||||
- `Subplan-05` 的最小过渡后端已经可用,可以作为 XCUI 在 editor 中落地的第一层渲染适配桥
|
||||
- XCUI 逻辑层仍然不直接依赖 ImGui API,ImGui 仅存在于过渡 backend 和 editor 接入层
|
||||
- 后续 `Subplan-08`、`Subplan-09` 可以直接基于这套 draw data 和 transition backend 继续推进
|
||||
|
||||
原始 subplan 内容归档如下:
|
||||
|
||||
# Subplan 05:XCUI ImGui Transition Backend
|
||||
|
||||
目标:
|
||||
- 在过渡阶段,让 ImGui 只承担宿主窗口和 draw submission 容器的职责
|
||||
- 由 XCUI 自己生成 draw list,再交给 ImGui backend 落屏
|
||||
|
||||
负责人边界:
|
||||
- 负责 `editor/src/XCUIBackend/` 或等价新目录
|
||||
- 负责 XCUI draw primitive 到 ImGui draw call 的映射
|
||||
- 不负责 XCUI tree、布局、样式的内部规则
|
||||
|
||||
建议目录:
|
||||
- `editor/src/XCUIBackend/`
|
||||
- `editor/src/UI/` 中与 XCUI backend 直接相关的桥接代码
|
||||
- `tests/Editor` 中 backend 相关测试
|
||||
|
||||
前置依赖:
|
||||
- 需要 `Subplan-01` 给出稳定 draw data 和 frame submission 契约
|
||||
- 需要 `Subplan-03` 提供样式查询结果
|
||||
|
||||
现在就可以先做的内容:
|
||||
- 定义 `UIDrawList` / `UIDrawCommand` / `UIDrawText` / `UIDrawImage` / `UIDrawClip`
|
||||
- 先做矩形、边框、文字、图片四类 primitive
|
||||
- 设计 frame begin / submit / end 的 adapter 流程
|
||||
- 写一个最小 demo panel,用 XCUI draw list 通过 ImGui 显示
|
||||
|
||||
明确不做:
|
||||
- 不做 RHI native backend
|
||||
- 不做 docking 逻辑
|
||||
|
||||
交付物:
|
||||
- XCUI 到 ImGui 的过渡 backend
|
||||
- primitive 转换测试或快照测试
|
||||
- 最小 editor 接入样例
|
||||
|
||||
验收标准:
|
||||
- XCUI 逻辑层不直接依赖 ImGui API
|
||||
- ImGui 只出现在 backend 适配层
|
||||
- 可以渲染基础控件和文本
|
||||
@@ -1,38 +0,0 @@
|
||||
# XCUI Parallel Subplans
|
||||
|
||||
基于 [XCUI完整架构设计与执行计划](../XCUI完整架构设计与执行计划.md) 的并行拆分版本。
|
||||
|
||||
当前建议:
|
||||
- `Phase 0` 由主线继续推进,目标是把 ImGui 从 `engine/editor` 公共边界剥离出来。
|
||||
- 其他人不要再去碰 `Phase 0` 正在修改的边界文件,优先认领下面的独立 subplan。
|
||||
- 每个人只领一个 subplan,按“自己负责的目录”做增量开发,避免跨 subplan 修改核心契约。
|
||||
|
||||
推荐并行顺序:
|
||||
- 可以立即开始:`03` `06`
|
||||
- 建议在 Core/Backend 契约初步稳定后启动:`07` `08` `09`
|
||||
|
||||
已完成归档:
|
||||
- `Subplan-01`:已于 `2026-04-04` 归档到 [../used/XCUI_Subplan-01_Core_Tree_State_完成归档_2026-04-04.md](../used/XCUI_Subplan-01_Core_Tree_State_完成归档_2026-04-04.md)
|
||||
- `Subplan-02`:已于 `2026-04-04` 归档到 [../used/XCUI_Subplan-02_LayoutEngine_完成归档_2026-04-04.md](../used/XCUI_Subplan-02_LayoutEngine_完成归档_2026-04-04.md)
|
||||
- `Subplan-04`:已于 `2026-04-04` 归档到 [../used/XCUI_Input_Focus_Shortcut_Subplan_完成归档_2026-04-04.md](../used/XCUI_Input_Focus_Shortcut_Subplan_完成归档_2026-04-04.md)
|
||||
- `Subplan-05`:已于 `2026-04-04` 归档到 [../used/XCUI_Subplan-05_ImGui_Transition_Backend_完成归档_2026-04-04.md](../used/XCUI_Subplan-05_ImGui_Transition_Backend_完成归档_2026-04-04.md)
|
||||
|
||||
统一协作约束:
|
||||
- 共享契约文件尽量只由主线或对应 owner 修改。
|
||||
- 新模块优先放到新目录,不要把 XCUI 新逻辑继续塞进旧的 ImGui helper。
|
||||
- 每个 subplan 都要自带最小测试或样例,不接受只落抽象不落验证。
|
||||
- 每个 subplan 完成后,至少产出一个可被其他 subplan 直接依赖的稳定入口。
|
||||
|
||||
共享高风险边界:
|
||||
- `engine/include/XCEngine/UI/`
|
||||
- `engine/include/XCEngine/Core/Layer.h`
|
||||
- `engine/include/XCEngine/Core/LayerStack.h`
|
||||
- `editor/src/Application.cpp`
|
||||
- `editor/src/Viewport/IViewportHostService.h`
|
||||
|
||||
Subplan 列表:
|
||||
- `Subplan-03`:XCUI Style / Theme / Token
|
||||
- `Subplan-06`:XCUI Markup / Import / Hot Reload
|
||||
- `Subplan-07`:XCUI Schema Inspector / PropertyGrid
|
||||
- `Subplan-08`:XCUI DockHost / Menu / Panel Shell
|
||||
- `Subplan-09`:XCUI ViewportSlot / Editor Integration
|
||||
@@ -1,46 +0,0 @@
|
||||
# Subplan 03:XCUI Style / Theme / Token
|
||||
|
||||
目标:
|
||||
|
||||
- 建立 XCUI 的样式模型、token 体系与主题覆盖规则。
|
||||
- 让控件视觉不再散落在代码硬编码常量里。
|
||||
|
||||
负责人边界:
|
||||
|
||||
- 负责 `Style` / `Theme` / `Token` 数据模型和解析规则。
|
||||
- 不负责 `.xcui` 导入器。
|
||||
- 不负责最终 renderer 具体绘制。
|
||||
|
||||
建议目录:
|
||||
|
||||
- `engine/include/XCEngine/UI/Style/`
|
||||
- `engine/src/UI/Style/`
|
||||
- `tests` 中 style/theme 测试
|
||||
|
||||
前置依赖:
|
||||
|
||||
- 依赖 `Subplan 01` 的节点属性注入点。
|
||||
|
||||
现在就可以先做的内容:
|
||||
|
||||
- 设计 style 层级:默认样式、类型样式、命名样式、局部覆盖
|
||||
- 设计 token:颜色、圆角、边距、字号、线宽
|
||||
- 设计主题切换与 token 查询接口
|
||||
- 写冲突优先级测试
|
||||
|
||||
明确不做:
|
||||
|
||||
- 不做具体面板视觉重构
|
||||
- 不做字体资源导入
|
||||
|
||||
交付物:
|
||||
|
||||
- 统一样式查询入口
|
||||
- `.xctheme` 对应的数据结构
|
||||
- 样式优先级与 token 解析测试
|
||||
|
||||
验收标准:
|
||||
|
||||
- 样式优先级可预测
|
||||
- 主题替换不需要改控件逻辑
|
||||
- 其他 subplan 能通过统一 API 获取视觉参数
|
||||
@@ -1,53 +0,0 @@
|
||||
# Subplan 04:XCUI Input / Focus / Shortcut
|
||||
|
||||
状态:
|
||||
|
||||
- 已于 `2026-04-04` 完成当前 subplan 定义边界内的实现。
|
||||
- 已归档到:
|
||||
[../used/XCUI_Input_Focus_Shortcut_Subplan_完成归档_2026-04-04.md](../used/XCUI_Input_Focus_Shortcut_Subplan_完成归档_2026-04-04.md)
|
||||
|
||||
目标:
|
||||
|
||||
- 建立 XCUI 的输入事件、焦点流转和快捷键分发模型。
|
||||
- 让输入不再直接写死在 ImGui 调用点里。
|
||||
|
||||
负责人边界:
|
||||
|
||||
- 负责 `engine/src/UI/Input/`。
|
||||
- 负责 pointer / keyboard / focus / command dispatch 的抽象。
|
||||
- 不负责平台消息采集本身。
|
||||
|
||||
建议目录:
|
||||
|
||||
- `engine/include/XCEngine/UI/Input/`
|
||||
- `engine/src/UI/Input/`
|
||||
- `tests` 中 input/focus 测试
|
||||
|
||||
前置依赖:
|
||||
|
||||
- 依赖 `Subplan 01` 的 tree 和 hit-test 基础契约。
|
||||
- 需要和 `Subplan 05` 对齐 adapter 输入桥接接口。
|
||||
|
||||
现在就可以先做的内容:
|
||||
|
||||
- 设计 `UIInputEvent` 丰富版本
|
||||
- 设计 focus path / active path / capture path
|
||||
- 设计 shortcut scope:global / window / panel / widget
|
||||
- 写 focus 切换和冒泡/捕获测试
|
||||
|
||||
明确不做:
|
||||
|
||||
- 不做 Win32 原生消息处理细节
|
||||
- 不做具体文本输入 widget
|
||||
|
||||
交付物:
|
||||
|
||||
- XCUI 输入分发器
|
||||
- 焦点管理器
|
||||
- 快捷键绑定与分发机制
|
||||
|
||||
验收标准:
|
||||
|
||||
- 可以确定事件从哪里来、往哪里走、谁消费
|
||||
- 焦点切换规则稳定可测
|
||||
- 快捷键系统可与 editor shell 直接对接
|
||||
@@ -1,46 +0,0 @@
|
||||
# Subplan 06:XCUI Markup / Import / Hot Reload
|
||||
|
||||
目标:
|
||||
|
||||
- 把 `.xcui` / `.xctheme` / `.xcschema` 拉进资源系统。
|
||||
- 建立导入、编译产物、热重载、诊断输出的第一版链路。
|
||||
|
||||
负责人边界:
|
||||
|
||||
- 负责资源类型、导入器、artifact、诊断日志。
|
||||
- 不负责 widget 运行时逻辑本身。
|
||||
|
||||
建议目录:
|
||||
|
||||
- `engine/include/XCEngine/Resources/UI/`
|
||||
- `engine/src/Resources/UI/`
|
||||
- `editor/src` 中与导入面板、诊断输出相关的接入口
|
||||
|
||||
前置依赖:
|
||||
|
||||
- 需要主计划中的资源类型命名拍板。
|
||||
- 与 `Subplan 03`、`Subplan 07` 协调格式字段。
|
||||
|
||||
现在就可以先做的内容:
|
||||
|
||||
- 定义三类资源描述结构
|
||||
- 设计导入错误诊断格式
|
||||
- 设计热重载触发和缓存失效策略
|
||||
- 先做一个最小 parser,可以把简单 `.xcui` 编成中间结构
|
||||
|
||||
明确不做:
|
||||
|
||||
- 不做完整 markup 语法大全
|
||||
- 不做 inspector 的最终渲染
|
||||
|
||||
交付物:
|
||||
|
||||
- UI 资源类型定义
|
||||
- 导入器与 artifact 结构
|
||||
- 热重载与错误输出最小闭环
|
||||
|
||||
验收标准:
|
||||
|
||||
- UI 资源可被 ResourceManager 识别
|
||||
- 导入失败时有可读诊断
|
||||
- 改动文件后可触发重新加载
|
||||
Reference in New Issue
Block a user