Files
video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeCoordinator.cpp
Aiden b2369c418b
All checks were successful
CI / React UI Build (push) Successful in 10s
CI / Native Windows Build And Tests (push) Successful in 2m40s
CI / Windows Release Package (push) Successful in 2m39s
pass 2
2026-05-11 01:29:44 +10:00

371 lines
13 KiB
C++

#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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(mMutex);
return BuildQueuedReloadResult(preserveFeedbackState);
}
RuntimeCoordinatorResult RuntimeCoordinator::PollRuntimeStoreChanges(bool& registryChanged)
{
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(mMutex);
return BuildQueuedReloadResult(false);
}
void RuntimeCoordinator::ApplyCommittedStateMode(RuntimeCoordinatorCommittedStateMode mode)
{
std::lock_guard<std::mutex> 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<std::mutex> 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;
}