pass 2
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

This commit is contained in:
Aiden
2026-05-11 01:29:44 +10:00
parent c4883d3413
commit b2369c418b
18 changed files with 1143 additions and 484 deletions

View File

@@ -1,10 +1,8 @@
#include "RuntimeCoordinator.h"
#include "RuntimeParameterUtils.h"
#include "RuntimeStore.h"
#include <algorithm>
#include <chrono>
RuntimeCoordinator::RuntimeCoordinator(RuntimeStore& runtimeStore) :
mRuntimeStore(runtimeStore)
{
@@ -252,84 +250,50 @@ bool RuntimeCoordinator::PreserveFeedbackOnNextShaderBuild() const
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<std::mutex> lock(mRuntimeStore.mRuntimeHost.mMutex);
RuntimeHost::LayerPersistentState* layer = mRuntimeStore.mRuntimeHost.FindLayerById(layerId);
if (!layer)
{
error = "Unknown layer id: " + layerId;
RuntimeStore::StoredParameterSnapshot snapshot;
if (!mRuntimeStore.TryGetStoredParameterById(layerId, parameterId, snapshot, error))
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(),
[&parameterId](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);
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
{
std::lock_guard<std::mutex> lock(mRuntimeStore.mRuntimeHost.mMutex);
RuntimeHost::LayerPersistentState* matchedLayer = nullptr;
const ShaderPackage* matchedPackage = nullptr;
std::vector<ShaderParameterDefinition>::const_iterator parameterIt;
if (!mRuntimeStore.TryResolveStoredLayerAndParameterByControlKeyLocked(layerKey, parameterKey, matchedLayer, matchedPackage, parameterIt, error))
RuntimeStore::StoredParameterSnapshot snapshot;
if (!mRuntimeStore.TryGetStoredParameterByControlKey(layerKey, parameterKey, snapshot, error))
return false;
return BuildParameterMutationLocked(*matchedLayer, *parameterIt, newValue, persistState, mutation, error);
return BuildParameterMutationFromSnapshot(snapshot.layerId, snapshot.definition, snapshot.currentValue, snapshot.hasCurrentValue,
newValue, persistState, mutation, error);
}
bool RuntimeCoordinator::BuildParameterMutationLocked(RuntimeHost::LayerPersistentState& layer, const ShaderParameterDefinition& definition, const JsonValue& newValue,
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 = layer.id;
mutation.layerId = layerId;
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()
const double previousCount = !hasCurrentValue || currentValue.numberValues.empty()
? 0.0
: existingValue->second.numberValues[0];
const double triggerTime = std::chrono::duration_cast<std::chrono::duration<double>>(
std::chrono::steady_clock::now() - mRuntimeStore.mRuntimeHost.mStartTime).count();
: currentValue.numberValues[0];
const double triggerTime = mRuntimeStore.GetRuntimeElapsedSeconds();
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();
return NormalizeAndValidateParameterValue(definition, newValue, mutation.value, error);
}
bool RuntimeCoordinator::ValidateLayerExists(const std::string& layerId, std::string& error) const
{
std::lock_guard<std::mutex> lock(mRuntimeStore.mRuntimeHost.mMutex);
if (HasLayerLocked(layerId))
if (mRuntimeStore.HasStoredLayer(layerId))
return true;
error = "Unknown layer id: " + layerId;
@@ -338,8 +302,7 @@ bool RuntimeCoordinator::ValidateLayerExists(const std::string& layerId, std::st
bool RuntimeCoordinator::ValidateShaderExists(const std::string& shaderId, std::string& error) const
{
std::lock_guard<std::mutex> lock(mRuntimeStore.mRuntimeHost.mMutex);
if (HasShaderLocked(shaderId))
if (mRuntimeStore.HasStoredShader(shaderId))
return true;
error = "Unknown shader id: " + shaderId;
@@ -348,49 +311,17 @@ bool RuntimeCoordinator::ValidateShaderExists(const std::string& shaderId, std::
bool RuntimeCoordinator::ResolveLayerMove(const std::string& layerId, int direction, bool& shouldMove, std::string& error) const
{
std::lock_guard<std::mutex> 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<std::ptrdiff_t>(layers.size()) && newIndex != index;
return true;
return mRuntimeStore.ResolveStoredLayerMove(layerId, direction, shouldMove, error);
}
bool RuntimeCoordinator::ResolveLayerMoveToIndex(const std::string& layerId, std::size_t targetIndex, bool& shouldMove, std::string& error) const
{
std::lock_guard<std::mutex> 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::size_t>(std::distance(layers.begin(), it));
shouldMove = sourceIndex != clampedTargetIndex;
return true;
return mRuntimeStore.ResolveStoredLayerMoveToIndex(layerId, targetIndex, shouldMove, error);
}
bool RuntimeCoordinator::ValidatePresetName(const std::string& presetName, std::string& error) const
{
if (!mRuntimeStore.MakeSafePresetFileStem(presetName).empty())
if (mRuntimeStore.IsValidStackPresetName(presetName))
return true;
error = "Preset name must include at least one letter or number.";

View File

@@ -1,7 +1,7 @@
#pragma once
#include "RuntimeHost.h"
#include "RuntimeJson.h"
#include "ShaderTypes.h"
#include <atomic>
#include <cstddef>
@@ -80,10 +80,9 @@ private:
bool persistState, ResolvedParameterMutation& mutation, std::string& error) const;
bool BuildParameterMutationByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue,
bool persistState, ResolvedParameterMutation& mutation, std::string& error) const;
bool BuildParameterMutationLocked(RuntimeHost::LayerPersistentState& layer, const ShaderParameterDefinition& definition, const JsonValue& newValue,
bool BuildParameterMutationFromSnapshot(const std::string& layerId, const ShaderParameterDefinition& definition,
const ShaderParameterValue& currentValue, bool hasCurrentValue, const JsonValue& newValue,
bool persistState, ResolvedParameterMutation& mutation, std::string& error) const;
bool HasLayerLocked(const std::string& layerId) const;
bool HasShaderLocked(const std::string& shaderId) const;
bool ValidateLayerExists(const std::string& layerId, std::string& error) const;
bool ValidateShaderExists(const std::string& shaderId, std::string& error) const;
bool ResolveLayerMove(const std::string& layerId, int direction, bool& shouldMove, std::string& error) const;

View File

@@ -75,11 +75,14 @@ void RuntimeSnapshotProvider::AdvanceFrame()
mRuntimeStore.AdvanceFrameCounter();
}
RuntimeRenderStateSnapshot RuntimeSnapshotProvider::GetRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight) const
RuntimeRenderStateSnapshot RuntimeSnapshotProvider::PublishRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight) const
{
for (;;)
{
const RuntimeSnapshotVersions versionsBefore = GetVersions();
RuntimeRenderStateSnapshot publishedSnapshot;
if (TryGetPublishedRenderStateSnapshot(outputWidth, outputHeight, versionsBefore, publishedSnapshot))
return publishedSnapshot;
RuntimeRenderStateSnapshot snapshot;
snapshot.outputWidth = outputWidth;
@@ -91,14 +94,17 @@ RuntimeRenderStateSnapshot RuntimeSnapshotProvider::GetRenderStateSnapshot(unsig
versionsBefore.parameterStateVersion == versionsAfter.parameterStateVersion)
{
snapshot.versions = versionsAfter;
StorePublishedRenderStateSnapshot(snapshot);
return snapshot;
}
}
}
bool RuntimeSnapshotProvider::TryGetRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight, RuntimeRenderStateSnapshot& snapshot) const
bool RuntimeSnapshotProvider::TryPublishRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight, RuntimeRenderStateSnapshot& snapshot) const
{
const RuntimeSnapshotVersions versionsBefore = GetVersions();
if (TryGetPublishedRenderStateSnapshot(outputWidth, outputHeight, versionsBefore, snapshot))
return true;
std::vector<RuntimeRenderState> states;
if (!mRuntimeStore.TryBuildLayerRenderStates(outputWidth, outputHeight, states))
@@ -115,10 +121,11 @@ bool RuntimeSnapshotProvider::TryGetRenderStateSnapshot(unsigned outputWidth, un
snapshot.outputHeight = outputHeight;
snapshot.versions = versionsAfter;
snapshot.states = std::move(states);
StorePublishedRenderStateSnapshot(snapshot);
return true;
}
bool RuntimeSnapshotProvider::TryRefreshSnapshotParameters(RuntimeRenderStateSnapshot& snapshot) const
bool RuntimeSnapshotProvider::TryRefreshPublishedSnapshotParameters(RuntimeRenderStateSnapshot& snapshot) const
{
const uint64_t expectedRenderStateVersion = snapshot.versions.renderStateVersion;
if (!mRuntimeStore.TryRefreshLayerParameters(snapshot.states))
@@ -129,6 +136,7 @@ bool RuntimeSnapshotProvider::TryRefreshSnapshotParameters(RuntimeRenderStateSna
return false;
snapshot.versions = versions;
StorePublishedRenderStateSnapshot(snapshot);
return true;
}
@@ -136,3 +144,33 @@ void RuntimeSnapshotProvider::RefreshDynamicRenderStateFields(std::vector<Runtim
{
mRuntimeStore.RefreshDynamicRenderStateFields(states);
}
bool RuntimeSnapshotProvider::TryGetPublishedRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight,
const RuntimeSnapshotVersions& versions, RuntimeRenderStateSnapshot& snapshot) const
{
std::lock_guard<std::mutex> lock(mPublishedSnapshotMutex);
if (!mHasPublishedRenderStateSnapshot ||
!SnapshotMatches(mPublishedRenderStateSnapshot, outputWidth, outputHeight, versions))
{
return false;
}
snapshot = mPublishedRenderStateSnapshot;
return true;
}
void RuntimeSnapshotProvider::StorePublishedRenderStateSnapshot(const RuntimeRenderStateSnapshot& snapshot) const
{
std::lock_guard<std::mutex> lock(mPublishedSnapshotMutex);
mPublishedRenderStateSnapshot = snapshot;
mHasPublishedRenderStateSnapshot = true;
}
bool RuntimeSnapshotProvider::SnapshotMatches(const RuntimeRenderStateSnapshot& snapshot, unsigned outputWidth, unsigned outputHeight,
const RuntimeSnapshotVersions& versions)
{
return snapshot.outputWidth == outputWidth &&
snapshot.outputHeight == outputHeight &&
snapshot.versions.renderStateVersion == versions.renderStateVersion &&
snapshot.versions.parameterStateVersion == versions.parameterStateVersion;
}

View File

@@ -3,6 +3,7 @@
#include "RuntimeStore.h"
#include <cstdint>
#include <mutex>
#include <string>
#include <vector>
@@ -29,11 +30,20 @@ public:
unsigned GetMaxTemporalHistoryFrames() const;
RuntimeSnapshotVersions GetVersions() const;
void AdvanceFrame();
RuntimeRenderStateSnapshot GetRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight) const;
bool TryGetRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight, RuntimeRenderStateSnapshot& snapshot) const;
bool TryRefreshSnapshotParameters(RuntimeRenderStateSnapshot& snapshot) const;
RuntimeRenderStateSnapshot PublishRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight) const;
bool TryPublishRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight, RuntimeRenderStateSnapshot& snapshot) const;
bool TryRefreshPublishedSnapshotParameters(RuntimeRenderStateSnapshot& snapshot) const;
void RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState>& states) const;
private:
bool TryGetPublishedRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight,
const RuntimeSnapshotVersions& versions, RuntimeRenderStateSnapshot& snapshot) const;
void StorePublishedRenderStateSnapshot(const RuntimeRenderStateSnapshot& snapshot) const;
static bool SnapshotMatches(const RuntimeRenderStateSnapshot& snapshot, unsigned outputWidth, unsigned outputHeight,
const RuntimeSnapshotVersions& versions);
RuntimeStore& mRuntimeStore;
mutable std::mutex mPublishedSnapshotMutex;
mutable bool mHasPublishedRenderStateSnapshot = false;
mutable RuntimeRenderStateSnapshot mPublishedRenderStateSnapshot;
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,61 @@
#pragma once
#include "RuntimeHost.h"
#include "HealthTelemetry.h"
#include "RuntimeJson.h"
#include "ShaderTypes.h"
#include <atomic>
#include <chrono>
#include <cstdint>
#include <filesystem>
#include <map>
#include <mutex>
#include <string>
#include <vector>
class RuntimeSnapshotProvider;
class RuntimeStore
{
public:
struct StoredParameterSnapshot
{
std::string layerId;
ShaderParameterDefinition definition;
ShaderParameterValue currentValue;
bool hasCurrentValue = false;
};
struct AppConfig
{
std::string shaderLibrary = "shaders";
unsigned short serverPort = 8080;
unsigned short oscPort = 9000;
std::string oscBindAddress = "127.0.0.1";
double oscSmoothing = 0.18;
bool autoReload = true;
unsigned maxTemporalHistoryFrames = 4;
unsigned previewFps = 30;
bool enableExternalKeying = false;
std::string inputVideoFormat = "1080p";
std::string inputFrameRate = "59.94";
std::string outputVideoFormat = "1080p";
std::string outputFrameRate = "59.94";
};
struct LayerPersistentState
{
std::string id;
std::string shaderId;
bool bypass = false;
std::map<std::string, ShaderParameterValue> parameterValues;
};
struct PersistentState
{
std::vector<LayerPersistentState> layers;
};
RuntimeStore();
HealthTelemetry& GetHealthTelemetry();
const HealthTelemetry& GetHealthTelemetry() const;
@@ -29,6 +74,14 @@ public:
bool ResetStoredLayerParameterValues(const std::string& layerId, std::string& error);
bool SaveStackPresetSnapshot(const std::string& presetName, std::string& error) const;
bool LoadStackPresetSnapshot(const std::string& presetName, std::string& error);
bool HasStoredLayer(const std::string& layerId) const;
bool HasStoredShader(const std::string& shaderId) const;
bool TryGetStoredParameterById(const std::string& layerId, const std::string& parameterId, StoredParameterSnapshot& snapshot, std::string& error) const;
bool TryGetStoredParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, StoredParameterSnapshot& snapshot, std::string& error) const;
bool ResolveStoredLayerMove(const std::string& layerId, int direction, bool& shouldMove, std::string& error) const;
bool ResolveStoredLayerMoveToIndex(const std::string& layerId, std::size_t targetIndex, bool& shouldMove, std::string& error) const;
bool IsValidStackPresetName(const std::string& presetName) const;
double GetRuntimeElapsedSeconds() const;
const std::filesystem::path& GetRuntimeRepositoryRoot() const;
const std::filesystem::path& GetRuntimeUiRoot() const;
@@ -51,7 +104,6 @@ public:
void ClearReloadRequest();
private:
friend class RuntimeCoordinator;
friend class RuntimeSnapshotProvider;
bool LoadConfig(std::string& error);
bool LoadPersistentState(std::string& error);
@@ -63,7 +115,7 @@ private:
std::vector<std::string> GetStackPresetNamesLocked() const;
std::string MakeSafePresetFileStem(const std::string& presetName) const;
bool TryResolveStoredLayerAndParameterByControlKeyLocked(const std::string& layerKey, const std::string& parameterKey,
RuntimeHost::LayerPersistentState*& matchedLayer, const ShaderPackage*& matchedPackage,
const LayerPersistentState*& matchedLayer, const ShaderPackage*& matchedPackage,
std::vector<ShaderParameterDefinition>::const_iterator& parameterIt, std::string& error) const;
bool CopyShaderPackageForStoredLayer(const std::string& layerId, ShaderPackage& shaderPackage, std::string& error) const;
void GetShaderCompilerInputs(std::filesystem::path& repoRoot, std::filesystem::path& wrapperPath,
@@ -80,6 +132,48 @@ private:
void RefreshDynamicRenderStateFieldsLocked(std::vector<RuntimeRenderState>& states) const;
JsonValue BuildRuntimeStateValue() const;
JsonValue SerializeLayerStack() const;
bool NormalizeAndValidateValue(const ShaderParameterDefinition& definition, const JsonValue& value, ShaderParameterValue& normalizedValue, std::string& error) const;
ShaderParameterValue DefaultValueForDefinition(const ShaderParameterDefinition& definition) const;
void EnsureLayerDefaultsLocked(LayerPersistentState& layerState, const ShaderPackage& shaderPackage) const;
JsonValue SerializeLayerStackLocked() const;
bool DeserializeLayerStackLocked(const JsonValue& layersValue, std::vector<LayerPersistentState>& layers, std::string& error);
void NormalizePersistentLayerIdsLocked();
JsonValue SerializeParameterValue(const ShaderParameterDefinition& definition, const ShaderParameterValue& value) const;
std::string TemporalHistorySourceToString(TemporalHistorySource source) const;
LayerPersistentState* FindLayerById(const std::string& layerId);
const LayerPersistentState* FindLayerById(const std::string& layerId) const;
std::string GenerateLayerId();
void MarkRenderStateDirtyLocked();
void MarkParameterStateDirtyLocked();
mutable RuntimeHost mRuntimeHost;
HealthTelemetry mHealthTelemetry;
mutable std::mutex mMutex;
AppConfig mConfig;
PersistentState mPersistentState;
std::filesystem::path mRepoRoot;
std::filesystem::path mUiRoot;
std::filesystem::path mDocsRoot;
std::filesystem::path mShaderRoot;
std::filesystem::path mRuntimeRoot;
std::filesystem::path mPresetRoot;
std::filesystem::path mRuntimeStatePath;
std::filesystem::path mConfigPath;
std::filesystem::path mWrapperPath;
std::filesystem::path mGeneratedGlslPath;
std::filesystem::path mPatchedGlslPath;
std::map<std::string, ShaderPackage> mPackagesById;
std::vector<std::string> mPackageOrder;
std::vector<ShaderPackageStatus> mPackageStatuses;
bool mReloadRequested;
bool mCompileSucceeded;
std::string mCompileMessage;
double mStartupRandom;
unsigned short mServerPort;
bool mAutoReloadEnabled;
std::chrono::steady_clock::time_point mStartTime;
std::chrono::steady_clock::time_point mLastScanTime;
std::atomic<uint64_t> mFrameCounter{ 0 };
std::atomic<uint64_t> mRenderStateVersion{ 0 };
std::atomic<uint64_t> mParameterStateVersion{ 0 };
uint64_t mNextLayerId;
};