554 lines
21 KiB
C++
554 lines
21 KiB
C++
#include "RuntimeStore.h"
|
|
|
|
namespace
|
|
{
|
|
std::string TrimCopy(const std::string& text)
|
|
{
|
|
std::size_t start = 0;
|
|
while (start < text.size() && std::isspace(static_cast<unsigned char>(text[start])))
|
|
++start;
|
|
|
|
std::size_t end = text.size();
|
|
while (end > start && std::isspace(static_cast<unsigned char>(text[end - 1])))
|
|
--end;
|
|
|
|
return text.substr(start, end - start);
|
|
}
|
|
}
|
|
|
|
RuntimeStore::RuntimeStore(RuntimeHost& runtimeHost) :
|
|
mRuntimeHost(runtimeHost)
|
|
{
|
|
}
|
|
|
|
bool RuntimeStore::InitializeStore(std::string& error)
|
|
{
|
|
try
|
|
{
|
|
std::lock_guard<std::mutex> lock(mRuntimeHost.mMutex);
|
|
|
|
if (!mRuntimeHost.ResolvePaths(error))
|
|
return false;
|
|
if (!mRuntimeHost.LoadConfig(error))
|
|
return false;
|
|
mRuntimeHost.mShaderRoot = mRuntimeHost.mRepoRoot / mRuntimeHost.mConfig.shaderLibrary;
|
|
if (!mRuntimeHost.LoadPersistentState(error))
|
|
return false;
|
|
if (!mRuntimeHost.ScanShaderPackages(error))
|
|
return false;
|
|
mRuntimeHost.NormalizePersistentLayerIdsLocked();
|
|
|
|
for (RuntimeHost::LayerPersistentState& layer : mRuntimeHost.mPersistentState.layers)
|
|
{
|
|
auto shaderIt = mRuntimeHost.mPackagesById.find(layer.shaderId);
|
|
if (shaderIt != mRuntimeHost.mPackagesById.end())
|
|
mRuntimeHost.EnsureLayerDefaultsLocked(layer, shaderIt->second);
|
|
}
|
|
|
|
if (mRuntimeHost.mPersistentState.layers.empty() && !mRuntimeHost.mPackageOrder.empty())
|
|
{
|
|
RuntimeHost::LayerPersistentState layer;
|
|
layer.id = mRuntimeHost.GenerateLayerId();
|
|
layer.shaderId = mRuntimeHost.mPackageOrder.front();
|
|
layer.bypass = false;
|
|
mRuntimeHost.EnsureLayerDefaultsLocked(layer, mRuntimeHost.mPackagesById[layer.shaderId]);
|
|
mRuntimeHost.mPersistentState.layers.push_back(layer);
|
|
}
|
|
|
|
mRuntimeHost.mServerPort = mRuntimeHost.mConfig.serverPort;
|
|
mRuntimeHost.mAutoReloadEnabled = mRuntimeHost.mConfig.autoReload;
|
|
mRuntimeHost.mReloadRequested = true;
|
|
mRuntimeHost.mCompileMessage = "Waiting for shader compile.";
|
|
return true;
|
|
}
|
|
catch (const std::exception& exception)
|
|
{
|
|
error = std::string("RuntimeStore::InitializeStore exception: ") + exception.what();
|
|
return false;
|
|
}
|
|
catch (...)
|
|
{
|
|
error = "RuntimeStore::InitializeStore threw a non-standard exception.";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string RuntimeStore::BuildPersistentStateJson() const
|
|
{
|
|
return SerializeJson(BuildRuntimeStateValue(), true);
|
|
}
|
|
|
|
bool RuntimeStore::CreateStoredLayer(const std::string& shaderId, std::string& error)
|
|
{
|
|
std::lock_guard<std::mutex> 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)
|
|
{
|
|
std::lock_guard<std::mutex> 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)
|
|
{
|
|
std::lock_guard<std::mutex> 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<std::ptrdiff_t>(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)
|
|
{
|
|
std::lock_guard<std::mutex> 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::size_t>(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<std::ptrdiff_t>(sourceIndex));
|
|
mRuntimeHost.mPersistentState.layers.insert(mRuntimeHost.mPersistentState.layers.begin() + static_cast<std::ptrdiff_t>(targetIndex), movedLayer);
|
|
mRuntimeHost.mReloadRequested = true;
|
|
mRuntimeHost.MarkRenderStateDirtyLocked();
|
|
return mRuntimeHost.SavePersistentState(error);
|
|
}
|
|
|
|
bool RuntimeStore::SetStoredLayerBypassState(const std::string& layerId, bool bypassed, std::string& error)
|
|
{
|
|
std::lock_guard<std::mutex> 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)
|
|
{
|
|
std::lock_guard<std::mutex> 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)
|
|
{
|
|
std::lock_guard<std::mutex> 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::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;
|
|
|
|
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)
|
|
{
|
|
return mRuntimeHost.UpdateLayerParameterByControlKey(layerKey, parameterKey, newValue, 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);
|
|
}
|
|
|
|
bool RuntimeStore::ResetStoredLayerParameterValues(const std::string& layerId, std::string& error)
|
|
{
|
|
std::lock_guard<std::mutex> 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
|
|
{
|
|
std::lock_guard<std::mutex> 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)
|
|
{
|
|
std::lock_guard<std::mutex> 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<RuntimeHost::LayerPersistentState> 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
|
|
{
|
|
return mRuntimeHost.GetRepoRoot();
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeStore::GetRuntimeUiRoot() const
|
|
{
|
|
return mRuntimeHost.GetUiRoot();
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeStore::GetRuntimeDocsRoot() const
|
|
{
|
|
return mRuntimeHost.GetDocsRoot();
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeStore::GetRuntimeDataRoot() const
|
|
{
|
|
return mRuntimeHost.GetRuntimeRoot();
|
|
}
|
|
|
|
unsigned short RuntimeStore::GetConfiguredControlServerPort() const
|
|
{
|
|
return mRuntimeHost.GetServerPort();
|
|
}
|
|
|
|
unsigned short RuntimeStore::GetConfiguredOscPort() const
|
|
{
|
|
return mRuntimeHost.GetOscPort();
|
|
}
|
|
|
|
const std::string& RuntimeStore::GetConfiguredOscBindAddress() const
|
|
{
|
|
return mRuntimeHost.GetOscBindAddress();
|
|
}
|
|
|
|
double RuntimeStore::GetConfiguredOscSmoothing() const
|
|
{
|
|
return mRuntimeHost.GetOscSmoothing();
|
|
}
|
|
|
|
unsigned RuntimeStore::GetConfiguredMaxTemporalHistoryFrames() const
|
|
{
|
|
return mRuntimeHost.GetMaxTemporalHistoryFrames();
|
|
}
|
|
|
|
unsigned RuntimeStore::GetConfiguredPreviewFps() const
|
|
{
|
|
return mRuntimeHost.GetPreviewFps();
|
|
}
|
|
|
|
bool RuntimeStore::IsExternalKeyingConfigured() const
|
|
{
|
|
return mRuntimeHost.ExternalKeyingEnabled();
|
|
}
|
|
|
|
const std::string& RuntimeStore::GetConfiguredInputVideoFormat() const
|
|
{
|
|
return mRuntimeHost.GetInputVideoFormat();
|
|
}
|
|
|
|
const std::string& RuntimeStore::GetConfiguredInputFrameRate() const
|
|
{
|
|
return mRuntimeHost.GetInputFrameRate();
|
|
}
|
|
|
|
const std::string& RuntimeStore::GetConfiguredOutputVideoFormat() const
|
|
{
|
|
return mRuntimeHost.GetOutputVideoFormat();
|
|
}
|
|
|
|
const std::string& RuntimeStore::GetConfiguredOutputFrameRate() const
|
|
{
|
|
return mRuntimeHost.GetOutputFrameRate();
|
|
}
|
|
|
|
void RuntimeStore::SetCompileStatus(bool succeeded, const std::string& message)
|
|
{
|
|
mRuntimeHost.SetCompileStatus(succeeded, message);
|
|
}
|
|
|
|
void RuntimeStore::ClearReloadRequest()
|
|
{
|
|
mRuntimeHost.ClearReloadRequest();
|
|
}
|
|
|
|
JsonValue RuntimeStore::BuildRuntimeStateValue() const
|
|
{
|
|
const HealthTelemetry::Snapshot telemetrySnapshot = mRuntimeHost.mHealthTelemetry.GetSnapshot();
|
|
std::lock_guard<std::mutex> lock(mRuntimeHost.mMutex);
|
|
|
|
JsonValue root = JsonValue::MakeObject();
|
|
|
|
JsonValue app = JsonValue::MakeObject();
|
|
app.set("serverPort", JsonValue(static_cast<double>(mRuntimeHost.mServerPort)));
|
|
app.set("oscPort", JsonValue(static_cast<double>(mRuntimeHost.mConfig.oscPort)));
|
|
app.set("oscBindAddress", JsonValue(mRuntimeHost.mConfig.oscBindAddress));
|
|
app.set("oscSmoothing", JsonValue(mRuntimeHost.mConfig.oscSmoothing));
|
|
app.set("autoReload", JsonValue(mRuntimeHost.mAutoReloadEnabled));
|
|
app.set("maxTemporalHistoryFrames", JsonValue(static_cast<double>(mRuntimeHost.mConfig.maxTemporalHistoryFrames)));
|
|
app.set("previewFps", JsonValue(static_cast<double>(mRuntimeHost.mConfig.previewFps)));
|
|
app.set("enableExternalKeying", JsonValue(mRuntimeHost.mConfig.enableExternalKeying));
|
|
app.set("inputVideoFormat", JsonValue(mRuntimeHost.mConfig.inputVideoFormat));
|
|
app.set("inputFrameRate", JsonValue(mRuntimeHost.mConfig.inputFrameRate));
|
|
app.set("outputVideoFormat", JsonValue(mRuntimeHost.mConfig.outputVideoFormat));
|
|
app.set("outputFrameRate", JsonValue(mRuntimeHost.mConfig.outputFrameRate));
|
|
root.set("app", app);
|
|
|
|
JsonValue runtime = JsonValue::MakeObject();
|
|
runtime.set("layerCount", JsonValue(static_cast<double>(mRuntimeHost.mPersistentState.layers.size())));
|
|
runtime.set("compileSucceeded", JsonValue(mRuntimeHost.mCompileSucceeded));
|
|
runtime.set("compileMessage", JsonValue(mRuntimeHost.mCompileMessage));
|
|
root.set("runtime", runtime);
|
|
|
|
JsonValue video = JsonValue::MakeObject();
|
|
video.set("hasSignal", JsonValue(telemetrySnapshot.signal.hasSignal));
|
|
video.set("width", JsonValue(static_cast<double>(telemetrySnapshot.signal.width)));
|
|
video.set("height", JsonValue(static_cast<double>(telemetrySnapshot.signal.height)));
|
|
video.set("modeName", JsonValue(telemetrySnapshot.signal.modeName));
|
|
root.set("video", video);
|
|
|
|
JsonValue deckLink = JsonValue::MakeObject();
|
|
deckLink.set("modelName", JsonValue(telemetrySnapshot.videoIO.modelName));
|
|
deckLink.set("supportsInternalKeying", JsonValue(telemetrySnapshot.videoIO.supportsInternalKeying));
|
|
deckLink.set("supportsExternalKeying", JsonValue(telemetrySnapshot.videoIO.supportsExternalKeying));
|
|
deckLink.set("keyerInterfaceAvailable", JsonValue(telemetrySnapshot.videoIO.keyerInterfaceAvailable));
|
|
deckLink.set("externalKeyingRequested", JsonValue(telemetrySnapshot.videoIO.externalKeyingRequested));
|
|
deckLink.set("externalKeyingActive", JsonValue(telemetrySnapshot.videoIO.externalKeyingActive));
|
|
deckLink.set("statusMessage", JsonValue(telemetrySnapshot.videoIO.statusMessage));
|
|
root.set("decklink", deckLink);
|
|
|
|
JsonValue videoIO = JsonValue::MakeObject();
|
|
videoIO.set("backend", JsonValue(telemetrySnapshot.videoIO.backendName));
|
|
videoIO.set("modelName", JsonValue(telemetrySnapshot.videoIO.modelName));
|
|
videoIO.set("supportsInternalKeying", JsonValue(telemetrySnapshot.videoIO.supportsInternalKeying));
|
|
videoIO.set("supportsExternalKeying", JsonValue(telemetrySnapshot.videoIO.supportsExternalKeying));
|
|
videoIO.set("keyerInterfaceAvailable", JsonValue(telemetrySnapshot.videoIO.keyerInterfaceAvailable));
|
|
videoIO.set("externalKeyingRequested", JsonValue(telemetrySnapshot.videoIO.externalKeyingRequested));
|
|
videoIO.set("externalKeyingActive", JsonValue(telemetrySnapshot.videoIO.externalKeyingActive));
|
|
videoIO.set("statusMessage", JsonValue(telemetrySnapshot.videoIO.statusMessage));
|
|
root.set("videoIO", videoIO);
|
|
|
|
JsonValue performance = JsonValue::MakeObject();
|
|
performance.set("frameBudgetMs", JsonValue(telemetrySnapshot.performance.frameBudgetMilliseconds));
|
|
performance.set("renderMs", JsonValue(telemetrySnapshot.performance.renderMilliseconds));
|
|
performance.set("smoothedRenderMs", JsonValue(telemetrySnapshot.performance.smoothedRenderMilliseconds));
|
|
performance.set("budgetUsedPercent", JsonValue(
|
|
telemetrySnapshot.performance.frameBudgetMilliseconds > 0.0
|
|
? (telemetrySnapshot.performance.smoothedRenderMilliseconds / telemetrySnapshot.performance.frameBudgetMilliseconds) * 100.0
|
|
: 0.0));
|
|
performance.set("completionIntervalMs", JsonValue(telemetrySnapshot.performance.completionIntervalMilliseconds));
|
|
performance.set("smoothedCompletionIntervalMs", JsonValue(telemetrySnapshot.performance.smoothedCompletionIntervalMilliseconds));
|
|
performance.set("maxCompletionIntervalMs", JsonValue(telemetrySnapshot.performance.maxCompletionIntervalMilliseconds));
|
|
performance.set("lateFrameCount", JsonValue(static_cast<double>(telemetrySnapshot.performance.lateFrameCount)));
|
|
performance.set("droppedFrameCount", JsonValue(static_cast<double>(telemetrySnapshot.performance.droppedFrameCount)));
|
|
performance.set("flushedFrameCount", JsonValue(static_cast<double>(telemetrySnapshot.performance.flushedFrameCount)));
|
|
root.set("performance", performance);
|
|
|
|
JsonValue shaderLibrary = JsonValue::MakeArray();
|
|
for (const ShaderPackageStatus& status : mRuntimeHost.mPackageStatuses)
|
|
{
|
|
JsonValue shader = JsonValue::MakeObject();
|
|
shader.set("id", JsonValue(status.id));
|
|
shader.set("name", JsonValue(status.displayName));
|
|
shader.set("description", JsonValue(status.description));
|
|
shader.set("category", JsonValue(status.category));
|
|
shader.set("available", JsonValue(status.available));
|
|
if (!status.available)
|
|
shader.set("error", JsonValue(status.error));
|
|
|
|
auto shaderIt = mRuntimeHost.mPackagesById.find(status.id);
|
|
if (status.available && shaderIt != mRuntimeHost.mPackagesById.end() && shaderIt->second.temporal.enabled)
|
|
{
|
|
JsonValue temporal = JsonValue::MakeObject();
|
|
temporal.set("enabled", JsonValue(true));
|
|
temporal.set("historySource", JsonValue(mRuntimeHost.TemporalHistorySourceToString(shaderIt->second.temporal.historySource)));
|
|
temporal.set("requestedHistoryLength", JsonValue(static_cast<double>(shaderIt->second.temporal.requestedHistoryLength)));
|
|
temporal.set("effectiveHistoryLength", JsonValue(static_cast<double>(shaderIt->second.temporal.effectiveHistoryLength)));
|
|
shader.set("temporal", temporal);
|
|
}
|
|
if (status.available && shaderIt != mRuntimeHost.mPackagesById.end() && shaderIt->second.feedback.enabled)
|
|
{
|
|
JsonValue feedback = JsonValue::MakeObject();
|
|
feedback.set("enabled", JsonValue(true));
|
|
feedback.set("writePass", JsonValue(shaderIt->second.feedback.writePassId));
|
|
shader.set("feedback", feedback);
|
|
}
|
|
shaderLibrary.pushBack(shader);
|
|
}
|
|
root.set("shaders", shaderLibrary);
|
|
|
|
JsonValue stackPresets = JsonValue::MakeArray();
|
|
for (const std::string& presetName : mRuntimeHost.GetStackPresetNamesLocked())
|
|
stackPresets.pushBack(JsonValue(presetName));
|
|
root.set("stackPresets", stackPresets);
|
|
|
|
root.set("layers", SerializeLayerStack());
|
|
return root;
|
|
}
|
|
|
|
JsonValue RuntimeStore::SerializeLayerStack() const
|
|
{
|
|
return mRuntimeHost.SerializeLayerStackLocked();
|
|
}
|