xwayland/present: Run fallback timer callback after more than a second

If the Wayland compositor doesn't send a pending frame event, e.g.
because the Wayland surface isn't visible anywhere, it could happen that
the timer kept getting pushed back and never fired. This resulted in an
enormous list of pending vblank events, which could take minutes to
process when the frame event finally arrived.

Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1110
Reviewed-by: Olivier Fourdan <ofourdan@redhat.com>
Tested-by: Jaap Buurman <jaapbuurman@gmail.com>
This commit is contained in:
Michel Dänzer 2021-12-24 19:06:47 +01:00 committed by Michel Dänzer
parent 38291fa86c
commit 288ec0e046
2 changed files with 26 additions and 5 deletions

View File

@ -93,6 +93,7 @@ xwl_present_free_timer(struct xwl_present_window *xwl_present_window)
{
TimerFree(xwl_present_window->frame_timer);
xwl_present_window->frame_timer = NULL;
xwl_present_window->timer_armed = 0;
}
static CARD32
@ -130,6 +131,7 @@ static void
xwl_present_reset_timer(struct xwl_present_window *xwl_present_window)
{
if (xwl_present_has_pending_events(xwl_present_window)) {
CARD32 now = GetTimeInMillis();
CARD32 timeout;
if (!xorg_list_is_empty(&xwl_present_window->frame_callback_list))
@ -137,6 +139,21 @@ xwl_present_reset_timer(struct xwl_present_window *xwl_present_window)
else
timeout = TIMER_LEN_COPY;
/* Make sure the timer callback runs if at least a second has passed
* since we first armed the timer. This can happen e.g. if the Wayland
* compositor doesn't send a pending frame event, e.g. because the
* Wayland surface isn't visible anywhere.
*/
if (xwl_present_window->timer_armed) {
if ((int)(now - xwl_present_window->timer_armed) > 1000) {
xwl_present_timer_callback(xwl_present_window->frame_timer, now,
xwl_present_window);
return;
}
} else {
xwl_present_window->timer_armed = now;
}
xwl_present_window->frame_timer = TimerSet(xwl_present_window->frame_timer,
0, timeout,
&xwl_present_timer_callback,
@ -386,6 +403,8 @@ xwl_present_msc_bump(struct xwl_present_window *xwl_present_window)
xwl_present_window->ust = GetTimeInMicros();
xwl_present_window->timer_armed = 0;
if (flip_pending && flip_pending->sync_flip)
xwl_present_flip_notify_vblank(flip_pending, xwl_present_window->ust, msc);
@ -801,14 +820,14 @@ xwl_present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
present_execute_copy(vblank, crtc_msc);
assert(!vblank->queued);
/* Clear the pixmap field, so this will fall through to present_execute_post next time */
dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
vblank->pixmap = NULL;
if (xwl_present_queue_vblank(screen, window, vblank->crtc,
vblank->event_id, crtc_msc + 1)
== Success) {
/* Clear the pixmap field, so this will fall through to present_execute_post next time */
dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
vblank->pixmap = NULL;
== Success)
return;
}
}
present_execute_post(vblank, ust, crtc_msc);

View File

@ -41,6 +41,8 @@ struct xwl_present_window {
uint64_t ust;
OsTimerPtr frame_timer;
/* Timestamp when the current timer was first armed */
CARD32 timer_armed;
struct wl_callback *sync_callback;