docs: sync api and planning docs

This commit is contained in:
2026-04-08 16:07:03 +08:00
parent 08c3278e10
commit 31756847ab
1826 changed files with 44502 additions and 29645 deletions

View File

@@ -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` 只减不增

View File

@@ -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/3D3D12 / OpenGL / Vulkan
- 已验证:`rendering_integration_unlit_scene` 3/3D3D12 / OpenGL / Vulkan
- 已验证:`rendering_integration_object_id_scene` 3/3D3D12 / 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 扩展

View File

@@ -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`
其状态应视为:
- 已完成
- 已归档
- 不再作为活跃开发计划继续扩写

View File

@@ -0,0 +1,349 @@
# XCUI Phase Status 2026-04-05
## Scope
Current execution stays inside the XCUI module and `new_editor`.
Old `editor` replacement is explicitly out of scope for this phase.
## Latest Checkpoint
- Phase 1 sandbox batch committed and pushed as `67a28bd` (`Add XCUI new editor sandbox phase 1`).
- Phase 2 common/runtime batch committed and pushed as `ade5be3` (`Add XCUI runtime screen layer and demo textarea`).
- Phase 3 has now produced a stable mixed batch across common/runtime/editor:
- schema document definition data is now retained on `UIDocumentModel` and round-trips through the UI artifact path
- schema self-definition validation is now stricter around enum/document-only metadata combinations
- engine runtime coverage was tightened again around `UISystem` and concrete document-host rendering
- `LayoutLab` continues as the editor widget proving ground for tree/list/property-section style controls
- the demo sandbox and editor bridge APIs were tightened again without touching the old editor replacement scope
- `new_editor` now has an explicit two-step window compositor seam through `IWindowUICompositor` / `ImGuiWindowUICompositor`, layered on top of `IEditorHostCompositor` / `ImGuiHostCompositor`
- The native-host / hosted-preview publication follow-up is now landed in `new_editor`:
- `NativeWindowUICompositor` is now buildable alongside the legacy ImGui compositor
- `Application` now defaults to a native XCUI host path instead of creating the ImGui shell by default
- the native default path now reuses shell-agnostic `XCUIDemoPanel::ComposeFrame(...)` / `XCUILayoutLabPanel::ComposeFrame(...)` results instead of keeping a second demo/layout runtime + input-bridge stack inside `Application`
- the native compositor now publishes hosted-preview textures as SRV-backed `UITextureRegistration` / `UITextureHandle` values instead of relying on ImGui-only descriptor semantics
- the native shell now begins the hosted-preview queue/registry lifecycle each frame, queues native preview frames, drains them during the native render pass, and consumes published hosted-surface images directly in panel cards with live/warming placeholder states
- the old ImGui shell path remains present as an explicit compatibility host instead of the default host
- `XCUIInputBridge.h` no longer drags `imgui.h` through the public XCUI input seam
- The default native text path is now also de-ImGuiized inside `new_editor`:
- `XCUIStandaloneTextAtlasProvider` now builds and owns its atlas through a Windows/GDI raster path instead of using ImGui font-atlas internals
- the standalone atlas provider now exposes both `RGBA32` and `Alpha8` views, supports non-nominal size resolution, lazily adds non-prebaked BMP glyphs, and falls back to `?` when a glyph cannot be rasterized
- The default native shell path now also has a cleaner translation-unit seam:
- legacy ImGui shell chrome / HUD rendering now lives in a dedicated legacy-only `Application` translation unit instead of keeping direct `ImGui::*` calls inside the main native host TU
- `Application.cpp` no longer directly includes `<imgui.h>`, even though the compatibility host path is still compiled into `new_editor`
- The `new_editor` build now also has an explicit compatibility-source slice:
- legacy ImGui shell sources and vendored ImGui backend sources are now grouped into a dedicated compatibility static library instead of being compiled directly as part of the main `XCNewEditor` source list
- The main-target header-isolation milestone is now also landed in `new_editor`:
- `XCNewEditor` drops direct `${IMGUI_SOURCE_DIR}` / `${IMGUI_SOURCE_DIR}/backends` include-directory requirements
- the generated `XCNewEditor.vcxproj` no longer advertises `imgui-src` or `editor/src` on the default include surface
- the default native compile path no longer reaches `<imgui.h>` through `editor/src/UI/ImGuiBackendBridge.h` or equivalent bridge headers
- `XCNewEditorImGuiCompat` remains the explicit compatibility-only consumer of legacy ImGui headers and backend sources
- The compositor/font compat boundary is now tighter as well:
- `IWindowUICompositor.h` and `IEditorHostCompositor.h` no longer declare `CreateImGui*` factories on the generic XCUI seam
- ImGui factory entry points now live only in `LegacyImGuiHostInterop.h`
- `XCUIEditorFontSetup.h` no longer exposes `ImFont` / `ImFontAtlas`; the public API is now a current-context font bootstrap helper
- `XCUIStandaloneTextAtlasProvider.h` no longer depends on the ImGui-oriented font bootstrap header
- The generic shell command/state boundary is now narrower as well:
- `XCUIShellChromeState` no longer carries the legacy host demo-window toggle or command id on the generic shell-state surface
- `Application.h` no longer exposes that legacy demo command through generic `ShellCommandIds`, `ShellCommandBindings`, or `RegisterShellViewCommands(...)`
- the legacy demo window toggle now lives as a compatibility-only command inside `ApplicationLegacyImGui.cpp`
- `Application.h` now also uses backend-neutral compatibility-host naming for the non-native window-host path, so the generic application header no longer names `LegacyImGui` in its mode enum or private host-frame method declarations
- generic `Application` host-mode/member naming is now also shifting from `LegacyImGui` toward `CompatibilityHost`, so the default shell surface does not have to encode ImGui into every host-facing method name
- Old `editor` replacement remains deferred; all active execution still stays inside XCUI shared code and `new_editor`.
## Three-Layer Status
### 1. Common Core
- `UI::DrawData`, input event types, focus routing, style/theme resolution are in active use.
- `UIDocumentCompiler` is buildable again after repairing the duplicated schema helper regression introduced by overlapping schema work.
- `UIDocumentModel` / `UIDocumentResource` now retain schema definition metadata explicitly, including memory accounting and `UISchema` accessors.
- `.xcschema` round-trip coverage is now present through compile, loader, artifact write, and artifact read paths.
- Build-system hardening for MSVC/PDB output paths has started in root CMake, `engine/CMakeLists.txt`, `new_editor/CMakeLists.txt`, and `tests/NewEditor/CMakeLists.txt`.
- Shared engine-side XCUI runtime scaffolding is now present under `engine/include/XCEngine/UI/Runtime` and `engine/src/UI/Runtime`.
- Shared engine-side `UIDocumentScreenHost` now compiles `.xcui` / `.xctheme` screen documents into a runtime-facing document host path instead of leaving all document ownership in `new_editor`.
- Shared text-editing primitives now live under `engine/include/XCEngine/UI/Text` and `engine/src/UI/Text`, so UTF-8 caret movement, line splitting, and multiline navigation are no longer trapped inside `XCUI Demo`.
- Shared text-input controller/state now also lives under `engine/include/XCEngine/UI/Text` and `engine/src/UI/Text`, so character insertion, backspace/delete, submit, and multiline key handling no longer need to be reimplemented per host.
- Shared editor collection primitive classification and metric helpers now also live under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets`, covering the current `ScrollView` / `TreeView` / `ListView` / `PropertySection` / `FieldRow` prototype taxonomy.
- Shared single-selection state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UISelectionModel`, so collection-style widget selection no longer has to stay private to `LayoutLab`.
- Shared expansion state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UIExpansionModel`, so collapsible tree/property-style widget state no longer has to stay private to `LayoutLab`.
- Shared keyboard-navigation state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UIKeyboardNavigationModel`, so list/tree/property-style widgets can share current-index, anchor, and home/end/step navigation rules instead of re-rolling them per sandbox.
- Shared property-edit session state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UIPropertyEditModel`, so editor-facing field rows can reuse begin/edit/commit/cancel transaction state instead of baking that directly into sandbox runtimes.
- Core regression coverage now includes `UIContext`, layout, style, runtime screen player/system, and real document-host tests through `core_ui_tests`.
Current gap:
- Minimal schema self-definition support is landed, including consistency checks for enum/document-only schema metadata, but schema-driven validation for `.xcui` / `.xctheme` instances is still not implemented.
- Shared widget/runtime instantiation is still thin and mostly editor-side.
- Common widget primitives are still incomplete: shared text-input presentation/composition on top of the new text controller, multi-selection/focus-traversal/virtualized collection state on top of the new editor-primitive helpers, shared scroll/navigation-scope/caret-layout helpers, and promotion of the current native text-atlas path into a shared/cross-platform text subsystem.
### 2. Runtime/Game Layer
- The main concrete progress here is that the retained-mode demo runtime now supports a real `TextField` input path with UTF-8 text entry and backspace handling.
- The demo runtime has moved past single-line input: multiline `TextArea` behavior is now covered in the sandbox testbed.
- Engine-side runtime ownership is no longer zero: `UIScreenPlayer`, `UIDocumentScreenHost`, and `UISystem` now define a shared runtime contract for loading a screen document, ticking it with input, and collecting `UI::UIDrawData`.
- `UISystem` now supports layered screen composition semantics: stacked screen players, top-interactive input routing, and modal layers that block lower screens.
- `UIScreenStackController::ReplaceTop` now preserves the previous top screen if the replacement screen fails to load, so runtime menu flows do not silently drop their active layer on bad assets.
- `SceneRuntime` now owns a dedicated `UISceneRuntimeContext`, so game/runtime code has a first-class place to configure viewport/focus, queue `UIInputEvent`s, drive `UISystem` each `Update`, and inspect the latest UI frame result.
- Runtime screen emission now also carries concrete button text in the shared document host path instead of silently dropping button labels.
- `UISystemFrameResult` now also preserves viewport rect, submitted input count, frame delta, and focus state, and both `UISystem` and `UISceneRuntimeContext` now expose `ConsumeLastFrame()` so runtime/game hosts can drain the last retained frame packet without copying editor-side concepts into the shared layer.
- `UIScreenPlayer` now also exposes `ConsumeLastFrame()`, so player/system/context all share the same consume-vs-borrow frame ownership semantics in the runtime layer.
Current gap:
- Runtime UI is now wired into `SceneRuntime`, but render submission is still limited to producing `UIDrawData`; there is no game-view/runtime presenter path that automatically draws those frames yet.
- The runtime widget library is still shallow and missing the editor-grade controls that will later be shared downward.
### 3. Editor Layer
- `new_editor` remains the isolated XCUI sandbox.
- Native hosted preview is now working end-to-end as `RHI offscreen surface -> SRV-backed publication -> hosted surface image present` through the native shell path.
- Hosted preview surface descriptors now stay on XCUI-owned value types (`UITextureHandle`, `UIPoint`, `UIRect`) instead of exposing ImGui texture/UV types through the generic preview contract.
- Shared `UI::UIDrawData` image commands now carry explicit `uvMin` / `uvMax` source-rect semantics, and the native panel canvas host preserves those UVs when it records hosted surface-image preview commands.
- `XCUI Demo` remains the long-lived effect and behavior testbed.
- `XCUI Demo` now covers both single-line and multiline text authoring behavior, including click caret placement, delete/backspace, tab indentation, and optional text-area line numbers.
- `XCUI Demo` now consumes the shared `UITextInputController` path for text editing instead of carrying a private key-handling state machine.
- `LayoutLab` now includes a `ScrollView` prototype and a more editor-like three-column authored layout.
- `LayoutLab` now also covers editor-facing widget prototypes: `TreeView`, `TreeItem`, `ListView`, `ListItem`, `PropertySection`, and `FieldRow`.
- `LayoutLab` now consumes the shared `UIEditorCollectionPrimitives` helper layer for collection-widget tag classification, clipping flags, and default metric resolution instead of keeping that taxonomy private to the sandbox runtime.
- `LayoutLab` now also consumes the shared `UISelectionModel` for click-selection persistence across collection-style widgets, and the diagnostics panel now exposes both hovered and selected element ids.
- `LayoutLab` now also consumes the shared `UIExpansionModel` for tree expansion and property-section collapse, with reserved property headers, disclosure glyphs, and persisted click-toggle behavior in the sandbox runtime.
- `new_editor` now also has an isolated `XCUIEditorCommandRouter` model with shortcut matching, enable predicates, and direct command invocation semantics covered by dedicated tests, ready for shell-frame integration.
- `XCUI Demo` now exports pending per-frame command ids through `DrainPendingCommandIds()`, so editor-side hosts have a clean seam for observing demo/runtime command traffic without parsing draw data.
- `XCUI Demo` and `LayoutLab` panel canvases are now being pulled behind a dedicated `IXCUIPanelCanvasHost` seam, so canvas surface presentation, hover/focus fallback state, and overlay draw hooks no longer have to stay hard-coded inside each ImGui panel implementation.
- The panel-canvas seam now keeps the generic host contract on observable canvas behavior only; backend/capability identity probing has been removed from `IXCUIPanelCanvasHost`, and the minimal `NullXCUIPanelCanvasHost` remains the concrete placeholder host for non-ImGui paths.
- `XCUI Demo` and `LayoutLab` panel input now also flows through an explicit `IXCUIInputSnapshotSource` seam, so panel/runtime code no longer reads `ImGuiIO` / `ImGui::IsKeyPressed` / `ImGui::IsMouseClicked` directly when the shell wants to use an ImGui adapter.
- `new_editor` now also has an explicit `ImGuiXCUIInputSnapshotSource` adapter, keeping ImGui-specific input capture in the host adapter layer instead of inside panel/runtime update code.
- `XCUI Demo` and `LayoutLab` panels now both expose shell-agnostic per-frame composition results (`ComposeFrame(...)` + `GetLastFrameComposition()`), so native/compat shells can reuse one panel-local frame pipeline instead of duplicating runtime/input/preview assembly logic.
- `XCUIDemoPanel::ComposeFrame(...)` now also accepts an injected input snapshot plus explicit placeholder/frame options, which lets the native shell reuse the panel pipeline without falling back to the compatibility-path placeholder behavior on direct draw-data cards.
- Panel diagnostics were expanded to clearly separate preview/runtime/input state and native vs legacy paths.
- The editor bridge layer now has smoke coverage for swapchain after-UI rendering hooks and SRV-backed ImGui texture descriptor registration.
- `Application` no longer owns the ImGui backend directly; window presentation now routes through `IWindowUICompositor` with an `ImGuiWindowUICompositor` implementation, which currently delegates to `IEditorHostCompositor` / `ImGuiHostCompositor`.
- Hosted preview offscreen surfaces now keep compositor-returned `UITextureRegistration` / `UITextureHandle` data inside `Application` instead of storing `ImTextureID` directly.
- The generic hosted-preview presenter contract no longer owns `ImGuiTransitionBackend`; the ImGui presenter now sits in a separate `ImGuiXCUIHostedPreviewPresenter` header while the native queue/surface registry remains XCUI-generic.
- The generic hosted-preview frame contract no longer carries an ImGui draw-list pointer; the legacy ImGui presenter resolves its inline draw target from the active ImGui window context instead of pushing that type through the XCUI contract.
- The legacy ImGui hosted-preview presenter now also accepts an explicit draw-target binding object, so presenter-side `ImGui::GetWindowDrawList()` lookup is no longer hard-coded inside the generic presenter path and can stay isolated behind the ImGui adapter layer.
- `Application` shell menu toggles and global shortcuts now route through `XCUIEditorCommandRouter` instead of directly mutating shell booleans from menu callbacks, giving the editor layer a real command-routing seam.
- `LayoutLab` runtime now consumes the shared `UIKeyboardNavigationModel` for abstract list/tree/property navigation actions (`previous/next/home/end/collapse/expand`), so keyboard collection traversal rules are no longer trapped in sandbox-local state.
- `LayoutLab` panel input now also maps concrete arrow/home/end keys into those shared navigation actions, so keyboard traversal is reachable from the sandbox UI instead of staying runtime-only.
- `XCUIDemoRuntime` now bridges pointer activation, text-edit commands, and shortcut-triggered commands through a unified command path, and `DrainPendingCommandIds()` now preserves mixed pointer/text/shortcut ordering.
- `new_editor` now also has a pure `XCUIShellChromeState` model covering panel visibility, hosted-preview mode, and shell-level view toggles without depending on ImGui, `Application`, or the old editor.
- `XCUIShellChromeState` hosted-preview mode naming is now backend-neutral (`HostedPresenter` / `NativeOffscreen`) instead of encoding `ImGui` into the XCUI shell model.
- The shell chrome view-toggle model no longer carries the legacy host demo window at all on the generic XCUI seam; that command now lives only inside the compatibility-only legacy host implementation.
- `new_editor` now also has a concrete `NativeWindowUICompositor` path and native-focused compositor tests, so the window compositor seam is no longer ImGui-only.
- `Application` now also has a native XCUI shell path that:
- becomes the default `new_editor` startup path
- lays out `XCUI Demo` and `Layout Lab` as native cards directly in the swapchain window
- routes shell shortcuts through the same command router without reading ImGui capture state in the default host path
- reuses panel-local `ComposeFrame(...)` entry points for demo/layout runtime, input, hosted-preview, and overlay composition instead of maintaining duplicate native-shell runtime/input state in `Application`
- composes one native `UIDrawData` packet and submits it through `NativeWindowUICompositor`
- Native shell preview-mode reconfiguration now rebuilds the native panel bindings instead of rebinding a legacy hosted presenter, so the default host path no longer needs the ImGui presenter when a card stays on direct draw-data composition.
- The native shell layout policy now also lives behind `XCUINativeShellLayout`, so top-bar/footer/workspace geometry, panel split rules, and active-panel transfer on pointer press are no longer hard-coded inline inside `Application.cpp`.
- `NativeXCUIPanelCanvasHost` now backs that direct shell path as an externally driven canvas/session host for native cards instead of assuming an ImGui child-window model, and it now emits native `Image` draw commands for hosted surface-image previews while preserving per-surface UVs.
- `NativeWindowUICompositor` now creates and frees SRV-backed texture registrations for hosted preview surfaces, so native publication no longer depends on ImGui descriptor handles.
- `Application` now runs the hosted-preview lifecycle in both legacy and native frame paths, treats published textures as XCUI-owned `UITextureHandle` state, queues native preview frames from `BuildNativeShellDrawData(...)`, and drains them during native rendering before shell chrome overlays.
- `XCUIInputBridge.h` no longer includes `imgui.h`, so the public XCUI input bridge seam is now host-neutral at the header boundary.
- `XCNewEditor` builds successfully to `build/new_editor/bin/Debug/XCNewEditor.exe`.
Current gap:
- The default shell host is now native, and the legacy ImGui shell/panel path has been split out of the default executable into the standalone `XCNewEditorImGuiCompat` compatibility slice.
- The default native shell path is now split away from direct `ImGui::*` calls at the main-target header/include level and no longer links the compatibility slice by default.
- The default native shell now also consumes shared `XCUINativeShellLayout` and `UIEditorPanelChrome` helpers for panel split/chrome policy instead of duplicating that card layout logic entirely inside `Application.cpp`.
- The native shell currently proves direct runtime composition, but its shell chrome is still a bespoke `Application`-side layout rather than a fully shared XCUI-authored editor shell document.
- Editor-specialized widgets are still incomplete at the shared-module level: the authored prototypes exist, but virtualization, multi-selection/focus traversal, toolbar/menu chrome, menu interaction widgets, and icon-atlas widgets are not yet extracted into reusable XCUI modules.
- The default native text path now uses a standalone Windows/GDI atlas through `XCUIStandaloneTextAtlasProvider`, but that provider still lives inside `new_editor` and is not yet promoted into a shared/cross-platform text subsystem.
## Validated This Phase
- `new_editor_xcui_demo_panel_tests`: `4/4`
- `new_editor_xcui_demo_runtime_tests`: `12/12`
- `new_editor_xcui_input_bridge_tests`: `4/4`
- `new_editor_imgui_xcui_input_adapter_tests`: `2/2`
- `new_editor_xcui_layout_lab_runtime_tests`: `12/12`
- `new_editor_xcui_rhi_command_compiler_tests`: `7/7`
- `new_editor_xcui_rhi_render_backend_tests`: `5/5`
- `new_editor_xcui_standalone_text_atlas_provider_tests`: `6/6`
- `new_editor_xcui_hosted_preview_presenter_tests`: `20/20`
- `new_editor_legacy_imgui_host_interop_tests`: `4/4`
- `new_editor_imgui_window_ui_compositor_tests`: `7/7`
- `new_editor_native_window_ui_compositor_tests`: `8/8`
- `new_editor_xcui_editor_command_router_tests`: `5/5`
- `new_editor_application_shell_command_bindings_tests`: `11/11`
- `new_editor_xcui_shell_chrome_state_tests`: `11/11`
- `new_editor_xcui_panel_canvas_host_tests`: `4/4`
- `new_editor_imgui_xcui_panel_canvas_host_tests`: `1/1`
- `new_editor_native_xcui_panel_canvas_host_tests`: `4/4`
- `new_editor_xcui_layout_lab_panel_tests`: `6/6`
- `XCNewEditor` Debug target builds successfully
- `XCNewEditor.exe` native-default smoke run stayed alive for `5s`
- `core_ui_tests`: `52 total` (`50` passed, `2` skipped because `KeyCode::Delete` currently aliases `Backspace`)
- `scene_tests`: `68/68`
- `core_ui_style_tests`: `5/5`
- `ui_resource_tests`: `11/11`
- `editor_tests` targeted bridge smoke: `3/3`
## Landed This Phase
- Demo runtime `TextField` with UTF-8 text insertion, caret state, and backspace.
- Demo runtime multiline `TextArea` path in the sandbox and test coverage for caret movement / multiline input.
- Common-core `UITextEditing` extraction now owns UTF-8 offset stepping, codepoint counting, line splitting, and vertical caret motion with dedicated `core_ui_tests` coverage.
- Common-core `UITextInputController` extraction now owns per-field text state, character insertion, enter-submit, and multiline keyboard editing behavior with dedicated `core_ui_tests` coverage.
- Common-core `UIEditorCollectionPrimitives` extraction now owns the editor collection tag taxonomy and default metric resolution used by current `LayoutLab` widget prototypes, with dedicated `core_ui_tests` coverage.
- Common-core `UISelectionModel` extraction now owns reusable single-selection state for collection-style widgets, with dedicated `core_ui_tests` coverage.
- Common-core `UIExpansionModel` extraction now owns reusable expansion/collapse state for tree/property-style widgets, with dedicated `core_ui_tests` coverage.
- Common-core `UIKeyboardNavigationModel` extraction now owns reusable current-index/anchor navigation state for collection-style widgets, with dedicated `core_ui_tests` coverage.
- Common-core `UIPropertyEditModel` extraction now owns reusable property-field edit session state, including staged values and commit/cancel behavior, with dedicated `core_ui_tests` coverage.
- Runtime frame ownership was tightened again:
- `UIScreenPlayer::ConsumeLastFrame()` now exposes consume-style packet ownership at the player layer
- `UISystemFrameResult` now carries viewport rect, submitted input event count, frame delta, and focus state
- `UISystem::ConsumeLastFrame()` moves the retained packet out of the runtime layer
- `UISceneRuntimeContext::ConsumeLastFrame()` forwards the same shared runtime seam upward
- Demo runtime text editing was extended with:
- click-to-place caret
- `Delete` support
- `Tab` / `Shift+Tab` indentation for text areas
- optional text-area line-number gutter rendering
- Demo authored resources updated to exercise the input field.
- LayoutLab `ScrollView` prototype with clipping and hover rejection outside clipped content.
- LayoutLab editor-widget prototypes for tree/list/property-style sections with dedicated runtime coverage.
- LayoutLab click-selection now persists through the shared `UISelectionModel`, including selected-state diagnostics and reusable visual selection feedback on cards, collection rows, and field rows.
- LayoutLab tree expansion and property-section collapse now persist through the shared `UIExpansionModel`, including reserved property headers, disclosure glyphs, and runtime coverage for collapsed/expanded visibility.
- `XCUIDemoRuntime` now exposes `DrainPendingCommandIds()` so hosts can observe emitted runtime commands in order across pointer/text interactions without scraping UI text or draw-command payloads.
- `XCUIDemoRuntime` command recording was tightened so pointer activation, text editing, and shortcut-triggered commands now share one bridge path and preserve mixed ordering in `DrainPendingCommandIds()`.
- Schema document support extended with:
- retained `UISchemaDefinition` data on `UIDocumentModel`
- artifact schema version bump for UI documents
- loader/resource accessors and memory accounting
- schema compile/load/artifact regression coverage
- schema consistency rules for:
- `allowedValues` only on `enum`
- `documentKind` / `restrictDocumentKind` only on `document`
- explicit `documentKind` required when `restrictDocumentKind=true`
- Engine runtime layer added:
- `UIScreenPlayer`
- `UIDocumentScreenHost`
- `UISceneRuntimeContext`
- `UIScreenStackController`
- `UISystem`
- layered screen composition and modal blocking semantics
- Runtime/game integration scaffolding now includes reusable `HUD/menu/modal` stack helpers on top of `UISystem`.
- `UIScreenStackController` replacement now rolls back safely on failure instead of popping the active top layer first.
- `SceneRuntime` now exposes XCUI runtime ownership directly:
- `GetUISystem()`
- `GetUIScreenStackController()`
- `GetLastUIFrame()`
- `SetUIViewportRect(...)`
- `SetUIFocused(...)`
- `QueueUIInputEvent(...)`
- `ClearQueuedUIInputEvents()`
- automatic `UISystem` ticking during `SceneRuntime::Update(...)`
- Runtime document-host draw emission now preserves button labels for shared screen rendering.
- RHI image path improvements:
- clipped image UV adjustment
- mirrored image UV preservation
- explicit image UV/source-rect submission through `UI::UIDrawData`
- external texture binding reuse
- per-batch scissor application
- Editor bridge helpers now expose:
- an `afterUiRender` swapchain callback hook in `D3D12WindowRenderer`
- SRV-view based texture descriptor registration in `ImGuiBackendBridge`
- smoke tests for window renderer, ImGui backend bridge, and console sink registration
- `new_editor` host presentation now has a first-class compositor seam:
- `IWindowUICompositor`
- `ImGuiWindowUICompositor`
- `IEditorHostCompositor`
- `ImGuiHostCompositor`
- `Application` frame/present flow routed through the compositor instead of direct `m_imguiBackend` ownership
- The window-level XCUI compositor seam now also has a dedicated regression target around `ImGuiWindowUICompositor`, covering initialization, render-frame ordering, Win32 message forwarding, texture registration forwarding, and shutdown safety.
- The legacy compatibility seam now also has dedicated regression coverage around `LegacyImGuiHostInterop`, covering compat factory wiring, ImGui input-capture forwarding, legacy font bootstrap, and the disabled demo-window path.
- The window compositor and hosted-preview seams gained more edge-case coverage around no-UI render passes, compositor re-initialization/rebinding, partial logical-size fallback, and descriptor reuse for repeated queued-frame keys.
- `new_editor` now has a dedicated `XCUIEditorCommandRouter` test target covering command registration, replacement, enable predicates, accelerator matching, and policy gates around focus/keyboard capture/text input.
- `Application` now integrates `XCUIEditorCommandRouter` into the shell itself:
- `View` menu items invoke routed commands instead of directly mutating shell state
- shell-level shortcuts now flow from `XCUIWin32InputSource` through `XCUIInputBridge` into command matching
- hosted-preview mode toggles still trigger presenter reconfiguration through the routed command bindings
- `new_editor` panel canvas ownership is now being split behind `IXCUIPanelCanvasHost`, with an `ImGuiXCUIPanelCanvasHost` adapter carrying the legacy path so panel code stops directly owning `ImGui::Image` / `ImGui::InvisibleButton` / draw-list preview plumbing.
- `XCUIDemoPanel` and `XCUILayoutLabPanel` no longer create an ImGui hosted-preview presenter or ImGui panel canvas host implicitly; default construction now stays on null/explicitly injected backends until the outer shell binds a concrete host adapter.
- `Application` now binds `ImGuiXCUIPanelCanvasHost` explicitly at the shell composition root, so the current ImGui panel host path is visible as a host-layer decision instead of a panel-layer fallback.
- `XCUIDemoPanel` and `XCUILayoutLabPanel` no longer read ImGui input directly; both now consume an injected `IXCUIInputSnapshotSource`, and the new `ImGuiXCUIInputSnapshotSource` keeps the current ImGui-backed input path isolated behind an explicit adapter.
- `new_editor` now also has a pure `XCUIShellChromeState` model with dedicated tests, covering shell panel visibility, hosted-preview mode, and shell view toggles without depending on ImGui or `Application`.
- `XCUIShellChromeState` now also exposes effective hosted-preview state helpers and shell view-toggle command-id helpers, so shell routing code no longer has to manually combine enablement and requested preview mode.
- `XCUIShellChromeState` hosted-preview modes were renamed away from `LegacyImGui`, so XCUI shell state no longer treats ImGui as the generic fallback concept.
- The panel-canvas seam now has dedicated null/compat/native coverage around stable debug names, passive-session safety, externally driven native snapshots, and compat-host factory creation without relying on explicit backend/capability reporting.
- The native host follow-up is now present in `new_editor`:
- `NativeWindowUICompositor` provides a swapchain-native XCUI packet present path beside the legacy ImGui compositor
- `Application` now defaults to that native host path and directly composes `XCUI Demo` plus `Layout Lab` into one native shell frame
- `NativeXCUIPanelCanvasHost` now drives externally configured native card sessions for that shell path and records hosted surface-image preview commands with preserved UVs
- new native compositor/native canvas-host tests now cover the new host seam
- `XCUIInputBridge.h` no longer includes `imgui.h`, so XCUI input translation is no longer coupled to ImGui at the public header boundary.
- `SceneRuntime` layered XCUI routing now has dedicated regression coverage for:
- top-interactive layer input ownership
- blocking/modal layer suppression of lower layers
- hidden top-layer pass-through back to visible underlying layers
- Shared `UITextInputController` coverage now includes more caret-boundary / modifier branches; the remaining `Delete` distinction stays blocked on `KeyCode::Delete` and `KeyCode::Backspace` still sharing the same enum value.
- Window compositor texture registration now also flows back into `Application` as XCUI-owned `UITextureRegistration` / `UITextureHandle` data instead of exposing raw `ImTextureID` there.
- Hosted preview contracts were tightened again:
- generic preview surface metadata stays on XCUI-owned value types
- `ImGuiTransitionBackend` moved behind `ImGuiXCUIHostedPreviewPresenter`
- generic preview frame submission no longer carries an ImGui draw-list pointer
- the ImGui presenter now resolves inline draw targets through an explicit ImGui-only binding seam
- panel/runtime callers still preserve the same legacy and native-preview behavior
- Native hosted-preview publication milestone is now wired through the default shell path:
- `NativeWindowUICompositor` publishes hosted preview textures as SRV-backed XCUI registrations and frees them through the compositor seam
- `Application::BeginHostedPreviewFrameLifecycle(...)` now resets queue/registry state for both legacy and native frame paths
- hosted-preview surface readiness now keys on published texture availability instead of ImGui-style descriptor validity
- `BuildNativeShellDrawData(...)` now queues native preview frames for `XCUI Demo` / `LayoutLab`, while shell cards consume the previously published hosted-surface image with warming/live placeholder text
- native compositor and shell-command tests now cover the new publication / lifecycle guards
- `LayoutLab` now resolves editor collection widget taxonomy and metrics through shared `UIEditorCollectionPrimitives` helpers instead of duplicating the same tag and metric rules inside the sandbox runtime.
- `LayoutLab` runtime now consumes shared keyboard-navigation semantics for list/tree/property traversal, while the remaining panel-level key mapping is tracked as an editor-host integration gap rather than a runtime gap.
- `LayoutLab` panel now maps concrete arrow/home/end keys into the shared navigation model, with dedicated panel-level coverage proving that the sandbox UI can actually drive the runtime navigation seam end-to-end.
- `new_editor` panel/shell diagnostics improvements for hosted preview state.
- XCUI asset document loading changed to prefer direct source compilation before `ResourceManager` fallback for the sandbox path, fixing the LayoutLab crash.
- `UIDocumentCompiler.cpp` repaired enough to restore full local builds after the duplicated schema-helper regression.
- MSVC debug build hardening was tightened again so large parallel `engine` rebuilds stop tripping over compile-PDB contention.
- `XCUIStandaloneTextAtlasProvider` no longer uses ImGui font-atlas/internal baking helpers:
- atlas ownership now stays inside a standalone provider implementation built on Windows/GDI glyph rasterization
- default editor atlas prewarms the current supported nominal sizes, exposes both `RGBA32` and `Alpha8` atlas views, lazily inserts non-prebaked BMP glyphs, and falls back to `?` for unrasterizable codepoints
- standalone atlas coverage now includes reset/rebuild, non-nominal size resolution, lazy glyph insertion/fallback behavior, and smoke use without any ImGui context
- Legacy shell chrome / HUD rendering is now split out of the main `Application.cpp` translation unit:
- the direct `ImGui::*` shell rendering path now lives in a dedicated legacy-only `Application` implementation file
- the main `Application.cpp` native host path no longer directly includes `<imgui.h>`, reducing default-path compile-time coupling while the remaining main-target header/include cleanup stays open
- `new_editor` build composition is now split into main/native and compatibility slices:
- the main `XCNewEditor` target no longer compiles legacy ImGui shell/panel/backend source files directly
- legacy ImGui shell/panel/backend sources plus vendored ImGui sources now build behind a dedicated compatibility static library that the main executable links
- the main target no longer carries ImGui include directories or older editor bridge headers on its direct compile surface; those remain confined to the compatibility slice
- The default XCUI compile seam is now also cleaner:
- generic compositor headers no longer advertise compat-only `CreateImGui*` factories
- compat-only host/window compositor factories now live behind `LegacyImGuiHostInterop.*`
- public font bootstrap headers no longer leak `ImFont` / `ImFontAtlas` types into generic XCUI consumers
## Phase Risks Still Open
- Schema instance validation is still open beyond `.xcschema` self-definition and artifact round-trip coverage.
- `ScrollView` is still authored/static; no wheel-driven scrolling or virtualization yet.
- `XCNewEditor` no longer depends on ImGui at the default-path header/include level and no longer links `XCNewEditorImGuiCompat`; the legacy shell/panel path now lives only in the standalone compatibility slice.
- Legacy panel implementations such as `XCUIDemoPanel` / `XCUILayoutLabPanel` still render as ImGui windows inside the compatibility slice, so editor-layer behavior is not yet fully carried by XCUI-native shell composition.
- The default native text path now owns its atlas without ImGui, but the provider is still Windows-only and remains trapped inside `new_editor` instead of a shared/cross-platform text layer.
- Hosted-preview compatibility presentation still depends on an ImGui-only inline presenter path when not using the queued native surface path.
- Editor widget coverage is still prototype-driven inside `LayoutLab`; it has not yet been promoted into a full reusable shared widget/runtime layer with command routing, virtualization, and property-edit transactions.
## Execution-Plan Alignment
- Against `XCUI完整架构设计与执行计划.md`, current `new_editor` progress should be treated as an early `Phase 8` foothold rather than full `Milestone E` completion:
- landed: `NativeWindowUICompositor`, native shell packet composition, native hosted-preview publication, XCUI-owned texture registrations, native panel surface-image presentation, standalone native text-atlas ownership inside `new_editor`, legacy `Application` TU split, `XCNewEditorImGuiCompat`, main-target header/include isolation from ImGui, default-executable unlinking from the compatibility slice, compat-only factory/font seam tightening, shared native shell layout helpers, and shared panel-chrome/flat-hierarchy helpers
- not yet landed: promotion of the native text-atlas path into a shared/cross-platform text subsystem, shared XCUI-authored editor shell chrome, and retirement of legacy ImGui-window panel implementations
- That means the next de-ImGui push should not keep centering on hosted-preview publication; that milestone is now effectively closed for the default native shell path.
- The real remaining default-path blockers are:
- move editor-layer behavior off the legacy ImGui-window panel implementations and into native/XCUI shell composition
- keep compat-only factories/types from drifting back into generic XCUI seams while the compatibility slice remains linked
- harden and promote `XCUIStandaloneTextAtlasProvider` / editor font bootstrap into a shared native text subsystem
- move native shell chrome out of bespoke `Application` layout code and into a shared XCUI shell model or authored shell document
## Next Phase
1. Expand runtime/game-layer ownership from the current `SceneRuntime` UI context into scene-declared HUD/menu bootstrapping, draw submission, and higher-level runtime UI policies.
2. Promote the current editor-facing widget prototypes out of authored `LayoutLab` content and into reusable XCUI widget/runtime modules, then continue with toolbar/menu chrome, shell-state adoption, virtualization, and broader focus/multi-selection behavior.
3. Push the remaining de-ImGui cleanup from build/header isolation into behavior isolation: keep ImGui confined to `XCNewEditorImGuiCompat`, keep generic XCUI seams free of compat-only factories/types, and keep shrinking the legacy shell boundary.
4. Promote the current standalone native text/font path out of `new_editor`, harden its atlas invalidation/caching contract, and keep the remaining font/bootstrap ownership compatibility-only.
5. Promote the native shell chrome and card layout out of bespoke `Application` code into a shared XCUI/editor-layer shell model or authored shell document, then retire the remaining ImGui-window panel path.
6. Continue phased validation, commit, push, and plan refresh after each stable batch.

View File

@@ -1,93 +0,0 @@
# XCUI Subplan 01Core 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 01XCUI 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 可以基于该模块定义控件树和状态更新

View File

@@ -1,50 +0,0 @@
# XCUI Subplan 02Layout 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`、更完整容器族、文本测量桥接

View File

@@ -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 APIImGui 仅存在于过渡 backend 和 editor 接入层
- 后续 `Subplan-08``Subplan-09` 可以直接基于这套 draw data 和 transition backend 继续推进
原始 subplan 内容归档如下:
# Subplan 05XCUI 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 适配层
- 可以渲染基础控件和文本