phase 1 runtime complete
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m46s
CI / Windows Release Package (push) Successful in 2m59s

This commit is contained in:
Aiden
2026-05-11 02:23:01 +10:00
parent cbf1b541dc
commit 9cbb5d8004
20 changed files with 265 additions and 1288 deletions

View File

@@ -6,7 +6,7 @@ Phase checklist:
- [x] Define subsystem boundaries and target architecture
- [ ] Introduce an internal event model
- [ ] Split `RuntimeHost`
- [x] Split `RuntimeHost`
- [ ] Make the render thread the sole GL owner
- [ ] Refactor live state layering into an explicit composition model
- [ ] Move persistence onto a background snapshot writer
@@ -32,9 +32,9 @@ Those points are important because they affect not just average performance, but
## Key Findings
### 1. `RuntimeHost` is carrying too many responsibilities
### 1. The original runtime host carried too many responsibilities
`RuntimeHost` currently acts as:
The original `RuntimeHost` acted as:
- config store
- persistent state store
@@ -47,7 +47,7 @@ That makes it a single contention and failure domain. It is also why OSC and ren
Relevant code:
- [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:15)
- `RuntimeHost.h`
Recommended direction:
@@ -258,7 +258,7 @@ Recommended direction:
Relevant code:
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1841)
- `RuntimeHost.cpp`
Recent OSC work already reduced this problem for live automation, but the broader architecture would still benefit from:
@@ -365,7 +365,7 @@ Status:
- Design deliverable: complete.
- Compatibility seams in code: partially complete and expanding.
- Target boundary extraction: not complete; remaining work is tracked by later phases, especially the event model, `RuntimeHost` split, render ownership, and persistence work.
- Target boundary extraction: not complete across the whole app; remaining work is tracked by later phases, especially the event model, render ownership, and persistence work.
Target split:
@@ -453,9 +453,9 @@ Suggested outcome:
- the app stops relying on “shared object plus mutex plus polling” as the default coordination pattern
### Phase 3. Split `RuntimeHost` into persistent state, render snapshot state, and service-facing coordination
### Phase 3. Finish live-state and service-facing coordination
After the event model exists, break apart `RuntimeHost`.
After the event model exists, finish separating live committed state and service-facing coordination from the runtime facades.
Recommended split:
@@ -628,7 +628,7 @@ If this is approached as a serious architecture program rather than opportunisti
1. Define subsystem boundaries and target architecture.
2. Introduce the internal event model.
3. Split `RuntimeHost`.
3. Finish runtime live-state/service coordination.
4. Make the render thread the sole GL owner.
5. Formalize live state layering and composition.
6. Move persistence to a background snapshot writer.
@@ -647,7 +647,7 @@ This order tries to avoid doing foundational work twice.
## Short Version
The app is in a much better place than it was before the OSC timing work, but the main remaining architectural risk is still shared ownership. Too many responsibilities converge on `RuntimeHost` and the shared GL path. The most sensible path forward is:
The app is in a much better place than it was before the OSC timing work, but the main remaining architectural risk is still shared ownership around the shared GL path. The most sensible path forward is:
1. define boundaries
2. establish an event model

View File

@@ -1,6 +1,6 @@
# Phase 1 Design: Subsystem Boundaries and Target Architecture
This document expands Phase 1 of [ARCHITECTURE_RESILIENCE_REVIEW.md](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/docs/ARCHITECTURE_RESILIENCE_REVIEW.md) into a concrete target design. Its purpose is to define the long-term subsystem split before later phases introduce a full event model, split `RuntimeHost`, and move rendering onto a sole-owner render thread.
This document expands Phase 1 of [ARCHITECTURE_RESILIENCE_REVIEW.md](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/docs/ARCHITECTURE_RESILIENCE_REVIEW.md) into a concrete target design. Its purpose is to define the long-term subsystem split before later phases introduce a full event model and move rendering onto a sole-owner render thread.
The main goal of Phase 1 is not to immediately rewrite the app. It is to establish clear ownership boundaries so later refactors all move toward the same architecture instead of solving local problems in conflicting ways.
@@ -9,15 +9,15 @@ The main goal of Phase 1 is not to immediately rewrite the app. It is to establi
Phase 1 has two different meanings in this repo, and they should not be collapsed:
- Phase 1 design package: complete.
- Phase 1 target extraction in code: in progress.
- Phase 1 runtime target extraction in code: largely complete.
The completed design package includes the agreed subsystem names, responsibilities, dependency rules, state categories, and current-to-target migration map. The codebase also has concrete compatibility seams for those subsystems. That is different from saying every target boundary is fully extracted: several subsystems still delegate through compatibility helpers, and later roadmap phases are still responsible for the event model, remaining `RuntimeHost` split, sole-owner render thread, explicit live-state layering, background persistence, backend state machine, and fuller telemetry.
The completed design package includes the agreed subsystem names, responsibilities, dependency rules, state categories, and current-to-target migration map. The runtime code now has concrete subsystem folders and collaborators for those boundaries. That is different from saying every target boundary is fully extracted across the whole app: later roadmap phases are still responsible for the event model, sole-owner render thread, explicit live-state layering, background persistence, backend state machine, and fuller telemetry.
## Why Phase 1 Exists
Today the app works, but too many responsibilities still converge in a few places:
At the start of this phase the app worked, but too many responsibilities converged in a few places:
- `RuntimeHost` owns persistence, live layer state, shader package access, status reporting, and mutation entrypoints.
- `RuntimeHost` owned persistence, live layer state, shader package access, status reporting, and mutation entrypoints.
- `OpenGLComposite` coordinates runtime setup, render state retrieval, shader rebuild handling, transient OSC overlay behavior, and video backend integration.
- DeckLink callback-driven playout still reaches directly into render-facing work.
- Background services rely on polling and shared mutable state more than explicit subsystem contracts.
@@ -54,9 +54,9 @@ This phase is the target design and the dependency rules. Later phases perform t
The following current code paths are the strongest evidence for the split proposed here:
- `RuntimeHost` is both store and live authority:
- [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:15)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:726)
- `RuntimeHost` was both store and live authority:
- `RuntimeHost.h`
- `RuntimeHost.cpp`
- `OpenGLComposite` is both app orchestrator and render/runtime coordinator:
- [OpenGLComposite.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp:106)
- [OpenGLComposite.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp:283)
@@ -575,7 +575,7 @@ Core responsibilities:
This is not a one-to-one rename plan. It is a responsibility migration map.
### Current `RuntimeHost`
### Previous `RuntimeHost`
Should eventually split across:
@@ -635,7 +635,7 @@ Likely examples:
As later phases begin, these rules should be treated as guardrails.
### 1. No new cross-cutting state should be added to `RuntimeHost`
### 1. No new cross-cutting runtime object should be introduced
If a new feature needs durable state, place it conceptually under `RuntimeStore`.
If it needs render-local transient state, place it conceptually under `RenderEngine`.
@@ -664,7 +664,7 @@ Phase 1 is a design phase, but it should support incremental migration.
Recommended order after this document:
1. Introduce names and interfaces before moving logic.
2. Create compatibility adapters around `RuntimeHost` rather than forcing a flag day.
2. Create compatibility adapters around the subsystem facades rather than forcing a flag day.
3. Move read-only render snapshot publication out before moving all mutation logic.
4. Move service ingress boundaries out before removing the old polling shell.
5. Isolate timing/health setters from the core store as early as practical.
@@ -679,7 +679,7 @@ Phase 1 can reasonably be considered complete once the project has:
- agreed subsystem names and responsibilities
- agreed allowed dependency directions
- explicit state categories: persisted, committed live, transient overlay, health/timing
- a current-to-target responsibility map for `RuntimeHost`, `RuntimeServices`, `OpenGLComposite`, and backend/render bridge code
- a current-to-target responsibility map for runtime services, `OpenGLComposite`, and backend/render bridge code
- a decision that later phases will build against this target rather than inventing new boundaries ad hoc
By that definition, the Phase 1 design deliverable is complete. The implementation should still be described as partially extracted until the compatibility backing objects and cross-subsystem shims are removed.

View File

@@ -19,10 +19,10 @@ Today, those responsibilities are fragmented across `RuntimeHost` status setters
The current code already contains meaningful health and timing signals, but they are spread through unrelated ownership domains:
- `RuntimeHost` stores signal and timing status:
- [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:41)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1353)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1415)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1441)
- `RuntimeHost.h`
- `RuntimeHost.cpp`
- `RuntimeHost.cpp`
- `RuntimeHost.cpp`
- render and bridge code report timing by writing back into `RuntimeHost`:
- [OpenGLRenderPipeline.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLRenderPipeline.cpp:50)
- [OpenGLVideoIOBridge.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp:49)
@@ -386,10 +386,10 @@ These are the clearest existing candidates:
See:
- [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:41)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1353)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1415)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1441)
- `RuntimeHost.h`
- `RuntimeHost.cpp`
- `RuntimeHost.cpp`
- `RuntimeHost.cpp`
In the target architecture, this kind of state should no longer sit on the same object that owns persistent layer truth.

View File

@@ -9,8 +9,8 @@ This document defines the target design for the `RuntimeCoordinator` subsystem i
Today the app's mutation path is split across several places:
- `RuntimeHost` performs validation, mutation, persistence, render-state invalidation, and some status updates:
- [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:15)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:891)
- `RuntimeHost.h`
- `RuntimeHost.cpp`
- `OpenGLComposite` currently acts like an orchestration shell and a mutation coordinator at the same time:
- [OpenGLCompositeRuntimeControls.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLCompositeRuntimeControls.cpp:1)
- `RuntimeServices` still owns some deferred control flow around OSC commit and polling:
@@ -67,7 +67,7 @@ This is the main policy surface that is currently spread between `RuntimeHost` m
- `ApplyOscTargetByControlKey(...)`
- `ResetLayerParameters(...)`
See [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:15).
See `RuntimeHost.h`.
### 3. State classification
@@ -375,7 +375,7 @@ That "call host, then decide reload/broadcast policy" logic is a direct candidat
- persistence writes
- render-state dirty marking
Examples in [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:891):
Examples in `RuntimeHost.cpp`:
- `AddLayer(...)`
- `SetLayerShader(...)`

View File

@@ -16,11 +16,11 @@ It exists to solve three current problems:
Today the closest current behavior lives in:
- [RuntimeHost::GetLayerRenderStates(...)](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1535)
- [RuntimeHost::TryGetLayerRenderStates(...)](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1543)
- [RuntimeHost::TryRefreshCachedLayerStates(...)](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1554)
- [RuntimeHost::RefreshDynamicRenderStateFields(...)](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1582)
- [RuntimeHost::BuildLayerRenderStatesLocked(...)](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1598)
- `RuntimeHost::GetLayerRenderStates(...)`
- `RuntimeHost::TryGetLayerRenderStates(...)`
- `RuntimeHost::TryRefreshCachedLayerStates(...)`
- `RuntimeHost::RefreshDynamicRenderStateFields(...)`
- `RuntimeHost::BuildLayerRenderStatesLocked(...)`
- the render-side cache usage in [OpenGLComposite.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp:589)
`RuntimeSnapshotProvider` should absorb that responsibility, but in a cleaner and more publish-oriented way.

View File

@@ -301,22 +301,22 @@ Today, `RuntimeHost` contains most of the responsibilities that should move into
Key current code paths:
- config load:
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1651)
- `RuntimeHost.cpp`
- persistent state load:
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1748)
- `RuntimeHost.cpp`
- persistent state save:
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1842)
- `RuntimeHost.cpp`
- preset save/load:
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1286)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1304)
- `RuntimeHost.cpp`
- `RuntimeHost.cpp`
- state serialization helpers:
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:2061)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:2172)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:2268)
- `RuntimeHost.cpp`
- `RuntimeHost.cpp`
- `RuntimeHost.cpp`
- path and file helpers:
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1988)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:2002)
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:2034)
- `RuntimeHost.cpp`
- `RuntimeHost.cpp`
- `RuntimeHost.cpp`
Durable-state mutation entrypoints that currently live on `RuntimeHost` but conceptually split between coordinator and store:

View File

@@ -570,7 +570,7 @@ The most important migration is:
- remove render work from `PlayoutFrameCompleted()`
### Current `RuntimeHost` Status Updates
### Previous Runtime Status Updates
Frame pacing and signal status setters currently called from the bridge should ultimately be routed through: