Fix race condition between ConfigureNotify and PresentCompleteNotify

* compositor.h: Update prototypes.
* frame_clock.c (struct _FrameClock): New fields for keeping
track of configure events.
(FreezeForValue): If the freeze is outdated, bring the counter
up to date now.
(XLFrameClockHandleFrameEvent): Set the number of configure
events that should have been considered by the frame clock upon
processing.
(XLFrameClockNoteConfigure): New function.
* picture_renderer.c (SwapBackBuffers): Remove outdated comment.
* xdg_surface.c (NoteConfigure, XLXdgRoleReconstrain):
* xdg_toplevel.c (NoteWindowResized): Note frame clock
configure.
This commit is contained in:
hujianwei 2022-11-01 01:25:42 +00:00
parent da212fe729
commit 743d363ad2
5 changed files with 61 additions and 3 deletions

View file

@ -1288,6 +1288,7 @@ extern void XLFrameClockDisablePredictRefresh (FrameClock *);
extern void XLFrameClockSetFreezeCallback (FrameClock *, void (*) (void *), extern void XLFrameClockSetFreezeCallback (FrameClock *, void (*) (void *),
void *); void *);
extern uint64_t XLFrameClockGetFrameTime (FrameClock *); extern uint64_t XLFrameClockGetFrameTime (FrameClock *);
extern void XLFrameClockNoteConfigure (FrameClock *);
extern void *XLAddCursorClockCallback (void (*) (void *, struct timespec), extern void *XLAddCursorClockCallback (void (*) (void *, struct timespec),
void *); void *);
extern void XLStopCursorClockCallback (void *); extern void XLStopCursorClockCallback (void *);

View file

@ -109,6 +109,11 @@ struct _FrameClock
/* The delay between the start of vblank and the redraw point. */ /* The delay between the start of vblank and the redraw point. */
uint32_t frame_delay; uint32_t frame_delay;
/* The number of configure events received affecting freeze, and the
number of configure events that should be received after a freeze
is put in place. */
uint32_t got_configure_count, pending_configure_count;
/* Whether or not configury is in progress, and whether or not this /* Whether or not configury is in progress, and whether or not this
is frozen, and whether or not the frame shouldn't actually be is frozen, and whether or not the frame shouldn't actually be
unfrozen until EndFrame. */ unfrozen until EndFrame. */
@ -239,18 +244,31 @@ HandleEndFrame (Timer *timer, void *data, struct timespec time)
EndFrame (clock); EndFrame (clock);
} }
/* Forward declaration. */ /* Forward declarations. */
static void RunFrameCallbacks (FrameClock *); static void RunFrameCallbacks (FrameClock *);
static Bool StartFrame (FrameClock *, Bool, Bool);
static void static void
FreezeForValue (FrameClock *clock, uint64_t counter_value) FreezeForValue (FrameClock *clock, uint64_t counter_value)
{ {
Bool need_empty_frame;
/* If it took too long (1 second at 60fps) to obtain the counter /* If it took too long (1 second at 60fps) to obtain the counter
value, and said value is now out of date, don't do anything. */ value, and said value is now out of date, don't do anything. */
if (clock->next_frame_id > counter_value) if (clock->next_frame_id > counter_value)
return; return;
need_empty_frame = False;
/* If ending a frame waits for PresentCompleteNotify, then the
configure event after this freeze may have been put into effect
by the time the freeze itself. Start a new frame to bring up to
date contents to the display. */
if (clock->pending_configure_count <= clock->got_configure_count)
need_empty_frame = True;
/* The frame clock is now frozen, and we will have to wait for a /* The frame clock is now frozen, and we will have to wait for a
client to ack_configure and then commit something. */ client to ack_configure and then commit something. */
@ -278,7 +296,20 @@ FreezeForValue (FrameClock *clock, uint64_t counter_value)
clock->in_frame = False; clock->in_frame = False;
clock->need_configure = True; clock->need_configure = True;
clock->configure_id = counter_value; clock->configure_id = counter_value;
if (need_empty_frame)
{
/* Request a new frame and don't allow starting frames until it
finishes. See above for why. clock->in_frame is False for
now to really force the frame to happen. */
StartFrame (clock, True, False);
EndFrame (clock);
}
else
clock->frozen = True; clock->frozen = True;
return;
} }
static void static void
@ -408,6 +439,10 @@ StartFrame (FrameClock *clock, Bool urgent, Bool predict)
{ {
clock->next_frame_id = clock->configure_id; clock->next_frame_id = clock->configure_id;
clock->finished_frame_id = 0; clock->finished_frame_id = 0;
/* Don't start the end frame timer if this frame is being drawn
in response to configury. */
predict = True;
} }
clock->in_frame = True; clock->in_frame = True;
@ -700,6 +735,11 @@ XLFrameClockHandleFrameEvent (FrameClock *clock, XEvent *event)
if (value % 2) if (value % 2)
value += 1; value += 1;
/* Set the number of configure events that should be received by
the time the freeze is put into effect. */
clock->pending_configure_count
= clock->got_configure_count + 1;
/* If a frame is in progress, postpone this frame /* If a frame is in progress, postpone this frame
synchronization message. */ synchronization message. */
if (clock->in_frame && !clock->end_frame_called) if (clock->in_frame && !clock->end_frame_called)
@ -861,6 +901,13 @@ XLFrameClockGetFrameTime (FrameClock *clock)
return clock->last_frame_time; return clock->last_frame_time;
} }
void
XLFrameClockNoteConfigure (FrameClock *clock)
{
/* This value is to track resize event validity. */
clock->got_configure_count += 1;
}
/* Cursor animation clock-related functions. */ /* Cursor animation clock-related functions. */

View file

@ -804,7 +804,6 @@ SwapBackBuffers (PictureTarget *target, pixman_region32_t *damage)
if (!present_serial) if (!present_serial)
present_serial++; present_serial++;
/* TODO: handle completion correctly. */
XPresentPixmap (compositor.display, target->window, XPresentPixmap (compositor.display, target->window,
back_buffer->pixmap, present_serial, back_buffer->pixmap, present_serial,
None, region, 0, 0, None, None, fence, None, region, 0, 0, None, None, fence,

View file

@ -1064,6 +1064,11 @@ NoteConfigure (XdgRole *role, XEvent *event)
event->xconfigure.y); event->xconfigure.y);
} }
/* Tell the frame clock how many WM-generated configure events have
arrived. */
XLFrameClockNoteConfigure (role->clock);
/* Run reconstrain callbacks. */
RunReconstrainCallbacksForXEvent (role, event); RunReconstrainCallbacksForXEvent (role, event);
} }
@ -1956,6 +1961,10 @@ XLXdgRoleReconstrain (Role *role, XEvent *event)
xdg_role = XdgRoleFromRole (role); xdg_role = XdgRoleFromRole (role);
RunReconstrainCallbacksForXEvent (xdg_role, event); RunReconstrainCallbacksForXEvent (xdg_role, event);
/* If event is a configure event, tell the frame clock about it. */
if (event->type == ConfigureNotify)
XLFrameClockNoteConfigure (xdg_role->clock);
} }
void void

View file

@ -1660,6 +1660,8 @@ NoteWindowResized (Role *role, XdgRoleImplementation *impl,
if (event.xconfigure.send_event) if (event.xconfigure.send_event)
XLXdgRoleNoteConfigure (toplevel->role, &event); XLXdgRoleNoteConfigure (toplevel->role, &event);
else
XLXdgRoleReconstrain (toplevel->role, &event);
RecordStateSize (toplevel); RecordStateSize (toplevel);
} }