#include "RuntimeCoordinator.h" #include "RuntimeParameterUtils.h" #include "RuntimeStore.h" RuntimeCoordinator::RuntimeCoordinator(RuntimeStore& runtimeStore) : mRuntimeStore(runtimeStore) { } RuntimeCoordinatorResult RuntimeCoordinator::AddLayer(const std::string& shaderId) { std::lock_guard lock(mMutex); std::string error; if (!ValidateShaderExists(shaderId, error)) return ApplyStoreMutation(false, error, false, false); return ApplyStoreMutation(mRuntimeStore.CreateStoredLayer(shaderId, error), error, true, true); } RuntimeCoordinatorResult RuntimeCoordinator::RemoveLayer(const std::string& layerId) { std::lock_guard lock(mMutex); std::string error; if (!ValidateLayerExists(layerId, error)) return ApplyStoreMutation(false, error, false, false); return ApplyStoreMutation(mRuntimeStore.DeleteStoredLayer(layerId, error), error, true, true); } RuntimeCoordinatorResult RuntimeCoordinator::MoveLayer(const std::string& layerId, int direction) { std::lock_guard lock(mMutex); std::string error; bool shouldMove = false; if (!ResolveLayerMove(layerId, direction, shouldMove, error)) return ApplyStoreMutation(false, error, false, false); if (!shouldMove) return BuildAcceptedNoReloadResult(); return ApplyStoreMutation(mRuntimeStore.MoveStoredLayer(layerId, direction, error), error, true, true); } RuntimeCoordinatorResult RuntimeCoordinator::MoveLayerToIndex(const std::string& layerId, std::size_t targetIndex) { std::lock_guard lock(mMutex); std::string error; bool shouldMove = false; if (!ResolveLayerMoveToIndex(layerId, targetIndex, shouldMove, error)) return ApplyStoreMutation(false, error, false, false); if (!shouldMove) return BuildAcceptedNoReloadResult(); return ApplyStoreMutation(mRuntimeStore.MoveStoredLayerToIndex(layerId, targetIndex, error), error, true, true); } RuntimeCoordinatorResult RuntimeCoordinator::SetLayerBypass(const std::string& layerId, bool bypassed) { std::lock_guard lock(mMutex); std::string error; if (!ValidateLayerExists(layerId, error)) return ApplyStoreMutation(false, error, false, false); return ApplyStoreMutation(mRuntimeStore.SetStoredLayerBypassState(layerId, bypassed, error), error, true, false); } RuntimeCoordinatorResult RuntimeCoordinator::SetLayerShader(const std::string& layerId, const std::string& shaderId) { std::lock_guard lock(mMutex); std::string error; if (!ValidateLayerExists(layerId, error) || !ValidateShaderExists(shaderId, error)) return ApplyStoreMutation(false, error, false, false); return ApplyStoreMutation(mRuntimeStore.SetStoredLayerShaderSelection(layerId, shaderId, error), error, true, false); } RuntimeCoordinatorResult RuntimeCoordinator::UpdateLayerParameter(const std::string& layerId, const std::string& parameterId, const JsonValue& newValue) { std::lock_guard lock(mMutex); std::string error; ResolvedParameterMutation mutation; if (!BuildParameterMutationById(layerId, parameterId, newValue, true, mutation, error)) return ApplyStoreMutation(false, error, false, false); return ApplyStoreMutation(mRuntimeStore.SetStoredParameterValue(mutation.layerId, mutation.parameterId, mutation.value, mutation.persistState, error), error, false, false); } RuntimeCoordinatorResult RuntimeCoordinator::UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue) { std::lock_guard lock(mMutex); std::string error; ResolvedParameterMutation mutation; if (!BuildParameterMutationByControlKey(layerKey, parameterKey, newValue, true, mutation, error)) return ApplyStoreMutation(false, error, false, false); return ApplyStoreMutation(mRuntimeStore.SetStoredParameterValue(mutation.layerId, mutation.parameterId, mutation.value, mutation.persistState, error), error, false, false); } RuntimeCoordinatorResult RuntimeCoordinator::CommitOscParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue) { std::lock_guard lock(mMutex); std::string error; ResolvedParameterMutation mutation; if (!BuildParameterMutationByControlKey(layerKey, parameterKey, newValue, false, mutation, error)) return ApplyStoreMutation(false, error, false, false); return ApplyStoreMutation(mRuntimeStore.SetStoredParameterValue(mutation.layerId, mutation.parameterId, mutation.value, mutation.persistState, error), error, false, false); } RuntimeCoordinatorResult RuntimeCoordinator::ResetLayerParameters(const std::string& layerId) { std::lock_guard lock(mMutex); std::string error; if (!ValidateLayerExists(layerId, error)) return ApplyStoreMutation(false, error, false, false); RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.ResetStoredLayerParameterValues(layerId, error), error, false, false); if (!result.accepted) return result; result.clearTransientOscState = true; result.renderResetScope = RuntimeCoordinatorRenderResetScope::TemporalHistoryAndFeedback; return result; } RuntimeCoordinatorResult RuntimeCoordinator::SaveStackPreset(const std::string& presetName) { std::lock_guard lock(mMutex); std::string error; if (!ValidatePresetName(presetName, error)) return ApplyStoreMutation(false, error, false, false); return ApplyStoreMutation(mRuntimeStore.SaveStackPresetSnapshot(presetName, error), error, false, false); } RuntimeCoordinatorResult RuntimeCoordinator::LoadStackPreset(const std::string& presetName) { std::lock_guard lock(mMutex); std::string error; if (!ValidatePresetName(presetName, error)) return ApplyStoreMutation(false, error, false, false); return ApplyStoreMutation(mRuntimeStore.LoadStackPresetSnapshot(presetName, error), error, true, false); } RuntimeCoordinatorResult RuntimeCoordinator::RequestShaderReload(bool preserveFeedbackState) { std::lock_guard lock(mMutex); return BuildQueuedReloadResult(preserveFeedbackState); } RuntimeCoordinatorResult RuntimeCoordinator::PollRuntimeStoreChanges(bool& registryChanged) { std::lock_guard lock(mMutex); registryChanged = false; bool reloadRequested = false; std::string error; if (!mRuntimeStore.PollStoredFileChanges(registryChanged, reloadRequested, error)) return HandleRuntimePollFailure(error); if (reloadRequested) return BuildQueuedReloadResult(false); if (registryChanged) return BuildAcceptedNoReloadResult(); RuntimeCoordinatorResult result; result.accepted = true; return result; } RuntimeCoordinatorResult RuntimeCoordinator::HandleRuntimePollFailure(const std::string& error) { RuntimeCoordinatorResult result; result.accepted = true; result.runtimeStateBroadcastRequired = true; result.compileStatusChanged = true; result.compileStatusSucceeded = false; result.compileStatusMessage = error; return result; } RuntimeCoordinatorResult RuntimeCoordinator::HandlePreparedShaderBuildFailure(const std::string& error) { std::lock_guard lock(mMutex); mPreserveFeedbackOnNextShaderBuild = false; mUseCommittedLayerStates = true; RuntimeCoordinatorResult result; result.accepted = true; result.runtimeStateBroadcastRequired = true; result.compileStatusChanged = true; result.compileStatusSucceeded = false; result.compileStatusMessage = error; result.committedStateMode = RuntimeCoordinatorCommittedStateMode::UseCommittedStates; return result; } RuntimeCoordinatorResult RuntimeCoordinator::HandlePreparedShaderBuildSuccess() { std::lock_guard lock(mMutex); mUseCommittedLayerStates = false; RuntimeCoordinatorResult result; result.accepted = true; result.runtimeStateBroadcastRequired = true; result.compileStatusChanged = true; result.compileStatusSucceeded = true; result.compileStatusMessage = "Shader layers compiled successfully."; result.committedStateMode = RuntimeCoordinatorCommittedStateMode::UseLiveSnapshots; mPreserveFeedbackOnNextShaderBuild = false; return result; } RuntimeCoordinatorResult RuntimeCoordinator::HandleRuntimeReloadRequest() { std::lock_guard lock(mMutex); return BuildQueuedReloadResult(false); } void RuntimeCoordinator::ApplyCommittedStateMode(RuntimeCoordinatorCommittedStateMode mode) { std::lock_guard lock(mMutex); switch (mode) { case RuntimeCoordinatorCommittedStateMode::UseCommittedStates: mUseCommittedLayerStates = true; break; case RuntimeCoordinatorCommittedStateMode::UseLiveSnapshots: mUseCommittedLayerStates = false; break; case RuntimeCoordinatorCommittedStateMode::Unchanged: default: break; } } bool RuntimeCoordinator::UseCommittedLayerStates() const { return mUseCommittedLayerStates.load(); } bool RuntimeCoordinator::PreserveFeedbackOnNextShaderBuild() const { std::lock_guard lock(mMutex); return mPreserveFeedbackOnNextShaderBuild; } bool RuntimeCoordinator::BuildParameterMutationById(const std::string& layerId, const std::string& parameterId, const JsonValue& newValue, bool persistState, ResolvedParameterMutation& mutation, std::string& error) const { RuntimeStore::StoredParameterSnapshot snapshot; if (!mRuntimeStore.TryGetStoredParameterById(layerId, parameterId, snapshot, error)) return false; return BuildParameterMutationFromSnapshot(snapshot.layerId, snapshot.definition, snapshot.currentValue, snapshot.hasCurrentValue, newValue, persistState, mutation, error); } bool RuntimeCoordinator::BuildParameterMutationByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, bool persistState, ResolvedParameterMutation& mutation, std::string& error) const { RuntimeStore::StoredParameterSnapshot snapshot; if (!mRuntimeStore.TryGetStoredParameterByControlKey(layerKey, parameterKey, snapshot, error)) return false; return BuildParameterMutationFromSnapshot(snapshot.layerId, snapshot.definition, snapshot.currentValue, snapshot.hasCurrentValue, newValue, persistState, mutation, error); } bool RuntimeCoordinator::BuildParameterMutationFromSnapshot(const std::string& layerId, const ShaderParameterDefinition& definition, const ShaderParameterValue& currentValue, bool hasCurrentValue, const JsonValue& newValue, bool persistState, ResolvedParameterMutation& mutation, std::string& error) const { mutation.layerId = layerId; mutation.parameterId = definition.id; mutation.persistState = persistState; if (definition.type == ShaderParameterType::Trigger) { const double previousCount = !hasCurrentValue || currentValue.numberValues.empty() ? 0.0 : currentValue.numberValues[0]; const double triggerTime = mRuntimeStore.GetRuntimeElapsedSeconds(); mutation.value.numberValues = { previousCount + 1.0, triggerTime }; mutation.persistState = false; return true; } return NormalizeAndValidateParameterValue(definition, newValue, mutation.value, error); } bool RuntimeCoordinator::ValidateLayerExists(const std::string& layerId, std::string& error) const { if (mRuntimeStore.HasStoredLayer(layerId)) return true; error = "Unknown layer id: " + layerId; return false; } bool RuntimeCoordinator::ValidateShaderExists(const std::string& shaderId, std::string& error) const { if (mRuntimeStore.HasStoredShader(shaderId)) return true; error = "Unknown shader id: " + shaderId; return false; } bool RuntimeCoordinator::ResolveLayerMove(const std::string& layerId, int direction, bool& shouldMove, std::string& error) const { return mRuntimeStore.ResolveStoredLayerMove(layerId, direction, shouldMove, error); } bool RuntimeCoordinator::ResolveLayerMoveToIndex(const std::string& layerId, std::size_t targetIndex, bool& shouldMove, std::string& error) const { return mRuntimeStore.ResolveStoredLayerMoveToIndex(layerId, targetIndex, shouldMove, error); } bool RuntimeCoordinator::ValidatePresetName(const std::string& presetName, std::string& error) const { if (mRuntimeStore.IsValidStackPresetName(presetName)) return true; error = "Preset name must include at least one letter or number."; return false; } RuntimeCoordinatorResult RuntimeCoordinator::ApplyStoreMutation(bool succeeded, const std::string& errorMessage, bool reloadRequired, bool preserveFeedbackState) { if (!succeeded) { RuntimeCoordinatorResult result; result.accepted = false; result.errorMessage = errorMessage; return result; } if (reloadRequired) return BuildQueuedReloadResult(preserveFeedbackState); return BuildAcceptedNoReloadResult(); } RuntimeCoordinatorResult RuntimeCoordinator::BuildQueuedReloadResult(bool preserveFeedbackState) { mPreserveFeedbackOnNextShaderBuild = preserveFeedbackState; mUseCommittedLayerStates = true; RuntimeCoordinatorResult result; result.accepted = true; result.runtimeStateBroadcastRequired = true; result.shaderBuildRequested = true; result.compileStatusChanged = true; result.compileStatusSucceeded = true; result.compileStatusMessage = "Shader rebuild queued."; result.clearReloadRequest = true; result.committedStateMode = RuntimeCoordinatorCommittedStateMode::UseCommittedStates; return result; } RuntimeCoordinatorResult RuntimeCoordinator::BuildAcceptedNoReloadResult() const { RuntimeCoordinatorResult result; result.accepted = true; result.runtimeStateBroadcastRequired = true; return result; }