mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[client/idd/obs] scheduler: isolate timing owner delivery
This commit is contained in:
@@ -135,7 +135,9 @@ typedef struct LG_TransportFrame
|
||||
uint64_t serial;
|
||||
uint64_t timestamp;
|
||||
uint32_t scheduleGeneration;
|
||||
uint32_t scheduleEpoch;
|
||||
LG_TransportFrameFlags flags;
|
||||
bool scheduleOwner;
|
||||
// Backend-owned immutable metadata, valid until releaseFrame.
|
||||
const LG_TransportFrameFormat * format;
|
||||
|
||||
@@ -207,6 +209,7 @@ typedef struct LG_TransportControl
|
||||
uint64_t targetSlack;
|
||||
int64_t phaseError;
|
||||
uint32_t feedbackFrameSerial;
|
||||
uint32_t feedbackScheduleEpoch;
|
||||
uint32_t lease;
|
||||
}
|
||||
frameSchedule;
|
||||
|
||||
@@ -171,10 +171,13 @@ bool egl_texBufferStreamInit(EGL_Texture ** texture, EGL_TexType type,
|
||||
{
|
||||
case EGL_TEXTYPE_BUFFER_STREAM:
|
||||
case EGL_TEXTYPE_FRAMEBUFFER:
|
||||
case EGL_TEXTYPE_DMABUF:
|
||||
this->texCount = 2;
|
||||
break;
|
||||
|
||||
case EGL_TEXTYPE_DMABUF:
|
||||
this->texCount = LGMP_Q_FRAME_BUFFER_LEN;
|
||||
break;
|
||||
|
||||
case EGL_TEXTYPE_BUFFER_MAP:
|
||||
this->texCount = 1;
|
||||
break;
|
||||
|
||||
@@ -22,9 +22,10 @@
|
||||
|
||||
#include "texture.h"
|
||||
#include "texture_util.h"
|
||||
#include "common/LGMPConfig.h"
|
||||
#include "common/locking.h"
|
||||
|
||||
#define EGL_TEX_BUFFER_MAX 2
|
||||
#define EGL_TEX_BUFFER_MAX LGMP_Q_FRAME_BUFFER_LEN
|
||||
|
||||
typedef struct TextureBuffer
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ typedef struct TexDMABUF
|
||||
|
||||
EGLDisplay display;
|
||||
|
||||
struct FdImage images[2];
|
||||
struct FdImage images[EGL_TEX_BUFFER_MAX];
|
||||
int lastIndex;
|
||||
int renderIndex;
|
||||
|
||||
@@ -220,11 +220,28 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture,
|
||||
|
||||
DEBUG_ASSERT(update->type == EGL_TEXTYPE_DMABUF);
|
||||
|
||||
struct FdImage *fdImage =
|
||||
(this->images[0].fd == update->dmaFD) ? &this->images[0] :
|
||||
(this->images[1].fd == update->dmaFD) ? &this->images[1] :
|
||||
(this->images[0].fd == -1) ? &this->images[0] :
|
||||
&this->images[1];
|
||||
struct FdImage *fdImage = NULL;
|
||||
for (int i = 0; i < ARRAY_LENGTH(this->images); ++i)
|
||||
if (this->images[i].fd == update->dmaFD)
|
||||
{
|
||||
fdImage = &this->images[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!fdImage)
|
||||
for (int i = 0; i < ARRAY_LENGTH(this->images); ++i)
|
||||
if (this->images[i].fd == -1)
|
||||
{
|
||||
fdImage = &this->images[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!fdImage)
|
||||
{
|
||||
DEBUG_ERROR("No free DMABUF image slot");
|
||||
return false;
|
||||
}
|
||||
|
||||
EGLImage image = fdImage->image;
|
||||
if (unlikely(image == EGL_NO_IMAGE))
|
||||
{
|
||||
@@ -255,7 +272,7 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture,
|
||||
fdImage->fd = update->dmaFD;
|
||||
fdImage->image = image;
|
||||
|
||||
int slot = (fdImage == &this->images[0]) ? 0 : 1;
|
||||
const int slot = (int)(fdImage - this->images);
|
||||
fdImage->texIndex = slot;
|
||||
GLsync sync = 0;
|
||||
INTERLOCKED_SECTION(parent->copyLock,
|
||||
@@ -287,7 +304,7 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture,
|
||||
INTERLOCKED_SECTION(parent->copyLock,
|
||||
{
|
||||
fdImage->frameToken = update->frameToken;
|
||||
this->lastIndex = (fdImage == &this->images[0]) ? 0 : 1;
|
||||
this->lastIndex = (int)(fdImage - this->images);
|
||||
});
|
||||
|
||||
return true;
|
||||
|
||||
@@ -43,6 +43,7 @@ static struct
|
||||
_Atomic(bool) supported;
|
||||
_Atomic(bool) active;
|
||||
bool controlPending;
|
||||
bool immediatePending;
|
||||
_Atomic(uint32_t) generation;
|
||||
_Atomic(uint64_t) period;
|
||||
uint64_t lastSend;
|
||||
@@ -50,6 +51,7 @@ static struct
|
||||
|
||||
int64_t phaseError;
|
||||
uint32_t feedbackFrameSerial;
|
||||
uint32_t feedbackScheduleEpoch;
|
||||
unsigned feedbackSamples;
|
||||
bool feedbackDirty;
|
||||
|
||||
@@ -89,21 +91,24 @@ static bool sendSchedule(LG_TransportFrameScheduleFlags flags,
|
||||
|
||||
int64_t phaseError;
|
||||
uint32_t feedbackFrameSerial;
|
||||
uint32_t feedbackScheduleEpoch;
|
||||
LG_LOCK(l_frameScheduler.lock);
|
||||
phaseError = l_frameScheduler.phaseError;
|
||||
feedbackFrameSerial = l_frameScheduler.feedbackFrameSerial;
|
||||
phaseError = l_frameScheduler.phaseError;
|
||||
feedbackFrameSerial = l_frameScheduler.feedbackFrameSerial;
|
||||
feedbackScheduleEpoch = l_frameScheduler.feedbackScheduleEpoch;
|
||||
LG_UNLOCK(l_frameScheduler.lock);
|
||||
|
||||
const LG_TransportControl control = {
|
||||
.type = LG_TRANSPORT_CONTROL_FRAME_SCHEDULE,
|
||||
.frameSchedule = {
|
||||
.generation = l_frameScheduler.generation,
|
||||
.flags = flags,
|
||||
.period = period,
|
||||
.targetSlack = FRAME_SCHEDULER_TARGET_SLACK_NS,
|
||||
.phaseError = phaseError,
|
||||
.feedbackFrameSerial = feedbackFrameSerial,
|
||||
.lease = FRAME_SCHEDULER_LEASE_MS,
|
||||
.generation = l_frameScheduler.generation,
|
||||
.flags = flags,
|
||||
.period = period,
|
||||
.targetSlack = FRAME_SCHEDULER_TARGET_SLACK_NS,
|
||||
.phaseError = phaseError,
|
||||
.feedbackFrameSerial = feedbackFrameSerial,
|
||||
.feedbackScheduleEpoch = feedbackScheduleEpoch,
|
||||
.lease = FRAME_SCHEDULER_LEASE_MS,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -118,8 +123,11 @@ static bool sendSchedule(LG_TransportFrameScheduleFlags flags,
|
||||
}
|
||||
|
||||
l_frameScheduler.controlPending = true;
|
||||
if (flags & LG_TRANSPORT_FRAME_SCHEDULE_IMMEDIATE)
|
||||
l_frameScheduler.immediatePending = false;
|
||||
LG_LOCK(l_frameScheduler.lock);
|
||||
if (l_frameScheduler.feedbackFrameSerial == feedbackFrameSerial)
|
||||
if (l_frameScheduler.feedbackFrameSerial == feedbackFrameSerial &&
|
||||
l_frameScheduler.feedbackScheduleEpoch == feedbackScheduleEpoch)
|
||||
l_frameScheduler.feedbackDirty = false;
|
||||
LG_UNLOCK(l_frameScheduler.lock);
|
||||
return true;
|
||||
@@ -140,17 +148,19 @@ void frameScheduler_start(LG_TransportFeatureFlags features)
|
||||
{
|
||||
l_frameScheduler.supported =
|
||||
features & LG_TRANSPORT_FEATURE_FRAME_SCHEDULE;
|
||||
l_frameScheduler.active = false;
|
||||
l_frameScheduler.controlPending = false;
|
||||
l_frameScheduler.lastSend = 0;
|
||||
l_frameScheduler.lastCadence = 0;
|
||||
l_frameScheduler.active = false;
|
||||
l_frameScheduler.controlPending = false;
|
||||
l_frameScheduler.immediatePending = true;
|
||||
l_frameScheduler.lastSend = 0;
|
||||
l_frameScheduler.lastCadence = 0;
|
||||
++l_frameScheduler.generation;
|
||||
|
||||
LG_LOCK(l_frameScheduler.lock);
|
||||
l_frameScheduler.phaseError = 0;
|
||||
l_frameScheduler.feedbackFrameSerial = 0;
|
||||
l_frameScheduler.feedbackSamples = 0;
|
||||
l_frameScheduler.feedbackDirty = false;
|
||||
l_frameScheduler.phaseError = 0;
|
||||
l_frameScheduler.feedbackFrameSerial = 0;
|
||||
l_frameScheduler.feedbackScheduleEpoch = 0;
|
||||
l_frameScheduler.feedbackSamples = 0;
|
||||
l_frameScheduler.feedbackDirty = false;
|
||||
LG_UNLOCK(l_frameScheduler.lock);
|
||||
}
|
||||
|
||||
@@ -159,9 +169,10 @@ void frameScheduler_stop(void)
|
||||
if (l_frameScheduler.supported && l_frameScheduler.active)
|
||||
sendSchedule(LG_TRANSPORT_FRAME_SCHEDULE_RELEASE, 0);
|
||||
|
||||
l_frameScheduler.supported = false;
|
||||
l_frameScheduler.active = false;
|
||||
l_frameScheduler.controlPending = false;
|
||||
l_frameScheduler.supported = false;
|
||||
l_frameScheduler.active = false;
|
||||
l_frameScheduler.controlPending = false;
|
||||
l_frameScheduler.immediatePending = false;
|
||||
}
|
||||
|
||||
void frameScheduler_update(void)
|
||||
@@ -179,13 +190,15 @@ void frameScheduler_update(void)
|
||||
FRAME_SCHEDULER_CADENCE_GRACE_NS &&
|
||||
sendSchedule(LG_TRANSPORT_FRAME_SCHEDULE_RELEASE, 0))
|
||||
{
|
||||
l_frameScheduler.active = false;
|
||||
l_frameScheduler.period = 0;
|
||||
l_frameScheduler.active = false;
|
||||
l_frameScheduler.period = 0;
|
||||
l_frameScheduler.immediatePending = true;
|
||||
LG_LOCK(l_frameScheduler.lock);
|
||||
l_frameScheduler.phaseError = 0;
|
||||
l_frameScheduler.feedbackFrameSerial = 0;
|
||||
l_frameScheduler.feedbackSamples = 0;
|
||||
l_frameScheduler.feedbackDirty = false;
|
||||
l_frameScheduler.phaseError = 0;
|
||||
l_frameScheduler.feedbackFrameSerial = 0;
|
||||
l_frameScheduler.feedbackScheduleEpoch = 0;
|
||||
l_frameScheduler.feedbackSamples = 0;
|
||||
l_frameScheduler.feedbackDirty = false;
|
||||
LG_UNLOCK(l_frameScheduler.lock);
|
||||
}
|
||||
return;
|
||||
@@ -204,18 +217,21 @@ void frameScheduler_update(void)
|
||||
{
|
||||
l_frameScheduler.period = period;
|
||||
++l_frameScheduler.generation;
|
||||
l_frameScheduler.immediatePending = true;
|
||||
LG_LOCK(l_frameScheduler.lock);
|
||||
l_frameScheduler.phaseError = 0;
|
||||
l_frameScheduler.feedbackFrameSerial = 0;
|
||||
l_frameScheduler.feedbackSamples = 0;
|
||||
l_frameScheduler.feedbackDirty = false;
|
||||
l_frameScheduler.phaseError = 0;
|
||||
l_frameScheduler.feedbackFrameSerial = 0;
|
||||
l_frameScheduler.feedbackScheduleEpoch = 0;
|
||||
l_frameScheduler.feedbackSamples = 0;
|
||||
l_frameScheduler.feedbackDirty = false;
|
||||
LG_UNLOCK(l_frameScheduler.lock);
|
||||
}
|
||||
else
|
||||
l_frameScheduler.period =
|
||||
(l_frameScheduler.period * 7 + period) / 8;
|
||||
|
||||
if (l_frameScheduler.active && !reset)
|
||||
if (l_frameScheduler.active && !reset &&
|
||||
!l_frameScheduler.immediatePending)
|
||||
{
|
||||
LG_LOCK(l_frameScheduler.lock);
|
||||
const bool feedbackDirty = l_frameScheduler.feedbackDirty;
|
||||
@@ -230,6 +246,8 @@ void frameScheduler_update(void)
|
||||
LG_TRANSPORT_FRAME_SCHEDULE_ACTIVE;
|
||||
if (reset)
|
||||
flags |= LG_TRANSPORT_FRAME_SCHEDULE_RESET;
|
||||
if (l_frameScheduler.immediatePending)
|
||||
flags |= LG_TRANSPORT_FRAME_SCHEDULE_IMMEDIATE;
|
||||
|
||||
if (sendSchedule(flags, l_frameScheduler.period))
|
||||
{
|
||||
@@ -239,9 +257,9 @@ void frameScheduler_update(void)
|
||||
}
|
||||
|
||||
void frameScheduler_feedback(uint64_t frameSerial, uint32_t generation,
|
||||
uint64_t measuredPhase)
|
||||
uint32_t scheduleEpoch, uint64_t measuredPhase)
|
||||
{
|
||||
if (!generation)
|
||||
if (!generation || !scheduleEpoch)
|
||||
return;
|
||||
|
||||
LG_LOCK(l_frameScheduler.lock);
|
||||
@@ -259,6 +277,13 @@ void frameScheduler_feedback(uint64_t frameSerial, uint32_t generation,
|
||||
return;
|
||||
}
|
||||
|
||||
if (l_frameScheduler.feedbackScheduleEpoch &&
|
||||
l_frameScheduler.feedbackScheduleEpoch != scheduleEpoch)
|
||||
{
|
||||
l_frameScheduler.phaseError = 0;
|
||||
l_frameScheduler.feedbackSamples = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -276,7 +301,8 @@ void frameScheduler_feedback(uint64_t frameSerial, uint32_t generation,
|
||||
if (l_frameScheduler.feedbackSamples < 32)
|
||||
++l_frameScheduler.feedbackSamples;
|
||||
|
||||
l_frameScheduler.feedbackFrameSerial = (uint32_t)frameSerial;
|
||||
l_frameScheduler.feedbackDirty = true;
|
||||
l_frameScheduler.feedbackFrameSerial = (uint32_t)frameSerial;
|
||||
l_frameScheduler.feedbackScheduleEpoch = scheduleEpoch;
|
||||
l_frameScheduler.feedbackDirty = true;
|
||||
LG_UNLOCK(l_frameScheduler.lock);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,6 @@ void frameScheduler_start(LG_TransportFeatureFlags features);
|
||||
void frameScheduler_stop(void);
|
||||
void frameScheduler_update(void);
|
||||
void frameScheduler_feedback(uint64_t frameSerial, uint32_t generation,
|
||||
uint64_t measuredPhase);
|
||||
uint32_t scheduleEpoch, uint64_t measuredPhase);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -214,8 +214,10 @@ struct FrameTimingRecord
|
||||
LG_RendererFrameToken token;
|
||||
uint64_t frameSerial;
|
||||
uint32_t scheduleGeneration;
|
||||
uint32_t scheduleEpoch;
|
||||
unsigned readyMask;
|
||||
bool producerValid;
|
||||
bool scheduleOwner;
|
||||
|
||||
uint64_t captureTime;
|
||||
uint64_t postProcessTime;
|
||||
@@ -326,8 +328,9 @@ static void frameTimingCancel(LG_RendererFrameToken token)
|
||||
}
|
||||
|
||||
static void frameTimingQueue(LG_RendererFrameToken token, uint64_t frameSerial,
|
||||
uint32_t scheduleGeneration, uint64_t importTime,
|
||||
uint64_t importWaitTime, uint64_t dispatchStart, uint64_t queueStart)
|
||||
uint32_t scheduleGeneration, uint32_t scheduleEpoch, bool scheduleOwner,
|
||||
uint64_t importTime, uint64_t importWaitTime, uint64_t dispatchStart,
|
||||
uint64_t queueStart)
|
||||
{
|
||||
INTERLOCKED_SECTION(l_frameTiming.lock, {
|
||||
struct FrameTimingRecord * record = frameTimingRecord(token);
|
||||
@@ -342,6 +345,8 @@ static void frameTimingQueue(LG_RendererFrameToken token, uint64_t frameSerial,
|
||||
record->queueStart = queueStart;
|
||||
record->frameSerial = frameSerial;
|
||||
record->scheduleGeneration = scheduleGeneration;
|
||||
record->scheduleEpoch = scheduleEpoch;
|
||||
record->scheduleOwner = scheduleOwner;
|
||||
if (record->timestamp < queueStart)
|
||||
record->timestamp = queueStart;
|
||||
}
|
||||
@@ -390,6 +395,8 @@ static void frameTimingFinishRender(const LG_RendererFrameTiming * timing,
|
||||
uint64_t feedbackFrameSerial = 0;
|
||||
uint64_t feedbackQueueStart = 0;
|
||||
uint32_t feedbackGeneration = 0;
|
||||
uint32_t feedbackEpoch = 0;
|
||||
bool feedbackOwner = false;
|
||||
|
||||
LG_LOCK(l_frameTiming.lock);
|
||||
if (l_frameTiming.retireToken <= timing->frameToken)
|
||||
@@ -411,6 +418,8 @@ static void frameTimingFinishRender(const LG_RendererFrameTiming * timing,
|
||||
|
||||
feedbackFrameSerial = record->frameSerial;
|
||||
feedbackGeneration = record->scheduleGeneration;
|
||||
feedbackEpoch = record->scheduleEpoch;
|
||||
feedbackOwner = record->scheduleOwner;
|
||||
feedbackQueueStart = record->queueStart;
|
||||
|
||||
if (unlikely(
|
||||
@@ -434,11 +443,12 @@ static void frameTimingFinishRender(const LG_RendererFrameTiming * timing,
|
||||
}
|
||||
LG_UNLOCK(l_frameTiming.lock);
|
||||
|
||||
if (g_state.jitRender && feedbackFrameSerial && feedbackGeneration &&
|
||||
feedbackQueueStart && prepareStart >= feedbackQueueStart)
|
||||
if (g_state.jitRender && feedbackOwner && feedbackFrameSerial &&
|
||||
feedbackGeneration && feedbackEpoch && feedbackQueueStart &&
|
||||
prepareStart >= feedbackQueueStart)
|
||||
{
|
||||
frameScheduler_feedback(
|
||||
feedbackFrameSerial, feedbackGeneration,
|
||||
feedbackFrameSerial, feedbackGeneration, feedbackEpoch,
|
||||
prepareStart - feedbackQueueStart);
|
||||
}
|
||||
}
|
||||
@@ -936,7 +946,8 @@ int main_frameThread(void * unused)
|
||||
break;
|
||||
}
|
||||
|
||||
if (frame.serial == frameSerial && g_state.formatValid)
|
||||
if (frame.serial == frameSerial && g_state.formatValid &&
|
||||
!frame.scheduleOwner)
|
||||
{
|
||||
g_state.transportOps->releaseFrame(g_state.transport, &frame);
|
||||
continue;
|
||||
@@ -1113,8 +1124,8 @@ int main_frameThread(void * unused)
|
||||
memory_order_release);
|
||||
#endif
|
||||
frameTimingQueue(frameToken, frame.serial, frame.scheduleGeneration,
|
||||
g_state.frameImportTime, g_state.frameImportWaitTime,
|
||||
dispatchStart, queueStart);
|
||||
frame.scheduleEpoch, frame.scheduleOwner, g_state.frameImportTime,
|
||||
g_state.frameImportWaitTime, dispatchStart, queueStart);
|
||||
|
||||
if (g_state.jitRender)
|
||||
{
|
||||
|
||||
@@ -47,24 +47,28 @@ struct DMAFrameInfo
|
||||
|
||||
struct LG_Transport
|
||||
{
|
||||
struct IVSHMEM shm;
|
||||
PLGMPClient client;
|
||||
PLGMPClientQueue frameQueue;
|
||||
PLGMPClientQueue pointerQueue;
|
||||
LG_Lock pointerLock;
|
||||
struct IVSHMEM shm;
|
||||
PLGMPClient client;
|
||||
PLGMPClientQueue frameQueue;
|
||||
PLGMPClientQueue ownerFrameQueue[LGMP_Q_FRAME_LEN];
|
||||
PLGMPClientQueue pendingFrameQueue;
|
||||
PLGMPClientQueue pointerQueue;
|
||||
LG_Lock pointerLock;
|
||||
|
||||
unsigned cursorPollInterval;
|
||||
unsigned framePollInterval;
|
||||
bool allowDMA;
|
||||
bool connected;
|
||||
bool framePending;
|
||||
bool frameScheduleSupported;
|
||||
const KVMFRFrame * pendingFrame;
|
||||
uint32_t clientID;
|
||||
uint32_t frameSerial;
|
||||
bool frameSerialValid;
|
||||
bool formatValid;
|
||||
LG_TransportFrameFormat format;
|
||||
|
||||
struct DMAFrameInfo dma[LGMP_Q_FRAME_LEN];
|
||||
struct DMAFrameInfo dma[LGMP_Q_FRAME_BUFFER_LEN];
|
||||
uint8_t * pointerData;
|
||||
size_t pointerDataSize;
|
||||
};
|
||||
@@ -180,7 +184,7 @@ static bool lgmp_create(LG_Transport ** result)
|
||||
this->framePollInterval = framePoll;
|
||||
this->cursorPollInterval = cursorPoll;
|
||||
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i)
|
||||
this->dma[i].fd = -1;
|
||||
|
||||
LG_LOCK_INIT(this->pointerLock);
|
||||
@@ -210,25 +214,39 @@ static bool lgmp_create(LG_Transport ** result)
|
||||
|
||||
static void lgmp_stopFrame(struct LG_Transport * this)
|
||||
{
|
||||
if (this->framePending && this->frameQueue)
|
||||
if (this->framePending && this->pendingFrameQueue)
|
||||
{
|
||||
const LGMP_STATUS status = lgmpClientMessageDone(this->frameQueue);
|
||||
const LGMP_STATUS status =
|
||||
lgmpClientMessageDone(this->pendingFrameQueue);
|
||||
if (status != LGMP_OK)
|
||||
DEBUG_WARN("Failed to release pending LGMP frame: %s",
|
||||
lgmpStatusString(status));
|
||||
}
|
||||
this->framePending = false;
|
||||
this->pendingFrame = NULL;
|
||||
this->framePending = false;
|
||||
this->pendingFrame = NULL;
|
||||
this->pendingFrameQueue = NULL;
|
||||
|
||||
LGMP_STATUS status = lgmpClientUnsubscribe(&this->frameQueue);
|
||||
if (status != LGMP_OK)
|
||||
{
|
||||
DEBUG_WARN("Failed to unsubscribe from the LGMP frame queue: %s",
|
||||
DEBUG_WARN("Failed to unsubscribe from the shared LGMP frame queue: %s",
|
||||
lgmpStatusString(status));
|
||||
this->frameQueue = NULL;
|
||||
}
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
{
|
||||
status = lgmpClientUnsubscribe(&this->ownerFrameQueue[i]);
|
||||
if (status != LGMP_OK)
|
||||
{
|
||||
DEBUG_WARN("Failed to unsubscribe from owner LGMP frame queue %u: %s",
|
||||
i, lgmpStatusString(status));
|
||||
this->ownerFrameQueue[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
this->frameSerial = 0;
|
||||
this->formatValid = false;
|
||||
this->frameSerial = 0;
|
||||
this->frameSerialValid = false;
|
||||
this->formatValid = false;
|
||||
}
|
||||
|
||||
static void lgmp_stopPointer(struct LG_Transport * this)
|
||||
@@ -252,7 +270,7 @@ static void lgmp_closeQueues(struct LG_Transport * this)
|
||||
|
||||
static void lgmp_closeDMA(struct LG_Transport * this)
|
||||
{
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i)
|
||||
{
|
||||
if (this->dma[i].fd >= 0)
|
||||
close(this->dma[i].fd);
|
||||
@@ -370,9 +388,12 @@ static LG_TransportStatus lgmp_connect(LG_Transport * this,
|
||||
case LGMP_OK:
|
||||
if (!lgmp_parseSession(data, size, session))
|
||||
return LG_TRANSPORT_INVALID_VERSION;
|
||||
this->connected = true;
|
||||
this->frameSerial = 0;
|
||||
this->formatValid = false;
|
||||
this->connected = true;
|
||||
this->frameScheduleSupported =
|
||||
session->features & LG_TRANSPORT_FEATURE_FRAME_SCHEDULE;
|
||||
this->frameSerial = 0;
|
||||
this->frameSerialValid = false;
|
||||
this->formatValid = false;
|
||||
return LG_TRANSPORT_OK;
|
||||
|
||||
case LGMP_ERR_INVALID_VERSION:
|
||||
@@ -392,8 +413,9 @@ static void lgmp_disconnect(LG_Transport * this)
|
||||
{
|
||||
lgmp_closeQueues(this);
|
||||
lgmp_closeDMA(this);
|
||||
this->connected = false;
|
||||
this->clientID = 0;
|
||||
this->connected = false;
|
||||
this->frameScheduleSupported = false;
|
||||
this->clientID = 0;
|
||||
}
|
||||
|
||||
static bool lgmp_sessionValid(LG_Transport * this)
|
||||
@@ -457,11 +479,119 @@ static LG_TransportStatus lgmp_process(PLGMPClientQueue queue,
|
||||
}
|
||||
}
|
||||
|
||||
struct LGMPFrameMessage
|
||||
{
|
||||
PLGMPClientQueue queue;
|
||||
LGMPMessage message;
|
||||
const KVMFRFrame * frame;
|
||||
bool owner;
|
||||
};
|
||||
|
||||
static LG_TransportStatus lgmp_pollFrameQueue(PLGMPClientQueue queue,
|
||||
bool owner, struct LGMPFrameMessage * result)
|
||||
{
|
||||
const LGMP_STATUS advance = lgmpClientAdvanceToLast(queue);
|
||||
switch (advance)
|
||||
{
|
||||
case LGMP_OK:
|
||||
break;
|
||||
case LGMP_ERR_QUEUE_EMPTY:
|
||||
break;
|
||||
case LGMP_ERR_INVALID_SESSION:
|
||||
return LG_TRANSPORT_DISCONNECTED;
|
||||
default:
|
||||
DEBUG_ERROR("lgmpClientAdvanceToLast failed: %s",
|
||||
lgmpStatusString(advance));
|
||||
return LG_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
const LG_TransportStatus status = lgmp_process(queue, 0, &result->message);
|
||||
if (status == LG_TRANSPORT_OK)
|
||||
{
|
||||
result->queue = queue;
|
||||
result->owner = owner || result->message.udata != 0;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static LG_TransportStatus lgmp_doneFrameMessage(
|
||||
struct LGMPFrameMessage * message)
|
||||
{
|
||||
if (!message || !message->queue)
|
||||
return LG_TRANSPORT_OK;
|
||||
|
||||
const LGMP_STATUS status = lgmpClientMessageDone(message->queue);
|
||||
message->queue = NULL;
|
||||
message->frame = NULL;
|
||||
|
||||
if (status == LGMP_OK)
|
||||
return LG_TRANSPORT_OK;
|
||||
if (status == LGMP_ERR_INVALID_SESSION)
|
||||
return LG_TRANSPORT_DISCONNECTED;
|
||||
|
||||
DEBUG_WARN("Failed to release discarded LGMP frame: %s",
|
||||
lgmpStatusString(status));
|
||||
return LG_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
static void lgmp_mergeFrameStatus(LG_TransportStatus status,
|
||||
LG_TransportStatus * result)
|
||||
{
|
||||
if (status != LG_TRANSPORT_OK &&
|
||||
(*result == LG_TRANSPORT_OK ||
|
||||
status == LG_TRANSPORT_DISCONNECTED))
|
||||
*result = status;
|
||||
}
|
||||
|
||||
static bool lgmp_validateFrameMessage(struct LGMPFrameMessage * message)
|
||||
{
|
||||
if (message->message.size < sizeof(KVMFRFrame))
|
||||
{
|
||||
DEBUG_ERROR("LGMP frame payload is too small");
|
||||
return false;
|
||||
}
|
||||
|
||||
const KVMFRFrame * frame = (const KVMFRFrame *)message->message.mem;
|
||||
const size_t frameDataSize = (size_t)frame->dataHeight * frame->pitch;
|
||||
if (frame->type <= FRAME_TYPE_INVALID || frame->type >= FRAME_TYPE_MAX ||
|
||||
frame->offset > message->message.size - sizeof(FrameBuffer) ||
|
||||
frameDataSize >
|
||||
message->message.size - frame->offset - sizeof(FrameBuffer))
|
||||
{
|
||||
DEBUG_ERROR("LGMP frame payload contains invalid dimensions or offsets");
|
||||
return false;
|
||||
}
|
||||
|
||||
message->frame = frame;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool lgmp_frameSerialNewer(uint32_t lhs, uint32_t rhs)
|
||||
{
|
||||
return lhs != rhs &&
|
||||
(uint32_t)(lhs - rhs) < UINT32_C(0x80000000);
|
||||
}
|
||||
|
||||
static void lgmp_selectNewestFrameMessage(
|
||||
struct LGMPFrameMessage * candidate,
|
||||
struct LGMPFrameMessage ** selected)
|
||||
{
|
||||
if (!candidate->frame)
|
||||
return;
|
||||
|
||||
if (!*selected ||
|
||||
lgmp_frameSerialNewer(candidate->frame->frameSerial,
|
||||
(*selected)->frame->frameSerial) ||
|
||||
(candidate->frame->frameSerial == (*selected)->frame->frameSerial &&
|
||||
candidate->owner && !(*selected)->owner))
|
||||
*selected = candidate;
|
||||
}
|
||||
|
||||
static int lgmp_getDMA(struct LG_Transport * this, const KVMFRFrame * frame,
|
||||
size_t dataSize)
|
||||
{
|
||||
struct DMAFrameInfo * dma = NULL;
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i)
|
||||
if (this->dma[i].frame == frame)
|
||||
{
|
||||
dma = &this->dma[i];
|
||||
@@ -474,7 +604,7 @@ static int lgmp_getDMA(struct LG_Transport * this, const KVMFRFrame * frame,
|
||||
}
|
||||
|
||||
if (!dma)
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i)
|
||||
if (!this->dma[i].frame)
|
||||
{
|
||||
dma = &this->dma[i];
|
||||
@@ -502,48 +632,144 @@ static LG_TransportStatus lgmp_nextFrame(LG_Transport * this, bool useDMA,
|
||||
if (this->framePending)
|
||||
return LG_TRANSPORT_ERROR;
|
||||
|
||||
LG_TransportStatus status = lgmp_subscribe(this->client, LGMP_Q_FRAME,
|
||||
&this->frameQueue);
|
||||
if (status != LG_TRANSPORT_OK)
|
||||
if (this->frameScheduleSupported)
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
{
|
||||
const LG_TransportStatus status = lgmp_subscribe(this->client,
|
||||
LGMP_Q_FRAME_OWNER + i, &this->ownerFrameQueue[i]);
|
||||
if (status != LG_TRANSPORT_OK && status != LG_TRANSPORT_TIMEOUT)
|
||||
return status;
|
||||
}
|
||||
|
||||
const LG_TransportStatus sharedSubscribe = lgmp_subscribe(this->client,
|
||||
LGMP_Q_FRAME, &this->frameQueue);
|
||||
if (sharedSubscribe != LG_TRANSPORT_OK &&
|
||||
sharedSubscribe != LG_TRANSPORT_TIMEOUT)
|
||||
return sharedSubscribe;
|
||||
|
||||
struct LGMPFrameMessage owner[LGMP_Q_FRAME_LEN] = {};
|
||||
struct LGMPFrameMessage shared = {};
|
||||
LG_TransportStatus sharedStatus = LG_TRANSPORT_TIMEOUT;
|
||||
LG_TransportStatus ownerStatus[LGMP_Q_FRAME_LEN];
|
||||
|
||||
/* Select the newest frame across the shared and owner delivery lanes. */
|
||||
if (this->frameQueue)
|
||||
sharedStatus = lgmp_pollFrameQueue(this->frameQueue, false, &shared);
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
{
|
||||
if (status == LG_TRANSPORT_TIMEOUT)
|
||||
usleep(1000);
|
||||
return status;
|
||||
ownerStatus[i] = LG_TRANSPORT_TIMEOUT;
|
||||
if (this->ownerFrameQueue[i])
|
||||
ownerStatus[i] = lgmp_pollFrameQueue(
|
||||
this->ownerFrameQueue[i], true, &owner[i]);
|
||||
}
|
||||
|
||||
LGMPMessage message;
|
||||
status = lgmp_process(this->frameQueue, this->framePollInterval, &message);
|
||||
if (status != LG_TRANSPORT_OK)
|
||||
return status;
|
||||
LG_TransportStatus failure = LG_TRANSPORT_OK;
|
||||
if (sharedStatus != LG_TRANSPORT_OK &&
|
||||
sharedStatus != LG_TRANSPORT_TIMEOUT)
|
||||
failure = sharedStatus;
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
if (ownerStatus[i] != LG_TRANSPORT_OK &&
|
||||
ownerStatus[i] != LG_TRANSPORT_TIMEOUT &&
|
||||
(failure == LG_TRANSPORT_OK ||
|
||||
ownerStatus[i] == LG_TRANSPORT_DISCONNECTED))
|
||||
failure = ownerStatus[i];
|
||||
|
||||
if (message.size < sizeof(KVMFRFrame))
|
||||
if (failure != LG_TRANSPORT_OK)
|
||||
{
|
||||
lgmpClientMessageDone(this->frameQueue);
|
||||
DEBUG_ERROR("LGMP frame payload is too small");
|
||||
return LG_TRANSPORT_ERROR;
|
||||
lgmp_mergeFrameStatus(
|
||||
lgmp_doneFrameMessage(&shared), &failure);
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
lgmp_mergeFrameStatus(
|
||||
lgmp_doneFrameMessage(&owner[i]), &failure);
|
||||
return failure;
|
||||
}
|
||||
|
||||
const KVMFRFrame * frame = (const KVMFRFrame *)message.mem;
|
||||
const size_t frameDataSize = (size_t)frame->dataHeight * frame->pitch;
|
||||
if (frame->type <= FRAME_TYPE_INVALID || frame->type >= FRAME_TYPE_MAX ||
|
||||
frame->offset > message.size - sizeof(FrameBuffer) ||
|
||||
frameDataSize > message.size - frame->offset - sizeof(FrameBuffer))
|
||||
bool malformed = false;
|
||||
LG_TransportStatus releaseFailure = LG_TRANSPORT_OK;
|
||||
if (sharedStatus == LG_TRANSPORT_OK &&
|
||||
!lgmp_validateFrameMessage(&shared))
|
||||
{
|
||||
lgmpClientMessageDone(this->frameQueue);
|
||||
DEBUG_ERROR("LGMP frame payload contains invalid dimensions or offsets");
|
||||
return LG_TRANSPORT_ERROR;
|
||||
malformed = true;
|
||||
releaseFailure = lgmp_doneFrameMessage(&shared);
|
||||
}
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
if (ownerStatus[i] == LG_TRANSPORT_OK &&
|
||||
!lgmp_validateFrameMessage(&owner[i]))
|
||||
{
|
||||
malformed = true;
|
||||
const LG_TransportStatus done =
|
||||
lgmp_doneFrameMessage(&owner[i]);
|
||||
lgmp_mergeFrameStatus(done, &releaseFailure);
|
||||
}
|
||||
|
||||
if (releaseFailure != LG_TRANSPORT_OK)
|
||||
{
|
||||
lgmp_mergeFrameStatus(
|
||||
lgmp_doneFrameMessage(&shared), &releaseFailure);
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
lgmp_mergeFrameStatus(
|
||||
lgmp_doneFrameMessage(&owner[i]), &releaseFailure);
|
||||
return releaseFailure;
|
||||
}
|
||||
|
||||
if (frame->frameSerial == this->frameSerial && this->frameSerial)
|
||||
struct LGMPFrameMessage * selected = NULL;
|
||||
lgmp_selectNewestFrameMessage(&shared, &selected);
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
lgmp_selectNewestFrameMessage(&owner[i], &selected);
|
||||
|
||||
if (!selected)
|
||||
{
|
||||
lgmpClientMessageDone(this->frameQueue);
|
||||
return LG_TRANSPORT_TIMEOUT;
|
||||
if (this->framePollInterval)
|
||||
usleep(this->framePollInterval);
|
||||
return malformed ? LG_TRANSPORT_ERROR : LG_TRANSPORT_TIMEOUT;
|
||||
}
|
||||
this->frameSerial = frame->frameSerial;
|
||||
|
||||
if (selected != &shared)
|
||||
releaseFailure = lgmp_doneFrameMessage(&shared);
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
if (selected != &owner[i])
|
||||
{
|
||||
const LG_TransportStatus done =
|
||||
lgmp_doneFrameMessage(&owner[i]);
|
||||
lgmp_mergeFrameStatus(done, &releaseFailure);
|
||||
}
|
||||
|
||||
if (releaseFailure != LG_TRANSPORT_OK)
|
||||
{
|
||||
lgmp_mergeFrameStatus(
|
||||
lgmp_doneFrameMessage(selected), &releaseFailure);
|
||||
return releaseFailure;
|
||||
}
|
||||
|
||||
const KVMFRFrame * frame = selected->frame;
|
||||
const bool equalSerial = this->frameSerialValid &&
|
||||
frame->frameSerial == this->frameSerial;
|
||||
if (this->frameSerialValid && !equalSerial &&
|
||||
!lgmp_frameSerialNewer(frame->frameSerial, this->frameSerial))
|
||||
{
|
||||
const LG_TransportStatus done = lgmp_doneFrameMessage(selected);
|
||||
return done == LG_TRANSPORT_OK ? LG_TRANSPORT_TIMEOUT : done;
|
||||
}
|
||||
if (equalSerial && !selected->owner)
|
||||
{
|
||||
const LG_TransportStatus done = lgmp_doneFrameMessage(selected);
|
||||
return done == LG_TRANSPORT_OK ? LG_TRANSPORT_TIMEOUT : done;
|
||||
}
|
||||
|
||||
const bool fullDamage = !this->frameSerialValid || equalSerial ||
|
||||
frame->frameSerial != this->frameSerial + 1;
|
||||
this->frameSerial = frame->frameSerial;
|
||||
this->frameSerialValid = true;
|
||||
|
||||
memset(result, 0, sizeof(*result));
|
||||
result->serial = frame->frameSerial;
|
||||
result->scheduleGeneration = message.udata;
|
||||
result->serial = frame->frameSerial;
|
||||
if (selected->owner)
|
||||
{
|
||||
const uint64_t scheduleToken = selected->message.udata;
|
||||
result->scheduleGeneration = (uint32_t)(scheduleToken >> 32);
|
||||
result->scheduleEpoch = (uint32_t)scheduleToken;
|
||||
result->scheduleOwner = true;
|
||||
}
|
||||
if (frame->flags & FRAME_FLAG_BLOCK_SCREENSAVER)
|
||||
result->flags |= LG_TRANSPORT_FRAME_BLOCK_SCREENSAVER;
|
||||
if (frame->flags & FRAME_FLAG_REQUEST_ACTIVATION)
|
||||
@@ -596,22 +822,24 @@ static LG_TransportStatus lgmp_nextFrame(LG_Transport * this, bool useDMA,
|
||||
result->dmaFD = lgmp_getDMA(this, frame, dataSize);
|
||||
if (result->dmaFD < 0)
|
||||
{
|
||||
lgmpClientMessageDone(this->frameQueue);
|
||||
const LG_TransportStatus done = lgmp_doneFrameMessage(selected);
|
||||
DEBUG_ERROR("Failed to obtain a DMA buffer for the frame");
|
||||
return LG_TRANSPORT_ERROR;
|
||||
return done == LG_TRANSPORT_DISCONNECTED ?
|
||||
done : LG_TRANSPORT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame->damageRectsCount <= KVMFR_MAX_DAMAGE_RECTS)
|
||||
if (!fullDamage && frame->damageRectsCount <= KVMFR_MAX_DAMAGE_RECTS)
|
||||
{
|
||||
result->damageRects = frame->damageRects;
|
||||
result->damageRectsCount = frame->damageRectsCount;
|
||||
}
|
||||
else
|
||||
else if (!fullDamage)
|
||||
DEBUG_WARN("Invalid damage rectangles, forcing a full update");
|
||||
|
||||
this->framePending = true;
|
||||
this->pendingFrame = frame;
|
||||
this->framePending = true;
|
||||
this->pendingFrame = frame;
|
||||
this->pendingFrameQueue = selected->queue;
|
||||
return LG_TRANSPORT_OK;
|
||||
}
|
||||
|
||||
@@ -652,10 +880,11 @@ static void lgmp_getFrameTiming(LG_Transport * this,
|
||||
|
||||
static void lgmp_releaseFrame(LG_Transport * this, LG_TransportFrame * frame)
|
||||
{
|
||||
if (this->framePending && this->frameQueue)
|
||||
lgmpClientMessageDone(this->frameQueue);
|
||||
this->framePending = false;
|
||||
this->pendingFrame = NULL;
|
||||
if (this->framePending && this->pendingFrameQueue)
|
||||
lgmpClientMessageDone(this->pendingFrameQueue);
|
||||
this->framePending = false;
|
||||
this->pendingFrame = NULL;
|
||||
this->pendingFrameQueue = NULL;
|
||||
memset(frame, 0, sizeof(*frame));
|
||||
}
|
||||
|
||||
@@ -721,7 +950,7 @@ static LG_TransportStatus lgmp_nextPointer(LG_Transport * this,
|
||||
if (status == LG_TRANSPORT_OK)
|
||||
{
|
||||
memcpy(this->pointerData, message.mem, needed);
|
||||
pointerFlags = message.udata;
|
||||
pointerFlags = (uint32_t)message.udata;
|
||||
}
|
||||
lgmpClientMessageDone(this->pointerQueue);
|
||||
}
|
||||
@@ -797,15 +1026,17 @@ static LG_TransportStatus lgmp_sendControl(LG_Transport * this,
|
||||
case LG_TRANSPORT_CONTROL_FRAME_SCHEDULE:
|
||||
{
|
||||
const KVMFRFrameSchedule message = {
|
||||
.msg.type = KVMFR_MESSAGE_FRAME_SCHEDULE,
|
||||
.clientID = this->clientID,
|
||||
.generation = control->frameSchedule.generation,
|
||||
.flags = control->frameSchedule.flags,
|
||||
.period = control->frameSchedule.period,
|
||||
.targetSlack = control->frameSchedule.targetSlack,
|
||||
.phaseError = control->frameSchedule.phaseError,
|
||||
.feedbackFrameSerial = control->frameSchedule.feedbackFrameSerial,
|
||||
.lease = control->frameSchedule.lease,
|
||||
.msg.type = KVMFR_MESSAGE_FRAME_SCHEDULE,
|
||||
.clientID = this->clientID,
|
||||
.generation = control->frameSchedule.generation,
|
||||
.flags = control->frameSchedule.flags,
|
||||
.period = control->frameSchedule.period,
|
||||
.targetSlack = control->frameSchedule.targetSlack,
|
||||
.phaseError = control->frameSchedule.phaseError,
|
||||
.feedbackFrameSerial = control->frameSchedule.feedbackFrameSerial,
|
||||
.feedbackScheduleEpoch =
|
||||
control->frameSchedule.feedbackScheduleEpoch,
|
||||
.lease = control->frameSchedule.lease,
|
||||
};
|
||||
memcpy(buffer, &message, sizeof(message));
|
||||
size = sizeof(message);
|
||||
|
||||
Reference in New Issue
Block a user