#include "RenderStateComposer.h" #include "RuntimeLiveState.h" #include #include #include namespace { int gFailures = 0; void Expect(bool condition, const char* message) { if (condition) return; std::cerr << "FAIL: " << message << "\n"; ++gFailures; } ShaderParameterDefinition FloatDefinition(const std::string& id, const std::string& label) { ShaderParameterDefinition definition; definition.id = id; definition.label = label; definition.type = ShaderParameterType::Float; definition.defaultNumbers = { 0.0 }; definition.minNumbers = { 0.0 }; definition.maxNumbers = { 1.0 }; return definition; } RuntimeRenderState MakeLayerState() { RuntimeRenderState state; state.layerId = "layer-one"; state.shaderId = "test-shader"; state.shaderName = "Test Shader"; state.parameterDefinitions.push_back(FloatDefinition("amount", "Amount")); ShaderParameterValue amount; amount.numberValues = { 0.25 }; state.parameterValues["amount"] = amount; return state; } void TestRuntimeLiveStateAppliesLatestOscOverlay() { 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.targetValue = JsonValue(0.75); liveState.ApplyOscUpdates({ first, second }); Expect(liveState.OverlayCount() == 1, "live state keeps one overlay per route"); std::vector states = { MakeLayerState() }; RuntimeLiveStateApplyOptions options; options.allowCommit = false; options.smoothing = 0.0; liveState.ApplyToLayerStates(states, options, nullptr); const auto valueIt = states[0].parameterValues.find("amount"); Expect(valueIt != states[0].parameterValues.end(), "overlay writes the target parameter"); Expect(!valueIt->second.numberValues.empty() && std::fabs(valueIt->second.numberValues[0] - 0.75) < 0.0001, "overlay applies the latest target value"); } void TestRuntimeLiveStateQueuesAndCompletesCommit() { RuntimeLiveState liveState; RuntimeLiveOscUpdate update; update.routeKey = "layer-one\namount"; update.layerKey = "layer-one"; update.parameterKey = "amount"; update.targetValue = JsonValue(0.9); liveState.ApplyOscUpdates({ update }); std::vector states = { MakeLayerState() }; std::vector commitRequests; RuntimeLiveStateApplyOptions options; options.allowCommit = true; options.smoothing = 0.0; options.commitDelay = std::chrono::milliseconds(0); options.now = std::chrono::steady_clock::now() + std::chrono::milliseconds(1); liveState.ApplyToLayerStates(states, options, &commitRequests); Expect(commitRequests.size() == 1, "live state queues a commit request once the overlay can settle"); Expect(commitRequests[0].routeKey == "layer-one\namount", "commit request preserves route"); Expect(commitRequests[0].generation == 1, "commit request carries overlay generation"); liveState.ApplyOscCommitCompletions({ { commitRequests[0].routeKey, commitRequests[0].generation } }); Expect(liveState.OverlayCount() == 0, "matching commit completion removes settled overlay"); } void TestRenderStateComposerBuildsFrameState() { RuntimeLiveState liveState; RuntimeLiveOscUpdate update; update.routeKey = "layer-one\namount"; update.layerKey = "Test Shader"; update.parameterKey = "Amount"; update.targetValue = JsonValue(0.6); liveState.ApplyOscUpdates({ update }); RenderStateCompositionInput input; input.baseLayerStates = { MakeLayerState() }; input.liveState = &liveState; input.liveStateOptions.allowCommit = false; input.liveStateOptions.smoothing = 0.0; RenderStateComposer composer; RenderStateCompositionResult result = composer.BuildFrameState(input); Expect(result.layerStates.size() == 1, "composer returns composed layer state"); const auto valueIt = result.layerStates[0].parameterValues.find("amount"); Expect(valueIt != result.layerStates[0].parameterValues.end(), "composer applies live overlay through live state"); Expect(!valueIt->second.numberValues.empty() && std::fabs(valueIt->second.numberValues[0] - 0.6) < 0.0001, "composer uses OSC key matching against shader names and labels"); } } int main() { TestRuntimeLiveStateAppliesLatestOscOverlay(); TestRuntimeLiveStateQueuesAndCompletesCommit(); TestRenderStateComposerBuildsFrameState(); if (gFailures != 0) { std::cerr << gFailures << " RuntimeLiveState test failure(s).\n"; return 1; } std::cout << "RuntimeLiveState tests passed.\n"; return 0; }