7.0 KiB
EditorEngineServices Refactor Plan
Problem
The root problem is no longer that EngineEditorServices.cpp used to be too large.
That implementation has already been split. The real architectural problem is
still EditorEngineServices itself:
- it is a god interface
- it mixes scene backend creation, viewport bridge logic, shader loading, object-id resolving, and engine lifecycle
- editor subsystems still depend on one shared service-locator style entrypoint
- new bridge needs will keep accumulating in
editor/app/Services/Engineunless that dependency surface is actively reduced
This refactor must therefore optimize for dependency reduction, not file splitting.
Target State
The goal is not "no bridge code".
The goal is "distributed, narrow bridges at clear boundaries instead of one central editor-to-engine gateway".
The target editor-side interfaces are:
EditorSceneBackendFactory- only
CreateSceneBackend
- only
SceneViewportEngineBridge- only scene viewport frame-plan build, render, and object-id resolve
GameViewportEngineBridge- only game viewport frame-plan build and render
EditorShaderProvider- only shader or resource acquisition
EditorEngineLifecycle- only
UpdateAsyncLoadsandShutdown
- only
If EngineEditorServices.cpp still exists at the end, it may only exist as an
internal composition-shell implementation. It must not remain a shared runtime
dependency across editor features.
Freeze Rule
EditorEngineServices is now a compatibility shell under freeze:
- no new public methods
- no new feature bridges added to it
- no new panels, tools, passes, or runtimes depending on it
- only subtraction is allowed: move capabilities out of it
Without this rule, the refactor will fail even if some files get cleaner.
Current Consumer Map
Refactoring must follow actual consumers, not folder aesthetics.
EditorContext
Current usage:
CreateSceneBackend
Conclusion:
- it does not need viewport, shader, game, or lifecycle capabilities
- it should depend only on
EditorSceneBackendFactory
File:
editor/app/Composition/EditorContext.cpp
SceneViewportRenderService
Current usage:
BuildSceneViewportFramePlanRenderSceneViewportFramePlanTryResolveActiveSceneRenderObjectId
Conclusion:
- it should depend only on
SceneViewportEngineBridge
File:
editor/app/Rendering/Viewport/SceneViewportRenderService.cpp
GameViewportRenderService
Current usage:
BuildGameViewportFramePlansRenderGameViewportFramePlans
Conclusion:
- it should depend only on
GameViewportEngineBridge
File:
editor/app/Rendering/Viewport/GameViewportRenderService.cpp
Scene Viewport Passes
Current usage:
LoadShader
Affected code:
SceneViewportGridPassSceneViewportSelectionOutlinePass
Conclusion:
- low-level render passes are taking a full engine facade just to load shaders
- this is the wrong dependency direction
- they should depend only on
EditorShaderProvider
Files:
editor/app/Rendering/Viewport/Passes/SceneViewportGridPass.cppeditor/app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp
Application
Current usage:
UpdateAsyncLoadsShutdown
Conclusion:
- it should depend only on
EditorEngineLifecycle
File:
editor/app/Bootstrap/Application.cpp
Execution Order
This work should proceed in dependency order, not in arbitrary file order.
Phase A. Freeze the Interface
Goals:
- document
EditorEngineServicesas a temporary compatibility shell - enforce the "no new methods" rule in code review and follow-up work
Acceptance:
EditorEngineServices.hclearly states the compatibility-shell role- no new consumers are introduced
Phase B. Split by Consumer
Goals:
- remove the easy, high-signal consumers from the god interface first
Execution order:
EditorContext->EditorSceneBackendFactorySceneViewportRenderService->SceneViewportEngineBridgeGameViewportRenderService->GameViewportEngineBridgeApplication->EditorEngineLifecycle
Why this order:
- low behavior risk
- mostly editor-side injection cleanup
- removes the largest legitimate consumers first
Acceptance:
- those four consumers no longer include or store
EditorEngineServices - each one is injected with a narrow dependency
Phase C. Remove Low-Level Resource Leakage
Goals:
- remove
LoadShaderreach from low-level viewport passes
Execution order:
- introduce
EditorShaderProvider - migrate
SceneViewportGridPass - migrate
SceneViewportSelectionOutlinePass
Acceptance:
- scene viewport passes no longer include
EditorEngineServices.h - shader access flows through a narrow provider
Phase D. Collapse the Compatibility Shell
Goals:
- remove the public architectural role of
EditorEngineServices
Allowed end states:
- delete
EditorEngineServices - keep only an internal composition object that does not appear in feature, runtime, or pass dependencies
Disallowed fake end states:
- split implementation files but keep the same shared public facade forever
- rename the god interface without shrinking its surface
Non-Goals
This plan intentionally does not include:
- mixing this work with
EditorSceneComponentMutationtyped-command refactors - copying Unreal Engine module structure mechanically
- removing all bridge code as a principle
- large behavior rewrites when dependency shrinkage is sufficient
Code Movement Rules
All follow-up implementation should obey these rules:
- interfaces are named by consumer boundary, not by generic service language
- each interface exposes only the methods that one consumer actually uses
- render passes must not depend on scene backend factory or lifecycle code
- composition owns assembly; features and passes must not locate services on their own
- bridge code is acceptable only when it stays narrow, local, and scoped to a concrete boundary
Completion Criteria
This refactor is complete only when all of the following are true:
EditorContextno longer depends onEditorEngineServicesSceneViewportRenderServiceno longer depends onEditorEngineServicesGameViewportRenderServiceno longer depends onEditorEngineServicesApplicationno longer depends onEditorEngineServices- scene viewport passes no longer depend on
EditorEngineServices EditorEngineServiceshas gained no new methodseditor/app/Services/Enginehas stopped being the default dumping ground for every editor-engine bridge
Immediate Next Cut
The next cut is not "split EngineEditorServices.cpp again".
The correct next cut is:
- define
EditorSceneBackendFactory - make
EditorContextdepend only on that interface - remove scene backend creation from the
EditorEngineServicesconsumer surface
Why this is next:
- it is the safest cut
- it has the narrowest dependency
- it starts emptying
EditorEngineServicesfrom the top of the editor stack - it creates the migration template for viewport, game, lifecycle, and shader provider splits