|
|
|
|
@@ -359,6 +359,12 @@ void VideoBackend::StartOutputProducerWorker()
|
|
|
|
|
if (mOutputProducerWorkerRunning)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const double frameBudgetMilliseconds = State().frameBudgetMilliseconds;
|
|
|
|
|
const auto frameDuration = frameBudgetMilliseconds > 0.0
|
|
|
|
|
? std::chrono::duration_cast<RenderCadenceController::Duration>(
|
|
|
|
|
std::chrono::duration<double, std::milli>(frameBudgetMilliseconds))
|
|
|
|
|
: std::chrono::milliseconds(16);
|
|
|
|
|
mRenderCadenceController.Configure(frameDuration, std::chrono::steady_clock::now());
|
|
|
|
|
mLastOutputProductionCompletion = VideoIOCompletion();
|
|
|
|
|
mLastOutputProductionTime = std::chrono::steady_clock::time_point();
|
|
|
|
|
mOutputProducerWorkerStopping = false;
|
|
|
|
|
@@ -433,11 +439,16 @@ void VideoBackend::OutputProducerWorkerMain()
|
|
|
|
|
|
|
|
|
|
const RenderOutputQueueMetrics metrics = mReadyOutputQueue.GetMetrics();
|
|
|
|
|
RecordReadyQueueDepthSample(metrics);
|
|
|
|
|
const OutputProductionDecision decision = mOutputProductionController.Decide(BuildOutputProductionPressure(metrics));
|
|
|
|
|
if (decision.action != OutputProductionAction::Produce || decision.requestedFrames == 0)
|
|
|
|
|
|
|
|
|
|
const auto now = std::chrono::steady_clock::now();
|
|
|
|
|
RenderCadenceDecision cadenceDecision = mRenderCadenceController.Tick(now);
|
|
|
|
|
if (cadenceDecision.action == RenderCadenceAction::Wait)
|
|
|
|
|
{
|
|
|
|
|
const auto waitDuration = (std::min)(
|
|
|
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(cadenceDecision.waitDuration),
|
|
|
|
|
OutputProducerWakeInterval());
|
|
|
|
|
std::unique_lock<std::mutex> lock(mOutputProducerMutex);
|
|
|
|
|
mOutputProducerCondition.wait_for(lock, OutputProducerWakeInterval());
|
|
|
|
|
mOutputProducerCondition.wait_for(lock, waitDuration);
|
|
|
|
|
if (mOutputProducerWorkerStopping)
|
|
|
|
|
{
|
|
|
|
|
mOutputProducerWorkerRunning = false;
|
|
|
|
|
@@ -454,16 +465,7 @@ void VideoBackend::OutputProducerWorkerMain()
|
|
|
|
|
completion = mLastOutputProductionCompletion;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool belowTargetDepth = metrics.depth < decision.targetReadyFrames;
|
|
|
|
|
const auto now = std::chrono::steady_clock::now();
|
|
|
|
|
if (!belowTargetDepth &&
|
|
|
|
|
mLastOutputProductionTime != std::chrono::steady_clock::time_point() &&
|
|
|
|
|
now - mLastOutputProductionTime < OutputProducerWakeInterval())
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::size_t producedFrames = ProduceReadyOutputFrames(completion, decision.requestedFrames);
|
|
|
|
|
const std::size_t producedFrames = ProduceReadyOutputFrames(completion, 1);
|
|
|
|
|
if (producedFrames > 0)
|
|
|
|
|
{
|
|
|
|
|
mLastOutputProductionTime = std::chrono::steady_clock::now();
|
|
|
|
|
@@ -600,10 +602,6 @@ std::size_t VideoBackend::ProduceReadyOutputFrames(const VideoIOCompletion& comp
|
|
|
|
|
std::size_t producedFrames = 0;
|
|
|
|
|
while (producedFrames < maxFrames)
|
|
|
|
|
{
|
|
|
|
|
const OutputProductionDecision decision = mOutputProductionController.Decide(BuildOutputProductionPressure(metrics));
|
|
|
|
|
if (decision.action != OutputProductionAction::Produce)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (!RenderReadyOutputFrame(mVideoIODevice->State(), completion))
|
|
|
|
|
break;
|
|
|
|
|
++producedFrames;
|
|
|
|
|
@@ -634,7 +632,10 @@ bool VideoBackend::RenderReadyOutputFrame(const VideoIOState& state, const Video
|
|
|
|
|
VideoIOOutputFrame outputFrame;
|
|
|
|
|
const auto acquireStart = std::chrono::steady_clock::now();
|
|
|
|
|
if (!mSystemOutputFramePool.AcquireFreeSlot(outputSlot))
|
|
|
|
|
return false;
|
|
|
|
|
{
|
|
|
|
|
if (!mReadyOutputQueue.DropOldestFrame() || !mSystemOutputFramePool.AcquireFreeSlot(outputSlot))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
outputFrame = outputSlot.frame;
|
|
|
|
|
const auto acquireEnd = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
|
|
|
|