Phase 5 step 2
All checks were successful
CI / React UI Build (push) Successful in 10s
CI / Native Windows Build And Tests (push) Successful in 2m39s
CI / Windows Release Package (push) Successful in 2m54s

This commit is contained in:
Aiden
2026-05-11 18:56:39 +10:00
parent 77590f4a62
commit 7740fe209c
5 changed files with 103 additions and 56 deletions

View File

@@ -100,13 +100,13 @@ std::vector<RuntimeRenderState> RenderFrameStateResolver::ComposeLayerStates(
double smoothing, double smoothing,
std::vector<RuntimeLiveOscCommitRequest>* commitRequests) const std::vector<RuntimeLiveOscCommitRequest>* commitRequests) const
{ {
RenderStateCompositionInput input; LayeredRenderStateInput input;
input.baseLayerStates = &baseStates; input.committedLiveLayerStates = &baseStates;
input.liveState = &liveState; input.transientAutomationOverlay = &liveState;
input.allowLiveCommits = allowCommit; input.allowTransientAutomationCommits = allowCommit;
input.collectLiveCommitRequests = commitRequests != nullptr; input.collectTransientAutomationCommitRequests = commitRequests != nullptr;
input.liveSmoothing = smoothing; input.transientAutomationSmoothing = smoothing;
input.liveCommitDelay = kOscOverlayCommitDelay; input.transientAutomationCommitDelay = kOscOverlayCommitDelay;
input.now = std::chrono::steady_clock::now(); input.now = std::chrono::steady_clock::now();
const RenderStateCompositionResult result = mRenderStateComposer.BuildFrameState(input); const RenderStateCompositionResult result = mRenderStateComposer.BuildFrameState(input);

View File

@@ -1,24 +1,26 @@
#include "RenderStateComposer.h" #include "RenderStateComposer.h"
RenderStateCompositionResult RenderStateComposer::BuildFrameState(const RenderStateCompositionInput& input) const RenderStateCompositionResult RenderStateComposer::BuildFrameState(const LayeredRenderStateInput& input) const
{ {
RenderStateCompositionResult result; RenderStateCompositionResult result;
if (!input.baseLayerStates) const std::vector<RuntimeRenderState>* layerStates =
input.committedLiveLayerStates ? input.committedLiveLayerStates : input.basePersistedLayerStates;
if (!layerStates)
return result; return result;
result.layerStates = *input.baseLayerStates; result.layerStates = *layerStates;
result.hasLayerStates = !result.layerStates.empty(); result.hasLayerStates = !result.layerStates.empty();
if (input.liveState) if (input.transientAutomationOverlay)
{ {
RuntimeLiveStateApplyOptions options; RuntimeLiveStateApplyOptions options;
options.allowCommit = input.allowLiveCommits; options.allowCommit = input.allowTransientAutomationCommits;
options.smoothing = input.liveSmoothing; options.smoothing = input.transientAutomationSmoothing;
options.commitDelay = input.liveCommitDelay; options.commitDelay = input.transientAutomationCommitDelay;
options.now = input.now; options.now = input.now;
input.liveState->ApplyToLayerStates( input.transientAutomationOverlay->ApplyToLayerStates(
result.layerStates, result.layerStates,
options, options,
input.collectLiveCommitRequests ? &result.commitRequests : nullptr); input.collectTransientAutomationCommitRequests ? &result.commitRequests : nullptr);
} }
return result; return result;
} }

View File

@@ -5,14 +5,15 @@
#include <chrono> #include <chrono>
#include <vector> #include <vector>
struct RenderStateCompositionInput struct LayeredRenderStateInput
{ {
const std::vector<RuntimeRenderState>* baseLayerStates = nullptr; const std::vector<RuntimeRenderState>* basePersistedLayerStates = nullptr;
RuntimeLiveState* liveState = nullptr; const std::vector<RuntimeRenderState>* committedLiveLayerStates = nullptr;
bool allowLiveCommits = false; RuntimeLiveState* transientAutomationOverlay = nullptr;
bool collectLiveCommitRequests = true; bool allowTransientAutomationCommits = false;
double liveSmoothing = 0.0; bool collectTransientAutomationCommitRequests = true;
std::chrono::milliseconds liveCommitDelay = std::chrono::milliseconds(150); double transientAutomationSmoothing = 0.0;
std::chrono::milliseconds transientAutomationCommitDelay = std::chrono::milliseconds(150);
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
}; };
@@ -26,5 +27,5 @@ struct RenderStateCompositionResult
class RenderStateComposer class RenderStateComposer
{ {
public: public:
RenderStateCompositionResult BuildFrameState(const RenderStateCompositionInput& input) const; RenderStateCompositionResult BuildFrameState(const LayeredRenderStateInput& input) const;
}; };

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: Step 1 started. - Phase 5 implementation: Step 2 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 alignment: Phase 3 introduced the first pure composition boundary and transient OSC overlay owner. Phase 5 now has a small `RuntimeStateLayerModel` inventory that names the current state categories, and `RenderStateComposer` consumes a `LayeredRenderStateInput` whose fields make base persisted, committed live, and transient automation inputs explicit. Committed runtime values are still physically stored through `RuntimeStore`/`LayerStackStore`, and transient OSC overlay state is still applied through `RuntimeLiveState`.
Current live-state footholds: Current live-state footholds:
@@ -16,7 +16,7 @@ Current live-state footholds:
- `RuntimeCoordinator` owns mutation validation, classification, accepted/rejected event publication, snapshot/reload follow-ups, and the policy switch between committed states and live snapshots. - `RuntimeCoordinator` owns mutation validation, classification, accepted/rejected event publication, snapshot/reload follow-ups, and the policy switch between committed states and live snapshots.
- `RuntimeSnapshotProvider` publishes render-facing snapshots from committed runtime state. - `RuntimeSnapshotProvider` publishes render-facing snapshots from committed runtime state.
- `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` consumes `LayeredRenderStateInput`, chooses committed-live layer states over base-persisted layer states when both are supplied, applies transient automation on top, 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. - `RuntimeStateLayerModel` names the Phase 5 state categories and classifies current fields as base persisted, committed live, transient automation, render-local, or health/config state.
@@ -246,19 +246,18 @@ Initial target:
### Step 2. Name The Layered Composition Input ### Step 2. Name The Layered Composition Input
Introduce a named composition input model around the current `RenderStateCompositionInput`. Introduce a named composition input model around the previous `RenderStateCompositionInput`.
Initial target: Initial target:
- make base/committed/transient inputs visible in type names or field names - [x] make base/committed/transient inputs visible in type names or field names
- keep `RenderStateComposer` behavior unchanged at first - [x] keep `RenderStateComposer` behavior unchanged at first
- add tests that assert precedence with no GL - [x] add tests that assert precedence with no GL
Possible outcomes: Possible outcomes:
- evolve `RenderStateCompositionInput` - [x] add a new `LayeredRenderStateInput`
- add a new `LayeredRenderStateInput` - [ ] add a thin adapter if a later migration needs compatibility with the previous input shape
- add a thin adapter that feeds existing `RenderStateComposer`
### Step 3. Make Reset And Reload Policy Explicit ### Step 3. Make Reset And Reload Policy Explicit
@@ -366,9 +365,9 @@ Render-local resources such as temporal history, feedback buffers, readback cach
Phase 5 can be considered complete once the project can say: Phase 5 can be considered complete once the project can say:
- [ ] persisted, committed-live, and transient automation layers are named in code or clear read models - [x] persisted, committed-live, and transient automation layers are named in code or clear read models
- [ ] final render-value precedence is explicit and covered by tests - [x] final render-value precedence is explicit and covered by tests
- [ ] `RenderStateComposer` or its replacement consumes a layered input contract - [x] `RenderStateComposer` or its replacement consumes a layered input contract
- [ ] reset/reload/preset behavior for transient overlays is centralized or clearly delegated - [ ] reset/reload/preset behavior for transient overlays is centralized or clearly delegated
- [ ] OSC overlay settle/commit behavior is explicit, including persistence policy - [ ] OSC overlay settle/commit behavior is explicit, including persistence policy
- [ ] `RuntimeStore` remains durable-state focused and does not absorb transient automation policy - [ ] `RuntimeStore` remains durable-state focused and does not absorb transient automation policy

View File

@@ -359,12 +359,12 @@ void TestRenderStateComposerBuildsFrameState()
update.targetValue = JsonValue(0.6); update.targetValue = JsonValue(0.6);
liveState.ApplyOscUpdates({ update }); liveState.ApplyOscUpdates({ update });
RenderStateCompositionInput input; LayeredRenderStateInput input;
std::vector<RuntimeRenderState> baseLayerStates = { MakeLayerState() }; std::vector<RuntimeRenderState> baseLayerStates = { MakeLayerState() };
input.baseLayerStates = &baseLayerStates; input.committedLiveLayerStates = &baseLayerStates;
input.liveState = &liveState; input.transientAutomationOverlay = &liveState;
input.allowLiveCommits = false; input.allowTransientAutomationCommits = false;
input.liveSmoothing = 0.0; input.transientAutomationSmoothing = 0.0;
RenderStateComposer composer; RenderStateComposer composer;
RenderStateCompositionResult result = composer.BuildFrameState(input); RenderStateCompositionResult result = composer.BuildFrameState(input);
@@ -382,6 +382,49 @@ void TestRenderStateComposerBuildsFrameState()
"composer leaves base layer states unchanged"); "composer leaves base layer states unchanged");
} }
void TestRenderStateComposerUsesCommittedLayerOverBaseLayer()
{
std::vector<RuntimeRenderState> basePersistedLayerStates = { MakeLayerState() };
std::vector<RuntimeRenderState> committedLiveLayerStates = { MakeLayerState() };
committedLiveLayerStates[0].parameterValues["amount"].numberValues = { 0.4 };
LayeredRenderStateInput input;
input.basePersistedLayerStates = &basePersistedLayerStates;
input.committedLiveLayerStates = &committedLiveLayerStates;
RenderStateComposer composer;
RenderStateCompositionResult result = composer.BuildFrameState(input);
const auto valueIt = result.layerStates[0].parameterValues.find("amount");
Expect(valueIt != result.layerStates[0].parameterValues.end() &&
!valueIt->second.numberValues.empty() &&
std::fabs(valueIt->second.numberValues[0] - 0.4) < 0.0001,
"committed live layer overrides base persisted layer");
const auto baseValueIt = basePersistedLayerStates[0].parameterValues.find("amount");
Expect(baseValueIt != basePersistedLayerStates[0].parameterValues.end() &&
!baseValueIt->second.numberValues.empty() &&
std::fabs(baseValueIt->second.numberValues[0] - 0.25) < 0.0001,
"committed override leaves base persisted layer unchanged");
}
void TestRenderStateComposerUsesBaseLayerWhenCommittedLayerMissing()
{
std::vector<RuntimeRenderState> basePersistedLayerStates = { MakeLayerState() };
LayeredRenderStateInput input;
input.basePersistedLayerStates = &basePersistedLayerStates;
RenderStateComposer composer;
RenderStateCompositionResult result = composer.BuildFrameState(input);
Expect(result.hasLayerStates, "composer can use base persisted layer states without committed layer states");
const auto valueIt = result.layerStates[0].parameterValues.find("amount");
Expect(valueIt != result.layerStates[0].parameterValues.end() &&
!valueIt->second.numberValues.empty() &&
std::fabs(valueIt->second.numberValues[0] - 0.25) < 0.0001,
"base persisted value is used when no committed live value exists");
}
void TestRenderStateComposerQueuesCommitRequestsWhenEnabled() void TestRenderStateComposerQueuesCommitRequestsWhenEnabled()
{ {
RuntimeLiveState liveState; RuntimeLiveState liveState;
@@ -393,13 +436,13 @@ void TestRenderStateComposerQueuesCommitRequestsWhenEnabled()
liveState.ApplyOscUpdates({ update }); liveState.ApplyOscUpdates({ update });
std::vector<RuntimeRenderState> baseLayerStates = { MakeLayerState() }; std::vector<RuntimeRenderState> baseLayerStates = { MakeLayerState() };
RenderStateCompositionInput input; LayeredRenderStateInput input;
input.baseLayerStates = &baseLayerStates; input.committedLiveLayerStates = &baseLayerStates;
input.liveState = &liveState; input.transientAutomationOverlay = &liveState;
input.allowLiveCommits = true; input.allowTransientAutomationCommits = true;
input.collectLiveCommitRequests = true; input.collectTransientAutomationCommitRequests = true;
input.liveSmoothing = 0.0; input.transientAutomationSmoothing = 0.0;
input.liveCommitDelay = std::chrono::milliseconds(0); input.transientAutomationCommitDelay = std::chrono::milliseconds(0);
input.now = std::chrono::steady_clock::now() + std::chrono::milliseconds(1); input.now = std::chrono::steady_clock::now() + std::chrono::milliseconds(1);
RenderStateComposer composer; RenderStateComposer composer;
@@ -421,13 +464,13 @@ void TestRenderStateComposerSuppressesCommitCollection()
liveState.ApplyOscUpdates({ update }); liveState.ApplyOscUpdates({ update });
std::vector<RuntimeRenderState> baseLayerStates = { MakeLayerState() }; std::vector<RuntimeRenderState> baseLayerStates = { MakeLayerState() };
RenderStateCompositionInput input; LayeredRenderStateInput input;
input.baseLayerStates = &baseLayerStates; input.committedLiveLayerStates = &baseLayerStates;
input.liveState = &liveState; input.transientAutomationOverlay = &liveState;
input.allowLiveCommits = true; input.allowTransientAutomationCommits = true;
input.collectLiveCommitRequests = false; input.collectTransientAutomationCommitRequests = false;
input.liveSmoothing = 0.0; input.transientAutomationSmoothing = 0.0;
input.liveCommitDelay = std::chrono::milliseconds(0); input.transientAutomationCommitDelay = std::chrono::milliseconds(0);
input.now = std::chrono::steady_clock::now() + std::chrono::milliseconds(1); input.now = std::chrono::steady_clock::now() + std::chrono::milliseconds(1);
RenderStateComposer composer; RenderStateComposer composer;
@@ -454,6 +497,8 @@ int main()
TestRuntimeLiveStateSmoothingVectorSizeMismatchUsesTargetShape(); TestRuntimeLiveStateSmoothingVectorSizeMismatchUsesTargetShape();
TestRuntimeLiveStateTriggerOverlayIncrementsAndClears(); TestRuntimeLiveStateTriggerOverlayIncrementsAndClears();
TestRenderStateComposerBuildsFrameState(); TestRenderStateComposerBuildsFrameState();
TestRenderStateComposerUsesCommittedLayerOverBaseLayer();
TestRenderStateComposerUsesBaseLayerWhenCommittedLayerMissing();
TestRenderStateComposerQueuesCommitRequestsWhenEnabled(); TestRenderStateComposerQueuesCommitRequestsWhenEnabled();
TestRenderStateComposerSuppressesCommitCollection(); TestRenderStateComposerSuppressesCommitCollection();