Phase 5 step 1
This commit is contained in:
@@ -119,6 +119,8 @@ set(APP_SOURCES
|
||||
"${APP_DIR}/runtime/events/RuntimeEventType.h"
|
||||
"${APP_DIR}/runtime/live/RenderStateComposer.cpp"
|
||||
"${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.h"
|
||||
"${APP_DIR}/runtime/presentation/RuntimeStateJson.cpp"
|
||||
@@ -316,6 +318,23 @@ endif()
|
||||
|
||||
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
|
||||
"${APP_DIR}/runtime/coordination/RuntimeCoordinator.cpp"
|
||||
"${APP_DIR}/runtime/snapshot/RenderSnapshotBuilder.cpp"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -7,8 +7,8 @@ Phase 1 named the subsystems. Phase 2 added the typed event substrate. Phase 3 m
|
||||
## Status
|
||||
|
||||
- Phase 5 design package: proposed.
|
||||
- Phase 5 implementation: not 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.
|
||||
- Phase 5 implementation: Step 1 started.
|
||||
- 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:
|
||||
|
||||
@@ -18,6 +18,7 @@ Current live-state footholds:
|
||||
- `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.
|
||||
- `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
|
||||
|
||||
@@ -239,9 +240,9 @@ Document and/or encode where each current state category lives:
|
||||
|
||||
Initial target:
|
||||
|
||||
- identify which fields are durable, committed-live, transient automation, render-local, or health/config
|
||||
- update subsystem docs where the current ownership is misleading
|
||||
- add small tests for classification if a pure helper exists
|
||||
- [x] identify which fields are durable, committed-live, transient automation, render-local, or health/config
|
||||
- [x] update subsystem docs where the current ownership is misleading
|
||||
- [x] add small tests for classification if a pure helper exists
|
||||
|
||||
### Step 2. Name The Layered Composition Input
|
||||
|
||||
|
||||
@@ -109,6 +109,8 @@ Examples:
|
||||
|
||||
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
|
||||
|
||||
Compilation itself may eventually move into a separate build service, but once shader build outputs exist, `RenderEngine` owns:
|
||||
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
|
||||
When a mutation changes render-facing state, the coordinator asks `RuntimeSnapshotProvider` to publish a new snapshot or mark one dirty for publication.
|
||||
|
||||
@@ -95,6 +95,8 @@ Those are coordinator concerns, not store concerns.
|
||||
|
||||
`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
|
||||
|
||||
Examples:
|
||||
|
||||
88
tests/RuntimeStateLayerModelTests.cpp
Normal file
88
tests/RuntimeStateLayerModelTests.cpp
Normal 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user