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_LEASE_MS 1000U
|
||||||
#define FRAME_SCHEDULER_RENEW_NS 250000000ULL
|
#define FRAME_SCHEDULER_RENEW_NS 250000000ULL
|
||||||
#define FRAME_SCHEDULER_FEEDBACK_NS 50000000ULL
|
#define FRAME_SCHEDULER_FEEDBACK_NS 50000000ULL
|
||||||
|
#define FRAME_SCHEDULER_TICK_GRACE_NS 500000000ULL
|
||||||
#define FRAME_SCHEDULER_TARGET_SLACK_NS 1000000ULL
|
#define FRAME_SCHEDULER_TARGET_SLACK_NS 1000000ULL
|
||||||
#define FRAME_SCHEDULER_MIN_PERIOD_NS 2000000ULL
|
#define FRAME_SCHEDULER_MIN_PERIOD_NS 2000000ULL
|
||||||
#define FRAME_SCHEDULER_MAX_PERIOD_NS 1000000000ULL
|
#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,
|
void lgFrameSchedulerInit(LGFrameScheduler * scheduler, bool supported,
|
||||||
uint32_t clientID)
|
uint32_t clientID)
|
||||||
{
|
{
|
||||||
@@ -40,14 +56,30 @@ void lgFrameSchedulerInit(LGFrameScheduler * scheduler, bool supported,
|
|||||||
scheduler->clientID = clientID;
|
scheduler->clientID = clientID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lgFrameSchedulerSetPeriod(LGFrameScheduler * scheduler,
|
void lgFrameSchedulerSetActive(LGFrameScheduler * scheduler, bool active)
|
||||||
uint64_t period)
|
|
||||||
{
|
{
|
||||||
if (!scheduler->supported ||
|
if (!scheduler->supported || scheduler->sourceActive == active)
|
||||||
period < FRAME_SCHEDULER_MIN_PERIOD_NS ||
|
|
||||||
period > FRAME_SCHEDULER_MAX_PERIOD_NS)
|
|
||||||
return;
|
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;
|
bool reset = !scheduler->period;
|
||||||
if (!reset)
|
if (!reset)
|
||||||
{
|
{
|
||||||
@@ -63,12 +95,7 @@ void lgFrameSchedulerSetPeriod(LGFrameScheduler * scheduler,
|
|||||||
++scheduler->generation;
|
++scheduler->generation;
|
||||||
scheduler->resetPending = true;
|
scheduler->resetPending = true;
|
||||||
scheduler->immediatePending = true;
|
scheduler->immediatePending = true;
|
||||||
scheduler->phaseError = 0;
|
lgFrameSchedulerResetFeedback(scheduler);
|
||||||
scheduler->feedbackFrameSerial = 0;
|
|
||||||
scheduler->feedbackScheduleEpoch = 0;
|
|
||||||
scheduler->feedbackDeadlineSerial = 0;
|
|
||||||
scheduler->feedbackSamples = 0;
|
|
||||||
scheduler->feedbackDirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lgFrameSchedulerRequestImmediate(LGFrameScheduler * scheduler)
|
void lgFrameSchedulerRequestImmediate(LGFrameScheduler * scheduler)
|
||||||
@@ -145,9 +172,35 @@ void lgFrameSchedulerFeedback(LGFrameScheduler * scheduler,
|
|||||||
void lgFrameSchedulerUpdate(LGFrameScheduler * scheduler,
|
void lgFrameSchedulerUpdate(LGFrameScheduler * scheduler,
|
||||||
PLGMPClientQueue queue, uint64_t now)
|
PLGMPClientQueue queue, uint64_t now)
|
||||||
{
|
{
|
||||||
if (!scheduler->supported || !scheduler->period || !queue)
|
if (!scheduler->supported || !queue)
|
||||||
return;
|
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 ?
|
const uint64_t interval = scheduler->feedbackDirty ?
|
||||||
FRAME_SCHEDULER_FEEDBACK_NS : FRAME_SCHEDULER_RENEW_NS;
|
FRAME_SCHEDULER_FEEDBACK_NS : FRAME_SCHEDULER_RENEW_NS;
|
||||||
if (scheduler->active && !scheduler->resetPending &&
|
if (scheduler->active && !scheduler->resetPending &&
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
typedef struct LGFrameScheduler
|
typedef struct LGFrameScheduler
|
||||||
{
|
{
|
||||||
bool supported;
|
bool supported;
|
||||||
|
bool sourceActive;
|
||||||
bool active;
|
bool active;
|
||||||
bool resetPending;
|
bool resetPending;
|
||||||
bool immediatePending;
|
bool immediatePending;
|
||||||
@@ -37,6 +38,7 @@ typedef struct LGFrameScheduler
|
|||||||
uint32_t generation;
|
uint32_t generation;
|
||||||
uint64_t period;
|
uint64_t period;
|
||||||
uint64_t lastSend;
|
uint64_t lastSend;
|
||||||
|
uint64_t lastTick;
|
||||||
|
|
||||||
int64_t phaseError;
|
int64_t phaseError;
|
||||||
uint32_t feedbackFrameSerial;
|
uint32_t feedbackFrameSerial;
|
||||||
@@ -54,8 +56,9 @@ LGFrameScheduler;
|
|||||||
|
|
||||||
void lgFrameSchedulerInit(LGFrameScheduler * scheduler, bool supported,
|
void lgFrameSchedulerInit(LGFrameScheduler * scheduler, bool supported,
|
||||||
uint32_t clientID);
|
uint32_t clientID);
|
||||||
|
void lgFrameSchedulerSetActive(LGFrameScheduler * scheduler, bool active);
|
||||||
void lgFrameSchedulerSetPeriod(LGFrameScheduler * scheduler,
|
void lgFrameSchedulerSetPeriod(LGFrameScheduler * scheduler,
|
||||||
uint64_t period);
|
uint64_t period, uint64_t tickTime);
|
||||||
void lgFrameSchedulerRequestImmediate(LGFrameScheduler * scheduler);
|
void lgFrameSchedulerRequestImmediate(LGFrameScheduler * scheduler);
|
||||||
void lgFrameSchedulerObserveFrame(LGFrameScheduler * scheduler,
|
void lgFrameSchedulerObserveFrame(LGFrameScheduler * scheduler,
|
||||||
uint32_t frameSerial, uint32_t generation, uint32_t scheduleEpoch,
|
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;
|
bool frameSerialValid;
|
||||||
pthread_mutex_t pointerLock;
|
pthread_mutex_t pointerLock;
|
||||||
LGFrameScheduler frameScheduler;
|
LGFrameScheduler frameScheduler;
|
||||||
|
_Atomic(bool) sourceActive;
|
||||||
|
|
||||||
bool cursorMono;
|
bool cursorMono;
|
||||||
gs_texture_t * cursorTex;
|
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->cursorVer, 0);
|
||||||
atomic_store(&this->cursorTransformVer, 0);
|
atomic_store(&this->cursorTransformVer, 0);
|
||||||
atomic_store(&this->sdrWhiteLevel, KVMFR_SDR_WHITE_LEVEL_DEFAULT);
|
atomic_store(&this->sdrWhiteLevel, KVMFR_SDR_WHITE_LEVEL_DEFAULT);
|
||||||
|
atomic_store(&this->sourceActive, obs_source_active(context));
|
||||||
lgUpdate(this, settings);
|
lgUpdate(this, settings);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -723,6 +725,8 @@ static void * frameThread(void * data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&this->pointerLock);
|
pthread_mutex_lock(&this->pointerLock);
|
||||||
|
lgFrameSchedulerSetActive(&this->frameScheduler,
|
||||||
|
atomic_load_explicit(&this->sourceActive, memory_order_acquire));
|
||||||
lgFrameSchedulerUpdate(&this->frameScheduler,
|
lgFrameSchedulerUpdate(&this->frameScheduler,
|
||||||
this->pointerQueue, now);
|
this->pointerQueue, now);
|
||||||
pthread_mutex_unlock(&this->pointerLock);
|
pthread_mutex_unlock(&this->pointerLock);
|
||||||
@@ -970,7 +974,6 @@ static void lgUpdate(void * data, obs_data_t * settings)
|
|||||||
|
|
||||||
lgFrameSchedulerInit(&this->frameScheduler,
|
lgFrameSchedulerInit(&this->frameScheduler,
|
||||||
udata->features & KVMFR_FEATURE_FRAME_SCHEDULE, clientID);
|
udata->features & KVMFR_FEATURE_FRAME_SCHEDULE, clientID);
|
||||||
lgFrameSchedulerSetPeriod(&this->frameScheduler, lgFramePeriod());
|
|
||||||
|
|
||||||
this->state = STATE_STARTING;
|
this->state = STATE_STARTING;
|
||||||
createThreads(this);
|
createThreads(this);
|
||||||
@@ -1443,7 +1446,12 @@ static void lgVideoTick(void * data, float seconds)
|
|||||||
return;
|
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.x = this->cursor.x;
|
||||||
this->cursorRect.y = this->cursor.y;
|
this->cursorRect.y = this->cursor.y;
|
||||||
@@ -1724,6 +1732,20 @@ static void lgVideoTick(void * data, float seconds)
|
|||||||
obs_leave_graphics();
|
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)
|
static void lgSetupCursorEffect(LGPlugin * this, int outputTransfer)
|
||||||
{
|
{
|
||||||
const KVMFRColorTransform * transform = &this->activeCursorTransform;
|
const KVMFRColorTransform * transform = &this->activeCursorTransform;
|
||||||
@@ -1916,6 +1938,8 @@ struct obs_source_info lg_source =
|
|||||||
.update = lgUpdate,
|
.update = lgUpdate,
|
||||||
.get_defaults = lgGetDefaults,
|
.get_defaults = lgGetDefaults,
|
||||||
.get_properties = lgGetProperties,
|
.get_properties = lgGetProperties,
|
||||||
|
.activate = lgActivate,
|
||||||
|
.deactivate = lgDeactivate,
|
||||||
.video_tick = lgVideoTick,
|
.video_tick = lgVideoTick,
|
||||||
.video_render = lgVideoRender,
|
.video_render = lgVideoRender,
|
||||||
#if LIBOBS_API_MAJOR_VER >= 28
|
#if LIBOBS_API_MAJOR_VER >= 28
|
||||||
|
|||||||
Reference in New Issue
Block a user