Fix frame timing prediction algorithm

* compositor.h (struct _RoleFuncs): Replace subframe with
`subsurface_update'.
* dmabuf.c (ExistingModifier):
* fns.c (RebalanceBusfault): Pacify various compiler warnings.
* frame_clock.c (struct _FrameClock, PostEndFrame)
(XLFrameClockHandleFrameEvent, XLMakeFrameClockForWindow): Fix
presentation logic.  First, store absolute presentation
timestamps.  Secondly, use the frame_delay to adjust timestamps.
* icon_surface.c (Subframe): Delete function.
(SubsurfaceUpdate): New function.
(EndSubframe): Delete function.
(XLGetIconSurface): Change attached functions.
* picture_renderer.c (struct _PictureTarget, RecordBufferActivity)
(HandleActivityEvent, DestroyRenderTarget)
(FillBoxesWithTransparency, Composite, FinishRender)
(picture_render_funcs): Send roundtrip event only once, and
flush upon FinishRender.
* process.c (ProcessPendingDescriptions, RunProcess,
ProcessPoll): Don't block signals where unnecessary.
* seat.c (struct _Seat, CursorFromRole, ReleaseSeat, Subframe)
(EndSubframe, MakeCurrentCursor, HandleBind, MakeSeatForDevicePair)
(StartResizeTracking):
* subsurface.c (Subframe, EndSubframe, Commit, Teardown)
(GetSubsurface):
* surface.c (HandleScaleChanged):
* xdg_surface.c (CheckFrame, Commit, Subframe, SubsurfaceUpdate)
(EndSubframe, XLGetXdgSurface):
* xdg_toplevel.c (Unmap): Adjust for above changes, and fix some
compiler warnings.
This commit is contained in:
hujianwei 2022-10-24 03:45:57 +00:00
parent 491f1a6a1c
commit a63a7ebdd6
12 changed files with 254 additions and 341 deletions

View file

@ -1071,8 +1071,7 @@ struct _RoleFuncs
/* These are optional. */ /* These are optional. */
Bool (*early_commit) (Surface *, Role *); Bool (*early_commit) (Surface *, Role *);
Bool (*subframe) (Surface *, Role *); void (*subsurface_update) (Surface *, Role *);
void (*end_subframe) (Surface *, Role *);
Window (*get_window) (Surface *, Role *); Window (*get_window) (Surface *, Role *);
void (*get_resize_dimensions) (Surface *, Role *, int *, int *); void (*get_resize_dimensions) (Surface *, Role *, int *, int *);
void (*post_resize) (Surface *, Role *, int, int, int, int); void (*post_resize) (Surface *, Role *, int, int, int, int);

View file

@ -182,6 +182,12 @@ ExistingModifier (BufferParams *params, uint32_t *current_hi,
{ {
int i, count; int i, count;
/* Pacify -Wmaybe-uninitialized under -O2. count is only non-zero
if both return arguments are initialized, but GCC thinks
otherwise. */
*current_hi = 0;
*current_lo = 0;
for (i = 0, count = 0; i < ArrayElements (params->entries); ++i) for (i = 0, count = 0; i < ArrayElements (params->entries); ++i)
{ {
if (params->entries[i].fd != -1) if (params->entries[i].fd != -1)

4
fns.c
View file

@ -593,6 +593,8 @@ RebalanceBusfault (Busfault **tree)
RotateLeft (tree); RotateLeft (tree);
else else
{ {
XLAssert ((*tree)->left->right != NULL);
RotateRight (&(*tree)->left); RotateRight (&(*tree)->left);
RotateLeft (tree); RotateLeft (tree);
} }
@ -611,6 +613,8 @@ RebalanceBusfault (Busfault **tree)
RotateRight (tree); RotateRight (tree);
else else
{ {
XLAssert ((*tree)->right->left != NULL);
RotateLeft (&(*tree)->right); RotateLeft (&(*tree)->right);
RotateRight (tree); RotateRight (tree);
} }

View file

@ -30,10 +30,6 @@ enum
{ {
/* 150ms. */ /* 150ms. */
MaxPresentationAge = 150000, MaxPresentationAge = 150000,
/* 5000 microseconds. This arbitrary value is the longest it
normally takes for a sync counter update to be processed by the
compositor. */
PresentationThreshold = 5000,
}; };
/* Whether or not the compositor supports frame synchronization. */ /* Whether or not the compositor supports frame synchronization. */
@ -74,10 +70,6 @@ struct _FrameClock
and the value of the last frame that was marked as complete. */ and the value of the last frame that was marked as complete. */
uint64_t next_frame_id, finished_frame_id; uint64_t next_frame_id, finished_frame_id;
/* Whether or not we are waiting for a frame to be completely
painted. */
Bool in_frame;
/* A timer used as a fake synchronization source if frame /* A timer used as a fake synchronization source if frame
synchronization is not supported. */ synchronization is not supported. */
Timer *static_frame_timer; Timer *static_frame_timer;
@ -85,6 +77,38 @@ struct _FrameClock
/* A timer used to end the next frame. */ /* A timer used to end the next frame. */
Timer *end_frame_timer; Timer *end_frame_timer;
/* Callback run when the frame is frozen. */
void (*freeze_callback) (void *);
/* Data for that callback. */
void *freeze_callback_data;
/* The wanted configure value. */
uint64_t configure_id;
/* The time the last frame was drawn. */
uint64_t last_frame_time;
/* Any pending frame synchronization counter value, or 0. */
uint64_t pending_sync_value;
/* The last frame drawn for whom a _NET_WM_FRAME_TIMINGS message has
not yet arrived. */
uint64_t frame_timings_id;
/* The time the frame at frame_timings_id was drawn. Used to
compute the presentation time. */
uint64_t frame_timings_drawn_time;
/* The last known presentation time. */
uint64_t last_presentation_time;
/* The refresh interval. */
uint32_t refresh_interval;
/* The delay between the start of vblank and the redraw point. */
uint32_t frame_delay;
/* 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. */
@ -93,30 +117,13 @@ struct _FrameClock
/* Whether or not EndFrame was called after StartFrame. */ /* Whether or not EndFrame was called after StartFrame. */
Bool end_frame_called; Bool end_frame_called;
/* The wanted configure value. */ /* Whether or not we are waiting for a frame to be completely
uint64_t configure_id; painted. */
Bool in_frame;
/* The time the last frame was drawn. */
uint64_t last_frame_time;
/* The presentation time. */
int32_t presentation_time;
/* The refresh interval. */
uint32_t refresh_interval;
/* Whether or not this frame clock should try to predict /* Whether or not this frame clock should try to predict
presentation times, in order to group frames together. */ presentation times, in order to group frames together. */
Bool predict_refresh; Bool predict_refresh;
/* Callback run when the frame is frozen. */
void (*freeze_callback) (void *);
/* Data for that callback. */
void *freeze_callback_data;
/* Any pending frame synchronization counter value, or 0. */
uint64_t pending_sync_value;
}; };
struct _CursorClockCallback struct _CursorClockCallback
@ -283,17 +290,20 @@ PostEndFrame (FrameClock *clock)
XLAssert (clock->end_frame_timer == NULL); XLAssert (clock->end_frame_timer == NULL);
if (!clock->refresh_interval if (!clock->refresh_interval
|| !clock->presentation_time) || !clock->last_presentation_time)
return; return;
/* Obtain the monotonic clock time. */ /* Obtain the monotonic clock time. */
clock_gettime (CLOCK_MONOTONIC, &current_time); clock_gettime (CLOCK_MONOTONIC, &current_time);
/* Calculate the time by which the next frame must be drawn. It is /* target is now the time the last frame was presented. This is the
a multiple of the refresh rate with the vertical blanking end of a vertical blanking period. */
period added. */ target = clock->last_presentation_time;
target = clock->last_frame_time + clock->presentation_time;
/* now is the current time. */
now = HighPrecisionTimestamp (&current_time); now = HighPrecisionTimestamp (&current_time);
/* There is no additional offset to add to the time. */
additional = 0; additional = 0;
/* If now is more than UINT32_MAX * 1000, then this timestamp may /* If now is more than UINT32_MAX * 1000, then this timestamp may
@ -313,9 +323,9 @@ PostEndFrame (FrameClock *clock)
/* If the last time the frame time was obtained was that long ago, /* If the last time the frame time was obtained was that long ago,
return immediately. */ return immediately. */
if (now - clock->last_frame_time >= MaxPresentationAge) if (now - clock->last_presentation_time >= MaxPresentationAge)
{ {
if ((fallback - clock->last_frame_time) <= MaxPresentationAge) if ((fallback - clock->last_presentation_time) <= MaxPresentationAge)
{ {
/* Some compositors wrap around once the X server time /* Some compositors wrap around once the X server time
overflows the 32-bit Time type. If now happens to be overflows the 32-bit Time type. If now happens to be
@ -331,23 +341,21 @@ PostEndFrame (FrameClock *clock)
return; return;
} }
/* Keep adding the refresh interval until target becomes the
presentation time of a frame in the future. */
while (target < now) while (target < now)
{ {
if (IntAddWrapv (target, clock->refresh_interval, &target)) if (IntAddWrapv (target, clock->refresh_interval, &target))
return; return;
} }
/* Use 5000 microseconds before the presentation time, or 3/4th of /* The vertical blanking period itself can't actually be computed
it if it is less than 5000. Any more and we risk the counter based on available data. However, frame_delay must be inside the
value change signalling the end of the frame arriving after the vertical blanking period for it to make any sense, so use it to
presentation deadline. */ compute the deadline instead. Add about 100 us to the frame
if (clock->presentation_time > PresentationThreshold) delay to compensate for the roundtrip time. */
target = target - (clock->presentation_time - PresentationThreshold); target -= clock->frame_delay - 100;
else
/* However, if the presentation time is less than 5000
microseconds, use 3/4ths of it. This computation seems to be a
good enough fallback. */
target = target - (clock->presentation_time / 4 * 3);
/* Add the remainder of now if it was probably truncated by the /* Add the remainder of now if it was probably truncated by the
compositor. */ compositor. */
@ -627,22 +635,52 @@ XLFrameClockHandleFrameEvent (FrameClock *clock, XEvent *event)
/* Run any frame callbacks, since drawing has finished. */ /* Run any frame callbacks, since drawing has finished. */
clock->in_frame = False; clock->in_frame = False;
RunFrameCallbacks (clock); RunFrameCallbacks (clock);
if (clock->frame_timings_id == -1)
{
/* Wait for the frame's presentation time to arrive,
unless we are already waiting on a previous
frame. */
clock->frame_timings_id = value;
/* Also save the frame drawn time. */
clock->frame_timings_drawn_time = clock->last_frame_time;
}
} }
} }
if (event->xclient.message_type == _NET_WM_FRAME_TIMINGS) if (event->xclient.message_type == _NET_WM_FRAME_TIMINGS)
{ {
/* Check that the frame timings are up to date. */
low = event->xclient.data.l[0] & 0xffffffff;
high = event->xclient.data.l[1] & 0xffffffff;
value = low | (high << 32);
if (value != clock->frame_timings_id)
/* They are not. */
return;
/* The timings message has arrived, so clear
frame_timings_id. */
clock->frame_timings_id = -1;
/* And set the last known presentation time. */
clock->last_presentation_time = (clock->frame_timings_drawn_time
+ event->xclient.data.l[2]);
/* Save the presentation time and refresh interval. There is no /* Save the presentation time and refresh interval. There is no
need to mask these values, since they are being put into need to mask these values, since they are being put into
(u)int32_t. */ (u)int32_t. */
clock->presentation_time = event->xclient.data.l[2];
clock->refresh_interval = event->xclient.data.l[3]; clock->refresh_interval = event->xclient.data.l[3];
clock->frame_delay = event->xclient.data.l[4];
if (clock->refresh_interval & (1U << 31)) if (clock->refresh_interval & (1U << 31)
|| clock->frame_delay == 0x80000000)
{ {
/* This means frame timing information is unavailable. */ /* This means frame timing information is unavailable. */
clock->presentation_time = 0;
clock->refresh_interval = 0; clock->refresh_interval = 0;
clock->frame_delay = 0;
clock->last_presentation_time = 0;
} }
} }
@ -702,6 +740,9 @@ XLMakeFrameClockForWindow (Window window)
clock = XLCalloc (1, sizeof *clock); clock = XLCalloc (1, sizeof *clock);
clock->next_frame_id = 0; clock->next_frame_id = 0;
/* Set this to an invalid value. */
clock->frame_timings_id = -1;
XLOutputGetMinRefresh (&default_refresh_rate); XLOutputGetMinRefresh (&default_refresh_rate);
XSyncIntToValue (&initial_value, 0); XSyncIntToValue (&initial_value, 0);

View file

@ -342,8 +342,8 @@ Commit (Surface *surface, Role *role)
MaybeUnmapWindow (icon); MaybeUnmapWindow (icon);
} }
static Bool static void
Subframe (Surface *surface, Role *role) SubsurfaceUpdate (Surface *surface, Role *role)
{ {
IconSurface *icon; IconSurface *icon;
@ -354,20 +354,12 @@ Subframe (Surface *surface, Role *role)
/* A frame is already in progress; schedule another one for /* A frame is already in progress; schedule another one for
later. */ later. */
icon->state |= StateLateFrame; icon->state |= StateLateFrame;
return False; return;
} }
/* I guess subsurface updates don't count as urgent frames? */ /* I guess subsurface updates don't count as urgent frames? */
XLFrameClockStartFrame (icon->clock, False); XLFrameClockStartFrame (icon->clock, False);
return True; SubcompositorUpdate (icon->subcompositor);
}
static void
EndSubframe (Surface *surface, Role *role)
{
IconSurface *icon;
icon = IconSurfaceFromRole (role);
XLFrameClockEndFrame (icon->clock); XLFrameClockEndFrame (icon->clock);
} }
@ -394,8 +386,7 @@ XLGetIconSurface (Surface *surface)
role->role.funcs.teardown = Teardown; role->role.funcs.teardown = Teardown;
role->role.funcs.setup = Setup; role->role.funcs.setup = Setup;
role->role.funcs.release_buffer = ReleaseBuffer; role->role.funcs.release_buffer = ReleaseBuffer;
role->role.funcs.subframe = Subframe; role->role.funcs.subsurface_update = SubsurfaceUpdate;
role->role.funcs.end_subframe = EndSubframe;
role->role.funcs.get_window = GetWindow; role->role.funcs.get_window = GetWindow;
/* Make an override-redirect window to use as the icon surface. */ /* Make an override-redirect window to use as the icon surface. */

View file

@ -212,6 +212,9 @@ struct _PictureTarget
/* List of present completion callbacks. */ /* List of present completion callbacks. */
PresentCompletionCallback completion_callbacks; PresentCompletionCallback completion_callbacks;
/* List of buffers that were used in the course of an update. */
XLList *buffers_used;
}; };
struct _DrmFormatInfo struct _DrmFormatInfo
@ -485,14 +488,10 @@ FindBufferActivityRecord (PictureBuffer *buffer, PictureTarget *target)
/* Record buffer activity involving the given buffer and target. */ /* Record buffer activity involving the given buffer and target. */
static void static void
RecordBufferActivity (RenderBuffer src, RenderTarget dest) RecordBufferActivity (PictureBuffer *buffer, PictureTarget *target,
uint64_t roundtrip_id)
{ {
BufferActivityRecord *record; BufferActivityRecord *record;
PictureBuffer *buffer;
PictureTarget *target;
buffer = src.pointer;
target = dest.pointer;
/* Try to find an existing record. */ /* Try to find an existing record. */
record = FindBufferActivityRecord (buffer, target); record = FindBufferActivityRecord (buffer, target);
@ -531,7 +530,7 @@ RecordBufferActivity (RenderBuffer src, RenderTarget dest)
record->target = target; record->target = target;
} }
record->id = SendRoundtripMessage (); record->id = roundtrip_id;
} }
static void static void
@ -614,33 +613,29 @@ UnlinkActivityRecord (BufferActivityRecord *record)
static void static void
HandleActivityEvent (uint64_t counter) HandleActivityEvent (uint64_t counter)
{ {
BufferActivityRecord *record; BufferActivityRecord *record, *last;
/* Look through the global activity list for a record matching /* Look through the global activity list for a record matching
counter. */ counter. */
record = all_activity.global_next; record = all_activity.global_next;
while (record != &all_activity) while (record != &all_activity)
{ {
if (record->id == counter) last = record;
break;
record = record->global_next; record = record->global_next;
if (last->id == counter)
{
/* Remove the record. Then, run any callbacks pertaining to
it. This code mandates that there only be a single
activity record for each buffer-target combination on the
global list at any given time. */
UnlinkActivityRecord (last);
MaybeRunIdleCallbacks (last->buffer, last->target);
/* Free the record. */
XLFree (last);
}
} }
/* If record is all_activity (meaning no matching record was found),
return. */
if (record == &all_activity)
return;
/* Remove the record. Then, run any callbacks pertaining to it.
This code mandates that there only be a single activity record
for each buffer-target combination on the global list at any
given time. */
UnlinkActivityRecord (record);
MaybeRunIdleCallbacks (record->buffer, record->target);
/* Free the record. */
XLFree (record);
} }
@ -916,6 +911,10 @@ DestroyRenderTarget (RenderTarget target)
pict_target = target.pointer; pict_target = target.pointer;
/* Assert that there are no more buffers left in the active buffer
list. */
XLAssert (pict_target->buffers_used == NULL);
if (pict_target->presentation_window) if (pict_target->presentation_window)
{ {
XPresentFreeInput (compositor.display, XPresentFreeInput (compositor.display,
@ -994,6 +993,9 @@ FillBoxesWithTransparency (RenderTarget target, pixman_box32_t *boxes,
else else
rects = XLMalloc (sizeof *rects * nboxes); rects = XLMalloc (sizeof *rects * nboxes);
/* Pacify GCC. */
memset (rects, 0, sizeof *rects * nboxes);
for (i = 0; i < nboxes; ++i) for (i = 0; i < nboxes; ++i)
{ {
rects[i].x = BoxStartX (boxes[i]) - min_x; rects[i].x = BoxStartX (boxes[i]) - min_x;
@ -1189,8 +1191,41 @@ Composite (RenderBuffer buffer, RenderTarget target,
/* dst-x, dst-y, width, height. */ /* dst-x, dst-y, width, height. */
x, y, width, height); x, y, width, height);
/* Record pending buffer activity. */ /* Record pending buffer activity; the roundtrip message is then
RecordBufferActivity (buffer, target); sent later. */
picture_target->buffers_used
= XLListPrepend (picture_target->buffers_used, picture_buffer);
}
static void
FinishRender (RenderTarget target, pixman_region32_t *damage)
{
XLList *tem, *last;
PictureTarget *pict_target;
uint64_t roundtrip_id;
/* Finish rendering. This function then sends a single roundtrip
message and records buffer activity for each buffer involved in
the update based on that. */
roundtrip_id = SendRoundtripMessage ();
pict_target = target.pointer;
tem = pict_target->buffers_used;
while (tem)
{
last = tem;
tem = tem->next;
/* Record buffer activity on this one buffer. */
RecordBufferActivity (last->data, pict_target,
roundtrip_id);
/* Free the list element. */
XLFree (last);
}
/* Clear buffers_used. */
pict_target->buffers_used = NULL;
} }
static int static int
@ -1449,6 +1484,7 @@ static RenderFuncs picture_render_funcs =
.fill_boxes_with_transparency = FillBoxesWithTransparency, .fill_boxes_with_transparency = FillBoxesWithTransparency,
.clear_rectangle = ClearRectangle, .clear_rectangle = ClearRectangle,
.composite = Composite, .composite = Composite,
.finish_render = FinishRender,
.target_age = TargetAge, .target_age = TargetAge,
.import_fd_fence = ImportFdFence, .import_fd_fence = ImportFdFence,
.wait_fence = WaitFence, .wait_fence = WaitFence,

View file

@ -198,11 +198,12 @@ RunNext (ProcessQueue *queue)
} }
static void static void
ProcessPendingDescriptions (void) ProcessPendingDescriptions (Bool need_block)
{ {
ProcessQueue *queue; ProcessQueue *queue;
Block (NULL); if (need_block)
Block (NULL);
for (queue = all_queues; queue; queue = queue->next) for (queue = all_queues; queue; queue = queue->next)
{ {
@ -210,7 +211,8 @@ ProcessPendingDescriptions (void)
RunNext (queue); RunNext (queue);
} }
Unblock (); if (need_block)
Unblock ();
} }
static void static void
@ -373,7 +375,7 @@ RunProcess (ProcessQueue *queue, char **arguments)
queue->descriptions.next = desc; queue->descriptions.next = desc;
/* Process pending process descriptions. */ /* Process pending process descriptions. */
ProcessPendingDescriptions (); ProcessPendingDescriptions (True);
} }
ProcessQueue * ProcessQueue *
@ -405,7 +407,7 @@ ProcessPoll (struct pollfd *fds, nfds_t nfds,
be run by ProcessPendingDescriptions. If it arrives after, then be run by ProcessPendingDescriptions. If it arrives after, then
ppoll will be interrupted with EINTR. */ ppoll will be interrupted with EINTR. */
Block (&oldset); Block (&oldset);
ProcessPendingDescriptions (); ProcessPendingDescriptions (False);
rc = ppoll (fds, nfds, timeout, &oldset); rc = ppoll (fds, nfds, timeout, &oldset);
Unblock (); Unblock ();

139
seat.c
View file

@ -472,6 +472,9 @@ struct _Seat
/* The time of the last key event sent. */ /* The time of the last key event sent. */
Time its_depress_time; Time its_depress_time;
/* The name of the seat. */
char *name;
/* The grab surface. While it exists, events for different clients /* The grab surface. While it exists, events for different clients
will be reported relative to it. */ will be reported relative to it. */
Surface *grab_surface; Surface *grab_surface;
@ -584,12 +587,6 @@ static TextInputFuncs *input_funcs;
#define CursorFromRole(role) ((SeatCursor *) (role)) #define CursorFromRole(role) ((SeatCursor *) (role))
/* Subcompositor targets used inside cursor subframes. */
static RenderTarget cursor_subframe_target;
/* Its associated pixmap. */
static Pixmap cursor_subframe_pixmap;
static Bool static Bool
@ -1037,6 +1034,7 @@ ReleaseSeat (Seat *seat)
FreeDestroyListeners (seat); FreeDestroyListeners (seat);
FreeModifierCallbacks (seat); FreeModifierCallbacks (seat);
XLFree (seat->name);
XLFree (seat->key_pressed); XLFree (seat->key_pressed);
XLFree (seat); XLFree (seat);
} }
@ -1277,120 +1275,16 @@ ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer)
XLReleaseBuffer (buffer); XLReleaseBuffer (buffer);
} }
static Bool
Subframe (Surface *surface, Role *role)
{
RenderTarget target;
Pixmap pixmap;
int min_x, min_y, max_x, max_y, width, height, x, y;
int index;
Bool need_clear;
SeatCursor *cursor;
cursor = CursorFromRole (role);
if (SubcompositorIsEmpty (cursor->subcompositor))
/* The subcompositor is empty. Don't set up the cursor
pixmap. */
return False;
/* First, compute the bounds of the subcompositor. */
SubcompositorBounds (cursor->subcompositor,
&min_x, &min_y, &max_x, &max_y);
/* Then, its width and height. */
width = max_x - min_x + 1;
height = max_y - min_y + 1;
/* If the cursor hotspot extends outside width and height, extend
the picture. */
ComputeHotspot (cursor, min_x, min_y, &x, &y);
if (x < 0 || y < 0 || x >= width || y >= height)
{
if (x >= width)
width = x;
if (y >= width)
height = y;
if (x < 0)
width += -x;
if (y < 0)
height += -y;
need_clear = True;
}
else
need_clear = False;
if (cursor->cursor_ring)
/* If the width or height of the cursor ring changed, resize its
contents. */
ResizeCursorRing (cursor->cursor_ring, width, height);
else
/* Otherwise, there is not yet a cursor ring. Create one. */
cursor->cursor_ring = MakeCursorRing (width, height);
/* Get an unused cursor from the cursor ring. */
index = GetUnusedCursor (cursor->cursor_ring);
XLAssert (index != CursorRingBusy);
/* Set it as the cursor being used. */
cursor->cursor_ring->used = index;
/* Get the target and pixmap. */
target = cursor->cursor_ring->targets[index];
pixmap = cursor->cursor_ring->pixmaps[index];
/* If the bounds extend beyond the subcompositor, clear the
picture. */
if (need_clear)
RenderClearRectangle (target, 0, 0, width, height);
/* Garbage the subcompositor, since cursor contents are not
preserved. */
SubcompositorGarbage (cursor->subcompositor);
/* Set the right transform if the hotspot is negative. */
SubcompositorSetProjectiveTransform (cursor->subcompositor,
MAX (0, -x), MAX (0, -x));
/* Attach the rendering target. */
SubcompositorSetTarget (cursor->subcompositor, &target);
/* Set the subframe target and pixmap to the target and pixmap in
use. */
cursor_subframe_target = target;
cursor_subframe_pixmap = pixmap;
/* Return True to let the drawing proceed. */
return True;
}
static void static void
EndSubframe (Surface *surface, Role *role) SubsurfaceUpdate (Surface *surface, Role *role)
{ {
SeatCursor *cursor; SeatCursor *cursor;
int min_x, min_y, max_x, max_y;
cursor = CursorFromRole (role); cursor = CursorFromRole (role);
if (cursor_subframe_pixmap != None) /* A desync subsurface's contents changed. Update the cursor
{ again. */
/* First, compute the bounds of the subcompositor. */ UpdateCursorFromSubcompositor (cursor);
SubcompositorBounds (cursor->subcompositor,
&min_x, &min_y, &max_x, &max_y);
/* Apply the cursor. */
ApplyCursor (cursor, cursor_subframe_target, min_x, min_y);
/* Finally, clear the target. */
SubcompositorSetTarget (cursor->subcompositor, NULL);
}
else
ApplyEmptyCursor (cursor);
cursor_subframe_pixmap = None;
} }
static void static void
@ -1422,8 +1316,7 @@ MakeCurrentCursor (Seat *seat, Surface *surface, int x, int y)
role->role.funcs.teardown = Teardown; role->role.funcs.teardown = Teardown;
role->role.funcs.setup = Setup; role->role.funcs.setup = Setup;
role->role.funcs.release_buffer = ReleaseBuffer; role->role.funcs.release_buffer = ReleaseBuffer;
role->role.funcs.subframe = Subframe; role->role.funcs.subsurface_update = SubsurfaceUpdate;
role->role.funcs.end_subframe = EndSubframe;
/* Set up the subcompositor. */ /* Set up the subcompositor. */
@ -1759,8 +1652,6 @@ HandleBind (struct wl_client *client, void *data,
uint32_t version, uint32_t id) uint32_t version, uint32_t id)
{ {
struct wl_resource *resource; struct wl_resource *resource;
char *name;
size_t length;
Seat *seat; Seat *seat;
seat = data; seat = data;
@ -1779,14 +1670,8 @@ HandleBind (struct wl_client *client, void *data,
wl_seat_send_capabilities (resource, (WL_SEAT_CAPABILITY_POINTER wl_seat_send_capabilities (resource, (WL_SEAT_CAPABILITY_POINTER
| WL_SEAT_CAPABILITY_KEYBOARD)); | WL_SEAT_CAPABILITY_KEYBOARD));
length = snprintf (NULL, 0, "X11 master device %d %d",
seat->master_pointer, seat->master_keyboard);
name = alloca (length + 1);
snprintf (name, length + 1, "X11 master device %d %d",
seat->master_pointer, seat->master_keyboard);
if (wl_resource_get_version (resource) > 2) if (wl_resource_get_version (resource) > 2)
wl_seat_send_name (resource, name); wl_seat_send_name (resource, seat->name);
RetainSeat (data); RetainSeat (data);
} }
@ -1835,6 +1720,7 @@ MakeSeatForDevicePair (int master_keyboard, int master_pointer,
seat = XLCalloc (1, sizeof *seat); seat = XLCalloc (1, sizeof *seat);
seat->master_keyboard = master_keyboard; seat->master_keyboard = master_keyboard;
seat->master_pointer = master_pointer; seat->master_pointer = master_pointer;
seat->name = XLStrdup (pointer_info->name);
seat->global = wl_global_create (compositor.wl_display, seat->global = wl_global_create (compositor.wl_display,
&wl_seat_interface, 8, &wl_seat_interface, 8,
seat, HandleBind); seat, HandleBind);
@ -4853,6 +4739,9 @@ StartResizeTracking (Seat *seat, Surface *surface, uint32_t serial,
{ {
WhatEdge type; WhatEdge type;
/* Seat cannot be NULL here, but -Wanalyzer disagrees. */
XLAssert (seat != NULL);
if (seat != IdentifySeat (&type, serial)) if (seat != IdentifySeat (&type, serial))
return False; return False;

View file

@ -618,34 +618,19 @@ AfterParentCommit (Surface *surface, void *data)
subsurface->pending_substate.flags = 0; subsurface->pending_substate.flags = 0;
} }
static Bool
Subframe (Surface *surface, Role *role)
{
Subsurface *subsurface;
subsurface = SubsurfaceFromRole (role);
if (!subsurface->parent || !subsurface->parent->role
|| !subsurface->parent->role->funcs.subframe)
return True;
return subsurface->parent->role->funcs.subframe (subsurface->parent,
subsurface->parent->role);
}
static void static void
EndSubframe (Surface *surface, Role *role) SubsurfaceUpdate (Surface *surface, Role *role)
{ {
Subsurface *subsurface; Subsurface *subsurface;
subsurface = SubsurfaceFromRole (role); subsurface = SubsurfaceFromRole (role);
if (!subsurface->parent || !subsurface->parent->role if (!subsurface->parent || !subsurface->parent->role
|| !subsurface->parent->role->funcs.end_subframe) || !subsurface->parent->role->funcs.subsurface_update)
return; return;
subsurface->parent->role->funcs.end_subframe (subsurface->parent, subsurface->parent->role->funcs.subsurface_update (subsurface->parent,
subsurface->parent->role); subsurface->parent->role);
} }
static Window static Window
@ -704,15 +689,9 @@ Commit (Surface *surface, Role *role)
if (!subsurface->synchronous) if (!subsurface->synchronous)
{ {
/* If the surface is asynchronous, draw this subframe now. /* Tell the parent that a subsurface changed. It should then do
Otherwise it is synchronous, so we should wait for the whatever is appropriate to update the subsurface. */
toplevel to end the frame. */ SubsurfaceUpdate (surface, role);
if (Subframe (surface, role))
{
SubcompositorUpdate (subcompositor);
EndSubframe (surface, role);
}
/* If the size changed, update the outputs this surface is in /* If the size changed, update the outputs this surface is in
the scanout area of. */ the scanout area of. */
@ -845,14 +824,11 @@ Teardown (Surface *surface, Role *role)
/* According to the spec, this removal should take effect /* According to the spec, this removal should take effect
immediately. */ immediately. */
if (subcompositor if (subcompositor)
&& Subframe (surface, role)) SubsurfaceUpdate (surface, role);
{
SubcompositorUpdate (subcompositor);
EndSubframe (surface, role);
}
} }
/* Destroy the backing data of the subsurface. */
DestroyBacking (subsurface); DestroyBacking (subsurface);
/* Update whether or not idle inhibition should continue. */ /* Update whether or not idle inhibition should continue. */
@ -971,8 +947,7 @@ GetSubsurface (struct wl_client *client, struct wl_resource *resource,
subsurface->role.funcs.teardown = Teardown; subsurface->role.funcs.teardown = Teardown;
subsurface->role.funcs.setup = Setup; subsurface->role.funcs.setup = Setup;
subsurface->role.funcs.release_buffer = ReleaseBuffer; subsurface->role.funcs.release_buffer = ReleaseBuffer;
subsurface->role.funcs.subframe = Subframe; subsurface->role.funcs.subsurface_update = SubsurfaceUpdate;
subsurface->role.funcs.end_subframe = EndSubframe;
subsurface->role.funcs.early_commit = EarlyCommit; subsurface->role.funcs.early_commit = EarlyCommit;
subsurface->role.funcs.get_window = GetWindow; subsurface->role.funcs.get_window = GetWindow;
subsurface->role.funcs.rescale = Rescale; subsurface->role.funcs.rescale = Rescale;

View file

@ -818,20 +818,10 @@ HandleScaleChanged (void *data, int new_scale)
attached. */ attached. */
subcompositor = ViewGetSubcompositor (surface->view); subcompositor = ViewGetSubcompositor (surface->view);
if (subcompositor) if (subcompositor
{ && surface->role
/* When updating stuff out-of-band, a subframe must be started && surface->role->funcs.subsurface_update)
around the update. */ surface->role->funcs.subsurface_update (surface, surface->role);
if (surface->role && surface->role->funcs.subframe
&& surface->role->funcs.subframe (surface, surface->role))
{
SubcompositorUpdate (subcompositor);
if (surface->role && surface->role->funcs.end_subframe)
surface->role->funcs.end_subframe (surface, surface->role);
}
}
/* The scale has changed, so pointer confinement must be redone. */ /* The scale has changed, so pointer confinement must be redone. */
XLPointerConstraintsReconfineSurface (surface); XLPointerConstraintsReconfineSurface (surface);

View file

@ -630,6 +630,33 @@ IsRoleMapped (XdgRole *role)
role->impl); role->impl);
} }
/* Check if a frame can be drawn. Return True if it is okay to call
SubcompositorUpdate, or schedule a frame after the current frame is
drawn and return False. */
static Bool
CheckFrame (XdgRole *role)
{
if (XLFrameClockFrameInProgress (role->clock))
{
if (XLFrameClockCanBatch (role->clock))
/* But if we can squeeze the frame inside the vertical
blanking period, or a frame is in progress but EndFrame has
not yet been called, go ahead. */
return True;
role->state |= StateLateFrame;
role->state &= ~StatePendingFrameCallback;
if (role->state & StateWaitingForAckConfigure)
role->state &= ~StateLateFrameAcked;
return False;
}
return True;
}
static void static void
Commit (Surface *surface, Role *role) Commit (Surface *surface, Role *role)
{ {
@ -682,42 +709,21 @@ Commit (Surface *surface, Role *role)
xdg_role->state &= ~StateWaitingForAckCommit; xdg_role->state &= ~StateWaitingForAckCommit;
} }
/* If the window is unmapped, skip all of this code! Once the
window is mapped again, the compositor will send _NET_FRAME_DRAWN
should a frame still be in progress. */
if (!IsRoleMapped (xdg_role))
goto start_drawing;
/* If the frame clock is frozen but we are no longer waiting for the /* If the frame clock is frozen but we are no longer waiting for the
configure event to be acknowledged by the client, unfreeze the configure event to be acknowledged by the client, unfreeze the
frame clock. */ frame clock. */
if (!(xdg_role->state & StateWaitingForAckConfigure)) if (!(xdg_role->state & StateWaitingForAckConfigure))
Unfreeze (xdg_role); Unfreeze (xdg_role);
/* A frame is already in progress, so instead say that an urgent /* Now, check if a frame can be drawn, or schedule a frame to be
update is needed immediately after the frame completes. In any drawn after this one completes. */
case, don't run frame callbacks upon buffer release anymore. */
if (XLFrameClockFrameInProgress (xdg_role->clock))
{
if (XLFrameClockCanBatch (xdg_role->clock))
/* But if we can squeeze the frame inside the vertical
blanking period, or a frame is in progress but EndFrame has
not yet been called, go ahead. */
goto start_drawing;
xdg_role->state |= StateLateFrame; if (!CheckFrame (xdg_role))
xdg_role->state &= ~StatePendingFrameCallback; /* The frame cannot be drawn, because the compositor has not yet
drawn a previous frame. */
if (xdg_role->state & StateWaitingForAckConfigure) return;
xdg_role->state &= ~StateLateFrameAcked;
else
xdg_role->state |= StateLateFrameAcked;
return;
}
start_drawing:
/* The frame can be drawn, so update the window contents now. */
SubcompositorUpdate (xdg_role->subcompositor); SubcompositorUpdate (xdg_role->subcompositor);
/* Do not end frames explicitly. Instead, wait for the /* Do not end frames explicitly. Instead, wait for the
@ -871,52 +877,26 @@ ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer)
} }
} }
static Bool static void
Subframe (Surface *surface, Role *role) SubsurfaceUpdate (Surface *surface, Role *role)
{ {
XdgRole *xdg_role; XdgRole *xdg_role;
xdg_role = XdgRoleFromRole (role); xdg_role = XdgRoleFromRole (role);
/* If the frame clock is frozen, return False. */ /* If the frame clock is frozen, don't update anything. */
if (XLFrameClockIsFrozen (xdg_role->clock)) if (XLFrameClockIsFrozen (xdg_role->clock))
{ return;
/* However, run frame callbacks. */
RunFrameCallbacksConditionally (xdg_role);
return False;
}
/* Similarly, return False if the role is unmapped. */ /* If a frame is already in progress, return, but schedule a frame
if (!IsRoleMapped (xdg_role)) to be drawn later. */
return False;
/* If a frame is already in progress, return False. Then, require a if (!CheckFrame (xdg_role))
late frame. */ /* The frame cannot be drawn. */
if (XLFrameClockFrameInProgress (xdg_role->clock)) return;
{
if (XLFrameClockCanBatch (xdg_role->clock))
/* But if we can squeeze the frame inside the vertical
blanking period, or a frame is in progress but EndFrame has
not yet been called, go ahead. */
return True;
xdg_role->state |= StateLateFrame; /* The frame can be drawn, so update the window contents. */
xdg_role->state &= ~StatePendingFrameCallback; SubcompositorUpdate (xdg_role->subcompositor);
if (xdg_role->state & StateWaitingForAckConfigure)
xdg_role->state &= ~StateLateFrameAcked;
return False;
}
return True;
}
static void
EndSubframe (Surface *surface, Role *role)
{
/* Don't end the frame here; instead, wait for the frame callback to
note that drawing the frame has finished. */
} }
static Window static Window
@ -1573,8 +1553,7 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
role->role.funcs.teardown = Teardown; role->role.funcs.teardown = Teardown;
role->role.funcs.setup = Setup; role->role.funcs.setup = Setup;
role->role.funcs.release_buffer = ReleaseBuffer; role->role.funcs.release_buffer = ReleaseBuffer;
role->role.funcs.subframe = Subframe; role->role.funcs.subsurface_update = SubsurfaceUpdate;
role->role.funcs.end_subframe = EndSubframe;
role->role.funcs.get_window = GetWindow; role->role.funcs.get_window = GetWindow;
role->role.funcs.get_resize_dimensions = GetResizeDimensions; role->role.funcs.get_resize_dimensions = GetResizeDimensions;
role->role.funcs.post_resize = PostResize; role->role.funcs.post_resize = PostResize;

View file

@ -1173,7 +1173,8 @@ Unmap (XdgToplevel *toplevel)
toplevel->min_width = 0; toplevel->min_width = 0;
toplevel->min_height = 0; toplevel->min_height = 0;
memset (&toplevel->state, 0, sizeof toplevel->states); memset (&toplevel->toplevel_state, 0,
sizeof toplevel->toplevel_state);
/* If there is a pending configure timer, remove it. */ /* If there is a pending configure timer, remove it. */
if (toplevel->configuration_timer) if (toplevel->configuration_timer)