Runtime snapshot provider changes
This commit is contained in:
@@ -1,47 +1,3 @@
|
|||||||
/* -LICENSE-START-
|
|
||||||
** Copyright (c) 2012 Blackmagic Design
|
|
||||||
**
|
|
||||||
** Permission is hereby granted, free of charge, to any person or organization
|
|
||||||
** obtaining a copy of the software and accompanying documentation (the
|
|
||||||
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
|
||||||
** and transmit the Software, and to prepare derivative works of the Software,
|
|
||||||
** and to permit third-parties to whom the Software is furnished to do so, in
|
|
||||||
** accordance with:
|
|
||||||
**
|
|
||||||
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
|
||||||
** Agreement for the Software Development Kit ("EULA") available at
|
|
||||||
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
|
||||||
**
|
|
||||||
** (2) if the Software is obtained from any third party, such licensing terms
|
|
||||||
** as notified by that third party,
|
|
||||||
**
|
|
||||||
** and all subject to the following:
|
|
||||||
**
|
|
||||||
** (3) the copyright notices in the Software and this entire statement,
|
|
||||||
** including the above license grant, this restriction and the following
|
|
||||||
** disclaimer, must be included in all copies of the Software, in whole or in
|
|
||||||
** part, and all derivative works of the Software, unless such copies or
|
|
||||||
** derivative works are solely in the form of machine-executable object code
|
|
||||||
** generated by a source language processor.
|
|
||||||
**
|
|
||||||
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
||||||
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
||||||
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
||||||
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
** DEALINGS IN THE SOFTWARE.
|
|
||||||
**
|
|
||||||
** A copy of the Software is available free of charge at
|
|
||||||
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
|
||||||
**
|
|
||||||
** -LICENSE-END-
|
|
||||||
*/
|
|
||||||
//
|
|
||||||
// LoopThroughWithOpenGLCompositing.cpp
|
|
||||||
// LoopThroughWithOpenGLCompositing
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "OpenGLComposite.h"
|
#include "OpenGLComposite.h"
|
||||||
|
|||||||
@@ -1,47 +1,3 @@
|
|||||||
/* -LICENSE-START-
|
|
||||||
** Copyright (c) 2012 Blackmagic Design
|
|
||||||
**
|
|
||||||
** Permission is hereby granted, free of charge, to any person or organization
|
|
||||||
** obtaining a copy of the software and accompanying documentation (the
|
|
||||||
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
|
||||||
** and transmit the Software, and to prepare derivative works of the Software,
|
|
||||||
** and to permit third-parties to whom the Software is furnished to do so, in
|
|
||||||
** accordance with:
|
|
||||||
**
|
|
||||||
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
|
||||||
** Agreement for the Software Development Kit ("EULA") available at
|
|
||||||
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
|
||||||
**
|
|
||||||
** (2) if the Software is obtained from any third party, such licensing terms
|
|
||||||
** as notified by that third party,
|
|
||||||
**
|
|
||||||
** and all subject to the following:
|
|
||||||
**
|
|
||||||
** (3) the copyright notices in the Software and this entire statement,
|
|
||||||
** including the above license grant, this restriction and the following
|
|
||||||
** disclaimer, must be included in all copies of the Software, in whole or in
|
|
||||||
** part, and all derivative works of the Software, unless such copies or
|
|
||||||
** derivative works are solely in the form of machine-executable object code
|
|
||||||
** generated by a source language processor.
|
|
||||||
**
|
|
||||||
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
||||||
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
||||||
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
||||||
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
** DEALINGS IN THE SOFTWARE.
|
|
||||||
**
|
|
||||||
** A copy of the Software is available free of charge at
|
|
||||||
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
|
||||||
**
|
|
||||||
** -LICENSE-END-
|
|
||||||
*/
|
|
||||||
//
|
|
||||||
// LoopThroughWithOpenGLCompositing.h
|
|
||||||
// LoopThroughWithOpenGLCompositing
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
|||||||
@@ -712,58 +712,6 @@ RuntimeHost::RuntimeHost()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RuntimeHost::Initialize(std::string& error)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
|
||||||
|
|
||||||
if (!ResolvePaths(error))
|
|
||||||
return false;
|
|
||||||
if (!LoadConfig(error))
|
|
||||||
return false;
|
|
||||||
mShaderRoot = mRepoRoot / mConfig.shaderLibrary;
|
|
||||||
if (!LoadPersistentState(error))
|
|
||||||
return false;
|
|
||||||
if (!ScanShaderPackages(error))
|
|
||||||
return false;
|
|
||||||
NormalizePersistentLayerIdsLocked();
|
|
||||||
|
|
||||||
for (LayerPersistentState& layer : mPersistentState.layers)
|
|
||||||
{
|
|
||||||
auto shaderIt = mPackagesById.find(layer.shaderId);
|
|
||||||
if (shaderIt != mPackagesById.end())
|
|
||||||
EnsureLayerDefaultsLocked(layer, shaderIt->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mPersistentState.layers.empty() && !mPackageOrder.empty())
|
|
||||||
{
|
|
||||||
LayerPersistentState layer;
|
|
||||||
layer.id = GenerateLayerId();
|
|
||||||
layer.shaderId = mPackageOrder.front();
|
|
||||||
layer.bypass = false;
|
|
||||||
EnsureLayerDefaultsLocked(layer, mPackagesById[layer.shaderId]);
|
|
||||||
mPersistentState.layers.push_back(layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
mServerPort = mConfig.serverPort;
|
|
||||||
mAutoReloadEnabled = mConfig.autoReload;
|
|
||||||
mReloadRequested = true;
|
|
||||||
mCompileMessage = "Waiting for shader compile.";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (const std::exception& exception)
|
|
||||||
{
|
|
||||||
error = std::string("RuntimeHost::Initialize exception: ") + exception.what();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
error = "RuntimeHost::Initialize threw a non-standard exception.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RuntimeHost::PollFileChanges(bool& registryChanged, bool& reloadRequested, std::string& error)
|
bool RuntimeHost::PollFileChanges(bool& registryChanged, bool& reloadRequested, std::string& error)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -1491,118 +1439,6 @@ bool RuntimeHost::BuildLayerPassFragmentShaderSources(const std::string& layerId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<RuntimeRenderState> RuntimeHost::GetLayerRenderStates(unsigned outputWidth, unsigned outputHeight) const
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
|
||||||
std::vector<RuntimeRenderState> states;
|
|
||||||
BuildLayerRenderStatesLocked(outputWidth, outputHeight, states);
|
|
||||||
return states;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RuntimeHost::TryGetLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
|
|
||||||
if (!lock.owns_lock())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
states.clear();
|
|
||||||
BuildLayerRenderStatesLocked(outputWidth, outputHeight, states);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RuntimeHost::TryRefreshCachedLayerStates(std::vector<RuntimeRenderState>& states) const
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
|
|
||||||
if (!lock.owns_lock())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (RuntimeRenderState& state : states)
|
|
||||||
{
|
|
||||||
const auto layerIt = std::find_if(mPersistentState.layers.begin(), mPersistentState.layers.end(),
|
|
||||||
[&state](const LayerPersistentState& layer) { return layer.id == state.layerId; });
|
|
||||||
if (layerIt == mPersistentState.layers.end())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
state.bypass = layerIt->bypass ? 1.0 : 0.0;
|
|
||||||
state.parameterValues.clear();
|
|
||||||
for (const ShaderParameterDefinition& definition : state.parameterDefinitions)
|
|
||||||
{
|
|
||||||
ShaderParameterValue value = DefaultValueForDefinition(definition);
|
|
||||||
auto valueIt = layerIt->parameterValues.find(definition.id);
|
|
||||||
if (valueIt != layerIt->parameterValues.end())
|
|
||||||
value = valueIt->second;
|
|
||||||
state.parameterValues[definition.id] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RuntimeHost::RefreshDynamicRenderStateFields(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() - mStartTime).count();
|
|
||||||
const double frameCount = static_cast<double>(mFrameCounter.load(std::memory_order_relaxed));
|
|
||||||
|
|
||||||
for (RuntimeRenderState& state : states)
|
|
||||||
{
|
|
||||||
state.timeSeconds = timeSeconds;
|
|
||||||
state.utcTimeSeconds = clock.utcTimeSeconds;
|
|
||||||
state.utcOffsetSeconds = clock.utcOffsetSeconds;
|
|
||||||
state.startupRandom = mStartupRandom;
|
|
||||||
state.frameCount = frameCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
|
||||||
{
|
|
||||||
const HealthTelemetry::SignalStatusSnapshot signalStatus = mHealthTelemetry.GetSignalStatusSnapshot();
|
|
||||||
|
|
||||||
for (const LayerPersistentState& layer : mPersistentState.layers)
|
|
||||||
{
|
|
||||||
auto shaderIt = mPackagesById.find(layer.shaderId);
|
|
||||||
if (shaderIt == mPackagesById.end())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
RuntimeRenderState state;
|
|
||||||
state.layerId = layer.id;
|
|
||||||
state.shaderId = layer.shaderId;
|
|
||||||
state.shaderName = shaderIt->second.displayName;
|
|
||||||
state.mixAmount = 1.0;
|
|
||||||
state.bypass = layer.bypass ? 1.0 : 0.0;
|
|
||||||
state.inputWidth = signalStatus.width;
|
|
||||||
state.inputHeight = signalStatus.height;
|
|
||||||
state.outputWidth = outputWidth;
|
|
||||||
state.outputHeight = outputHeight;
|
|
||||||
state.parameterDefinitions = shaderIt->second.parameters;
|
|
||||||
state.textureAssets = shaderIt->second.textureAssets;
|
|
||||||
state.fontAssets = shaderIt->second.fontAssets;
|
|
||||||
state.isTemporal = shaderIt->second.temporal.enabled;
|
|
||||||
state.temporalHistorySource = shaderIt->second.temporal.historySource;
|
|
||||||
state.requestedTemporalHistoryLength = shaderIt->second.temporal.requestedHistoryLength;
|
|
||||||
state.effectiveTemporalHistoryLength = shaderIt->second.temporal.effectiveHistoryLength;
|
|
||||||
state.feedback = shaderIt->second.feedback;
|
|
||||||
|
|
||||||
for (const ShaderParameterDefinition& definition : shaderIt->second.parameters)
|
|
||||||
{
|
|
||||||
ShaderParameterValue value = DefaultValueForDefinition(definition);
|
|
||||||
auto valueIt = layer.parameterValues.find(definition.id);
|
|
||||||
if (valueIt != layer.parameterValues.end())
|
|
||||||
value = valueIt->second;
|
|
||||||
state.parameterValues[definition.id] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
states.push_back(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshDynamicRenderStateFields(states);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string RuntimeHost::BuildStateJson() const
|
|
||||||
{
|
|
||||||
return SerializeJson(BuildStateValue(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RuntimeHost::SetServerPort(unsigned short port)
|
void RuntimeHost::SetServerPort(unsigned short port)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
@@ -2019,121 +1855,6 @@ bool RuntimeHost::ResolvePaths(std::string& error)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonValue RuntimeHost::BuildStateValue() const
|
|
||||||
{
|
|
||||||
const HealthTelemetry::Snapshot telemetrySnapshot = mHealthTelemetry.GetSnapshot();
|
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
|
||||||
|
|
||||||
JsonValue root = JsonValue::MakeObject();
|
|
||||||
|
|
||||||
JsonValue app = JsonValue::MakeObject();
|
|
||||||
app.set("serverPort", JsonValue(static_cast<double>(mServerPort)));
|
|
||||||
app.set("oscPort", JsonValue(static_cast<double>(mConfig.oscPort)));
|
|
||||||
app.set("oscBindAddress", JsonValue(mConfig.oscBindAddress));
|
|
||||||
app.set("oscSmoothing", JsonValue(mConfig.oscSmoothing));
|
|
||||||
app.set("autoReload", JsonValue(mAutoReloadEnabled));
|
|
||||||
app.set("maxTemporalHistoryFrames", JsonValue(static_cast<double>(mConfig.maxTemporalHistoryFrames)));
|
|
||||||
app.set("previewFps", JsonValue(static_cast<double>(mConfig.previewFps)));
|
|
||||||
app.set("enableExternalKeying", JsonValue(mConfig.enableExternalKeying));
|
|
||||||
app.set("inputVideoFormat", JsonValue(mConfig.inputVideoFormat));
|
|
||||||
app.set("inputFrameRate", JsonValue(mConfig.inputFrameRate));
|
|
||||||
app.set("outputVideoFormat", JsonValue(mConfig.outputVideoFormat));
|
|
||||||
app.set("outputFrameRate", JsonValue(mConfig.outputFrameRate));
|
|
||||||
root.set("app", app);
|
|
||||||
|
|
||||||
JsonValue runtime = JsonValue::MakeObject();
|
|
||||||
runtime.set("layerCount", JsonValue(static_cast<double>(mPersistentState.layers.size())));
|
|
||||||
runtime.set("compileSucceeded", JsonValue(mCompileSucceeded));
|
|
||||||
runtime.set("compileMessage", JsonValue(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 : 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 = mPackagesById.find(status.id);
|
|
||||||
if (status.available && shaderIt != mPackagesById.end() && shaderIt->second.temporal.enabled)
|
|
||||||
{
|
|
||||||
JsonValue temporal = JsonValue::MakeObject();
|
|
||||||
temporal.set("enabled", JsonValue(true));
|
|
||||||
temporal.set("historySource", JsonValue(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 != 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 : GetStackPresetNamesLocked())
|
|
||||||
stackPresets.pushBack(JsonValue(presetName));
|
|
||||||
root.set("stackPresets", stackPresets);
|
|
||||||
|
|
||||||
root.set("layers", SerializeLayerStackLocked());
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonValue RuntimeHost::SerializeLayerStackLocked() const
|
JsonValue RuntimeHost::SerializeLayerStackLocked() const
|
||||||
{
|
{
|
||||||
JsonValue layers = JsonValue::MakeArray();
|
JsonValue layers = JsonValue::MakeArray();
|
||||||
|
|||||||
@@ -13,13 +13,14 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class RuntimeStore;
|
||||||
|
class RuntimeSnapshotProvider;
|
||||||
|
|
||||||
class RuntimeHost
|
class RuntimeHost
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RuntimeHost();
|
RuntimeHost();
|
||||||
|
|
||||||
bool Initialize(std::string& error);
|
|
||||||
|
|
||||||
bool PollFileChanges(bool& registryChanged, bool& reloadRequested, std::string& error);
|
bool PollFileChanges(bool& registryChanged, bool& reloadRequested, std::string& error);
|
||||||
bool ManualReloadRequested();
|
bool ManualReloadRequested();
|
||||||
void ClearReloadRequest();
|
void ClearReloadRequest();
|
||||||
@@ -57,11 +58,6 @@ public:
|
|||||||
const HealthTelemetry& GetHealthTelemetry() const { return mHealthTelemetry; }
|
const HealthTelemetry& GetHealthTelemetry() const { return mHealthTelemetry; }
|
||||||
|
|
||||||
bool BuildLayerPassFragmentShaderSources(const std::string& layerId, std::vector<ShaderPassBuildSource>& passSources, std::string& error);
|
bool BuildLayerPassFragmentShaderSources(const std::string& layerId, std::vector<ShaderPassBuildSource>& passSources, std::string& error);
|
||||||
std::vector<RuntimeRenderState> GetLayerRenderStates(unsigned outputWidth, unsigned outputHeight) const;
|
|
||||||
bool TryGetLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const;
|
|
||||||
bool TryRefreshCachedLayerStates(std::vector<RuntimeRenderState>& states) const;
|
|
||||||
void RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState>& states) const;
|
|
||||||
std::string BuildStateJson() const;
|
|
||||||
uint64_t GetRenderStateVersion() const { return mRenderStateVersion.load(std::memory_order_relaxed); }
|
uint64_t GetRenderStateVersion() const { return mRenderStateVersion.load(std::memory_order_relaxed); }
|
||||||
uint64_t GetParameterStateVersion() const { return mParameterStateVersion.load(std::memory_order_relaxed); }
|
uint64_t GetParameterStateVersion() const { return mParameterStateVersion.load(std::memory_order_relaxed); }
|
||||||
|
|
||||||
@@ -124,8 +120,6 @@ private:
|
|||||||
std::string ReadTextFile(const std::filesystem::path& path, std::string& error) const;
|
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;
|
bool WriteTextFile(const std::filesystem::path& path, const std::string& contents, std::string& error) const;
|
||||||
bool ResolvePaths(std::string& error);
|
bool ResolvePaths(std::string& error);
|
||||||
void BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const;
|
|
||||||
JsonValue BuildStateValue() const;
|
|
||||||
JsonValue SerializeLayerStackLocked() const;
|
JsonValue SerializeLayerStackLocked() const;
|
||||||
bool DeserializeLayerStackLocked(const JsonValue& layersValue, std::vector<LayerPersistentState>& layers, std::string& error);
|
bool DeserializeLayerStackLocked(const JsonValue& layersValue, std::vector<LayerPersistentState>& layers, std::string& error);
|
||||||
void NormalizePersistentLayerIdsLocked();
|
void NormalizePersistentLayerIdsLocked();
|
||||||
@@ -140,6 +134,8 @@ private:
|
|||||||
void MarkParameterStateDirtyLocked();
|
void MarkParameterStateDirtyLocked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class RuntimeStore;
|
||||||
|
friend class RuntimeSnapshotProvider;
|
||||||
HealthTelemetry mHealthTelemetry;
|
HealthTelemetry mHealthTelemetry;
|
||||||
mutable std::mutex mMutex;
|
mutable std::mutex mMutex;
|
||||||
AppConfig mConfig;
|
AppConfig mConfig;
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
#include "RuntimeSnapshotProvider.h"
|
#include "RuntimeSnapshotProvider.h"
|
||||||
|
|
||||||
|
#include "RuntimeClock.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <mutex>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
RuntimeSnapshotProvider::RuntimeSnapshotProvider(RuntimeHost& runtimeHost) :
|
RuntimeSnapshotProvider::RuntimeSnapshotProvider(RuntimeHost& runtimeHost) :
|
||||||
@@ -28,7 +32,7 @@ RuntimeSnapshotVersions RuntimeSnapshotProvider::GetVersions() const
|
|||||||
RuntimeRenderFrameContext RuntimeSnapshotProvider::GetFrameContext() const
|
RuntimeRenderFrameContext RuntimeSnapshotProvider::GetFrameContext() const
|
||||||
{
|
{
|
||||||
std::vector<RuntimeRenderState> stateScratch(1);
|
std::vector<RuntimeRenderState> stateScratch(1);
|
||||||
mRuntimeHost.RefreshDynamicRenderStateFields(stateScratch);
|
RefreshDynamicRenderStateFields(stateScratch);
|
||||||
|
|
||||||
RuntimeRenderFrameContext frameContext;
|
RuntimeRenderFrameContext frameContext;
|
||||||
const RuntimeRenderState& state = stateScratch.front();
|
const RuntimeRenderState& state = stateScratch.front();
|
||||||
@@ -59,7 +63,7 @@ RuntimeRenderStateSnapshot RuntimeSnapshotProvider::GetRenderStateSnapshot(unsig
|
|||||||
RuntimeRenderStateSnapshot snapshot;
|
RuntimeRenderStateSnapshot snapshot;
|
||||||
snapshot.outputWidth = outputWidth;
|
snapshot.outputWidth = outputWidth;
|
||||||
snapshot.outputHeight = outputHeight;
|
snapshot.outputHeight = outputHeight;
|
||||||
snapshot.states = mRuntimeHost.GetLayerRenderStates(outputWidth, outputHeight);
|
snapshot.states = GetLayerRenderStates(outputWidth, outputHeight);
|
||||||
|
|
||||||
const RuntimeSnapshotVersions versionsAfter = GetVersions();
|
const RuntimeSnapshotVersions versionsAfter = GetVersions();
|
||||||
if (versionsBefore.renderStateVersion == versionsAfter.renderStateVersion &&
|
if (versionsBefore.renderStateVersion == versionsAfter.renderStateVersion &&
|
||||||
@@ -76,7 +80,7 @@ bool RuntimeSnapshotProvider::TryGetRenderStateSnapshot(unsigned outputWidth, un
|
|||||||
const RuntimeSnapshotVersions versionsBefore = GetVersions();
|
const RuntimeSnapshotVersions versionsBefore = GetVersions();
|
||||||
|
|
||||||
std::vector<RuntimeRenderState> states;
|
std::vector<RuntimeRenderState> states;
|
||||||
if (!mRuntimeHost.TryGetLayerRenderStates(outputWidth, outputHeight, states))
|
if (!TryGetLayerRenderStates(outputWidth, outputHeight, states))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const RuntimeSnapshotVersions versionsAfter = GetVersions();
|
const RuntimeSnapshotVersions versionsAfter = GetVersions();
|
||||||
@@ -96,7 +100,7 @@ bool RuntimeSnapshotProvider::TryGetRenderStateSnapshot(unsigned outputWidth, un
|
|||||||
bool RuntimeSnapshotProvider::TryRefreshSnapshotParameters(RuntimeRenderStateSnapshot& snapshot) const
|
bool RuntimeSnapshotProvider::TryRefreshSnapshotParameters(RuntimeRenderStateSnapshot& snapshot) const
|
||||||
{
|
{
|
||||||
const uint64_t expectedRenderStateVersion = snapshot.versions.renderStateVersion;
|
const uint64_t expectedRenderStateVersion = snapshot.versions.renderStateVersion;
|
||||||
if (!mRuntimeHost.TryRefreshCachedLayerStates(snapshot.states))
|
if (!TryRefreshCachedLayerStates(snapshot.states))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const RuntimeSnapshotVersions versions = GetVersions();
|
const RuntimeSnapshotVersions versions = GetVersions();
|
||||||
@@ -126,35 +130,37 @@ void RuntimeSnapshotProvider::ApplyFrameContext(RuntimeRenderStateSnapshot& snap
|
|||||||
|
|
||||||
std::vector<RuntimeRenderState> RuntimeSnapshotProvider::GetLayerRenderStates(unsigned outputWidth, unsigned outputHeight) const
|
std::vector<RuntimeRenderState> RuntimeSnapshotProvider::GetLayerRenderStates(unsigned outputWidth, unsigned outputHeight) const
|
||||||
{
|
{
|
||||||
return GetRenderStateSnapshot(outputWidth, outputHeight).states;
|
std::lock_guard<std::mutex> lock(mRuntimeHost.mMutex);
|
||||||
|
std::vector<RuntimeRenderState> states;
|
||||||
|
BuildLayerRenderStatesLocked(outputWidth, outputHeight, states);
|
||||||
|
return states;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RuntimeSnapshotProvider::TryGetLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
bool RuntimeSnapshotProvider::TryGetLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
||||||
{
|
{
|
||||||
RuntimeRenderStateSnapshot snapshot;
|
std::unique_lock<std::mutex> lock(mRuntimeHost.mMutex, std::try_to_lock);
|
||||||
if (!TryGetRenderStateSnapshot(outputWidth, outputHeight, snapshot))
|
if (!lock.owns_lock())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
states = std::move(snapshot.states);
|
states.clear();
|
||||||
|
BuildLayerRenderStatesLocked(outputWidth, outputHeight, states);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RuntimeSnapshotProvider::TryRefreshCachedLayerStates(std::vector<RuntimeRenderState>& states) const
|
bool RuntimeSnapshotProvider::TryRefreshCachedLayerStates(std::vector<RuntimeRenderState>& states) const
|
||||||
{
|
{
|
||||||
RuntimeRenderStateSnapshot snapshot;
|
std::unique_lock<std::mutex> lock(mRuntimeHost.mMutex, std::try_to_lock);
|
||||||
snapshot.versions.renderStateVersion = mRuntimeHost.GetRenderStateVersion();
|
if (!lock.owns_lock())
|
||||||
snapshot.versions.parameterStateVersion = mRuntimeHost.GetParameterStateVersion();
|
|
||||||
snapshot.states = states;
|
|
||||||
if (!TryRefreshSnapshotParameters(snapshot))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
states = std::move(snapshot.states);
|
RefreshCachedLayerStatesLocked(states);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeSnapshotProvider::RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState>& states) const
|
void RuntimeSnapshotProvider::RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState>& states) const
|
||||||
{
|
{
|
||||||
ApplyFrameContext(states, GetFrameContext());
|
std::lock_guard<std::mutex> lock(mRuntimeHost.mMutex);
|
||||||
|
RefreshDynamicRenderStateFieldsLocked(states);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t RuntimeSnapshotProvider::GetRenderStateVersion() const
|
uint64_t RuntimeSnapshotProvider::GetRenderStateVersion() const
|
||||||
@@ -166,3 +172,85 @@ uint64_t RuntimeSnapshotProvider::GetParameterStateVersion() const
|
|||||||
{
|
{
|
||||||
return GetVersions().parameterStateVersion;
|
return GetVersions().parameterStateVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RuntimeSnapshotProvider::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
||||||
|
{
|
||||||
|
const HealthTelemetry::SignalStatusSnapshot signalStatus = mRuntimeHost.mHealthTelemetry.GetSignalStatusSnapshot();
|
||||||
|
|
||||||
|
for (const RuntimeHost::LayerPersistentState& layer : mRuntimeHost.mPersistentState.layers)
|
||||||
|
{
|
||||||
|
auto shaderIt = mRuntimeHost.mPackagesById.find(layer.shaderId);
|
||||||
|
if (shaderIt == mRuntimeHost.mPackagesById.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
RuntimeRenderState state;
|
||||||
|
state.layerId = layer.id;
|
||||||
|
state.shaderId = layer.shaderId;
|
||||||
|
state.shaderName = shaderIt->second.displayName;
|
||||||
|
state.mixAmount = 1.0;
|
||||||
|
state.bypass = layer.bypass ? 1.0 : 0.0;
|
||||||
|
state.inputWidth = signalStatus.width;
|
||||||
|
state.inputHeight = signalStatus.height;
|
||||||
|
state.outputWidth = outputWidth;
|
||||||
|
state.outputHeight = outputHeight;
|
||||||
|
state.parameterDefinitions = shaderIt->second.parameters;
|
||||||
|
state.textureAssets = shaderIt->second.textureAssets;
|
||||||
|
state.fontAssets = shaderIt->second.fontAssets;
|
||||||
|
state.isTemporal = shaderIt->second.temporal.enabled;
|
||||||
|
state.temporalHistorySource = shaderIt->second.temporal.historySource;
|
||||||
|
state.requestedTemporalHistoryLength = shaderIt->second.temporal.requestedHistoryLength;
|
||||||
|
state.effectiveTemporalHistoryLength = shaderIt->second.temporal.effectiveHistoryLength;
|
||||||
|
state.feedback = shaderIt->second.feedback;
|
||||||
|
|
||||||
|
for (const ShaderParameterDefinition& definition : shaderIt->second.parameters)
|
||||||
|
{
|
||||||
|
ShaderParameterValue value = mRuntimeHost.DefaultValueForDefinition(definition);
|
||||||
|
auto valueIt = layer.parameterValues.find(definition.id);
|
||||||
|
if (valueIt != layer.parameterValues.end())
|
||||||
|
value = valueIt->second;
|
||||||
|
state.parameterValues[definition.id] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
states.push_back(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshDynamicRenderStateFieldsLocked(states);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeSnapshotProvider::RefreshCachedLayerStatesLocked(std::vector<RuntimeRenderState>& states) const
|
||||||
|
{
|
||||||
|
for (RuntimeRenderState& state : states)
|
||||||
|
{
|
||||||
|
const auto layerIt = std::find_if(mRuntimeHost.mPersistentState.layers.begin(), mRuntimeHost.mPersistentState.layers.end(),
|
||||||
|
[&state](const RuntimeHost::LayerPersistentState& layer) { return layer.id == state.layerId; });
|
||||||
|
if (layerIt == mRuntimeHost.mPersistentState.layers.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
state.bypass = layerIt->bypass ? 1.0 : 0.0;
|
||||||
|
state.parameterValues.clear();
|
||||||
|
for (const ShaderParameterDefinition& definition : state.parameterDefinitions)
|
||||||
|
{
|
||||||
|
ShaderParameterValue value = mRuntimeHost.DefaultValueForDefinition(definition);
|
||||||
|
auto valueIt = layerIt->parameterValues.find(definition.id);
|
||||||
|
if (valueIt != layerIt->parameterValues.end())
|
||||||
|
value = valueIt->second;
|
||||||
|
state.parameterValues[definition.id] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeSnapshotProvider::RefreshDynamicRenderStateFieldsLocked(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() - mRuntimeHost.mStartTime).count();
|
||||||
|
const double frameCount = static_cast<double>(mRuntimeHost.mFrameCounter.load(std::memory_order_relaxed));
|
||||||
|
|
||||||
|
for (RuntimeRenderState& state : states)
|
||||||
|
{
|
||||||
|
state.timeSeconds = timeSeconds;
|
||||||
|
state.utcTimeSeconds = clock.utcTimeSeconds;
|
||||||
|
state.utcOffsetSeconds = clock.utcOffsetSeconds;
|
||||||
|
state.startupRandom = mRuntimeHost.mStartupRandom;
|
||||||
|
state.frameCount = frameCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,5 +54,9 @@ public:
|
|||||||
uint64_t GetParameterStateVersion() const;
|
uint64_t GetParameterStateVersion() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const;
|
||||||
|
void RefreshCachedLayerStatesLocked(std::vector<RuntimeRenderState>& states) const;
|
||||||
|
void RefreshDynamicRenderStateFieldsLocked(std::vector<RuntimeRenderState>& states) const;
|
||||||
|
|
||||||
RuntimeHost& mRuntimeHost;
|
RuntimeHost& mRuntimeHost;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,12 +7,59 @@ RuntimeStore::RuntimeStore(RuntimeHost& runtimeHost) :
|
|||||||
|
|
||||||
bool RuntimeStore::InitializeStore(std::string& error)
|
bool RuntimeStore::InitializeStore(std::string& error)
|
||||||
{
|
{
|
||||||
return mRuntimeHost.Initialize(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
|
std::string RuntimeStore::BuildPersistentStateJson() const
|
||||||
{
|
{
|
||||||
return mRuntimeHost.BuildStateJson();
|
return SerializeJson(BuildRuntimeStateValue(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RuntimeStore::CreateStoredLayer(const std::string& shaderId, std::string& error)
|
bool RuntimeStore::CreateStoredLayer(const std::string& shaderId, std::string& error)
|
||||||
@@ -159,3 +206,122 @@ void RuntimeStore::ClearReloadRequest()
|
|||||||
{
|
{
|
||||||
mRuntimeHost.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();
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,5 +46,8 @@ public:
|
|||||||
void ClearReloadRequest();
|
void ClearReloadRequest();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
JsonValue BuildRuntimeStateValue() const;
|
||||||
|
JsonValue SerializeLayerStack() const;
|
||||||
|
|
||||||
RuntimeHost& mRuntimeHost;
|
RuntimeHost& mRuntimeHost;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -115,8 +115,11 @@ The codebase now has an initial Phase 1 compatibility split in place:
|
|||||||
These are still compatibility seams, not a completed subsystem extraction. Most of them continue to delegate heavily to `RuntimeHost`, `OpenGLComposite`, `DeckLinkSession`, and the existing bridge/pipeline classes. Their purpose is to give later Phase 1 work real code boundaries that can be expanded in parallel:
|
These are still compatibility seams, not a completed subsystem extraction. Most of them continue to delegate heavily to `RuntimeHost`, `OpenGLComposite`, `DeckLinkSession`, and the existing bridge/pipeline classes. Their purpose is to give later Phase 1 work real code boundaries that can be expanded in parallel:
|
||||||
|
|
||||||
- store-facing UI/runtime control calls in `OpenGLCompositeRuntimeControls.cpp` now route through `RuntimeStore`
|
- 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`
|
||||||
- mutation and reload policy now routes through `RuntimeCoordinator`
|
- 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 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
|
||||||
- service ingress and polling coordination now route through `ControlServices`
|
- service ingress and polling coordination now route through `ControlServices`
|
||||||
- timing and status writes now route through `HealthTelemetry`
|
- timing and status writes now route through `HealthTelemetry`
|
||||||
- `HealthTelemetry` now owns the live signal, video-I/O, and performance snapshots directly instead of `RuntimeHost` keeping those backing fields
|
- `HealthTelemetry` now owns the live signal, video-I/O, and performance snapshots directly instead of `RuntimeHost` keeping those backing fields
|
||||||
|
|||||||
Reference in New Issue
Block a user