Files
video-shader-toys/apps/RenderCadenceCompositor/control/RuntimeStateJson.h
2026-05-12 22:04:46 +10:00

326 lines
10 KiB
C++

#pragma once
#include "../app/AppConfig.h"
#include "../app/AppConfigProvider.h"
#include "../json/JsonWriter.h"
#include "../runtime/RuntimeLayerModel.h"
#include "../runtime/SupportedShaderCatalog.h"
#include "../telemetry/CadenceTelemetryJson.h"
#include <cstdint>
#include <string>
#include <vector>
namespace RenderCadenceCompositor
{
struct RuntimeStateJsonInput
{
const AppConfig& config;
const CadenceTelemetrySnapshot& telemetry;
unsigned short serverPort = 0;
bool videoOutputEnabled = false;
std::string videoOutputStatus;
const SupportedShaderCatalog& shaderCatalog;
const RuntimeLayerModelSnapshot& runtimeLayers;
};
inline void WriteVideoIoStatusJson(JsonWriter& writer, const RuntimeStateJsonInput& input)
{
writer.BeginObject();
writer.KeyString("backend", "decklink");
writer.KeyNull("modelName");
writer.KeyBool("supportsInternalKeying", false);
writer.KeyBool("supportsExternalKeying", false);
writer.KeyBool("keyerInterfaceAvailable", false);
writer.KeyBool("externalKeyingRequested", input.config.deckLink.externalKeyingEnabled);
writer.KeyBool("externalKeyingActive", input.videoOutputEnabled && input.config.deckLink.externalKeyingEnabled);
writer.KeyString("statusMessage", input.videoOutputStatus);
writer.EndObject();
}
inline void OutputDimensions(const RuntimeStateJsonInput& input, unsigned& width, unsigned& height)
{
VideoFormatDimensions(input.config.outputVideoFormat, width, height);
}
inline const char* ShaderParameterTypeName(ShaderParameterType type)
{
switch (type)
{
case ShaderParameterType::Float: return "float";
case ShaderParameterType::Vec2: return "vec2";
case ShaderParameterType::Color: return "color";
case ShaderParameterType::Boolean: return "bool";
case ShaderParameterType::Enum: return "enum";
case ShaderParameterType::Text: return "text";
case ShaderParameterType::Trigger: return "trigger";
}
return "unknown";
}
inline void WriteNumberArray(JsonWriter& writer, const std::vector<double>& values)
{
writer.BeginArray();
for (double value : values)
writer.Double(value);
writer.EndArray();
}
inline void WriteDefaultParameterValue(JsonWriter& writer, const ShaderParameterDefinition& parameter)
{
switch (parameter.type)
{
case ShaderParameterType::Boolean:
writer.Bool(parameter.defaultBoolean);
return;
case ShaderParameterType::Enum:
writer.String(parameter.defaultEnumValue);
return;
case ShaderParameterType::Text:
writer.String(parameter.defaultTextValue);
return;
case ShaderParameterType::Trigger:
writer.Double(0.0);
return;
case ShaderParameterType::Float:
writer.Double(parameter.defaultNumbers.empty() ? 0.0 : parameter.defaultNumbers.front());
return;
case ShaderParameterType::Vec2:
case ShaderParameterType::Color:
WriteNumberArray(writer, parameter.defaultNumbers);
return;
}
writer.Null();
}
inline void WriteParameterValue(JsonWriter& writer, const ShaderParameterDefinition& parameter, const ShaderParameterValue& value)
{
switch (parameter.type)
{
case ShaderParameterType::Boolean:
writer.Bool(value.booleanValue);
return;
case ShaderParameterType::Enum:
writer.String(value.enumValue);
return;
case ShaderParameterType::Text:
writer.String(value.textValue);
return;
case ShaderParameterType::Trigger:
case ShaderParameterType::Float:
writer.Double(value.numberValues.empty() ? 0.0 : value.numberValues.front());
return;
case ShaderParameterType::Vec2:
case ShaderParameterType::Color:
WriteNumberArray(writer, value.numberValues);
return;
}
writer.Null();
}
inline void WriteTemporalJson(JsonWriter& writer, const TemporalSettings& temporal)
{
writer.BeginObject();
writer.KeyBool("enabled", temporal.enabled);
writer.KeyString("historySource", "none");
writer.KeyUInt("requestedHistoryLength", temporal.requestedHistoryLength);
writer.KeyUInt("effectiveHistoryLength", temporal.effectiveHistoryLength);
writer.EndObject();
}
inline void WriteFeedbackJson(JsonWriter& writer, const FeedbackSettings& feedback)
{
writer.BeginObject();
writer.KeyBool("enabled", feedback.enabled);
writer.KeyString("writePass", feedback.writePassId);
writer.EndObject();
}
inline const char* RuntimeLayerBuildStateName(RuntimeLayerBuildState state)
{
switch (state)
{
case RuntimeLayerBuildState::Pending: return "pending";
case RuntimeLayerBuildState::Ready: return "ready";
case RuntimeLayerBuildState::Failed: return "failed";
}
return "unknown";
}
inline void WriteParameterDefinitionJson(JsonWriter& writer, const ShaderParameterDefinition& parameter, const ShaderParameterValue* value)
{
writer.BeginObject();
writer.KeyString("id", parameter.id);
writer.KeyString("label", parameter.label.empty() ? parameter.id : parameter.label);
writer.KeyString("description", parameter.description);
writer.KeyString("type", ShaderParameterTypeName(parameter.type));
writer.Key("defaultValue");
WriteDefaultParameterValue(writer, parameter);
writer.Key("value");
if (value)
WriteParameterValue(writer, parameter, *value);
else
WriteDefaultParameterValue(writer, parameter);
if (!parameter.minNumbers.empty())
{
writer.Key("min");
WriteNumberArray(writer, parameter.minNumbers);
}
if (!parameter.maxNumbers.empty())
{
writer.Key("max");
WriteNumberArray(writer, parameter.maxNumbers);
}
if (!parameter.stepNumbers.empty())
{
writer.Key("step");
WriteNumberArray(writer, parameter.stepNumbers);
}
if (parameter.type == ShaderParameterType::Enum)
{
writer.Key("options");
writer.BeginArray();
for (const ShaderParameterOption& option : parameter.enumOptions)
{
writer.BeginObject();
writer.KeyString("value", option.value);
writer.KeyString("label", option.label.empty() ? option.value : option.label);
writer.EndObject();
}
writer.EndArray();
}
if (parameter.type == ShaderParameterType::Text)
{
writer.KeyUInt("maxLength", parameter.maxLength);
if (!parameter.fontId.empty())
writer.KeyString("font", parameter.fontId);
}
writer.EndObject();
}
inline void WriteLayersJson(JsonWriter& writer, const RuntimeStateJsonInput& input)
{
writer.BeginArray();
for (const RuntimeLayerReadModel& layer : input.runtimeLayers.displayLayers)
{
const ShaderPackage* shaderPackage = input.shaderCatalog.FindPackage(layer.shaderId);
writer.BeginObject();
writer.KeyString("id", layer.id);
writer.KeyString("shaderId", layer.shaderId);
writer.KeyString("shaderName", layer.shaderName);
writer.KeyBool("bypass", layer.bypass);
writer.KeyString("buildState", RuntimeLayerBuildStateName(layer.buildState));
writer.KeyBool("renderReady", layer.renderReady);
writer.KeyString("message", layer.message);
writer.Key("temporal");
if (shaderPackage)
WriteTemporalJson(writer, shaderPackage->temporal);
else
WriteTemporalJson(writer, TemporalSettings());
writer.Key("feedback");
if (shaderPackage)
WriteFeedbackJson(writer, shaderPackage->feedback);
else
WriteFeedbackJson(writer, FeedbackSettings());
writer.Key("parameters");
writer.BeginArray();
for (const ShaderParameterDefinition& parameter : layer.parameterDefinitions)
{
const auto valueIt = layer.parameterValues.find(parameter.id);
WriteParameterDefinitionJson(writer, parameter, valueIt == layer.parameterValues.end() ? nullptr : &valueIt->second);
}
writer.EndArray();
writer.EndObject();
}
writer.EndArray();
}
inline std::string RuntimeStateToJson(const RuntimeStateJsonInput& input)
{
JsonWriter writer;
writer.BeginObject();
writer.Key("app");
writer.BeginObject();
writer.KeyUInt("serverPort", input.serverPort);
writer.KeyUInt("oscPort", input.config.oscPort);
writer.KeyString("oscBindAddress", input.config.oscBindAddress);
writer.KeyDouble("oscSmoothing", input.config.oscSmoothing);
writer.KeyBool("autoReload", input.config.autoReload);
writer.KeyUInt("maxTemporalHistoryFrames", static_cast<uint64_t>(input.config.maxTemporalHistoryFrames));
writer.KeyDouble("previewFps", input.config.previewFps);
writer.KeyBool("enableExternalKeying", input.config.deckLink.externalKeyingEnabled);
writer.KeyUInt("startupSettleMs", static_cast<uint64_t>(input.config.startupSettle.count()));
writer.KeyString("inputVideoFormat", input.config.inputVideoFormat);
writer.KeyString("inputFrameRate", input.config.inputFrameRate);
writer.KeyString("outputVideoFormat", input.config.outputVideoFormat);
writer.KeyString("outputFrameRate", input.config.outputFrameRate);
writer.EndObject();
writer.Key("runtime");
writer.BeginObject();
writer.KeyUInt("layerCount", static_cast<uint64_t>(input.runtimeLayers.displayLayers.size()));
writer.KeyBool("compileSucceeded", input.runtimeLayers.compileSucceeded);
writer.KeyString("compileMessage", input.runtimeLayers.compileMessage);
writer.EndObject();
writer.Key("video");
writer.BeginObject();
unsigned outputWidth = 0;
unsigned outputHeight = 0;
OutputDimensions(input, outputWidth, outputHeight);
writer.KeyBool("hasSignal", input.videoOutputEnabled);
writer.KeyUInt("width", outputWidth);
writer.KeyUInt("height", outputHeight);
writer.KeyString("modeName", input.config.outputVideoFormat + " output-only");
writer.EndObject();
writer.Key("decklink");
WriteVideoIoStatusJson(writer, input);
writer.Key("videoIO");
WriteVideoIoStatusJson(writer, input);
writer.Key("performance");
writer.BeginObject();
writer.KeyDouble("frameBudgetMs", FrameDurationMillisecondsFromRateString(input.config.outputFrameRate));
writer.KeyNull("renderMs");
writer.KeyNull("smoothedRenderMs");
writer.KeyNull("budgetUsedPercent");
writer.KeyNull("completionIntervalMs");
writer.KeyNull("smoothedCompletionIntervalMs");
writer.KeyNull("maxCompletionIntervalMs");
writer.KeyUInt("lateFrameCount", input.telemetry.displayedLate);
writer.KeyUInt("droppedFrameCount", input.telemetry.dropped);
writer.KeyNull("flushedFrameCount");
writer.Key("cadence");
WriteCadenceTelemetryJson(writer, input.telemetry);
writer.EndObject();
writer.KeyNull("backendPlayout");
writer.KeyNull("runtimeEvents");
writer.Key("shaders");
writer.BeginArray();
for (const SupportedShaderSummary& shader : input.shaderCatalog.Shaders())
{
writer.BeginObject();
writer.KeyString("id", shader.id);
writer.KeyString("name", shader.name);
writer.KeyString("description", shader.description);
writer.KeyString("category", shader.category);
writer.KeyBool("available", true);
writer.KeyNull("error");
writer.EndObject();
}
writer.EndArray();
writer.Key("stackPresets");
writer.BeginArray();
writer.EndArray();
writer.Key("layers");
WriteLayersJson(writer, input);
writer.EndObject();
return writer.StringValue();
}
}