Phase 5 step 1
Some checks failed
CI / React UI Build (push) Successful in 11s
CI / Windows Release Package (push) Has been cancelled
CI / Native Windows Build And Tests (push) Has been cancelled

This commit is contained in:
Aiden
2026-05-11 18:53:59 +10:00
parent e8a3805fff
commit 77590f4a62
8 changed files with 410 additions and 5 deletions

View File

@@ -119,6 +119,8 @@ set(APP_SOURCES
"${APP_DIR}/runtime/events/RuntimeEventType.h" "${APP_DIR}/runtime/events/RuntimeEventType.h"
"${APP_DIR}/runtime/live/RenderStateComposer.cpp" "${APP_DIR}/runtime/live/RenderStateComposer.cpp"
"${APP_DIR}/runtime/live/RenderStateComposer.h" "${APP_DIR}/runtime/live/RenderStateComposer.h"
"${APP_DIR}/runtime/live/RuntimeStateLayerModel.cpp"
"${APP_DIR}/runtime/live/RuntimeStateLayerModel.h"
"${APP_DIR}/runtime/live/RuntimeLiveState.cpp" "${APP_DIR}/runtime/live/RuntimeLiveState.cpp"
"${APP_DIR}/runtime/live/RuntimeLiveState.h" "${APP_DIR}/runtime/live/RuntimeLiveState.h"
"${APP_DIR}/runtime/presentation/RuntimeStateJson.cpp" "${APP_DIR}/runtime/presentation/RuntimeStateJson.cpp"
@@ -316,6 +318,23 @@ endif()
add_test(NAME RuntimeLiveStateTests COMMAND RuntimeLiveStateTests) add_test(NAME RuntimeLiveStateTests COMMAND RuntimeLiveStateTests)
add_executable(RuntimeStateLayerModelTests
"${APP_DIR}/runtime/live/RuntimeStateLayerModel.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RuntimeStateLayerModelTests.cpp"
)
target_include_directories(RuntimeStateLayerModelTests PRIVATE
"${APP_DIR}"
"${APP_DIR}/runtime"
"${APP_DIR}/runtime/live"
)
if(MSVC)
target_compile_options(RuntimeStateLayerModelTests PRIVATE /W3)
endif()
add_test(NAME RuntimeStateLayerModelTests COMMAND RuntimeStateLayerModelTests)
add_executable(RuntimeSubsystemTests add_executable(RuntimeSubsystemTests
"${APP_DIR}/runtime/coordination/RuntimeCoordinator.cpp" "${APP_DIR}/runtime/coordination/RuntimeCoordinator.cpp"
"${APP_DIR}/runtime/snapshot/RenderSnapshotBuilder.cpp" "${APP_DIR}/runtime/snapshot/RenderSnapshotBuilder.cpp"

View File

@@ -0,0 +1,230 @@
#include "RuntimeStateLayerModel.h"
const char* RuntimeStateLayerKindName(RuntimeStateLayerKind kind)
{
switch (kind)
{
case RuntimeStateLayerKind::BasePersisted:
return "base persisted";
case RuntimeStateLayerKind::CommittedLive:
return "committed live";
case RuntimeStateLayerKind::TransientAutomation:
return "transient automation";
case RuntimeStateLayerKind::RenderLocal:
return "render local";
case RuntimeStateLayerKind::HealthConfig:
return "health/config";
default:
return "unknown";
}
}
int RuntimeStateLayerCompositionPrecedence(RuntimeStateLayerKind kind)
{
switch (kind)
{
case RuntimeStateLayerKind::BasePersisted:
return 0;
case RuntimeStateLayerKind::CommittedLive:
return 1;
case RuntimeStateLayerKind::TransientAutomation:
return 2;
case RuntimeStateLayerKind::RenderLocal:
case RuntimeStateLayerKind::HealthConfig:
default:
return -1;
}
}
bool RuntimeStateLayerIsDurable(RuntimeStateLayerKind kind)
{
return kind == RuntimeStateLayerKind::BasePersisted;
}
bool RuntimeStateLayerParticipatesInParameterComposition(RuntimeStateLayerKind kind)
{
return RuntimeStateLayerCompositionPrecedence(kind) >= 0;
}
bool RuntimeStateLayerIsRenderLocal(RuntimeStateLayerKind kind)
{
return kind == RuntimeStateLayerKind::RenderLocal;
}
RuntimeStateLayerKind ClassifyRuntimeStateField(RuntimeStateField field)
{
switch (field)
{
case RuntimeStateField::PersistedLayerStack:
case RuntimeStateField::PersistedParameterValues:
case RuntimeStateField::StackPresets:
return RuntimeStateLayerKind::BasePersisted;
case RuntimeStateField::CommittedSessionParameterValues:
case RuntimeStateField::CommittedLayerBypass:
case RuntimeStateField::RuntimeCompileReloadFlags:
return RuntimeStateLayerKind::CommittedLive;
case RuntimeStateField::TransientOscOverlay:
case RuntimeStateField::TransientAutomationCommitState:
return RuntimeStateLayerKind::TransientAutomation;
case RuntimeStateField::RenderLocalTemporalHistory:
case RuntimeStateField::RenderLocalFeedbackState:
case RuntimeStateField::RenderLocalInputFrames:
case RuntimeStateField::RenderLocalOutputFrames:
return RuntimeStateLayerKind::RenderLocal;
case RuntimeStateField::RuntimeConfiguration:
case RuntimeStateField::HealthTelemetry:
default:
return RuntimeStateLayerKind::HealthConfig;
}
}
std::vector<RuntimeStateLayerDescriptor> GetRuntimeStateLayerInventory()
{
return {
{
RuntimeStateLayerKind::BasePersisted,
"Base persisted state",
"RuntimeStore / LayerStackStore",
"Survives restart",
"Written to disk",
"Default layer stack, shader selections, saved parameter values"
},
{
RuntimeStateLayerKind::CommittedLive,
"Committed live state",
"RuntimeCoordinator, physically backed by RuntimeStore during migration",
"Current running session",
"May request persistence depending on mutation policy",
"Operator/session truth until changed again"
},
{
RuntimeStateLayerKind::TransientAutomation,
"Transient automation overlay",
"RuntimeLiveState / RuntimeServiceLiveBridge",
"High-rate and short-lived",
"Not persisted directly",
"Temporary OSC/automation target applied over committed truth"
},
{
RuntimeStateLayerKind::RenderLocal,
"Render-local state",
"RenderEngine",
"Render-thread/resource lifetime",
"Not persisted",
"Temporal history, feedback, input/output queues, and GL-local caches"
},
{
RuntimeStateLayerKind::HealthConfig,
"Health/config state",
"RuntimeConfigStore / HealthTelemetry",
"Config survives restart; health is observational",
"Config is file-backed; health is reported, not composed",
"Does not participate in parameter composition"
}
};
}
std::vector<RuntimeStateFieldDescriptor> GetRuntimeStateFieldInventory()
{
return {
{
RuntimeStateField::PersistedLayerStack,
ClassifyRuntimeStateField(RuntimeStateField::PersistedLayerStack),
"persisted layer stack",
"LayerStackStore",
"Durable layer order, ids, shader selections, and bypass flags"
},
{
RuntimeStateField::PersistedParameterValues,
ClassifyRuntimeStateField(RuntimeStateField::PersistedParameterValues),
"persisted parameter values",
"LayerStackStore",
"Saved parameter values used as the baseline for snapshots and presets"
},
{
RuntimeStateField::StackPresets,
ClassifyRuntimeStateField(RuntimeStateField::StackPresets),
"stack presets",
"RuntimeStore / LayerStackStore",
"Durable preset files and preset serialization shape"
},
{
RuntimeStateField::CommittedSessionParameterValues,
ClassifyRuntimeStateField(RuntimeStateField::CommittedSessionParameterValues),
"committed session parameter values",
"RuntimeCoordinator policy, RuntimeStore backing during migration",
"Operator/API truth after accepted mutations"
},
{
RuntimeStateField::CommittedLayerBypass,
ClassifyRuntimeStateField(RuntimeStateField::CommittedLayerBypass),
"committed layer bypass",
"RuntimeCoordinator policy, RuntimeStore backing during migration",
"Current operator/API bypass state"
},
{
RuntimeStateField::RuntimeCompileReloadFlags,
ClassifyRuntimeStateField(RuntimeStateField::RuntimeCompileReloadFlags),
"runtime compile/reload flags",
"RuntimeCoordinator / RuntimeUpdateController",
"Session coordination state used to request snapshot or render rebuild work"
},
{
RuntimeStateField::TransientOscOverlay,
ClassifyRuntimeStateField(RuntimeStateField::TransientOscOverlay),
"transient OSC overlays",
"RuntimeLiveState",
"High-rate automation values applied above committed state"
},
{
RuntimeStateField::TransientAutomationCommitState,
ClassifyRuntimeStateField(RuntimeStateField::TransientAutomationCommitState),
"transient automation commit state",
"RuntimeLiveState / RuntimeServiceLiveBridge",
"Generation and completion bookkeeping for settled overlay commits"
},
{
RuntimeStateField::RenderLocalTemporalHistory,
ClassifyRuntimeStateField(RuntimeStateField::RenderLocalTemporalHistory),
"render-local temporal history",
"RenderEngine",
"GL/resource history that must stay out of parameter layering"
},
{
RuntimeStateField::RenderLocalFeedbackState,
ClassifyRuntimeStateField(RuntimeStateField::RenderLocalFeedbackState),
"render-local feedback state",
"RenderEngine",
"Feedback buffers and ping-pong resources"
},
{
RuntimeStateField::RenderLocalInputFrames,
ClassifyRuntimeStateField(RuntimeStateField::RenderLocalInputFrames),
"render-local input frames",
"RenderEngine",
"Latest accepted input frame payloads and upload staging"
},
{
RuntimeStateField::RenderLocalOutputFrames,
ClassifyRuntimeStateField(RuntimeStateField::RenderLocalOutputFrames),
"render-local output frames",
"RenderEngine",
"Readback, packed output, screenshot, and preview staging"
},
{
RuntimeStateField::RuntimeConfiguration,
ClassifyRuntimeStateField(RuntimeStateField::RuntimeConfiguration),
"runtime configuration",
"RuntimeConfigStore",
"File-backed config, not a live parameter layer"
},
{
RuntimeStateField::HealthTelemetry,
ClassifyRuntimeStateField(RuntimeStateField::HealthTelemetry),
"health telemetry",
"HealthTelemetry",
"Operational observations, not source state for render values"
}
};
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include <string>
#include <vector>
enum class RuntimeStateLayerKind
{
BasePersisted,
CommittedLive,
TransientAutomation,
RenderLocal,
HealthConfig
};
enum class RuntimeStateField
{
PersistedLayerStack,
PersistedParameterValues,
StackPresets,
CommittedSessionParameterValues,
CommittedLayerBypass,
RuntimeCompileReloadFlags,
TransientOscOverlay,
TransientAutomationCommitState,
RenderLocalTemporalHistory,
RenderLocalFeedbackState,
RenderLocalInputFrames,
RenderLocalOutputFrames,
RuntimeConfiguration,
HealthTelemetry
};
struct RuntimeStateLayerDescriptor
{
RuntimeStateLayerKind kind = RuntimeStateLayerKind::BasePersisted;
const char* name = "";
const char* owner = "";
const char* lifetime = "";
const char* persistence = "";
const char* renderRole = "";
};
struct RuntimeStateFieldDescriptor
{
RuntimeStateField field = RuntimeStateField::PersistedLayerStack;
RuntimeStateLayerKind layerKind = RuntimeStateLayerKind::BasePersisted;
const char* name = "";
const char* currentOwner = "";
const char* notes = "";
};
const char* RuntimeStateLayerKindName(RuntimeStateLayerKind kind);
int RuntimeStateLayerCompositionPrecedence(RuntimeStateLayerKind kind);
bool RuntimeStateLayerIsDurable(RuntimeStateLayerKind kind);
bool RuntimeStateLayerParticipatesInParameterComposition(RuntimeStateLayerKind kind);
bool RuntimeStateLayerIsRenderLocal(RuntimeStateLayerKind kind);
RuntimeStateLayerKind ClassifyRuntimeStateField(RuntimeStateField field);
std::vector<RuntimeStateLayerDescriptor> GetRuntimeStateLayerInventory();
std::vector<RuntimeStateFieldDescriptor> GetRuntimeStateFieldInventory();

View File

@@ -7,8 +7,8 @@ Phase 1 named the subsystems. Phase 2 added the typed event substrate. Phase 3 m
## Status ## Status
- Phase 5 design package: proposed. - Phase 5 design package: proposed.
- Phase 5 implementation: not started. - Phase 5 implementation: Step 1 started.
- Current alignment: Phase 3 introduced the first pure composition boundary and transient OSC overlay owner, but committed runtime values are still physically stored through `RuntimeStore`/`LayerStackStore`, and transient OSC overlay state is applied through render-facing helpers rather than through a first-class layered state model. - Current alignment: Phase 3 introduced the first pure composition boundary and transient OSC overlay owner, and Phase 5 now has a small `RuntimeStateLayerModel` inventory that names the current state categories. Committed runtime values are still physically stored through `RuntimeStore`/`LayerStackStore`, and transient OSC overlay state is still applied through render-facing helpers rather than through the final layered composition contract.
Current live-state footholds: Current live-state footholds:
@@ -18,6 +18,7 @@ Current live-state footholds:
- `RuntimeLiveState` owns transient OSC overlay bookkeeping, smoothing, generation tracking, and commit-settlement policy. - `RuntimeLiveState` owns transient OSC overlay bookkeeping, smoothing, generation tracking, and commit-settlement policy.
- `RenderStateComposer` combines base render states with live overlay state and returns final per-frame layer states plus settled commit requests. - `RenderStateComposer` combines base render states with live overlay state and returns final per-frame layer states plus settled commit requests.
- `RuntimeServiceLiveBridge` drains OSC ingress/completion queues and applies them to render live state during frame preparation. - `RuntimeServiceLiveBridge` drains OSC ingress/completion queues and applies them to render live state during frame preparation.
- `RuntimeStateLayerModel` names the Phase 5 state categories and classifies current fields as base persisted, committed live, transient automation, render-local, or health/config state.
## Why Phase 5 Exists ## Why Phase 5 Exists
@@ -239,9 +240,9 @@ Document and/or encode where each current state category lives:
Initial target: Initial target:
- identify which fields are durable, committed-live, transient automation, render-local, or health/config - [x] identify which fields are durable, committed-live, transient automation, render-local, or health/config
- update subsystem docs where the current ownership is misleading - [x] update subsystem docs where the current ownership is misleading
- add small tests for classification if a pure helper exists - [x] add small tests for classification if a pure helper exists
### Step 2. Name The Layered Composition Input ### Step 2. Name The Layered Composition Input

View File

@@ -109,6 +109,8 @@ Examples:
This state should remain render-local even when it influences visible output. This state should remain render-local even when it influences visible output.
Phase 5's `RuntimeStateLayerModel` explicitly keeps temporal history, feedback state, accepted input frames, staged output frames, preview staging, and screenshot/readback staging in the render-local category. These are deliberately outside the persisted/committed/transient-automation parameter composition rule.
### 5. Shader Build Application ### 5. Shader Build Application
Compilation itself may eventually move into a separate build service, but once shader build outputs exist, `RenderEngine` owns: Compilation itself may eventually move into a separate build service, but once shader build outputs exist, `RenderEngine` owns:

View File

@@ -80,6 +80,8 @@ The coordinator decides which state category a mutation affects:
The design rule is that classification belongs here, not in the ingress layer and not in render code. The design rule is that classification belongs here, not in the ingress layer and not in render code.
Phase 5 has started codifying the shared vocabulary for this classification in `RuntimeStateLayerModel`. The current model records committed session parameter values, layer bypass state, and runtime compile/reload flags as committed-live/session coordination state, even though some of those values are still physically backed by `RuntimeStore` during migration.
### 4. Snapshot publication requests ### 4. Snapshot publication requests
When a mutation changes render-facing state, the coordinator asks `RuntimeSnapshotProvider` to publish a new snapshot or mark one dirty for publication. When a mutation changes render-facing state, the coordinator asks `RuntimeSnapshotProvider` to publish a new snapshot or mark one dirty for publication.

View File

@@ -95,6 +95,8 @@ Those are coordinator concerns, not store concerns.
`RuntimeStore` should own the following state categories. `RuntimeStore` should own the following state categories.
Phase 5 names this boundary in code through `RuntimeStateLayerModel`: persisted layer stack data, saved parameter values, and stack presets are classified as base persisted state. Operator/session values may still be backed by the store during migration, but their mutation policy is committed-live policy owned by the coordinator, not durable-store policy by default.
### Runtime Configuration ### Runtime Configuration
Examples: Examples:

View File

@@ -0,0 +1,88 @@
#include "RuntimeStateLayerModel.h"
#include <iostream>
namespace
{
int gFailures = 0;
void Expect(bool condition, const char* message)
{
if (condition)
return;
std::cerr << "FAIL: " << message << "\n";
++gFailures;
}
void TestLayerPrecedence()
{
Expect(RuntimeStateLayerCompositionPrecedence(RuntimeStateLayerKind::BasePersisted) == 0,
"base persisted state is the first composition layer");
Expect(RuntimeStateLayerCompositionPrecedence(RuntimeStateLayerKind::CommittedLive) == 1,
"committed live state overlays persisted state");
Expect(RuntimeStateLayerCompositionPrecedence(RuntimeStateLayerKind::TransientAutomation) == 2,
"transient automation overlays committed state");
Expect(!RuntimeStateLayerParticipatesInParameterComposition(RuntimeStateLayerKind::RenderLocal),
"render-local state does not participate in parameter composition");
Expect(!RuntimeStateLayerParticipatesInParameterComposition(RuntimeStateLayerKind::HealthConfig),
"health/config state does not participate in parameter composition");
}
void TestFieldClassification()
{
Expect(ClassifyRuntimeStateField(RuntimeStateField::PersistedLayerStack) == RuntimeStateLayerKind::BasePersisted,
"layer stack is base persisted state");
Expect(ClassifyRuntimeStateField(RuntimeStateField::PersistedParameterValues) == RuntimeStateLayerKind::BasePersisted,
"saved parameters are base persisted state");
Expect(ClassifyRuntimeStateField(RuntimeStateField::CommittedSessionParameterValues) == RuntimeStateLayerKind::CommittedLive,
"session parameter values are committed live state");
Expect(ClassifyRuntimeStateField(RuntimeStateField::RuntimeCompileReloadFlags) == RuntimeStateLayerKind::CommittedLive,
"compile/reload flags are committed session coordination state");
Expect(ClassifyRuntimeStateField(RuntimeStateField::TransientOscOverlay) == RuntimeStateLayerKind::TransientAutomation,
"OSC overlays are transient automation state");
Expect(ClassifyRuntimeStateField(RuntimeStateField::TransientAutomationCommitState) == RuntimeStateLayerKind::TransientAutomation,
"overlay commit generations are transient automation state");
Expect(ClassifyRuntimeStateField(RuntimeStateField::RenderLocalTemporalHistory) == RuntimeStateLayerKind::RenderLocal,
"temporal history is render-local state");
Expect(ClassifyRuntimeStateField(RuntimeStateField::RenderLocalFeedbackState) == RuntimeStateLayerKind::RenderLocal,
"feedback state is render-local state");
Expect(ClassifyRuntimeStateField(RuntimeStateField::RuntimeConfiguration) == RuntimeStateLayerKind::HealthConfig,
"runtime configuration is outside parameter composition");
Expect(ClassifyRuntimeStateField(RuntimeStateField::HealthTelemetry) == RuntimeStateLayerKind::HealthConfig,
"health telemetry is outside parameter composition");
}
void TestInventoriesStayInSyncWithEnums()
{
const std::vector<RuntimeStateLayerDescriptor> layers = GetRuntimeStateLayerInventory();
Expect(layers.size() == 5, "layer inventory names all Phase 5 state categories");
const std::vector<RuntimeStateFieldDescriptor> fields = GetRuntimeStateFieldInventory();
Expect(fields.size() == 14, "field inventory names current state categories");
for (const RuntimeStateFieldDescriptor& field : fields)
{
Expect(field.layerKind == ClassifyRuntimeStateField(field.field),
"field inventory layer kind matches classifier");
Expect(field.name != nullptr && field.name[0] != '\0', "field inventory has a display name");
Expect(field.currentOwner != nullptr && field.currentOwner[0] != '\0', "field inventory has an owner");
}
}
}
int main()
{
TestLayerPrecedence();
TestFieldClassification();
TestInventoriesStayInSyncWithEnums();
if (gFailures != 0)
{
std::cerr << gFailures << " RuntimeStateLayerModel test failure(s).\n";
return 1;
}
std::cout << "RuntimeStateLayerModel tests passed.\n";
return 0;
}