Improvement
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m52s
CI / Windows Release Package (push) Successful in 3m0s

This commit is contained in:
Aiden
2026-05-12 00:00:23 +10:00
parent a434a88108
commit 9e3412712c
22 changed files with 1409 additions and 34 deletions

View File

@@ -34,6 +34,7 @@ void VideoBackend::ReleaseResources()
mReadyOutputQueue.Clear();
if (mVideoIODevice)
mVideoIODevice->ReleaseResources();
mSystemOutputFramePool.Clear();
if (!VideoBackendLifecycle::CanTransition(mLifecycle.State(), VideoBackendLifecycleState::Stopped))
ApplyLifecycleFailure("Video backend resources released before lifecycle completed.");
ApplyLifecycleTransition(VideoBackendLifecycleState::Stopped, "Video backend resources released.");
@@ -95,6 +96,14 @@ bool VideoBackend::ConfigureOutput(const VideoFormat& outputVideoMode, bool exte
ApplyLifecycleFailure(error.empty() ? "Video backend output configuration failed." : error);
return false;
}
SystemOutputFramePoolConfig poolConfig;
poolConfig.width = mVideoIODevice->OutputFrameWidth();
poolConfig.height = mVideoIODevice->OutputFrameHeight();
poolConfig.pixelFormat = mVideoIODevice->OutputPixelFormat();
poolConfig.rowBytes = mVideoIODevice->OutputFrameRowBytes();
poolConfig.capacity = mPlayoutPolicy.outputFramePoolSize;
mSystemOutputFramePool.Configure(poolConfig);
RecordSystemMemoryPlayoutStats();
return ApplyLifecycleTransition(VideoBackendLifecycleState::Configured, "Video backend configured.");
}
@@ -460,6 +469,8 @@ std::chrono::milliseconds VideoBackend::OutputProducerWakeInterval() const
void VideoBackend::ProcessOutputFrameCompletion(const VideoIOCompletion& completion)
{
if (completion.outputFrameBuffer != nullptr)
mSystemOutputFramePool.ReleaseSlotByBuffer(completion.outputFrameBuffer);
RecordFramePacing(completion.result);
PublishOutputFrameCompleted(completion);
const RenderOutputQueueMetrics initialQueueMetrics = mReadyOutputQueue.GetMetrics();
@@ -483,6 +494,7 @@ void VideoBackend::ProcessOutputFrameCompletion(const VideoIOCompletion& complet
}
NotifyOutputProducer();
RecordBackendPlayoutHealth(completion.result, recoveryDecision);
RecordSystemMemoryPlayoutStats();
}
void VideoBackend::RecordBackendPlayoutHealth(VideoIOCompletionResult result, const VideoPlayoutRecoveryDecision& recoveryDecision)
@@ -582,10 +594,12 @@ OutputProductionPressure VideoBackend::BuildOutputProductionPressure(const Rende
bool VideoBackend::RenderReadyOutputFrame(const VideoIOState& state, const VideoIOCompletion& completion)
{
const auto renderStart = std::chrono::steady_clock::now();
OutputFrameSlot outputSlot;
VideoIOOutputFrame outputFrame;
const auto acquireStart = std::chrono::steady_clock::now();
if (!BeginOutputFrame(outputFrame))
if (!mSystemOutputFramePool.AcquireFreeSlot(outputSlot))
return false;
outputFrame = outputSlot.frame;
const auto acquireEnd = std::chrono::steady_clock::now();
bool rendered = true;
@@ -595,7 +609,7 @@ bool VideoBackend::RenderReadyOutputFrame(const VideoIOState& state, const Video
const auto renderRequestEnd = std::chrono::steady_clock::now();
const auto endAccessStart = std::chrono::steady_clock::now();
EndOutputFrame(outputFrame);
const bool publishedReady = mSystemOutputFramePool.PublishReadySlot(outputSlot);
const auto endAccessEnd = std::chrono::steady_clock::now();
const double acquireMilliseconds = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(acquireEnd - acquireStart).count();
const double renderRequestMilliseconds = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(renderRequestEnd - renderRequestStart).count();
@@ -603,15 +617,17 @@ bool VideoBackend::RenderReadyOutputFrame(const VideoIOState& state, const Video
if (!rendered)
{
mSystemOutputFramePool.ReleaseSlot(outputSlot);
ApplyLifecycleTransition(VideoBackendLifecycleState::Degraded, "Output frame render request failed; skipping schedule for this frame.");
const double renderMilliseconds = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(
std::chrono::steady_clock::now() - renderStart).count();
RecordOutputRenderDuration(renderMilliseconds, acquireMilliseconds, renderRequestMilliseconds, endAccessMilliseconds);
if (outputFrame.nativeFrame != nullptr)
{
static_cast<IUnknown*>(outputFrame.nativeFrame)->Release();
outputFrame.nativeFrame = nullptr;
}
return false;
}
if (!publishedReady)
{
mSystemOutputFramePool.ReleaseSlot(outputSlot);
return false;
}
@@ -622,12 +638,13 @@ bool VideoBackend::RenderReadyOutputFrame(const VideoIOState& state, const Video
RenderOutputFrame readyFrame;
readyFrame.frame = outputFrame;
readyFrame.frameIndex = ++mNextReadyOutputFrameIndex;
readyFrame.releaseFrame = [this](VideoIOOutputFrame& frame) {
mSystemOutputFramePool.ReleaseSlotByBuffer(frame.bytes);
};
const bool pushed = mReadyOutputQueue.Push(readyFrame);
if (!pushed && outputFrame.nativeFrame != nullptr)
{
static_cast<IUnknown*>(outputFrame.nativeFrame)->Release();
outputFrame.nativeFrame = nullptr;
}
if (!pushed)
mSystemOutputFramePool.ReleaseSlot(outputSlot);
RecordSystemMemoryPlayoutStats();
return pushed;
}
@@ -638,10 +655,21 @@ bool VideoBackend::ScheduleReadyOutputFrame()
return false;
RecordReadyQueueDepthSample(mReadyOutputQueue.GetMetrics());
if (!ScheduleOutputFrame(readyFrame.frame))
if (!mSystemOutputFramePool.MarkScheduledByBuffer(readyFrame.frame.bytes))
{
if (readyFrame.releaseFrame)
readyFrame.releaseFrame(readyFrame.frame);
return false;
}
if (!ScheduleOutputFrame(readyFrame.frame))
{
mSystemOutputFramePool.ReleaseSlotByBuffer(readyFrame.frame.bytes);
return false;
}
PublishOutputFrameScheduled(readyFrame.frame);
RecordSystemMemoryPlayoutStats();
return true;
}
@@ -721,6 +749,21 @@ void VideoBackend::RecordReadyQueueDepthSample(const RenderOutputQueueMetrics& m
++mReadyQueueZeroDepthCount;
}
void VideoBackend::RecordSystemMemoryPlayoutStats()
{
const SystemOutputFramePoolMetrics poolMetrics = mSystemOutputFramePool.GetMetrics();
const RenderOutputQueueMetrics queueMetrics = mReadyOutputQueue.GetMetrics();
mHealthTelemetry.TryRecordSystemMemoryPlayoutStats(
poolMetrics.freeCount,
poolMetrics.readyCount,
poolMetrics.scheduledCount,
poolMetrics.readyUnderrunCount,
0,
queueMetrics.droppedCount,
0.0,
0.0);
}
void VideoBackend::RecordOutputRenderDuration(double renderMilliseconds, double acquireMilliseconds, double renderRequestMilliseconds, double endAccessMilliseconds)
{
std::lock_guard<std::mutex> lock(mOutputMetricsMutex);