mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[obs] scheduler: release ownership when source is idle
Renew frame scheduling only while the source is active and OBS is delivering video ticks. Release ownership after deactivation or a stale tick stream. Reacquire with a new generation and an immediate frame when ticking resumes.
This commit is contained in:
@@ -27,10 +27,26 @@
|
||||
#define FRAME_SCHEDULER_LEASE_MS 1000U
|
||||
#define FRAME_SCHEDULER_RENEW_NS 250000000ULL
|
||||
#define FRAME_SCHEDULER_FEEDBACK_NS 50000000ULL
|
||||
#define FRAME_SCHEDULER_TICK_GRACE_NS 500000000ULL
|
||||
#define FRAME_SCHEDULER_TARGET_SLACK_NS 1000000ULL
|
||||
#define FRAME_SCHEDULER_MIN_PERIOD_NS 2000000ULL
|
||||
#define FRAME_SCHEDULER_MAX_PERIOD_NS 1000000000ULL
|
||||
|
||||
static void lgFrameSchedulerResetFeedback(LGFrameScheduler * scheduler)
|
||||
{
|
||||
scheduler->phaseError = 0;
|
||||
scheduler->feedbackFrameSerial = 0;
|
||||
scheduler->feedbackScheduleEpoch = 0;
|
||||
scheduler->feedbackDeadlineSerial = 0;
|
||||
scheduler->feedbackSamples = 0;
|
||||
scheduler->feedbackDirty = false;
|
||||
scheduler->readyFrameSerial = 0;
|
||||
scheduler->readyGeneration = 0;
|
||||
scheduler->readyScheduleEpoch = 0;
|
||||
scheduler->readyDeadlineSerial = 0;
|
||||
scheduler->readyTime = 0;
|
||||
}
|
||||
|
||||
void lgFrameSchedulerInit(LGFrameScheduler * scheduler, bool supported,
|
||||
uint32_t clientID)
|
||||
{
|
||||
@@ -40,14 +56,30 @@ void lgFrameSchedulerInit(LGFrameScheduler * scheduler, bool supported,
|
||||
scheduler->clientID = clientID;
|
||||
}
|
||||
|
||||
void lgFrameSchedulerSetPeriod(LGFrameScheduler * scheduler,
|
||||
uint64_t period)
|
||||
void lgFrameSchedulerSetActive(LGFrameScheduler * scheduler, bool active)
|
||||
{
|
||||
if (!scheduler->supported ||
|
||||
period < FRAME_SCHEDULER_MIN_PERIOD_NS ||
|
||||
period > FRAME_SCHEDULER_MAX_PERIOD_NS)
|
||||
if (!scheduler->supported || scheduler->sourceActive == active)
|
||||
return;
|
||||
|
||||
scheduler->sourceActive = active;
|
||||
if (!active)
|
||||
scheduler->lastTick = 0;
|
||||
}
|
||||
|
||||
void lgFrameSchedulerSetPeriod(LGFrameScheduler * scheduler,
|
||||
uint64_t period, uint64_t tickTime)
|
||||
{
|
||||
if (!scheduler->supported)
|
||||
return;
|
||||
|
||||
scheduler->lastTick = tickTime;
|
||||
if (period < FRAME_SCHEDULER_MIN_PERIOD_NS ||
|
||||
period > FRAME_SCHEDULER_MAX_PERIOD_NS)
|
||||
{
|
||||
scheduler->period = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
bool reset = !scheduler->period;
|
||||
if (!reset)
|
||||
{
|
||||
@@ -63,12 +95,7 @@ void lgFrameSchedulerSetPeriod(LGFrameScheduler * scheduler,
|
||||
++scheduler->generation;
|
||||
scheduler->resetPending = true;
|
||||
scheduler->immediatePending = true;
|
||||
scheduler->phaseError = 0;
|
||||
scheduler->feedbackFrameSerial = 0;
|
||||
scheduler->feedbackScheduleEpoch = 0;
|
||||
scheduler->feedbackDeadlineSerial = 0;
|
||||
scheduler->feedbackSamples = 0;
|
||||
scheduler->feedbackDirty = false;
|
||||
lgFrameSchedulerResetFeedback(scheduler);
|
||||
}
|
||||
|
||||
void lgFrameSchedulerRequestImmediate(LGFrameScheduler * scheduler)
|
||||
@@ -145,9 +172,35 @@ void lgFrameSchedulerFeedback(LGFrameScheduler * scheduler,
|
||||
void lgFrameSchedulerUpdate(LGFrameScheduler * scheduler,
|
||||
PLGMPClientQueue queue, uint64_t now)
|
||||
{
|
||||
if (!scheduler->supported || !scheduler->period || !queue)
|
||||
if (!scheduler->supported || !queue)
|
||||
return;
|
||||
|
||||
const bool tickStale = !scheduler->lastTick ||
|
||||
now - scheduler->lastTick > FRAME_SCHEDULER_TICK_GRACE_NS;
|
||||
if (!scheduler->sourceActive || tickStale || !scheduler->period)
|
||||
{
|
||||
if (!scheduler->active)
|
||||
return;
|
||||
|
||||
const KVMFRFrameSchedule message = {
|
||||
.msg.type = KVMFR_MESSAGE_FRAME_SCHEDULE,
|
||||
.clientID = scheduler->clientID,
|
||||
.generation = scheduler->generation,
|
||||
.flags = KVMFR_FRAME_SCHEDULE_RELEASE,
|
||||
};
|
||||
|
||||
if (lgmpClientSendData(queue, &message, sizeof(message), NULL) != LGMP_OK)
|
||||
return;
|
||||
|
||||
scheduler->active = false;
|
||||
scheduler->resetPending = false;
|
||||
scheduler->immediatePending = true;
|
||||
scheduler->period = 0;
|
||||
scheduler->lastSend = now;
|
||||
lgFrameSchedulerResetFeedback(scheduler);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t interval = scheduler->feedbackDirty ?
|
||||
FRAME_SCHEDULER_FEEDBACK_NS : FRAME_SCHEDULER_RENEW_NS;
|
||||
if (scheduler->active && !scheduler->resetPending &&
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
typedef struct LGFrameScheduler
|
||||
{
|
||||
bool supported;
|
||||
bool sourceActive;
|
||||
bool active;
|
||||
bool resetPending;
|
||||
bool immediatePending;
|
||||
@@ -37,6 +38,7 @@ typedef struct LGFrameScheduler
|
||||
uint32_t generation;
|
||||
uint64_t period;
|
||||
uint64_t lastSend;
|
||||
uint64_t lastTick;
|
||||
|
||||
int64_t phaseError;
|
||||
uint32_t feedbackFrameSerial;
|
||||
@@ -54,8 +56,9 @@ LGFrameScheduler;
|
||||
|
||||
void lgFrameSchedulerInit(LGFrameScheduler * scheduler, bool supported,
|
||||
uint32_t clientID);
|
||||
void lgFrameSchedulerSetActive(LGFrameScheduler * scheduler, bool active);
|
||||
void lgFrameSchedulerSetPeriod(LGFrameScheduler * scheduler,
|
||||
uint64_t period);
|
||||
uint64_t period, uint64_t tickTime);
|
||||
void lgFrameSchedulerRequestImmediate(LGFrameScheduler * scheduler);
|
||||
void lgFrameSchedulerObserveFrame(LGFrameScheduler * scheduler,
|
||||
uint32_t frameSerial, uint32_t generation, uint32_t scheduleEpoch,
|
||||
|
||||
28
obs/lg.c
28
obs/lg.c
@@ -135,6 +135,7 @@ typedef struct
|
||||
bool frameSerialValid;
|
||||
pthread_mutex_t pointerLock;
|
||||
LGFrameScheduler frameScheduler;
|
||||
_Atomic(bool) sourceActive;
|
||||
|
||||
bool cursorMono;
|
||||
gs_texture_t * cursorTex;
|
||||
@@ -437,6 +438,7 @@ static void * lgCreate(obs_data_t * settings, obs_source_t * context)
|
||||
atomic_store(&this->cursorVer, 0);
|
||||
atomic_store(&this->cursorTransformVer, 0);
|
||||
atomic_store(&this->sdrWhiteLevel, KVMFR_SDR_WHITE_LEVEL_DEFAULT);
|
||||
atomic_store(&this->sourceActive, obs_source_active(context));
|
||||
lgUpdate(this, settings);
|
||||
return this;
|
||||
}
|
||||
@@ -723,6 +725,8 @@ static void * frameThread(void * data)
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&this->pointerLock);
|
||||
lgFrameSchedulerSetActive(&this->frameScheduler,
|
||||
atomic_load_explicit(&this->sourceActive, memory_order_acquire));
|
||||
lgFrameSchedulerUpdate(&this->frameScheduler,
|
||||
this->pointerQueue, now);
|
||||
pthread_mutex_unlock(&this->pointerLock);
|
||||
@@ -970,7 +974,6 @@ static void lgUpdate(void * data, obs_data_t * settings)
|
||||
|
||||
lgFrameSchedulerInit(&this->frameScheduler,
|
||||
udata->features & KVMFR_FEATURE_FRAME_SCHEDULE, clientID);
|
||||
lgFrameSchedulerSetPeriod(&this->frameScheduler, lgFramePeriod());
|
||||
|
||||
this->state = STATE_STARTING;
|
||||
createThreads(this);
|
||||
@@ -1443,7 +1446,12 @@ static void lgVideoTick(void * data, float seconds)
|
||||
return;
|
||||
}
|
||||
|
||||
lgFrameSchedulerSetPeriod(&this->frameScheduler, framePeriod);
|
||||
const bool sourceActive = atomic_load_explicit(
|
||||
&this->sourceActive, memory_order_acquire);
|
||||
lgFrameSchedulerSetActive(&this->frameScheduler, sourceActive);
|
||||
if (sourceActive)
|
||||
lgFrameSchedulerSetPeriod(
|
||||
&this->frameScheduler, framePeriod, tickTime);
|
||||
|
||||
this->cursorRect.x = this->cursor.x;
|
||||
this->cursorRect.y = this->cursor.y;
|
||||
@@ -1724,6 +1732,20 @@ static void lgVideoTick(void * data, float seconds)
|
||||
obs_leave_graphics();
|
||||
}
|
||||
|
||||
static void lgActivate(void * data)
|
||||
{
|
||||
LGPlugin * this = (LGPlugin *)data;
|
||||
atomic_store_explicit(
|
||||
&this->sourceActive, true, memory_order_release);
|
||||
}
|
||||
|
||||
static void lgDeactivate(void * data)
|
||||
{
|
||||
LGPlugin * this = (LGPlugin *)data;
|
||||
atomic_store_explicit(
|
||||
&this->sourceActive, false, memory_order_release);
|
||||
}
|
||||
|
||||
static void lgSetupCursorEffect(LGPlugin * this, int outputTransfer)
|
||||
{
|
||||
const KVMFRColorTransform * transform = &this->activeCursorTransform;
|
||||
@@ -1916,6 +1938,8 @@ struct obs_source_info lg_source =
|
||||
.update = lgUpdate,
|
||||
.get_defaults = lgGetDefaults,
|
||||
.get_properties = lgGetProperties,
|
||||
.activate = lgActivate,
|
||||
.deactivate = lgDeactivate,
|
||||
.video_tick = lgVideoTick,
|
||||
.video_render = lgVideoRender,
|
||||
#if LIBOBS_API_MAJOR_VER >= 28
|
||||
|
||||
Reference in New Issue
Block a user