diff --git a/obs/frame_scheduler.c b/obs/frame_scheduler.c index 0caa2478..4fb8508c 100644 --- a/obs/frame_scheduler.c +++ b/obs/frame_scheduler.c @@ -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 && diff --git a/obs/frame_scheduler.h b/obs/frame_scheduler.h index d7272343..08cc7f0e 100644 --- a/obs/frame_scheduler.h +++ b/obs/frame_scheduler.h @@ -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, diff --git a/obs/lg.c b/obs/lg.c index 8137bad3..1a291047 100644 --- a/obs/lg.c +++ b/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