#include "stdafx.h" #include "HealthTelemetry.h" void HealthTelemetry::ReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName) { std::lock_guard 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) { std::unique_lock 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 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 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) { std::lock_guard 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) { std::unique_lock 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) { std::lock_guard 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) { std::unique_lock 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; } void HealthTelemetry::RecordRuntimeEventQueueMetrics(const std::string& queueName, std::size_t depth, std::size_t capacity, uint64_t droppedCount, double oldestEventAgeMilliseconds) { std::lock_guard lock(mMutex); mRuntimeEvents.queue.queueName = queueName; mRuntimeEvents.queue.depth = depth; mRuntimeEvents.queue.capacity = capacity; mRuntimeEvents.queue.droppedCount = droppedCount; mRuntimeEvents.queue.oldestEventAgeMilliseconds = std::max(oldestEventAgeMilliseconds, 0.0); } bool HealthTelemetry::TryRecordRuntimeEventQueueMetrics(const std::string& queueName, std::size_t depth, std::size_t capacity, uint64_t droppedCount, double oldestEventAgeMilliseconds) { std::unique_lock lock(mMutex, std::try_to_lock); if (!lock.owns_lock()) return false; mRuntimeEvents.queue.queueName = queueName; mRuntimeEvents.queue.depth = depth; mRuntimeEvents.queue.capacity = capacity; mRuntimeEvents.queue.droppedCount = droppedCount; mRuntimeEvents.queue.oldestEventAgeMilliseconds = std::max(oldestEventAgeMilliseconds, 0.0); return true; } void HealthTelemetry::RecordRuntimeEventDispatchStats(std::size_t dispatchedEvents, std::size_t handlerInvocations, std::size_t handlerFailures, double dispatchDurationMilliseconds) { std::lock_guard lock(mMutex); ++mRuntimeEvents.dispatch.dispatchCallCount; mRuntimeEvents.dispatch.dispatchedEventCount += static_cast(dispatchedEvents); mRuntimeEvents.dispatch.handlerInvocationCount += static_cast(handlerInvocations); mRuntimeEvents.dispatch.handlerFailureCount += static_cast(handlerFailures); mRuntimeEvents.dispatch.lastDispatchDurationMilliseconds = std::max(dispatchDurationMilliseconds, 0.0); mRuntimeEvents.dispatch.maxDispatchDurationMilliseconds = std::max( mRuntimeEvents.dispatch.maxDispatchDurationMilliseconds, mRuntimeEvents.dispatch.lastDispatchDurationMilliseconds); } bool HealthTelemetry::TryRecordRuntimeEventDispatchStats(std::size_t dispatchedEvents, std::size_t handlerInvocations, std::size_t handlerFailures, double dispatchDurationMilliseconds) { std::unique_lock lock(mMutex, std::try_to_lock); if (!lock.owns_lock()) return false; ++mRuntimeEvents.dispatch.dispatchCallCount; mRuntimeEvents.dispatch.dispatchedEventCount += static_cast(dispatchedEvents); mRuntimeEvents.dispatch.handlerInvocationCount += static_cast(handlerInvocations); mRuntimeEvents.dispatch.handlerFailureCount += static_cast(handlerFailures); mRuntimeEvents.dispatch.lastDispatchDurationMilliseconds = std::max(dispatchDurationMilliseconds, 0.0); mRuntimeEvents.dispatch.maxDispatchDurationMilliseconds = std::max( mRuntimeEvents.dispatch.maxDispatchDurationMilliseconds, mRuntimeEvents.dispatch.lastDispatchDurationMilliseconds); return true; } void HealthTelemetry::RecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath, const std::string& reason, const std::string& errorMessage, bool newerRequestPending) { std::lock_guard lock(mMutex); if (succeeded) ++mPersistence.writeSuccessCount; else ++mPersistence.writeFailureCount; mPersistence.lastWriteSucceeded = succeeded; mPersistence.unsavedChanges = !succeeded || newerRequestPending; mPersistence.newerRequestPending = newerRequestPending; mPersistence.lastTargetKind = targetKind; mPersistence.lastTargetPath = targetPath; mPersistence.lastReason = reason; mPersistence.lastErrorMessage = errorMessage; } bool HealthTelemetry::TryRecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath, const std::string& reason, const std::string& errorMessage, bool newerRequestPending) { std::unique_lock lock(mMutex, std::try_to_lock); if (!lock.owns_lock()) return false; if (succeeded) ++mPersistence.writeSuccessCount; else ++mPersistence.writeFailureCount; mPersistence.lastWriteSucceeded = succeeded; mPersistence.unsavedChanges = !succeeded || newerRequestPending; mPersistence.newerRequestPending = newerRequestPending; mPersistence.lastTargetKind = targetKind; mPersistence.lastTargetPath = targetPath; mPersistence.lastReason = reason; mPersistence.lastErrorMessage = errorMessage; return true; } void HealthTelemetry::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) { std::lock_guard lock(mMutex); mBackendPlayout.lifecycleState = lifecycleState; mBackendPlayout.completionResult = completionResult; mBackendPlayout.readyQueueDepth = readyQueueDepth; mBackendPlayout.readyQueueCapacity = readyQueueCapacity; mBackendPlayout.minReadyQueueDepth = minReadyQueueDepth; mBackendPlayout.maxReadyQueueDepth = maxReadyQueueDepth; mBackendPlayout.readyQueueZeroDepthCount = readyQueueZeroDepthCount; mBackendPlayout.readyQueuePushedCount = readyQueuePushedCount; mBackendPlayout.readyQueuePoppedCount = readyQueuePoppedCount; mBackendPlayout.readyQueueDroppedCount = readyQueueDroppedCount; mBackendPlayout.readyQueueUnderrunCount = readyQueueUnderrunCount; mBackendPlayout.outputRenderMilliseconds = std::max(outputRenderMilliseconds, 0.0); mBackendPlayout.smoothedOutputRenderMilliseconds = std::max(smoothedOutputRenderMilliseconds, 0.0); mBackendPlayout.maxOutputRenderMilliseconds = std::max(maxOutputRenderMilliseconds, 0.0); mBackendPlayout.outputFrameAcquireMilliseconds = std::max(outputFrameAcquireMilliseconds, 0.0); mBackendPlayout.outputFrameRenderRequestMilliseconds = std::max(outputFrameRenderRequestMilliseconds, 0.0); mBackendPlayout.outputFrameEndAccessMilliseconds = std::max(outputFrameEndAccessMilliseconds, 0.0); mBackendPlayout.completedFrameIndex = completedFrameIndex; mBackendPlayout.scheduledFrameIndex = scheduledFrameIndex; mBackendPlayout.scheduledLeadFrames = scheduledLeadFrames; mBackendPlayout.measuredLagFrames = measuredLagFrames; mBackendPlayout.catchUpFrames = catchUpFrames; mBackendPlayout.lateStreak = lateStreak; mBackendPlayout.dropStreak = dropStreak; mBackendPlayout.lateFrameCount = lateFrameCount; mBackendPlayout.droppedFrameCount = droppedFrameCount; mBackendPlayout.flushedFrameCount = flushedFrameCount; mBackendPlayout.degraded = degraded; mBackendPlayout.statusMessage = statusMessage; } bool HealthTelemetry::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) { std::unique_lock lock(mMutex, std::try_to_lock); if (!lock.owns_lock()) return false; mBackendPlayout.lifecycleState = lifecycleState; mBackendPlayout.completionResult = completionResult; mBackendPlayout.readyQueueDepth = readyQueueDepth; mBackendPlayout.readyQueueCapacity = readyQueueCapacity; mBackendPlayout.minReadyQueueDepth = minReadyQueueDepth; mBackendPlayout.maxReadyQueueDepth = maxReadyQueueDepth; mBackendPlayout.readyQueueZeroDepthCount = readyQueueZeroDepthCount; mBackendPlayout.readyQueuePushedCount = readyQueuePushedCount; mBackendPlayout.readyQueuePoppedCount = readyQueuePoppedCount; mBackendPlayout.readyQueueDroppedCount = readyQueueDroppedCount; mBackendPlayout.readyQueueUnderrunCount = readyQueueUnderrunCount; mBackendPlayout.outputRenderMilliseconds = std::max(outputRenderMilliseconds, 0.0); mBackendPlayout.smoothedOutputRenderMilliseconds = std::max(smoothedOutputRenderMilliseconds, 0.0); mBackendPlayout.maxOutputRenderMilliseconds = std::max(maxOutputRenderMilliseconds, 0.0); mBackendPlayout.outputFrameAcquireMilliseconds = std::max(outputFrameAcquireMilliseconds, 0.0); mBackendPlayout.outputFrameRenderRequestMilliseconds = std::max(outputFrameRenderRequestMilliseconds, 0.0); mBackendPlayout.outputFrameEndAccessMilliseconds = std::max(outputFrameEndAccessMilliseconds, 0.0); mBackendPlayout.completedFrameIndex = completedFrameIndex; mBackendPlayout.scheduledFrameIndex = scheduledFrameIndex; mBackendPlayout.scheduledLeadFrames = scheduledLeadFrames; mBackendPlayout.measuredLagFrames = measuredLagFrames; mBackendPlayout.catchUpFrames = catchUpFrames; mBackendPlayout.lateStreak = lateStreak; mBackendPlayout.dropStreak = dropStreak; mBackendPlayout.lateFrameCount = lateFrameCount; mBackendPlayout.droppedFrameCount = droppedFrameCount; mBackendPlayout.flushedFrameCount = flushedFrameCount; mBackendPlayout.degraded = degraded; mBackendPlayout.statusMessage = statusMessage; return true; } void HealthTelemetry::RecordOutputRenderQueueWait(double queueWaitMilliseconds) { std::lock_guard lock(mMutex); mBackendPlayout.outputRenderQueueWaitMilliseconds = std::max(queueWaitMilliseconds, 0.0); } bool HealthTelemetry::TryRecordOutputRenderQueueWait(double queueWaitMilliseconds) { std::unique_lock lock(mMutex, std::try_to_lock); if (!lock.owns_lock()) return false; mBackendPlayout.outputRenderQueueWaitMilliseconds = std::max(queueWaitMilliseconds, 0.0); return true; } void HealthTelemetry::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) { std::lock_guard lock(mMutex); mBackendPlayout.outputRenderDrawMilliseconds = std::max(drawMilliseconds, 0.0); mBackendPlayout.outputReadbackFenceWaitMilliseconds = std::max(fenceWaitMilliseconds, 0.0); mBackendPlayout.outputReadbackMapMilliseconds = std::max(mapMilliseconds, 0.0); mBackendPlayout.outputReadbackCopyMilliseconds = std::max(readbackCopyMilliseconds, 0.0); mBackendPlayout.outputCachedCopyMilliseconds = std::max(cachedCopyMilliseconds, 0.0); mBackendPlayout.outputAsyncQueueMilliseconds = std::max(asyncQueueMilliseconds, 0.0); mBackendPlayout.outputAsyncQueueBufferMilliseconds = std::max(asyncQueueBufferMilliseconds, 0.0); mBackendPlayout.outputAsyncQueueSetupMilliseconds = std::max(asyncQueueSetupMilliseconds, 0.0); mBackendPlayout.outputAsyncQueueReadPixelsMilliseconds = std::max(asyncQueueReadPixelsMilliseconds, 0.0); mBackendPlayout.outputAsyncQueueFenceMilliseconds = std::max(asyncQueueFenceMilliseconds, 0.0); mBackendPlayout.outputSyncReadMilliseconds = std::max(syncReadMilliseconds, 0.0); if (asyncReadbackMissed) ++mBackendPlayout.outputAsyncReadbackMissCount; if (cachedFallbackUsed) ++mBackendPlayout.outputCachedFallbackCount; if (syncFallbackUsed) ++mBackendPlayout.outputSyncFallbackCount; } bool HealthTelemetry::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) { std::unique_lock lock(mMutex, std::try_to_lock); if (!lock.owns_lock()) return false; mBackendPlayout.outputRenderDrawMilliseconds = std::max(drawMilliseconds, 0.0); mBackendPlayout.outputReadbackFenceWaitMilliseconds = std::max(fenceWaitMilliseconds, 0.0); mBackendPlayout.outputReadbackMapMilliseconds = std::max(mapMilliseconds, 0.0); mBackendPlayout.outputReadbackCopyMilliseconds = std::max(readbackCopyMilliseconds, 0.0); mBackendPlayout.outputCachedCopyMilliseconds = std::max(cachedCopyMilliseconds, 0.0); mBackendPlayout.outputAsyncQueueMilliseconds = std::max(asyncQueueMilliseconds, 0.0); mBackendPlayout.outputAsyncQueueBufferMilliseconds = std::max(asyncQueueBufferMilliseconds, 0.0); mBackendPlayout.outputAsyncQueueSetupMilliseconds = std::max(asyncQueueSetupMilliseconds, 0.0); mBackendPlayout.outputAsyncQueueReadPixelsMilliseconds = std::max(asyncQueueReadPixelsMilliseconds, 0.0); mBackendPlayout.outputAsyncQueueFenceMilliseconds = std::max(asyncQueueFenceMilliseconds, 0.0); mBackendPlayout.outputSyncReadMilliseconds = std::max(syncReadMilliseconds, 0.0); if (asyncReadbackMissed) ++mBackendPlayout.outputAsyncReadbackMissCount; if (cachedFallbackUsed) ++mBackendPlayout.outputCachedFallbackCount; if (syncFallbackUsed) ++mBackendPlayout.outputSyncFallbackCount; return true; } HealthTelemetry::SignalStatusSnapshot HealthTelemetry::GetSignalStatusSnapshot() const { std::lock_guard lock(mMutex); return mSignalStatus; } HealthTelemetry::VideoIOStatusSnapshot HealthTelemetry::GetVideoIOStatusSnapshot() const { std::lock_guard lock(mMutex); return mVideoIOStatus; } HealthTelemetry::PerformanceSnapshot HealthTelemetry::GetPerformanceSnapshot() const { std::lock_guard lock(mMutex); return mPerformance; } HealthTelemetry::RuntimeEventMetricsSnapshot HealthTelemetry::GetRuntimeEventMetricsSnapshot() const { std::lock_guard lock(mMutex); return mRuntimeEvents; } HealthTelemetry::PersistenceSnapshot HealthTelemetry::GetPersistenceSnapshot() const { std::lock_guard lock(mMutex); return mPersistence; } HealthTelemetry::BackendPlayoutSnapshot HealthTelemetry::GetBackendPlayoutSnapshot() const { std::lock_guard lock(mMutex); return mBackendPlayout; } HealthTelemetry::Snapshot HealthTelemetry::GetSnapshot() const { std::lock_guard lock(mMutex); Snapshot snapshot; snapshot.signal = mSignalStatus; snapshot.videoIO = mVideoIOStatus; snapshot.performance = mPerformance; snapshot.runtimeEvents = mRuntimeEvents; snapshot.persistence = mPersistence; snapshot.backendPlayout = mBackendPlayout; return snapshot; }