step 3
This commit is contained in:
@@ -155,6 +155,40 @@ void ControlServices::ClearOscState()
|
||||
}
|
||||
}
|
||||
|
||||
void ControlServices::ClearOscStateForLayerKey(const std::string& layerKey)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mPendingOscMutex);
|
||||
for (auto it = mPendingOscUpdates.begin(); it != mPendingOscUpdates.end();)
|
||||
{
|
||||
if (it->second.layerKey == layerKey)
|
||||
it = mPendingOscUpdates.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mPendingOscCommitMutex);
|
||||
for (auto it = mPendingOscCommits.begin(); it != mPendingOscCommits.end();)
|
||||
{
|
||||
if (it->second.layerKey == layerKey)
|
||||
it = mPendingOscCommits.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mCompletedOscCommitMutex);
|
||||
for (auto it = mCompletedOscCommits.begin(); it != mCompletedOscCommits.end();)
|
||||
{
|
||||
if (it->routeKey.rfind(layerKey + "\n", 0) == 0)
|
||||
it = mCompletedOscCommits.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControlServices::ConsumeCompletedOscCommits(std::vector<CompletedOscCommit>& completedCommits)
|
||||
{
|
||||
completedCommits.clear();
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
bool ApplyPendingOscUpdates(std::vector<AppliedOscUpdate>& appliedUpdates, std::string& error);
|
||||
bool QueueOscCommit(const std::string& routeKey, const std::string& layerKey, const std::string& parameterKey, const JsonValue& value, uint64_t generation, std::string& error);
|
||||
void ClearOscState();
|
||||
void ClearOscStateForLayerKey(const std::string& layerKey);
|
||||
void ConsumeCompletedOscCommits(std::vector<CompletedOscCommit>& completedCommits);
|
||||
|
||||
private:
|
||||
|
||||
@@ -68,6 +68,12 @@ void RuntimeServices::ClearOscState()
|
||||
mControlServices->ClearOscState();
|
||||
}
|
||||
|
||||
void RuntimeServices::ClearOscStateForLayerKey(const std::string& layerKey)
|
||||
{
|
||||
if (mControlServices)
|
||||
mControlServices->ClearOscStateForLayerKey(layerKey);
|
||||
}
|
||||
|
||||
void RuntimeServices::ConsumeCompletedOscCommits(std::vector<CompletedOscCommit>& completedCommits)
|
||||
{
|
||||
if (!mControlServices)
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
bool ApplyPendingOscUpdates(std::vector<AppliedOscUpdate>& appliedUpdates, std::string& error);
|
||||
bool QueueOscCommit(const std::string& routeKey, const std::string& layerKey, const std::string& parameterKey, const JsonValue& value, uint64_t generation, std::string& error);
|
||||
void ClearOscState();
|
||||
void ClearOscStateForLayerKey(const std::string& layerKey);
|
||||
void ConsumeCompletedOscCommits(std::vector<CompletedOscCommit>& completedCommits);
|
||||
|
||||
private:
|
||||
|
||||
@@ -415,7 +415,16 @@ void RenderEngine::ProcessScreenshotCaptureCommandsOnRenderThread()
|
||||
|
||||
void RenderEngine::ClearOscOverlayState()
|
||||
{
|
||||
mRuntimeLiveState.Clear();
|
||||
InvokeOnRenderThread([this]() {
|
||||
mRuntimeLiveState.Clear();
|
||||
});
|
||||
}
|
||||
|
||||
void RenderEngine::ClearOscOverlayStateForLayerKey(const std::string& layerKey)
|
||||
{
|
||||
InvokeOnRenderThread([this, layerKey]() {
|
||||
mRuntimeLiveState.ClearForLayerKey(layerKey);
|
||||
});
|
||||
}
|
||||
|
||||
void RenderEngine::UpdateOscOverlayState(
|
||||
|
||||
@@ -95,6 +95,7 @@ public:
|
||||
void ResetShaderFeedbackState();
|
||||
void ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderResetScope resetScope);
|
||||
void ClearOscOverlayState();
|
||||
void ClearOscOverlayStateForLayerKey(const std::string& layerKey);
|
||||
void UpdateOscOverlayState(
|
||||
const std::vector<OscOverlayUpdate>& updates,
|
||||
const std::vector<OscOverlayCommitCompletion>& completedCommits);
|
||||
|
||||
@@ -85,10 +85,19 @@ bool RuntimeUpdateController::ApplyRuntimeCoordinatorResult(const RuntimeCoordin
|
||||
|
||||
mRuntimeCoordinator.ApplyCommittedStateMode(result.committedStateMode);
|
||||
|
||||
if (result.clearTransientOscState)
|
||||
switch (result.transientOscInvalidation)
|
||||
{
|
||||
case RuntimeCoordinatorTransientOscInvalidation::All:
|
||||
mRenderEngine.ClearOscOverlayState();
|
||||
mRuntimeServices.ClearOscState();
|
||||
break;
|
||||
case RuntimeCoordinatorTransientOscInvalidation::Layer:
|
||||
mRenderEngine.ClearOscOverlayStateForLayerKey(result.transientOscLayerKey);
|
||||
mRuntimeServices.ClearOscStateForLayerKey(result.transientOscLayerKey);
|
||||
break;
|
||||
case RuntimeCoordinatorTransientOscInvalidation::None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mRenderEngine.ApplyRuntimeCoordinatorRenderReset(result.renderResetScope);
|
||||
|
||||
@@ -56,6 +56,11 @@ RuntimeCoordinatorResult RuntimeCoordinator::RemoveLayer(const std::string& laye
|
||||
}
|
||||
|
||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.DeleteStoredLayer(layerId, error), error, true, true, true);
|
||||
if (result.accepted)
|
||||
{
|
||||
result.transientOscInvalidation = RuntimeCoordinatorTransientOscInvalidation::Layer;
|
||||
result.transientOscLayerKey = layerId;
|
||||
}
|
||||
PublishCoordinatorResult("RemoveLayer", result);
|
||||
return result;
|
||||
}
|
||||
@@ -205,7 +210,8 @@ RuntimeCoordinatorResult RuntimeCoordinator::ResetLayerParameters(const std::str
|
||||
return result;
|
||||
}
|
||||
|
||||
result.clearTransientOscState = true;
|
||||
result.transientOscInvalidation = RuntimeCoordinatorTransientOscInvalidation::Layer;
|
||||
result.transientOscLayerKey = layerId;
|
||||
result.renderResetScope = RuntimeCoordinatorRenderResetScope::TemporalHistoryAndFeedback;
|
||||
PublishCoordinatorResult("ResetLayerParameters", result);
|
||||
return result;
|
||||
@@ -538,7 +544,7 @@ void RuntimeCoordinator::PublishCoordinatorResult(const std::string& action, con
|
||||
mutation.runtimeStateBroadcastRequired = result.runtimeStateBroadcastRequired;
|
||||
mutation.shaderBuildRequested = result.shaderBuildRequested;
|
||||
mutation.persistenceRequested = result.persistenceRequested;
|
||||
mutation.clearTransientOscState = result.clearTransientOscState;
|
||||
mutation.clearTransientOscState = result.transientOscInvalidation != RuntimeCoordinatorTransientOscInvalidation::None;
|
||||
mutation.renderResetScope = ToRuntimeEventRenderResetScope(result.renderResetScope);
|
||||
mutation.errorMessage = result.errorMessage;
|
||||
mRuntimeEventDispatcher.PublishPayload(mutation, "RuntimeCoordinator");
|
||||
|
||||
@@ -25,18 +25,26 @@ enum class RuntimeCoordinatorRenderResetScope
|
||||
TemporalHistoryAndFeedback
|
||||
};
|
||||
|
||||
enum class RuntimeCoordinatorTransientOscInvalidation
|
||||
{
|
||||
None,
|
||||
Layer,
|
||||
All
|
||||
};
|
||||
|
||||
struct RuntimeCoordinatorResult
|
||||
{
|
||||
bool accepted = false;
|
||||
bool runtimeStateBroadcastRequired = false;
|
||||
bool shaderBuildRequested = false;
|
||||
bool persistenceRequested = false;
|
||||
bool clearTransientOscState = false;
|
||||
bool compileStatusChanged = false;
|
||||
bool compileStatusSucceeded = false;
|
||||
bool clearReloadRequest = false;
|
||||
RuntimeCoordinatorCommittedStateMode committedStateMode = RuntimeCoordinatorCommittedStateMode::Unchanged;
|
||||
RuntimeCoordinatorRenderResetScope renderResetScope = RuntimeCoordinatorRenderResetScope::None;
|
||||
RuntimeCoordinatorTransientOscInvalidation transientOscInvalidation = RuntimeCoordinatorTransientOscInvalidation::None;
|
||||
std::string transientOscLayerKey;
|
||||
std::string compileStatusMessage;
|
||||
std::string errorMessage;
|
||||
};
|
||||
|
||||
@@ -74,6 +74,7 @@ JsonValue BuildOscCommitValue(const ShaderParameterDefinition& definition, const
|
||||
|
||||
return JsonValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RuntimeLiveState::Clear()
|
||||
@@ -81,6 +82,47 @@ void RuntimeLiveState::Clear()
|
||||
mOscOverlayStates.clear();
|
||||
}
|
||||
|
||||
void RuntimeLiveState::ClearForLayerKey(const std::string& layerKey)
|
||||
{
|
||||
for (auto it = mOscOverlayStates.begin(); it != mOscOverlayStates.end();)
|
||||
{
|
||||
if (OverlayMatchesLayerKey(it->second, layerKey))
|
||||
it = mOscOverlayStates.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
bool RuntimeLiveState::OverlayMatchesLayerKey(const OscOverlayState& overlay, const std::string& layerKey)
|
||||
{
|
||||
return MatchesOscControlKey(overlay.layerKey, layerKey);
|
||||
}
|
||||
|
||||
bool RuntimeLiveState::TryResolveOverlayTarget(
|
||||
const OscOverlayState& overlay,
|
||||
const std::vector<RuntimeRenderState>& states,
|
||||
std::vector<RuntimeRenderState>::const_iterator& stateIt,
|
||||
std::vector<ShaderParameterDefinition>::const_iterator& definitionIt)
|
||||
{
|
||||
stateIt = std::find_if(states.begin(), states.end(),
|
||||
[&overlay](const RuntimeRenderState& state)
|
||||
{
|
||||
return MatchesOscControlKey(state.layerId, overlay.layerKey) ||
|
||||
MatchesOscControlKey(state.shaderId, overlay.layerKey) ||
|
||||
MatchesOscControlKey(state.shaderName, overlay.layerKey);
|
||||
});
|
||||
if (stateIt == states.end())
|
||||
return false;
|
||||
|
||||
definitionIt = std::find_if(stateIt->parameterDefinitions.begin(), stateIt->parameterDefinitions.end(),
|
||||
[&overlay](const ShaderParameterDefinition& definition)
|
||||
{
|
||||
return MatchesOscControlKey(definition.id, overlay.parameterKey) ||
|
||||
MatchesOscControlKey(definition.label, overlay.parameterKey);
|
||||
});
|
||||
return definitionIt != stateIt->parameterDefinitions.end();
|
||||
}
|
||||
|
||||
std::size_t RuntimeLiveState::OverlayCount() const
|
||||
{
|
||||
return mOscOverlayStates.size();
|
||||
@@ -131,6 +173,27 @@ void RuntimeLiveState::ApplyOscCommitCompletions(const std::vector<RuntimeLiveOs
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeLiveState::PruneIncompatibleOverlays(const std::vector<RuntimeRenderState>& states)
|
||||
{
|
||||
for (auto it = mOscOverlayStates.begin(); it != mOscOverlayStates.end();)
|
||||
{
|
||||
std::vector<RuntimeRenderState>::const_iterator stateIt;
|
||||
std::vector<ShaderParameterDefinition>::const_iterator definitionIt;
|
||||
if (TryResolveOverlayTarget(it->second, states, stateIt, definitionIt))
|
||||
{
|
||||
ShaderParameterValue targetValue;
|
||||
std::string normalizeError;
|
||||
if (NormalizeAndValidateParameterValue(*definitionIt, it->second.targetValue, targetValue, normalizeError))
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
it = mOscOverlayStates.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeLiveState::ApplyToLayerStates(
|
||||
std::vector<RuntimeRenderState>& states,
|
||||
const RuntimeLiveStateApplyOptions& options,
|
||||
@@ -139,6 +202,10 @@ void RuntimeLiveState::ApplyToLayerStates(
|
||||
if (states.empty() || mOscOverlayStates.empty())
|
||||
return;
|
||||
|
||||
PruneIncompatibleOverlays(states);
|
||||
if (mOscOverlayStates.empty())
|
||||
return;
|
||||
|
||||
const auto now = options.now;
|
||||
const double clampedSmoothing = ClampOscAlpha(options.smoothing);
|
||||
std::vector<std::string> overlayKeysToRemove;
|
||||
|
||||
@@ -44,9 +44,11 @@ class RuntimeLiveState
|
||||
{
|
||||
public:
|
||||
void Clear();
|
||||
void ClearForLayerKey(const std::string& layerKey);
|
||||
std::size_t OverlayCount() const;
|
||||
void ApplyOscUpdates(const std::vector<RuntimeLiveOscUpdate>& updates);
|
||||
void ApplyOscCommitCompletions(const std::vector<RuntimeLiveOscCommitCompletion>& completedCommits);
|
||||
void PruneIncompatibleOverlays(const std::vector<RuntimeRenderState>& states);
|
||||
void ApplyToLayerStates(
|
||||
std::vector<RuntimeRenderState>& states,
|
||||
const RuntimeLiveStateApplyOptions& options,
|
||||
@@ -67,5 +69,12 @@ private:
|
||||
bool commitQueued = false;
|
||||
};
|
||||
|
||||
static bool OverlayMatchesLayerKey(const OscOverlayState& overlay, const std::string& layerKey);
|
||||
static bool TryResolveOverlayTarget(
|
||||
const OscOverlayState& overlay,
|
||||
const std::vector<RuntimeRenderState>& states,
|
||||
std::vector<RuntimeRenderState>::const_iterator& stateIt,
|
||||
std::vector<ShaderParameterDefinition>::const_iterator& definitionIt);
|
||||
|
||||
std::map<std::string, OscOverlayState> mOscOverlayStates;
|
||||
};
|
||||
|
||||
@@ -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: Step 2 started.
|
||||
- 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`.
|
||||
- Phase 5 implementation: Step 3 complete.
|
||||
- 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, `RenderStateComposer` consumes a `LayeredRenderStateInput` whose fields make base persisted, committed live, and transient automation inputs explicit, and `RuntimeLiveState` owns transient-overlay invalidation against current layer/parameter compatibility. Committed runtime values are still physically stored through `RuntimeStore`/`LayerStackStore`.
|
||||
|
||||
Current live-state footholds:
|
||||
|
||||
@@ -19,6 +19,7 @@ Current live-state footholds:
|
||||
- `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.
|
||||
- `RuntimeStateLayerModel` names the Phase 5 state categories and classifies current fields as base persisted, committed live, transient automation, render-local, or health/config state.
|
||||
- `RuntimeCoordinator` can request layer-scoped transient OSC invalidation, while `RuntimeLiveState` prunes overlays that no longer map to the current render-facing layer/parameter definitions.
|
||||
|
||||
## Why Phase 5 Exists
|
||||
|
||||
@@ -265,14 +266,21 @@ Move reset/reload transient-state decisions into one policy point.
|
||||
|
||||
Initial target:
|
||||
|
||||
- layer removal clears matching transient overlays
|
||||
- shader change clears incompatible overlays
|
||||
- preset load clears incompatible overlays
|
||||
- shader reload can preserve compatible overlays when requested
|
||||
- temporal/feedback resets stay render-local and separate from parameter overlays
|
||||
- [x] layer removal clears matching transient overlays
|
||||
- [x] shader change clears incompatible overlays
|
||||
- [x] preset load clears incompatible overlays
|
||||
- [x] shader reload can preserve compatible overlays when requested
|
||||
- [x] temporal/feedback resets stay render-local and separate from parameter overlays
|
||||
|
||||
This is where Phase 5 should prevent "clear everything" and "preserve everything" from being scattered through unrelated code.
|
||||
|
||||
Current implementation:
|
||||
|
||||
- `RuntimeCoordinatorResult` carries a named `RuntimeCoordinatorTransientOscInvalidation` request rather than a raw clear-all flag.
|
||||
- `RuntimeUpdateController` applies layer-scoped invalidation to both render-owned overlay state and queued OSC service state.
|
||||
- `RuntimeLiveState::PruneIncompatibleOverlays(...)` is the central compatibility policy for current render-facing layer/parameter definitions.
|
||||
- `RuntimeLiveState::ApplyToLayerStates(...)` prunes incompatible overlays before applying transient values, so shader changes, preset loads, and layer removals stop carrying stale overlays once the current frame state no longer maps them.
|
||||
|
||||
### Step 4. Clarify OSC Commit Semantics
|
||||
|
||||
Make the transient-to-committed path explicit.
|
||||
@@ -368,7 +376,7 @@ Phase 5 can be considered complete once the project can say:
|
||||
- [x] persisted, committed-live, and transient automation layers are named in code or clear read models
|
||||
- [x] final render-value precedence is explicit and covered by tests
|
||||
- [x] `RenderStateComposer` or its replacement consumes a layered input contract
|
||||
- [ ] reset/reload/preset behavior for transient overlays is centralized or clearly delegated
|
||||
- [x] reset/reload/preset behavior for transient overlays is centralized or clearly delegated
|
||||
- [ ] OSC overlay settle/commit behavior is explicit, including persistence policy
|
||||
- [ ] `RuntimeStore` remains durable-state focused and does not absorb transient automation policy
|
||||
- [ ] render-local temporal/feedback state remains separate from live parameter layering
|
||||
|
||||
@@ -111,6 +111,8 @@ 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.
|
||||
|
||||
`RuntimeLiveState` now owns transient automation invalidation for render-facing compatibility. It can clear overlays for a target layer/control key and prunes overlays that no longer resolve to the current layer and parameter definitions before applying them to a frame. This keeps shader reload, preset load, and layer removal behavior local to the live-state/composition boundary instead of scattering it through GL drawing code.
|
||||
|
||||
### 5. Shader Build Application
|
||||
|
||||
Compilation itself may eventually move into a separate build service, but once shader build outputs exist, `RenderEngine` owns:
|
||||
|
||||
@@ -446,6 +446,8 @@ The coordinator should define explicit reset scopes such as:
|
||||
|
||||
That allows later phases to stop encoding reset behavior implicitly in UI handlers or render rebuild code.
|
||||
|
||||
Phase 5 has made this more concrete for OSC overlays: coordinator results now carry a named transient OSC invalidation request, with layer-scoped invalidation used for layer removal and manual parameter reset. The render/live-state owner still decides compatibility details, but callers no longer infer transient reset behavior from a generic boolean.
|
||||
|
||||
## Migration Plan From Current Code
|
||||
|
||||
The coordinator should be introduced incrementally.
|
||||
|
||||
@@ -349,6 +349,100 @@ void TestRuntimeLiveStateTriggerOverlayIncrementsAndClears()
|
||||
Expect(liveState.OverlayCount() == 0, "trigger overlay clears after apply");
|
||||
}
|
||||
|
||||
void TestRuntimeLiveStateClearsOverlaysForLayerKey()
|
||||
{
|
||||
RuntimeLiveState liveState;
|
||||
|
||||
RuntimeLiveOscUpdate first;
|
||||
first.routeKey = "layer-one\namount";
|
||||
first.layerKey = "layer-one";
|
||||
first.parameterKey = "amount";
|
||||
first.targetValue = JsonValue(0.5);
|
||||
|
||||
RuntimeLiveOscUpdate second = first;
|
||||
second.routeKey = "layer-two\namount";
|
||||
second.layerKey = "layer-two";
|
||||
second.targetValue = JsonValue(0.75);
|
||||
|
||||
liveState.ApplyOscUpdates({ first, second });
|
||||
liveState.ClearForLayerKey("layer-one");
|
||||
|
||||
Expect(liveState.OverlayCount() == 1, "layer-scoped invalidation only removes matching overlays");
|
||||
|
||||
std::vector<RuntimeRenderState> states = { MakeLayerState() };
|
||||
states[0].layerId = "layer-two";
|
||||
RuntimeLiveStateApplyOptions options;
|
||||
options.smoothing = 0.0;
|
||||
liveState.ApplyToLayerStates(states, options, nullptr);
|
||||
|
||||
const auto valueIt = states[0].parameterValues.find("amount");
|
||||
Expect(valueIt != states[0].parameterValues.end() &&
|
||||
!valueIt->second.numberValues.empty() &&
|
||||
std::fabs(valueIt->second.numberValues[0] - 0.75) < 0.0001,
|
||||
"unmatched layer invalidation preserves unrelated overlay");
|
||||
}
|
||||
|
||||
void TestRuntimeLiveStatePrunesRemovedLayerOverlay()
|
||||
{
|
||||
RuntimeLiveState liveState;
|
||||
|
||||
RuntimeLiveOscUpdate update;
|
||||
update.routeKey = "removed-layer\namount";
|
||||
update.layerKey = "removed-layer";
|
||||
update.parameterKey = "amount";
|
||||
update.targetValue = JsonValue(0.5);
|
||||
liveState.ApplyOscUpdates({ update });
|
||||
|
||||
std::vector<RuntimeRenderState> states = { MakeLayerState() };
|
||||
liveState.PruneIncompatibleOverlays(states);
|
||||
|
||||
Expect(liveState.OverlayCount() == 0, "invalidation policy removes overlays for missing layers");
|
||||
}
|
||||
|
||||
void TestRuntimeLiveStatePrunesIncompatibleParameterOverlay()
|
||||
{
|
||||
RuntimeLiveState liveState;
|
||||
|
||||
RuntimeLiveOscUpdate update;
|
||||
update.routeKey = "layer-one\namount";
|
||||
update.layerKey = "layer-one";
|
||||
update.parameterKey = "amount";
|
||||
update.targetValue = JsonValue(0.5);
|
||||
liveState.ApplyOscUpdates({ update });
|
||||
|
||||
std::vector<RuntimeRenderState> states = { MakeLayerStateWithDefinitions({ FloatDefinition("other", "Other") }) };
|
||||
liveState.PruneIncompatibleOverlays(states);
|
||||
|
||||
Expect(liveState.OverlayCount() == 0, "invalidation policy removes overlays for missing parameters");
|
||||
}
|
||||
|
||||
void TestRuntimeLiveStatePreservesCompatibleOverlayAfterShaderReload()
|
||||
{
|
||||
RuntimeLiveState liveState;
|
||||
|
||||
RuntimeLiveOscUpdate update;
|
||||
update.routeKey = "layer-one\namount";
|
||||
update.layerKey = "layer-one";
|
||||
update.parameterKey = "Amount";
|
||||
update.targetValue = JsonValue(0.5);
|
||||
liveState.ApplyOscUpdates({ update });
|
||||
|
||||
std::vector<RuntimeRenderState> states = { MakeLayerStateWithDefinitions({ FloatDefinition("amount-renamed", "Amount") }) };
|
||||
liveState.PruneIncompatibleOverlays(states);
|
||||
|
||||
Expect(liveState.OverlayCount() == 1, "invalidation policy preserves overlays that still map by control key");
|
||||
|
||||
RuntimeLiveStateApplyOptions options;
|
||||
options.smoothing = 0.0;
|
||||
liveState.ApplyToLayerStates(states, options, nullptr);
|
||||
|
||||
const auto valueIt = states[0].parameterValues.find("amount-renamed");
|
||||
Expect(valueIt != states[0].parameterValues.end() &&
|
||||
!valueIt->second.numberValues.empty() &&
|
||||
std::fabs(valueIt->second.numberValues[0] - 0.5) < 0.0001,
|
||||
"compatible overlay applies to the reloaded parameter id");
|
||||
}
|
||||
|
||||
void TestRenderStateComposerBuildsFrameState()
|
||||
{
|
||||
RuntimeLiveState liveState;
|
||||
@@ -496,6 +590,10 @@ int main()
|
||||
TestRuntimeLiveStateSmoothingPartiallyConverges();
|
||||
TestRuntimeLiveStateSmoothingVectorSizeMismatchUsesTargetShape();
|
||||
TestRuntimeLiveStateTriggerOverlayIncrementsAndClears();
|
||||
TestRuntimeLiveStateClearsOverlaysForLayerKey();
|
||||
TestRuntimeLiveStatePrunesRemovedLayerOverlay();
|
||||
TestRuntimeLiveStatePrunesIncompatibleParameterOverlay();
|
||||
TestRuntimeLiveStatePreservesCompatibleOverlayAfterShaderReload();
|
||||
TestRenderStateComposerBuildsFrameState();
|
||||
TestRenderStateComposerUsesCommittedLayerOverBaseLayer();
|
||||
TestRenderStateComposerUsesBaseLayerWhenCommittedLayerMissing();
|
||||
|
||||
@@ -278,6 +278,14 @@ void TestRuntimeCoordinatorPersistenceEvents()
|
||||
expectAcceptedPersistence(coordinator.UpdateLayerParameter(alphaLayerId, "gain", JsonValue(0.75)), "UpdateLayerParameter",
|
||||
"parameter changes are accepted");
|
||||
|
||||
RuntimeCoordinatorResult resetResult = coordinator.ResetLayerParameters(alphaLayerId);
|
||||
std::vector<RuntimeEvent> resetEvents = dispatchAndClear();
|
||||
Expect(resetResult.accepted, "parameter reset is accepted");
|
||||
Expect(resetResult.transientOscInvalidation == RuntimeCoordinatorTransientOscInvalidation::Layer,
|
||||
"parameter reset requests layer-scoped transient OSC invalidation");
|
||||
Expect(resetResult.transientOscLayerKey == alphaLayerId, "parameter reset invalidates the target layer overlays");
|
||||
Expect(countEvents(resetEvents, RuntimeEventType::RuntimeMutationAccepted) == 1, "parameter reset publishes accepted fact");
|
||||
|
||||
expectAcceptedPersistence(coordinator.AddLayer("beta"), "AddLayer", "stack edits are accepted");
|
||||
layers = store.CopyLayerStates();
|
||||
Expect(layers.size() == 2, "stack edit creates a second layer");
|
||||
|
||||
Reference in New Issue
Block a user