Health
All checks were successful
CI / React UI Build (push) Successful in 10s
CI / Native Windows Build And Tests (push) Successful in 2m35s
CI / Windows Release Package (push) Successful in 2m46s

This commit is contained in:
Aiden
2026-05-11 00:08:12 +10:00
parent a24cdc0630
commit 34c145e80b
6 changed files with 244 additions and 200 deletions

View File

@@ -1,43 +1,141 @@
#include "stdafx.h"
#include "HealthTelemetry.h"
#include "RuntimeHost.h"
HealthTelemetry::HealthTelemetry(RuntimeHost& runtimeHost) :
mRuntimeHost(runtimeHost)
{
}
void HealthTelemetry::ReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName)
{
mRuntimeHost.WriteSignalStatus(hasSignal, width, height, modeName);
std::lock_guard<std::mutex> lock(mMutex);
mSignalStatus.hasSignal = hasSignal;
mSignalStatus.width = width;
mSignalStatus.height = height;
mSignalStatus.modeName = modeName;
}
bool HealthTelemetry::TryReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName)
{
return mRuntimeHost.TryWriteSignalStatus(hasSignal, width, height, modeName);
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
if (!lock.owns_lock())
return false;
mSignalStatus.hasSignal = hasSignal;
mSignalStatus.width = width;
mSignalStatus.height = height;
mSignalStatus.modeName = modeName;
return true;
}
void HealthTelemetry::ReportVideoIOStatus(const std::string& backendName, const std::string& modelName,
bool supportsInternalKeying, bool supportsExternalKeying, bool keyerInterfaceAvailable,
bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage)
{
std::lock_guard<std::mutex> lock(mMutex);
mVideoIOStatus.backendName = backendName;
mVideoIOStatus.modelName = modelName;
mVideoIOStatus.supportsInternalKeying = supportsInternalKeying;
mVideoIOStatus.supportsExternalKeying = supportsExternalKeying;
mVideoIOStatus.keyerInterfaceAvailable = keyerInterfaceAvailable;
mVideoIOStatus.externalKeyingRequested = externalKeyingRequested;
mVideoIOStatus.externalKeyingActive = externalKeyingActive;
mVideoIOStatus.statusMessage = statusMessage;
}
bool HealthTelemetry::TryReportVideoIOStatus(const std::string& backendName, const std::string& modelName,
bool supportsInternalKeying, bool supportsExternalKeying, bool keyerInterfaceAvailable,
bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage)
{
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
if (!lock.owns_lock())
return false;
mVideoIOStatus.backendName = backendName;
mVideoIOStatus.modelName = modelName;
mVideoIOStatus.supportsInternalKeying = supportsInternalKeying;
mVideoIOStatus.supportsExternalKeying = supportsExternalKeying;
mVideoIOStatus.keyerInterfaceAvailable = keyerInterfaceAvailable;
mVideoIOStatus.externalKeyingRequested = externalKeyingRequested;
mVideoIOStatus.externalKeyingActive = externalKeyingActive;
mVideoIOStatus.statusMessage = statusMessage;
return true;
}
void HealthTelemetry::RecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
{
mRuntimeHost.WritePerformanceStats(frameBudgetMilliseconds, renderMilliseconds);
std::lock_guard<std::mutex> lock(mMutex);
mPerformance.frameBudgetMilliseconds = std::max(frameBudgetMilliseconds, 0.0);
mPerformance.renderMilliseconds = std::max(renderMilliseconds, 0.0);
if (mPerformance.smoothedRenderMilliseconds <= 0.0)
mPerformance.smoothedRenderMilliseconds = mPerformance.renderMilliseconds;
else
mPerformance.smoothedRenderMilliseconds = mPerformance.smoothedRenderMilliseconds * 0.9 + mPerformance.renderMilliseconds * 0.1;
}
bool HealthTelemetry::TryRecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
{
return mRuntimeHost.TryWritePerformanceStats(frameBudgetMilliseconds, renderMilliseconds);
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
if (!lock.owns_lock())
return false;
mPerformance.frameBudgetMilliseconds = std::max(frameBudgetMilliseconds, 0.0);
mPerformance.renderMilliseconds = std::max(renderMilliseconds, 0.0);
if (mPerformance.smoothedRenderMilliseconds <= 0.0)
mPerformance.smoothedRenderMilliseconds = mPerformance.renderMilliseconds;
else
mPerformance.smoothedRenderMilliseconds = mPerformance.smoothedRenderMilliseconds * 0.9 + mPerformance.renderMilliseconds * 0.1;
return true;
}
void HealthTelemetry::RecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
{
mRuntimeHost.WriteFramePacingStats(completionIntervalMilliseconds, smoothedCompletionIntervalMilliseconds,
maxCompletionIntervalMilliseconds, lateFrameCount, droppedFrameCount, flushedFrameCount);
std::lock_guard<std::mutex> lock(mMutex);
mPerformance.completionIntervalMilliseconds = std::max(completionIntervalMilliseconds, 0.0);
mPerformance.smoothedCompletionIntervalMilliseconds = std::max(smoothedCompletionIntervalMilliseconds, 0.0);
mPerformance.maxCompletionIntervalMilliseconds = std::max(maxCompletionIntervalMilliseconds, 0.0);
mPerformance.lateFrameCount = lateFrameCount;
mPerformance.droppedFrameCount = droppedFrameCount;
mPerformance.flushedFrameCount = flushedFrameCount;
}
bool HealthTelemetry::TryRecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
{
return mRuntimeHost.TryWriteFramePacingStats(completionIntervalMilliseconds, smoothedCompletionIntervalMilliseconds,
maxCompletionIntervalMilliseconds, lateFrameCount, droppedFrameCount, flushedFrameCount);
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
if (!lock.owns_lock())
return false;
mPerformance.completionIntervalMilliseconds = std::max(completionIntervalMilliseconds, 0.0);
mPerformance.smoothedCompletionIntervalMilliseconds = std::max(smoothedCompletionIntervalMilliseconds, 0.0);
mPerformance.maxCompletionIntervalMilliseconds = std::max(maxCompletionIntervalMilliseconds, 0.0);
mPerformance.lateFrameCount = lateFrameCount;
mPerformance.droppedFrameCount = droppedFrameCount;
mPerformance.flushedFrameCount = flushedFrameCount;
return true;
}
HealthTelemetry::SignalStatusSnapshot HealthTelemetry::GetSignalStatusSnapshot() const
{
std::lock_guard<std::mutex> lock(mMutex);
return mSignalStatus;
}
HealthTelemetry::VideoIOStatusSnapshot HealthTelemetry::GetVideoIOStatusSnapshot() const
{
std::lock_guard<std::mutex> lock(mMutex);
return mVideoIOStatus;
}
HealthTelemetry::PerformanceSnapshot HealthTelemetry::GetPerformanceSnapshot() const
{
std::lock_guard<std::mutex> lock(mMutex);
return mPerformance;
}
HealthTelemetry::Snapshot HealthTelemetry::GetSnapshot() const
{
std::lock_guard<std::mutex> lock(mMutex);
Snapshot snapshot;
snapshot.signal = mSignalStatus;
snapshot.videoIO = mVideoIOStatus;
snapshot.performance = mPerformance;
return snapshot;
}

View File

@@ -1,21 +1,67 @@
#pragma once
#include <mutex>
#include <cstdint>
#include <string>
class RuntimeHost;
// Phase 1 compatibility seam for status and timing reporting. The current
// implementation still writes through RuntimeHost, but callers can now target
// HealthTelemetry as the home for operational visibility work.
// Phase 1 compatibility seam for status and timing reporting. HealthTelemetry
// now owns the current operational status snapshot directly, so callers can
// target it without flowing through RuntimeHost-owned backing fields.
class HealthTelemetry
{
public:
explicit HealthTelemetry(RuntimeHost& runtimeHost);
struct SignalStatusSnapshot
{
bool hasSignal = false;
unsigned width = 0;
unsigned height = 0;
std::string modeName;
};
struct VideoIOStatusSnapshot
{
std::string backendName = "decklink";
std::string modelName;
bool supportsInternalKeying = false;
bool supportsExternalKeying = false;
bool keyerInterfaceAvailable = false;
bool externalKeyingRequested = false;
bool externalKeyingActive = false;
std::string statusMessage;
};
struct PerformanceSnapshot
{
double frameBudgetMilliseconds = 0.0;
double renderMilliseconds = 0.0;
double smoothedRenderMilliseconds = 0.0;
double completionIntervalMilliseconds = 0.0;
double smoothedCompletionIntervalMilliseconds = 0.0;
double maxCompletionIntervalMilliseconds = 0.0;
uint64_t lateFrameCount = 0;
uint64_t droppedFrameCount = 0;
uint64_t flushedFrameCount = 0;
};
struct Snapshot
{
SignalStatusSnapshot signal;
VideoIOStatusSnapshot videoIO;
PerformanceSnapshot performance;
};
HealthTelemetry() = default;
void ReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
bool TryReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
void ReportVideoIOStatus(const std::string& backendName, const std::string& modelName,
bool supportsInternalKeying, bool supportsExternalKeying, bool keyerInterfaceAvailable,
bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage);
bool TryReportVideoIOStatus(const std::string& backendName, const std::string& modelName,
bool supportsInternalKeying, bool supportsExternalKeying, bool keyerInterfaceAvailable,
bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage);
void RecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
bool TryRecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
@@ -24,6 +70,14 @@ public:
bool TryRecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount);
SignalStatusSnapshot GetSignalStatusSnapshot() const;
VideoIOStatusSnapshot GetVideoIOStatusSnapshot() const;
PerformanceSnapshot GetPerformanceSnapshot() const;
Snapshot GetSnapshot() const;
private:
RuntimeHost& mRuntimeHost;
mutable std::mutex mMutex;
SignalStatusSnapshot mSignalStatus;
VideoIOStatusSnapshot mVideoIOStatus;
PerformanceSnapshot mPerformance;
};

View File

@@ -699,22 +699,10 @@ bool ParseParameterDefinitions(const JsonValue& manifestJson, ShaderPackage& sha
}
RuntimeHost::RuntimeHost()
: mHealthTelemetry(*this),
: mHealthTelemetry(),
mReloadRequested(false),
mCompileSucceeded(false),
mHasSignal(false),
mSignalWidth(0),
mSignalHeight(0),
mFrameBudgetMilliseconds(0.0),
mRenderMilliseconds(0.0),
mSmoothedRenderMilliseconds(0.0),
mCompletionIntervalMilliseconds(0.0),
mSmoothedCompletionIntervalMilliseconds(0.0),
mMaxCompletionIntervalMilliseconds(0.0),
mStartupRandom(GenerateStartupRandom()),
mLateFrameCount(0),
mDroppedFrameCount(0),
mFlushedFrameCount(0),
mServerPort(8080),
mAutoReloadEnabled(true),
mStartTime(std::chrono::steady_clock::now()),
@@ -1353,42 +1341,35 @@ void RuntimeHost::SetCompileStatus(bool succeeded, const std::string& message)
void RuntimeHost::SetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName)
{
const HealthTelemetry::SignalStatusSnapshot previousStatus = mHealthTelemetry.GetSignalStatusSnapshot();
mHealthTelemetry.ReportSignalStatus(hasSignal, width, height, modeName);
if (previousStatus.hasSignal != hasSignal ||
previousStatus.width != width ||
previousStatus.height != height ||
previousStatus.modeName != modeName)
{
std::lock_guard<std::mutex> lock(mMutex);
MarkRenderStateDirtyLocked();
}
}
bool RuntimeHost::TrySetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName)
{
return mHealthTelemetry.TryReportSignalStatus(hasSignal, width, height, modeName);
}
void RuntimeHost::WriteSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName)
{
std::lock_guard<std::mutex> lock(mMutex);
SetSignalStatusLocked(hasSignal, width, height, modeName);
}
bool RuntimeHost::TryWriteSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName)
{
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
if (!lock.owns_lock())
const HealthTelemetry::SignalStatusSnapshot previousStatus = mHealthTelemetry.GetSignalStatusSnapshot();
if (!mHealthTelemetry.TryReportSignalStatus(hasSignal, width, height, modeName))
return false;
SetSignalStatusLocked(hasSignal, width, height, modeName);
return true;
}
void RuntimeHost::SetSignalStatusLocked(bool hasSignal, unsigned width, unsigned height, const std::string& modeName)
{
const bool changed = mHasSignal != hasSignal ||
mSignalWidth != width ||
mSignalHeight != height ||
mSignalModeName != modeName;
mHasSignal = hasSignal;
mSignalWidth = width;
mSignalHeight = height;
mSignalModeName = modeName;
if (changed)
if (previousStatus.hasSignal != hasSignal ||
previousStatus.width != width ||
previousStatus.height != height ||
previousStatus.modeName != modeName)
{
std::lock_guard<std::mutex> lock(mMutex);
MarkRenderStateDirtyLocked();
}
return true;
}
void RuntimeHost::MarkRenderStateDirtyLocked()
@@ -1412,15 +1393,15 @@ void RuntimeHost::SetDeckLinkOutputStatus(const std::string& modelName, bool sup
void RuntimeHost::SetVideoIOStatus(const std::string& backendName, const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying,
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage)
{
std::lock_guard<std::mutex> lock(mMutex);
mDeckLinkOutputStatus.backendName = backendName;
mDeckLinkOutputStatus.modelName = modelName;
mDeckLinkOutputStatus.supportsInternalKeying = supportsInternalKeying;
mDeckLinkOutputStatus.supportsExternalKeying = supportsExternalKeying;
mDeckLinkOutputStatus.keyerInterfaceAvailable = keyerInterfaceAvailable;
mDeckLinkOutputStatus.externalKeyingRequested = externalKeyingRequested;
mDeckLinkOutputStatus.externalKeyingActive = externalKeyingActive;
mDeckLinkOutputStatus.statusMessage = statusMessage;
mHealthTelemetry.ReportVideoIOStatus(
backendName,
modelName,
supportsInternalKeying,
supportsExternalKeying,
keyerInterfaceAvailable,
externalKeyingRequested,
externalKeyingActive,
statusMessage);
}
void RuntimeHost::SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
@@ -1433,32 +1414,6 @@ bool RuntimeHost::TrySetPerformanceStats(double frameBudgetMilliseconds, double
return mHealthTelemetry.TryRecordPerformanceStats(frameBudgetMilliseconds, renderMilliseconds);
}
void RuntimeHost::WritePerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
{
std::lock_guard<std::mutex> lock(mMutex);
SetPerformanceStatsLocked(frameBudgetMilliseconds, renderMilliseconds);
}
bool RuntimeHost::TryWritePerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
{
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
if (!lock.owns_lock())
return false;
SetPerformanceStatsLocked(frameBudgetMilliseconds, renderMilliseconds);
return true;
}
void RuntimeHost::SetPerformanceStatsLocked(double frameBudgetMilliseconds, double renderMilliseconds)
{
mFrameBudgetMilliseconds = std::max(frameBudgetMilliseconds, 0.0);
mRenderMilliseconds = std::max(renderMilliseconds, 0.0);
if (mSmoothedRenderMilliseconds <= 0.0)
mSmoothedRenderMilliseconds = mRenderMilliseconds;
else
mSmoothedRenderMilliseconds = mSmoothedRenderMilliseconds * 0.9 + mRenderMilliseconds * 0.1;
}
void RuntimeHost::SetFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
{
@@ -1473,37 +1428,6 @@ bool RuntimeHost::TrySetFramePacingStats(double completionIntervalMilliseconds,
maxCompletionIntervalMilliseconds, lateFrameCount, droppedFrameCount, flushedFrameCount);
}
void RuntimeHost::WriteFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
{
std::lock_guard<std::mutex> lock(mMutex);
SetFramePacingStatsLocked(completionIntervalMilliseconds, smoothedCompletionIntervalMilliseconds,
maxCompletionIntervalMilliseconds, lateFrameCount, droppedFrameCount, flushedFrameCount);
}
bool RuntimeHost::TryWriteFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
{
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
if (!lock.owns_lock())
return false;
SetFramePacingStatsLocked(completionIntervalMilliseconds, smoothedCompletionIntervalMilliseconds,
maxCompletionIntervalMilliseconds, lateFrameCount, droppedFrameCount, flushedFrameCount);
return true;
}
void RuntimeHost::SetFramePacingStatsLocked(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
{
mCompletionIntervalMilliseconds = std::max(completionIntervalMilliseconds, 0.0);
mSmoothedCompletionIntervalMilliseconds = std::max(smoothedCompletionIntervalMilliseconds, 0.0);
mMaxCompletionIntervalMilliseconds = std::max(maxCompletionIntervalMilliseconds, 0.0);
mLateFrameCount = lateFrameCount;
mDroppedFrameCount = droppedFrameCount;
mFlushedFrameCount = flushedFrameCount;
}
void RuntimeHost::AdvanceFrame()
{
++mFrameCounter;
@@ -1632,6 +1556,8 @@ void RuntimeHost::RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState
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);
@@ -1644,8 +1570,8 @@ void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned ou
state.shaderName = shaderIt->second.displayName;
state.mixAmount = 1.0;
state.bypass = layer.bypass ? 1.0 : 0.0;
state.inputWidth = mSignalWidth;
state.inputHeight = mSignalHeight;
state.inputWidth = signalStatus.width;
state.inputHeight = signalStatus.height;
state.outputWidth = outputWidth;
state.outputHeight = outputHeight;
state.parameterDefinitions = shaderIt->second.parameters;
@@ -2095,6 +2021,7 @@ bool RuntimeHost::ResolvePaths(std::string& error)
JsonValue RuntimeHost::BuildStateValue() const
{
const HealthTelemetry::Snapshot telemetrySnapshot = mHealthTelemetry.GetSnapshot();
std::lock_guard<std::mutex> lock(mMutex);
JsonValue root = JsonValue::MakeObject();
@@ -2121,44 +2048,47 @@ JsonValue RuntimeHost::BuildStateValue() const
root.set("runtime", runtime);
JsonValue video = JsonValue::MakeObject();
video.set("hasSignal", JsonValue(mHasSignal));
video.set("width", JsonValue(static_cast<double>(mSignalWidth)));
video.set("height", JsonValue(static_cast<double>(mSignalHeight)));
video.set("modeName", JsonValue(mSignalModeName));
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(mDeckLinkOutputStatus.modelName));
deckLink.set("supportsInternalKeying", JsonValue(mDeckLinkOutputStatus.supportsInternalKeying));
deckLink.set("supportsExternalKeying", JsonValue(mDeckLinkOutputStatus.supportsExternalKeying));
deckLink.set("keyerInterfaceAvailable", JsonValue(mDeckLinkOutputStatus.keyerInterfaceAvailable));
deckLink.set("externalKeyingRequested", JsonValue(mDeckLinkOutputStatus.externalKeyingRequested));
deckLink.set("externalKeyingActive", JsonValue(mDeckLinkOutputStatus.externalKeyingActive));
deckLink.set("statusMessage", JsonValue(mDeckLinkOutputStatus.statusMessage));
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(mDeckLinkOutputStatus.backendName));
videoIO.set("modelName", JsonValue(mDeckLinkOutputStatus.modelName));
videoIO.set("supportsInternalKeying", JsonValue(mDeckLinkOutputStatus.supportsInternalKeying));
videoIO.set("supportsExternalKeying", JsonValue(mDeckLinkOutputStatus.supportsExternalKeying));
videoIO.set("keyerInterfaceAvailable", JsonValue(mDeckLinkOutputStatus.keyerInterfaceAvailable));
videoIO.set("externalKeyingRequested", JsonValue(mDeckLinkOutputStatus.externalKeyingRequested));
videoIO.set("externalKeyingActive", JsonValue(mDeckLinkOutputStatus.externalKeyingActive));
videoIO.set("statusMessage", JsonValue(mDeckLinkOutputStatus.statusMessage));
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(mFrameBudgetMilliseconds));
performance.set("renderMs", JsonValue(mRenderMilliseconds));
performance.set("smoothedRenderMs", JsonValue(mSmoothedRenderMilliseconds));
performance.set("budgetUsedPercent", JsonValue(mFrameBudgetMilliseconds > 0.0 ? (mSmoothedRenderMilliseconds / mFrameBudgetMilliseconds) * 100.0 : 0.0));
performance.set("completionIntervalMs", JsonValue(mCompletionIntervalMilliseconds));
performance.set("smoothedCompletionIntervalMs", JsonValue(mSmoothedCompletionIntervalMilliseconds));
performance.set("maxCompletionIntervalMs", JsonValue(mMaxCompletionIntervalMilliseconds));
performance.set("lateFrameCount", JsonValue(static_cast<double>(mLateFrameCount)));
performance.set("droppedFrameCount", JsonValue(static_cast<double>(mDroppedFrameCount)));
performance.set("flushedFrameCount", JsonValue(static_cast<double>(mFlushedFrameCount)));
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();

View File

@@ -101,18 +101,6 @@ private:
std::string outputFrameRate = "59.94";
};
struct DeckLinkOutputStatus
{
std::string backendName = "decklink";
std::string modelName;
bool supportsInternalKeying = false;
bool supportsExternalKeying = false;
bool keyerInterfaceAvailable = false;
bool externalKeyingRequested = false;
bool externalKeyingActive = false;
std::string statusMessage;
};
struct LayerPersistentState
{
std::string id;
@@ -148,23 +136,10 @@ private:
LayerPersistentState* FindLayerById(const std::string& layerId);
const LayerPersistentState* FindLayerById(const std::string& layerId) const;
std::string GenerateLayerId();
void WriteSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
bool TryWriteSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
void SetSignalStatusLocked(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
void MarkRenderStateDirtyLocked();
void MarkParameterStateDirtyLocked();
void WritePerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
bool TryWritePerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
void SetPerformanceStatsLocked(double frameBudgetMilliseconds, double renderMilliseconds);
void WriteFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount);
bool TryWriteFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount);
void SetFramePacingStatsLocked(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount);
private:
friend class HealthTelemetry;
HealthTelemetry mHealthTelemetry;
mutable std::mutex mMutex;
AppConfig mConfig;
@@ -186,21 +161,7 @@ private:
bool mReloadRequested;
bool mCompileSucceeded;
std::string mCompileMessage;
bool mHasSignal;
unsigned mSignalWidth;
unsigned mSignalHeight;
std::string mSignalModeName;
double mFrameBudgetMilliseconds;
double mRenderMilliseconds;
double mSmoothedRenderMilliseconds;
double mCompletionIntervalMilliseconds;
double mSmoothedCompletionIntervalMilliseconds;
double mMaxCompletionIntervalMilliseconds;
double mStartupRandom;
uint64_t mLateFrameCount;
uint64_t mDroppedFrameCount;
uint64_t mFlushedFrameCount;
DeckLinkOutputStatus mDeckLinkOutputStatus;
unsigned short mServerPort;
bool mAutoReloadEnabled;
std::chrono::steady_clock::time_point mStartTime;