#include "RuntimeStatePresenter.h" #include "RuntimeStateJson.h" #include "RuntimeStore.h" std::string RuntimeStatePresenter::BuildRuntimeStateJson(const RuntimeStore& runtimeStore) { return SerializeJson(BuildRuntimeStateValue(runtimeStore), true); } JsonValue RuntimeStatePresenter::BuildRuntimeStateValue(const RuntimeStore& runtimeStore) { const RuntimeStatePresentationReadModel model = runtimeStore.BuildRuntimeStatePresentationReadModel(); const HealthTelemetry::Snapshot& telemetrySnapshot = model.telemetry; JsonValue root = JsonValue::MakeObject(); JsonValue app = JsonValue::MakeObject(); app.set("serverPort", JsonValue(static_cast(model.serverPort))); app.set("oscPort", JsonValue(static_cast(model.config.oscPort))); app.set("oscBindAddress", JsonValue(model.config.oscBindAddress)); app.set("oscSmoothing", JsonValue(model.config.oscSmoothing)); app.set("autoReload", JsonValue(model.autoReloadEnabled)); app.set("maxTemporalHistoryFrames", JsonValue(static_cast(model.config.maxTemporalHistoryFrames))); app.set("previewFps", JsonValue(static_cast(model.config.previewFps))); app.set("enableExternalKeying", JsonValue(model.config.enableExternalKeying)); app.set("inputVideoFormat", JsonValue(model.config.inputVideoFormat)); app.set("inputFrameRate", JsonValue(model.config.inputFrameRate)); app.set("outputVideoFormat", JsonValue(model.config.outputVideoFormat)); app.set("outputFrameRate", JsonValue(model.config.outputFrameRate)); root.set("app", app); JsonValue runtime = JsonValue::MakeObject(); runtime.set("layerCount", JsonValue(static_cast(model.layerStack.LayerCount()))); runtime.set("compileSucceeded", JsonValue(model.compileSucceeded)); runtime.set("compileMessage", JsonValue(model.compileMessage)); root.set("runtime", runtime); JsonValue video = JsonValue::MakeObject(); video.set("hasSignal", JsonValue(telemetrySnapshot.signal.hasSignal)); video.set("width", JsonValue(static_cast(telemetrySnapshot.signal.width))); video.set("height", JsonValue(static_cast(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(telemetrySnapshot.performance.lateFrameCount))); performance.set("droppedFrameCount", JsonValue(static_cast(telemetrySnapshot.performance.droppedFrameCount))); performance.set("flushedFrameCount", JsonValue(static_cast(telemetrySnapshot.performance.flushedFrameCount))); root.set("performance", performance); JsonValue readyQueue = JsonValue::MakeObject(); readyQueue.set("depth", JsonValue(static_cast(telemetrySnapshot.backendPlayout.readyQueueDepth))); readyQueue.set("capacity", JsonValue(static_cast(telemetrySnapshot.backendPlayout.readyQueueCapacity))); readyQueue.set("minDepth", JsonValue(static_cast(telemetrySnapshot.backendPlayout.minReadyQueueDepth))); readyQueue.set("maxDepth", JsonValue(static_cast(telemetrySnapshot.backendPlayout.maxReadyQueueDepth))); readyQueue.set("zeroDepthCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.readyQueueZeroDepthCount))); readyQueue.set("pushedCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.readyQueuePushedCount))); readyQueue.set("poppedCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.readyQueuePoppedCount))); readyQueue.set("droppedCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.readyQueueDroppedCount))); readyQueue.set("underrunCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.readyQueueUnderrunCount))); JsonValue systemMemory = JsonValue::MakeObject(); systemMemory.set("freeFrameCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.systemFramePoolFree))); systemMemory.set("readyFrameCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.systemFramePoolReady))); systemMemory.set("scheduledFrameCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.systemFramePoolScheduled))); systemMemory.set("underrunCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.systemFrameUnderrunCount))); systemMemory.set("repeatCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.systemFrameRepeatCount))); systemMemory.set("dropCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.systemFrameDropCount))); systemMemory.set("ageAtScheduleMs", JsonValue(telemetrySnapshot.backendPlayout.systemFrameAgeAtScheduleMilliseconds)); systemMemory.set("ageAtCompletionMs", JsonValue(telemetrySnapshot.backendPlayout.systemFrameAgeAtCompletionMilliseconds)); JsonValue outputRender = JsonValue::MakeObject(); outputRender.set("renderMs", JsonValue(telemetrySnapshot.backendPlayout.outputRenderMilliseconds)); outputRender.set("smoothedRenderMs", JsonValue(telemetrySnapshot.backendPlayout.smoothedOutputRenderMilliseconds)); outputRender.set("maxRenderMs", JsonValue(telemetrySnapshot.backendPlayout.maxOutputRenderMilliseconds)); outputRender.set("acquireFrameMs", JsonValue(telemetrySnapshot.backendPlayout.outputFrameAcquireMilliseconds)); outputRender.set("renderRequestMs", JsonValue(telemetrySnapshot.backendPlayout.outputFrameRenderRequestMilliseconds)); outputRender.set("endAccessMs", JsonValue(telemetrySnapshot.backendPlayout.outputFrameEndAccessMilliseconds)); outputRender.set("queueWaitMs", JsonValue(telemetrySnapshot.backendPlayout.outputRenderQueueWaitMilliseconds)); outputRender.set("drawMs", JsonValue(telemetrySnapshot.backendPlayout.outputRenderDrawMilliseconds)); outputRender.set("fenceWaitMs", JsonValue(telemetrySnapshot.backendPlayout.outputReadbackFenceWaitMilliseconds)); outputRender.set("mapMs", JsonValue(telemetrySnapshot.backendPlayout.outputReadbackMapMilliseconds)); outputRender.set("readbackCopyMs", JsonValue(telemetrySnapshot.backendPlayout.outputReadbackCopyMilliseconds)); outputRender.set("cachedCopyMs", JsonValue(telemetrySnapshot.backendPlayout.outputCachedCopyMilliseconds)); outputRender.set("asyncQueueMs", JsonValue(telemetrySnapshot.backendPlayout.outputAsyncQueueMilliseconds)); outputRender.set("asyncQueueBufferMs", JsonValue(telemetrySnapshot.backendPlayout.outputAsyncQueueBufferMilliseconds)); outputRender.set("asyncQueueSetupMs", JsonValue(telemetrySnapshot.backendPlayout.outputAsyncQueueSetupMilliseconds)); outputRender.set("asyncQueueReadPixelsMs", JsonValue(telemetrySnapshot.backendPlayout.outputAsyncQueueReadPixelsMilliseconds)); outputRender.set("asyncQueueFenceMs", JsonValue(telemetrySnapshot.backendPlayout.outputAsyncQueueFenceMilliseconds)); outputRender.set("syncReadMs", JsonValue(telemetrySnapshot.backendPlayout.outputSyncReadMilliseconds)); outputRender.set("asyncReadbackMissCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.outputAsyncReadbackMissCount))); outputRender.set("cachedFallbackCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.outputCachedFallbackCount))); outputRender.set("syncFallbackCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.outputSyncFallbackCount))); JsonValue recovery = JsonValue::MakeObject(); recovery.set("completionResult", JsonValue(telemetrySnapshot.backendPlayout.completionResult)); recovery.set("completedFrameIndex", JsonValue(static_cast(telemetrySnapshot.backendPlayout.completedFrameIndex))); recovery.set("scheduledFrameIndex", JsonValue(static_cast(telemetrySnapshot.backendPlayout.scheduledFrameIndex))); recovery.set("scheduledLeadFrames", JsonValue(static_cast(telemetrySnapshot.backendPlayout.scheduledLeadFrames))); recovery.set("measuredLagFrames", JsonValue(static_cast(telemetrySnapshot.backendPlayout.measuredLagFrames))); recovery.set("catchUpFrames", JsonValue(static_cast(telemetrySnapshot.backendPlayout.catchUpFrames))); recovery.set("lateStreak", JsonValue(static_cast(telemetrySnapshot.backendPlayout.lateStreak))); recovery.set("dropStreak", JsonValue(static_cast(telemetrySnapshot.backendPlayout.dropStreak))); JsonValue backendPlayout = JsonValue::MakeObject(); backendPlayout.set("lifecycleState", JsonValue(telemetrySnapshot.backendPlayout.lifecycleState)); backendPlayout.set("degraded", JsonValue(telemetrySnapshot.backendPlayout.degraded)); backendPlayout.set("statusMessage", JsonValue(telemetrySnapshot.backendPlayout.statusMessage)); backendPlayout.set("lateFrameCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.lateFrameCount))); backendPlayout.set("droppedFrameCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.droppedFrameCount))); backendPlayout.set("flushedFrameCount", JsonValue(static_cast(telemetrySnapshot.backendPlayout.flushedFrameCount))); backendPlayout.set("readyQueue", readyQueue); backendPlayout.set("systemMemory", systemMemory); backendPlayout.set("outputRender", outputRender); backendPlayout.set("recovery", recovery); root.set("backendPlayout", backendPlayout); JsonValue eventQueue = JsonValue::MakeObject(); eventQueue.set("name", JsonValue(telemetrySnapshot.runtimeEvents.queue.queueName)); eventQueue.set("depth", JsonValue(static_cast(telemetrySnapshot.runtimeEvents.queue.depth))); eventQueue.set("capacity", JsonValue(static_cast(telemetrySnapshot.runtimeEvents.queue.capacity))); eventQueue.set("droppedCount", JsonValue(static_cast(telemetrySnapshot.runtimeEvents.queue.droppedCount))); eventQueue.set("oldestEventAgeMs", JsonValue(telemetrySnapshot.runtimeEvents.queue.oldestEventAgeMilliseconds)); JsonValue eventDispatch = JsonValue::MakeObject(); eventDispatch.set("dispatchCallCount", JsonValue(static_cast(telemetrySnapshot.runtimeEvents.dispatch.dispatchCallCount))); eventDispatch.set("dispatchedEventCount", JsonValue(static_cast(telemetrySnapshot.runtimeEvents.dispatch.dispatchedEventCount))); eventDispatch.set("handlerInvocationCount", JsonValue(static_cast(telemetrySnapshot.runtimeEvents.dispatch.handlerInvocationCount))); eventDispatch.set("handlerFailureCount", JsonValue(static_cast(telemetrySnapshot.runtimeEvents.dispatch.handlerFailureCount))); eventDispatch.set("lastDispatchDurationMs", JsonValue(telemetrySnapshot.runtimeEvents.dispatch.lastDispatchDurationMilliseconds)); eventDispatch.set("maxDispatchDurationMs", JsonValue(telemetrySnapshot.runtimeEvents.dispatch.maxDispatchDurationMilliseconds)); JsonValue runtimeEvents = JsonValue::MakeObject(); runtimeEvents.set("queue", eventQueue); runtimeEvents.set("dispatch", eventDispatch); root.set("runtimeEvents", runtimeEvents); JsonValue shaderLibrary = JsonValue::MakeArray(); for (const ShaderPackageStatus& status : model.packageStatuses) { 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 = model.shaderCatalog.packagesById.find(status.id); if (status.available && shaderIt != model.shaderCatalog.packagesById.end() && shaderIt->second.temporal.enabled) { const ShaderPackage& shaderPackage = shaderIt->second; JsonValue temporal = JsonValue::MakeObject(); temporal.set("enabled", JsonValue(true)); temporal.set("historySource", JsonValue(RuntimeStateJson::TemporalHistorySourceToString(shaderPackage.temporal.historySource))); temporal.set("requestedHistoryLength", JsonValue(static_cast(shaderPackage.temporal.requestedHistoryLength))); temporal.set("effectiveHistoryLength", JsonValue(static_cast(shaderPackage.temporal.effectiveHistoryLength))); shader.set("temporal", temporal); } if (status.available && shaderIt != model.shaderCatalog.packagesById.end() && shaderIt->second.feedback.enabled) { const ShaderPackage& shaderPackage = shaderIt->second; JsonValue feedback = JsonValue::MakeObject(); feedback.set("enabled", JsonValue(true)); feedback.set("writePass", JsonValue(shaderPackage.feedback.writePassId)); shader.set("feedback", feedback); } shaderLibrary.pushBack(shader); } root.set("shaders", shaderLibrary); JsonValue stackPresets = JsonValue::MakeArray(); for (const std::string& presetName : model.stackPresetNames) stackPresets.pushBack(JsonValue(presetName)); root.set("stackPresets", stackPresets); root.set("layers", RuntimeStateJson::SerializeLayerStack(model.layerStack.Layers(), model.shaderCatalog.packagesById)); return root; }