[client/idd] harden demand-driven frame delivery

This commit is contained in:
Geoffrey McRae
2026-08-04 13:22:19 +10:00
parent 97e7f439c2
commit 5ebc208d43
6 changed files with 129 additions and 28 deletions

View File

@@ -217,7 +217,16 @@ void frameScheduler_update(void)
now - l_frameScheduler.lastCadence > FRAME_SCHEDULER_RENEW_NS * 2) now - l_frameScheduler.lastCadence > FRAME_SCHEDULER_RENEW_NS * 2)
{ {
if (sendSchedule(LG_TRANSPORT_FRAME_SCHEDULE_RELEASE, 0)) if (sendSchedule(LG_TRANSPORT_FRAME_SCHEDULE_RELEASE, 0))
{
l_frameScheduler.active = false; l_frameScheduler.active = false;
l_frameScheduler.period = 0;
LG_LOCK(l_frameScheduler.lock);
l_frameScheduler.phaseError = 0;
l_frameScheduler.feedbackFrameSerial = 0;
l_frameScheduler.feedbackSamples = 0;
l_frameScheduler.feedbackDirty = false;
LG_UNLOCK(l_frameScheduler.lock);
}
} }
return; return;
} }
@@ -235,6 +244,12 @@ void frameScheduler_update(void)
{ {
l_frameScheduler.period = period; l_frameScheduler.period = period;
++l_frameScheduler.generation; ++l_frameScheduler.generation;
LG_LOCK(l_frameScheduler.lock);
l_frameScheduler.phaseError = 0;
l_frameScheduler.feedbackFrameSerial = 0;
l_frameScheduler.feedbackSamples = 0;
l_frameScheduler.feedbackDirty = false;
LG_UNLOCK(l_frameScheduler.lock);
} }
else else
l_frameScheduler.period = l_frameScheduler.period =
@@ -282,12 +297,11 @@ void frameScheduler_observeRender(uint64_t timestamp)
} }
void frameScheduler_feedback(uint64_t frameSerial, uint32_t generation, void frameScheduler_feedback(uint64_t frameSerial, uint32_t generation,
uint64_t queueStart, uint64_t prepareStart) uint64_t measuredPhase)
{ {
if (!generation || !queueStart || prepareStart < queueStart) if (!generation)
return; return;
const uint64_t slack = prepareStart - queueStart;
LG_LOCK(l_frameScheduler.lock); LG_LOCK(l_frameScheduler.lock);
if (!l_frameScheduler.supported || !l_frameScheduler.active || if (!l_frameScheduler.supported || !l_frameScheduler.active ||
generation != l_frameScheduler.generation) generation != l_frameScheduler.generation)
@@ -296,14 +310,21 @@ void frameScheduler_feedback(uint64_t frameSerial, uint32_t generation,
return; return;
} }
int64_t error = slack > FRAME_SCHEDULER_TARGET_SLACK_NS ? const int64_t period = (int64_t)l_frameScheduler.period;
(int64_t)(slack - FRAME_SCHEDULER_TARGET_SLACK_NS) : if (!period)
-(int64_t)(FRAME_SCHEDULER_TARGET_SLACK_NS - slack); {
const int64_t limit = (int64_t)(l_frameScheduler.period / 2); LG_UNLOCK(l_frameScheduler.lock);
if (error > limit) return;
error = limit; }
else if (error < -limit)
error = -limit; int64_t error = measuredPhase > FRAME_SCHEDULER_TARGET_SLACK_NS ?
(int64_t)(measuredPhase - FRAME_SCHEDULER_TARGET_SLACK_NS) :
-(int64_t)(FRAME_SCHEDULER_TARGET_SLACK_NS - measuredPhase);
error %= period;
if (error > period / 2)
error -= period;
else if (error < -period / 2)
error += period;
if (!l_frameScheduler.feedbackSamples) if (!l_frameScheduler.feedbackSamples)
l_frameScheduler.phaseError = error; l_frameScheduler.phaseError = error;

View File

@@ -32,6 +32,6 @@ void frameScheduler_stop(void);
void frameScheduler_update(void); void frameScheduler_update(void);
void frameScheduler_observeRender(uint64_t timestamp); void frameScheduler_observeRender(uint64_t timestamp);
void frameScheduler_feedback(uint64_t frameSerial, uint32_t generation, void frameScheduler_feedback(uint64_t frameSerial, uint32_t generation,
uint64_t queueStart, uint64_t prepareStart); uint64_t measuredPhase);
#endif #endif

View File

@@ -434,8 +434,18 @@ static void frameTimingFinishRender(const LG_RendererFrameTiming * timing,
} }
LG_UNLOCK(l_frameTiming.lock); LG_UNLOCK(l_frameTiming.lock);
frameScheduler_feedback(feedbackFrameSerial, feedbackGeneration, if (feedbackFrameSerial && feedbackGeneration)
feedbackQueueStart, prepareStart); {
uint64_t measuredPhase = timing->swapTime;
if (g_state.jitRender)
{
if (!feedbackQueueStart || prepareStart < feedbackQueueStart)
return;
measuredPhase = prepareStart - feedbackQueueStart;
}
frameScheduler_feedback(
feedbackFrameSerial, feedbackGeneration, measuredPhase);
}
} }
static void frameTimingPublishReady(void) static void frameTimingPublishReady(void)

View File

@@ -22,13 +22,12 @@
#include "CDebug.h" #include "CDebug.h"
#include <algorithm> static const uint64_t MIN_PERIOD_NS = 2000000ULL;
static const uint64_t MAX_PERIOD_NS = 1000000000ULL;
static const uint64_t MIN_PERIOD_NS = 2000000ULL; static const uint32_t MIN_LEASE_MS = 100;
static const uint64_t MAX_PERIOD_NS = 1000000000ULL; static const uint32_t MAX_LEASE_MS = 5000;
static const uint32_t MIN_LEASE_MS = 100; static const uint64_t MIN_SAFETY_NS = 250000ULL;
static const uint32_t MAX_LEASE_MS = 5000; static const uint64_t LOG_INTERVAL_NS = 5000000000ULL;
static const uint64_t MIN_SAFETY_NS = 250000ULL;
uint64_t CFrameScheduler::Nanotime() uint64_t CFrameScheduler::Nanotime()
{ {
@@ -112,6 +111,11 @@ void CFrameScheduler::ElectOwner(uint64_t now)
m_forceNext = m_scheduling; m_forceNext = m_scheduling;
m_lastPublishedFrameSerial = 0; m_lastPublishedFrameSerial = 0;
m_lastPhaseError = 0;
m_lastLog = now;
m_lastLogAcquired = m_acquiredFrames;
m_lastLogSkipped = m_skippedFrames;
m_lastLogPublished = m_publishedFrames;
if (m_scheduling) if (m_scheduling)
DEBUG_INFO("Frame timing owner %u generation %u at %.3f Hz", DEBUG_INFO("Frame timing owner %u generation %u at %.3f Hz",
@@ -140,6 +144,15 @@ void CFrameScheduler::Reset()
m_timingSamples = 0; m_timingSamples = 0;
m_lastPublishedFrameSerial = 0; m_lastPublishedFrameSerial = 0;
m_lastPhaseError = 0;
m_acquiredFrames = 0;
m_skippedFrames = 0;
m_publishedFrames = 0;
m_lastLog = 0;
m_lastLogAcquired = 0;
m_lastLogSkipped = 0;
m_lastLogPublished = 0;
ReleaseSRWLockExclusive(&m_lock); ReleaseSRWLockExclusive(&m_lock);
} }
@@ -260,9 +273,20 @@ void CFrameScheduler::ApplyFeedback(Client& client,
m_nextDeadline = m_nextDeadline > advance ? m_nextDeadline = m_nextDeadline > advance ?
m_nextDeadline - advance : 0; m_nextDeadline - advance : 0;
} }
m_lastPhaseError = schedule.phaseError;
client.lastFeedbackFrameSerial = schedule.feedbackFrameSerial; client.lastFeedbackFrameSerial = schedule.feedbackFrameSerial;
} }
void CFrameScheduler::AdvanceDeadline(uint64_t now)
{
if (m_nextDeadline > now)
return;
const uint64_t periods =
(now - m_nextDeadline) / m_schedule.period + 1;
m_nextDeadline += periods * m_schedule.period;
}
bool CFrameScheduler::GetSchedule(Schedule& schedule) const bool CFrameScheduler::GetSchedule(Schedule& schedule) const
{ {
AcquireSRWLockShared(&m_lock); AcquireSRWLockShared(&m_lock);
@@ -276,6 +300,7 @@ bool CFrameScheduler::GetSchedule(Schedule& schedule) const
void CFrameScheduler::ObserveFrame(uint64_t now) void CFrameScheduler::ObserveFrame(uint64_t now)
{ {
AcquireSRWLockExclusive(&m_lock); AcquireSRWLockExclusive(&m_lock);
++m_acquiredFrames;
if (m_lastArrival && now > m_lastArrival) if (m_lastArrival && now > m_lastArrival)
{ {
const uint64_t interval = now - m_lastArrival; const uint64_t interval = now - m_lastArrival;
@@ -311,8 +336,7 @@ bool CFrameScheduler::SelectFrame(uint64_t now, bool force,
} }
generation = m_schedule.generation; generation = m_schedule.generation;
while (m_nextDeadline <= now) AdvanceDeadline(now);
m_nextDeadline += m_schedule.period;
if (force) if (force)
m_forceNext = true; m_forceNext = true;
@@ -324,11 +348,13 @@ bool CFrameScheduler::SelectFrame(uint64_t now, bool force,
} }
const uint64_t safety = const uint64_t safety =
std::max(MIN_SAFETY_NS, max(MIN_SAFETY_NS,
std::max(m_guestJitter * 2, m_workEstimate / 8)); max(m_guestJitter * 2, m_workEstimate / 8));
const uint64_t nextArrival = m_lastArrival + m_guestPeriod; const uint64_t nextArrival = m_lastArrival + m_guestPeriod;
const bool process = nextArrival <= now || const bool process = nextArrival <= now ||
nextArrival + m_workEstimate + safety > m_nextDeadline; nextArrival + m_workEstimate + safety > m_nextDeadline;
if (!process)
++m_skippedFrames;
ReleaseSRWLockExclusive(&m_lock); ReleaseSRWLockExclusive(&m_lock);
return process; return process;
} }
@@ -341,13 +367,44 @@ void CFrameScheduler::FramePublished(uint32_t generation,
{ {
m_forceNext = false; m_forceNext = false;
m_lastPublishedFrameSerial = frameSerial; m_lastPublishedFrameSerial = frameSerial;
do ++m_publishedFrames;
m_nextDeadline += m_schedule.period; m_nextDeadline += m_schedule.period;
while (m_nextDeadline <= now); AdvanceDeadline(now);
} }
ReleaseSRWLockExclusive(&m_lock); ReleaseSRWLockExclusive(&m_lock);
} }
void CFrameScheduler::LogStatistics(uint64_t now)
{
AcquireSRWLockExclusive(&m_lock);
if (!m_scheduling || now - m_lastLog < LOG_INTERVAL_NS)
{
ReleaseSRWLockExclusive(&m_lock);
return;
}
const uint64_t acquired = m_acquiredFrames - m_lastLogAcquired;
const uint64_t skipped = m_skippedFrames - m_lastLogSkipped;
const uint64_t published = m_publishedFrames - m_lastLogPublished;
DEBUG_TRACE("Frame schedule owner %u: %.3f Hz client, %.3f Hz guest, "
"%.3f ms work, %.3f ms phase; %llu acquired, %llu skipped, "
"%llu published",
m_schedule.clientID,
1000000000.0 / m_schedule.period,
m_guestPeriod ? 1000000000.0 / m_guestPeriod : 0.0,
m_workEstimate / 1000000.0,
m_lastPhaseError / 1000000.0,
static_cast<unsigned long long>(acquired),
static_cast<unsigned long long>(skipped),
static_cast<unsigned long long>(published));
m_lastLog = now;
m_lastLogAcquired = m_acquiredFrames;
m_lastLogSkipped = m_skippedFrames;
m_lastLogPublished = m_publishedFrames;
ReleaseSRWLockExclusive(&m_lock);
}
void CFrameScheduler::RecordFrameTiming(uint64_t duration) void CFrameScheduler::RecordFrameTiming(uint64_t duration)
{ {
if (!duration) if (!duration)

View File

@@ -69,9 +69,19 @@ private:
uint32_t m_lastPublishedFrameSerial = 0; uint32_t m_lastPublishedFrameSerial = 0;
int64_t m_lastPhaseError = 0;
uint64_t m_acquiredFrames = 0;
uint64_t m_skippedFrames = 0;
uint64_t m_publishedFrames = 0;
uint64_t m_lastLog = 0;
uint64_t m_lastLogAcquired = 0;
uint64_t m_lastLogSkipped = 0;
uint64_t m_lastLogPublished = 0;
Client * FindClient(uint32_t clientID); Client * FindClient(uint32_t clientID);
void ElectOwner(uint64_t now); void ElectOwner(uint64_t now);
void ApplyFeedback(Client& client, const KVMFRFrameSchedule& schedule); void ApplyFeedback(Client& client, const KVMFRFrameSchedule& schedule);
void AdvanceDeadline(uint64_t now);
public: public:
static uint64_t Nanotime(); static uint64_t Nanotime();
@@ -86,4 +96,5 @@ public:
void FramePublished(uint32_t generation, uint32_t frameSerial, void FramePublished(uint32_t generation, uint32_t frameSerial,
uint64_t now); uint64_t now);
void RecordFrameTiming(uint64_t duration); void RecordFrameTiming(uint64_t duration);
void LogStatistics(uint64_t now);
}; };

View File

@@ -1304,6 +1304,8 @@ void CIndirectDeviceContext::LGMPTimer()
lgmpHostAckData(m_pointerQueue); lgmpHostAckData(m_pointerQueue);
} }
m_frameScheduler.LogStatistics(now);
AcquireSRWLockExclusive(&m_framePublishLock); AcquireSRWLockExclusive(&m_framePublishLock);
if (lgmpHostQueueNewSubs(m_frameQueue)) if (lgmpHostQueueNewSubs(m_frameQueue))
m_frameResendPending = true; m_frameResendPending = true;