mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-04 06:12:04 +00:00
[client/idd] harden demand-driven frame delivery
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user