Files
XCEngine/docs/plan/NewEditor_FrameLifecycleRuntimeRefactorPlan_2026-04-22.md

252 lines
8.4 KiB
Markdown
Raw Normal View History

# NewEditor Frame/Lifecycle Runtime Refactor Plan
Date: 2026-04-22
## 1. Objective
This plan targets the highest-priority remaining architecture debt in `new_editor`:
1. frame execution still has multiple live owners
2. window destruction still has multiple live owners
3. `EditorShellRuntime` is still a per-frame god-object
The goal is not to split files mechanically.
The goal is to collapse ownership so that:
1. only one module can drive a frame
2. only one module can finalize window death
3. `EditorShellRuntime` becomes a thin facade over explicit per-frame stages
## 2. Confirmed Root Issues
### 2.1 Frame scheduling is not single-owner
`RenderFrame()` is currently called from:
- main host render loop
- `WM_PAINT`
- `WM_SIZE`
- `WM_DPICHANGED`
- `WM_EXITSIZEMOVE`
- borderless chrome transition logic
This means rendering is still driven by both:
1. the normal frame loop
2. synchronous Win32 message-side side effects
As long as this remains true, frame timing, transfer request ordering, and present timing
will keep depending on which path happened to trigger first.
### 2.2 Window lifetime is not single-owner
`Shutdown()`, `DestroyWindow(...)`, `MarkDestroyed()`, and host erase currently happen from
multiple places in `WindowManager`, host runtime, and workspace coordination.
This means there is still no single authoritative answer to:
1. who requests close
2. who observes native window destruction
3. who shuts runtime down
4. who removes the managed window from the host container
This is the same root class of problem that previously caused sub-window close regressions.
### 2.3 EditorShellRuntime still owns too many per-frame responsibilities
`EditorShellRuntime::Update(...)` still performs, in one place:
1. shell definition build
2. workspace/session sync
3. dock host interaction update
4. viewport request/update
5. hosted panel input routing
6. hosted panel update
7. status/trace/command-focus synchronization
That makes the runtime difficult to reason about and causes unrelated per-frame concerns to
stay tightly coupled.
## 3. Target End State
After this refactor:
1. only one frame driver can execute `BeginFrame -> UpdateAndAppend -> Present`
2. Win32 message dispatch only records frame requests / dirty reasons, never renders directly
3. borderless chrome logic can request a new frame, but cannot render directly
4. only one lifecycle coordinator can finalize managed window destruction
5. `WorkspaceCoordinator` can request a window-set mutation, but cannot directly destroy windows
6. `EditorShellRuntime` becomes a thin composition facade over smaller stage modules
## 4. New Modules
### 4.1 Frame ownership
Add:
- `new_editor/app/Platform/Win32/EditorWindowFrameDriver.h`
- `new_editor/app/Platform/Win32/EditorWindowFrameDriver.cpp`
- `new_editor/app/Platform/Win32/EditorWindowFrameRequest.h`
Responsibilities:
- own the only live path that executes a frame
- consume frame request reasons / dirty state
- normalize when transfer requests are collected and applied
### 4.2 Lifecycle ownership
Add:
- `new_editor/app/Platform/Win32/WindowManager/EditorWindowLifecycleCoordinator.h`
- `new_editor/app/Platform/Win32/WindowManager/EditorWindowLifecycleCoordinator.cpp`
Responsibilities:
- own close request state
- own runtime shutdown sequencing
- own native destroy finalization and managed erase
- own primary-window cascade behavior
### 4.3 Shell runtime stage split
Add:
- `new_editor/app/Composition/EditorShellSessionCoordinator.h`
- `new_editor/app/Composition/EditorShellSessionCoordinator.cpp`
- `new_editor/app/Composition/EditorShellInteractionEngine.h`
- `new_editor/app/Composition/EditorShellInteractionEngine.cpp`
- `new_editor/app/Composition/EditorShellHostedPanelCoordinator.h`
- `new_editor/app/Composition/EditorShellHostedPanelCoordinator.cpp`
- `new_editor/app/Composition/EditorShellDrawComposer.h`
- `new_editor/app/Composition/EditorShellDrawComposer.cpp`
Responsibilities:
- `SessionCoordinator`: context/workspace/session/command-focus/status synchronization
- `InteractionEngine`: shell definition, dock host interaction, shell frame/state update
- `HostedPanelCoordinator`: hosted panel input filtering, panel update, panel capture ownership
- `DrawComposer`: shell draw packets, panel draw packets, viewport frame application to draw data
## 5. Refactor Strategy
### Phase A. Collapse frame execution to one driver
Refactor current flow so that:
1. `EditorWindowHostRuntime` requests frame execution through `EditorWindowFrameDriver`
2. `EditorWindowMessageDispatcher` no longer calls `RenderFrame(...)` directly
3. `EditorWindowChromeController` no longer calls `RenderFrame(...)` directly
4. `EditorWindow` no longer exposes uncontrolled render entrypoints to unrelated callers
Message-side behavior must become:
- mark dirty
- enqueue reason
- invalidate host window when needed
But never:
- begin a frame
- build draw data
- present directly
### Phase B. Collapse lifecycle to one owner
Refactor current flow so that:
1. `WM_CLOSE` only means `RequestClose`
2. `WM_DESTROY` only reports native death
3. `EditorWindowLifecycleCoordinator` becomes the only place that can:
- call `Shutdown()`
- finalize managed destruction
- erase the managed window from host storage
- cascade close from the primary window
4. `WorkspaceCoordinator` stops duplicating host destroy logic
### Phase C. Split EditorShellRuntime by stage, not by helper dumping
Do not split by random utility extraction.
Split by stage ownership:
1. session sync stage
2. shell interaction stage
3. hosted panel stage
4. draw composition stage
`EditorShellRuntime` should remain as a thin orchestrating facade that wires these stages
together in a stable order.
### Phase D. Normalize panel capture ownership
As part of hosted panel coordination:
1. stop hardcoding panel capture checks directly in the runtime facade where possible
2. centralize panel capture ownership through one hosted-panel coordination boundary
3. make it explicit which hosted panels participate in pointer-stream ownership decisions
## 6. File-Level Impact
### 6.1 Existing files to shrink / re-scope
- `new_editor/app/Platform/Win32/EditorWindow.cpp`
- `new_editor/app/Platform/Win32/EditorWindow.h`
- `new_editor/app/Platform/Win32/EditorWindowChromeController.cpp`
- `new_editor/app/Platform/Win32/WindowManager/EditorWindowHostRuntime.cpp`
- `new_editor/app/Platform/Win32/WindowManager/EditorWindowHostRuntime.h`
- `new_editor/app/Platform/Win32/WindowManager/EditorWindowMessageDispatcher.cpp`
- `new_editor/app/Platform/Win32/WindowManager/EditorWindowWorkspaceCoordinator.cpp`
- `new_editor/app/Composition/EditorShellRuntime.cpp`
- `new_editor/app/Composition/EditorShellRuntime.h`
### 6.2 Build graph
Update:
- `new_editor/CMakeLists.txt`
to register the new frame/lifecycle/runtime stage units.
## 7. Validation
### 7.1 Structural validation
After completion:
1. direct `RenderFrame(...)` call sites outside the frame driver must be gone
2. duplicate `Shutdown()/DestroyWindow()/MarkDestroyed()/erase` chains must be gone
3. `WorkspaceCoordinator` must no longer perform raw window destruction
4. `EditorShellRuntime` facade must delegate to explicit stage modules
### 7.2 Runtime validation
Must validate:
1. editor starts normally
2. main window continues rendering normally
3. resize / maximize / drag-restore no longer perform direct message-side rendering
4. detached panel windows and tool windows still open/close correctly
5. closing child windows does not close the main window
6. closing the primary window still cascades correctly
7. frame transfer requests still behave correctly during docking / detach workflows
## 8. Execution Order
1. introduce frame request model and frame driver
2. remove message-side and chrome-side direct rendering
3. introduce lifecycle coordinator
4. remove duplicate destroy/shutdown ownership from host runtime and workspace coordinator
5. split `EditorShellRuntime` into session/interaction/panel/draw stages
6. rebuild and smoke-test
7. perform structural grep validation
## 9. Success Criteria
This refactor is complete only if all of the following are true:
1. `new_editor` frame execution has a single live owner
2. `new_editor` managed window destruction has a single live owner
3. `EditorShellRuntime` is no longer a monolithic per-frame god-object
4. resize / dpi / chrome transitions no longer trigger direct rendering from message handlers
5. build and smoke validation both pass