Health
This commit is contained in:
@@ -202,7 +202,7 @@ void OpenGLComposite::PublishVideoIOStatus(const std::string& statusMessage)
|
|||||||
if (!statusMessage.empty())
|
if (!statusMessage.empty())
|
||||||
mVideoBackend->SetStatusMessage(statusMessage);
|
mVideoBackend->SetStatusMessage(statusMessage);
|
||||||
|
|
||||||
mRuntimeHost->SetVideoIOStatus(
|
mRuntimeHost->GetHealthTelemetry().ReportVideoIOStatus(
|
||||||
"decklink",
|
"decklink",
|
||||||
mVideoBackend->OutputModelName(),
|
mVideoBackend->OutputModelName(),
|
||||||
mVideoBackend->SupportsInternalKeying(),
|
mVideoBackend->SupportsInternalKeying(),
|
||||||
|
|||||||
@@ -1,43 +1,141 @@
|
|||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "HealthTelemetry.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)
|
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)
|
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)
|
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)
|
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,
|
void HealthTelemetry::RecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
|
||||||
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
|
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
|
||||||
{
|
{
|
||||||
mRuntimeHost.WriteFramePacingStats(completionIntervalMilliseconds, smoothedCompletionIntervalMilliseconds,
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
maxCompletionIntervalMilliseconds, lateFrameCount, droppedFrameCount, flushedFrameCount);
|
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,
|
bool HealthTelemetry::TryRecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
|
||||||
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
|
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
|
||||||
{
|
{
|
||||||
return mRuntimeHost.TryWriteFramePacingStats(completionIntervalMilliseconds, smoothedCompletionIntervalMilliseconds,
|
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
|
||||||
maxCompletionIntervalMilliseconds, lateFrameCount, droppedFrameCount, flushedFrameCount);
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,67 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class RuntimeHost;
|
// Phase 1 compatibility seam for status and timing reporting. HealthTelemetry
|
||||||
|
// now owns the current operational status snapshot directly, so callers can
|
||||||
// Phase 1 compatibility seam for status and timing reporting. The current
|
// target it without flowing through RuntimeHost-owned backing fields.
|
||||||
// implementation still writes through RuntimeHost, but callers can now target
|
|
||||||
// HealthTelemetry as the home for operational visibility work.
|
|
||||||
class HealthTelemetry
|
class HealthTelemetry
|
||||||
{
|
{
|
||||||
public:
|
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);
|
void ReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
|
||||||
bool TryReportSignalStatus(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);
|
void RecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
|
||||||
bool TryRecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
|
bool TryRecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
|
||||||
|
|
||||||
@@ -24,6 +70,14 @@ public:
|
|||||||
bool TryRecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
|
bool TryRecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
|
||||||
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount);
|
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount);
|
||||||
|
|
||||||
|
SignalStatusSnapshot GetSignalStatusSnapshot() const;
|
||||||
|
VideoIOStatusSnapshot GetVideoIOStatusSnapshot() const;
|
||||||
|
PerformanceSnapshot GetPerformanceSnapshot() const;
|
||||||
|
Snapshot GetSnapshot() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RuntimeHost& mRuntimeHost;
|
mutable std::mutex mMutex;
|
||||||
|
SignalStatusSnapshot mSignalStatus;
|
||||||
|
VideoIOStatusSnapshot mVideoIOStatus;
|
||||||
|
PerformanceSnapshot mPerformance;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -699,22 +699,10 @@ bool ParseParameterDefinitions(const JsonValue& manifestJson, ShaderPackage& sha
|
|||||||
}
|
}
|
||||||
|
|
||||||
RuntimeHost::RuntimeHost()
|
RuntimeHost::RuntimeHost()
|
||||||
: mHealthTelemetry(*this),
|
: mHealthTelemetry(),
|
||||||
mReloadRequested(false),
|
mReloadRequested(false),
|
||||||
mCompileSucceeded(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()),
|
mStartupRandom(GenerateStartupRandom()),
|
||||||
mLateFrameCount(0),
|
|
||||||
mDroppedFrameCount(0),
|
|
||||||
mFlushedFrameCount(0),
|
|
||||||
mServerPort(8080),
|
mServerPort(8080),
|
||||||
mAutoReloadEnabled(true),
|
mAutoReloadEnabled(true),
|
||||||
mStartTime(std::chrono::steady_clock::now()),
|
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)
|
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);
|
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)
|
bool RuntimeHost::TrySetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName)
|
||||||
{
|
{
|
||||||
return mHealthTelemetry.TryReportSignalStatus(hasSignal, width, height, modeName);
|
const HealthTelemetry::SignalStatusSnapshot previousStatus = mHealthTelemetry.GetSignalStatusSnapshot();
|
||||||
}
|
if (!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())
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SetSignalStatusLocked(hasSignal, width, height, modeName);
|
if (previousStatus.hasSignal != hasSignal ||
|
||||||
return true;
|
previousStatus.width != width ||
|
||||||
|
previousStatus.height != height ||
|
||||||
|
previousStatus.modeName != modeName)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
MarkRenderStateDirtyLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeHost::SetSignalStatusLocked(bool hasSignal, unsigned width, unsigned height, const std::string& modeName)
|
return true;
|
||||||
{
|
|
||||||
const bool changed = mHasSignal != hasSignal ||
|
|
||||||
mSignalWidth != width ||
|
|
||||||
mSignalHeight != height ||
|
|
||||||
mSignalModeName != modeName;
|
|
||||||
mHasSignal = hasSignal;
|
|
||||||
mSignalWidth = width;
|
|
||||||
mSignalHeight = height;
|
|
||||||
mSignalModeName = modeName;
|
|
||||||
if (changed)
|
|
||||||
MarkRenderStateDirtyLocked();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeHost::MarkRenderStateDirtyLocked()
|
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,
|
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)
|
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
mHealthTelemetry.ReportVideoIOStatus(
|
||||||
mDeckLinkOutputStatus.backendName = backendName;
|
backendName,
|
||||||
mDeckLinkOutputStatus.modelName = modelName;
|
modelName,
|
||||||
mDeckLinkOutputStatus.supportsInternalKeying = supportsInternalKeying;
|
supportsInternalKeying,
|
||||||
mDeckLinkOutputStatus.supportsExternalKeying = supportsExternalKeying;
|
supportsExternalKeying,
|
||||||
mDeckLinkOutputStatus.keyerInterfaceAvailable = keyerInterfaceAvailable;
|
keyerInterfaceAvailable,
|
||||||
mDeckLinkOutputStatus.externalKeyingRequested = externalKeyingRequested;
|
externalKeyingRequested,
|
||||||
mDeckLinkOutputStatus.externalKeyingActive = externalKeyingActive;
|
externalKeyingActive,
|
||||||
mDeckLinkOutputStatus.statusMessage = statusMessage;
|
statusMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeHost::SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
|
void RuntimeHost::SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
|
||||||
@@ -1433,32 +1414,6 @@ bool RuntimeHost::TrySetPerformanceStats(double frameBudgetMilliseconds, double
|
|||||||
return mHealthTelemetry.TryRecordPerformanceStats(frameBudgetMilliseconds, renderMilliseconds);
|
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,
|
void RuntimeHost::SetFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
|
||||||
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
|
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
|
||||||
{
|
{
|
||||||
@@ -1473,37 +1428,6 @@ bool RuntimeHost::TrySetFramePacingStats(double completionIntervalMilliseconds,
|
|||||||
maxCompletionIntervalMilliseconds, lateFrameCount, droppedFrameCount, flushedFrameCount);
|
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()
|
void RuntimeHost::AdvanceFrame()
|
||||||
{
|
{
|
||||||
++mFrameCounter;
|
++mFrameCounter;
|
||||||
@@ -1632,6 +1556,8 @@ void RuntimeHost::RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState
|
|||||||
|
|
||||||
void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
||||||
{
|
{
|
||||||
|
const HealthTelemetry::SignalStatusSnapshot signalStatus = mHealthTelemetry.GetSignalStatusSnapshot();
|
||||||
|
|
||||||
for (const LayerPersistentState& layer : mPersistentState.layers)
|
for (const LayerPersistentState& layer : mPersistentState.layers)
|
||||||
{
|
{
|
||||||
auto shaderIt = mPackagesById.find(layer.shaderId);
|
auto shaderIt = mPackagesById.find(layer.shaderId);
|
||||||
@@ -1644,8 +1570,8 @@ void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned ou
|
|||||||
state.shaderName = shaderIt->second.displayName;
|
state.shaderName = shaderIt->second.displayName;
|
||||||
state.mixAmount = 1.0;
|
state.mixAmount = 1.0;
|
||||||
state.bypass = layer.bypass ? 1.0 : 0.0;
|
state.bypass = layer.bypass ? 1.0 : 0.0;
|
||||||
state.inputWidth = mSignalWidth;
|
state.inputWidth = signalStatus.width;
|
||||||
state.inputHeight = mSignalHeight;
|
state.inputHeight = signalStatus.height;
|
||||||
state.outputWidth = outputWidth;
|
state.outputWidth = outputWidth;
|
||||||
state.outputHeight = outputHeight;
|
state.outputHeight = outputHeight;
|
||||||
state.parameterDefinitions = shaderIt->second.parameters;
|
state.parameterDefinitions = shaderIt->second.parameters;
|
||||||
@@ -2095,6 +2021,7 @@ bool RuntimeHost::ResolvePaths(std::string& error)
|
|||||||
|
|
||||||
JsonValue RuntimeHost::BuildStateValue() const
|
JsonValue RuntimeHost::BuildStateValue() const
|
||||||
{
|
{
|
||||||
|
const HealthTelemetry::Snapshot telemetrySnapshot = mHealthTelemetry.GetSnapshot();
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
JsonValue root = JsonValue::MakeObject();
|
JsonValue root = JsonValue::MakeObject();
|
||||||
@@ -2121,44 +2048,47 @@ JsonValue RuntimeHost::BuildStateValue() const
|
|||||||
root.set("runtime", runtime);
|
root.set("runtime", runtime);
|
||||||
|
|
||||||
JsonValue video = JsonValue::MakeObject();
|
JsonValue video = JsonValue::MakeObject();
|
||||||
video.set("hasSignal", JsonValue(mHasSignal));
|
video.set("hasSignal", JsonValue(telemetrySnapshot.signal.hasSignal));
|
||||||
video.set("width", JsonValue(static_cast<double>(mSignalWidth)));
|
video.set("width", JsonValue(static_cast<double>(telemetrySnapshot.signal.width)));
|
||||||
video.set("height", JsonValue(static_cast<double>(mSignalHeight)));
|
video.set("height", JsonValue(static_cast<double>(telemetrySnapshot.signal.height)));
|
||||||
video.set("modeName", JsonValue(mSignalModeName));
|
video.set("modeName", JsonValue(telemetrySnapshot.signal.modeName));
|
||||||
root.set("video", video);
|
root.set("video", video);
|
||||||
|
|
||||||
JsonValue deckLink = JsonValue::MakeObject();
|
JsonValue deckLink = JsonValue::MakeObject();
|
||||||
deckLink.set("modelName", JsonValue(mDeckLinkOutputStatus.modelName));
|
deckLink.set("modelName", JsonValue(telemetrySnapshot.videoIO.modelName));
|
||||||
deckLink.set("supportsInternalKeying", JsonValue(mDeckLinkOutputStatus.supportsInternalKeying));
|
deckLink.set("supportsInternalKeying", JsonValue(telemetrySnapshot.videoIO.supportsInternalKeying));
|
||||||
deckLink.set("supportsExternalKeying", JsonValue(mDeckLinkOutputStatus.supportsExternalKeying));
|
deckLink.set("supportsExternalKeying", JsonValue(telemetrySnapshot.videoIO.supportsExternalKeying));
|
||||||
deckLink.set("keyerInterfaceAvailable", JsonValue(mDeckLinkOutputStatus.keyerInterfaceAvailable));
|
deckLink.set("keyerInterfaceAvailable", JsonValue(telemetrySnapshot.videoIO.keyerInterfaceAvailable));
|
||||||
deckLink.set("externalKeyingRequested", JsonValue(mDeckLinkOutputStatus.externalKeyingRequested));
|
deckLink.set("externalKeyingRequested", JsonValue(telemetrySnapshot.videoIO.externalKeyingRequested));
|
||||||
deckLink.set("externalKeyingActive", JsonValue(mDeckLinkOutputStatus.externalKeyingActive));
|
deckLink.set("externalKeyingActive", JsonValue(telemetrySnapshot.videoIO.externalKeyingActive));
|
||||||
deckLink.set("statusMessage", JsonValue(mDeckLinkOutputStatus.statusMessage));
|
deckLink.set("statusMessage", JsonValue(telemetrySnapshot.videoIO.statusMessage));
|
||||||
root.set("decklink", deckLink);
|
root.set("decklink", deckLink);
|
||||||
|
|
||||||
JsonValue videoIO = JsonValue::MakeObject();
|
JsonValue videoIO = JsonValue::MakeObject();
|
||||||
videoIO.set("backend", JsonValue(mDeckLinkOutputStatus.backendName));
|
videoIO.set("backend", JsonValue(telemetrySnapshot.videoIO.backendName));
|
||||||
videoIO.set("modelName", JsonValue(mDeckLinkOutputStatus.modelName));
|
videoIO.set("modelName", JsonValue(telemetrySnapshot.videoIO.modelName));
|
||||||
videoIO.set("supportsInternalKeying", JsonValue(mDeckLinkOutputStatus.supportsInternalKeying));
|
videoIO.set("supportsInternalKeying", JsonValue(telemetrySnapshot.videoIO.supportsInternalKeying));
|
||||||
videoIO.set("supportsExternalKeying", JsonValue(mDeckLinkOutputStatus.supportsExternalKeying));
|
videoIO.set("supportsExternalKeying", JsonValue(telemetrySnapshot.videoIO.supportsExternalKeying));
|
||||||
videoIO.set("keyerInterfaceAvailable", JsonValue(mDeckLinkOutputStatus.keyerInterfaceAvailable));
|
videoIO.set("keyerInterfaceAvailable", JsonValue(telemetrySnapshot.videoIO.keyerInterfaceAvailable));
|
||||||
videoIO.set("externalKeyingRequested", JsonValue(mDeckLinkOutputStatus.externalKeyingRequested));
|
videoIO.set("externalKeyingRequested", JsonValue(telemetrySnapshot.videoIO.externalKeyingRequested));
|
||||||
videoIO.set("externalKeyingActive", JsonValue(mDeckLinkOutputStatus.externalKeyingActive));
|
videoIO.set("externalKeyingActive", JsonValue(telemetrySnapshot.videoIO.externalKeyingActive));
|
||||||
videoIO.set("statusMessage", JsonValue(mDeckLinkOutputStatus.statusMessage));
|
videoIO.set("statusMessage", JsonValue(telemetrySnapshot.videoIO.statusMessage));
|
||||||
root.set("videoIO", videoIO);
|
root.set("videoIO", videoIO);
|
||||||
|
|
||||||
JsonValue performance = JsonValue::MakeObject();
|
JsonValue performance = JsonValue::MakeObject();
|
||||||
performance.set("frameBudgetMs", JsonValue(mFrameBudgetMilliseconds));
|
performance.set("frameBudgetMs", JsonValue(telemetrySnapshot.performance.frameBudgetMilliseconds));
|
||||||
performance.set("renderMs", JsonValue(mRenderMilliseconds));
|
performance.set("renderMs", JsonValue(telemetrySnapshot.performance.renderMilliseconds));
|
||||||
performance.set("smoothedRenderMs", JsonValue(mSmoothedRenderMilliseconds));
|
performance.set("smoothedRenderMs", JsonValue(telemetrySnapshot.performance.smoothedRenderMilliseconds));
|
||||||
performance.set("budgetUsedPercent", JsonValue(mFrameBudgetMilliseconds > 0.0 ? (mSmoothedRenderMilliseconds / mFrameBudgetMilliseconds) * 100.0 : 0.0));
|
performance.set("budgetUsedPercent", JsonValue(
|
||||||
performance.set("completionIntervalMs", JsonValue(mCompletionIntervalMilliseconds));
|
telemetrySnapshot.performance.frameBudgetMilliseconds > 0.0
|
||||||
performance.set("smoothedCompletionIntervalMs", JsonValue(mSmoothedCompletionIntervalMilliseconds));
|
? (telemetrySnapshot.performance.smoothedRenderMilliseconds / telemetrySnapshot.performance.frameBudgetMilliseconds) * 100.0
|
||||||
performance.set("maxCompletionIntervalMs", JsonValue(mMaxCompletionIntervalMilliseconds));
|
: 0.0));
|
||||||
performance.set("lateFrameCount", JsonValue(static_cast<double>(mLateFrameCount)));
|
performance.set("completionIntervalMs", JsonValue(telemetrySnapshot.performance.completionIntervalMilliseconds));
|
||||||
performance.set("droppedFrameCount", JsonValue(static_cast<double>(mDroppedFrameCount)));
|
performance.set("smoothedCompletionIntervalMs", JsonValue(telemetrySnapshot.performance.smoothedCompletionIntervalMilliseconds));
|
||||||
performance.set("flushedFrameCount", JsonValue(static_cast<double>(mFlushedFrameCount)));
|
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);
|
root.set("performance", performance);
|
||||||
|
|
||||||
JsonValue shaderLibrary = JsonValue::MakeArray();
|
JsonValue shaderLibrary = JsonValue::MakeArray();
|
||||||
|
|||||||
@@ -101,18 +101,6 @@ private:
|
|||||||
std::string outputFrameRate = "59.94";
|
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
|
struct LayerPersistentState
|
||||||
{
|
{
|
||||||
std::string id;
|
std::string id;
|
||||||
@@ -148,23 +136,10 @@ private:
|
|||||||
LayerPersistentState* FindLayerById(const std::string& layerId);
|
LayerPersistentState* FindLayerById(const std::string& layerId);
|
||||||
const LayerPersistentState* FindLayerById(const std::string& layerId) const;
|
const LayerPersistentState* FindLayerById(const std::string& layerId) const;
|
||||||
std::string GenerateLayerId();
|
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 MarkRenderStateDirtyLocked();
|
||||||
void MarkParameterStateDirtyLocked();
|
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:
|
private:
|
||||||
friend class HealthTelemetry;
|
|
||||||
HealthTelemetry mHealthTelemetry;
|
HealthTelemetry mHealthTelemetry;
|
||||||
mutable std::mutex mMutex;
|
mutable std::mutex mMutex;
|
||||||
AppConfig mConfig;
|
AppConfig mConfig;
|
||||||
@@ -186,21 +161,7 @@ private:
|
|||||||
bool mReloadRequested;
|
bool mReloadRequested;
|
||||||
bool mCompileSucceeded;
|
bool mCompileSucceeded;
|
||||||
std::string mCompileMessage;
|
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;
|
double mStartupRandom;
|
||||||
uint64_t mLateFrameCount;
|
|
||||||
uint64_t mDroppedFrameCount;
|
|
||||||
uint64_t mFlushedFrameCount;
|
|
||||||
DeckLinkOutputStatus mDeckLinkOutputStatus;
|
|
||||||
unsigned short mServerPort;
|
unsigned short mServerPort;
|
||||||
bool mAutoReloadEnabled;
|
bool mAutoReloadEnabled;
|
||||||
std::chrono::steady_clock::time_point mStartTime;
|
std::chrono::steady_clock::time_point mStartTime;
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ These are still compatibility seams, not a completed subsystem extraction. Most
|
|||||||
- render-state and shader-build reads in `OpenGLComposite.cpp`, `OpenGLShaderPrograms.cpp`, and `ShaderBuildQueue.cpp` now route through `RuntimeSnapshotProvider`
|
- render-state and shader-build reads in `OpenGLComposite.cpp`, `OpenGLShaderPrograms.cpp`, and `ShaderBuildQueue.cpp` now route through `RuntimeSnapshotProvider`
|
||||||
- service ingress and polling coordination now route through `ControlServices`
|
- service ingress and polling coordination now route through `ControlServices`
|
||||||
- timing and status writes now route through `HealthTelemetry`
|
- timing and status writes now route through `HealthTelemetry`
|
||||||
|
- `HealthTelemetry` now owns the live signal, video-I/O, and performance snapshots directly instead of `RuntimeHost` keeping those backing fields
|
||||||
- render-side frame advancement and render-performance reporting now flow through `RuntimeSnapshotProvider` and `HealthTelemetry` instead of directly through `RuntimeHost`
|
- render-side frame advancement and render-performance reporting now flow through `RuntimeSnapshotProvider` and `HealthTelemetry` instead of directly through `RuntimeHost`
|
||||||
- live OSC overlay state and smoothing/commit decisions now live under `RenderEngine` instead of `OpenGLComposite`
|
- live OSC overlay state and smoothing/commit decisions now live under `RenderEngine` instead of `OpenGLComposite`
|
||||||
- `OpenGLComposite` now owns a `RenderEngine` seam for renderer, pipeline, render-pass, and shader-program responsibilities
|
- `OpenGLComposite` now owns a `RenderEngine` seam for renderer, pipeline, render-pass, and shader-program responsibilities
|
||||||
|
|||||||
Reference in New Issue
Block a user