#pragma once #include #include #include #include namespace RenderCadenceCompositor { struct CadenceTelemetrySnapshot { double sampleSeconds = 0.0; double renderFps = 0.0; double scheduleFps = 0.0; std::size_t freeFrames = 0; std::size_t completedFrames = 0; std::size_t scheduledFrames = 0; uint64_t renderedTotal = 0; uint64_t scheduledTotal = 0; uint64_t completedPollMisses = 0; uint64_t scheduleFailures = 0; uint64_t completions = 0; uint64_t displayedLate = 0; uint64_t dropped = 0; uint64_t clockOverruns = 0; uint64_t clockSkippedFrames = 0; uint64_t shaderBuildsCommitted = 0; uint64_t shaderBuildFailures = 0; uint64_t inputFramesReceived = 0; uint64_t inputFramesDropped = 0; uint64_t inputConsumeMisses = 0; uint64_t inputUploadMisses = 0; std::size_t inputReadyFrames = 0; std::size_t inputReadingFrames = 0; double inputLatestAgeMilliseconds = 0.0; double inputUploadMilliseconds = 0.0; bool inputFormatSupported = true; bool inputSignalPresent = false; double inputCaptureFps = 0.0; double inputConvertMilliseconds = 0.0; double inputSubmitMilliseconds = 0.0; uint64_t inputNoSignalFrames = 0; uint64_t inputUnsupportedFrames = 0; uint64_t inputSubmitMisses = 0; std::string inputCaptureFormat = "none"; bool deckLinkBufferedAvailable = false; uint64_t deckLinkBuffered = 0; double deckLinkScheduleCallMilliseconds = 0.0; }; class CadenceTelemetry { public: template CadenceTelemetrySnapshot Sample( const SystemFrameExchange& exchange, const Output& output, const OutputThread& outputThread) { const auto now = Clock::now(); const double seconds = mHasLastSample ? std::chrono::duration_cast>(now - mLastSampleTime).count() : 0.0; const auto exchangeMetrics = exchange.Metrics(); const auto outputMetrics = output.Metrics(); const auto threadMetrics = outputThread.Metrics(); CadenceTelemetrySnapshot snapshot; snapshot.sampleSeconds = seconds; snapshot.renderedTotal = exchangeMetrics.completedFrames; snapshot.scheduledTotal = exchangeMetrics.scheduledFrames; snapshot.freeFrames = exchangeMetrics.freeCount; snapshot.completedFrames = exchangeMetrics.completedCount; snapshot.scheduledFrames = exchangeMetrics.scheduledCount; snapshot.completedPollMisses = threadMetrics.completedPollMisses; snapshot.scheduleFailures = outputMetrics.scheduleFailures > threadMetrics.scheduleFailures ? outputMetrics.scheduleFailures : threadMetrics.scheduleFailures; snapshot.completions = outputMetrics.completions; snapshot.displayedLate = outputMetrics.displayedLate; snapshot.dropped = outputMetrics.dropped; snapshot.deckLinkBufferedAvailable = outputMetrics.actualBufferedFramesAvailable; snapshot.deckLinkBuffered = outputMetrics.actualBufferedFrames; snapshot.deckLinkScheduleCallMilliseconds = outputMetrics.scheduleCallMilliseconds; if (mHasLastSample && seconds > 0.0) { snapshot.renderFps = static_cast(snapshot.renderedTotal - mLastRenderedFrames) / seconds; snapshot.scheduleFps = static_cast(snapshot.scheduledTotal - mLastScheduledFrames) / seconds; } mLastSampleTime = now; mLastRenderedFrames = snapshot.renderedTotal; mLastScheduledFrames = snapshot.scheduledTotal; mHasLastSample = true; return snapshot; } template CadenceTelemetrySnapshot Sample( const SystemFrameExchange& exchange, const Output& output, const OutputThread& outputThread, const RenderThread& renderThread) { CadenceTelemetrySnapshot snapshot = Sample(exchange, output, outputThread); const auto renderMetrics = renderThread.GetMetrics(); snapshot.clockOverruns = renderMetrics.clockOverruns; snapshot.clockSkippedFrames = renderMetrics.skippedFrames; snapshot.shaderBuildsCommitted = renderMetrics.shaderBuildsCommitted; snapshot.shaderBuildFailures = renderMetrics.shaderBuildFailures; snapshot.inputFramesReceived = renderMetrics.inputFramesReceived; snapshot.inputFramesDropped = renderMetrics.inputFramesDropped; snapshot.inputConsumeMisses = renderMetrics.inputConsumeMisses; snapshot.inputUploadMisses = renderMetrics.inputUploadMisses; snapshot.inputReadyFrames = renderMetrics.inputReadyFrames; snapshot.inputReadingFrames = renderMetrics.inputReadingFrames; snapshot.inputLatestAgeMilliseconds = renderMetrics.inputLatestAgeMilliseconds; snapshot.inputUploadMilliseconds = renderMetrics.inputUploadMilliseconds; snapshot.inputFormatSupported = renderMetrics.inputFormatSupported; snapshot.inputSignalPresent = renderMetrics.inputSignalPresent; return snapshot; } template CadenceTelemetrySnapshot Sample( const SystemFrameExchange& exchange, const Output& output, const OutputThread& outputThread, const RenderThread& renderThread, const InputEdge& inputEdge) { CadenceTelemetrySnapshot snapshot = Sample(exchange, output, outputThread, renderThread); const auto inputMetrics = inputEdge.Metrics(); snapshot.inputConvertMilliseconds = inputMetrics.convertMilliseconds; snapshot.inputSubmitMilliseconds = inputMetrics.submitMilliseconds; snapshot.inputNoSignalFrames = inputMetrics.noInputSourceFrames; snapshot.inputUnsupportedFrames = inputMetrics.unsupportedFrames; snapshot.inputSubmitMisses = inputMetrics.submitMisses; snapshot.inputCaptureFormat = inputMetrics.captureFormat ? inputMetrics.captureFormat : "none"; if (snapshot.sampleSeconds > 0.0) snapshot.inputCaptureFps = static_cast(inputMetrics.capturedFrames - mLastInputCapturedFrames) / snapshot.sampleSeconds; mLastInputCapturedFrames = inputMetrics.capturedFrames; return snapshot; } private: using Clock = std::chrono::steady_clock; Clock::time_point mLastSampleTime = Clock::now(); uint64_t mLastRenderedFrames = 0; uint64_t mLastScheduledFrames = 0; uint64_t mLastInputCapturedFrames = 0; bool mHasLastSample = false; }; }