Files
2026-05-12 00:52:33 +10:00

274 lines
12 KiB
C++

#pragma once
#include <mutex>
#include <cstddef>
#include <cstdint>
#include <string>
// HealthTelemetry owns the current operational status snapshot directly, so
// callers can report health without sharing runtime-store state.
class HealthTelemetry
{
public:
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 RuntimeEventQueueSnapshot
{
std::string queueName = "runtime-events";
std::size_t depth = 0;
std::size_t capacity = 0;
uint64_t droppedCount = 0;
double oldestEventAgeMilliseconds = 0.0;
};
struct RuntimeEventDispatchSnapshot
{
uint64_t dispatchCallCount = 0;
uint64_t dispatchedEventCount = 0;
uint64_t handlerInvocationCount = 0;
uint64_t handlerFailureCount = 0;
double lastDispatchDurationMilliseconds = 0.0;
double maxDispatchDurationMilliseconds = 0.0;
};
struct RuntimeEventMetricsSnapshot
{
RuntimeEventQueueSnapshot queue;
RuntimeEventDispatchSnapshot dispatch;
};
struct PersistenceSnapshot
{
uint64_t writeSuccessCount = 0;
uint64_t writeFailureCount = 0;
bool lastWriteSucceeded = true;
bool unsavedChanges = false;
bool newerRequestPending = false;
std::string lastTargetKind;
std::string lastTargetPath;
std::string lastReason;
std::string lastErrorMessage;
};
struct BackendPlayoutSnapshot
{
std::string lifecycleState = "NotStarted";
std::string completionResult = "Unknown";
std::size_t readyQueueDepth = 0;
std::size_t readyQueueCapacity = 0;
std::size_t minReadyQueueDepth = 0;
std::size_t maxReadyQueueDepth = 0;
uint64_t readyQueueZeroDepthCount = 0;
uint64_t readyQueuePushedCount = 0;
uint64_t readyQueuePoppedCount = 0;
uint64_t readyQueueDroppedCount = 0;
uint64_t readyQueueUnderrunCount = 0;
std::size_t systemFramePoolFree = 0;
std::size_t systemFramePoolReady = 0;
std::size_t systemFramePoolScheduled = 0;
uint64_t systemFrameUnderrunCount = 0;
uint64_t systemFrameRepeatCount = 0;
uint64_t systemFrameDropCount = 0;
double systemFrameAgeAtScheduleMilliseconds = 0.0;
double systemFrameAgeAtCompletionMilliseconds = 0.0;
double outputRenderMilliseconds = 0.0;
double smoothedOutputRenderMilliseconds = 0.0;
double maxOutputRenderMilliseconds = 0.0;
double outputFrameAcquireMilliseconds = 0.0;
double outputFrameRenderRequestMilliseconds = 0.0;
double outputFrameEndAccessMilliseconds = 0.0;
double outputRenderQueueWaitMilliseconds = 0.0;
double outputRenderDrawMilliseconds = 0.0;
double outputReadbackFenceWaitMilliseconds = 0.0;
double outputReadbackMapMilliseconds = 0.0;
double outputReadbackCopyMilliseconds = 0.0;
double outputCachedCopyMilliseconds = 0.0;
double outputAsyncQueueMilliseconds = 0.0;
double outputAsyncQueueBufferMilliseconds = 0.0;
double outputAsyncQueueSetupMilliseconds = 0.0;
double outputAsyncQueueReadPixelsMilliseconds = 0.0;
double outputAsyncQueueFenceMilliseconds = 0.0;
double outputSyncReadMilliseconds = 0.0;
uint64_t outputAsyncReadbackMissCount = 0;
uint64_t outputCachedFallbackCount = 0;
uint64_t outputSyncFallbackCount = 0;
uint64_t completedFrameIndex = 0;
uint64_t scheduledFrameIndex = 0;
uint64_t scheduledLeadFrames = 0;
bool actualDeckLinkBufferedFramesAvailable = false;
uint64_t actualDeckLinkBufferedFrames = 0;
std::size_t targetDeckLinkBufferedFrames = 0;
double deckLinkScheduleCallMilliseconds = 0.0;
uint64_t deckLinkScheduleFailureCount = 0;
uint64_t measuredLagFrames = 0;
uint64_t catchUpFrames = 0;
uint64_t lateStreak = 0;
uint64_t dropStreak = 0;
uint64_t lateFrameCount = 0;
uint64_t droppedFrameCount = 0;
uint64_t flushedFrameCount = 0;
bool degraded = false;
std::string statusMessage;
};
struct Snapshot
{
SignalStatusSnapshot signal;
VideoIOStatusSnapshot videoIO;
PerformanceSnapshot performance;
RuntimeEventMetricsSnapshot runtimeEvents;
PersistenceSnapshot persistence;
BackendPlayoutSnapshot backendPlayout;
};
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);
void RecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount);
bool TryRecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount);
void RecordRuntimeEventQueueMetrics(const std::string& queueName, std::size_t depth, std::size_t capacity,
uint64_t droppedCount, double oldestEventAgeMilliseconds);
bool TryRecordRuntimeEventQueueMetrics(const std::string& queueName, std::size_t depth, std::size_t capacity,
uint64_t droppedCount, double oldestEventAgeMilliseconds);
void RecordRuntimeEventDispatchStats(std::size_t dispatchedEvents, std::size_t handlerInvocations,
std::size_t handlerFailures, double dispatchDurationMilliseconds);
bool TryRecordRuntimeEventDispatchStats(std::size_t dispatchedEvents, std::size_t handlerInvocations,
std::size_t handlerFailures, double dispatchDurationMilliseconds);
void RecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath,
const std::string& reason, const std::string& errorMessage, bool newerRequestPending);
bool TryRecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath,
const std::string& reason, const std::string& errorMessage, bool newerRequestPending);
void RecordBackendPlayoutHealth(const std::string& lifecycleState, const std::string& completionResult,
std::size_t readyQueueDepth, std::size_t readyQueueCapacity, uint64_t readyQueuePushedCount,
std::size_t minReadyQueueDepth, std::size_t maxReadyQueueDepth, uint64_t readyQueueZeroDepthCount,
uint64_t readyQueuePoppedCount, uint64_t readyQueueDroppedCount, uint64_t readyQueueUnderrunCount,
double outputRenderMilliseconds, double smoothedOutputRenderMilliseconds, double maxOutputRenderMilliseconds,
double outputFrameAcquireMilliseconds, double outputFrameRenderRequestMilliseconds, double outputFrameEndAccessMilliseconds,
uint64_t completedFrameIndex, uint64_t scheduledFrameIndex, uint64_t scheduledLeadFrames,
uint64_t measuredLagFrames, uint64_t catchUpFrames, uint64_t lateStreak, uint64_t dropStreak,
uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount,
bool degraded, const std::string& statusMessage);
bool TryRecordBackendPlayoutHealth(const std::string& lifecycleState, const std::string& completionResult,
std::size_t readyQueueDepth, std::size_t readyQueueCapacity, uint64_t readyQueuePushedCount,
std::size_t minReadyQueueDepth, std::size_t maxReadyQueueDepth, uint64_t readyQueueZeroDepthCount,
uint64_t readyQueuePoppedCount, uint64_t readyQueueDroppedCount, uint64_t readyQueueUnderrunCount,
double outputRenderMilliseconds, double smoothedOutputRenderMilliseconds, double maxOutputRenderMilliseconds,
double outputFrameAcquireMilliseconds, double outputFrameRenderRequestMilliseconds, double outputFrameEndAccessMilliseconds,
uint64_t completedFrameIndex, uint64_t scheduledFrameIndex, uint64_t scheduledLeadFrames,
uint64_t measuredLagFrames, uint64_t catchUpFrames, uint64_t lateStreak, uint64_t dropStreak,
uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount,
bool degraded, const std::string& statusMessage);
void RecordOutputRenderQueueWait(double queueWaitMilliseconds);
bool TryRecordOutputRenderQueueWait(double queueWaitMilliseconds);
void RecordSystemMemoryPlayoutStats(std::size_t freeFrameCount, std::size_t readyFrameCount,
std::size_t scheduledFrameCount, uint64_t underrunCount, uint64_t repeatCount, uint64_t dropCount,
double frameAgeAtScheduleMilliseconds, double frameAgeAtCompletionMilliseconds);
bool TryRecordSystemMemoryPlayoutStats(std::size_t freeFrameCount, std::size_t readyFrameCount,
std::size_t scheduledFrameCount, uint64_t underrunCount, uint64_t repeatCount, uint64_t dropCount,
double frameAgeAtScheduleMilliseconds, double frameAgeAtCompletionMilliseconds);
void RecordDeckLinkBufferTelemetry(bool actualBufferedFramesAvailable, uint64_t actualBufferedFrames,
std::size_t targetBufferedFrames, double scheduleCallMilliseconds, uint64_t scheduleFailureCount);
bool TryRecordDeckLinkBufferTelemetry(bool actualBufferedFramesAvailable, uint64_t actualBufferedFrames,
std::size_t targetBufferedFrames, double scheduleCallMilliseconds, uint64_t scheduleFailureCount);
void RecordOutputRenderPipelineTiming(
double drawMilliseconds,
double fenceWaitMilliseconds,
double mapMilliseconds,
double readbackCopyMilliseconds,
double cachedCopyMilliseconds,
double asyncQueueMilliseconds,
double asyncQueueBufferMilliseconds,
double asyncQueueSetupMilliseconds,
double asyncQueueReadPixelsMilliseconds,
double asyncQueueFenceMilliseconds,
double syncReadMilliseconds,
bool asyncReadbackMissed,
bool cachedFallbackUsed,
bool syncFallbackUsed);
bool TryRecordOutputRenderPipelineTiming(
double drawMilliseconds,
double fenceWaitMilliseconds,
double mapMilliseconds,
double readbackCopyMilliseconds,
double cachedCopyMilliseconds,
double asyncQueueMilliseconds,
double asyncQueueBufferMilliseconds,
double asyncQueueSetupMilliseconds,
double asyncQueueReadPixelsMilliseconds,
double asyncQueueFenceMilliseconds,
double syncReadMilliseconds,
bool asyncReadbackMissed,
bool cachedFallbackUsed,
bool syncFallbackUsed);
SignalStatusSnapshot GetSignalStatusSnapshot() const;
VideoIOStatusSnapshot GetVideoIOStatusSnapshot() const;
PerformanceSnapshot GetPerformanceSnapshot() const;
RuntimeEventMetricsSnapshot GetRuntimeEventMetricsSnapshot() const;
PersistenceSnapshot GetPersistenceSnapshot() const;
BackendPlayoutSnapshot GetBackendPlayoutSnapshot() const;
Snapshot GetSnapshot() const;
private:
mutable std::mutex mMutex;
SignalStatusSnapshot mSignalStatus;
VideoIOStatusSnapshot mVideoIOStatus;
PerformanceSnapshot mPerformance;
RuntimeEventMetricsSnapshot mRuntimeEvents;
PersistenceSnapshot mPersistence;
BackendPlayoutSnapshot mBackendPlayout;
};