OSC seperation
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include "OscServer.h"
|
||||
#include "RuntimeControlBridge.h"
|
||||
#include "RuntimeHost.h"
|
||||
#include "RuntimeStore.h"
|
||||
#include <windows.h>
|
||||
|
||||
ControlServices::ControlServices() :
|
||||
@@ -34,9 +35,9 @@ bool ControlServices::Start(OpenGLComposite& composite, RuntimeHost& runtimeHost
|
||||
return true;
|
||||
}
|
||||
|
||||
void ControlServices::BeginPolling(RuntimeHost& runtimeHost)
|
||||
void ControlServices::BeginPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore)
|
||||
{
|
||||
StartPolling(runtimeHost);
|
||||
StartPolling(runtimeHost, runtimeStore);
|
||||
}
|
||||
|
||||
void ControlServices::Stop()
|
||||
@@ -174,12 +175,12 @@ RuntimePollEvents ControlServices::ConsumePollEvents()
|
||||
return events;
|
||||
}
|
||||
|
||||
void ControlServices::StartPolling(RuntimeHost& runtimeHost)
|
||||
void ControlServices::StartPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore)
|
||||
{
|
||||
if (mPollRunning.exchange(true))
|
||||
return;
|
||||
|
||||
mPollThread = std::thread([this, &runtimeHost]() { PollLoop(runtimeHost); });
|
||||
mPollThread = std::thread([this, &runtimeHost, &runtimeStore]() { PollLoop(runtimeHost, runtimeStore); });
|
||||
}
|
||||
|
||||
void ControlServices::StopPolling()
|
||||
@@ -191,7 +192,7 @@ void ControlServices::StopPolling()
|
||||
mPollThread.join();
|
||||
}
|
||||
|
||||
void ControlServices::PollLoop(RuntimeHost& runtimeHost)
|
||||
void ControlServices::PollLoop(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore)
|
||||
{
|
||||
while (mPollRunning)
|
||||
{
|
||||
@@ -203,7 +204,7 @@ void ControlServices::PollLoop(RuntimeHost& runtimeHost)
|
||||
for (const auto& entry : pendingCommits)
|
||||
{
|
||||
std::string commitError;
|
||||
if (runtimeHost.UpdateLayerParameterByControlKey(
|
||||
if (runtimeStore.SetStoredParameterValueByControlKey(
|
||||
entry.second.layerKey,
|
||||
entry.second.parameterKey,
|
||||
entry.second.value,
|
||||
|
||||
@@ -15,6 +15,7 @@ class ControlServer;
|
||||
class OpenGLComposite;
|
||||
class OscServer;
|
||||
class RuntimeHost;
|
||||
class RuntimeStore;
|
||||
|
||||
struct RuntimePollEvents
|
||||
{
|
||||
@@ -45,7 +46,7 @@ public:
|
||||
~ControlServices();
|
||||
|
||||
bool Start(OpenGLComposite& composite, RuntimeHost& runtimeHost, std::string& error);
|
||||
void BeginPolling(RuntimeHost& runtimeHost);
|
||||
void BeginPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore);
|
||||
void Stop();
|
||||
void BroadcastState();
|
||||
void RequestBroadcastState();
|
||||
@@ -73,9 +74,9 @@ private:
|
||||
uint64_t generation = 0;
|
||||
};
|
||||
|
||||
void StartPolling(RuntimeHost& runtimeHost);
|
||||
void StartPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore);
|
||||
void StopPolling();
|
||||
void PollLoop(RuntimeHost& runtimeHost);
|
||||
void PollLoop(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore);
|
||||
|
||||
std::unique_ptr<ControlServer> mControlServer;
|
||||
std::unique_ptr<OscServer> mOscServer;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "RuntimeServices.h"
|
||||
|
||||
#include "RuntimeStore.h"
|
||||
|
||||
RuntimeServices::RuntimeServices() :
|
||||
mControlServices(std::make_unique<ControlServices>())
|
||||
{
|
||||
@@ -15,10 +17,10 @@ bool RuntimeServices::Start(OpenGLComposite& composite, RuntimeHost& runtimeHost
|
||||
return mControlServices && mControlServices->Start(composite, runtimeHost, error);
|
||||
}
|
||||
|
||||
void RuntimeServices::BeginPolling(RuntimeHost& runtimeHost)
|
||||
void RuntimeServices::BeginPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore)
|
||||
{
|
||||
if (mControlServices)
|
||||
mControlServices->BeginPolling(runtimeHost);
|
||||
mControlServices->BeginPolling(runtimeHost, runtimeStore);
|
||||
}
|
||||
|
||||
void RuntimeServices::Stop()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <string>
|
||||
class OpenGLComposite;
|
||||
class RuntimeHost;
|
||||
class RuntimeStore;
|
||||
|
||||
class RuntimeServices
|
||||
{
|
||||
@@ -17,7 +18,7 @@ public:
|
||||
~RuntimeServices();
|
||||
|
||||
bool Start(OpenGLComposite& composite, RuntimeHost& runtimeHost, std::string& error);
|
||||
void BeginPolling(RuntimeHost& runtimeHost);
|
||||
void BeginPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore);
|
||||
void Stop();
|
||||
void BroadcastState();
|
||||
void RequestBroadcastState();
|
||||
|
||||
@@ -270,7 +270,7 @@ bool OpenGLComposite::InitOpenGLState()
|
||||
mRenderEngine->ResetShaderFeedbackState();
|
||||
|
||||
broadcastRuntimeState();
|
||||
mRuntimeServices->BeginPolling(*mRuntimeHost);
|
||||
mRuntimeServices->BeginPolling(*mRuntimeHost, *mRuntimeStore);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -825,201 +825,6 @@ void RuntimeHost::ClearReloadRequest()
|
||||
mReloadRequested = false;
|
||||
}
|
||||
|
||||
bool RuntimeHost::UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, std::string& error)
|
||||
{
|
||||
return UpdateLayerParameterByControlKey(layerKey, parameterKey, newValue, true, error);
|
||||
}
|
||||
|
||||
bool RuntimeHost::UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, bool persistState, std::string& error)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
LayerPersistentState* matchedLayer = nullptr;
|
||||
const ShaderPackage* matchedPackage = nullptr;
|
||||
for (LayerPersistentState& layer : mPersistentState.layers)
|
||||
{
|
||||
auto shaderIt = mPackagesById.find(layer.shaderId);
|
||||
if (shaderIt == mPackagesById.end())
|
||||
continue;
|
||||
|
||||
if (MatchesControlKey(layer.id, layerKey) || MatchesControlKey(shaderIt->second.id, layerKey) ||
|
||||
MatchesControlKey(shaderIt->second.displayName, layerKey))
|
||||
{
|
||||
matchedLayer = &layer;
|
||||
matchedPackage = &shaderIt->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchedLayer || !matchedPackage)
|
||||
{
|
||||
error = "Unknown OSC layer key: " + layerKey;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto parameterIt = std::find_if(matchedPackage->parameters.begin(), matchedPackage->parameters.end(),
|
||||
[¶meterKey](const ShaderParameterDefinition& definition)
|
||||
{
|
||||
return MatchesControlKey(definition.id, parameterKey) || MatchesControlKey(definition.label, parameterKey);
|
||||
});
|
||||
if (parameterIt == matchedPackage->parameters.end())
|
||||
{
|
||||
error = "Unknown OSC parameter key: " + parameterKey;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parameterIt->type == ShaderParameterType::Trigger)
|
||||
{
|
||||
ShaderParameterValue& value = matchedLayer->parameterValues[parameterIt->id];
|
||||
const double previousCount = value.numberValues.empty() ? 0.0 : value.numberValues[0];
|
||||
const double triggerTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - mStartTime).count();
|
||||
value.numberValues = { previousCount + 1.0, triggerTime };
|
||||
MarkParameterStateDirtyLocked();
|
||||
return true;
|
||||
}
|
||||
|
||||
ShaderParameterValue normalized;
|
||||
if (!NormalizeAndValidateValue(*parameterIt, newValue, normalized, error))
|
||||
return false;
|
||||
|
||||
matchedLayer->parameterValues[parameterIt->id] = normalized;
|
||||
MarkParameterStateDirtyLocked();
|
||||
return !persistState || SavePersistentState(error);
|
||||
}
|
||||
|
||||
bool RuntimeHost::ApplyOscTargetByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& targetValue, double smoothingAmount, bool& keepApplying, std::string& resolvedLayerId, std::string& resolvedParameterId, ShaderParameterValue& appliedValue, std::string& error)
|
||||
{
|
||||
keepApplying = false;
|
||||
resolvedLayerId.clear();
|
||||
resolvedParameterId.clear();
|
||||
appliedValue = ShaderParameterValue();
|
||||
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
LayerPersistentState* matchedLayer = nullptr;
|
||||
const ShaderPackage* matchedPackage = nullptr;
|
||||
for (LayerPersistentState& layer : mPersistentState.layers)
|
||||
{
|
||||
auto shaderIt = mPackagesById.find(layer.shaderId);
|
||||
if (shaderIt == mPackagesById.end())
|
||||
continue;
|
||||
|
||||
if (MatchesControlKey(layer.id, layerKey) || MatchesControlKey(shaderIt->second.id, layerKey) ||
|
||||
MatchesControlKey(shaderIt->second.displayName, layerKey))
|
||||
{
|
||||
matchedLayer = &layer;
|
||||
matchedPackage = &shaderIt->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchedLayer || !matchedPackage)
|
||||
{
|
||||
error = "Unknown OSC layer key: " + layerKey;
|
||||
return false;
|
||||
}
|
||||
|
||||
resolvedLayerId = matchedLayer->id;
|
||||
|
||||
const auto parameterIt = std::find_if(matchedPackage->parameters.begin(), matchedPackage->parameters.end(),
|
||||
[¶meterKey](const ShaderParameterDefinition& definition)
|
||||
{
|
||||
return MatchesControlKey(definition.id, parameterKey) || MatchesControlKey(definition.label, parameterKey);
|
||||
});
|
||||
if (parameterIt == matchedPackage->parameters.end())
|
||||
{
|
||||
error = "Unknown OSC parameter key: " + parameterKey;
|
||||
return false;
|
||||
}
|
||||
|
||||
resolvedParameterId = parameterIt->id;
|
||||
|
||||
if (parameterIt->type == ShaderParameterType::Trigger)
|
||||
{
|
||||
ShaderParameterValue& value = matchedLayer->parameterValues[parameterIt->id];
|
||||
const double previousCount = value.numberValues.empty() ? 0.0 : value.numberValues[0];
|
||||
const double triggerTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - mStartTime).count();
|
||||
value.numberValues = { previousCount + 1.0, triggerTime };
|
||||
MarkParameterStateDirtyLocked();
|
||||
appliedValue = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
ShaderParameterValue normalizedTarget;
|
||||
if (!NormalizeAndValidateValue(*parameterIt, targetValue, normalizedTarget, error))
|
||||
return false;
|
||||
|
||||
const bool smoothableType =
|
||||
parameterIt->type == ShaderParameterType::Float ||
|
||||
parameterIt->type == ShaderParameterType::Vec2 ||
|
||||
parameterIt->type == ShaderParameterType::Color;
|
||||
const bool smoothableInput = targetValue.isNumber() || JsonValueContainsOnlyNumbers(targetValue);
|
||||
const double alpha = Clamp01(smoothingAmount);
|
||||
|
||||
if (!smoothableType || !smoothableInput || alpha <= 0.0)
|
||||
{
|
||||
matchedLayer->parameterValues[parameterIt->id] = normalizedTarget;
|
||||
MarkParameterStateDirtyLocked();
|
||||
appliedValue = normalizedTarget;
|
||||
return true;
|
||||
}
|
||||
|
||||
ShaderParameterValue currentValue = DefaultValueForDefinition(*parameterIt);
|
||||
auto currentIt = matchedLayer->parameterValues.find(parameterIt->id);
|
||||
if (currentIt != matchedLayer->parameterValues.end())
|
||||
currentValue = currentIt->second;
|
||||
|
||||
ShaderParameterValue nextValue = normalizedTarget;
|
||||
nextValue.numberValues = normalizedTarget.numberValues;
|
||||
|
||||
if (currentValue.numberValues.size() != normalizedTarget.numberValues.size())
|
||||
currentValue.numberValues = normalizedTarget.numberValues;
|
||||
|
||||
bool changed = false;
|
||||
bool converged = true;
|
||||
for (std::size_t index = 0; index < normalizedTarget.numberValues.size(); ++index)
|
||||
{
|
||||
const double currentNumber = currentValue.numberValues[index];
|
||||
const double targetNumber = normalizedTarget.numberValues[index];
|
||||
const double delta = targetNumber - currentNumber;
|
||||
double nextNumber = currentNumber + delta * alpha;
|
||||
|
||||
if (std::fabs(delta) <= 0.0005)
|
||||
{
|
||||
nextNumber = targetNumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
converged = false;
|
||||
}
|
||||
|
||||
if (std::fabs(nextNumber - currentNumber) > 0.0000001)
|
||||
changed = true;
|
||||
|
||||
nextValue.numberValues[index] = nextNumber;
|
||||
}
|
||||
|
||||
if (!converged)
|
||||
{
|
||||
keepApplying = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextValue.numberValues = normalizedTarget.numberValues;
|
||||
}
|
||||
|
||||
if (!changed && !keepApplying)
|
||||
{
|
||||
appliedValue = matchedLayer->parameterValues[parameterIt->id];
|
||||
return true;
|
||||
}
|
||||
|
||||
matchedLayer->parameterValues[parameterIt->id] = nextValue;
|
||||
MarkParameterStateDirtyLocked();
|
||||
appliedValue = nextValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuntimeHost::SetCompileStatus(bool succeeded, const std::string& message)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
@@ -25,10 +25,6 @@ public:
|
||||
bool ManualReloadRequested();
|
||||
void ClearReloadRequest();
|
||||
|
||||
bool UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, std::string& error);
|
||||
bool UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, bool persistState, std::string& error);
|
||||
bool ApplyOscTargetByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& targetValue, double smoothingAmount, bool& keepApplying, std::string& resolvedLayerId, std::string& resolvedParameterId, ShaderParameterValue& appliedValue, std::string& error);
|
||||
|
||||
void SetCompileStatus(bool succeeded, const std::string& message);
|
||||
void SetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
|
||||
bool TrySetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
|
||||
|
||||
@@ -14,6 +14,22 @@ std::string TrimCopy(const std::string& text)
|
||||
|
||||
return text.substr(start, end - start);
|
||||
}
|
||||
|
||||
std::string SimplifyControlKey(const std::string& text)
|
||||
{
|
||||
std::string simplified;
|
||||
for (unsigned char ch : text)
|
||||
{
|
||||
if (std::isalnum(ch))
|
||||
simplified.push_back(static_cast<char>(std::tolower(ch)));
|
||||
}
|
||||
return simplified;
|
||||
}
|
||||
|
||||
bool MatchesControlKey(const std::string& candidate, const std::string& key)
|
||||
{
|
||||
return candidate == key || SimplifyControlKey(candidate) == SimplifyControlKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
RuntimeStore::RuntimeStore(RuntimeHost& runtimeHost) :
|
||||
@@ -256,12 +272,36 @@ bool RuntimeStore::SetStoredParameterValue(const std::string& layerId, const std
|
||||
|
||||
bool RuntimeStore::SetStoredParameterValueByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, std::string& error)
|
||||
{
|
||||
return mRuntimeHost.UpdateLayerParameterByControlKey(layerKey, parameterKey, newValue, error);
|
||||
return SetStoredParameterValueByControlKey(layerKey, parameterKey, newValue, true, error);
|
||||
}
|
||||
|
||||
bool RuntimeStore::SetStoredParameterValueByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, bool persistState, std::string& error)
|
||||
{
|
||||
return mRuntimeHost.UpdateLayerParameterByControlKey(layerKey, parameterKey, newValue, persistState, error);
|
||||
std::lock_guard<std::mutex> lock(mRuntimeHost.mMutex);
|
||||
|
||||
RuntimeHost::LayerPersistentState* matchedLayer = nullptr;
|
||||
const ShaderPackage* matchedPackage = nullptr;
|
||||
std::vector<ShaderParameterDefinition>::const_iterator parameterIt;
|
||||
if (!TryResolveStoredLayerAndParameterByControlKeyLocked(layerKey, parameterKey, matchedLayer, matchedPackage, parameterIt, error))
|
||||
return false;
|
||||
|
||||
if (parameterIt->type == ShaderParameterType::Trigger)
|
||||
{
|
||||
ShaderParameterValue& value = matchedLayer->parameterValues[parameterIt->id];
|
||||
const double previousCount = value.numberValues.empty() ? 0.0 : value.numberValues[0];
|
||||
const double triggerTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - mRuntimeHost.mStartTime).count();
|
||||
value.numberValues = { previousCount + 1.0, triggerTime };
|
||||
mRuntimeHost.MarkParameterStateDirtyLocked();
|
||||
return true;
|
||||
}
|
||||
|
||||
ShaderParameterValue normalized;
|
||||
if (!mRuntimeHost.NormalizeAndValidateValue(*parameterIt, newValue, normalized, error))
|
||||
return false;
|
||||
|
||||
matchedLayer->parameterValues[parameterIt->id] = normalized;
|
||||
mRuntimeHost.MarkParameterStateDirtyLocked();
|
||||
return !persistState || mRuntimeHost.SavePersistentState(error);
|
||||
}
|
||||
|
||||
bool RuntimeStore::ResetStoredLayerParameterValues(const std::string& layerId, std::string& error)
|
||||
@@ -551,3 +591,45 @@ JsonValue RuntimeStore::SerializeLayerStack() const
|
||||
{
|
||||
return mRuntimeHost.SerializeLayerStackLocked();
|
||||
}
|
||||
|
||||
bool RuntimeStore::TryResolveStoredLayerAndParameterByControlKeyLocked(const std::string& layerKey, const std::string& parameterKey,
|
||||
RuntimeHost::LayerPersistentState*& matchedLayer, const ShaderPackage*& matchedPackage,
|
||||
std::vector<ShaderParameterDefinition>::const_iterator& parameterIt, std::string& error) const
|
||||
{
|
||||
matchedLayer = nullptr;
|
||||
matchedPackage = nullptr;
|
||||
|
||||
for (RuntimeHost::LayerPersistentState& layer : mRuntimeHost.mPersistentState.layers)
|
||||
{
|
||||
auto shaderIt = mRuntimeHost.mPackagesById.find(layer.shaderId);
|
||||
if (shaderIt == mRuntimeHost.mPackagesById.end())
|
||||
continue;
|
||||
|
||||
if (MatchesControlKey(layer.id, layerKey) || MatchesControlKey(shaderIt->second.id, layerKey) ||
|
||||
MatchesControlKey(shaderIt->second.displayName, layerKey))
|
||||
{
|
||||
matchedLayer = &layer;
|
||||
matchedPackage = &shaderIt->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchedLayer || !matchedPackage)
|
||||
{
|
||||
error = "Unknown OSC layer key: " + layerKey;
|
||||
return false;
|
||||
}
|
||||
|
||||
parameterIt = std::find_if(matchedPackage->parameters.begin(), matchedPackage->parameters.end(),
|
||||
[¶meterKey](const ShaderParameterDefinition& definition)
|
||||
{
|
||||
return MatchesControlKey(definition.id, parameterKey) || MatchesControlKey(definition.label, parameterKey);
|
||||
});
|
||||
if (parameterIt == matchedPackage->parameters.end())
|
||||
{
|
||||
error = "Unknown OSC parameter key: " + parameterKey;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,9 @@ public:
|
||||
void ClearReloadRequest();
|
||||
|
||||
private:
|
||||
bool TryResolveStoredLayerAndParameterByControlKeyLocked(const std::string& layerKey, const std::string& parameterKey,
|
||||
RuntimeHost::LayerPersistentState*& matchedLayer, const ShaderPackage*& matchedPackage,
|
||||
std::vector<ShaderParameterDefinition>::const_iterator& parameterIt, std::string& error) const;
|
||||
JsonValue BuildRuntimeStateValue() const;
|
||||
JsonValue SerializeLayerStack() const;
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ These are still compatibility seams, not a completed subsystem extraction. Most
|
||||
- runtime startup for path resolution, config load, persistent state load, and shader package scan now initializes through `RuntimeStore`
|
||||
- runtime/UI state JSON composition now lives in `RuntimeStore` instead of `RuntimeHost`
|
||||
- regular stored layer mutations and stack preset save/load now live in `RuntimeStore` instead of `RuntimeHost` public APIs
|
||||
- persisted OSC-by-control-key commits now apply through `RuntimeStore`, while `RuntimeHost` no longer exposes the old OSC smoothing/apply helper
|
||||
- mutation and reload policy now routes through `RuntimeCoordinator`
|
||||
- render-state and shader-build reads in `OpenGLComposite.cpp`, `OpenGLShaderPrograms.cpp`, and `ShaderBuildQueue.cpp` now route through `RuntimeSnapshotProvider`
|
||||
- render-state assembly, cached parameter refresh, and frame-context application now live in `RuntimeSnapshotProvider` instead of `RuntimeHost` public APIs
|
||||
|
||||
Reference in New Issue
Block a user