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

8.4 KiB

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