Skip to content

Commit

Permalink
vblank: reset SGI_video_sync scheduler if it's getting nonsense
Browse files Browse the repository at this point in the history
If resetting fails, set a flag so all future schedule calls will fail.

Fixes #1168

(cherry picked from commit 238c3cc)
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
  • Loading branch information
yshui committed Feb 13, 2024
1 parent fc1d1d4 commit 56d8874
Showing 1 changed file with 42 additions and 16 deletions.
58 changes: 42 additions & 16 deletions src/vblank.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,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;
};
Expand Down Expand Up @@ -207,8 +208,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);
Expand Down Expand Up @@ -241,7 +242,10 @@ static void *sgi_video_sync_thread(void *data) {

static void 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;
Expand All @@ -250,16 +254,7 @@ static void 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 void sgi_video_sync_scheduler_init(struct vblank_scheduler *base) {
auto self = (struct sgi_video_sync_vblank_scheduler *)base;
Expand Down Expand Up @@ -288,6 +283,8 @@ static void sgi_video_sync_scheduler_init(struct vblank_scheduler *base) {
args.start_status);
abort();
}
self->error = !succeeded;
self->last_msc = 0;
pthread_mutex_destroy(&args.start_mtx);
pthread_cond_destroy(&args.start_cnd);
log_info("Started sgi_video_sync_thread");
Expand All @@ -306,6 +303,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 void present_vblank_scheduler_schedule(struct vblank_scheduler *base) {
Expand Down

0 comments on commit 56d8874

Please sign in to comment.