Files
XCEngine/docs/plan/NewEditor_PortBoundaryRefactorPlan_2026-04-23.md

6.3 KiB

NewEditor Port Boundary Refactor Plan

Date: 2026-04-23 Status: In Progress

1. Objective

Refactor new_editor so that external boundaries are modeled explicitly instead of being mixed into a single app/Ports bucket.

This plan targets three concrete issues:

  1. SystemInteractionPort is a platform/system boundary.
  2. TexturePort and ViewportRenderPort are rendering host boundaries.
  3. ShaderResourceDescriptorAllocatorPort is not a real application boundary because it leaks d3d12.h types directly into the interface.

The goal is to make the directory structure match the actual architecture, reduce "add another port" cargo cult growth, and make later editor features cheaper to extend.

2. Current Problems

2.1 Mixed abstraction levels

app/Ports currently mixes:

  1. operating-system integration
  2. rendering-host integration
  3. backend-internal D3D12 helpers

These are not the same architectural seam and should not share one bucket.

2.2 The word "Port" is being used too broadly

The current structure encourages a bad rule:

"If something crosses a layer, add a port."

That is not the intended use of ports. A port should exist only for a real boundary contract, not for every data handoff or helper collaboration.

2.3 Fake abstraction in D3D12 descriptor allocation

ShaderResourceDescriptorAllocatorPort exposes:

  1. D3D12_CPU_DESCRIPTOR_HANDLE
  2. D3D12_GPU_DESCRIPTOR_HANDLE
  3. UINT
  4. direct D3D12 header dependency

That makes it a D3D12-specific helper wearing an interface costume. It should be concrete backend code unless there is a proven need for a cross-backend contract.

3. Target End State

After the refactor:

  1. system-facing services live in a system-oriented location and namespace
  2. rendering host contracts live under rendering host boundaries
  3. D3D12-only helpers stay D3D12-local unless a backend-agnostic contract is justified
  4. new features do not add interfaces by default; they only add one when a real host boundary appears

4. Boundary Rules

Use a boundary interface only when all of the following are true:

  1. the caller depends on an external capability
  2. dependency inversion is useful at the application boundary
  3. the contract can be expressed without leaking backend-only details
  4. there is a realistic need for substitution, testing, or multiple implementations

Do not add a boundary interface for:

  1. feature-to-feature communication
  2. panel-to-runtime state transfer
  3. helpers inside one rendering backend
  4. temporary include avoidance

5. Execution Phases

Phase A. Split the app/Ports bucket by responsibility

Create explicit homes for:

  1. system interaction service
  2. rendering host contracts

Then move call sites to the new locations and names.

Phase B. Remove the fake D3D12 port

Delete ShaderResourceDescriptorAllocatorPort and make the descriptor allocator a concrete D3D12 helper again.

Phase C. Clean up forwarding and naming

Replace Ports::... references with names that explain the real role:

  1. System::SystemInteractionService
  2. Rendering::Host::UiTextureHost
  3. Rendering::Host::ViewportRenderHost

Phase D. Validate and harden

  1. ensure no remaining includes depend on app/Ports
  2. ensure the app still builds
  3. document the rule for future extension

6. File Plan

New files

  1. new_editor/app/System/SystemInteractionService.h
  2. new_editor/app/Rendering/Host/HostFwd.h
  3. new_editor/app/Rendering/Host/UiTextureHost.h
  4. new_editor/app/Rendering/Host/ViewportRenderHost.h

Files expected to be updated

  1. new_editor/app/Bootstrap/Application.h
  2. new_editor/app/Bootstrap/Application.cpp
  3. new_editor/app/Composition/EditorContext.h
  4. new_editor/app/Composition/EditorContext.cpp
  5. new_editor/app/Composition/EditorShellRuntime.h
  6. new_editor/app/Composition/EditorShellRuntime.cpp
  7. new_editor/app/Features/Project/ProjectPanel.h
  8. new_editor/app/Features/Project/ProjectPanel.cpp
  9. new_editor/app/Features/Scene/SceneViewportFeature.h
  10. new_editor/app/Features/Scene/SceneViewportFeature.cpp
  11. new_editor/app/Features/Scene/SceneViewportController.h
  12. new_editor/app/Features/Scene/SceneViewportController.cpp
  13. new_editor/app/Features/Scene/SceneViewportToolOverlay.h
  14. new_editor/app/Features/Scene/SceneViewportToolOverlay.cpp
  15. new_editor/app/Rendering/Assets/BuiltInIcons.h
  16. new_editor/app/Rendering/Assets/BuiltInIcons.cpp
  17. new_editor/app/Rendering/D3D12/D3D12UiTextureHost.h
  18. new_editor/app/Rendering/D3D12/D3D12WindowRenderer.h
  19. new_editor/app/Rendering/D3D12/D3D12ShaderResourceDescriptorAllocator.h
  20. new_editor/app/Rendering/Viewport/ViewportHostService.h
  21. new_editor/app/Rendering/Viewport/ViewportHostService.cpp
  22. new_editor/app/Rendering/Viewport/ViewportRenderTargets.h
  23. new_editor/app/Rendering/Viewport/ViewportRenderTargets.cpp
  24. new_editor/app/Support/EmbeddedPngLoader.h
  25. new_editor/app/Support/EmbeddedPngLoader.cpp
  26. new_editor/app/Platform/Win32/System/Win32SystemInteractionHost.h
  27. new_editor/app/Platform/Win32/Content/EditorWindowContentController.h

Files expected to be retired

  1. new_editor/app/Ports/SystemInteractionPort.h
  2. new_editor/app/Ports/TexturePort.h
  3. new_editor/app/Ports/ViewportRenderPort.h
  4. new_editor/app/Ports/PortFwd.h
  5. new_editor/app/Ports/ShaderResourceDescriptorAllocatorPort.h

7. First Execution Slice

Execute the smallest high-value slice first:

  1. introduce the new system and rendering host interface headers
  2. switch call sites away from Ports::
  3. remove ShaderResourceDescriptorAllocatorPort
  4. leave behavior unchanged

This keeps risk lower while immediately fixing the worst architectural confusion.

8. Validation

Minimum validation for this refactor:

  1. rg "Ports::|Ports/" new_editor/app returns no production references
  2. ShaderResourceDescriptorAllocatorPort no longer exists
  3. XCUIEditorApp compiles, or any build failure is isolated and reported precisely

9. Completion Criteria

This refactor is complete only when:

  1. app/Ports is no longer the active boundary bucket
  2. system and rendering host boundaries are visibly separated
  3. D3D12-only helpers are no longer presented as generic app ports
  4. future contributors can answer "where should a new boundary go?" without guessing