[client/idd] align frame delivery to presentation

This commit is contained in:
Geoffrey McRae
2026-08-04 13:15:37 +10:00
parent 32ced2f24a
commit 97e7f439c2
7 changed files with 211 additions and 71 deletions

View File

@@ -111,6 +111,8 @@ void CFrameScheduler::ElectOwner(uint64_t now)
m_nextDeadline = m_scheduling ? now + m_schedule.period : 0;
m_forceNext = m_scheduling;
m_lastPublishedFrameSerial = 0;
if (m_scheduling)
DEBUG_INFO("Frame timing owner %u generation %u at %.3f Hz",
m_schedule.clientID, m_schedule.generation,
@@ -136,6 +138,8 @@ void CFrameScheduler::Reset()
m_nextDeadline = 0;
m_arrivalSamples = 0;
m_timingSamples = 0;
m_lastPublishedFrameSerial = 0;
ReleaseSRWLockExclusive(&m_lock);
}
@@ -204,22 +208,61 @@ bool CFrameScheduler::UpdateSchedule(const KVMFRFrameSchedule& schedule,
schedule.period < MIN_PERIOD_NS ||
schedule.period > MAX_PERIOD_NS ||
schedule.targetSlack >= schedule.period ||
schedule.phaseError > static_cast<int64_t>(schedule.period) ||
schedule.phaseError < -static_cast<int64_t>(schedule.period) ||
schedule.lease < MIN_LEASE_MS || schedule.lease > MAX_LEASE_MS)
{
ReleaseSRWLockExclusive(&m_lock);
return false;
}
if (client->generation != schedule.generation)
client->lastFeedbackFrameSerial = 0;
client->generation = schedule.generation;
client->period = schedule.period;
client->targetSlack = schedule.targetSlack;
client->expiry = now + static_cast<uint64_t>(schedule.lease) * 1000000;
client->active = true;
if (schedule.flags & KVMFR_FRAME_SCHEDULE_IMMEDIATE)
m_forceNext = true;
ElectOwner(now);
ApplyFeedback(*client, schedule);
ReleaseSRWLockExclusive(&m_lock);
return true;
}
void CFrameScheduler::ApplyFeedback(Client& client,
const KVMFRFrameSchedule& schedule)
{
if (!m_scheduling || client.clientID != m_schedule.clientID ||
schedule.generation != m_schedule.generation ||
!schedule.feedbackFrameSerial ||
(client.lastFeedbackFrameSerial &&
static_cast<int32_t>(schedule.feedbackFrameSerial -
client.lastFeedbackFrameSerial) <= 0) ||
!m_lastPublishedFrameSerial ||
static_cast<int32_t>(schedule.feedbackFrameSerial -
m_lastPublishedFrameSerial) > 0)
return;
int64_t correction = schedule.phaseError / 4;
const int64_t limit = static_cast<int64_t>(m_schedule.period / 4);
if (correction > limit)
correction = limit;
else if (correction < -limit)
correction = -limit;
if (correction >= 0)
m_nextDeadline += static_cast<uint64_t>(correction);
else
{
const uint64_t advance = static_cast<uint64_t>(-correction);
m_nextDeadline = m_nextDeadline > advance ?
m_nextDeadline - advance : 0;
}
client.lastFeedbackFrameSerial = schedule.feedbackFrameSerial;
}
bool CFrameScheduler::GetSchedule(Schedule& schedule) const
{
AcquireSRWLockShared(&m_lock);
@@ -290,12 +333,14 @@ bool CFrameScheduler::SelectFrame(uint64_t now, bool force,
return process;
}
void CFrameScheduler::FramePublished(uint32_t generation, uint64_t now)
void CFrameScheduler::FramePublished(uint32_t generation,
uint32_t frameSerial, uint64_t now)
{
AcquireSRWLockExclusive(&m_lock);
if (m_scheduling && generation == m_schedule.generation)
{
m_forceNext = false;
m_lastPublishedFrameSerial = frameSerial;
do
m_nextDeadline += m_schedule.period;
while (m_nextDeadline <= now);

View File

@@ -48,6 +48,7 @@ private:
uint64_t period;
uint64_t targetSlack;
uint64_t expiry;
uint32_t lastFeedbackFrameSerial;
bool subscribed;
bool active;
};
@@ -66,8 +67,11 @@ private:
unsigned m_arrivalSamples = 0;
unsigned m_timingSamples = 0;
uint32_t m_lastPublishedFrameSerial = 0;
Client * FindClient(uint32_t clientID);
void ElectOwner(uint64_t now);
void ApplyFeedback(Client& client, const KVMFRFrameSchedule& schedule);
public:
static uint64_t Nanotime();
@@ -79,6 +83,7 @@ public:
bool GetSchedule(Schedule& schedule) const;
void ObserveFrame(uint64_t now);
bool SelectFrame(uint64_t now, bool force, uint32_t& generation);
void FramePublished(uint32_t generation, uint64_t now);
void FramePublished(uint32_t generation, uint32_t frameSerial,
uint64_t now);
void RecordFrameTiming(uint64_t duration);
};

View File

@@ -945,7 +945,8 @@ bool CIndirectDeviceContext::InitializeLGMP()
kvmfr.version = KVMFR_VERSION;
kvmfr.features =
KVMFR_FEATURE_SETCURSORPOS |
KVMFR_FEATURE_WINDOWSIZE;
KVMFR_FEATURE_WINDOWSIZE |
KVMFR_FEATURE_FRAME_SCHEDULE;
strncpy_s(kvmfr.hostver, LG_VERSION_STR, sizeof(kvmfr.hostver) - 1);
ss.write(reinterpret_cast<const char *>(&kvmfr), sizeof(kvmfr));
}
@@ -1523,7 +1524,8 @@ bool CIndirectDeviceContext::PublishFrameBuffer(unsigned frameIndex,
if (status == LGMP_OK)
m_frameScheduler.FramePublished(
scheduleGeneration, CFrameScheduler::Nanotime());
scheduleGeneration, m_frame[frameIndex]->frameSerial,
CFrameScheduler::Nanotime());
if (status != LGMP_OK)
{