From 238c3cc8336fe24370a3ca44ff0d891ca8907c94 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 29 Jan 2024 22:28:22 +0000 Subject: [PATCH] vblank: reset SGI_video_sync scheduler if it's getting nonsense If resetting fails, set a flag so all future schedule calls will fail. Fixes #1168 Signed-off-by: Yuxuan Shui --- CHANGELOG.md | 4 ++++ src/vblank.c | 58 +++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e06f2106..a1108567 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ * Allow `corner-radius-rules` to override `corner-radius = 0`. Previously setting corner radius to 0 globally disables rounded corners. (#1170) +## Bug fixes + +* Workaround a NVIDIA problem that causes high CPU usage after suspend/resume (#1172, #1168) + # v11.1 (2024-Jan-28) ## Bug fixes diff --git a/src/vblank.c b/src/vblank.c index 6242c7a1..a9f82116 100644 --- a/src/vblank.c +++ b/src/vblank.c @@ -77,13 +77,14 @@ struct sgi_video_sync_vblank_scheduler { // Since glXWaitVideoSyncSGI blocks, we need to run it in a separate thread. // ... and all the thread shenanigans that come with it. - _Atomic unsigned int last_msc; - _Atomic uint64_t last_ust; + _Atomic unsigned int current_msc; + _Atomic uint64_t current_ust; ev_async notify; pthread_t sync_thread; bool running, error; + unsigned int last_msc; - /// Protects `running`, `error` and `base.vblank_event_requested` + /// Protects `running`, and `base.vblank_event_requested` pthread_mutex_t vblank_requested_mtx; pthread_cond_t vblank_requested_cnd; }; @@ -208,8 +209,8 @@ static void *sgi_video_sync_thread(void *data) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - atomic_store(&self->last_msc, last_msc); - atomic_store(&self->last_ust, + atomic_store(&self->current_msc, last_msc); + atomic_store(&self->current_ust, (uint64_t)(now.tv_sec * 1000000 + now.tv_nsec / 1000)); ev_async_send(self->base.loop, &self->notify); pthread_mutex_lock(&self->vblank_requested_mtx); @@ -242,7 +243,10 @@ cleanup: static bool sgi_video_sync_scheduler_schedule(struct vblank_scheduler *base) { auto self = (struct sgi_video_sync_vblank_scheduler *)base; - log_verbose("Requesting vblank event for msc %d", self->last_msc + 1); + if (self->error) { + return false; + } + log_verbose("Requesting vblank event for msc %d", self->current_msc + 1); pthread_mutex_lock(&self->vblank_requested_mtx); assert(!base->vblank_event_requested); base->vblank_event_requested = true; @@ -252,16 +256,7 @@ static bool sgi_video_sync_scheduler_schedule(struct vblank_scheduler *base) { } static void -sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents) { - auto sched = container_of(w, struct sgi_video_sync_vblank_scheduler, notify); - auto event = (struct vblank_event){ - .msc = atomic_load(&sched->last_msc), - .ust = atomic_load(&sched->last_ust), - }; - sched->base.vblank_event_requested = false; - log_verbose("Received vblank event for msc %lu", event.msc); - vblank_scheduler_invoke_callbacks(&sched->base, &event); -} +sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents); static bool sgi_video_sync_scheduler_init(struct vblank_scheduler *base) { auto self = (struct sgi_video_sync_vblank_scheduler *)base; @@ -293,6 +288,8 @@ static bool sgi_video_sync_scheduler_init(struct vblank_scheduler *base) { } else { log_info("Started sgi_video_sync_thread"); } + self->error = !succeeded; + self->last_msc = 0; pthread_mutex_destroy(&args.start_mtx); pthread_cond_destroy(&args.start_cnd); return succeeded; @@ -311,6 +308,35 @@ static void sgi_video_sync_scheduler_deinit(struct vblank_scheduler *base) { pthread_mutex_destroy(&self->vblank_requested_mtx); pthread_cond_destroy(&self->vblank_requested_cnd); } + +static void +sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents) { + auto sched = container_of(w, struct sgi_video_sync_vblank_scheduler, notify); + auto msc = atomic_load(&sched->current_msc); + if (sched->last_msc == msc) { + // NVIDIA spams us with duplicate vblank events after a suspend/resume + // cycle. Recreating the X connection and GLX context seems to fix this. + // Oh NVIDIA. + log_warn("Duplicate vblank event found with msc %d. Possible NVIDIA bug?", msc); + log_warn("Resetting the vblank scheduler"); + sgi_video_sync_scheduler_deinit(&sched->base); + sched->base.vblank_event_requested = false; + if (!sgi_video_sync_scheduler_init(&sched->base)) { + log_error("Failed to reset the vblank scheduler"); + } else { + sgi_video_sync_scheduler_schedule(&sched->base); + } + return; + } + auto event = (struct vblank_event){ + .msc = msc, + .ust = atomic_load(&sched->current_ust), + }; + sched->base.vblank_event_requested = false; + sched->last_msc = msc; + log_verbose("Received vblank event for msc %lu", event.msc); + vblank_scheduler_invoke_callbacks(&sched->base, &event); +} #endif static bool present_vblank_scheduler_schedule(struct vblank_scheduler *base) {