#include "RuntimeCoordinator.h" #include "RuntimeStore.h" #include #include 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 { std::lock_guard lock(mRuntimeStore.mRuntimeHost.mMutex); RuntimeHost::LayerPersistentState* layer = mRuntimeStore.mRuntimeHost.FindLayerById(layerId); if (!layer) { error = "Unknown layer id: " + layerId; return false; } auto shaderIt = mRuntimeStore.mRuntimeHost.mPackagesById.find(layer->shaderId); if (shaderIt == mRuntimeStore.mRuntimeHost.mPackagesById.end()) { error = "Unknown shader id: " + layer->shaderId; return false; } auto parameterIt = std::find_if(shaderIt->second.parameters.begin(), shaderIt->second.parameters.end(), [¶meterId](const ShaderParameterDefinition& definition) { return definition.id == parameterId; }); if (parameterIt == shaderIt->second.parameters.end()) { error = "Unknown parameter id: " + parameterId; return false; } return BuildParameterMutationLocked(*layer, *parameterIt, 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 { std::lock_guard lock(mRuntimeStore.mRuntimeHost.mMutex); RuntimeHost::LayerPersistentState* matchedLayer = nullptr; const ShaderPackage* matchedPackage = nullptr; std::vector::const_iterator parameterIt; if (!mRuntimeStore.TryResolveStoredLayerAndParameterByControlKeyLocked(layerKey, parameterKey, matchedLayer, matchedPackage, parameterIt, error)) return false; return BuildParameterMutationLocked(*matchedLayer, *parameterIt, newValue, persistState, mutation, error); } bool RuntimeCoordinator::BuildParameterMutationLocked(RuntimeHost::LayerPersistentState& layer, const ShaderParameterDefinition& definition, const JsonValue& newValue, bool persistState, ResolvedParameterMutation& mutation, std::string& error) const { mutation.layerId = layer.id; mutation.parameterId = definition.id; mutation.persistState = persistState; if (definition.type == ShaderParameterType::Trigger) { const auto existingValue = layer.parameterValues.find(definition.id); const double previousCount = existingValue == layer.parameterValues.end() || existingValue->second.numberValues.empty() ? 0.0 : existingValue->second.numberValues[0]; const double triggerTime = std::chrono::duration_cast>( std::chrono::steady_clock::now() - mRuntimeStore.mRuntimeHost.mStartTime).count(); mutation.value.numberValues = { previousCount + 1.0, triggerTime }; mutation.persistState = false; return true; } return mRuntimeStore.mRuntimeHost.NormalizeAndValidateValue(definition, newValue, mutation.value, error); } bool RuntimeCoordinator::HasLayerLocked(const std::string& layerId) const { return mRuntimeStore.mRuntimeHost.FindLayerById(layerId) != nullptr; } bool RuntimeCoordinator::HasShaderLocked(const std::string& shaderId) const { return mRuntimeStore.mRuntimeHost.mPackagesById.find(shaderId) != mRuntimeStore.mRuntimeHost.mPackagesById.end(); } bool RuntimeCoordinator::ValidateLayerExists(const std::string& layerId, std::string& error) const { std::lock_guard lock(mRuntimeStore.mRuntimeHost.mMutex); if (HasLayerLocked(layerId)) return true; error = "Unknown layer id: " + layerId; return false; } bool RuntimeCoordinator::ValidateShaderExists(const std::string& shaderId, std::string& error) const { std::lock_guard lock(mRuntimeStore.mRuntimeHost.mMutex); if (HasShaderLocked(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 { std::lock_guard lock(mRuntimeStore.mRuntimeHost.mMutex); const auto& layers = mRuntimeStore.mRuntimeHost.mPersistentState.layers; auto it = std::find_if(layers.begin(), layers.end(), [&layerId](const RuntimeHost::LayerPersistentState& layer) { return layer.id == layerId; }); if (it == layers.end()) { error = "Unknown layer id: " + layerId; return false; } const std::ptrdiff_t index = std::distance(layers.begin(), it); const std::ptrdiff_t newIndex = index + direction; shouldMove = newIndex >= 0 && newIndex < static_cast(layers.size()) && newIndex != index; return true; } bool RuntimeCoordinator::ResolveLayerMoveToIndex(const std::string& layerId, std::size_t targetIndex, bool& shouldMove, std::string& error) const { std::lock_guard lock(mRuntimeStore.mRuntimeHost.mMutex); const auto& layers = mRuntimeStore.mRuntimeHost.mPersistentState.layers; auto it = std::find_if(layers.begin(), layers.end(), [&layerId](const RuntimeHost::LayerPersistentState& layer) { return layer.id == layerId; }); if (it == layers.end()) { error = "Unknown layer id: " + layerId; return false; } if (layers.empty()) { shouldMove = false; return true; } const std::size_t clampedTargetIndex = (std::min)(targetIndex, layers.size() - 1); const std::size_t sourceIndex = static_cast(std::distance(layers.begin(), it)); shouldMove = sourceIndex != clampedTargetIndex; return true; } bool RuntimeCoordinator::ValidatePresetName(const std::string& presetName, std::string& error) const { if (!mRuntimeStore.MakeSafePresetFileStem(presetName).empty()) 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; }