mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 00:31:31 +00:00
[client/host/idd/obs] scheduler: validate phase feedback
Bump KVMFR to version 28 and attach generation, epoch, and deadline identities to cadence deliveries and their timing records. Accept phase corrections only for the exact completed publication. Keep VM and client clocks independent by exchanging relative phase errors. Make submission, completion, and work-estimate accounting best effort so scheduler state cannot delay GPU submission or frame publication.
This commit is contained in:
@@ -26,6 +26,8 @@
|
||||
#include <d3d12.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "CFrameScheduler.h"
|
||||
|
||||
class CSwapChainProcessor;
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
@@ -33,19 +35,22 @@ using namespace Microsoft::WRL;
|
||||
class CFrameBufferResource
|
||||
{
|
||||
private:
|
||||
unsigned m_frameIndex = 0;
|
||||
uint8_t * m_base = nullptr;
|
||||
size_t m_size = 0;
|
||||
size_t m_frameSize = 0;
|
||||
uint64_t m_captureTime = 0;
|
||||
uint64_t m_postProcessStart = 0;
|
||||
uint64_t m_copyStart = 0;
|
||||
unsigned m_timingEffectIndex = 0;
|
||||
uint64_t m_timingToken = 0;
|
||||
bool m_fullCopy = false;
|
||||
unsigned m_candidateIndex = 0;
|
||||
ComPtr<ID3D12Resource> m_res;
|
||||
void * m_map = nullptr;
|
||||
unsigned m_frameIndex = 0;
|
||||
uint8_t * m_base = nullptr;
|
||||
size_t m_size = 0;
|
||||
size_t m_frameSize = 0;
|
||||
uint64_t m_captureTime = 0;
|
||||
uint64_t m_postProcessStart = 0;
|
||||
uint64_t m_copyStart = 0;
|
||||
|
||||
CFrameScheduler::Schedule m_schedule = {};
|
||||
|
||||
unsigned m_timingEffectIndex = 0;
|
||||
uint64_t m_timingToken = 0;
|
||||
bool m_fullCopy = false;
|
||||
unsigned m_candidateIndex = 0;
|
||||
ComPtr<ID3D12Resource> m_res;
|
||||
void * m_map = nullptr;
|
||||
|
||||
public:
|
||||
bool Init(CSwapChainProcessor * swapChain, unsigned frameIndex, uint8_t * base, size_t size);
|
||||
@@ -65,6 +70,14 @@ class CFrameBufferResource
|
||||
uint64_t GetCaptureTime () const { return m_captureTime; }
|
||||
uint64_t GetPostProcessStart() const { return m_postProcessStart; }
|
||||
uint64_t GetCopyStart () const { return m_copyStart; }
|
||||
void SetSchedule(const CFrameScheduler::Schedule& schedule)
|
||||
{
|
||||
m_schedule = schedule;
|
||||
}
|
||||
const CFrameScheduler::Schedule& GetSchedule() const
|
||||
{
|
||||
return m_schedule;
|
||||
}
|
||||
|
||||
void SetPostProcessSample(
|
||||
unsigned effectIndex, uint64_t token, bool fullCopy)
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include "CDebug.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static const uint64_t MIN_SOURCE_PERIOD_NS = 100000ULL;
|
||||
static const uint64_t MIN_SCHEDULE_PERIOD_NS = 2000000ULL;
|
||||
static const uint64_t MAX_PERIOD_NS = 1000000000ULL;
|
||||
@@ -82,7 +84,21 @@ CFrameScheduler::Client * CFrameScheduler::FindClient(uint32_t clientID)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CFrameScheduler::ElectOwner(uint64_t now)
|
||||
CFrameScheduler::Publication * CFrameScheduler::FindPublication(
|
||||
const Schedule& schedule, uint32_t frameSerial)
|
||||
{
|
||||
for (Publication& publication : m_publications)
|
||||
if (publication.generation == schedule.generation &&
|
||||
publication.epoch == schedule.epoch &&
|
||||
publication.deadlineSerial == schedule.deadlineSerial &&
|
||||
publication.frameSerial == frameSerial &&
|
||||
publication.deadline == schedule.deadline)
|
||||
return &publication;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CFrameScheduler::ElectOwner(uint64_t now, uint32_t resetClientID)
|
||||
{
|
||||
Client * fastest = nullptr;
|
||||
Client * incumbent = FindClient(m_schedule.clientID);
|
||||
@@ -122,7 +138,8 @@ bool CFrameScheduler::ElectOwner(uint64_t now)
|
||||
const uint32_t oldEpoch = m_schedule.epoch;
|
||||
const uint64_t oldPeriod = m_schedule.period;
|
||||
const uint64_t oldSlack = m_schedule.targetSlack;
|
||||
if (incumbent && incumbent->active &&
|
||||
if (incumbent && incumbent->clientID != resetClientID &&
|
||||
incumbent->active &&
|
||||
incumbent->generation == oldGeneration)
|
||||
incumbent->nextDelivery = m_nextDeadline;
|
||||
|
||||
@@ -142,7 +159,11 @@ bool CFrameScheduler::ElectOwner(uint64_t now)
|
||||
}
|
||||
|
||||
const bool ownerChanged = oldClientID != m_schedule.clientID;
|
||||
if (ownerChanged || oldGeneration != m_schedule.generation)
|
||||
const bool ownerReset = resetClientID &&
|
||||
resetClientID == m_schedule.clientID;
|
||||
const bool identityChanged = ownerChanged || ownerReset ||
|
||||
oldGeneration != m_schedule.generation;
|
||||
if (identityChanged)
|
||||
{
|
||||
if (m_scheduling)
|
||||
{
|
||||
@@ -153,6 +174,8 @@ bool CFrameScheduler::ElectOwner(uint64_t now)
|
||||
m_nextDeadline = m_scheduling ? fastest->nextDelivery : 0;
|
||||
if (m_scheduling && !m_nextDeadline)
|
||||
m_nextDeadline = now + m_schedule.period;
|
||||
m_deadlineSerial = m_scheduling ? 1 : 0;
|
||||
m_pendingCorrection = 0;
|
||||
if (m_scheduling)
|
||||
{
|
||||
++m_forceRequestTicket;
|
||||
@@ -165,9 +188,10 @@ bool CFrameScheduler::ElectOwner(uint64_t now)
|
||||
}
|
||||
|
||||
if (fastest)
|
||||
fastest->lastFeedbackFrameSerial = 0;
|
||||
fastest->lastFeedbackDeadlineSerial = 0;
|
||||
|
||||
m_lastPublishedFrameSerial = 0;
|
||||
memset(m_publications, 0, sizeof(m_publications));
|
||||
m_publicationIndex = 0;
|
||||
m_lastPhaseError = 0;
|
||||
m_lastLog = now;
|
||||
m_lastLogAcquired = m_acquiredFrames;
|
||||
@@ -182,7 +206,7 @@ bool CFrameScheduler::ElectOwner(uint64_t now)
|
||||
DEBUG_INFO("Frame timing owner released; using push delivery");
|
||||
}
|
||||
|
||||
return ownerChanged || oldGeneration != m_schedule.generation ||
|
||||
return identityChanged ||
|
||||
oldPeriod != m_schedule.period || oldSlack != m_schedule.targetSlack ||
|
||||
clientExpired;
|
||||
}
|
||||
@@ -199,12 +223,15 @@ void CFrameScheduler::Reset()
|
||||
m_forceAckTicket = m_forceRequestTicket;
|
||||
m_republishAckTicket = m_republishRequestTicket;
|
||||
|
||||
m_lastArrival = 0;
|
||||
m_guestPeriod = 0;
|
||||
m_workEstimate = 0;
|
||||
m_nextDeadline = 0;
|
||||
m_lastArrival = 0;
|
||||
m_guestPeriod = 0;
|
||||
m_workEstimate = 0;
|
||||
m_nextDeadline = 0;
|
||||
m_deadlineSerial = 0;
|
||||
m_pendingCorrection = 0;
|
||||
|
||||
m_lastPublishedFrameSerial = 0;
|
||||
memset(m_publications, 0, sizeof(m_publications));
|
||||
m_publicationIndex = 0;
|
||||
|
||||
m_lastPhaseError = 0;
|
||||
m_acquiredFrames = 0;
|
||||
@@ -351,16 +378,18 @@ bool CFrameScheduler::UpdateSchedule(uint32_t sourceClientID,
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool explicitReset =
|
||||
(schedule.flags & KVMFR_FRAME_SCHEDULE_RESET) != 0;
|
||||
const bool reset = client->generation != schedule.generation ||
|
||||
(schedule.flags & KVMFR_FRAME_SCHEDULE_RESET);
|
||||
explicitReset;
|
||||
bool wake = reset || !client->active ||
|
||||
client->period != schedule.period ||
|
||||
client->targetSlack != schedule.targetSlack;
|
||||
if (reset)
|
||||
{
|
||||
client->lastFeedbackFrameSerial = 0;
|
||||
client->immediate = false;
|
||||
client->nextDelivery = now + schedule.period;
|
||||
client->lastFeedbackDeadlineSerial = 0;
|
||||
client->immediate = false;
|
||||
client->nextDelivery = now + schedule.period;
|
||||
}
|
||||
client->generation = schedule.generation;
|
||||
client->period = schedule.period;
|
||||
@@ -372,7 +401,7 @@ bool CFrameScheduler::UpdateSchedule(uint32_t sourceClientID,
|
||||
client->immediate = true;
|
||||
wake = true;
|
||||
}
|
||||
wake |= ElectOwner(now);
|
||||
wake |= ElectOwner(now, explicitReset ? schedule.clientID : 0);
|
||||
if (m_scheduling && client->clientID == m_schedule.clientID &&
|
||||
client->generation == m_schedule.generation &&
|
||||
(schedule.flags & KVMFR_FRAME_SCHEDULE_IMMEDIATE))
|
||||
@@ -395,14 +424,30 @@ bool CFrameScheduler::ApplyFeedback(Client& client,
|
||||
schedule.generation != m_schedule.generation ||
|
||||
schedule.feedbackScheduleEpoch != m_schedule.epoch ||
|
||||
!schedule.feedbackFrameSerial ||
|
||||
(client.lastFeedbackFrameSerial &&
|
||||
static_cast<int32_t>(schedule.feedbackFrameSerial -
|
||||
client.lastFeedbackFrameSerial) <= 0) ||
|
||||
!m_lastPublishedFrameSerial ||
|
||||
static_cast<int32_t>(schedule.feedbackFrameSerial -
|
||||
m_lastPublishedFrameSerial) > 0)
|
||||
!schedule.feedbackDeadlineSerial ||
|
||||
(client.lastFeedbackDeadlineSerial &&
|
||||
static_cast<int32_t>(schedule.feedbackDeadlineSerial -
|
||||
client.lastFeedbackDeadlineSerial) <= 0))
|
||||
return false;
|
||||
|
||||
Publication * publication = nullptr;
|
||||
for (Publication& candidate : m_publications)
|
||||
if (candidate.generation == schedule.generation &&
|
||||
candidate.epoch == schedule.feedbackScheduleEpoch &&
|
||||
candidate.deadlineSerial == schedule.feedbackDeadlineSerial &&
|
||||
candidate.frameSerial == schedule.feedbackFrameSerial)
|
||||
{
|
||||
publication = &candidate;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!publication || !publication->committed ||
|
||||
!publication->completed || !publication->phaseValid ||
|
||||
publication->accepted)
|
||||
return false;
|
||||
|
||||
publication->accepted = true;
|
||||
|
||||
int64_t correction = schedule.phaseError / 4;
|
||||
const int64_t limit = static_cast<int64_t>(m_schedule.period / 4);
|
||||
if (correction > limit)
|
||||
@@ -410,18 +455,50 @@ bool CFrameScheduler::ApplyFeedback(Client& client,
|
||||
else if (correction < -limit)
|
||||
correction = -limit;
|
||||
|
||||
if (correction >= 0)
|
||||
m_nextDeadline += static_cast<uint64_t>(correction);
|
||||
m_pendingCorrection += correction;
|
||||
m_lastPhaseError = schedule.phaseError;
|
||||
client.lastFeedbackDeadlineSerial = schedule.feedbackDeadlineSerial;
|
||||
return false;
|
||||
}
|
||||
|
||||
void CFrameScheduler::AdvanceCurrentDeadline()
|
||||
{
|
||||
m_nextDeadline += m_schedule.period;
|
||||
if (m_pendingCorrection >= 0)
|
||||
m_nextDeadline += static_cast<uint64_t>(m_pendingCorrection);
|
||||
else
|
||||
{
|
||||
const uint64_t advance = static_cast<uint64_t>(-correction);
|
||||
m_nextDeadline = m_nextDeadline > advance ?
|
||||
m_nextDeadline - advance : 0;
|
||||
const uint64_t correction =
|
||||
static_cast<uint64_t>(-m_pendingCorrection);
|
||||
m_nextDeadline = m_nextDeadline > correction ?
|
||||
m_nextDeadline - correction : 0;
|
||||
}
|
||||
m_lastPhaseError = schedule.phaseError;
|
||||
client.lastFeedbackFrameSerial = schedule.feedbackFrameSerial;
|
||||
client.nextDelivery = m_nextDeadline;
|
||||
return correction != 0;
|
||||
m_pendingCorrection = 0;
|
||||
AdvanceDeadlineSerial(1);
|
||||
}
|
||||
|
||||
void CFrameScheduler::AdvanceDeadlineSerial(uint64_t count)
|
||||
{
|
||||
if (!count || !m_deadlineSerial)
|
||||
return;
|
||||
|
||||
const uint64_t position =
|
||||
static_cast<uint64_t>(m_deadlineSerial - 1) + count;
|
||||
uint64_t epochAdvances = position / UINT32_MAX;
|
||||
m_deadlineSerial = static_cast<uint32_t>(position % UINT32_MAX) + 1;
|
||||
if (!epochAdvances)
|
||||
return;
|
||||
|
||||
for (; epochAdvances; --epochAdvances)
|
||||
if (!++m_epoch)
|
||||
++m_epoch;
|
||||
m_schedule.epoch = m_epoch;
|
||||
|
||||
Client * client = FindClient(m_schedule.clientID);
|
||||
if (client)
|
||||
client->lastFeedbackDeadlineSerial = 0;
|
||||
memset(m_publications, 0, sizeof(m_publications));
|
||||
m_publicationIndex = 0;
|
||||
}
|
||||
|
||||
void CFrameScheduler::AdvanceDeadline(uint64_t now)
|
||||
@@ -435,6 +512,7 @@ void CFrameScheduler::AdvanceDeadline(uint64_t now)
|
||||
const uint64_t periods =
|
||||
(target - m_nextDeadline) / m_schedule.period + 1;
|
||||
m_nextDeadline += periods * m_schedule.period;
|
||||
AdvanceDeadlineSerial(periods);
|
||||
}
|
||||
|
||||
void CFrameScheduler::AdvanceDelivery(Client& client, uint64_t now)
|
||||
@@ -520,6 +598,9 @@ bool CFrameScheduler::GetPublishTarget(uint64_t now, uint64_t& target,
|
||||
schedule.republishTicket = m_republishRequestTicket;
|
||||
republish = schedule.republishTicket != m_republishAckTicket;
|
||||
|
||||
schedule.deadline = m_nextDeadline;
|
||||
schedule.deadlineSerial = m_deadlineSerial;
|
||||
|
||||
const uint64_t lead =
|
||||
PublicationLead(m_schedule.targetSlack, m_workEstimate);
|
||||
const uint64_t periodicTarget = m_nextDeadline > lead ?
|
||||
@@ -528,22 +609,45 @@ bool CFrameScheduler::GetPublishTarget(uint64_t now, uint64_t& target,
|
||||
if (schedule.forceTicket != m_forceAckTicket)
|
||||
{
|
||||
periodic = periodicTarget <= now;
|
||||
if (periodic)
|
||||
target = periodicTarget;
|
||||
schedule.deliveryDeadlineSerial = periodic ? m_deadlineSerial : 0;
|
||||
schedule.phaseEligible = periodic;
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
periodic = true;
|
||||
if (periodicTarget <= now)
|
||||
{
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
target = periodicTarget;
|
||||
periodic = true;
|
||||
schedule.deliveryDeadlineSerial = m_deadlineSerial;
|
||||
schedule.phaseEligible = true;
|
||||
target = periodicTarget;
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFrameScheduler::FrameMissed(const Schedule& schedule, uint64_t now,
|
||||
bool periodic)
|
||||
{
|
||||
if (!periodic)
|
||||
return;
|
||||
|
||||
AcquireSRWLockExclusive(&m_lock);
|
||||
if (m_scheduling && schedule.clientID == m_schedule.clientID &&
|
||||
schedule.generation == m_schedule.generation &&
|
||||
schedule.epoch == m_schedule.epoch &&
|
||||
schedule.deadlineSerial == m_deadlineSerial &&
|
||||
schedule.deadline == m_nextDeadline)
|
||||
{
|
||||
AdvanceCurrentDeadline();
|
||||
AdvanceDeadline(now);
|
||||
|
||||
Client * client = FindClient(m_schedule.clientID);
|
||||
if (client)
|
||||
client->nextDelivery = m_nextDeadline;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
}
|
||||
|
||||
void CFrameScheduler::FrameSuperseded()
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_lock);
|
||||
@@ -551,14 +655,52 @@ void CFrameScheduler::FrameSuperseded()
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
}
|
||||
|
||||
bool CFrameScheduler::TryFrameSubmitted(const Schedule& schedule,
|
||||
uint32_t frameSerial)
|
||||
{
|
||||
if (!schedule.phaseEligible || !schedule.deliveryDeadlineSerial ||
|
||||
schedule.deliveryDeadlineSerial != schedule.deadlineSerial)
|
||||
return false;
|
||||
|
||||
if (!TryAcquireSRWLockExclusive(&m_lock))
|
||||
return false;
|
||||
|
||||
bool registered = false;
|
||||
if (m_scheduling && schedule.clientID == m_schedule.clientID &&
|
||||
schedule.generation == m_schedule.generation &&
|
||||
schedule.epoch == m_schedule.epoch &&
|
||||
schedule.deadlineSerial == m_deadlineSerial &&
|
||||
schedule.deadline == m_nextDeadline)
|
||||
{
|
||||
Publication& publication =
|
||||
m_publications[m_publicationIndex++ % PUBLICATION_HISTORY_SIZE];
|
||||
publication = {};
|
||||
publication.generation = schedule.generation;
|
||||
publication.epoch = schedule.epoch;
|
||||
publication.deadlineSerial = schedule.deadlineSerial;
|
||||
publication.frameSerial = frameSerial;
|
||||
publication.deadline = schedule.deadline;
|
||||
publication.committed = true;
|
||||
registered = true;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
return registered;
|
||||
}
|
||||
|
||||
void CFrameScheduler::FramePublished(const Schedule& schedule,
|
||||
uint32_t frameSerial, uint64_t now, bool periodic)
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_lock);
|
||||
if (m_scheduling && schedule.clientID == m_schedule.clientID &&
|
||||
schedule.generation == m_schedule.generation &&
|
||||
schedule.epoch == m_schedule.epoch)
|
||||
schedule.epoch == m_schedule.epoch &&
|
||||
schedule.deadlineSerial == m_deadlineSerial &&
|
||||
schedule.deadline == m_nextDeadline)
|
||||
{
|
||||
Publication * publication = FindPublication(schedule, frameSerial);
|
||||
if (publication)
|
||||
publication->committed = true;
|
||||
|
||||
if (schedule.forceTicket > m_forceAckTicket)
|
||||
m_forceAckTicket = schedule.forceTicket;
|
||||
if (schedule.republishTicket > m_republishAckTicket)
|
||||
@@ -572,11 +714,10 @@ void CFrameScheduler::FramePublished(const Schedule& schedule,
|
||||
client->lastDeliveredFrameSerial = frameSerial;
|
||||
client->deliveredFrameValid = true;
|
||||
}
|
||||
m_lastPublishedFrameSerial = frameSerial;
|
||||
++m_publishedFrames;
|
||||
if (periodic)
|
||||
{
|
||||
m_nextDeadline += m_schedule.period;
|
||||
AdvanceCurrentDeadline();
|
||||
AdvanceDeadline(now);
|
||||
}
|
||||
if (client)
|
||||
@@ -585,6 +726,34 @@ void CFrameScheduler::FramePublished(const Schedule& schedule,
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
}
|
||||
|
||||
bool CFrameScheduler::TryFrameCompleted(const Schedule& schedule,
|
||||
uint32_t frameSerial, uint64_t completedAt)
|
||||
{
|
||||
if (!schedule.phaseEligible || !schedule.deliveryDeadlineSerial ||
|
||||
schedule.deliveryDeadlineSerial != schedule.deadlineSerial ||
|
||||
!schedule.deadline)
|
||||
return false;
|
||||
|
||||
if (!TryAcquireSRWLockExclusive(&m_lock))
|
||||
return false;
|
||||
|
||||
bool phaseValid = false;
|
||||
if (m_scheduling && schedule.clientID == m_schedule.clientID &&
|
||||
schedule.generation == m_schedule.generation &&
|
||||
schedule.epoch == m_schedule.epoch)
|
||||
{
|
||||
Publication * publication = FindPublication(schedule, frameSerial);
|
||||
if (publication && publication->committed)
|
||||
{
|
||||
publication->completed = true;
|
||||
publication->phaseValid = completedAt <= publication->deadline;
|
||||
phaseValid = publication->phaseValid;
|
||||
}
|
||||
}
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
return phaseValid;
|
||||
}
|
||||
|
||||
void CFrameScheduler::FrameRepublished(const Schedule& schedule,
|
||||
uint32_t frameSerial)
|
||||
{
|
||||
@@ -605,7 +774,6 @@ void CFrameScheduler::FrameRepublished(const Schedule& schedule,
|
||||
client->deliveredFrameValid = true;
|
||||
client->nextDelivery = m_nextDeadline;
|
||||
}
|
||||
m_lastPublishedFrameSerial = frameSerial;
|
||||
++m_publishedFrames;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
@@ -765,12 +933,14 @@ void CFrameScheduler::LogStatistics(uint64_t now)
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
}
|
||||
|
||||
void CFrameScheduler::RecordFrameTiming(uint64_t duration)
|
||||
void CFrameScheduler::TryRecordFrameTiming(uint64_t duration)
|
||||
{
|
||||
if (!duration)
|
||||
return;
|
||||
|
||||
AcquireSRWLockExclusive(&m_lock);
|
||||
if (!TryAcquireSRWLockExclusive(&m_lock))
|
||||
return;
|
||||
|
||||
if (!m_workEstimate || duration > m_workEstimate)
|
||||
m_workEstimate = duration;
|
||||
else
|
||||
|
||||
@@ -39,8 +39,12 @@ public:
|
||||
uint32_t epoch;
|
||||
uint64_t period;
|
||||
uint64_t targetSlack;
|
||||
uint64_t deadline;
|
||||
uint64_t forceTicket;
|
||||
uint64_t republishTicket;
|
||||
uint32_t deadlineSerial;
|
||||
uint32_t deliveryDeadlineSerial;
|
||||
bool phaseEligible;
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -52,7 +56,7 @@ private:
|
||||
uint64_t targetSlack;
|
||||
uint64_t expiry;
|
||||
uint64_t nextDelivery;
|
||||
uint32_t lastFeedbackFrameSerial;
|
||||
uint32_t lastFeedbackDeadlineSerial;
|
||||
uint32_t lastDeliveredFrameSerial;
|
||||
bool subscribed;
|
||||
bool ownerCapable;
|
||||
@@ -62,6 +66,21 @@ private:
|
||||
bool deliveredFrameValid;
|
||||
};
|
||||
|
||||
struct Publication
|
||||
{
|
||||
uint32_t generation;
|
||||
uint32_t epoch;
|
||||
uint32_t deadlineSerial;
|
||||
uint32_t frameSerial;
|
||||
uint64_t deadline;
|
||||
bool committed;
|
||||
bool completed;
|
||||
bool phaseValid;
|
||||
bool accepted;
|
||||
};
|
||||
|
||||
static const unsigned PUBLICATION_HISTORY_SIZE = 128;
|
||||
|
||||
mutable SRWLOCK m_lock = SRWLOCK_INIT;
|
||||
HANDLE m_wakeEvent = nullptr;
|
||||
Client m_clients[LGMP_MAX_CLIENTS] = {};
|
||||
@@ -75,12 +94,15 @@ private:
|
||||
uint64_t m_republishRequestTicket = 0;
|
||||
uint64_t m_republishAckTicket = 0;
|
||||
|
||||
uint64_t m_lastArrival = 0;
|
||||
uint64_t m_guestPeriod = 0;
|
||||
uint64_t m_workEstimate = 0;
|
||||
uint64_t m_nextDeadline = 0;
|
||||
uint64_t m_lastArrival = 0;
|
||||
uint64_t m_guestPeriod = 0;
|
||||
uint64_t m_workEstimate = 0;
|
||||
uint64_t m_nextDeadline = 0;
|
||||
uint32_t m_deadlineSerial = 0;
|
||||
int64_t m_pendingCorrection = 0;
|
||||
|
||||
uint32_t m_lastPublishedFrameSerial = 0;
|
||||
Publication m_publications[PUBLICATION_HISTORY_SIZE] = {};
|
||||
unsigned m_publicationIndex = 0;
|
||||
|
||||
int64_t m_lastPhaseError = 0;
|
||||
uint64_t m_acquiredFrames = 0;
|
||||
@@ -92,8 +114,12 @@ private:
|
||||
uint64_t m_lastLogPublished = 0;
|
||||
|
||||
Client * FindClient(uint32_t clientID);
|
||||
bool ElectOwner(uint64_t now);
|
||||
Publication * FindPublication(const Schedule& schedule,
|
||||
uint32_t frameSerial);
|
||||
bool ElectOwner(uint64_t now, uint32_t resetClientID = 0);
|
||||
bool ApplyFeedback(Client& client, const KVMFRFrameSchedule& schedule);
|
||||
void AdvanceCurrentDeadline();
|
||||
void AdvanceDeadlineSerial(uint64_t count);
|
||||
void AdvanceDeadline(uint64_t now);
|
||||
static void AdvanceDelivery(Client& client, uint64_t now);
|
||||
void WakePublisher() const;
|
||||
@@ -115,10 +141,14 @@ public:
|
||||
void ForceFrame();
|
||||
bool GetPublishTarget(uint64_t now, uint64_t& target,
|
||||
Schedule& schedule, bool& periodic, bool& republish);
|
||||
void FrameMissed(const Schedule& schedule, uint64_t now, bool periodic);
|
||||
void FrameSuperseded();
|
||||
bool TryFrameSubmitted(const Schedule& schedule, uint32_t frameSerial);
|
||||
void FramePublished(const Schedule& schedule, uint32_t frameSerial,
|
||||
uint64_t now, bool periodic);
|
||||
void FrameRepublished(const Schedule& schedule, uint32_t frameSerial);
|
||||
bool TryFrameCompleted(const Schedule& schedule, uint32_t frameSerial,
|
||||
uint64_t completedAt);
|
||||
unsigned GetSecondaryRecipients(const uint32_t * clientIDs,
|
||||
unsigned count, uint32_t frameSerial, uint64_t now,
|
||||
uint32_t * recipients) const;
|
||||
@@ -128,6 +158,6 @@ public:
|
||||
void FrameDelivered(const uint32_t * clientIDs, unsigned count,
|
||||
uint32_t frameSerial, uint64_t now);
|
||||
void NotifyPublisher() const { WakePublisher(); }
|
||||
void RecordFrameTiming(uint64_t duration);
|
||||
void TryRecordFrameTiming(uint64_t duration);
|
||||
void LogStatistics(uint64_t now);
|
||||
};
|
||||
|
||||
@@ -47,8 +47,8 @@ static const struct LGMPQueueConfig POINTER_QUEUE_CONFIG =
|
||||
static uint64_t FrameScheduleToken(
|
||||
const CFrameScheduler::Schedule& schedule)
|
||||
{
|
||||
return static_cast<uint64_t>(schedule.generation) << 32 |
|
||||
schedule.epoch;
|
||||
return static_cast<uint64_t>(schedule.epoch) << 32 |
|
||||
schedule.deliveryDeadlineSerial;
|
||||
}
|
||||
|
||||
static bool FrameScheduleMatches(
|
||||
@@ -1884,11 +1884,16 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
// fi->offset is initialized at startup
|
||||
fi->flags = flags;
|
||||
fi->sdrWhiteLevel = dstFormat.sdrWhiteLevel;
|
||||
fi->captureTime = 0;
|
||||
fi->postProcessTime = 0;
|
||||
fi->copyTime = 0;
|
||||
fi->readyTime = 0;
|
||||
fi->timingSerial = 0;
|
||||
|
||||
fi->captureTime = 0;
|
||||
fi->postProcessTime = 0;
|
||||
fi->copyTime = 0;
|
||||
fi->readyTime = 0;
|
||||
fi->timingSerial = 0;
|
||||
fi->timingFlags = 0;
|
||||
fi->scheduleGeneration = 0;
|
||||
fi->scheduleEpoch = 0;
|
||||
fi->scheduleDeadlineSerial = 0;
|
||||
InterlockedExchange((volatile LONG *)&fi->timingValid, 0);
|
||||
fi->rotation = FRAME_ROT_0;
|
||||
fi->type = dstFormat.format;
|
||||
@@ -1953,6 +1958,12 @@ bool CIndirectDeviceContext::PublishFrameBuffer(unsigned frameIndex,
|
||||
return false;
|
||||
}
|
||||
|
||||
KVMFRFrame * frame = m_frame[frameIndex];
|
||||
frame->timingFlags = 0;
|
||||
frame->scheduleGeneration = schedule.generation;
|
||||
frame->scheduleEpoch = schedule.epoch;
|
||||
frame->scheduleDeadlineSerial = schedule.deliveryDeadlineSerial;
|
||||
|
||||
LGMP_STATUS status = LGMP_OK;
|
||||
bool published = false;
|
||||
if (schedule.clientID)
|
||||
@@ -2036,7 +2047,10 @@ bool CIndirectDeviceContext::RepublishFrameBuffer(
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t scheduleToken = FrameScheduleToken(schedule);
|
||||
CFrameScheduler::Schedule deliverySchedule = schedule;
|
||||
deliverySchedule.deliveryDeadlineSerial = 0;
|
||||
deliverySchedule.phaseEligible = false;
|
||||
const uint64_t scheduleToken = FrameScheduleToken(deliverySchedule);
|
||||
const uint32_t frameSerial = m_frame[frameIndex]->frameSerial;
|
||||
if (HasMatchingOwnerDelivery(schedule.clientID,
|
||||
static_cast<unsigned>(frameIndex), scheduleToken))
|
||||
@@ -2057,7 +2071,7 @@ bool CIndirectDeviceContext::RepublishFrameBuffer(
|
||||
if (ownerQueueIndex < 0)
|
||||
{
|
||||
const bool published = PostSharedOwnerFrame(
|
||||
static_cast<unsigned>(frameIndex), schedule);
|
||||
static_cast<unsigned>(frameIndex), deliverySchedule);
|
||||
ReleaseSRWLockExclusive(&m_framePublishLock);
|
||||
if (published)
|
||||
m_frameScheduler.FrameRepublished(schedule, frameSerial);
|
||||
@@ -2106,6 +2120,16 @@ void CIndirectDeviceContext::CommitFrameBuffer(unsigned frameIndex,
|
||||
CFrameScheduler::Nanotime(), periodic);
|
||||
}
|
||||
|
||||
bool CIndirectDeviceContext::TryFrameSubmitted(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule)
|
||||
{
|
||||
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
|
||||
return false;
|
||||
|
||||
return m_frameScheduler.TryFrameSubmitted(
|
||||
schedule, m_frame[frameIndex]->frameSerial);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::ObserveFrame(uint64_t now)
|
||||
{
|
||||
m_frameScheduler.ObserveFrame(now);
|
||||
@@ -2124,14 +2148,20 @@ bool CIndirectDeviceContext::GetPublishTarget(uint64_t now,
|
||||
now, target, schedule, periodic, republish);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::FrameMissed(
|
||||
const CFrameScheduler::Schedule& schedule, uint64_t now, bool periodic)
|
||||
{
|
||||
m_frameScheduler.FrameMissed(schedule, now, periodic);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::FrameSuperseded()
|
||||
{
|
||||
m_frameScheduler.FrameSuperseded();
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::RecordFrameTiming(uint64_t duration)
|
||||
void CIndirectDeviceContext::TryRecordFrameTiming(uint64_t duration)
|
||||
{
|
||||
m_frameScheduler.RecordFrameTiming(duration);
|
||||
m_frameScheduler.TryRecordFrameTiming(duration);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::AbortFrameBuffer(unsigned frameIndex)
|
||||
@@ -2183,17 +2213,23 @@ void CIndirectDeviceContext::CompleteFrameBuffer(
|
||||
|
||||
void CIndirectDeviceContext::SetFrameTiming(unsigned frameIndex,
|
||||
uint64_t captureTime, uint64_t postProcessTime, uint64_t copyTime,
|
||||
uint64_t readyTime)
|
||||
uint64_t readyTime, const CFrameScheduler::Schedule& schedule,
|
||||
uint64_t completedAt)
|
||||
{
|
||||
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
|
||||
return;
|
||||
|
||||
KVMFRFrame * frame = m_frame[frameIndex];
|
||||
frame->captureTime = captureTime;
|
||||
frame->postProcessTime = postProcessTime;
|
||||
frame->copyTime = copyTime;
|
||||
frame->readyTime = readyTime;
|
||||
frame->timingSerial = frame->frameSerial;
|
||||
KVMFRFrame * frame = m_frame[frameIndex];
|
||||
const bool phaseValid = m_frameScheduler.TryFrameCompleted(
|
||||
schedule, frame->frameSerial, completedAt);
|
||||
|
||||
frame->captureTime = captureTime;
|
||||
frame->postProcessTime = postProcessTime;
|
||||
frame->copyTime = copyTime;
|
||||
frame->readyTime = readyTime;
|
||||
frame->timingFlags = phaseValid ?
|
||||
KVMFR_FRAME_TIMING_PHASE_VALID : 0;
|
||||
frame->timingSerial = frame->frameSerial;
|
||||
InterlockedExchange((volatile LONG *)&frame->timingValid, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -282,13 +282,16 @@ public:
|
||||
bool PublishFrameBuffer(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule);
|
||||
bool RepublishFrameBuffer(const CFrameScheduler::Schedule& schedule);
|
||||
bool TryFrameSubmitted(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule);
|
||||
void CommitFrameBuffer(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule, bool periodic);
|
||||
void AbortFrameBuffer(unsigned frameIndex);
|
||||
void FailFrameBuffer(unsigned frameIndex);
|
||||
void CompleteFrameBuffer(unsigned frameIndex, bool succeeded);
|
||||
void SetFrameTiming(unsigned frameIndex, uint64_t captureTime,
|
||||
uint64_t postProcessTime, uint64_t copyTime, uint64_t readyTime);
|
||||
uint64_t postProcessTime, uint64_t copyTime, uint64_t readyTime,
|
||||
const CFrameScheduler::Schedule& schedule, uint64_t completedAt);
|
||||
void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const;
|
||||
void FinalizeFrameBuffer(unsigned frameIndex) const;
|
||||
|
||||
@@ -296,12 +299,14 @@ public:
|
||||
void ForceFrame();
|
||||
bool GetPublishTarget(uint64_t now, uint64_t& target,
|
||||
CFrameScheduler::Schedule& schedule, bool& periodic, bool& republish);
|
||||
void FrameMissed(const CFrameScheduler::Schedule& schedule,
|
||||
uint64_t now, bool periodic);
|
||||
void FrameSuperseded();
|
||||
HANDLE GetFrameScheduleEvent() const
|
||||
{
|
||||
return m_frameScheduler.GetWakeEvent();
|
||||
}
|
||||
void RecordFrameTiming(uint64_t duration);
|
||||
void TryRecordFrameTiming(uint64_t duration);
|
||||
|
||||
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR & info, const BYTE * data,
|
||||
UINT sdrWhiteLevel);
|
||||
|
||||
@@ -327,6 +327,16 @@ void CSwapChainProcessor::PublisherThread()
|
||||
}
|
||||
|
||||
const uint64_t replayNow = CFrameScheduler::Nanotime();
|
||||
uint64_t cadenceTarget = 0;
|
||||
if (schedule.deliveryDeadlineSerial && periodic &&
|
||||
schedule.deadline <= replayNow)
|
||||
{
|
||||
m_devContext->FrameMissed(schedule, replayNow, periodic);
|
||||
continue;
|
||||
}
|
||||
if (schedule.deliveryDeadlineSerial && periodic)
|
||||
cadenceTarget = schedule.deadline;
|
||||
|
||||
uint64_t replayTarget;
|
||||
if (m_devContext->GetSharedFrameTarget(replayNow, replayTarget))
|
||||
{
|
||||
@@ -335,18 +345,28 @@ void CSwapChainProcessor::PublisherThread()
|
||||
{
|
||||
if (m_devContext->ReplaySharedFrame(replayNow, retry))
|
||||
continue;
|
||||
if (!retry)
|
||||
if (retry)
|
||||
replayTarget = replayNow + PUBLISH_RETRY_NS;
|
||||
else
|
||||
{
|
||||
if (m_publishTimer.Get())
|
||||
CancelWaitableTimer(m_publishTimer.Get());
|
||||
if (WaitForMultipleObjects(
|
||||
ARRAYSIZE(idleHandles), idleHandles, FALSE, INFINITE) ==
|
||||
WAIT_OBJECT_0)
|
||||
break;
|
||||
continue;
|
||||
if (cadenceTarget)
|
||||
replayTarget = cadenceTarget;
|
||||
else
|
||||
{
|
||||
if (m_publishTimer.Get())
|
||||
CancelWaitableTimer(m_publishTimer.Get());
|
||||
if (WaitForMultipleObjects(
|
||||
ARRAYSIZE(idleHandles), idleHandles, FALSE, INFINITE) ==
|
||||
WAIT_OBJECT_0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cadenceTarget && cadenceTarget < replayTarget)
|
||||
replayTarget = cadenceTarget;
|
||||
|
||||
const uint64_t delay = replayTarget > replayNow ?
|
||||
replayTarget - replayNow : PUBLISH_RETRY_NS;
|
||||
ArmPublishTimer(m_publishTimer.Get(), delay);
|
||||
@@ -357,6 +377,17 @@ void CSwapChainProcessor::PublisherThread()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cadenceTarget)
|
||||
{
|
||||
ArmPublishTimer(
|
||||
m_publishTimer.Get(), cadenceTarget - replayNow);
|
||||
if (WaitForMultipleObjects(
|
||||
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
|
||||
WAIT_OBJECT_0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_publishTimer.Get())
|
||||
CancelWaitableTimer(m_publishTimer.Get());
|
||||
if (WaitForMultipleObjects(
|
||||
@@ -711,10 +742,7 @@ void CSwapChainProcessor::CompletionFunction(
|
||||
const bool gpuTimingValid =
|
||||
slot->GetGPUTimes(gpuCopyStart, gpuCopyEnd);
|
||||
|
||||
// Publish readiness before sampling the endpoint. Timing has its own valid
|
||||
// flag and is published immediately afterwards.
|
||||
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
|
||||
const uint64_t readyEnd = CFrameScheduler::Nanotime();
|
||||
const uint64_t copyReady = CFrameScheduler::Nanotime();
|
||||
|
||||
const uint64_t postProcessStart = fbRes->GetPostProcessStart();
|
||||
uint64_t postProcessTime = prepareCopyStart - postProcessStart;
|
||||
@@ -726,31 +754,41 @@ void CSwapChainProcessor::CompletionFunction(
|
||||
prepareCopyTime = prepareGPUEnd - prepareGPUStart;
|
||||
}
|
||||
|
||||
uint64_t publishCopyTime = readyEnd - publishStart;
|
||||
uint64_t publishCopyTime = copyReady - publishStart;
|
||||
if (gpuTimingValid && gpuCopyStart >= publishStart &&
|
||||
gpuCopyEnd >= gpuCopyStart && gpuCopyEnd <= readyEnd)
|
||||
gpuCopyEnd >= gpuCopyStart && gpuCopyEnd <= copyReady)
|
||||
publishCopyTime = gpuCopyEnd - gpuCopyStart + indirectCopyTime;
|
||||
|
||||
const uint64_t copyTime = prepareCopyTime + publishCopyTime;
|
||||
const uint64_t elapsed = readyEnd - postProcessStart;
|
||||
const uint64_t measured = postProcessTime + copyTime;
|
||||
const uint64_t readyTime = elapsed > measured ? elapsed - measured : 0;
|
||||
|
||||
// Make the framebuffer readable before phase bookkeeping. If the scheduler
|
||||
// lock is busy, the frame is still delivered and only this phase sample is
|
||||
// discarded.
|
||||
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
|
||||
const uint64_t publishedAt = CFrameScheduler::Nanotime();
|
||||
const uint64_t elapsed = publishedAt - postProcessStart;
|
||||
const uint64_t measured = postProcessTime + copyTime;
|
||||
const uint64_t readyTime = elapsed > measured ? elapsed - measured : 0;
|
||||
|
||||
sc->m_devContext->SetFrameTiming(fbRes->GetFrameIndex(),
|
||||
fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime,
|
||||
fbRes->GetSchedule(), publishedAt);
|
||||
sc->m_devContext->TryRecordFrameTiming(
|
||||
publishedAt - publishStart);
|
||||
|
||||
// Use matching wall-clock boundaries for both modes. The split excludes the
|
||||
// cadence hold while including the indirect CPU copy only when it occurs.
|
||||
const uint64_t timingToken = fbRes->GetTimingToken();
|
||||
if (timingToken && timingStart && prepareReady >= timingStart &&
|
||||
readyEnd >= publishStart)
|
||||
copyReady >= publishStart)
|
||||
{
|
||||
const uint64_t totalTime =
|
||||
(prepareReady - timingStart) + (readyEnd - publishStart);
|
||||
(prepareReady - timingStart) + (copyReady - publishStart);
|
||||
sc->m_postProcessors[candidateIndex].RecordTiming(
|
||||
fbRes->GetTimingEffectIndex(), timingToken,
|
||||
fbRes->IsFullCopy(), totalTime);
|
||||
}
|
||||
sc->m_devContext->RecordFrameTiming(readyEnd - publishStart);
|
||||
sc->m_devContext->SetFrameTiming(fbRes->GetFrameIndex(),
|
||||
fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime);
|
||||
|
||||
sc->m_devContext->CompleteFrameBuffer(fbRes->GetFrameIndex(), true);
|
||||
sc->ReleaseCandidate(candidateIndex);
|
||||
}
|
||||
@@ -1270,6 +1308,12 @@ bool CSwapChainProcessor::PublishNewestCandidate(
|
||||
restoreCandidates();
|
||||
return false;
|
||||
}
|
||||
CFrameScheduler::Schedule frameSchedule = schedule;
|
||||
// Phase accounting must never hold up D3D submission. Keep the immutable
|
||||
// delivery identity and discard only this feedback sample on contention.
|
||||
if (!m_devContext->TryFrameSubmitted(buffer.frameIndex, schedule))
|
||||
frameSchedule.phaseEligible = false;
|
||||
fbRes->SetSchedule(frameSchedule);
|
||||
|
||||
// Retire the candidate damage before submission. The completion callback
|
||||
// may run before Execute returns and make this candidate reusable.
|
||||
|
||||
Reference in New Issue
Block a user