[client/idd/obs] scheduler: isolate timing owner delivery

This commit is contained in:
Geoffrey McRae
2026-08-05 14:58:21 +10:00
parent 0b9b805530
commit c7ed04412e
21 changed files with 1572 additions and 362 deletions

View File

@@ -30,7 +30,7 @@ class CFrameBufferPool
{
CSwapChainProcessor * m_swapChain;
CFrameBufferResource m_buffers[LGMP_Q_FRAME_LEN];
CFrameBufferResource m_buffers[LGMP_Q_FRAME_BUFFER_LEN];
public:
void Init(CSwapChainProcessor * swapChain);
@@ -39,4 +39,4 @@ class CFrameBufferPool
CFrameBufferResource* CFrameBufferPool::Get(
const CIndirectDeviceContext::PreparedFrameBuffer& buffer,
size_t minSize);
};
};

View File

@@ -90,8 +90,7 @@ bool CFrameScheduler::ElectOwner(uint64_t now)
if (!client.active || client.expiry <= now)
{
client.active = false;
fastest = nullptr;
break;
continue;
}
if (!fastest || client.period < fastest->period)
@@ -108,6 +107,7 @@ bool CFrameScheduler::ElectOwner(uint64_t now)
const uint32_t oldClientID = m_schedule.clientID;
const uint32_t oldGeneration = m_schedule.generation;
const uint32_t oldEpoch = m_schedule.epoch;
const uint64_t oldPeriod = m_schedule.period;
const uint64_t oldSlack = m_schedule.targetSlack;
if (!fastest)
@@ -119,16 +119,27 @@ bool CFrameScheduler::ElectOwner(uint64_t now)
{
m_schedule.clientID = fastest->clientID;
m_schedule.generation = fastest->generation;
m_schedule.epoch = oldEpoch;
m_schedule.period = fastest->period;
m_schedule.targetSlack = fastest->targetSlack;
m_scheduling = true;
m_scheduling = true;
}
const bool ownerChanged = oldClientID != m_schedule.clientID;
if (ownerChanged || oldGeneration != m_schedule.generation)
{
m_nextDeadline = m_scheduling ? now + m_schedule.period : 0;
m_forceNext = m_scheduling;
if (m_scheduling)
{
if (!++m_epoch)
++m_epoch;
m_schedule.epoch = m_epoch;
}
m_nextDeadline = m_scheduling ? now + m_schedule.period : 0;
m_forceNext = m_scheduling;
m_republishNext = m_scheduling;
if (fastest)
fastest->lastFeedbackFrameSerial = 0;
m_lastPublishedFrameSerial = 0;
m_lastPhaseError = 0;
@@ -138,8 +149,8 @@ bool CFrameScheduler::ElectOwner(uint64_t now)
m_lastLogPublished = m_publishedFrames;
if (ownerChanged && m_scheduling)
DEBUG_INFO("Frame timing owner %u generation %u at %.3f Hz",
m_schedule.clientID, m_schedule.generation,
DEBUG_INFO("Frame timing owner %u generation %u epoch %u at %.3f Hz",
m_schedule.clientID, m_schedule.generation, m_schedule.epoch,
1000000000.0 / m_schedule.period);
else if (ownerChanged && oldClientID)
DEBUG_INFO("Frame timing owner released; using push delivery");
@@ -154,9 +165,11 @@ void CFrameScheduler::Reset()
AcquireSRWLockExclusive(&m_lock);
for (Client& client : m_clients)
client = {};
m_schedule = {};
m_scheduling = false;
m_forceNext = false;
m_schedule = {};
m_scheduling = false;
m_forceNext = false;
m_republishNext = false;
m_epoch = 0;
m_lastArrival = 0;
m_guestPeriod = 0;
@@ -198,11 +211,15 @@ void CFrameScheduler::UpdateSubscribers(const uint32_t * clientIDs,
}
if (client)
client->subscribed = true;
{
client->subscribed = true;
client->subscriptionSeen = true;
}
}
for (Client& client : m_clients)
if (client.clientID && !client.subscribed)
if (client.clientID && !client.subscribed &&
(client.subscriptionSeen || !client.active || client.expiry <= now))
client = {};
const bool changed = ElectOwner(now);
@@ -211,8 +228,8 @@ void CFrameScheduler::UpdateSubscribers(const uint32_t * clientIDs,
WakePublisher();
}
bool CFrameScheduler::UpdateSchedule(const KVMFRFrameSchedule& schedule,
uint64_t now)
bool CFrameScheduler::UpdateSchedule(uint32_t sourceClientID,
const KVMFRFrameSchedule& schedule, uint64_t now)
{
static const KVMFRFrameScheduleFlags validFlags =
KVMFR_FRAME_SCHEDULE_ACTIVE |
@@ -220,19 +237,20 @@ bool CFrameScheduler::UpdateSchedule(const KVMFRFrameSchedule& schedule,
KVMFR_FRAME_SCHEDULE_RESET |
KVMFR_FRAME_SCHEDULE_IMMEDIATE;
if (!schedule.clientID || schedule.flags & ~validFlags)
if (!sourceClientID || schedule.clientID != sourceClientID ||
schedule.flags & ~validFlags)
return false;
AcquireSRWLockExclusive(&m_lock);
Client * client = FindClient(schedule.clientID);
bool wake = false;
if (schedule.flags & KVMFR_FRAME_SCHEDULE_RELEASE)
{
if (client && client->subscribed)
AcquireSRWLockExclusive(&m_lock);
Client * client = FindClient(schedule.clientID);
bool wake = false;
if (client)
{
client->active = false;
client->expiry = 0;
client->active = false;
client->expiry = 0;
client->immediate = false;
wake = ElectOwner(now);
}
ReleaseSRWLockExclusive(&m_lock);
@@ -241,12 +259,6 @@ bool CFrameScheduler::UpdateSchedule(const KVMFRFrameSchedule& schedule,
return true;
}
if (!client || !client->subscribed)
{
ReleaseSRWLockExclusive(&m_lock);
return false;
}
if (!(schedule.flags & KVMFR_FRAME_SCHEDULE_ACTIVE) ||
schedule.period < MIN_PERIOD_NS ||
schedule.period > MAX_PERIOD_NS ||
@@ -254,24 +266,47 @@ bool CFrameScheduler::UpdateSchedule(const KVMFRFrameSchedule& schedule,
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)
return false;
AcquireSRWLockExclusive(&m_lock);
Client * client = FindClient(schedule.clientID);
if (!client)
for (Client& candidate : m_clients)
if (!candidate.clientID)
{
candidate.clientID = schedule.clientID;
client = &candidate;
break;
}
if (!client)
{
ReleaseSRWLockExclusive(&m_lock);
return false;
}
bool wake = false;
if (client->generation != schedule.generation)
{
client->lastFeedbackFrameSerial = 0;
client->immediate = false;
}
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;
wake = true;
}
client->immediate = true;
wake |= ElectOwner(now);
if (m_scheduling && client->clientID == m_schedule.clientID &&
client->generation == m_schedule.generation && client->immediate)
{
m_forceNext = true;
m_republishNext = true;
wake = true;
}
wake |= ApplyFeedback(*client, schedule);
ReleaseSRWLockExclusive(&m_lock);
if (wake)
@@ -284,6 +319,7 @@ bool CFrameScheduler::ApplyFeedback(Client& client,
{
if (!m_scheduling || client.clientID != m_schedule.clientID ||
schedule.generation != m_schedule.generation ||
schedule.feedbackScheduleEpoch != m_schedule.epoch ||
!schedule.feedbackFrameSerial ||
(client.lastFeedbackFrameSerial &&
static_cast<int32_t>(schedule.feedbackFrameSerial -
@@ -366,11 +402,12 @@ void CFrameScheduler::ForceFrame()
}
bool CFrameScheduler::GetPublishTarget(uint64_t now, uint64_t& target,
uint32_t& generation, bool& periodic)
Schedule& schedule, bool& periodic, bool& republish)
{
target = now;
generation = 0;
periodic = false;
target = now;
schedule = {};
periodic = false;
republish = false;
AcquireSRWLockExclusive(&m_lock);
if (!m_scheduling)
{
@@ -378,7 +415,8 @@ bool CFrameScheduler::GetPublishTarget(uint64_t now, uint64_t& target,
return true;
}
generation = m_schedule.generation;
schedule = m_schedule;
republish = m_republishNext;
if (m_forceNext)
{
@@ -412,13 +450,20 @@ void CFrameScheduler::FrameSuperseded()
ReleaseSRWLockExclusive(&m_lock);
}
void CFrameScheduler::FramePublished(uint32_t generation,
void CFrameScheduler::FramePublished(const Schedule& schedule,
uint32_t frameSerial, uint64_t now, bool periodic)
{
AcquireSRWLockExclusive(&m_lock);
if (m_scheduling && generation == m_schedule.generation)
if (m_scheduling && schedule.clientID == m_schedule.clientID &&
schedule.generation == m_schedule.generation &&
schedule.epoch == m_schedule.epoch)
{
m_forceNext = false;
m_forceNext = false;
m_republishNext = false;
Client * client = FindClient(m_schedule.clientID);
if (client)
client->immediate = false;
m_lastPublishedFrameSerial = frameSerial;
++m_publishedFrames;
if (periodic)
@@ -430,6 +475,38 @@ void CFrameScheduler::FramePublished(uint32_t generation,
ReleaseSRWLockExclusive(&m_lock);
}
void CFrameScheduler::FrameRepublished(const Schedule& schedule,
uint32_t frameSerial)
{
AcquireSRWLockExclusive(&m_lock);
if (m_scheduling && schedule.clientID == m_schedule.clientID &&
schedule.generation == m_schedule.generation &&
schedule.epoch == m_schedule.epoch)
{
m_republishNext = false;
Client * client = FindClient(m_schedule.clientID);
if (client)
client->immediate = false;
m_lastPublishedFrameSerial = frameSerial;
++m_publishedFrames;
}
ReleaseSRWLockExclusive(&m_lock);
}
void CFrameScheduler::FrameDelivered(const uint32_t * clientIDs,
unsigned count)
{
AcquireSRWLockExclusive(&m_lock);
for (unsigned i = 0; i < count; ++i)
{
Client * client = FindClient(clientIDs[i]);
if (client)
client->immediate = false;
}
ReleaseSRWLockExclusive(&m_lock);
}
void CFrameScheduler::LogStatistics(uint64_t now)
{
AcquireSRWLockExclusive(&m_lock);

View File

@@ -36,6 +36,7 @@ public:
{
uint32_t clientID;
uint32_t generation;
uint32_t epoch;
uint64_t period;
uint64_t targetSlack;
};
@@ -50,7 +51,9 @@ private:
uint64_t expiry;
uint32_t lastFeedbackFrameSerial;
bool subscribed;
bool subscriptionSeen;
bool active;
bool immediate;
};
mutable SRWLOCK m_lock = SRWLOCK_INIT;
@@ -59,6 +62,8 @@ private:
Schedule m_schedule = {};
bool m_scheduling = false;
bool m_forceNext = false;
bool m_republishNext = false;
uint32_t m_epoch = 0;
uint64_t m_lastArrival = 0;
uint64_t m_guestPeriod = 0;
@@ -91,16 +96,19 @@ public:
void Reset();
void UpdateSubscribers(const uint32_t * clientIDs, unsigned count,
uint64_t now);
bool UpdateSchedule(const KVMFRFrameSchedule& schedule, uint64_t now);
bool UpdateSchedule(uint32_t sourceClientID,
const KVMFRFrameSchedule& schedule, uint64_t now);
bool GetSchedule(Schedule& schedule) const;
HANDLE GetWakeEvent() const { return m_wakeEvent; }
void ObserveFrame(uint64_t now);
void ForceFrame();
bool GetPublishTarget(uint64_t now, uint64_t& target,
uint32_t& generation, bool& periodic);
Schedule& schedule, bool& periodic, bool& republish);
void FrameSuperseded();
void FramePublished(uint32_t generation, uint32_t frameSerial,
void FramePublished(const Schedule& schedule, uint32_t frameSerial,
uint64_t now, bool periodic);
void FrameRepublished(const Schedule& schedule, uint32_t frameSerial);
void FrameDelivered(const uint32_t * clientIDs, unsigned count);
void RecordFrameTiming(uint64_t duration);
void LogStatistics(uint64_t now);
};

View File

@@ -44,6 +44,22 @@ static const struct LGMPQueueConfig POINTER_QUEUE_CONFIG =
1000 //subTimeout
};
static uint64_t FrameScheduleToken(
const CFrameScheduler::Schedule& schedule)
{
return static_cast<uint64_t>(schedule.generation) << 32 |
schedule.epoch;
}
static bool FrameScheduleMatches(
const CFrameScheduler::Schedule& a,
const CFrameScheduler::Schedule& b)
{
return a.clientID == b.clientID &&
a.generation == b.generation &&
a.epoch == b.epoch;
}
static const UINT IDDCX_VERSION_1_10 = 0x1A00;
static const UINT64 FRAME_BYTES_PER_PIXEL = 4;
@@ -879,7 +895,7 @@ bool CIndirectDeviceContext::GetResolutionMemoryRequirements(
return false;
ivshmemSize = frameMemoryStart +
frameAllocationSize * LGMP_Q_FRAME_LEN;
frameAllocationSize * LGMP_Q_FRAME_BUFFER_LEN;
return true;
}
@@ -1026,6 +1042,23 @@ bool CIndirectDeviceContext::InitializeLGMP()
return false;
}
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
{
const struct LGMPQueueConfig config =
{
LGMP_Q_FRAME_OWNER + i, //queueID
LGMP_Q_FRAME_LEN, //numMessages
1000 //subTimeout
};
if ((status = lgmpHostQueueNew(
m_lgmp, config, &m_frameOwnerQueue[i])) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostQueueCreate Failed (Frame Owner %u): %s",
i, lgmpStatusString(status));
return false;
}
}
if ((status = lgmpHostQueueNew(m_lgmp, POINTER_QUEUE_CONFIG, &m_pointerQueue)) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostQueueCreate Failed (Pointer): %s", lgmpStatusString(status));
@@ -1110,7 +1143,7 @@ bool CIndirectDeviceContext::SetupLGMP(size_t alignSize)
const size_t alignmentMask = m_alignSize - 1;
size_t frameAllocationSize =
(available - alignmentPadding) / LGMP_Q_FRAME_LEN;
(available - alignmentPadding) / LGMP_Q_FRAME_BUFFER_LEN;
frameAllocationSize &= ~alignmentMask;
if (frameAllocationSize <= m_alignSize ||
frameAllocationSize > UINT32_MAX)
@@ -1127,7 +1160,7 @@ bool CIndirectDeviceContext::SetupLGMP(size_t alignSize)
(unsigned int)(maxFrameSize / 1048576));
LGMP_STATUS status;
for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
for (int i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i)
{
if ((status = lgmpHostMemAllocAligned(m_lgmp,
(uint32_t)frameAllocationSize,
@@ -1150,7 +1183,14 @@ bool CIndirectDeviceContext::SetupLGMP(size_t alignSize)
m_maxFrameSize = maxFrameSize;
m_publishedFrameIndex.store(-1, std::memory_order_release);
m_frameResendPending = false;
m_frameResendPending = false;
m_framePublishSequence = 0;
memset(m_frameLastPublishSequence, 0,
sizeof(m_frameLastPublishSequence));
for (FrameDelivery& delivery : m_frameDelivery)
delivery = {};
for (OwnerDelivery& delivery : m_ownerDelivery)
delivery = {};
WDF_TIMER_CONFIG config;
WDF_TIMER_CONFIG_INIT_PERIODIC(&config,
@@ -1198,7 +1238,14 @@ void CIndirectDeviceContext::DeInitLGMP()
{
m_frameScheduler.Reset();
m_publishedFrameIndex.store(-1, std::memory_order_release);
m_frameResendPending = false;
m_frameResendPending = false;
m_framePublishSequence = 0;
memset(m_frameLastPublishSequence, 0,
sizeof(m_frameLastPublishSequence));
for (FrameDelivery& delivery : m_frameDelivery)
delivery = {};
for (OwnerDelivery& delivery : m_ownerDelivery)
delivery = {};
return;
}
@@ -1212,13 +1259,20 @@ void CIndirectDeviceContext::DeInitLGMP()
AcquireSRWLockExclusive(&m_framePublishLock);
m_publishedFrameIndex.store(-1, std::memory_order_release);
m_frameResendPending = false;
m_frameResendPending = false;
m_framePublishSequence = 0;
memset(m_frameLastPublishSequence, 0,
sizeof(m_frameLastPublishSequence));
for (FrameDelivery& delivery : m_frameDelivery)
delivery = {};
for (OwnerDelivery& delivery : m_ownerDelivery)
delivery = {};
ReleaseSRWLockExclusive(&m_framePublishLock);
for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
for (int i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i)
m_frameInFlight[i].store(false, std::memory_order_release);
for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
for (int i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i)
lgmpHostMemFree(&m_frameMemory[i]);
for (int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
lgmpHostMemFree(&m_pointerMemory[i]);
@@ -1264,18 +1318,37 @@ void CIndirectDeviceContext::LGMPTimer()
}
const uint64_t now = CFrameScheduler::Nanotime();
uint32_t clientIDs[LGMP_MAX_CLIENTS] = {};
unsigned clientCount = 0;
status = lgmpHostGetClientIDs(m_frameQueue, clientIDs, &clientCount);
if (status == LGMP_OK)
m_frameScheduler.UpdateSubscribers(clientIDs, clientCount, now);
else
DEBUG_WARN("Failed to query LGMP frame subscribers: %s",
lgmpStatusString(status));
uint32_t clientIDs[LGMP_MAX_CLIENTS] = {};
unsigned clientCount = 0;
LGMP_STATUS subscriberStatus = lgmpHostGetClientIDs(
m_frameQueue, clientIDs, &clientCount);
for (unsigned queueIndex = 0;
subscriberStatus == LGMP_OK && queueIndex < LGMP_Q_FRAME_LEN;
++queueIndex)
{
uint32_t queueClientIDs[LGMP_MAX_CLIENTS] = {};
unsigned queueClientCount = 0;
subscriberStatus = lgmpHostGetClientIDs(
m_frameOwnerQueue[queueIndex], queueClientIDs, &queueClientCount);
unsigned commonCount = 0;
for (unsigned i = 0;
subscriberStatus == LGMP_OK && i < clientCount;
++i)
for (unsigned candidate = 0; candidate < queueClientCount; ++candidate)
if (clientIDs[i] == queueClientIDs[candidate])
{
clientIDs[commonCount++] = clientIDs[i];
break;
}
clientCount = commonCount;
}
uint8_t data[LGMP_MSGS_SIZE];
size_t size;
while ((status = lgmpHostReadData(m_pointerQueue, &data, &size)) == LGMP_OK)
uint32_t sourceClientID;
while ((status = lgmpHostReadDataWithSource(
m_pointerQueue, &data, &size, &sourceClientID)) == LGMP_OK)
{
KVMFRMessage * msg = (KVMFRMessage *)data;
switch (msg->type)
@@ -1296,10 +1369,28 @@ void CIndirectDeviceContext::LGMPTimer()
case KVMFR_MESSAGE_FRAME_SCHEDULE:
{
if (size != sizeof(KVMFRFrameSchedule) ||
!m_frameScheduler.UpdateSchedule(
*reinterpret_cast<KVMFRFrameSchedule *>(msg), now))
const KVMFRFrameSchedule * frameSchedule =
reinterpret_cast<KVMFRFrameSchedule *>(msg);
const bool valid = size == sizeof(*frameSchedule) &&
m_frameScheduler.UpdateSchedule(
sourceClientID, *frameSchedule, now);
if (!valid)
DEBUG_WARN("Ignoring invalid KVMFR frame schedule");
else if ((frameSchedule->flags &
(KVMFR_FRAME_SCHEDULE_ACTIVE |
KVMFR_FRAME_SCHEDULE_IMMEDIATE)) ==
(KVMFR_FRAME_SCHEDULE_ACTIVE |
KVMFR_FRAME_SCHEDULE_IMMEDIATE))
{
CFrameScheduler::Schedule owner = {};
if (!m_frameScheduler.GetSchedule(owner) ||
owner.clientID != sourceClientID)
{
AcquireSRWLockExclusive(&m_framePublishLock);
m_frameResendPending = true;
ReleaseSRWLockExclusive(&m_framePublishLock);
}
}
break;
}
}
@@ -1307,28 +1398,22 @@ void CIndirectDeviceContext::LGMPTimer()
lgmpHostAckData(m_pointerQueue);
}
if (subscriberStatus == LGMP_OK)
m_frameScheduler.UpdateSubscribers(clientIDs, clientCount, now);
else
DEBUG_WARN("Failed to query LGMP frame subscribers: %s",
lgmpStatusString(subscriberStatus));
m_frameScheduler.LogStatistics(now);
AcquireSRWLockExclusive(&m_framePublishLock);
if (lgmpHostQueueNewSubs(m_frameQueue))
m_frameResendPending = true;
if (m_frameResendPending && m_monitor &&
lgmpHostQueuePending(m_frameQueue) == 0)
{
const LONG frameIndex =
m_publishedFrameIndex.load(std::memory_order_acquire);
if (frameIndex >= 0)
{
status = lgmpHostQueuePost(
m_frameQueue, 0, m_frameMemory[frameIndex]);
if (status == LGMP_OK)
m_frameResendPending = false;
else if (status != LGMP_ERR_QUEUE_FULL)
DEBUG_ERROR("Failed to resend frame: %s", lgmpStatusString(status));
}
AcquireSRWLockExclusive(&m_framePublishLock);
m_frameResendPending = true;
ReleaseSRWLockExclusive(&m_framePublishLock);
}
ReleaseSRWLockExclusive(&m_framePublishLock);
ProcessFrameDeliveries();
if (lgmpHostQueueNewSubs(m_pointerQueue))
{
@@ -1337,18 +1422,347 @@ void CIndirectDeviceContext::LGMPTimer()
}
}
bool CIndirectDeviceContext::FrameBufferAvailable() const
bool CIndirectDeviceContext::PostSharedFrame(unsigned frameIndex,
uint32_t excludeClientID)
{
if (!m_lgmp || !m_frameQueue ||
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN ||
lgmpHostQueuePending(m_frameQueue) != 0)
return false;
uint32_t clientIDs[LGMP_MAX_CLIENTS] = {};
unsigned clientCount = 0;
LGMP_STATUS status =
lgmpHostGetClientIDs(m_frameQueue, clientIDs, &clientCount);
if (status != LGMP_OK)
{
DEBUG_ERROR("Failed to query shared frame subscribers: %s",
lgmpStatusString(status));
return false;
}
unsigned targetCount = 0;
for (unsigned i = 0; i < clientCount; ++i)
{
bool excluded = clientIDs[i] == excludeClientID;
for (const OwnerDelivery& delivery : m_ownerDelivery)
if (delivery.active && delivery.clientID == clientIDs[i])
{
excluded = true;
break;
}
if (!excluded)
clientIDs[targetCount++] = clientIDs[i];
}
if (!targetCount)
{
m_frameDelivery[frameIndex].sharedOwnerToken = 0;
m_frameDelivery[frameIndex].sharedOwnerClientID = 0;
m_frameDelivery[frameIndex].sharedOwnerPending = false;
m_frameDelivery[frameIndex].sharedPending = false;
m_frameDelivery[frameIndex].sharedDelivered = true;
m_frameResendPending = false;
return true;
}
unsigned recipientCount = 0;
status = lgmpHostQueuePostForClients(
m_frameQueue, 0, m_frameMemory[frameIndex],
clientIDs, targetCount, &recipientCount);
if (status != LGMP_OK)
{
if (status != LGMP_ERR_QUEUE_FULL)
DEBUG_ERROR("Failed to publish shared frame: %s",
lgmpStatusString(status));
return false;
}
if (!recipientCount)
{
m_frameDelivery[frameIndex].sharedOwnerToken = 0;
m_frameDelivery[frameIndex].sharedOwnerClientID = 0;
m_frameDelivery[frameIndex].sharedOwnerPending = false;
m_frameDelivery[frameIndex].sharedPending = false;
m_frameDelivery[frameIndex].sharedDelivered = true;
m_frameResendPending = false;
return true;
}
m_frameDelivery[frameIndex].sharedOwnerToken = 0;
m_frameDelivery[frameIndex].sharedOwnerClientID = 0;
m_frameDelivery[frameIndex].sharedOwnerPending = false;
m_frameDelivery[frameIndex].sharedPending = true;
m_frameDelivery[frameIndex].sharedDelivered = true;
m_frameResendPending = false;
m_frameScheduler.FrameDelivered(clientIDs, targetCount);
return true;
}
bool CIndirectDeviceContext::PostSharedOwnerFrame(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule)
{
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN || !schedule.clientID ||
lgmpHostQueuePending(m_frameQueue) >= LGMP_Q_FRAME_LEN)
return false;
unsigned recipientCount = 0;
const LGMP_STATUS status = lgmpHostQueuePostForClients(
m_frameQueue, FrameScheduleToken(schedule), m_frameMemory[frameIndex],
&schedule.clientID, 1, &recipientCount);
if (status != LGMP_OK || !recipientCount)
{
if (status != LGMP_OK && status != LGMP_ERR_QUEUE_FULL)
DEBUG_ERROR("Failed to publish shared owner frame: %s",
lgmpStatusString(status));
return false;
}
FrameDelivery& delivery = m_frameDelivery[frameIndex];
delivery.sharedOwnerToken = FrameScheduleToken(schedule);
delivery.sharedOwnerClientID = schedule.clientID;
delivery.sharedOwnerPending = true;
delivery.sharedPending = true;
if (!delivery.sharedDelivered)
m_frameResendPending = true;
return true;
}
void CIndirectDeviceContext::ProcessFrameDeliveries()
{
if (!m_frameQueue)
return;
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
if (!m_frameOwnerQueue[i])
return;
AcquireSRWLockExclusive(&m_framePublishLock);
for (unsigned i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i)
{
if (m_frameDelivery[i].sharedOwnerPending &&
!lgmpHostQueueMessagePending(
m_frameQueue, m_frameMemory[i],
m_frameDelivery[i].sharedOwnerToken))
m_frameDelivery[i].sharedOwnerPending = false;
if (m_frameDelivery[i].sharedPending &&
!lgmpHostQueuePayloadPending(m_frameQueue, m_frameMemory[i]))
m_frameDelivery[i].sharedPending = false;
}
CFrameScheduler::Schedule schedule = {};
const bool scheduled = m_frameScheduler.GetSchedule(schedule);
const uint64_t scheduleToken = scheduled ?
FrameScheduleToken(schedule) : 0;
const LONG publishedFrameIndex =
m_publishedFrameIndex.load(std::memory_order_acquire);
const unsigned frameIndex =
static_cast<unsigned>(publishedFrameIndex + 1) % LGMP_Q_FRAME_LEN;
return !m_frameInFlight[frameIndex].load(std::memory_order_acquire);
int newestAcked = -1;
uint32_t newestAckedClient = 0;
for (unsigned queueIndex = 0;
queueIndex < LGMP_Q_FRAME_LEN;
++queueIndex)
{
OwnerDelivery& owner = m_ownerDelivery[queueIndex];
if (!owner.active ||
lgmpHostQueuePayloadPending(
m_frameOwnerQueue[queueIndex],
m_frameMemory[owner.frameIndex]))
continue;
const unsigned frameIndex = owner.frameIndex;
const bool latestDelivery =
static_cast<LONG>(frameIndex) == publishedFrameIndex &&
m_frameLastPublishSequence[frameIndex] == m_framePublishSequence;
const bool authoritative = (!scheduled && latestDelivery) ||
(scheduled && owner.clientID == schedule.clientID &&
owner.token == scheduleToken);
if (authoritative &&
(newestAcked < 0 ||
m_frameLastPublishSequence[frameIndex] >
m_frameLastPublishSequence[newestAcked]))
{
newestAcked = static_cast<int>(frameIndex);
newestAckedClient = owner.clientID;
}
else if (!authoritative && !latestDelivery)
m_frameResendPending = true;
m_frameDelivery[frameIndex].ownerQueueMask &=
~(1U << queueIndex);
owner = {};
}
if (newestAcked >= 0)
{
FrameDelivery& delivery = m_frameDelivery[newestAcked];
const bool needsShared = !delivery.sharedDelivered;
const bool latest = newestAcked == publishedFrameIndex &&
m_frameLastPublishSequence[newestAcked] == m_framePublishSequence;
if (needsShared &&
(!latest ||
m_frameInFlight[newestAcked].load(std::memory_order_acquire) ||
lgmpHostQueuePending(m_frameQueue) != 0))
{
m_frameResendPending = true;
}
else if (needsShared &&
!PostSharedFrame(
static_cast<unsigned>(newestAcked), newestAckedClient))
{
m_frameResendPending = true;
}
}
if (m_frameResendPending &&
lgmpHostQueuePending(m_frameQueue) == 0)
{
const LONG frameIndex =
m_publishedFrameIndex.load(std::memory_order_acquire);
if (frameIndex >= 0 &&
!m_frameDelivery[frameIndex].sharedPending &&
!m_frameInFlight[frameIndex].load(std::memory_order_acquire))
{
FrameDelivery& delivery = m_frameDelivery[frameIndex];
bool ownerDelivered = !scheduled;
if (scheduled)
{
ownerDelivered =
delivery.sharedOwnerClientID == schedule.clientID &&
delivery.sharedOwnerToken == scheduleToken;
for (const OwnerDelivery& owner : m_ownerDelivery)
if (owner.active &&
owner.clientID == schedule.clientID &&
owner.token == scheduleToken &&
owner.frameIndex == static_cast<unsigned>(frameIndex))
{
ownerDelivered = true;
break;
}
}
const bool reserveSharedOwner = scheduled &&
delivery.sharedOwnerClientID == schedule.clientID &&
delivery.sharedOwnerToken == scheduleToken &&
FindAvailableOwnerQueue(static_cast<unsigned>(frameIndex)) < 0;
if (ownerDelivered && !reserveSharedOwner)
{
const uint32_t excludeClientID = scheduled ?
schedule.clientID : delivery.sharedOwnerClientID;
PostSharedFrame(
static_cast<unsigned>(frameIndex), excludeClientID);
}
}
}
ReleaseSRWLockExclusive(&m_framePublishLock);
}
int CIndirectDeviceContext::FindAvailableOwnerQueue(
unsigned preferredIndex) const
{
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
{
const unsigned queueIndex =
(preferredIndex + i) % LGMP_Q_FRAME_LEN;
if (!m_ownerDelivery[queueIndex].active &&
m_frameOwnerQueue[queueIndex] &&
lgmpHostQueuePending(m_frameOwnerQueue[queueIndex]) == 0)
return static_cast<int>(queueIndex);
}
return -1;
}
int CIndirectDeviceContext::FindOwnerDelivery(uint32_t clientID) const
{
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
if (m_ownerDelivery[i].active &&
m_ownerDelivery[i].clientID == clientID)
return static_cast<int>(i);
return -1;
}
int CIndirectDeviceContext::FindSharedOwnerDelivery(
uint32_t clientID) const
{
for (unsigned i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i)
if (m_frameDelivery[i].sharedOwnerPending &&
m_frameDelivery[i].sharedOwnerClientID == clientID)
return static_cast<int>(i);
return -1;
}
bool CIndirectDeviceContext::HasOwnerDelivery(uint32_t clientID) const
{
return FindOwnerDelivery(clientID) >= 0 ||
FindSharedOwnerDelivery(clientID) >= 0;
}
int CIndirectDeviceContext::FindAvailableFrameBuffer() const
{
const LONG publishedFrameIndex =
m_publishedFrameIndex.load(std::memory_order_acquire);
int available = -1;
uint64_t newestPublish = 0;
for (unsigned frameIndex = 0;
frameIndex < LGMP_Q_FRAME_BUFFER_LEN;
++frameIndex)
{
if (static_cast<LONG>(frameIndex) == publishedFrameIndex ||
m_frameInFlight[frameIndex].load(std::memory_order_acquire) ||
m_frameDelivery[frameIndex].ownerQueueMask ||
m_frameDelivery[frameIndex].sharedPending ||
lgmpHostQueuePayloadPending(
m_frameQueue, m_frameMemory[frameIndex]))
continue;
bool ownerPending = false;
for (unsigned queueIndex = 0;
!ownerPending && queueIndex < LGMP_Q_FRAME_LEN;
++queueIndex)
ownerPending = lgmpHostQueuePayloadPending(
m_frameOwnerQueue[queueIndex], m_frameMemory[frameIndex]);
if (ownerPending)
continue;
if (available < 0 ||
m_frameLastPublishSequence[frameIndex] > newestPublish)
{
available = static_cast<int>(frameIndex);
newestPublish = m_frameLastPublishSequence[frameIndex];
}
}
return available;
}
bool CIndirectDeviceContext::FrameBufferAvailable(
const CFrameScheduler::Schedule& schedule)
{
if (!m_lgmp || !m_frameQueue)
return false;
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
if (!m_frameOwnerQueue[i])
return false;
AcquireSRWLockShared(&m_framePublishLock);
bool deliveryAvailable;
if (schedule.clientID)
deliveryAvailable = !HasOwnerDelivery(schedule.clientID) &&
(FindAvailableOwnerQueue(0) >= 0 ||
lgmpHostQueuePending(m_frameQueue) < LGMP_Q_FRAME_LEN);
else
deliveryAvailable = lgmpHostQueuePending(m_frameQueue) == 0;
const bool available = deliveryAvailable &&
FindAvailableFrameBuffer() >= 0;
ReleaseSRWLockShared(&m_framePublishLock);
return available;
}
void CIndirectDeviceContext::ProcessFrameQueue()
@@ -1362,6 +1776,9 @@ void CIndirectDeviceContext::ProcessFrameQueue()
if (status != LGMP_OK && status != LGMP_ERR_CORRUPTED)
DEBUG_ERROR("lgmpHostProcess Failed: %s", lgmpStatusString(status));
if (status == LGMP_OK)
ProcessFrameDeliveries();
}
CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrameBuffer(
@@ -1375,24 +1792,28 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
const unsigned dataHeight = dstFormat.dataHeight ?
dstFormat.dataHeight : dstFormat.desc.Height;
if (!FrameBufferAvailable())
return result;
if (dstFormat.format == FRAME_TYPE_INVALID)
{
DEBUG_ERROR("Unsupported frame format, skipping frame");
return result;
}
const LONG publishedFrameIndex =
m_publishedFrameIndex.load(std::memory_order_acquire);
const unsigned frameIndex =
static_cast<unsigned>(publishedFrameIndex + 1) % LGMP_Q_FRAME_LEN;
AcquireSRWLockExclusive(&m_framePublishLock);
const int availableFrameIndex = FindAvailableFrameBuffer();
bool expected = false;
if (!m_frameInFlight[frameIndex].compare_exchange_strong(
expected, true, std::memory_order_acq_rel))
const bool acquired = availableFrameIndex >= 0 &&
m_frameInFlight[availableFrameIndex].compare_exchange_strong(
expected, true, std::memory_order_acq_rel);
if (acquired)
m_frameDelivery[availableFrameIndex] = {};
const bool fullCopy = acquired &&
(!m_frameLastPublishSequence[availableFrameIndex] ||
m_framePublishSequence >
m_frameLastPublishSequence[availableFrameIndex] + 1);
ReleaseSRWLockExclusive(&m_framePublishLock);
if (!acquired)
return result;
const unsigned frameIndex = static_cast<unsigned>(availableFrameIndex);
if (m_width != dataWidth ||
m_height != dataHeight ||
@@ -1518,45 +1939,198 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
result.frameIndex = frameIndex;
result.mem = fb->data;
result.fullCopy = fullCopy;
return result;
}
bool CIndirectDeviceContext::PublishFrameBuffer(unsigned frameIndex,
uint32_t scheduleGeneration)
const CFrameScheduler::Schedule& schedule)
{
if (!m_frameQueue || frameIndex >= LGMP_Q_FRAME_LEN)
if (!m_frameQueue || frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
return false;
AcquireSRWLockExclusive(&m_framePublishLock);
const LGMP_STATUS status =
lgmpHostQueuePost(
m_frameQueue, scheduleGeneration, m_frameMemory[frameIndex]);
if (status == LGMP_OK)
CFrameScheduler::Schedule currentSchedule = {};
const bool scheduling =
m_frameScheduler.GetSchedule(currentSchedule);
if (scheduling != (schedule.clientID != 0) ||
(scheduling && !FrameScheduleMatches(schedule, currentSchedule)))
{
ReleaseSRWLockExclusive(&m_framePublishLock);
return false;
}
LGMP_STATUS status = LGMP_OK;
bool published = false;
if (schedule.clientID)
{
if (HasOwnerDelivery(schedule.clientID))
status = LGMP_ERR_QUEUE_FULL;
else
{
const int ownerQueueIndex = FindAvailableOwnerQueue(frameIndex);
if (ownerQueueIndex < 0)
published = PostSharedOwnerFrame(frameIndex, schedule);
else
{
unsigned recipientCount = 0;
status = lgmpHostQueuePostForClients(
m_frameOwnerQueue[ownerQueueIndex], FrameScheduleToken(schedule),
m_frameMemory[frameIndex],
&schedule.clientID, 1, &recipientCount);
if (status == LGMP_OK && recipientCount)
{
const unsigned queueIndex =
static_cast<unsigned>(ownerQueueIndex);
OwnerDelivery& owner = m_ownerDelivery[queueIndex];
owner.token = FrameScheduleToken(schedule);
owner.clientID = schedule.clientID;
owner.frameIndex = frameIndex;
owner.active = true;
FrameDelivery& delivery = m_frameDelivery[frameIndex];
delivery.ownerQueueMask |= 1U << queueIndex;
delivery.sharedDelivered = false;
published = true;
if (!PostSharedFrame(frameIndex, schedule.clientID))
m_frameResendPending = true;
}
}
}
}
else
published = PostSharedFrame(frameIndex, 0);
if (published)
{
m_frameLastPublishSequence[frameIndex] = ++m_framePublishSequence;
m_publishedFrameIndex.store(
static_cast<LONG>(frameIndex), std::memory_order_release);
m_frameResendPending = false;
}
ReleaseSRWLockExclusive(&m_framePublishLock);
if (status != LGMP_OK)
if (!published)
{
DEBUG_ERROR("Failed to publish frame: %s", lgmpStatusString(status));
if (status != LGMP_OK && status != LGMP_ERR_QUEUE_FULL)
DEBUG_ERROR("Failed to publish frame: %s",
lgmpStatusString(status));
return false;
}
return true;
}
void CIndirectDeviceContext::CommitFrameBuffer(unsigned frameIndex,
uint32_t scheduleGeneration, bool periodic)
bool CIndirectDeviceContext::RepublishFrameBuffer(
const CFrameScheduler::Schedule& schedule)
{
if (frameIndex >= LGMP_Q_FRAME_LEN)
if (!schedule.clientID)
return false;
AcquireSRWLockExclusive(&m_framePublishLock);
CFrameScheduler::Schedule currentSchedule = {};
if (!m_frameScheduler.GetSchedule(currentSchedule) ||
!FrameScheduleMatches(schedule, currentSchedule))
{
ReleaseSRWLockExclusive(&m_framePublishLock);
return false;
}
const LONG frameIndex =
m_publishedFrameIndex.load(std::memory_order_acquire);
if (frameIndex < 0 ||
m_frameInFlight[frameIndex].load(std::memory_order_acquire))
{
ReleaseSRWLockExclusive(&m_framePublishLock);
return false;
}
const uint64_t scheduleToken = FrameScheduleToken(schedule);
const int existingSharedDelivery =
FindSharedOwnerDelivery(schedule.clientID);
if (existingSharedDelivery >= 0)
{
const FrameDelivery& delivery =
m_frameDelivery[existingSharedDelivery];
const bool delivered = existingSharedDelivery == frameIndex &&
delivery.sharedOwnerToken == scheduleToken;
const uint32_t frameSerial = m_frame[frameIndex]->frameSerial;
ReleaseSRWLockExclusive(&m_framePublishLock);
if (delivered)
m_frameScheduler.FrameRepublished(schedule, frameSerial);
return delivered;
}
const int existingDelivery =
FindOwnerDelivery(schedule.clientID);
if (existingDelivery >= 0)
{
const OwnerDelivery& owner = m_ownerDelivery[existingDelivery];
const bool delivered = owner.frameIndex ==
static_cast<unsigned>(frameIndex) &&
owner.token == scheduleToken;
const uint32_t frameSerial = m_frame[frameIndex]->frameSerial;
ReleaseSRWLockExclusive(&m_framePublishLock);
if (delivered)
m_frameScheduler.FrameRepublished(schedule, frameSerial);
return delivered;
}
const int ownerQueueIndex =
FindAvailableOwnerQueue(static_cast<unsigned>(frameIndex));
if (ownerQueueIndex < 0)
{
const bool published = PostSharedOwnerFrame(
static_cast<unsigned>(frameIndex), schedule);
const uint32_t frameSerial = m_frame[frameIndex]->frameSerial;
ReleaseSRWLockExclusive(&m_framePublishLock);
if (published)
m_frameScheduler.FrameRepublished(schedule, frameSerial);
return published;
}
const uint32_t frameSerial = m_frame[frameIndex]->frameSerial;
unsigned recipientCount = 0;
const LGMP_STATUS status = lgmpHostQueuePostForClients(
m_frameOwnerQueue[ownerQueueIndex], scheduleToken,
m_frameMemory[frameIndex],
&schedule.clientID, 1, &recipientCount);
if (status == LGMP_OK && recipientCount)
{
const unsigned queueIndex = static_cast<unsigned>(ownerQueueIndex);
OwnerDelivery& owner = m_ownerDelivery[queueIndex];
owner.token = scheduleToken;
owner.clientID = schedule.clientID;
owner.frameIndex = static_cast<unsigned>(frameIndex);
owner.active = true;
FrameDelivery& delivery = m_frameDelivery[frameIndex];
delivery.ownerQueueMask |= 1U << queueIndex;
}
ReleaseSRWLockExclusive(&m_framePublishLock);
if (status != LGMP_OK || !recipientCount)
{
if (status != LGMP_OK && status != LGMP_ERR_QUEUE_FULL)
DEBUG_ERROR("Failed to republish frame: %s",
lgmpStatusString(status));
return false;
}
m_frameScheduler.FrameRepublished(schedule, frameSerial);
return true;
}
void CIndirectDeviceContext::CommitFrameBuffer(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule, bool periodic)
{
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
return;
m_frameScheduler.FramePublished(
scheduleGeneration, m_frame[frameIndex]->frameSerial,
schedule, m_frame[frameIndex]->frameSerial,
CFrameScheduler::Nanotime(), periodic);
}
@@ -1571,10 +2145,11 @@ void CIndirectDeviceContext::ForceFrame()
}
bool CIndirectDeviceContext::GetPublishTarget(uint64_t now,
uint64_t& target, uint32_t& generation, bool& periodic)
uint64_t& target, CFrameScheduler::Schedule& schedule, bool& periodic,
bool& republish)
{
return m_frameScheduler.GetPublishTarget(
now, target, generation, periodic);
now, target, schedule, periodic, republish);
}
void CIndirectDeviceContext::FrameSuperseded()
@@ -1589,17 +2164,20 @@ void CIndirectDeviceContext::RecordFrameTiming(uint64_t duration)
void CIndirectDeviceContext::AbortFrameBuffer(unsigned frameIndex)
{
if (frameIndex >= LGMP_Q_FRAME_LEN)
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
return;
AcquireSRWLockExclusive(&m_framePublishLock);
m_frameBuffer[frameIndex]->wp = 0;
InterlockedExchange((volatile LONG *)&m_frame[frameIndex]->timingValid, 0);
InterlockedExchange(
(volatile LONG *)&m_frame[frameIndex]->timingValid, 0);
m_frameInFlight[frameIndex].store(false, std::memory_order_release);
ReleaseSRWLockExclusive(&m_framePublishLock);
}
void CIndirectDeviceContext::FailFrameBuffer(unsigned frameIndex)
{
if (frameIndex >= LGMP_Q_FRAME_LEN)
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
return;
InterlockedExchange((volatile LONG *)&m_frame[frameIndex]->timingValid, 0);
@@ -1609,7 +2187,7 @@ void CIndirectDeviceContext::FailFrameBuffer(unsigned frameIndex)
void CIndirectDeviceContext::CompleteFrameBuffer(unsigned frameIndex)
{
if (frameIndex < LGMP_Q_FRAME_LEN)
if (frameIndex < LGMP_Q_FRAME_BUFFER_LEN)
m_frameInFlight[frameIndex].store(false, std::memory_order_release);
}
@@ -1617,7 +2195,7 @@ void CIndirectDeviceContext::SetFrameTiming(unsigned frameIndex,
uint64_t captureTime, uint64_t postProcessTime, uint64_t copyTime,
uint64_t readyTime)
{
if (frameIndex >= LGMP_Q_FRAME_LEN)
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
return;
KVMFRFrame * frame = m_frame[frameIndex];

View File

@@ -89,6 +89,7 @@ private:
PLGMPHost m_lgmp = nullptr;
WDFTIMER m_lgmpTimer = nullptr;
PLGMPHostQueue m_frameQueue = nullptr;
PLGMPHostQueue m_frameOwnerQueue[LGMP_Q_FRAME_LEN] = {};
SRWLOCK m_lgmpProcessLock = SRWLOCK_INIT;
CFrameScheduler m_frameScheduler;
@@ -108,14 +109,37 @@ private:
size_t m_frameMemoryOffset = 0;
size_t m_maxFrameSize = 0;
std::atomic<LONG> m_publishedFrameIndex = -1;
std::atomic<bool> m_frameInFlight[LGMP_Q_FRAME_LEN] = {};
SRWLOCK m_framePublishLock = SRWLOCK_INIT;
bool m_frameResendPending = false;
uint32_t m_formatVer = 0;
uint32_t m_frameSerial = 0;
PLGMPMemory m_frameMemory[LGMP_Q_FRAME_LEN] = {};
KVMFRFrame * m_frame [LGMP_Q_FRAME_LEN] = {};
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_LEN] = {};
std::atomic<bool> m_frameInFlight[LGMP_Q_FRAME_BUFFER_LEN] = {};
SRWLOCK m_framePublishLock = SRWLOCK_INIT;
bool m_frameResendPending = false;
uint64_t m_framePublishSequence = 0;
uint64_t m_frameLastPublishSequence[LGMP_Q_FRAME_BUFFER_LEN] = {};
struct FrameDelivery
{
uint64_t sharedOwnerToken = 0;
unsigned ownerQueueMask = 0;
uint32_t sharedOwnerClientID = 0;
bool sharedOwnerPending = false;
bool sharedPending = false;
bool sharedDelivered = false;
};
struct OwnerDelivery
{
uint64_t token = 0;
uint32_t clientID = 0;
unsigned frameIndex = 0;
bool active = false;
};
FrameDelivery m_frameDelivery[LGMP_Q_FRAME_BUFFER_LEN] = {};
OwnerDelivery m_ownerDelivery[LGMP_Q_FRAME_LEN] = {};
uint32_t m_formatVer = 0;
uint32_t m_frameSerial = 0;
PLGMPMemory m_frameMemory[LGMP_Q_FRAME_BUFFER_LEN] = {};
KVMFRFrame * m_frame [LGMP_Q_FRAME_BUFFER_LEN] = {};
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_BUFFER_LEN] = {};
unsigned m_width = 0;
unsigned m_height = 0;
@@ -155,6 +179,15 @@ private:
bool InitializeLGMP();
void DeInitLGMP();
void LGMPTimer();
void ProcessFrameDeliveries();
int FindAvailableFrameBuffer() const;
int FindAvailableOwnerQueue(unsigned preferredIndex) const;
int FindOwnerDelivery(uint32_t clientID) const;
int FindSharedOwnerDelivery(uint32_t clientID) const;
bool HasOwnerDelivery(uint32_t clientID) const;
bool PostSharedFrame(unsigned frameIndex, uint32_t excludeClientID);
bool PostSharedOwnerFrame(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule);
void ResendCursor();
void SendColorTransform();
void InitializeEdid();
@@ -222,14 +255,21 @@ public:
{
unsigned frameIndex;
uint8_t* mem;
bool fullCopy;
};
bool FrameBufferAvailable() const;
bool FrameBufferAvailable(const CFrameScheduler::Schedule& schedule);
bool HasPublishedFrame() const
{
return m_publishedFrameIndex.load(std::memory_order_acquire) >= 0;
}
void ProcessFrameQueue();
PreparedFrameBuffer PrepareFrameBuffer(unsigned pitch, const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat, const RECT * dirtyRects, unsigned nbDirtyRects);
bool PublishFrameBuffer(unsigned frameIndex, uint32_t scheduleGeneration);
void CommitFrameBuffer(unsigned frameIndex, uint32_t scheduleGeneration,
bool periodic);
bool PublishFrameBuffer(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule);
bool RepublishFrameBuffer(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);
@@ -241,7 +281,7 @@ public:
void ObserveFrame(uint64_t now);
void ForceFrame();
bool GetPublishTarget(uint64_t now, uint64_t& target,
uint32_t& generation, bool& periodic);
CFrameScheduler::Schedule& schedule, bool& periodic, bool& republish);
void FrameSuperseded();
HANDLE GetFrameScheduleEvent() const
{

View File

@@ -35,7 +35,7 @@ static const uint64_t PUBLISH_RETRY_NS = 1000000ULL;
static const DWORD CANDIDATE_WAIT_MS = 2;
static_assert(LGMP_Q_FRAME_LEN == 2,
"IDD damage repair assumes two alternating frame buffers");
"IDD candidate pipeline assumes two slots");
class CSRWExclusiveLock
{
@@ -223,8 +223,30 @@ void CSwapChainProcessor::PublisherThread()
for (;;)
{
const uint64_t now = CFrameScheduler::Nanotime();
uint64_t target;
CFrameScheduler::Schedule schedule;
bool periodic;
bool republish;
m_devContext->GetPublishTarget(
now, target, schedule, periodic, republish);
if (!HasReadyCandidate())
{
if (republish && m_devContext->HasPublishedFrame())
{
m_devContext->ProcessFrameQueue();
if (m_devContext->RepublishFrameBuffer(schedule))
continue;
ArmPublishTimer(m_publishTimer.Get(), PUBLISH_RETRY_NS);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
if (m_publishTimer.Get())
CancelWaitableTimer(m_publishTimer.Get());
if (WaitForMultipleObjects(
@@ -234,12 +256,6 @@ void CSwapChainProcessor::PublisherThread()
continue;
}
const uint64_t now = CFrameScheduler::Nanotime();
uint64_t target;
uint32_t generation;
bool periodic;
m_devContext->GetPublishTarget(now, target, generation, periodic);
if (target > now)
{
ArmPublishTimer(m_publishTimer.Get(), target - now);
@@ -252,9 +268,9 @@ void CSwapChainProcessor::PublisherThread()
const uint64_t publishStart = CFrameScheduler::Nanotime();
m_devContext->ProcessFrameQueue();
if (!m_devContext->FrameBufferAvailable() ||
if (!m_devContext->FrameBufferAvailable(schedule) ||
!PublishNewestCandidate(
generation, periodic, publishStart))
schedule, periodic, publishStart))
{
ArmPublishTimer(m_publishTimer.Get(), PUBLISH_RETRY_NS);
if (WaitForMultipleObjects(
@@ -907,7 +923,8 @@ void CSwapChainProcessor::SignalCandidateState()
}
bool CSwapChainProcessor::PublishNewestCandidate(
uint32_t scheduleGeneration, bool periodic, uint64_t publishStart)
const CFrameScheduler::Schedule& schedule, bool periodic,
uint64_t publishStart)
{
int selectedCandidate = -1;
uint64_t newestSequence = 0;
@@ -1007,7 +1024,7 @@ bool CSwapChainProcessor::PublishNewestCandidate(
RECT copyDirtyRects[LG_MAX_DIRTY_RECTS * 2] = {};
unsigned nbCopyDirtyRects = 0;
bool fullCopy =
bool fullCopy = buffer.fullCopy ||
candidate.nbDirtyRects == 0 || nbPreviousDirtyRects == 0;
if (!fullCopy)
@@ -1060,7 +1077,7 @@ bool CSwapChainProcessor::PublishNewestCandidate(
// Reserve the LGMP message before submitting the copy. This makes post
// failure recoverable without racing a very fast GPU completion callback.
if (!m_devContext->PublishFrameBuffer(
buffer.frameIndex, scheduleGeneration))
buffer.frameIndex, schedule))
{
copySlot->Cancel();
m_devContext->AbortFrameBuffer(buffer.frameIndex);
@@ -1104,7 +1121,7 @@ bool CSwapChainProcessor::PublishNewestCandidate(
ReleaseSRWLockExclusive(&m_damageLock);
m_devContext->CommitFrameBuffer(
buffer.frameIndex, scheduleGeneration, periodic);
buffer.frameIndex, schedule, periodic);
unsigned superseded = 0;
AcquireSRWLockExclusive(&m_candidateLock);

View File

@@ -134,7 +134,8 @@ private:
static DWORD CALLBACK _PublisherThread(LPVOID arg);
void PublisherThread();
bool PublishNewestCandidate(
uint32_t scheduleGeneration, bool periodic, uint64_t publishStart);
const CFrameScheduler::Schedule& schedule, bool periodic,
uint64_t publishStart);
bool HasReadyCandidate();
int AcquireCandidate();
void ReleaseCandidate(unsigned candidateIndex);