docs(new_editor): archive legacy closure plan
This commit is contained in:
196
docs/plan/NewEditor_ArchitectureClosurePlan_2026-04-22.md
Normal file
196
docs/plan/NewEditor_ArchitectureClosurePlan_2026-04-22.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# NewEditor Architecture Closure Plan
|
||||
|
||||
Date: 2026-04-22
|
||||
|
||||
## 1. Objective
|
||||
|
||||
彻底收口 `new_editor` 当前仍然残留的旧语义、冗余结构和重复控制面。
|
||||
|
||||
这次不是补丁式修正,而是把当前主架构重新收成单一路径:
|
||||
|
||||
1. 主窗口 UI 渲染只保留一条 `D3D12` 实例化 primitive pass。
|
||||
2. 启动截图策略只保留一个控制源。
|
||||
3. `Rendering/Native` 这层已经失效的旧模块边界被移除。
|
||||
4. 已确认无用的死代码和错误语义命名被清理。
|
||||
|
||||
## 2. Confirmed Root Issues
|
||||
|
||||
### 2.1 D3D12 UI renderer still contains a legacy geometry sub-pipeline
|
||||
|
||||
当前 `D3D12UiRenderer` 虽然已经是 `D3D12` 主路径,但内部仍然并存两套 UI 几何编译/提交策略:
|
||||
|
||||
1. `QuadInstanced`
|
||||
2. `LegacyIndexed`
|
||||
|
||||
其中绝大多数 UI 命令已经走实例化 primitive 路径,只有少量三角形命令仍然维持整套:
|
||||
|
||||
- legacy vertex buffer
|
||||
- legacy index buffer
|
||||
- legacy pipeline state
|
||||
- legacy batch kind
|
||||
|
||||
这会导致:
|
||||
|
||||
- UI renderer 不是单一路径
|
||||
- 缓冲管理、缓存结构、绘制分发继续为旧几何方案付复杂度成本
|
||||
- “legacy” 语义真正活着,而不是只有名字没改
|
||||
|
||||
### 2.2 Startup auto-capture ownership is split across two layers
|
||||
|
||||
当前 startup capture 既在 `EditorWindowScreenshotController::Initialize(...)` 内通过环境变量触发,
|
||||
又在 `EditorWindowRuntimeController::Initialize(...)` 内通过
|
||||
`autoCaptureOnStartup + env` 触发。
|
||||
|
||||
这导致:
|
||||
|
||||
- 策略决定权不是单点
|
||||
- `autoCaptureOnStartup` 形参不是唯一真实来源
|
||||
- 非主窗口也可能被环境变量隐式触发 startup request
|
||||
|
||||
### 2.3 Rendering/Native module boundary is obsolete
|
||||
|
||||
`app/Rendering/Native/` 目录现在只剩 `AutoScreenshot.*`。
|
||||
|
||||
它已经不再承担旧的 native renderer backend 职责,但目录和 include 关系仍然保留旧时代边界。
|
||||
|
||||
### 2.4 Confirmed dead code remains in the hot renderer unit
|
||||
|
||||
`D3D12UiRenderer.cpp` 里存在已无调用方的辅助函数,说明之前的旧几何阶段没有清理彻底。
|
||||
|
||||
## 3. Target End State
|
||||
|
||||
完成后应满足:
|
||||
|
||||
1. `D3D12UiRenderer` 不再存在 `LegacyIndexed` / `legacy*` 动态几何资源。
|
||||
2. 所有 UI draw command 都编译为同一类实例化 primitive 数据。
|
||||
3. 渲染时只绑定一套 UI primitive pipeline。
|
||||
4. `EditorWindowScreenshotController` 不再自行读取 startup env,也不再拥有策略判断权。
|
||||
5. startup capture 是否发生,只由窗口创建策略单点决定。
|
||||
6. `Rendering/Native` 目录被清空或完全移除。
|
||||
7. 已确认无调用的渲染死代码删除。
|
||||
|
||||
## 4. Refactor Strategy
|
||||
|
||||
### Phase A. Collapse UI geometry into one primitive pipeline
|
||||
|
||||
把当前 UI pass 改成单一实例化 primitive 方案:
|
||||
|
||||
- 保留单位 quad carrier vertex/index buffer
|
||||
- 删除动态 legacy vertex/index buffer
|
||||
- 删除 legacy pipeline state
|
||||
- 删除 legacy batch kind
|
||||
- 统一 batch 为 instance range + texture + scissor
|
||||
|
||||
需要新增的 primitive 能力:
|
||||
|
||||
- 现有 rect / outline / circle / text / image / gradient
|
||||
- 新增 triangle primitive,让 `FilledTriangle` 也走实例化路径
|
||||
|
||||
triangle 的正确实现目标:
|
||||
|
||||
- 不是回退到 indexed geometry
|
||||
- 不是临时特判 draw call
|
||||
- 而是作为实例化 primitive 的一种 kind,被统一编译、缓存、上传和绘制
|
||||
|
||||
### Phase B. Rebuild renderer data model around the unified path
|
||||
|
||||
同步调整:
|
||||
|
||||
- `UiBatchKind` 去除或退化
|
||||
- `UiQuadInstance` 升级为更通用的 `UiPrimitiveInstance`
|
||||
- `CompiledDrawList` 不再缓存 legacy vertices / indices
|
||||
- `EnsureFrameBufferCapacity(...)` 只维护 primitive instance buffer
|
||||
- render dispatch 只保留单一 pipeline bind 分支
|
||||
|
||||
### Phase C. Collapse startup capture ownership to one source of truth
|
||||
|
||||
把 startup capture 的策略权上移并单点化:
|
||||
|
||||
- `Application` 或窗口创建层读取 env / policy
|
||||
- `CreateParams.autoCaptureOnStartup` 成为唯一窗口级输入
|
||||
- `EditorWindowRuntimeController` 只消费该参数
|
||||
- `EditorWindowScreenshotController` 只做请求/路径/结果管理,不做策略判断
|
||||
|
||||
### Phase D. Remove obsolete Native module shell
|
||||
|
||||
将截图控制器从 `app/Rendering/Native/` 迁移到更合理的位置,
|
||||
预计是窗口运行时或通用 support/capture 模块。
|
||||
|
||||
要求:
|
||||
|
||||
- 迁移后 active include 不再引用 `Rendering/Native`
|
||||
- `new_editor/CMakeLists.txt` 同步更新
|
||||
|
||||
### Phase E. Delete dead helpers and normalize semantics
|
||||
|
||||
删除已经确认无调用的旧辅助函数,并统一命名:
|
||||
|
||||
- `legacy*` -> 按真实职责改名,或随着删除一起消失
|
||||
- 清理无用 helper
|
||||
- 保证 renderer 术语与当前架构一致
|
||||
|
||||
## 5. File-Level Work
|
||||
|
||||
### 5.1 UI renderer core
|
||||
|
||||
Primary:
|
||||
|
||||
- `new_editor/app/Rendering/D3D12/D3D12UiRenderer.h`
|
||||
- `new_editor/app/Rendering/D3D12/D3D12UiRenderer.cpp`
|
||||
|
||||
### 5.2 Startup capture boundary
|
||||
|
||||
Primary:
|
||||
|
||||
- `new_editor/app/Bootstrap/Application.cpp`
|
||||
- `new_editor/app/Platform/Win32/EditorWindow.cpp`
|
||||
- `new_editor/app/Platform/Win32/EditorWindowRuntimeController.h`
|
||||
- `new_editor/app/Platform/Win32/EditorWindowRuntimeController.cpp`
|
||||
- `new_editor/app/Platform/Win32/EditorWindowManager.h`
|
||||
|
||||
### 5.3 Screenshot controller relocation
|
||||
|
||||
Current source:
|
||||
|
||||
- `new_editor/app/Platform/Win32/EditorWindowScreenshotController.h`
|
||||
- `new_editor/app/Platform/Win32/EditorWindowScreenshotController.cpp`
|
||||
|
||||
Target:
|
||||
|
||||
- move into non-legacy module location
|
||||
|
||||
### 5.4 Build graph cleanup
|
||||
|
||||
- `new_editor/CMakeLists.txt`
|
||||
|
||||
## 6. Validation
|
||||
|
||||
### 6.1 Structural validation
|
||||
|
||||
完成后搜索应满足:
|
||||
|
||||
- no `LegacyIndexed` in active UI renderer
|
||||
- no `m_legacyPipelineState`
|
||||
- no `legacyVertexBuffer`
|
||||
- no `legacyIndexBuffer`
|
||||
- no `app/Rendering/Native/AutoScreenshot.*` active include path
|
||||
|
||||
### 6.2 Runtime validation
|
||||
|
||||
必须验证:
|
||||
|
||||
1. `XCUIEditorApp` Debug 构建通过
|
||||
2. editor 正常启动
|
||||
3. 主窗口 UI 正常显示与交互
|
||||
4. startup auto-capture 正常生成
|
||||
5. detached window 不会错误触发 startup capture
|
||||
|
||||
## 7. Completion Criteria
|
||||
|
||||
只有满足以下条件,这个 plan 才算真正收口:
|
||||
|
||||
1. UI 渲染路径内部不再存在旧几何子管线
|
||||
2. startup capture 策略控制权单点化
|
||||
3. 旧 `Native` 模块边界消失
|
||||
4. 死代码删除
|
||||
5. 构建、启动、截图验证全部通过
|
||||
@@ -1,274 +0,0 @@
|
||||
# NewEditor D3D12 Legacy D2D Closure Plan
|
||||
|
||||
Date: 2026-04-22
|
||||
|
||||
## 1. Problem Statement
|
||||
|
||||
`new_editor` main-window realtime presentation has already moved onto the native `D3D12` path, but the codebase still retains a legacy `D2D / D3D11On12` bridge in the host layer.
|
||||
|
||||
That residual path is no longer the main realtime hot path, but it is still architecturally serious because it keeps a second rendering/composition implementation alive for the same UI draw model.
|
||||
|
||||
Current residual seam:
|
||||
|
||||
- `app/Rendering/Native/NativeRenderer.*`
|
||||
- `app/Rendering/D3D12/D3D12WindowInteropContext.*`
|
||||
- `app/Rendering/D3D12/D3D12WindowInteropHelpers.h`
|
||||
- host/build references that still compile and link the old bridge
|
||||
|
||||
## 2. Confirmed Current Facts
|
||||
|
||||
### 2.1 Main window realtime presentation no longer depends on D2D
|
||||
|
||||
Main realtime presentation flows through:
|
||||
|
||||
- `D3D12WindowRenderer`
|
||||
- `D3D12UiRenderer`
|
||||
- `D3D12WindowRenderLoop`
|
||||
|
||||
The active main path does not call `NativeRenderer::Render(...)`.
|
||||
|
||||
### 2.2 Residual D2D bridge is concentrated, not scattered
|
||||
|
||||
The old bridge is now effectively concentrated in:
|
||||
|
||||
- screenshot/export flow
|
||||
- dead hwnd-native D2D window rendering code that is no longer wired into the main frame loop
|
||||
|
||||
### 2.3 NativeRenderer is no longer the main texture host
|
||||
|
||||
`TexturePort` for the active editor path is already `D3D12UiTextureHost`.
|
||||
|
||||
This means the remaining `NativeRenderer` responsibilities can be removed instead of preserved as an active subsystem.
|
||||
|
||||
## 3. Root Architectural Issue
|
||||
|
||||
The root issue is not “some D2D symbols still exist”.
|
||||
|
||||
The root issue is that `new_editor` still retains two incompatible host-side ways to turn UI output into pixels:
|
||||
|
||||
1. Active path: native `D3D12` rendering into the swapchain backbuffer.
|
||||
2. Residual path: `UIDrawData -> D2D replay`, with optional `D3D11On12` resource interop.
|
||||
|
||||
As long as both remain alive:
|
||||
|
||||
- draw semantics can drift between live output and capture/export output
|
||||
- future UI/rendering changes must be kept compatible with two host backends
|
||||
- dead-window code remains available to be accidentally reused
|
||||
- build/link dependencies preserve obsolete platform coupling
|
||||
|
||||
So the correct goal is full single-path closure, not incremental patching.
|
||||
|
||||
## 4. Target End State
|
||||
|
||||
After this refactor:
|
||||
|
||||
1. Main-window realtime rendering remains native `D3D12`.
|
||||
2. Main-window screenshot capture also uses native `D3D12` backbuffer capture.
|
||||
3. `new_editor` no longer contains any host-side `D3D11On12` or `D2D` bridge code for the main window.
|
||||
4. Old hwnd `D2D` renderer code is deleted, not merely disconnected.
|
||||
5. `XCUIEditorHost` no longer compiles obsolete interop units.
|
||||
6. Host link dependencies only retain what is still genuinely required.
|
||||
|
||||
## 5. Non-Goals
|
||||
|
||||
- Do not roll the editor back to `D2D`.
|
||||
- Do not reintroduce a fallback composition backend.
|
||||
- Do not change the active `D3D12UiRenderer` architecture back into draw-data replay through another API.
|
||||
- Do not remove `DirectWrite` from `D3D12UiTextSystem`; text shaping/raster generation remains valid there.
|
||||
|
||||
## 6. Refactor Strategy
|
||||
|
||||
### Phase A. Replace screenshot bridge with native D3D12 capture
|
||||
|
||||
Introduce a native `D3D12` capture path that reads the final swapchain backbuffer and writes PNG through CPU-side WIC encoding.
|
||||
|
||||
This capture path must:
|
||||
|
||||
- capture the final composited editor frame
|
||||
- avoid `UIDrawData` replay
|
||||
- avoid `D3D11On12`
|
||||
- avoid `D2D`
|
||||
- operate on the same swapchain backbuffer the user actually sees
|
||||
|
||||
Preferred integration point:
|
||||
|
||||
- after UI rendering has finished for the active backbuffer
|
||||
- after the frame has been submitted to the queue
|
||||
- before `PresentFrame()`
|
||||
|
||||
Reason:
|
||||
|
||||
- the frame is already fully rendered
|
||||
- the current backbuffer is still the one about to be presented
|
||||
- capture remains aligned with the user-visible final image
|
||||
|
||||
### Phase B. Collapse screenshot orchestration around the active D3D12 path
|
||||
|
||||
Rework `AutoScreenshotController` so it only:
|
||||
|
||||
- tracks pending capture requests
|
||||
- resolves output/history paths
|
||||
- finalizes success/error summaries
|
||||
|
||||
It must no longer own the rendering backend choice.
|
||||
|
||||
### Phase C. Delete obsolete NativeRenderer window-render path
|
||||
|
||||
Delete the old hwnd `D2D` window renderer behavior:
|
||||
|
||||
- `Initialize(HWND)`
|
||||
- `Resize(...)`
|
||||
- `Render(...)`
|
||||
- hwnd render-target management
|
||||
- D2D draw command replay entrypoint for window presentation
|
||||
|
||||
If a remaining utility is still needed after this phase, it must live in a narrowly-scoped replacement component, not inside a zombie renderer class.
|
||||
|
||||
### Phase D. Delete D3D11On12 interop bridge
|
||||
|
||||
Once native `D3D12` capture is live:
|
||||
|
||||
- delete `D3D12WindowInteropContext.*`
|
||||
- delete `D3D12WindowInteropHelpers.h`
|
||||
- delete all `NativeRenderer` interop code
|
||||
|
||||
### Phase E. Build and link closure
|
||||
|
||||
Update host build definitions so obsolete units and libraries are removed.
|
||||
|
||||
Expected removals if no remaining consumer exists:
|
||||
|
||||
- `app/Rendering/Native/NativeRenderer.cpp`
|
||||
- `app/Rendering/D3D12/D3D12WindowInteropContext.cpp`
|
||||
- `d2d1.lib`
|
||||
- `d3d11.lib`
|
||||
|
||||
Expected retained dependencies:
|
||||
|
||||
- `d3d12.lib`
|
||||
- `d3dcompiler.lib`
|
||||
- `dwrite.lib`
|
||||
- `dxgi.lib`
|
||||
- `windowscodecs.lib`
|
||||
|
||||
## 7. Concrete File-Level Work
|
||||
|
||||
### 7.1 Add new native capture unit
|
||||
|
||||
Add new host-side unit(s), likely under `app/Rendering/D3D12/`:
|
||||
|
||||
- `D3D12WindowCapture.h`
|
||||
- `D3D12WindowCapture.cpp`
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- copy current swapchain backbuffer to readback
|
||||
- map CPU pixels
|
||||
- encode PNG via WIC
|
||||
- return rich error text
|
||||
|
||||
### 7.2 Extend window renderer / render loop
|
||||
|
||||
Modify:
|
||||
|
||||
- `app/Rendering/D3D12/D3D12WindowRenderer.h`
|
||||
- `app/Rendering/D3D12/D3D12WindowRenderer.cpp`
|
||||
- `app/Rendering/D3D12/D3D12WindowRenderLoop.h`
|
||||
- `app/Rendering/D3D12/D3D12WindowRenderLoop.cpp`
|
||||
|
||||
Needed changes:
|
||||
|
||||
- expose a native capture operation on the active backbuffer
|
||||
- extend present result with capture outcome
|
||||
- allow present flow to execute capture before `PresentFrame()`
|
||||
|
||||
### 7.3 Rework screenshot controller boundary
|
||||
|
||||
Modify:
|
||||
|
||||
- `app/Rendering/Native/AutoScreenshot.h`
|
||||
- `app/Rendering/Native/AutoScreenshot.cpp`
|
||||
- `app/Platform/Win32/EditorWindowRuntimeController.h`
|
||||
- `app/Platform/Win32/EditorWindowRuntimeController.cpp`
|
||||
- `app/Platform/Win32/EditorWindow.cpp`
|
||||
|
||||
Needed changes:
|
||||
|
||||
- stop passing `UIDrawData` into screenshot rendering
|
||||
- stop depending on `NativeRenderer`
|
||||
- move screenshot triggering into the D3D12 present path
|
||||
|
||||
### 7.4 Delete obsolete renderer/interop code
|
||||
|
||||
Delete if fully unreferenced after the above:
|
||||
|
||||
- `app/Rendering/Native/NativeRenderer.h`
|
||||
- `app/Rendering/Native/NativeRenderer.cpp`
|
||||
- `app/Rendering/Native/NativeRendererHelpers.h`
|
||||
- `app/Rendering/D3D12/D3D12WindowInteropContext.h`
|
||||
- `app/Rendering/D3D12/D3D12WindowInteropContext.cpp`
|
||||
- `app/Rendering/D3D12/D3D12WindowInteropHelpers.h`
|
||||
|
||||
### 7.5 Build-system cleanup
|
||||
|
||||
Modify:
|
||||
|
||||
- `new_editor/CMakeLists.txt`
|
||||
|
||||
Remove:
|
||||
|
||||
- old source units
|
||||
- dead link libraries
|
||||
|
||||
## 8. Validation Requirements
|
||||
|
||||
### 8.1 Build validation
|
||||
|
||||
Must build:
|
||||
|
||||
- `XCUIEditorHost`
|
||||
- `XCUIEditorAppLib`
|
||||
- `XCUIEditorApp`
|
||||
|
||||
### 8.2 Runtime validation
|
||||
|
||||
Must validate:
|
||||
|
||||
1. editor launches normally
|
||||
2. main window still presents correctly
|
||||
3. manual screenshot still succeeds
|
||||
4. startup auto-capture still succeeds if enabled
|
||||
5. no screenshot path closes or stalls the main window unexpectedly
|
||||
|
||||
### 8.3 Structural validation
|
||||
|
||||
Searches after completion must show:
|
||||
|
||||
- no `NativeRenderer` references in `new_editor/app`
|
||||
- no `D3D12WindowInteropContext` references in `new_editor`
|
||||
- no `D3D11On12` references in `new_editor`
|
||||
- no `ID2D` / `D2D1` references in `new_editor/app`
|
||||
|
||||
Exception:
|
||||
|
||||
- `DirectWrite` references inside `D3D12UiTextSystem` remain allowed
|
||||
|
||||
## 9. Execution Order
|
||||
|
||||
1. Add native `D3D12` backbuffer capture path.
|
||||
2. Route `AutoScreenshotController` through that new path.
|
||||
3. Remove runtime-controller dependence on `NativeRenderer`.
|
||||
4. Delete `NativeRenderer` and `D3D12WindowInteropContext`.
|
||||
5. Clean CMake/link dependencies.
|
||||
6. Build and smoke-test.
|
||||
7. Run structural grep validation.
|
||||
|
||||
## 10. Success Criteria
|
||||
|
||||
This plan is complete only if all of the following are true:
|
||||
|
||||
- screenshots come from the native `D3D12` backbuffer path
|
||||
- no main-window host rendering code path can fall back to `D2D`
|
||||
- no `D3D11On12` bridge remains in `new_editor`
|
||||
- old dead window-render code is physically removed
|
||||
- build and smoke validation pass
|
||||
Reference in New Issue
Block a user