#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 #include #include 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& 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 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) { 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"); 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(); if (shaderPackage) { for (const ShaderParameterDefinition& parameter : shaderPackage->parameters) WriteParameterDefinitionJson(writer, parameter); } 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(input.config.maxTemporalHistoryFrames)); writer.KeyDouble("previewFps", input.config.previewFps); writer.KeyBool("enableExternalKeying", input.config.deckLink.externalKeyingEnabled); 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(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(); } }