phase 1 runtime complete
This commit is contained in:
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
@@ -11,7 +11,8 @@
|
||||
"--config",
|
||||
"Debug",
|
||||
"--target",
|
||||
"LoopThroughWithOpenGLCompositing"
|
||||
"LoopThroughWithOpenGLCompositing",
|
||||
"--parallel"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
@@ -29,7 +30,8 @@
|
||||
"--config",
|
||||
"Release",
|
||||
"--target",
|
||||
"LoopThroughWithOpenGLCompositing"
|
||||
"LoopThroughWithOpenGLCompositing",
|
||||
"--parallel"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
|
||||
@@ -104,8 +104,6 @@ set(APP_SOURCES
|
||||
"${APP_DIR}/resource.h"
|
||||
"${APP_DIR}/runtime/coordination/RuntimeCoordinator.cpp"
|
||||
"${APP_DIR}/runtime/coordination/RuntimeCoordinator.h"
|
||||
"${APP_DIR}/runtime/legacy/RuntimeHost.cpp"
|
||||
"${APP_DIR}/runtime/legacy/RuntimeHost.h"
|
||||
"${APP_DIR}/runtime/presentation/RuntimeStateJson.cpp"
|
||||
"${APP_DIR}/runtime/presentation/RuntimeStateJson.h"
|
||||
"${APP_DIR}/runtime/presentation/RuntimeStatePresenter.cpp"
|
||||
@@ -120,6 +118,7 @@ set(APP_SOURCES
|
||||
"${APP_DIR}/runtime/store/RuntimeConfigStore.h"
|
||||
"${APP_DIR}/runtime/store/RuntimeStore.cpp"
|
||||
"${APP_DIR}/runtime/store/RuntimeStore.h"
|
||||
"${APP_DIR}/runtime/store/RuntimeStoreReadModels.h"
|
||||
"${APP_DIR}/runtime/store/ShaderPackageCatalog.cpp"
|
||||
"${APP_DIR}/runtime/store/ShaderPackageCatalog.h"
|
||||
"${APP_DIR}/runtime/support/RuntimeJson.cpp"
|
||||
@@ -159,7 +158,6 @@ target_include_directories(LoopThroughWithOpenGLCompositing PRIVATE
|
||||
"${APP_DIR}/platform"
|
||||
"${APP_DIR}/runtime"
|
||||
"${APP_DIR}/runtime/coordination"
|
||||
"${APP_DIR}/runtime/legacy"
|
||||
"${APP_DIR}/runtime/presentation"
|
||||
"${APP_DIR}/runtime/snapshot"
|
||||
"${APP_DIR}/runtime/store"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,103 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "HealthTelemetry.h"
|
||||
#include "RuntimeJson.h"
|
||||
#include "ShaderTypes.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class RuntimeStore;
|
||||
class RuntimeHost
|
||||
{
|
||||
public:
|
||||
RuntimeHost();
|
||||
HealthTelemetry& GetHealthTelemetry() { return mHealthTelemetry; }
|
||||
const HealthTelemetry& GetHealthTelemetry() const { return mHealthTelemetry; }
|
||||
bool AutoReloadEnabled() const { return mAutoReloadEnabled; }
|
||||
|
||||
private:
|
||||
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;
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
private:
|
||||
friend class RuntimeStore;
|
||||
friend class RuntimeCoordinator;
|
||||
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;
|
||||
};
|
||||
@@ -22,37 +22,50 @@ std::string ShaderParameterTypeToString(ShaderParameterType type)
|
||||
|
||||
JsonValue RuntimeStateJson::SerializeLayerStack(const LayerStackStore& layerStack, const ShaderPackageCatalog& shaderCatalog)
|
||||
{
|
||||
JsonValue layers = JsonValue::MakeArray();
|
||||
for (const LayerStackStore::LayerPersistentState& layer : layerStack.Layers())
|
||||
std::map<std::string, ShaderPackage> packagesById;
|
||||
for (const std::string& packageId : shaderCatalog.PackageOrder())
|
||||
{
|
||||
const ShaderPackage* shaderPackage = shaderCatalog.FindPackage(layer.shaderId);
|
||||
if (!shaderPackage)
|
||||
ShaderPackage shaderPackage;
|
||||
if (shaderCatalog.CopyPackage(packageId, shaderPackage))
|
||||
packagesById[packageId] = shaderPackage;
|
||||
}
|
||||
return SerializeLayerStack(layerStack.Layers(), packagesById);
|
||||
}
|
||||
|
||||
JsonValue RuntimeStateJson::SerializeLayerStack(const std::vector<LayerStackStore::LayerPersistentState>& layerStates, const std::map<std::string, ShaderPackage>& packagesById)
|
||||
{
|
||||
JsonValue layersValue = JsonValue::MakeArray();
|
||||
for (const LayerStackStore::LayerPersistentState& layer : layerStates)
|
||||
{
|
||||
auto shaderIt = packagesById.find(layer.shaderId);
|
||||
if (shaderIt == packagesById.end())
|
||||
continue;
|
||||
const ShaderPackage& shaderPackage = shaderIt->second;
|
||||
|
||||
JsonValue layerValue = JsonValue::MakeObject();
|
||||
layerValue.set("id", JsonValue(layer.id));
|
||||
layerValue.set("shaderId", JsonValue(layer.shaderId));
|
||||
layerValue.set("shaderName", JsonValue(shaderPackage->displayName));
|
||||
layerValue.set("shaderName", JsonValue(shaderPackage.displayName));
|
||||
layerValue.set("bypass", JsonValue(layer.bypass));
|
||||
if (shaderPackage->temporal.enabled)
|
||||
if (shaderPackage.temporal.enabled)
|
||||
{
|
||||
JsonValue temporal = JsonValue::MakeObject();
|
||||
temporal.set("enabled", JsonValue(true));
|
||||
temporal.set("historySource", JsonValue(TemporalHistorySourceToString(shaderPackage->temporal.historySource)));
|
||||
temporal.set("requestedHistoryLength", JsonValue(static_cast<double>(shaderPackage->temporal.requestedHistoryLength)));
|
||||
temporal.set("effectiveHistoryLength", JsonValue(static_cast<double>(shaderPackage->temporal.effectiveHistoryLength)));
|
||||
temporal.set("historySource", JsonValue(TemporalHistorySourceToString(shaderPackage.temporal.historySource)));
|
||||
temporal.set("requestedHistoryLength", JsonValue(static_cast<double>(shaderPackage.temporal.requestedHistoryLength)));
|
||||
temporal.set("effectiveHistoryLength", JsonValue(static_cast<double>(shaderPackage.temporal.effectiveHistoryLength)));
|
||||
layerValue.set("temporal", temporal);
|
||||
}
|
||||
if (shaderPackage->feedback.enabled)
|
||||
if (shaderPackage.feedback.enabled)
|
||||
{
|
||||
JsonValue feedback = JsonValue::MakeObject();
|
||||
feedback.set("enabled", JsonValue(true));
|
||||
feedback.set("writePass", JsonValue(shaderPackage->feedback.writePassId));
|
||||
feedback.set("writePass", JsonValue(shaderPackage.feedback.writePassId));
|
||||
layerValue.set("feedback", feedback);
|
||||
}
|
||||
|
||||
JsonValue parameters = JsonValue::MakeArray();
|
||||
for (const ShaderParameterDefinition& definition : shaderPackage->parameters)
|
||||
for (const ShaderParameterDefinition& definition : shaderPackage.parameters)
|
||||
{
|
||||
JsonValue parameter = JsonValue::MakeObject();
|
||||
parameter.set("id", JsonValue(definition.id));
|
||||
@@ -111,9 +124,9 @@ JsonValue RuntimeStateJson::SerializeLayerStack(const LayerStackStore& layerStac
|
||||
}
|
||||
|
||||
layerValue.set("parameters", parameters);
|
||||
layers.pushBack(layerValue);
|
||||
layersValue.pushBack(layerValue);
|
||||
}
|
||||
return layers;
|
||||
return layersValue;
|
||||
}
|
||||
|
||||
JsonValue RuntimeStateJson::SerializeParameterValue(const ShaderParameterDefinition& definition, const ShaderParameterValue& value)
|
||||
|
||||
@@ -5,12 +5,15 @@
|
||||
#include "ShaderPackageCatalog.h"
|
||||
#include "ShaderTypes.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class RuntimeStateJson
|
||||
{
|
||||
public:
|
||||
static JsonValue SerializeLayerStack(const LayerStackStore& layerStack, const ShaderPackageCatalog& shaderCatalog);
|
||||
static JsonValue SerializeLayerStack(const std::vector<LayerStackStore::LayerPersistentState>& layers, const std::map<std::string, ShaderPackage>& packagesById);
|
||||
static JsonValue SerializeParameterValue(const ShaderParameterDefinition& definition, const ShaderParameterValue& value);
|
||||
static std::string TemporalHistorySourceToString(TemporalHistorySource source);
|
||||
};
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
#include "RuntimeStateJson.h"
|
||||
#include "RuntimeStore.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
std::string RuntimeStatePresenter::BuildRuntimeStateJson(const RuntimeStore& runtimeStore)
|
||||
{
|
||||
return SerializeJson(BuildRuntimeStateValue(runtimeStore), true);
|
||||
@@ -12,30 +10,30 @@ std::string RuntimeStatePresenter::BuildRuntimeStateJson(const RuntimeStore& run
|
||||
|
||||
JsonValue RuntimeStatePresenter::BuildRuntimeStateValue(const RuntimeStore& runtimeStore)
|
||||
{
|
||||
const HealthTelemetry::Snapshot telemetrySnapshot = runtimeStore.mHealthTelemetry.GetSnapshot();
|
||||
std::lock_guard<std::mutex> lock(runtimeStore.mMutex);
|
||||
const RuntimeStatePresentationReadModel model = runtimeStore.BuildRuntimeStatePresentationReadModel();
|
||||
const HealthTelemetry::Snapshot& telemetrySnapshot = model.telemetry;
|
||||
|
||||
JsonValue root = JsonValue::MakeObject();
|
||||
|
||||
JsonValue app = JsonValue::MakeObject();
|
||||
app.set("serverPort", JsonValue(static_cast<double>(runtimeStore.mServerPort)));
|
||||
app.set("oscPort", JsonValue(static_cast<double>(runtimeStore.mConfigStore.GetConfig().oscPort)));
|
||||
app.set("oscBindAddress", JsonValue(runtimeStore.mConfigStore.GetConfig().oscBindAddress));
|
||||
app.set("oscSmoothing", JsonValue(runtimeStore.mConfigStore.GetConfig().oscSmoothing));
|
||||
app.set("autoReload", JsonValue(runtimeStore.mAutoReloadEnabled));
|
||||
app.set("maxTemporalHistoryFrames", JsonValue(static_cast<double>(runtimeStore.mConfigStore.GetConfig().maxTemporalHistoryFrames)));
|
||||
app.set("previewFps", JsonValue(static_cast<double>(runtimeStore.mConfigStore.GetConfig().previewFps)));
|
||||
app.set("enableExternalKeying", JsonValue(runtimeStore.mConfigStore.GetConfig().enableExternalKeying));
|
||||
app.set("inputVideoFormat", JsonValue(runtimeStore.mConfigStore.GetConfig().inputVideoFormat));
|
||||
app.set("inputFrameRate", JsonValue(runtimeStore.mConfigStore.GetConfig().inputFrameRate));
|
||||
app.set("outputVideoFormat", JsonValue(runtimeStore.mConfigStore.GetConfig().outputVideoFormat));
|
||||
app.set("outputFrameRate", JsonValue(runtimeStore.mConfigStore.GetConfig().outputFrameRate));
|
||||
app.set("serverPort", JsonValue(static_cast<double>(model.serverPort)));
|
||||
app.set("oscPort", JsonValue(static_cast<double>(model.config.oscPort)));
|
||||
app.set("oscBindAddress", JsonValue(model.config.oscBindAddress));
|
||||
app.set("oscSmoothing", JsonValue(model.config.oscSmoothing));
|
||||
app.set("autoReload", JsonValue(model.autoReloadEnabled));
|
||||
app.set("maxTemporalHistoryFrames", JsonValue(static_cast<double>(model.config.maxTemporalHistoryFrames)));
|
||||
app.set("previewFps", JsonValue(static_cast<double>(model.config.previewFps)));
|
||||
app.set("enableExternalKeying", JsonValue(model.config.enableExternalKeying));
|
||||
app.set("inputVideoFormat", JsonValue(model.config.inputVideoFormat));
|
||||
app.set("inputFrameRate", JsonValue(model.config.inputFrameRate));
|
||||
app.set("outputVideoFormat", JsonValue(model.config.outputVideoFormat));
|
||||
app.set("outputFrameRate", JsonValue(model.config.outputFrameRate));
|
||||
root.set("app", app);
|
||||
|
||||
JsonValue runtime = JsonValue::MakeObject();
|
||||
runtime.set("layerCount", JsonValue(static_cast<double>(runtimeStore.mLayerStack.LayerCount())));
|
||||
runtime.set("compileSucceeded", JsonValue(runtimeStore.mCompileSucceeded));
|
||||
runtime.set("compileMessage", JsonValue(runtimeStore.mCompileMessage));
|
||||
runtime.set("layerCount", JsonValue(static_cast<double>(model.layerStack.LayerCount())));
|
||||
runtime.set("compileSucceeded", JsonValue(model.compileSucceeded));
|
||||
runtime.set("compileMessage", JsonValue(model.compileMessage));
|
||||
root.set("runtime", runtime);
|
||||
|
||||
JsonValue video = JsonValue::MakeObject();
|
||||
@@ -83,7 +81,7 @@ JsonValue RuntimeStatePresenter::BuildRuntimeStateValue(const RuntimeStore& runt
|
||||
root.set("performance", performance);
|
||||
|
||||
JsonValue shaderLibrary = JsonValue::MakeArray();
|
||||
for (const ShaderPackageStatus& status : runtimeStore.mShaderCatalog.PackageStatuses())
|
||||
for (const ShaderPackageStatus& status : model.packageStatuses)
|
||||
{
|
||||
JsonValue shader = JsonValue::MakeObject();
|
||||
shader.set("id", JsonValue(status.id));
|
||||
@@ -94,21 +92,23 @@ JsonValue RuntimeStatePresenter::BuildRuntimeStateValue(const RuntimeStore& runt
|
||||
if (!status.available)
|
||||
shader.set("error", JsonValue(status.error));
|
||||
|
||||
const ShaderPackage* shaderPackage = runtimeStore.mShaderCatalog.FindPackage(status.id);
|
||||
if (status.available && shaderPackage && shaderPackage->temporal.enabled)
|
||||
auto shaderIt = model.shaderCatalog.packagesById.find(status.id);
|
||||
if (status.available && shaderIt != model.shaderCatalog.packagesById.end() && shaderIt->second.temporal.enabled)
|
||||
{
|
||||
const ShaderPackage& shaderPackage = shaderIt->second;
|
||||
JsonValue temporal = JsonValue::MakeObject();
|
||||
temporal.set("enabled", JsonValue(true));
|
||||
temporal.set("historySource", JsonValue(RuntimeStateJson::TemporalHistorySourceToString(shaderPackage->temporal.historySource)));
|
||||
temporal.set("requestedHistoryLength", JsonValue(static_cast<double>(shaderPackage->temporal.requestedHistoryLength)));
|
||||
temporal.set("effectiveHistoryLength", JsonValue(static_cast<double>(shaderPackage->temporal.effectiveHistoryLength)));
|
||||
temporal.set("historySource", JsonValue(RuntimeStateJson::TemporalHistorySourceToString(shaderPackage.temporal.historySource)));
|
||||
temporal.set("requestedHistoryLength", JsonValue(static_cast<double>(shaderPackage.temporal.requestedHistoryLength)));
|
||||
temporal.set("effectiveHistoryLength", JsonValue(static_cast<double>(shaderPackage.temporal.effectiveHistoryLength)));
|
||||
shader.set("temporal", temporal);
|
||||
}
|
||||
if (status.available && shaderPackage && shaderPackage->feedback.enabled)
|
||||
if (status.available && shaderIt != model.shaderCatalog.packagesById.end() && shaderIt->second.feedback.enabled)
|
||||
{
|
||||
const ShaderPackage& shaderPackage = shaderIt->second;
|
||||
JsonValue feedback = JsonValue::MakeObject();
|
||||
feedback.set("enabled", JsonValue(true));
|
||||
feedback.set("writePass", JsonValue(shaderPackage->feedback.writePassId));
|
||||
feedback.set("writePass", JsonValue(shaderPackage.feedback.writePassId));
|
||||
shader.set("feedback", feedback);
|
||||
}
|
||||
shaderLibrary.pushBack(shader);
|
||||
@@ -116,10 +116,10 @@ JsonValue RuntimeStatePresenter::BuildRuntimeStateValue(const RuntimeStore& runt
|
||||
root.set("shaders", shaderLibrary);
|
||||
|
||||
JsonValue stackPresets = JsonValue::MakeArray();
|
||||
for (const std::string& presetName : runtimeStore.GetStackPresetNamesLocked())
|
||||
for (const std::string& presetName : model.stackPresetNames)
|
||||
stackPresets.pushBack(JsonValue(presetName));
|
||||
root.set("stackPresets", stackPresets);
|
||||
|
||||
root.set("layers", RuntimeStateJson::SerializeLayerStack(runtimeStore.mLayerStack, runtimeStore.mShaderCatalog));
|
||||
root.set("layers", RuntimeStateJson::SerializeLayerStack(model.layerStack.Layers(), model.shaderCatalog.packagesById));
|
||||
return root;
|
||||
}
|
||||
|
||||
@@ -24,19 +24,14 @@ bool RenderSnapshotBuilder::BuildLayerPassFragmentShaderSources(const std::strin
|
||||
if (!mRuntimeStore.CopyShaderPackageForStoredLayer(layerId, shaderPackage, error))
|
||||
return false;
|
||||
|
||||
std::filesystem::path repoRoot;
|
||||
std::filesystem::path wrapperPath;
|
||||
std::filesystem::path generatedGlslPath;
|
||||
std::filesystem::path patchedGlslPath;
|
||||
unsigned maxTemporalHistoryFrames = 0;
|
||||
mRuntimeStore.GetShaderCompilerInputs(repoRoot, wrapperPath, generatedGlslPath, patchedGlslPath, maxTemporalHistoryFrames);
|
||||
const ShaderCompilerInputs inputs = mRuntimeStore.GetShaderCompilerInputs();
|
||||
|
||||
ShaderCompiler compiler(
|
||||
repoRoot,
|
||||
wrapperPath,
|
||||
generatedGlslPath,
|
||||
patchedGlslPath,
|
||||
maxTemporalHistoryFrames);
|
||||
inputs.repoRoot,
|
||||
inputs.wrapperPath,
|
||||
inputs.generatedGlslPath,
|
||||
inputs.patchedGlslPath,
|
||||
inputs.maxTemporalHistoryFrames);
|
||||
passSources.clear();
|
||||
passSources.reserve(shaderPackage.passes.size());
|
||||
for (const ShaderPassDefinition& pass : shaderPackage.passes)
|
||||
@@ -83,34 +78,24 @@ void RenderSnapshotBuilder::AdvanceFrame()
|
||||
|
||||
void RenderSnapshotBuilder::BuildLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mRuntimeStore.mMutex);
|
||||
BuildLayerRenderStatesLocked(outputWidth, outputHeight, states);
|
||||
BuildLayerRenderStates(outputWidth, outputHeight, mRuntimeStore.BuildRenderSnapshotReadModel(), states);
|
||||
}
|
||||
|
||||
bool RenderSnapshotBuilder::TryBuildLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mRuntimeStore.mMutex, std::try_to_lock);
|
||||
if (!lock.owns_lock())
|
||||
return false;
|
||||
|
||||
BuildLayerRenderStatesLocked(outputWidth, outputHeight, states);
|
||||
BuildLayerRenderStates(outputWidth, outputHeight, mRuntimeStore.BuildRenderSnapshotReadModel(), states);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderSnapshotBuilder::TryRefreshLayerParameters(std::vector<RuntimeRenderState>& states) const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mRuntimeStore.mMutex, std::try_to_lock);
|
||||
if (!lock.owns_lock())
|
||||
return false;
|
||||
|
||||
RefreshLayerParametersLocked(states);
|
||||
RefreshLayerParameters(mRuntimeStore.CopyLayerStates(), states);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderSnapshotBuilder::RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState>& states) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mRuntimeStore.mMutex);
|
||||
RefreshDynamicRenderStateFieldsLocked(states);
|
||||
RefreshDynamicRenderStateFields(mRuntimeStore.GetRenderTimingSnapshot(), states);
|
||||
}
|
||||
|
||||
void RenderSnapshotBuilder::MarkRenderStateDirty()
|
||||
@@ -124,37 +109,37 @@ void RenderSnapshotBuilder::MarkParameterStateDirty()
|
||||
mParameterStateVersion.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void RenderSnapshotBuilder::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
||||
void RenderSnapshotBuilder::BuildLayerRenderStates(unsigned outputWidth, unsigned outputHeight, const RenderSnapshotReadModel& readModel, std::vector<RuntimeRenderState>& states) const
|
||||
{
|
||||
states.clear();
|
||||
const HealthTelemetry::SignalStatusSnapshot signalStatus = mRuntimeStore.mHealthTelemetry.GetSignalStatusSnapshot();
|
||||
|
||||
for (const RuntimeStore::LayerPersistentState& layer : mRuntimeStore.mLayerStack.Layers())
|
||||
for (const LayerStackStore::LayerPersistentState& layer : readModel.layers)
|
||||
{
|
||||
const ShaderPackage* shaderPackage = mRuntimeStore.mShaderCatalog.FindPackage(layer.shaderId);
|
||||
if (!shaderPackage)
|
||||
auto shaderIt = readModel.packagesById.find(layer.shaderId);
|
||||
if (shaderIt == readModel.packagesById.end())
|
||||
continue;
|
||||
const ShaderPackage& shaderPackage = shaderIt->second;
|
||||
|
||||
RuntimeRenderState state;
|
||||
state.layerId = layer.id;
|
||||
state.shaderId = layer.shaderId;
|
||||
state.shaderName = shaderPackage->displayName;
|
||||
state.shaderName = shaderPackage.displayName;
|
||||
state.mixAmount = 1.0;
|
||||
state.bypass = layer.bypass ? 1.0 : 0.0;
|
||||
state.inputWidth = signalStatus.width;
|
||||
state.inputHeight = signalStatus.height;
|
||||
state.inputWidth = readModel.signalStatus.width;
|
||||
state.inputHeight = readModel.signalStatus.height;
|
||||
state.outputWidth = outputWidth;
|
||||
state.outputHeight = outputHeight;
|
||||
state.parameterDefinitions = shaderPackage->parameters;
|
||||
state.textureAssets = shaderPackage->textureAssets;
|
||||
state.fontAssets = shaderPackage->fontAssets;
|
||||
state.isTemporal = shaderPackage->temporal.enabled;
|
||||
state.temporalHistorySource = shaderPackage->temporal.historySource;
|
||||
state.requestedTemporalHistoryLength = shaderPackage->temporal.requestedHistoryLength;
|
||||
state.effectiveTemporalHistoryLength = shaderPackage->temporal.effectiveHistoryLength;
|
||||
state.feedback = shaderPackage->feedback;
|
||||
state.parameterDefinitions = shaderPackage.parameters;
|
||||
state.textureAssets = shaderPackage.textureAssets;
|
||||
state.fontAssets = shaderPackage.fontAssets;
|
||||
state.isTemporal = shaderPackage.temporal.enabled;
|
||||
state.temporalHistorySource = shaderPackage.temporal.historySource;
|
||||
state.requestedTemporalHistoryLength = shaderPackage.temporal.requestedHistoryLength;
|
||||
state.effectiveTemporalHistoryLength = shaderPackage.temporal.effectiveHistoryLength;
|
||||
state.feedback = shaderPackage.feedback;
|
||||
|
||||
for (const ShaderParameterDefinition& definition : shaderPackage->parameters)
|
||||
for (const ShaderParameterDefinition& definition : shaderPackage.parameters)
|
||||
{
|
||||
ShaderParameterValue value = DefaultValueForDefinition(definition);
|
||||
auto valueIt = layer.parameterValues.find(definition.id);
|
||||
@@ -166,16 +151,16 @@ void RenderSnapshotBuilder::BuildLayerRenderStatesLocked(unsigned outputWidth, u
|
||||
states.push_back(state);
|
||||
}
|
||||
|
||||
RefreshDynamicRenderStateFieldsLocked(states);
|
||||
RefreshDynamicRenderStateFields(readModel.timing, states);
|
||||
}
|
||||
|
||||
void RenderSnapshotBuilder::RefreshLayerParametersLocked(std::vector<RuntimeRenderState>& states) const
|
||||
void RenderSnapshotBuilder::RefreshLayerParameters(const std::vector<LayerStackStore::LayerPersistentState>& layers, std::vector<RuntimeRenderState>& states) const
|
||||
{
|
||||
for (RuntimeRenderState& state : states)
|
||||
{
|
||||
const auto layerIt = std::find_if(mRuntimeStore.mLayerStack.Layers().begin(), mRuntimeStore.mLayerStack.Layers().end(),
|
||||
[&state](const RuntimeStore::LayerPersistentState& layer) { return layer.id == state.layerId; });
|
||||
if (layerIt == mRuntimeStore.mLayerStack.Layers().end())
|
||||
const auto layerIt = std::find_if(layers.begin(), layers.end(),
|
||||
[&state](const LayerStackStore::LayerPersistentState& layer) { return layer.id == state.layerId; });
|
||||
if (layerIt == layers.end())
|
||||
continue;
|
||||
|
||||
state.bypass = layerIt->bypass ? 1.0 : 0.0;
|
||||
@@ -191,10 +176,10 @@ void RenderSnapshotBuilder::RefreshLayerParametersLocked(std::vector<RuntimeRend
|
||||
}
|
||||
}
|
||||
|
||||
void RenderSnapshotBuilder::RefreshDynamicRenderStateFieldsLocked(std::vector<RuntimeRenderState>& states) const
|
||||
void RenderSnapshotBuilder::RefreshDynamicRenderStateFields(const RenderTimingSnapshot& timing, std::vector<RuntimeRenderState>& states) const
|
||||
{
|
||||
const RuntimeClockSnapshot clock = GetRuntimeClockSnapshot();
|
||||
const double timeSeconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - mRuntimeStore.mStartTime).count();
|
||||
const double timeSeconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - timing.startTime).count();
|
||||
const double frameCount = static_cast<double>(mFrameCounter.load(std::memory_order_relaxed));
|
||||
|
||||
for (RuntimeRenderState& state : states)
|
||||
@@ -202,7 +187,7 @@ void RenderSnapshotBuilder::RefreshDynamicRenderStateFieldsLocked(std::vector<Ru
|
||||
state.timeSeconds = timeSeconds;
|
||||
state.utcTimeSeconds = clock.utcTimeSeconds;
|
||||
state.utcOffsetSeconds = clock.utcOffsetSeconds;
|
||||
state.startupRandom = mRuntimeStore.mStartupRandom;
|
||||
state.startupRandom = timing.startupRandom;
|
||||
state.frameCount = frameCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "RuntimeStoreReadModels.h"
|
||||
#include "ShaderTypes.h"
|
||||
|
||||
#include <atomic>
|
||||
@@ -32,9 +33,9 @@ public:
|
||||
void MarkParameterStateDirty();
|
||||
|
||||
private:
|
||||
void BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const;
|
||||
void RefreshLayerParametersLocked(std::vector<RuntimeRenderState>& states) const;
|
||||
void RefreshDynamicRenderStateFieldsLocked(std::vector<RuntimeRenderState>& states) const;
|
||||
void BuildLayerRenderStates(unsigned outputWidth, unsigned outputHeight, const RenderSnapshotReadModel& readModel, std::vector<RuntimeRenderState>& states) const;
|
||||
void RefreshLayerParameters(const std::vector<LayerStackStore::LayerPersistentState>& layers, std::vector<RuntimeRenderState>& states) const;
|
||||
void RefreshDynamicRenderStateFields(const RenderTimingSnapshot& timing, std::vector<RuntimeRenderState>& states) const;
|
||||
|
||||
RuntimeStore& mRuntimeStore;
|
||||
std::atomic<uint64_t> mFrameCounter{ 0 };
|
||||
|
||||
@@ -564,15 +564,60 @@ bool RuntimeStore::CopyShaderPackageForStoredLayer(const std::string& layerId, S
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuntimeStore::GetShaderCompilerInputs(std::filesystem::path& repoRoot, std::filesystem::path& wrapperPath,
|
||||
std::filesystem::path& generatedGlslPath, std::filesystem::path& patchedGlslPath, unsigned& maxTemporalHistoryFrames) const
|
||||
ShaderCompilerInputs RuntimeStore::GetShaderCompilerInputs() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
repoRoot = mConfigStore.GetRepoRoot();
|
||||
wrapperPath = mConfigStore.GetWrapperPath();
|
||||
generatedGlslPath = mConfigStore.GetGeneratedGlslPath();
|
||||
patchedGlslPath = mConfigStore.GetPatchedGlslPath();
|
||||
maxTemporalHistoryFrames = mConfigStore.GetConfig().maxTemporalHistoryFrames;
|
||||
ShaderCompilerInputs inputs;
|
||||
inputs.repoRoot = mConfigStore.GetRepoRoot();
|
||||
inputs.wrapperPath = mConfigStore.GetWrapperPath();
|
||||
inputs.generatedGlslPath = mConfigStore.GetGeneratedGlslPath();
|
||||
inputs.patchedGlslPath = mConfigStore.GetPatchedGlslPath();
|
||||
inputs.maxTemporalHistoryFrames = mConfigStore.GetConfig().maxTemporalHistoryFrames;
|
||||
return inputs;
|
||||
}
|
||||
|
||||
RenderSnapshotReadModel RuntimeStore::BuildRenderSnapshotReadModel() const
|
||||
{
|
||||
RenderSnapshotReadModel model;
|
||||
model.signalStatus = mHealthTelemetry.GetSignalStatusSnapshot();
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
model.layers = mLayerStack.Layers();
|
||||
model.packagesById = mShaderCatalog.CaptureSnapshot().packagesById;
|
||||
model.timing.startTime = mStartTime;
|
||||
model.timing.startupRandom = mStartupRandom;
|
||||
return model;
|
||||
}
|
||||
|
||||
std::vector<RuntimeStore::LayerPersistentState> RuntimeStore::CopyLayerStates() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
return mLayerStack.Layers();
|
||||
}
|
||||
|
||||
RenderTimingSnapshot RuntimeStore::GetRenderTimingSnapshot() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
RenderTimingSnapshot snapshot;
|
||||
snapshot.startTime = mStartTime;
|
||||
snapshot.startupRandom = mStartupRandom;
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
RuntimeStatePresentationReadModel RuntimeStore::BuildRuntimeStatePresentationReadModel() const
|
||||
{
|
||||
RuntimeStatePresentationReadModel model;
|
||||
model.telemetry = mHealthTelemetry.GetSnapshot();
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
model.config = mConfigStore.GetConfig();
|
||||
model.layerStack = mLayerStack;
|
||||
model.shaderCatalog = mShaderCatalog.CaptureSnapshot();
|
||||
model.packageStatuses = mShaderCatalog.PackageStatuses();
|
||||
model.stackPresetNames = GetStackPresetNamesLocked();
|
||||
model.serverPort = mServerPort;
|
||||
model.autoReloadEnabled = mAutoReloadEnabled;
|
||||
model.compileSucceeded = mCompileSucceeded;
|
||||
model.compileMessage = mCompileMessage;
|
||||
return model;
|
||||
}
|
||||
|
||||
void RuntimeStore::MarkRenderStateDirtyLocked()
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "RenderSnapshotBuilder.h"
|
||||
#include "RuntimeConfigStore.h"
|
||||
#include "RuntimeJson.h"
|
||||
#include "RuntimeStoreReadModels.h"
|
||||
#include "ShaderPackageCatalog.h"
|
||||
#include "ShaderTypes.h"
|
||||
|
||||
@@ -14,8 +15,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class RuntimeStatePresenter;
|
||||
|
||||
class RuntimeStore
|
||||
{
|
||||
public:
|
||||
@@ -70,19 +69,20 @@ public:
|
||||
|
||||
void SetCompileStatus(bool succeeded, const std::string& message);
|
||||
void ClearReloadRequest();
|
||||
bool CopyShaderPackageForStoredLayer(const std::string& layerId, ShaderPackage& shaderPackage, std::string& error) const;
|
||||
::ShaderCompilerInputs GetShaderCompilerInputs() const;
|
||||
::RenderSnapshotReadModel BuildRenderSnapshotReadModel() const;
|
||||
std::vector<LayerPersistentState> CopyLayerStates() const;
|
||||
::RenderTimingSnapshot GetRenderTimingSnapshot() const;
|
||||
::RuntimeStatePresentationReadModel BuildRuntimeStatePresentationReadModel() const;
|
||||
|
||||
private:
|
||||
friend class RenderSnapshotBuilder;
|
||||
friend class RuntimeStatePresenter;
|
||||
bool LoadPersistentState(std::string& error);
|
||||
bool SavePersistentState(std::string& error) const;
|
||||
bool ScanShaderPackages(std::string& error);
|
||||
std::string ReadTextFile(const std::filesystem::path& path, std::string& error) const;
|
||||
bool WriteTextFile(const std::filesystem::path& path, const std::string& contents, std::string& error) const;
|
||||
std::vector<std::string> GetStackPresetNamesLocked() const;
|
||||
bool CopyShaderPackageForStoredLayer(const std::string& layerId, ShaderPackage& shaderPackage, std::string& error) const;
|
||||
void GetShaderCompilerInputs(std::filesystem::path& repoRoot, std::filesystem::path& wrapperPath,
|
||||
std::filesystem::path& generatedGlslPath, std::filesystem::path& patchedGlslPath, unsigned& maxTemporalHistoryFrames) const;
|
||||
void MarkRenderStateDirtyLocked();
|
||||
void MarkParameterStateDirtyLocked();
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "HealthTelemetry.h"
|
||||
#include "LayerStackStore.h"
|
||||
#include "RuntimeConfigStore.h"
|
||||
#include "ShaderPackageCatalog.h"
|
||||
#include "ShaderTypes.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct ShaderCompilerInputs
|
||||
{
|
||||
std::filesystem::path repoRoot;
|
||||
std::filesystem::path wrapperPath;
|
||||
std::filesystem::path generatedGlslPath;
|
||||
std::filesystem::path patchedGlslPath;
|
||||
unsigned maxTemporalHistoryFrames = 0;
|
||||
};
|
||||
|
||||
struct RenderTimingSnapshot
|
||||
{
|
||||
std::chrono::steady_clock::time_point startTime;
|
||||
double startupRandom = 0.0;
|
||||
};
|
||||
|
||||
struct RenderSnapshotReadModel
|
||||
{
|
||||
std::vector<LayerStackStore::LayerPersistentState> layers;
|
||||
std::map<std::string, ShaderPackage> packagesById;
|
||||
HealthTelemetry::SignalStatusSnapshot signalStatus;
|
||||
RenderTimingSnapshot timing;
|
||||
};
|
||||
|
||||
struct RuntimeStatePresentationReadModel
|
||||
{
|
||||
RuntimeConfigStore::AppConfig config;
|
||||
HealthTelemetry::Snapshot telemetry;
|
||||
LayerStackStore layerStack;
|
||||
ShaderPackageCatalog::Snapshot shaderCatalog;
|
||||
std::vector<ShaderPackageStatus> packageStatuses;
|
||||
std::vector<std::string> stackPresetNames;
|
||||
unsigned short serverPort = 0;
|
||||
bool autoReloadEnabled = false;
|
||||
bool compileSucceeded = false;
|
||||
std::string compileMessage;
|
||||
};
|
||||
@@ -5,8 +5,8 @@
|
||||
#include <string>
|
||||
|
||||
// Phase 1 compatibility seam for status and timing reporting. HealthTelemetry
|
||||
// now owns the current operational status snapshot directly, so callers can
|
||||
// target it without flowing through RuntimeHost-owned backing fields.
|
||||
// owns the current operational status snapshot directly, so callers can report
|
||||
// health without sharing runtime-store state.
|
||||
class HealthTelemetry
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -6,7 +6,7 @@ Phase checklist:
|
||||
|
||||
- [x] Define subsystem boundaries and target architecture
|
||||
- [ ] Introduce an internal event model
|
||||
- [ ] Split `RuntimeHost`
|
||||
- [x] Split `RuntimeHost`
|
||||
- [ ] Make the render thread the sole GL owner
|
||||
- [ ] Refactor live state layering into an explicit composition model
|
||||
- [ ] Move persistence onto a background snapshot writer
|
||||
@@ -32,9 +32,9 @@ Those points are important because they affect not just average performance, but
|
||||
|
||||
## Key Findings
|
||||
|
||||
### 1. `RuntimeHost` is carrying too many responsibilities
|
||||
### 1. The original runtime host carried too many responsibilities
|
||||
|
||||
`RuntimeHost` currently acts as:
|
||||
The original `RuntimeHost` acted as:
|
||||
|
||||
- config store
|
||||
- persistent state store
|
||||
@@ -47,7 +47,7 @@ That makes it a single contention and failure domain. It is also why OSC and ren
|
||||
|
||||
Relevant code:
|
||||
|
||||
- [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:15)
|
||||
- `RuntimeHost.h`
|
||||
|
||||
Recommended direction:
|
||||
|
||||
@@ -258,7 +258,7 @@ Recommended direction:
|
||||
|
||||
Relevant code:
|
||||
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1841)
|
||||
- `RuntimeHost.cpp`
|
||||
|
||||
Recent OSC work already reduced this problem for live automation, but the broader architecture would still benefit from:
|
||||
|
||||
@@ -365,7 +365,7 @@ Status:
|
||||
|
||||
- Design deliverable: complete.
|
||||
- Compatibility seams in code: partially complete and expanding.
|
||||
- Target boundary extraction: not complete; remaining work is tracked by later phases, especially the event model, `RuntimeHost` split, render ownership, and persistence work.
|
||||
- Target boundary extraction: not complete across the whole app; remaining work is tracked by later phases, especially the event model, render ownership, and persistence work.
|
||||
|
||||
Target split:
|
||||
|
||||
@@ -453,9 +453,9 @@ Suggested outcome:
|
||||
|
||||
- the app stops relying on “shared object plus mutex plus polling” as the default coordination pattern
|
||||
|
||||
### Phase 3. Split `RuntimeHost` into persistent state, render snapshot state, and service-facing coordination
|
||||
### Phase 3. Finish live-state and service-facing coordination
|
||||
|
||||
After the event model exists, break apart `RuntimeHost`.
|
||||
After the event model exists, finish separating live committed state and service-facing coordination from the runtime facades.
|
||||
|
||||
Recommended split:
|
||||
|
||||
@@ -628,7 +628,7 @@ If this is approached as a serious architecture program rather than opportunisti
|
||||
|
||||
1. Define subsystem boundaries and target architecture.
|
||||
2. Introduce the internal event model.
|
||||
3. Split `RuntimeHost`.
|
||||
3. Finish runtime live-state/service coordination.
|
||||
4. Make the render thread the sole GL owner.
|
||||
5. Formalize live state layering and composition.
|
||||
6. Move persistence to a background snapshot writer.
|
||||
@@ -647,7 +647,7 @@ This order tries to avoid doing foundational work twice.
|
||||
|
||||
## Short Version
|
||||
|
||||
The app is in a much better place than it was before the OSC timing work, but the main remaining architectural risk is still shared ownership. Too many responsibilities converge on `RuntimeHost` and the shared GL path. The most sensible path forward is:
|
||||
The app is in a much better place than it was before the OSC timing work, but the main remaining architectural risk is still shared ownership around the shared GL path. The most sensible path forward is:
|
||||
|
||||
1. define boundaries
|
||||
2. establish an event model
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Phase 1 Design: Subsystem Boundaries and Target Architecture
|
||||
|
||||
This document expands Phase 1 of [ARCHITECTURE_RESILIENCE_REVIEW.md](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/docs/ARCHITECTURE_RESILIENCE_REVIEW.md) into a concrete target design. Its purpose is to define the long-term subsystem split before later phases introduce a full event model, split `RuntimeHost`, and move rendering onto a sole-owner render thread.
|
||||
This document expands Phase 1 of [ARCHITECTURE_RESILIENCE_REVIEW.md](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/docs/ARCHITECTURE_RESILIENCE_REVIEW.md) into a concrete target design. Its purpose is to define the long-term subsystem split before later phases introduce a full event model and move rendering onto a sole-owner render thread.
|
||||
|
||||
The main goal of Phase 1 is not to immediately rewrite the app. It is to establish clear ownership boundaries so later refactors all move toward the same architecture instead of solving local problems in conflicting ways.
|
||||
|
||||
@@ -9,15 +9,15 @@ The main goal of Phase 1 is not to immediately rewrite the app. It is to establi
|
||||
Phase 1 has two different meanings in this repo, and they should not be collapsed:
|
||||
|
||||
- Phase 1 design package: complete.
|
||||
- Phase 1 target extraction in code: in progress.
|
||||
- Phase 1 runtime target extraction in code: largely complete.
|
||||
|
||||
The completed design package includes the agreed subsystem names, responsibilities, dependency rules, state categories, and current-to-target migration map. The codebase also has concrete compatibility seams for those subsystems. That is different from saying every target boundary is fully extracted: several subsystems still delegate through compatibility helpers, and later roadmap phases are still responsible for the event model, remaining `RuntimeHost` split, sole-owner render thread, explicit live-state layering, background persistence, backend state machine, and fuller telemetry.
|
||||
The completed design package includes the agreed subsystem names, responsibilities, dependency rules, state categories, and current-to-target migration map. The runtime code now has concrete subsystem folders and collaborators for those boundaries. That is different from saying every target boundary is fully extracted across the whole app: later roadmap phases are still responsible for the event model, sole-owner render thread, explicit live-state layering, background persistence, backend state machine, and fuller telemetry.
|
||||
|
||||
## Why Phase 1 Exists
|
||||
|
||||
Today the app works, but too many responsibilities still converge in a few places:
|
||||
At the start of this phase the app worked, but too many responsibilities converged in a few places:
|
||||
|
||||
- `RuntimeHost` owns persistence, live layer state, shader package access, status reporting, and mutation entrypoints.
|
||||
- `RuntimeHost` owned persistence, live layer state, shader package access, status reporting, and mutation entrypoints.
|
||||
- `OpenGLComposite` coordinates runtime setup, render state retrieval, shader rebuild handling, transient OSC overlay behavior, and video backend integration.
|
||||
- DeckLink callback-driven playout still reaches directly into render-facing work.
|
||||
- Background services rely on polling and shared mutable state more than explicit subsystem contracts.
|
||||
@@ -54,9 +54,9 @@ This phase is the target design and the dependency rules. Later phases perform t
|
||||
|
||||
The following current code paths are the strongest evidence for the split proposed here:
|
||||
|
||||
- `RuntimeHost` is both store and live authority:
|
||||
- [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:15)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:726)
|
||||
- `RuntimeHost` was both store and live authority:
|
||||
- `RuntimeHost.h`
|
||||
- `RuntimeHost.cpp`
|
||||
- `OpenGLComposite` is both app orchestrator and render/runtime coordinator:
|
||||
- [OpenGLComposite.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp:106)
|
||||
- [OpenGLComposite.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp:283)
|
||||
@@ -575,7 +575,7 @@ Core responsibilities:
|
||||
|
||||
This is not a one-to-one rename plan. It is a responsibility migration map.
|
||||
|
||||
### Current `RuntimeHost`
|
||||
### Previous `RuntimeHost`
|
||||
|
||||
Should eventually split across:
|
||||
|
||||
@@ -635,7 +635,7 @@ Likely examples:
|
||||
|
||||
As later phases begin, these rules should be treated as guardrails.
|
||||
|
||||
### 1. No new cross-cutting state should be added to `RuntimeHost`
|
||||
### 1. No new cross-cutting runtime object should be introduced
|
||||
|
||||
If a new feature needs durable state, place it conceptually under `RuntimeStore`.
|
||||
If it needs render-local transient state, place it conceptually under `RenderEngine`.
|
||||
@@ -664,7 +664,7 @@ Phase 1 is a design phase, but it should support incremental migration.
|
||||
Recommended order after this document:
|
||||
|
||||
1. Introduce names and interfaces before moving logic.
|
||||
2. Create compatibility adapters around `RuntimeHost` rather than forcing a flag day.
|
||||
2. Create compatibility adapters around the subsystem facades rather than forcing a flag day.
|
||||
3. Move read-only render snapshot publication out before moving all mutation logic.
|
||||
4. Move service ingress boundaries out before removing the old polling shell.
|
||||
5. Isolate timing/health setters from the core store as early as practical.
|
||||
@@ -679,7 +679,7 @@ Phase 1 can reasonably be considered complete once the project has:
|
||||
- agreed subsystem names and responsibilities
|
||||
- agreed allowed dependency directions
|
||||
- explicit state categories: persisted, committed live, transient overlay, health/timing
|
||||
- a current-to-target responsibility map for `RuntimeHost`, `RuntimeServices`, `OpenGLComposite`, and backend/render bridge code
|
||||
- a current-to-target responsibility map for runtime services, `OpenGLComposite`, and backend/render bridge code
|
||||
- a decision that later phases will build against this target rather than inventing new boundaries ad hoc
|
||||
|
||||
By that definition, the Phase 1 design deliverable is complete. The implementation should still be described as partially extracted until the compatibility backing objects and cross-subsystem shims are removed.
|
||||
|
||||
@@ -19,10 +19,10 @@ Today, those responsibilities are fragmented across `RuntimeHost` status setters
|
||||
The current code already contains meaningful health and timing signals, but they are spread through unrelated ownership domains:
|
||||
|
||||
- `RuntimeHost` stores signal and timing status:
|
||||
- [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:41)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1353)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1415)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1441)
|
||||
- `RuntimeHost.h`
|
||||
- `RuntimeHost.cpp`
|
||||
- `RuntimeHost.cpp`
|
||||
- `RuntimeHost.cpp`
|
||||
- render and bridge code report timing by writing back into `RuntimeHost`:
|
||||
- [OpenGLRenderPipeline.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLRenderPipeline.cpp:50)
|
||||
- [OpenGLVideoIOBridge.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp:49)
|
||||
@@ -386,10 +386,10 @@ These are the clearest existing candidates:
|
||||
|
||||
See:
|
||||
|
||||
- [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:41)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1353)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1415)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1441)
|
||||
- `RuntimeHost.h`
|
||||
- `RuntimeHost.cpp`
|
||||
- `RuntimeHost.cpp`
|
||||
- `RuntimeHost.cpp`
|
||||
|
||||
In the target architecture, this kind of state should no longer sit on the same object that owns persistent layer truth.
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ This document defines the target design for the `RuntimeCoordinator` subsystem i
|
||||
Today the app's mutation path is split across several places:
|
||||
|
||||
- `RuntimeHost` performs validation, mutation, persistence, render-state invalidation, and some status updates:
|
||||
- [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:15)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:891)
|
||||
- `RuntimeHost.h`
|
||||
- `RuntimeHost.cpp`
|
||||
- `OpenGLComposite` currently acts like an orchestration shell and a mutation coordinator at the same time:
|
||||
- [OpenGLCompositeRuntimeControls.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLCompositeRuntimeControls.cpp:1)
|
||||
- `RuntimeServices` still owns some deferred control flow around OSC commit and polling:
|
||||
@@ -67,7 +67,7 @@ This is the main policy surface that is currently spread between `RuntimeHost` m
|
||||
- `ApplyOscTargetByControlKey(...)`
|
||||
- `ResetLayerParameters(...)`
|
||||
|
||||
See [RuntimeHost.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.h:15).
|
||||
See `RuntimeHost.h`.
|
||||
|
||||
### 3. State classification
|
||||
|
||||
@@ -375,7 +375,7 @@ That "call host, then decide reload/broadcast policy" logic is a direct candidat
|
||||
- persistence writes
|
||||
- render-state dirty marking
|
||||
|
||||
Examples in [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:891):
|
||||
Examples in `RuntimeHost.cpp`:
|
||||
|
||||
- `AddLayer(...)`
|
||||
- `SetLayerShader(...)`
|
||||
|
||||
@@ -16,11 +16,11 @@ It exists to solve three current problems:
|
||||
|
||||
Today the closest current behavior lives in:
|
||||
|
||||
- [RuntimeHost::GetLayerRenderStates(...)](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1535)
|
||||
- [RuntimeHost::TryGetLayerRenderStates(...)](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1543)
|
||||
- [RuntimeHost::TryRefreshCachedLayerStates(...)](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1554)
|
||||
- [RuntimeHost::RefreshDynamicRenderStateFields(...)](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1582)
|
||||
- [RuntimeHost::BuildLayerRenderStatesLocked(...)](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1598)
|
||||
- `RuntimeHost::GetLayerRenderStates(...)`
|
||||
- `RuntimeHost::TryGetLayerRenderStates(...)`
|
||||
- `RuntimeHost::TryRefreshCachedLayerStates(...)`
|
||||
- `RuntimeHost::RefreshDynamicRenderStateFields(...)`
|
||||
- `RuntimeHost::BuildLayerRenderStatesLocked(...)`
|
||||
- the render-side cache usage in [OpenGLComposite.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp:589)
|
||||
|
||||
`RuntimeSnapshotProvider` should absorb that responsibility, but in a cleaner and more publish-oriented way.
|
||||
|
||||
@@ -301,22 +301,22 @@ Today, `RuntimeHost` contains most of the responsibilities that should move into
|
||||
Key current code paths:
|
||||
|
||||
- config load:
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1651)
|
||||
- `RuntimeHost.cpp`
|
||||
- persistent state load:
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1748)
|
||||
- `RuntimeHost.cpp`
|
||||
- persistent state save:
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1842)
|
||||
- `RuntimeHost.cpp`
|
||||
- preset save/load:
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1286)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1304)
|
||||
- `RuntimeHost.cpp`
|
||||
- `RuntimeHost.cpp`
|
||||
- state serialization helpers:
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:2061)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:2172)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:2268)
|
||||
- `RuntimeHost.cpp`
|
||||
- `RuntimeHost.cpp`
|
||||
- `RuntimeHost.cpp`
|
||||
- path and file helpers:
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:1988)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:2002)
|
||||
- [RuntimeHost.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/legacy/RuntimeHost.cpp:2034)
|
||||
- `RuntimeHost.cpp`
|
||||
- `RuntimeHost.cpp`
|
||||
- `RuntimeHost.cpp`
|
||||
|
||||
Durable-state mutation entrypoints that currently live on `RuntimeHost` but conceptually split between coordinator and store:
|
||||
|
||||
|
||||
@@ -570,7 +570,7 @@ The most important migration is:
|
||||
|
||||
- remove render work from `PlayoutFrameCompleted()`
|
||||
|
||||
### Current `RuntimeHost` Status Updates
|
||||
### Previous Runtime Status Updates
|
||||
|
||||
Frame pacing and signal status setters currently called from the bridge should ultimately be routed through:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user