From f6b26bf28bcc283ccf7144d261f368fe4e5ac7db Mon Sep 17 00:00:00 2001 From: Aiden <68633820+awils27@users.noreply.github.com> Date: Mon, 11 May 2026 00:22:55 +1000 Subject: [PATCH] runtime udates --- .../runtime/RuntimeHost.cpp | 260 ------------------ .../runtime/RuntimeHost.h | 10 - .../runtime/RuntimeStore.cpp | 246 ++++++++++++++++- docs/PHASE_1_SUBSYSTEM_BOUNDARIES_DESIGN.md | 1 + 4 files changed, 237 insertions(+), 280 deletions(-) diff --git a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp index 0259c5e..f64119c 100644 --- a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp @@ -825,182 +825,6 @@ void RuntimeHost::ClearReloadRequest() mReloadRequested = false; } -bool RuntimeHost::AddLayer(const std::string& shaderId, std::string& error) -{ - std::lock_guard lock(mMutex); - auto shaderIt = mPackagesById.find(shaderId); - if (shaderIt == mPackagesById.end()) - { - error = "Unknown shader id: " + shaderId; - return false; - } - - LayerPersistentState layer; - layer.id = GenerateLayerId(); - layer.shaderId = shaderId; - layer.bypass = false; - EnsureLayerDefaultsLocked(layer, shaderIt->second); - mPersistentState.layers.push_back(layer); - mReloadRequested = true; - MarkRenderStateDirtyLocked(); - return SavePersistentState(error); -} - -bool RuntimeHost::RemoveLayer(const std::string& layerId, std::string& error) -{ - std::lock_guard lock(mMutex); - auto it = std::find_if(mPersistentState.layers.begin(), mPersistentState.layers.end(), - [&layerId](const LayerPersistentState& layer) { return layer.id == layerId; }); - if (it == mPersistentState.layers.end()) - { - error = "Unknown layer id: " + layerId; - return false; - } - - mPersistentState.layers.erase(it); - mReloadRequested = true; - MarkRenderStateDirtyLocked(); - return SavePersistentState(error); -} - -bool RuntimeHost::MoveLayer(const std::string& layerId, int direction, std::string& error) -{ - std::lock_guard lock(mMutex); - auto it = std::find_if(mPersistentState.layers.begin(), mPersistentState.layers.end(), - [&layerId](const LayerPersistentState& layer) { return layer.id == layerId; }); - if (it == mPersistentState.layers.end()) - { - error = "Unknown layer id: " + layerId; - return false; - } - - const std::ptrdiff_t index = std::distance(mPersistentState.layers.begin(), it); - const std::ptrdiff_t newIndex = index + direction; - if (newIndex < 0 || newIndex >= static_cast(mPersistentState.layers.size())) - return true; - - std::swap(mPersistentState.layers[index], mPersistentState.layers[newIndex]); - mReloadRequested = true; - MarkRenderStateDirtyLocked(); - return SavePersistentState(error); -} - -bool RuntimeHost::MoveLayerToIndex(const std::string& layerId, std::size_t targetIndex, std::string& error) -{ - std::lock_guard lock(mMutex); - auto it = std::find_if(mPersistentState.layers.begin(), mPersistentState.layers.end(), - [&layerId](const LayerPersistentState& layer) { return layer.id == layerId; }); - if (it == mPersistentState.layers.end()) - { - error = "Unknown layer id: " + layerId; - return false; - } - - if (mPersistentState.layers.empty()) - return true; - - if (targetIndex >= mPersistentState.layers.size()) - targetIndex = mPersistentState.layers.size() - 1; - - const std::size_t sourceIndex = static_cast(std::distance(mPersistentState.layers.begin(), it)); - if (sourceIndex == targetIndex) - return true; - - LayerPersistentState movedLayer = *it; - mPersistentState.layers.erase(mPersistentState.layers.begin() + static_cast(sourceIndex)); - mPersistentState.layers.insert(mPersistentState.layers.begin() + static_cast(targetIndex), movedLayer); - mReloadRequested = true; - MarkRenderStateDirtyLocked(); - return SavePersistentState(error); -} - -bool RuntimeHost::SetLayerBypass(const std::string& layerId, bool bypassed, std::string& error) -{ - std::lock_guard lock(mMutex); - LayerPersistentState* layer = FindLayerById(layerId); - if (!layer) - { - error = "Unknown layer id: " + layerId; - return false; - } - - layer->bypass = bypassed; - mReloadRequested = true; - MarkParameterStateDirtyLocked(); - return SavePersistentState(error); -} - -bool RuntimeHost::SetLayerShader(const std::string& layerId, const std::string& shaderId, std::string& error) -{ - std::lock_guard lock(mMutex); - LayerPersistentState* layer = FindLayerById(layerId); - if (!layer) - { - error = "Unknown layer id: " + layerId; - return false; - } - - auto shaderIt = mPackagesById.find(shaderId); - if (shaderIt == mPackagesById.end()) - { - error = "Unknown shader id: " + shaderId; - return false; - } - - layer->shaderId = shaderId; - layer->parameterValues.clear(); - EnsureLayerDefaultsLocked(*layer, shaderIt->second); - mReloadRequested = true; - MarkRenderStateDirtyLocked(); - return SavePersistentState(error); -} - -bool RuntimeHost::UpdateLayerParameter(const std::string& layerId, const std::string& parameterId, const JsonValue& newValue, std::string& error) -{ - std::lock_guard lock(mMutex); - - LayerPersistentState* layer = FindLayerById(layerId); - if (!layer) - { - error = "Unknown layer id: " + layerId; - return false; - } - - auto shaderIt = mPackagesById.find(layer->shaderId); - if (shaderIt == mPackagesById.end()) - { - error = "Unknown shader id: " + layer->shaderId; - return false; - } - - const ShaderPackage& shaderPackage = shaderIt->second; - auto parameterIt = std::find_if(shaderPackage.parameters.begin(), shaderPackage.parameters.end(), - [¶meterId](const ShaderParameterDefinition& definition) { return definition.id == parameterId; }); - if (parameterIt == shaderPackage.parameters.end()) - { - error = "Unknown parameter id: " + parameterId; - return false; - } - - if (parameterIt->type == ShaderParameterType::Trigger) - { - ShaderParameterValue& value = layer->parameterValues[parameterId]; - const double previousCount = value.numberValues.empty() ? 0.0 : value.numberValues[0]; - const double triggerTime = std::chrono::duration_cast>(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; - - layer->parameterValues[parameterId] = normalized; - MarkParameterStateDirtyLocked(); - return SavePersistentState(error); -} - bool RuntimeHost::UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, std::string& error) { return UpdateLayerParameterByControlKey(layerKey, parameterKey, newValue, true, error); @@ -1196,90 +1020,6 @@ bool RuntimeHost::ApplyOscTargetByControlKey(const std::string& layerKey, const return true; } -bool RuntimeHost::ResetLayerParameters(const std::string& layerId, std::string& error) -{ - std::lock_guard lock(mMutex); - - LayerPersistentState* layer = FindLayerById(layerId); - if (!layer) - { - error = "Unknown layer id: " + layerId; - return false; - } - - auto shaderIt = mPackagesById.find(layer->shaderId); - if (shaderIt == mPackagesById.end()) - { - error = "Unknown shader id: " + layer->shaderId; - return false; - } - - layer->parameterValues.clear(); - EnsureLayerDefaultsLocked(*layer, shaderIt->second); - MarkParameterStateDirtyLocked(); - return SavePersistentState(error); -} - -bool RuntimeHost::SaveStackPreset(const std::string& presetName, std::string& error) const -{ - std::lock_guard lock(mMutex); - const std::string safeStem = MakeSafePresetFileStem(presetName); - if (safeStem.empty()) - { - error = "Preset name must include at least one letter or number."; - return false; - } - - JsonValue root = JsonValue::MakeObject(); - root.set("version", JsonValue(1.0)); - root.set("name", JsonValue(Trim(presetName))); - root.set("layers", SerializeLayerStackLocked()); - - return WriteTextFile(mPresetRoot / (safeStem + ".json"), SerializeJson(root, true), error); -} - -bool RuntimeHost::LoadStackPreset(const std::string& presetName, std::string& error) -{ - std::lock_guard lock(mMutex); - const std::string safeStem = MakeSafePresetFileStem(presetName); - if (safeStem.empty()) - { - error = "Preset name must include at least one letter or number."; - return false; - } - - const std::filesystem::path presetPath = mPresetRoot / (safeStem + ".json"); - std::string presetText = ReadTextFile(presetPath, error); - if (presetText.empty()) - return false; - - JsonValue root; - if (!ParseJson(presetText, root, error)) - return false; - - const JsonValue* layersValue = root.find("layers"); - if (!layersValue || !layersValue->isArray()) - { - error = "Preset file is missing a valid 'layers' array."; - return false; - } - - std::vector nextLayers; - if (!DeserializeLayerStackLocked(*layersValue, nextLayers, error)) - return false; - - if (nextLayers.empty()) - { - error = "Preset does not contain any valid layers."; - return false; - } - - mPersistentState.layers = nextLayers; - mReloadRequested = true; - MarkRenderStateDirtyLocked(); - return SavePersistentState(error); -} - void RuntimeHost::SetCompileStatus(bool succeeded, const std::string& message) { std::lock_guard lock(mMutex); diff --git a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h index e5351d7..aeadc6d 100644 --- a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h +++ b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h @@ -25,19 +25,9 @@ public: bool ManualReloadRequested(); void ClearReloadRequest(); - bool AddLayer(const std::string& shaderId, std::string& error); - bool RemoveLayer(const std::string& layerId, std::string& error); - bool MoveLayer(const std::string& layerId, int direction, std::string& error); - bool MoveLayerToIndex(const std::string& layerId, std::size_t targetIndex, std::string& error); - bool SetLayerBypass(const std::string& layerId, bool bypassed, std::string& error); - bool SetLayerShader(const std::string& layerId, const std::string& shaderId, std::string& error); - bool UpdateLayerParameter(const std::string& layerId, const std::string& parameterId, const JsonValue& newValue, std::string& error); 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); - bool ResetLayerParameters(const std::string& layerId, std::string& error); - bool SaveStackPreset(const std::string& presetName, std::string& error) const; - bool LoadStackPreset(const std::string& presetName, std::string& error); void SetCompileStatus(bool succeeded, const std::string& message); void SetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName); diff --git a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeStore.cpp b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeStore.cpp index 0b7c8f8..6d704f3 100644 --- a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeStore.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeStore.cpp @@ -1,5 +1,21 @@ #include "RuntimeStore.h" +namespace +{ +std::string TrimCopy(const std::string& text) +{ + std::size_t start = 0; + while (start < text.size() && std::isspace(static_cast(text[start]))) + ++start; + + std::size_t end = text.size(); + while (end > start && std::isspace(static_cast(text[end - 1]))) + --end; + + return text.substr(start, end - start); +} +} + RuntimeStore::RuntimeStore(RuntimeHost& runtimeHost) : mRuntimeHost(runtimeHost) { @@ -64,37 +80,178 @@ std::string RuntimeStore::BuildPersistentStateJson() const bool RuntimeStore::CreateStoredLayer(const std::string& shaderId, std::string& error) { - return mRuntimeHost.AddLayer(shaderId, error); + std::lock_guard lock(mRuntimeHost.mMutex); + auto shaderIt = mRuntimeHost.mPackagesById.find(shaderId); + if (shaderIt == mRuntimeHost.mPackagesById.end()) + { + error = "Unknown shader id: " + shaderId; + return false; + } + + RuntimeHost::LayerPersistentState layer; + layer.id = mRuntimeHost.GenerateLayerId(); + layer.shaderId = shaderId; + layer.bypass = false; + mRuntimeHost.EnsureLayerDefaultsLocked(layer, shaderIt->second); + mRuntimeHost.mPersistentState.layers.push_back(layer); + mRuntimeHost.mReloadRequested = true; + mRuntimeHost.MarkRenderStateDirtyLocked(); + return mRuntimeHost.SavePersistentState(error); } bool RuntimeStore::DeleteStoredLayer(const std::string& layerId, std::string& error) { - return mRuntimeHost.RemoveLayer(layerId, error); + std::lock_guard lock(mRuntimeHost.mMutex); + auto it = std::find_if(mRuntimeHost.mPersistentState.layers.begin(), mRuntimeHost.mPersistentState.layers.end(), + [&layerId](const RuntimeHost::LayerPersistentState& layer) { return layer.id == layerId; }); + if (it == mRuntimeHost.mPersistentState.layers.end()) + { + error = "Unknown layer id: " + layerId; + return false; + } + + mRuntimeHost.mPersistentState.layers.erase(it); + mRuntimeHost.mReloadRequested = true; + mRuntimeHost.MarkRenderStateDirtyLocked(); + return mRuntimeHost.SavePersistentState(error); } bool RuntimeStore::MoveStoredLayer(const std::string& layerId, int direction, std::string& error) { - return mRuntimeHost.MoveLayer(layerId, direction, error); + std::lock_guard lock(mRuntimeHost.mMutex); + auto it = std::find_if(mRuntimeHost.mPersistentState.layers.begin(), mRuntimeHost.mPersistentState.layers.end(), + [&layerId](const RuntimeHost::LayerPersistentState& layer) { return layer.id == layerId; }); + if (it == mRuntimeHost.mPersistentState.layers.end()) + { + error = "Unknown layer id: " + layerId; + return false; + } + + const std::ptrdiff_t index = std::distance(mRuntimeHost.mPersistentState.layers.begin(), it); + const std::ptrdiff_t newIndex = index + direction; + if (newIndex < 0 || newIndex >= static_cast(mRuntimeHost.mPersistentState.layers.size())) + return true; + + std::swap(mRuntimeHost.mPersistentState.layers[index], mRuntimeHost.mPersistentState.layers[newIndex]); + mRuntimeHost.mReloadRequested = true; + mRuntimeHost.MarkRenderStateDirtyLocked(); + return mRuntimeHost.SavePersistentState(error); } bool RuntimeStore::MoveStoredLayerToIndex(const std::string& layerId, std::size_t targetIndex, std::string& error) { - return mRuntimeHost.MoveLayerToIndex(layerId, targetIndex, error); + std::lock_guard lock(mRuntimeHost.mMutex); + auto it = std::find_if(mRuntimeHost.mPersistentState.layers.begin(), mRuntimeHost.mPersistentState.layers.end(), + [&layerId](const RuntimeHost::LayerPersistentState& layer) { return layer.id == layerId; }); + if (it == mRuntimeHost.mPersistentState.layers.end()) + { + error = "Unknown layer id: " + layerId; + return false; + } + + if (mRuntimeHost.mPersistentState.layers.empty()) + return true; + + if (targetIndex >= mRuntimeHost.mPersistentState.layers.size()) + targetIndex = mRuntimeHost.mPersistentState.layers.size() - 1; + + const std::size_t sourceIndex = static_cast(std::distance(mRuntimeHost.mPersistentState.layers.begin(), it)); + if (sourceIndex == targetIndex) + return true; + + RuntimeHost::LayerPersistentState movedLayer = *it; + mRuntimeHost.mPersistentState.layers.erase(mRuntimeHost.mPersistentState.layers.begin() + static_cast(sourceIndex)); + mRuntimeHost.mPersistentState.layers.insert(mRuntimeHost.mPersistentState.layers.begin() + static_cast(targetIndex), movedLayer); + mRuntimeHost.mReloadRequested = true; + mRuntimeHost.MarkRenderStateDirtyLocked(); + return mRuntimeHost.SavePersistentState(error); } bool RuntimeStore::SetStoredLayerBypassState(const std::string& layerId, bool bypassed, std::string& error) { - return mRuntimeHost.SetLayerBypass(layerId, bypassed, error); + std::lock_guard lock(mRuntimeHost.mMutex); + RuntimeHost::LayerPersistentState* layer = mRuntimeHost.FindLayerById(layerId); + if (!layer) + { + error = "Unknown layer id: " + layerId; + return false; + } + + layer->bypass = bypassed; + mRuntimeHost.mReloadRequested = true; + mRuntimeHost.MarkParameterStateDirtyLocked(); + return mRuntimeHost.SavePersistentState(error); } bool RuntimeStore::SetStoredLayerShaderSelection(const std::string& layerId, const std::string& shaderId, std::string& error) { - return mRuntimeHost.SetLayerShader(layerId, shaderId, error); + std::lock_guard lock(mRuntimeHost.mMutex); + RuntimeHost::LayerPersistentState* layer = mRuntimeHost.FindLayerById(layerId); + if (!layer) + { + error = "Unknown layer id: " + layerId; + return false; + } + + auto shaderIt = mRuntimeHost.mPackagesById.find(shaderId); + if (shaderIt == mRuntimeHost.mPackagesById.end()) + { + error = "Unknown shader id: " + shaderId; + return false; + } + + layer->shaderId = shaderId; + layer->parameterValues.clear(); + mRuntimeHost.EnsureLayerDefaultsLocked(*layer, shaderIt->second); + mRuntimeHost.mReloadRequested = true; + mRuntimeHost.MarkRenderStateDirtyLocked(); + return mRuntimeHost.SavePersistentState(error); } bool RuntimeStore::SetStoredParameterValue(const std::string& layerId, const std::string& parameterId, const JsonValue& newValue, std::string& error) { - return mRuntimeHost.UpdateLayerParameter(layerId, parameterId, newValue, error); + std::lock_guard lock(mRuntimeHost.mMutex); + + RuntimeHost::LayerPersistentState* layer = mRuntimeHost.FindLayerById(layerId); + if (!layer) + { + error = "Unknown layer id: " + layerId; + return false; + } + + auto shaderIt = mRuntimeHost.mPackagesById.find(layer->shaderId); + if (shaderIt == mRuntimeHost.mPackagesById.end()) + { + error = "Unknown shader id: " + layer->shaderId; + return false; + } + + const ShaderPackage& shaderPackage = shaderIt->second; + auto parameterIt = std::find_if(shaderPackage.parameters.begin(), shaderPackage.parameters.end(), + [¶meterId](const ShaderParameterDefinition& definition) { return definition.id == parameterId; }); + if (parameterIt == shaderPackage.parameters.end()) + { + error = "Unknown parameter id: " + parameterId; + return false; + } + + if (parameterIt->type == ShaderParameterType::Trigger) + { + ShaderParameterValue& value = layer->parameterValues[parameterId]; + const double previousCount = value.numberValues.empty() ? 0.0 : value.numberValues[0]; + const double triggerTime = std::chrono::duration_cast>(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; + + layer->parameterValues[parameterId] = normalized; + mRuntimeHost.MarkParameterStateDirtyLocked(); + return mRuntimeHost.SavePersistentState(error); } bool RuntimeStore::SetStoredParameterValueByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, std::string& error) @@ -109,17 +266,86 @@ bool RuntimeStore::SetStoredParameterValueByControlKey(const std::string& layerK bool RuntimeStore::ResetStoredLayerParameterValues(const std::string& layerId, std::string& error) { - return mRuntimeHost.ResetLayerParameters(layerId, error); + std::lock_guard lock(mRuntimeHost.mMutex); + + RuntimeHost::LayerPersistentState* layer = mRuntimeHost.FindLayerById(layerId); + if (!layer) + { + error = "Unknown layer id: " + layerId; + return false; + } + + auto shaderIt = mRuntimeHost.mPackagesById.find(layer->shaderId); + if (shaderIt == mRuntimeHost.mPackagesById.end()) + { + error = "Unknown shader id: " + layer->shaderId; + return false; + } + + layer->parameterValues.clear(); + mRuntimeHost.EnsureLayerDefaultsLocked(*layer, shaderIt->second); + mRuntimeHost.MarkParameterStateDirtyLocked(); + return mRuntimeHost.SavePersistentState(error); } bool RuntimeStore::SaveStackPresetSnapshot(const std::string& presetName, std::string& error) const { - return mRuntimeHost.SaveStackPreset(presetName, error); + std::lock_guard lock(mRuntimeHost.mMutex); + const std::string safeStem = mRuntimeHost.MakeSafePresetFileStem(presetName); + if (safeStem.empty()) + { + error = "Preset name must include at least one letter or number."; + return false; + } + + JsonValue root = JsonValue::MakeObject(); + root.set("version", JsonValue(1.0)); + root.set("name", JsonValue(TrimCopy(presetName))); + root.set("layers", mRuntimeHost.SerializeLayerStackLocked()); + + return mRuntimeHost.WriteTextFile(mRuntimeHost.mPresetRoot / (safeStem + ".json"), SerializeJson(root, true), error); } bool RuntimeStore::LoadStackPresetSnapshot(const std::string& presetName, std::string& error) { - return mRuntimeHost.LoadStackPreset(presetName, error); + std::lock_guard lock(mRuntimeHost.mMutex); + const std::string safeStem = mRuntimeHost.MakeSafePresetFileStem(presetName); + if (safeStem.empty()) + { + error = "Preset name must include at least one letter or number."; + return false; + } + + const std::filesystem::path presetPath = mRuntimeHost.mPresetRoot / (safeStem + ".json"); + std::string presetText = mRuntimeHost.ReadTextFile(presetPath, error); + if (presetText.empty()) + return false; + + JsonValue root; + if (!ParseJson(presetText, root, error)) + return false; + + const JsonValue* layersValue = root.find("layers"); + if (!layersValue || !layersValue->isArray()) + { + error = "Preset file is missing a valid 'layers' array."; + return false; + } + + std::vector nextLayers; + if (!mRuntimeHost.DeserializeLayerStackLocked(*layersValue, nextLayers, error)) + return false; + + if (nextLayers.empty()) + { + error = "Preset does not contain any valid layers."; + return false; + } + + mRuntimeHost.mPersistentState.layers = nextLayers; + mRuntimeHost.mReloadRequested = true; + mRuntimeHost.MarkRenderStateDirtyLocked(); + return mRuntimeHost.SavePersistentState(error); } const std::filesystem::path& RuntimeStore::GetRuntimeRepositoryRoot() const diff --git a/docs/PHASE_1_SUBSYSTEM_BOUNDARIES_DESIGN.md b/docs/PHASE_1_SUBSYSTEM_BOUNDARIES_DESIGN.md index 716ea71..72d67bf 100644 --- a/docs/PHASE_1_SUBSYSTEM_BOUNDARIES_DESIGN.md +++ b/docs/PHASE_1_SUBSYSTEM_BOUNDARIES_DESIGN.md @@ -117,6 +117,7 @@ These are still compatibility seams, not a completed subsystem extraction. Most - store-facing UI/runtime control calls in `OpenGLCompositeRuntimeControls.cpp` now route through `RuntimeStore` - 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 - 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