Major refactoring of the frame clock code

This is in preparation for a migration to the Present extension
on systems that do not support frame synchronization.

* 12to11.man: Remove option that is no longer useful.
* Imakefile (SRCS, OBJS): Add `sync_source.c.'.
* compositor.h (enum _RenderMode): New enum.
(struct _RenderFuncs): Add some new functions.
(enum _FrameMode): Remove ModeNotifyDisablePresent.

* frame_clock.c (struct _FrameClockCallback): Add draw time to
frame callback.
(struct _FrameClock): Rearrange for alignment and remove many
unused members.
(BumpFrame): New function.
(FreezeForValue): Remove code that is no longer useful, as
clock->frozen no longer exists.
(StartFrame, EndFrame): Remove clock "freezing" logic.  Only
`need_configure' remains.
(RunFrameCallbacks, NoteFakeFrame): Add frame drawn time.
(XLFrameClockAfterFrame): Adjust for new type of callback.
(XLFrameClockFreeze): Remove function.
(XLFrameClockFrameInProgress): Remove test for
`frozen_until_end_frame'.
(XLFrameClockHandleFrameEvent): Improve code in accordance with
above changes.
(XLFrameClockUnfreeze, XLFrameClockNeedConfigure): Remove
functions.
(XLFrameClockIsFrozen): Remove function.
(XLFrameClockSetFreezeCallback): Accept new callback
`fast_forward_callback'.
(XLFrameClockGetFrameTime): Remove unused function.

* icon_surface.c (struct _IconSurface, ReleaseBacking)
(ReleaseBuffer, RunFrameCallbacks, AfterFrame, Commit)
(SubsurfaceUpdate, XLGetIconSurface)
(XLHandleOneXEventForIconSurfaces): Switch the icon surface to
the sync helper abstraction, and allow asynch buffer release
from inside.

* picture_renderer.c (struct _PictureTarget): New field
`next_msc' and `render_mode'.
(SwapBackBuffers): Respect the render mode.
(InitSynchronizedPresentation): Delete function.
(InitAdditionalModifiers): Remove incorrect comment.
(InitRenderFuncs): Stop initializing obsolete option.
(SetRenderMode): New function.
(PresentToWindow): Respect the render mode.
(NotifyMsc): New function.
(picture_render_funcs): Add notify_msc.
(HandlePresentCompleteNotify): Call completion callback with the
fraame counter.

* renderer.c (RenderSetRenderMode):
(RenderNotifyMsc): New functions.

* subcompositor.c (struct _Subcompositor)
(SubcompositorSetNoteFrameCallback): Add msc and ust to note
frame callback.
(PresentCompletedCallback, RenderCompletedCallback): Call with
msc and ust.
(BeginFrame): When presentation is being synchronized and there
is no existing presentation callback, ask for an event to be
sent on the next frame.
(EndFrame): Do not clear the presentation callbacks, as the
update might not touch anything.

* test.c (NoteFrame): Update prototype.
* xdg_popup.c (InternalReposition): Stop "freezing" the frame
clock.

* xdg_surface.c (struct _XdgRole): Replace the frame clock with
the sync helper abstraction.
(RunFrameCallbacks): Run with the frame time.
(RunFrameCallbacksConditionally): Save the pending frame time.
(UpdateFrameRefreshPrediction): Delete function.
(XLHandleXEventForXdgSurfaces): Give events to the sync helper
instead.
(Unfreeze, IsRoleMapped, CheckFrame): Remove functions.
(Commit, SubsurfaceUpdate): Update using the sync helper
instead, only if not pending ack commit.
(MaybeRunLateFrame, AfterFrame): Delete functions.
(NoteConfigure, NoteBounds): Update for the sync helper.
(WriteRedirectProperty): Always require redirection for now.
Disabling redirection requires sorting out some Present problems
on the X server side.
(WasFrameQueued, NoteFrame): Delete functions.
(HandleFreeze): Delete function.
(HandleResize, CheckFastForward, HandleFrameCallback): New
functions.
(XLGetXdgSurface): Use the sync helper for most things.
(XLXdgRoleSendConfigure, XLXdgRoleReconstrain)
(XLXdgRoleGetFrameClock): Delete function.
(XLXdgRoleNoteRejectedConfigure): Clean up for using the sync
clock instead.
This commit is contained in:
hujianwei 2022-11-22 10:57:50 +00:00
parent ed9a704e69
commit 1ce5081dfa
11 changed files with 484 additions and 654 deletions

View file

@ -115,11 +115,6 @@ composite the contents of Wayland surfaces onto X windows. This can
either be \fBpicture\fP (the XRender based compositor) or \fBegl\fP
(the OpenGL ES 2.0 based compositor).
.TP
.B useDirectPresentation\fP (class \fBUseDirectPresentation\fP)
If ``True'' or ``true'', this resource enables the use of direct
presentation on unredirected windows. This is not yet complete
pending the installation of required functionality in the X server.
.TP
.B wmProtocols\fP (class \fBWmProtocols\fP)
Comma-separated list of window manager protocols, similar to
\fBinputStyles\fP, that the protocol translator should enable or

View file

@ -18,8 +18,8 @@ MakeSubdirs($(SUBDIRS))
DependSubdirs($(SUBDIRS))
#endif
SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c surface.c region.c shm.c atoms.c subcompositor.c positioner.c xdg_wm.c xdg_surface.c xdg_toplevel.c frame_clock.c xerror.c ewmh.c timer.c subsurface.c seat.c data_device.c xdg_popup.c dmabuf.c buffer.c select.c xdata.c xsettings.c dnd.c icon_surface.c primary_selection.c renderer.c picture_renderer.c explicit_synchronization.c transform.c wp_viewporter.c decoration.c text_input.c single_pixel_buffer.c drm_lease.c pointer_constraints.c time.c relative_pointer.c keyboard_shortcuts_inhibit.c idle_inhibit.c process.c fence_ring.c pointer_gestures.c test.c buffer_release.c xdg_activation.c tearing_control.c
OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o surface.o region.o shm.o atoms.o subcompositor.o positioner.o xdg_wm.o xdg_surface.o xdg_toplevel.o frame_clock.o xerror.o ewmh.o timer.o subsurface.o seat.o data_device.o xdg_popup.o dmabuf.o buffer.o select.o xdata.o xsettings.o dnd.o icon_surface.o primary_selection.o renderer.o picture_renderer.o explicit_synchronization.o transform.o wp_viewporter.o decoration.o text_input.o single_pixel_buffer.o drm_lease.o pointer_constraints.o time.o relative_pointer.o keyboard_shortcuts_inhibit.o idle_inhibit.o process.o fence_ring.o pointer_gestures.o test.o buffer_release.o xdg_activation.o tearing_control.o
SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c surface.c region.c shm.c atoms.c subcompositor.c positioner.c xdg_wm.c xdg_surface.c xdg_toplevel.c frame_clock.c xerror.c ewmh.c timer.c subsurface.c seat.c data_device.c xdg_popup.c dmabuf.c buffer.c select.c xdata.c xsettings.c dnd.c icon_surface.c primary_selection.c renderer.c picture_renderer.c explicit_synchronization.c transform.c wp_viewporter.c decoration.c text_input.c single_pixel_buffer.c drm_lease.c pointer_constraints.c time.c relative_pointer.c keyboard_shortcuts_inhibit.c idle_inhibit.c process.c fence_ring.c pointer_gestures.c test.c buffer_release.c xdg_activation.c tearing_control.c sync_source.c
OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o surface.o region.o shm.o atoms.o subcompositor.o positioner.o xdg_wm.o xdg_surface.o xdg_toplevel.o frame_clock.o xerror.o ewmh.o timer.o subsurface.o seat.o data_device.o xdg_popup.o dmabuf.o buffer.o select.o xdata.o xsettings.o dnd.o icon_surface.o primary_selection.o renderer.o picture_renderer.o explicit_synchronization.o transform.o wp_viewporter.o decoration.o text_input.o single_pixel_buffer.o drm_lease.o pointer_constraints.o time.o relative_pointer.o keyboard_shortcuts_inhibit.o idle_inhibit.o process.o fence_ring.o pointer_gestures.o test.o buffer_release.o xdg_activation.o tearing_control.o sync_source.o
GENHEADERS = transfer_atoms.h drm_modifiers.h
HEADER = $(GENHEADERS) compositor.h

View file

@ -160,6 +160,7 @@ typedef struct _ShmFormat ShmFormat;
typedef enum _Operation Operation;
typedef enum _BufferTransform BufferTransform;
typedef enum _RenderMode RenderMode;
typedef void *IdleCallbackKey;
typedef void *PresentCompletionKey;
@ -169,8 +170,8 @@ typedef void (*DmaBufSuccessFunc) (RenderBuffer, void *);
typedef void (*DmaBufFailureFunc) (void *);
typedef void (*BufferIdleFunc) (RenderBuffer, void *);
typedef void (*PresentCompletionFunc) (void *);
typedef void (*RenderCompletionFunc) (void *);
typedef void (*PresentCompletionFunc) (void *, uint64_t, uint64_t);
typedef void (*RenderCompletionFunc) (void *, uint64_t, uint64_t);
enum _BufferTransform
{
@ -314,6 +315,14 @@ enum
SupportsDirectPresent = 1 << 4,
};
enum _RenderMode
{
/* Do not synchronize rendering. */
RenderModeAsync,
/* Synchronize rendering with the vertical refresh. */
RenderModeVsync,
};
struct _RenderFuncs
{
/* Initialize the visual and depth. */
@ -325,6 +334,12 @@ struct _RenderFuncs
/* Create a rendering target for the given pixmap. */
RenderTarget (*target_from_pixmap) (Pixmap);
/* Set the rendering mode for the render target. Return whether or
not the mode was successfully set. The default rendering mode is
RenderModeAsync. The last argument is the number of the next
frame as known to the caller. */
Bool (*set_render_mode) (RenderTarget, RenderMode, uint64_t);
/* Set the client associated with the target. This allows some
extra pixmap memory allocation tracking to be done. */
void (*set_client) (RenderTarget, struct wl_client *);
@ -423,6 +438,11 @@ struct _RenderFuncs
pixman_region32_t *,
PresentCompletionFunc, void *);
/* Call the specified render callback once it is time to display the
next frame. May be NULL. */
RenderCompletionKey (*notify_msc) (RenderTarget, RenderCompletionFunc,
void *);
/* Cancel the given presentation callback. */
void (*cancel_presentation_callback) (PresentCompletionKey);
@ -542,6 +562,7 @@ extern void InitRenderers (void);
extern RenderTarget RenderTargetFromWindow (Window, unsigned long);
extern RenderTarget RenderTargetFromPixmap (Pixmap);
extern Bool RenderSetRenderMode (RenderTarget, RenderMode, uint64_t);
extern void RenderSetClient (RenderTarget, struct wl_client *);
extern void RenderSetStandardEventMask (RenderTarget, unsigned long);
extern void RenderNoteTargetSize (RenderTarget, int, int);
@ -568,6 +589,8 @@ extern PresentCompletionKey RenderPresentToWindow (RenderTarget, RenderBuffer,
pixman_region32_t *,
PresentCompletionFunc,
void *);
extern RenderCompletionKey RenderNotifyMsc (RenderTarget, RenderCompletionFunc,
void *);
extern void RenderCancelPresentationCallback (PresentCompletionKey);
extern DrmFormat *RenderGetDrmFormats (int *);
@ -767,7 +790,6 @@ typedef enum _FrameMode FrameMode;
enum _FrameMode
{
ModeStarted,
ModeNotifyDisablePresent,
ModeComplete,
ModePresented,
};
@ -798,7 +820,8 @@ extern void SubcompositorSetBoundsCallback (Subcompositor *,
void *);
extern void SubcompositorSetNoteFrameCallback (Subcompositor *,
void (*) (FrameMode, uint64_t,
void *),
void *, uint64_t,
uint64_t),
void *);
extern void SubcompositorBounds (Subcompositor *, int *, int *, int *, int *);
extern void SubcompositorSetProjectiveTransform (Subcompositor *, int, int);
@ -1304,24 +1327,20 @@ typedef struct _FrameClock FrameClock;
extern FrameClock *XLMakeFrameClockForWindow (Window);
extern Bool XLFrameClockFrameInProgress (FrameClock *);
extern void XLFrameClockAfterFrame (FrameClock *, void (*) (FrameClock *,
void *),
void *,
uint64_t),
void *);
extern void XLFrameClockHandleFrameEvent (FrameClock *, XEvent *);
extern Bool XLFrameClockStartFrame (FrameClock *, Bool);
extern void XLFrameClockEndFrame (FrameClock *);
extern Bool XLFrameClockIsFrozen (FrameClock *);
extern Bool XLFrameClockCanBatch (FrameClock *);
extern Bool XLFrameClockNeedConfigure (FrameClock *);
extern void XLFrameClockFreeze (FrameClock *);
extern void XLFrameClockUnfreeze (FrameClock *);
extern void XLFreeFrameClock (FrameClock *);
extern Bool XLFrameClockSyncSupported (void);
extern Bool XLFrameClockCanRunFrame (FrameClock *);
extern void XLFrameClockSetPredictRefresh (FrameClock *);
extern void XLFrameClockDisablePredictRefresh (FrameClock *);
extern void XLFrameClockSetFreezeCallback (FrameClock *, void (*) (void *),
void *);
extern uint64_t XLFrameClockGetFrameTime (FrameClock *);
extern void XLFrameClockSetFreezeCallback (FrameClock *, void (*) (void *,
Bool),
Bool (*) (void *), void *);
extern void XLFrameClockNoteConfigure (FrameClock *);
extern void *XLAddCursorClockCallback (void (*) (void *, struct timespec),
void *);
@ -1905,6 +1924,25 @@ extern void XLInitXdgActivation (void);
extern void XLInitTearingControl (void);
/* Defined in sync_source.h. */
typedef struct _SyncHelper SyncHelper;
typedef enum _SynchronizationType SynchronizationType;
extern SyncHelper *MakeSyncHelper (Subcompositor *, Window,
RenderTarget,
void (*) (void *, uint32_t),
Role *);
extern void SyncHelperUpdate (SyncHelper *);
extern void FreeSyncHelper (SyncHelper *);
extern void SyncHelperHandleFrameEvent (SyncHelper *, XEvent *);
extern void SyncHelperSetResizeCallback (SyncHelper *, void (*) (void *,
Bool),
Bool (*) (void *));
extern void SyncHelperNoteConfigureEvent (SyncHelper *);
extern void SyncHelperCheckFrameCallback (SyncHelper *);
extern void SyncHelperClearPendingFrame (SyncHelper *);
/* Utility functions that don't belong in a specific file. */
#define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0])

View file

@ -33,23 +33,20 @@ enum
};
/* Whether or not the compositor supports frame synchronization. */
static Bool frame_sync_supported;
/* Timer used for cursor animations. */
static Timer *cursor_clock;
/* How many cursors want cursor animations. */
static int cursor_count;
struct _FrameClockCallback
{
/* Function called once a frame is completely written to display and
(ideally, whether or not this actually works depends on various
different factors) enters vblank. */
void (*frame) (FrameClock *, void *);
/* Function called once a frame is completely written to display.
The last arg is the time at which the frame was written, or
(uint64_t) -1. */
void (*frame) (FrameClock *, void *, uint64_t);
/* Data that function is called with. */
void *data;
@ -60,28 +57,12 @@ struct _FrameClockCallback
struct _FrameClock
{
/* List of frame clock callbacks. */
FrameClockCallback callbacks;
/* Two sync counters. */
XSyncCounter primary_counter, secondary_counter;
/* The value of the frame currently being drawn in this frame clock,
and the value of the last frame that was marked as complete. */
uint64_t next_frame_id, finished_frame_id;
/* A timer used as a fake synchronization source if frame
synchronization is not supported. */
Timer *static_frame_timer;
/* A timer used to end the next frame. */
Timer *end_frame_timer;
/* Callback run when the frame is frozen. */
void (*freeze_callback) (void *);
/* Data for that callback. */
void *freeze_callback_data;
/* List of frame clock callbacks. */
FrameClockCallback callbacks;
/* The wanted configure value. */
uint64_t configure_id;
@ -103,6 +84,28 @@ struct _FrameClock
/* The last known presentation time. */
uint64_t last_presentation_time;
/* Two sync counters. */
XSyncCounter primary_counter, secondary_counter;
/* A timer used as a fake synchronization source if frame
synchronization is not supported. */
Timer *static_frame_timer;
/* A timer used to end the next frame. */
Timer *end_frame_timer;
/* Callback run when the frame is frozen. The second arg means
whether or not the freeze should not result in more waiting for
ack_configure. */
void (*freeze_callback) (void *, Bool);
/* Callback run to determine whether or not the frame clock can be
fast forwarded. */
Bool (*can_forward_sync_counter) (void *);
/* Data for the above two callbacks. */
void *freeze_callback_data;
/* The refresh interval. */
uint32_t refresh_interval;
@ -114,10 +117,8 @@ struct _FrameClock
is put in place. */
uint32_t got_configure_count, pending_configure_count;
/* Whether or not configury is in progress, and whether or not this
is frozen, and whether or not the frame shouldn't actually be
unfrozen until EndFrame. */
Bool need_configure, frozen, frozen_until_end_frame;
/* Whether or not configury is in progress. */
Bool need_configure;
/* Whether or not EndFrame was called after StartFrame. */
Bool end_frame_called;
@ -246,9 +247,16 @@ HandleEndFrame (Timer *timer, void *data, struct timespec time)
/* Forward declarations. */
static void RunFrameCallbacks (FrameClock *);
static void RunFrameCallbacks (FrameClock *, uint64_t);
static Bool StartFrame (FrameClock *, Bool, Bool);
static void
BumpFrame (FrameClock *clock)
{
StartFrame (clock, True, False);
EndFrame (clock);
}
static void
FreezeForValue (FrameClock *clock, uint64_t counter_value)
{
@ -266,6 +274,7 @@ FreezeForValue (FrameClock *clock, uint64_t counter_value)
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;
@ -282,14 +291,6 @@ FreezeForValue (FrameClock *clock, uint64_t counter_value)
EndFrame (clock);
}
/* counter_value - 240 is the value seen by the compositor when the
frame contents were frozen in response to a resize. If it is
less than finished_frame_id, run frame callbacks now, or clients
like Chromium are confused and hang waiting for frame callbacks
to be called. */
if (counter_value - 240 <= clock->finished_frame_id)
RunFrameCallbacks (clock);
/* The reason for clearing in_frame is that otherwise a future
Commit after the configuration is acknowledged will not be able
to start a new frame and restart the frame clock. */
@ -297,17 +298,23 @@ FreezeForValue (FrameClock *clock, uint64_t counter_value)
clock->need_configure = True;
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. */
if (need_empty_frame
/* If the surface has not yet ack_configure'd, don't fast
forward the frame. The sync counter will be set at the next
commit after ack_configure. */
&& clock->can_forward_sync_counter
&& clock->can_forward_sync_counter (clock->freeze_callback_data))
/* If this event is already out of date, just fast forward the
sync counter. */
BumpFrame (clock);
StartFrame (clock, True, False);
EndFrame (clock);
}
else
clock->frozen = True;
clock->freeze_callback (clock->freeze_callback_data,
/* Only run the freeze callback if this
freeze is up to date. If it is not,
frame callbacks still have to be run,
which is what the following parameter
means. */
need_empty_frame);
return;
}
@ -410,12 +417,6 @@ PostEndFrame (FrameClock *clock)
static Bool
StartFrame (FrameClock *clock, Bool urgent, Bool predict)
{
if (clock->frozen)
return False;
if (clock->frozen_until_end_frame)
return False;
if (clock->in_frame)
{
if (clock->end_frame_timer
@ -487,11 +488,6 @@ StartFrame (FrameClock *clock, Bool urgent, Bool predict)
static void
EndFrame (FrameClock *clock)
{
if (clock->frozen)
return;
clock->frozen_until_end_frame = False;
/* Signal that end_frame was called and it is now safe to finish the
frame from the timer. */
clock->end_frame_called = True;
@ -521,8 +517,10 @@ EndFrame (FrameClock *clock)
/* The frame has ended. Freeze the frame clock if there is a
pending sync value. */
if (clock->pending_sync_value)
FreezeForValue (clock, clock->pending_sync_value);
clock->pending_sync_value = 0;
if (!frame_sync_supported)
@ -551,7 +549,7 @@ FreeFrameCallbacks (FrameClock *clock)
}
static void
RunFrameCallbacks (FrameClock *clock)
RunFrameCallbacks (FrameClock *clock, uint64_t frame_drawn_time)
{
FrameClockCallback *callback;
@ -559,7 +557,8 @@ RunFrameCallbacks (FrameClock *clock)
while (callback != &clock->callbacks)
{
callback->frame (clock, callback->data);
callback->frame (clock, callback->data,
frame_drawn_time);
callback = callback->next;
}
}
@ -575,13 +574,14 @@ NoteFakeFrame (Timer *timer, void *data, struct timespec time)
&& (clock->finished_frame_id == clock->next_frame_id))
{
clock->in_frame = False;
RunFrameCallbacks (clock);
RunFrameCallbacks (clock, (uint64_t) -1);
}
}
void
XLFrameClockAfterFrame (FrameClock *clock,
void (*frame_func) (FrameClock *, void *),
void (*frame_func) (FrameClock *, void *,
uint64_t),
void *data)
{
FrameClockCallback *callback;
@ -613,36 +613,9 @@ XLFrameClockEndFrame (FrameClock *clock)
Bool
XLFrameClockFrameInProgress (FrameClock *clock)
{
if (clock->frozen_until_end_frame)
/* Don't consider a frame as being in progress, since the frame
counter has been incremented to freeze the display. */
return False;
return clock->in_frame;
}
/* N.B. that this function is called from popups, where normal
freezing does not work, as the window manager does not
cooperate. */
void
XLFrameClockFreeze (FrameClock *clock)
{
/* Start a frame now, unless one is already in progress, in which
case it suffices to get rid of the timer. */
if (!clock->end_frame_timer)
StartFrame (clock, False, False);
else
{
RemoveTimer (clock->end_frame_timer);
clock->end_frame_timer = NULL;
}
/* Don't unfreeze until the next EndFrame. */
clock->frozen_until_end_frame = True;
clock->frozen = True;
}
void
XLFrameClockHandleFrameEvent (FrameClock *clock, XEvent *event)
{
@ -671,9 +644,11 @@ XLFrameClockHandleFrameEvent (FrameClock *clock, XEvent *event)
/* Actually compute the time and save it. */
clock->last_frame_time = low | (high << 32);
/* Run any frame callbacks, since drawing has finished. */
/* Clear the in_frame flag. */
clock->in_frame = False;
RunFrameCallbacks (clock);
/* Run any frame callbacks, since drawing has finished. */
RunFrameCallbacks (clock, clock->last_frame_time);
if (clock->frame_timings_id == -1)
{
@ -742,14 +717,11 @@ XLFrameClockHandleFrameEvent (FrameClock *clock, XEvent *event)
/* If a frame is in progress, postpone this frame
synchronization message. */
if (clock->in_frame && !clock->end_frame_called)
clock->pending_sync_value = value;
else
FreezeForValue (clock, value);
if (clock->freeze_callback)
/* Call the freeze callback in any case. */
clock->freeze_callback (clock->freeze_callback_data);
}
}
@ -821,30 +793,12 @@ XLMakeFrameClockForWindow (Window window)
return clock;
}
void
XLFrameClockUnfreeze (FrameClock *clock)
{
clock->frozen = False;
}
Bool
XLFrameClockNeedConfigure (FrameClock *clock)
{
return clock->need_configure;
}
Bool
XLFrameClockSyncSupported (void)
{
return frame_sync_supported;
}
Bool
XLFrameClockIsFrozen (FrameClock *clock)
{
return clock->frozen;
}
Bool
XLFrameClockCanBatch (FrameClock *clock)
{
@ -884,21 +838,14 @@ XLFrameClockDisablePredictRefresh (FrameClock *clock)
}
void
XLFrameClockSetFreezeCallback (FrameClock *clock, void (*callback) (void *),
XLFrameClockSetFreezeCallback (FrameClock *clock, void (*callback) (void *,
Bool),
Bool (*fast_forward_callback) (void *),
void *data)
{
clock->freeze_callback = callback;
clock->freeze_callback_data = data;
}
uint64_t
XLFrameClockGetFrameTime (FrameClock *clock)
{
/* Only return the time if it is actually a valid clock time. */
if (!compositor.server_time_monotonic)
return 0;
return clock->last_frame_time;
clock->can_forward_sync_counter = fast_forward_callback;
}
void

View file

@ -29,9 +29,10 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
enum
{
StateLateFrame = 1,
StateIsMapped = (1 << 1),
StateIsReleased = (1 << 2),
StateIsMapped = 1,
StateIsReleased = (1 << 1),
StatePendingBufferRelease = (1 << 2),
StatePendingFrameCallback = (1 << 3),
};
struct _IconSurface
@ -48,8 +49,11 @@ struct _IconSurface
/* The subcompositor associated with this role. */
Subcompositor *subcompositor;
/* The frame clock associated with this role. */
FrameClock *clock;
/* The associated buffer release helper. */
BufferReleaseHelper *release_helper;
/* The sync source associated with this role. */
SyncHelper *sync_helper;
/* The number of references to this role. */
int refcount;
@ -63,6 +67,9 @@ struct _IconSurface
/* The last known bounds of this icon surface. */
int min_x, min_y, max_x, max_y;
/* The time of any pending frame. */
uint32_t pending_frame_time;
};
/* Hash table of all icon surfaces. */
@ -90,16 +97,19 @@ ReleaseBacking (IconSurface *icon)
RenderDestroyRenderTarget (icon->target);
XDestroyWindow (compositor.display, icon->window);
/* And the buffer release helper. */
FreeBufferReleaseHelper (icon->release_helper);
/* And the association. */
XLDeleteAssoc (surfaces, icon->window);
/* Free the sync helper. */
FreeSyncHelper (icon->sync_helper);
/* There shouldn't be any children of the subcompositor at this
point. */
SubcompositorFree (icon->subcompositor);
/* The frame clock is no longer useful. */
XLFreeFrameClock (icon->clock);
/* And since there are no C level references to the icon surface
anymore, it can be freed. */
XLFree (icon);
@ -155,17 +165,22 @@ Setup (Surface *surface, Role *role)
static void
ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer)
{
RenderBuffer render_buffer;
IconSurface *icon;
icon = IconSurfaceFromRole (role);
render_buffer = XLRenderBufferFromBuffer (buffer);
/* Icon surfaces are not supposed to change much, so doing an XSync
(or XIfEvent) here is okay. */
RenderWaitForIdle (XLRenderBufferFromBuffer (buffer),
icon->target);
/* Now really release the buffer. */
XLReleaseBuffer (buffer);
if (RenderIsBufferIdle (render_buffer, icon->target))
/* If the buffer is already idle, release it now. */
XLReleaseBuffer (buffer);
else
{
/* Release the buffer once it is destroyed or becomes idle. */
ReleaseBufferWithHelper (icon->release_helper,
buffer, icon->target);
icon->state |= StatePendingBufferRelease;
}
}
static void
@ -218,48 +233,61 @@ NoteBounds (void *data, int min_x, int min_y, int max_x, int max_y)
}
static void
RunFrameCallbacks (Surface *surface, FrameClock *clock)
RunFrameCallbacks (Surface *surface, uint32_t ms_time)
{
struct timespec time;
uint64_t last_drawn_time;
/* Surface can be NULL for various reasons, especially events
arriving after the icon surface is detached. */
if (!surface)
return;
last_drawn_time = XLFrameClockGetFrameTime (clock);
if (!last_drawn_time)
{
clock_gettime (CLOCK_MONOTONIC, &time);
XLSurfaceRunFrameCallbacks (surface, time);
}
else
XLSurfaceRunFrameCallbacksMs (surface, last_drawn_time / 1000);
XLSurfaceRunFrameCallbacksMs (surface, ms_time);
}
static void
AfterFrame (FrameClock *clock, void *data)
RunFrameCallbacksConditionally (IconSurface *icon, uint32_t ms_time)
{
if (!icon->role.surface)
return;
if (icon->state & StatePendingBufferRelease)
{
/* Wait for all buffers to be released first. */
icon->state |= StatePendingFrameCallback;
icon->pending_frame_time = ms_time;
}
else
RunFrameCallbacks (icon->role.surface, ms_time);
}
static void
AllBuffersReleased (void *data)
{
IconSurface *icon;
Surface *surface;
icon = data;
surface = icon->role.surface;
/* Clear the buffer release flag. */
icon->state = ~StatePendingBufferRelease;
if (surface && icon->state & StatePendingFrameCallback)
{
/* Run frame callbacks now, as no more buffers are waiting to be
released. */
RunFrameCallbacks (surface, icon->pending_frame_time);
icon->state &= ~StatePendingFrameCallback;
}
}
static void
HandleFrameCallback (void *data, uint32_t ms_time)
{
IconSurface *icon;
icon = data;
if (icon->state & StateLateFrame)
{
icon->state &= ~StateLateFrame;
/* Since we are running late, make the compositor draw the frame
now. */
XLFrameClockStartFrame (clock, True);
SubcompositorUpdate (icon->subcompositor);
XLFrameClockEndFrame (clock);
return;
}
RunFrameCallbacks (icon->role.surface, clock);
RunFrameCallbacksConditionally (icon, ms_time);
}
static void
@ -316,20 +344,6 @@ Commit (Surface *surface, Role *role)
icon = IconSurfaceFromRole (role);
if (XLFrameClockFrameInProgress (icon->clock))
{
/* A frame is already in progress; schedule another one for
later. */
icon->state |= StateLateFrame;
}
else
{
/* Start a frame and update the icon surface now. */
XLFrameClockStartFrame (icon->clock, False);
SubcompositorUpdate (icon->subcompositor);
XLFrameClockEndFrame (icon->clock);
}
/* Move the window if any offset was specified. */
if (surface->pending_state.pending & PendingAttachments)
MoveWindowTo (icon, icon->x, icon->y);
@ -340,6 +354,9 @@ Commit (Surface *surface, Role *role)
MaybeMapWindow (icon);
else
MaybeUnmapWindow (icon);
/* Update via the sync helper. */
SyncHelperUpdate (icon->sync_helper);
}
static void
@ -348,19 +365,7 @@ SubsurfaceUpdate (Surface *surface, Role *role)
IconSurface *icon;
icon = IconSurfaceFromRole (role);
if (XLFrameClockFrameInProgress (icon->clock))
{
/* A frame is already in progress; schedule another one for
later. */
icon->state |= StateLateFrame;
return;
}
/* I guess subsurface updates don't count as urgent frames? */
XLFrameClockStartFrame (icon->clock, False);
SubcompositorUpdate (icon->subcompositor);
XLFrameClockEndFrame (icon->clock);
SyncHelperUpdate (icon->sync_helper);
}
static Window
@ -415,6 +420,8 @@ XLGetIconSurface (Surface *surface)
/* Create a target associated with the window. */
role->target = RenderTargetFromWindow (role->window, None);
role->release_helper = MakeBufferReleaseHelper (AllBuffersReleased,
role);
/* Set the client. */
if (surface->resource)
@ -427,7 +434,11 @@ XLGetIconSurface (Surface *surface)
/* Create a subcompositor associated with the window. */
role->subcompositor = MakeSubcompositor ();
role->clock = XLMakeFrameClockForWindow (role->window);
role->sync_helper = MakeSyncHelper (role->subcompositor,
role->window,
role->target,
HandleFrameCallback,
&role->role);
/* Set the subcompositor target and some callbacks. */
SubcompositorSetTarget (role->subcompositor, &role->target);
@ -445,9 +456,6 @@ XLGetIconSurface (Surface *surface)
If it does, frame synchronization will not work. */
WriteRedirectProperty (role);
/* Initialize frame callbacks. */
XLFrameClockAfterFrame (role->clock, AfterFrame, role);
if (!XLSurfaceAttachRole (surface, &role->role))
abort ();
@ -469,7 +477,7 @@ XLHandleOneXEventForIconSurfaces (XEvent *event)
if (icon)
{
XLFrameClockHandleFrameEvent (icon->clock, event);
SyncHelperHandleFrameEvent (icon->sync_helper, event);
return True;
}

View file

@ -220,6 +220,9 @@ enum
struct _PictureTarget
{
/* The next frame number. */
uint64_t next_msc;
/* The XID of the picture. */
Picture picture;
@ -265,6 +268,9 @@ struct _PictureTarget
/* List of buffers that were used in the course of an update. */
XLList *buffers_used;
/* What rendering mode should be used. */
RenderMode render_mode;
};
struct _DrmFormatInfo
@ -451,9 +457,6 @@ static BufferActivityRecord all_activity;
/* List of all presentations that have not yet been completed. */
static PresentCompletionCallback all_completion_callbacks;
/* Whether or not direct presentation should be used. */
static Bool use_direct_presentation;
/* The device nodes of each provider. */
static dev_t *render_devices;
@ -839,10 +842,18 @@ SwapBackBuffers (PictureTarget *target, pixman_region32_t *damage)
if (!present_serial)
present_serial++;
XPresentPixmap (compositor.display, target->window,
back_buffer->pixmap, present_serial,
None, region, 0, 0, None, None, fence,
PresentOptionAsync, 0, 0, 0, NULL, 0);
if (target->render_mode == RenderModeAsync)
XPresentPixmap (compositor.display, target->window,
back_buffer->pixmap, present_serial,
None, region, 0, 0, None, None, fence,
PresentOptionAsync, 0, 0, 0, NULL, 0);
else
/* Present the pixmap synchronously at the next frame. */
XPresentPixmap (compositor.display, target->window,
back_buffer->pixmap, present_serial,
None, region, 0, 0, None, None, fence,
PresentOptionNone, target->next_msc,
1, 0, NULL, 0);
/* Mark the back buffer as busy, and the other back buffer as having
been released. */
@ -1024,42 +1035,6 @@ PickVisual (int *depth)
return NULL;
}
static void
InitSynchronizedPresentation (void)
{
XrmDatabase rdb;
XrmName namelist[3];
XrmClass classlist[3];
XrmValue value;
XrmRepresentation type;
rdb = XrmGetDatabase (compositor.display);
if (!rdb)
return;
namelist[1] = XrmStringToQuark ("useDirectPresentation");
namelist[0] = app_quark;
namelist[2] = NULLQUARK;
classlist[1] = XrmStringToQuark ("UseDirectPresentation");
classlist[0] = resource_quark;
classlist[2] = NULLQUARK;
/* Enable the use of direct presentation if
*.UseDirectPresentation.*.useDirectPresentation is true. This is
still incomplete, as the features necessary for it to play nice
with frame synchronization have not yet been implemented in the X
server. */
if (XrmQGetResource (rdb, namelist, classlist,
&type, &value)
&& type == QString
&& (!strcmp (value.addr, "True")
|| !strcmp (value.addr, "true")))
use_direct_presentation = True;
}
static void
AddAdditionalModifier (const char *name)
{
@ -1154,12 +1129,6 @@ InitAdditionalModifiers (void)
classlist[0] = resource_quark;
classlist[2] = NULLQUARK;
/* Enable the use of direct presentation if
*.UseDirectPresentation.*.useDirectPresentation is true. This is
still incomplete, as the features necessary for it to play nice
with frame synchronization have not yet been implemented in the X
server. */
if (XrmQGetResource (rdb, namelist, classlist,
&type, &value)
&& type == QString)
@ -1190,15 +1159,11 @@ InitRenderFuncs (void)
return False;
}
/* Figure out whether or not the user wants synchronized
presentation. */
InitSynchronizedPresentation ();
/* Find out what additional modifiers the user wants. */
InitAdditionalModifiers ();
if (use_direct_presentation)
AddRenderFlag (SupportsDirectPresent);
/* Add the direct presentation support flag. */
AddRenderFlag (SupportsDirectPresent);
/* Create an unmapped, InputOnly window, that is used to receive
roundtrip events. */
@ -1276,16 +1241,32 @@ TargetFromDrawable (Drawable drawable, Window window,
return (RenderTarget) (void *) target;
}
static RenderTarget
TargetFromWindow (Window window, unsigned long event_mask)
{
return TargetFromDrawable (window, window, event_mask);
}
static RenderTarget
TargetFromPixmap (Pixmap pixmap)
{
return TargetFromDrawable (pixmap, None, NoEventMask);
}
static RenderTarget
TargetFromWindow (Window window, unsigned long event_mask)
static Bool
SetRenderMode (RenderTarget target, RenderMode mode,
uint64_t target_msc)
{
return TargetFromDrawable (window, window, event_mask);
PictureTarget *pict_target;
pict_target = target.pointer;
/* Set the rendering mode to use with target. */
pict_target->render_mode = mode;
/* And set the target msc. */
pict_target->next_msc = target_msc;
return True;
}
static void
@ -1941,21 +1922,16 @@ PresentToWindow (RenderTarget target, RenderBuffer source,
else
region = None;
if (use_direct_presentation)
{
/* Present the pixmap now from the damage; it will complete upon
the next vblank if direct presentation is enabled, or
immediately if it is not. */
XPresentPixmap (compositor.display, pict_target->window, buffer->pixmap,
++present_serial, None, region, 0, 0, None, None, None,
PresentOptionNone, 0, 1, 0, NULL, 0);
}
else
/* Direct presentation is off; present the pixmap asynchronously
at an msc of 0. */
if (pict_target->render_mode == RenderModeAsync)
/* Present the pixmap asynchronously at an msc of 0. */
XPresentPixmap (compositor.display, pict_target->window, buffer->pixmap,
++present_serial, None, region, 0, 0, None, None, None,
++present_serial, None, region, 0, 0, None, None, None,
PresentOptionAsync, 0, 0, 0, NULL, 0);
else
/* Present the pixmap at the next msc. */
XPresentPixmap (compositor.display, pict_target->window, buffer->pixmap,
++present_serial, None, region, 0, 0, None, None, None,
PresentOptionNone, pict_target->next_msc, 1, 0, NULL, 0);
if (region)
XFixesDestroyRegion (compositor.display, region);
@ -1987,6 +1963,30 @@ PresentToWindow (RenderTarget target, RenderBuffer source,
return callback_rec;
}
static RenderCompletionKey
NotifyMsc (RenderTarget target, RenderCompletionFunc callback,
void *data)
{
PictureTarget *pict_target;
PresentCompletionCallback *callback_rec;
pict_target = target.pointer;
if (pict_target->render_mode != RenderModeVsync)
return NULL;
/* Allocate a presentation completion callback. */
callback_rec = MakePresentationCallback ();
callback_rec->function = callback;
callback_rec->data = data;
callback_rec->id = ++present_serial;
/* Ask for a notification. */
XPresentNotifyMSC (compositor.display, pict_target->window,
present_serial, 0, 1, 0);
return callback_rec;
}
/* Cancel the given presentation callback. */
static void
@ -2006,6 +2006,7 @@ static RenderFuncs picture_render_funcs =
.init_render_funcs = InitRenderFuncs,
.target_from_window = TargetFromWindow,
.target_from_pixmap = TargetFromPixmap,
.set_render_mode = SetRenderMode,
.set_client = SetClient,
.set_standard_event_mask = SetStandardEventMask,
.note_target_size = NoteTargetSize,
@ -2023,6 +2024,7 @@ static RenderFuncs picture_render_funcs =
.delete_fence = DeleteFence,
.get_finish_fence = GetFinishFence,
.present_to_window = PresentToWindow,
.notify_msc = NotifyMsc,
.cancel_presentation_callback = CancelPresentationCallback,
};
@ -3379,7 +3381,7 @@ HandlePresentCompleteNotify (XPresentCompleteNotifyEvent *complete)
{
/* The presentation is complete. Run and unlink the
callback. */
last->function (last->data);
last->function (last->data, complete->msc, complete->ust);
last->next->last = last->last;
last->last->next = last->next;

View file

@ -80,6 +80,16 @@ RenderTargetFromPixmap (Pixmap pixmap)
return render_funcs.target_from_pixmap (pixmap);
}
Bool
RenderSetRenderMode (RenderTarget target, RenderMode mode,
uint64_t target_msc)
{
if (!render_funcs.set_render_mode)
return False;
return render_funcs.set_render_mode (target, mode, target_msc);
}
void
RenderSetClient (RenderTarget target, struct wl_client *client)
{
@ -215,6 +225,16 @@ RenderPresentToWindow (RenderTarget target, RenderBuffer source,
data);
}
RenderCompletionKey
RenderNotifyMsc (RenderTarget target, RenderCompletionFunc callback,
void *data)
{
if (!render_funcs.notify_msc)
return NULL;
return render_funcs.notify_msc (target, callback, data);
}
void
RenderCancelPresentationCallback (PresentCompletionKey key)
{

View file

@ -329,8 +329,10 @@ struct _Subcompositor
/* Function called with the bounds before each update. */
void (*note_bounds) (void *, int, int, int, int);
/* Function called with the frame counter on each update. */
void (*note_frame) (FrameMode, uint64_t, void *);
/* Function called with the frame counter on each update. Counter 3
is the msc and counter 4 is the ust. */
void (*note_frame) (FrameMode, uint64_t, void *, uint64_t,
uint64_t);
/* The current frame counter, incremented with each frame. */
uint64_t frame_counter;
@ -2105,7 +2107,8 @@ SubcompositorSetBoundsCallback (Subcompositor *subcompositor,
void
SubcompositorSetNoteFrameCallback (Subcompositor *subcompositor,
void (*note_frame) (FrameMode, uint64_t,
void *),
void *, uint64_t,
uint64_t),
void *data)
{
subcompositor->note_frame = note_frame;
@ -2176,7 +2179,7 @@ StorePreviousDamage (Subcompositor *subcompositor,
}
static void
PresentCompletedCallback (void *data)
PresentCompletedCallback (void *data, uint64_t msc, uint64_t ust)
{
Subcompositor *subcompositor;
@ -2190,11 +2193,12 @@ PresentCompletedCallback (void *data)
if (subcompositor->note_frame)
subcompositor->note_frame (ModePresented,
subcompositor->frame_counter,
subcompositor->note_frame_data);
subcompositor->note_frame_data,
msc, ust);
}
static void
RenderCompletedCallback (void *data)
RenderCompletedCallback (void *data, uint64_t msc, uint64_t ust)
{
Subcompositor *subcompositor;
@ -2206,9 +2210,10 @@ RenderCompletedCallback (void *data)
/* Call the frame function if it s still set. */
if (subcompositor->note_frame)
subcompositor->note_frame (ModeComplete,
subcompositor->note_frame (ModePresented,
subcompositor->frame_counter,
subcompositor->note_frame_data);
subcompositor->note_frame_data,
msc, ust);
}
/* Update ancillary data upon commit. This includes the input and
@ -2974,22 +2979,25 @@ static void
BeginFrame (Subcompositor *subcompositor)
{
if (!subcompositor->note_frame)
return;
{
/* Cancel any presentation callback that is currently in
progress. */
if (subcompositor->present_key)
RenderCancelPresentationCallback (subcompositor->present_key);
subcompositor->present_key = NULL;
/* Cancel any render callback that is currently in progress. */
if (subcompositor->render_key)
RenderCancelCompletionCallback (subcompositor->render_key);
subcompositor->render_key = NULL;
return;
}
subcompositor->note_frame (ModeStarted,
++subcompositor->frame_counter,
subcompositor->note_frame_data);
/* Cancel any presentation callback that is currently in
progress. */
if (subcompositor->present_key)
RenderCancelPresentationCallback (subcompositor->present_key);
subcompositor->present_key = NULL;
/* Cancel any render callback that is currently in progress. */
if (subcompositor->render_key)
RenderCancelCompletionCallback (subcompositor->render_key);
subcompositor->render_key = NULL;
subcompositor->note_frame_data,
-1, -1);
}
static void
@ -2999,12 +3007,22 @@ EndFrame (Subcompositor *subcompositor)
return;
/* Make sure that we wait for the presentation callback or render
callback if they are attached. */
callback if they are attached. If not, ask for a
notification. */
if (!subcompositor->present_key && !subcompositor->render_key)
subcompositor->note_frame (ModeComplete,
subcompositor->frame_counter,
subcompositor->note_frame_data);
{
subcompositor->render_key
= RenderNotifyMsc (subcompositor->target,
RenderCompletedCallback,
subcompositor);
if (!subcompositor->render_key)
subcompositor->note_frame (ModeComplete,
subcompositor->frame_counter,
subcompositor->note_frame_data,
-1, -1);
}
}
void

3
test.c
View file

@ -165,7 +165,8 @@ NoteBounds (void *data, int min_x, int min_y, int max_x, int max_y)
}
static void
NoteFrame (FrameMode mode, uint64_t id, void *data)
NoteFrame (FrameMode mode, uint64_t id, void *data,
uint64_t msc, uint64_t ust)
{
if (mode != ModeComplete && mode != ModePresented)
return;

View file

@ -442,7 +442,6 @@ static void
InternalReposition (XdgPopup *popup)
{
int x, y, width, height;
FrameClock *clock;
/* No parent was specified or the role is detached. */
if (!popup->role || !popup->parent)
@ -458,11 +457,6 @@ InternalReposition (XdgPopup *popup)
SendConfigure (popup, popup->pending_x, popup->pending_y,
width, height);
/* Now, freeze the frame clock, to avoid flicker when the client
commits before ack_configure. */
clock = XLXdgRoleGetFrameClock (popup->role);
XLFrameClockFreeze (clock);
popup->state |= StateAckPosition;
}

View file

@ -36,17 +36,13 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
enum
{
StatePendingFrameCallback = 1,
StateLateFrame = (1 << 1),
StatePendingWindowGeometry = (1 << 2),
StateWaitingForAckConfigure = (1 << 3),
StateWaitingForAckCommit = (1 << 4),
StateLateFrameAcked = (1 << 5),
StateMaybeConfigure = (1 << 6),
StateDirtyFrameExtents = (1 << 7),
StateTemporaryBounds = (1 << 8),
StateFrameStarted = (1 << 9),
StateAllowUnredirection = (1 << 10),
StatePendingBufferRelease = (1 << 11),
StateMaybeConfigure = (1 << 5),
StateDirtyFrameExtents = (1 << 6),
StateTemporaryBounds = (1 << 7),
StatePendingBufferRelease = (1 << 8),
};
typedef struct _XdgRole XdgRole;
@ -128,8 +124,8 @@ struct _XdgRole
/* Buffer release helper. */
BufferReleaseHelper *release_helper;
/* The frame clock. */
FrameClock *clock;
/* The synchronization helper. */
SyncHelper *sync_helper;
/* The pending xdg_surface state. */
XdgState pending_state;
@ -156,6 +152,9 @@ struct _XdgRole
events to wait for before ignoring those coordinates. */
int pending_synth_configure;
/* The pending frame time. */
uint32_t pending_frame_time;
/* The input region of the attached subsurface. */
pixman_region32_t input_region;
@ -244,63 +243,29 @@ FreeReconstrainCallbacks (XdgRole *role)
}
static void
RunFrameCallbacks (Surface *surface, XdgRole *role)
RunFrameCallbacks (Surface *surface, XdgRole *role, uint32_t frame_time)
{
struct timespec time;
uint64_t last_drawn_time;
/* Surface can be NULL for various reasons, especially events
arriving after the shell surface is detached. */
if (!surface)
return;
last_drawn_time = XLFrameClockGetFrameTime (role->clock);
if (!last_drawn_time)
{
clock_gettime (CLOCK_MONOTONIC, &time);
XLSurfaceRunFrameCallbacks (surface, time);
}
else
XLSurfaceRunFrameCallbacksMs (surface, last_drawn_time / 1000);
XLSurfaceRunFrameCallbacksMs (surface, frame_time);
}
static void
RunFrameCallbacksConditionally (XdgRole *role)
RunFrameCallbacksConditionally (XdgRole *role, uint32_t frame_time)
{
if (!(role->state & StatePendingBufferRelease))
RunFrameCallbacks (role->role.surface, role);
RunFrameCallbacks (role->role.surface, role, frame_time);
else if (role->role.surface)
/* weston-simple-shm seems to assume that a frame callback can
only arrive after all buffers have been released. */
role->state |= StatePendingFrameCallback;
}
static Bool
UpdateFrameRefreshPrediction (XdgRole *role)
{
int desync_children;
/* Count the number of desynchronous children attached to this
surface, directly or indirectly. When this number is more than
1, enable frame refresh prediction, which allows separate frames
from subsurfaces to be batched together. */
if (role->role.surface)
{
desync_children = 0;
XLUpdateDesynchronousChildren (role->role.surface,
&desync_children);
if (desync_children)
XLFrameClockSetPredictRefresh (role->clock);
else
XLFrameClockDisablePredictRefresh (role->clock);
return desync_children > 0;
/* weston-simple-shm seems to assume that a frame callback can
only arrive after all buffers have been released. */
role->state |= StatePendingFrameCallback;
role->pending_frame_time = frame_time;
}
return False;
}
static void
@ -319,7 +284,8 @@ AllBuffersReleased (void *data)
released. */
if (surface && role->state & StatePendingFrameCallback)
{
RunFrameCallbacks (surface, role);
RunFrameCallbacks (surface, role,
role->pending_frame_time);
role->state &= ~StatePendingFrameCallback;
}
@ -341,7 +307,7 @@ XLHandleXEventForXdgSurfaces (XEvent *event)
if (role)
{
XLFrameClockHandleFrameEvent (role->clock, event);
SyncHelperHandleFrameEvent (role->sync_helper, event);
return True;
}
@ -354,11 +320,7 @@ XLHandleXEventForXdgSurfaces (XEvent *event)
if (role)
{
/* If resizing is in progress with WM synchronization, don't
handle exposure events, since that causes updates outside
a frame. */
if (!XLFrameClockNeedConfigure (role->clock))
SubcompositorExpose (role->subcompositor, event);
SubcompositorExpose (role->subcompositor, event);
return True;
}
@ -535,11 +497,6 @@ AckConfigure (struct wl_client *client, struct wl_resource *resource,
exposed due to changes in bounds. */
SubcompositorGarbage (xdg_role->subcompositor);
/* Also run frame callbacks if the frame clock is frozen. */
if (XLFrameClockIsFrozen (xdg_role->clock)
&& xdg_role->role.surface)
RunFrameCallbacksConditionally (xdg_role);
#ifdef DEBUG_GEOMETRY_CALCULATION
fprintf (stderr, "Client acknowledged configuration\n");
#endif
@ -560,51 +517,6 @@ static const struct xdg_surface_interface xdg_surface_impl =
.ack_configure = AckConfigure,
};
static void
Unfreeze (XdgRole *role)
{
XLFrameClockUnfreeze (role->clock);
}
static Bool
IsRoleMapped (XdgRole *role)
{
if (!role->impl)
/* This can happen when destroying subsurfaces as part of client
cleanup. */
return False;
return role->impl->funcs.is_window_mapped (&role->role,
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
Commit (Surface *surface, Role *role)
{
@ -627,11 +539,13 @@ Commit (Surface *surface, Role *role)
= xdg_role->pending_state.window_geometry_height;
#ifdef DEBUG_GEOMETRY_CALCULATION
fprintf (stderr, "Client set window geometry to: [%d %d %d %d]\n",
fprintf (stderr, "Client set window geometry to: [%d %d %d %d]\n"
"State is: %d\n",
xdg_role->current_state.window_geometry_x,
xdg_role->current_state.window_geometry_y,
xdg_role->current_state.window_geometry_width,
xdg_role->current_state.window_geometry_height);
xdg_role->current_state.window_geometry_height,
xdg_role->state & StateWaitingForAckConfigure);
#endif
/* Now, clear the "pending window geometry" flag. */
@ -657,35 +571,40 @@ Commit (Surface *surface, Role *role)
xdg_role->state &= ~StateWaitingForAckCommit;
}
/* If the frame clock is frozen but we are no longer waiting for the
configure event to be acknowledged by the client, unfreeze the
frame clock. */
if (!(xdg_role->state & StateWaitingForAckConfigure))
Unfreeze (xdg_role);
if (!(xdg_role->state & StateWaitingForAckCommit))
{
/* Tell the sync helper to update the frame. This will also
complete any resize if necessary. */
SyncHelperUpdate (xdg_role->sync_helper);
/* Now, check if a frame can be drawn, or schedule a frame to be
drawn after this one completes. */
/* Run the after_commit function of the role implementation,
which peforms actions such as posting pending configure
events for built-in resize. */
if (!CheckFrame (xdg_role))
/* The frame cannot be drawn, because the compositor has not yet
drawn a previous frame. */
goto after_commit;
if (xdg_role->impl->funcs.after_commit)
xdg_role->impl->funcs.after_commit (role, surface,
xdg_role->impl);
}
else
/* Now, tell the sync helper to generate a frame.
Many clients do this:
/* The frame can be drawn, so update the window contents now. */
SubcompositorUpdate (xdg_role->subcompositor);
wl_surface@1.frame (new id wl_callback@2)
wl_surface@1.commit ()
/* Do not end frames explicitly. Instead, wait for the
NoteFrameCallback to end the frame. */
and upon receiving a configure event, potentially call:
after_commit:
xdg_surface@3.ack_configure (1)
/* Run the after_commit function of the role implementation, which
peforms actions such as posting pending configure events for
built-in resize. */
but do not commit (or even ack_configure) until the frame
callback is triggered.
That is problematic because the frame clock is not unfrozen
until the commit happens. To work around the problem, tell the
sync helper to check for this situation, and run frame
callbacks if necessary. */
SyncHelperCheckFrameCallback (xdg_role->sync_helper);
if (xdg_role->impl->funcs.after_commit)
xdg_role->impl->funcs.after_commit (role, surface,
xdg_role->impl);
return;
}
@ -753,13 +672,13 @@ ReleaseBacking (XdgRole *role)
/* And the association. */
XLDeleteAssoc (surfaces, role->window);
/* Destroy the sync helper. */
FreeSyncHelper (role->sync_helper);
/* There shouldn't be any children of the subcompositor at this
point. */
SubcompositorFree (role->subcompositor);
/* The frame clock is no longer useful. */
XLFreeFrameClock (role->clock);
/* Free the input region. */
pixman_region32_fini (&role->input_region);
@ -822,19 +741,34 @@ SubsurfaceUpdate (Surface *surface, Role *role)
xdg_role = XdgRoleFromRole (role);
/* If the frame clock is frozen, don't update anything. */
if (XLFrameClockIsFrozen (xdg_role->clock))
return;
if (xdg_role->state & StateWaitingForAckCommit)
{
/* Updates are being postponed until the next commit after
ack_configure.
/* If a frame is already in progress, return, but schedule a frame
to be drawn later. */
Now, tell the sync helper to generate a frame.
Many clients do this:
if (!CheckFrame (xdg_role))
/* The frame cannot be drawn. */
return;
wl_surface@1.frame (new id wl_callback@2)
wl_surface@1.commit ()
/* The frame can be drawn, so update the window contents. */
SubcompositorUpdate (xdg_role->subcompositor);
and upon receiving a configure event, potentially call:
xdg_surface@3.ack_configure (1)
but do not commit (or even ack_configure) until the frame
callback is triggered.
That is problematic because the frame clock is not unfrozen
until the commit happens. To work around the problem, tell
the sync helper to check for this situation, and run frame
callbacks if necessary. */
SyncHelperCheckFrameCallback (xdg_role->sync_helper);
return;
}
/* Tell the sync helper to do an update. */
SyncHelperUpdate (xdg_role->sync_helper);
}
static Window
@ -858,48 +792,6 @@ HandleResourceDestroy (struct wl_resource *resource)
ReleaseBacking (role);
}
static Bool
MaybeRunLateFrame (XdgRole *role)
{
/* If there is a late frame, run it now. Return whether or not a
late frame was run. */
if (role->state & StateLateFrame)
{
if (role->state & StateLateFrameAcked)
XLFrameClockUnfreeze (role->clock);
/* Now apply the state in the late frame. */
SubcompositorUpdate (role->subcompositor);
/* Clear the late frame flag. */
role->state &= ~StateLateFrame;
/* Return True, as a new update has started. */
return True;
}
return False;
}
static void
AfterFrame (FrameClock *clock, void *data)
{
XdgRole *role;
role = data;
/* Run any late frame. */
if (MaybeRunLateFrame (role))
return;
/* If all pending frames have been drawn, run frame callbacks.
Unless some buffers have not yet been released, in which case the
callbacks will be run when they are. */
RunFrameCallbacksConditionally (role);
}
static void
OpaqueRegionChanged (Subcompositor *subcompositor,
void *client_data,
@ -1004,7 +896,7 @@ NoteConfigure (XdgRole *role, XEvent *event)
/* Tell the frame clock how many WM-generated configure events have
arrived. */
XLFrameClockNoteConfigure (role->clock);
SyncHelperNoteConfigureEvent (role->sync_helper);
/* Run reconstrain callbacks. */
RunReconstrainCallbacksForXEvent (role, event);
@ -1040,11 +932,6 @@ NoteBounds (void *data, int min_x, int min_y,
run_reconstrain_callbacks = False;
root_position_initialized = False;
if (XLFrameClockIsFrozen (role->clock))
/* We are waiting for the acknowledgement of a configure event.
Don't resize the window until it's acknowledged. */
return;
if (role->state & StateWaitingForAckCommit)
/* Don't resize the window until all configure events are
acknowledged. We wait for a commit on the xdg_toplevel to do
@ -1160,12 +1047,7 @@ WriteRedirectProperty (XdgRole *role)
{
unsigned long bypass_compositor;
if (role->state & StateAllowUnredirection)
/* The subcompositor determined that the window should be
uncomposited to allow for direct buffer flipping. */
bypass_compositor = 0;
else
bypass_compositor = 2;
bypass_compositor = 2;
XChangeProperty (compositor.display, role->window,
_NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL,
@ -1173,113 +1055,6 @@ WriteRedirectProperty (XdgRole *role)
(unsigned char *) &bypass_compositor, 1);
}
static Bool
WasFrameQueued (XdgRole *role)
{
/* Return whether or not the translator slept before displaying this
frame in response to a frame drawn event. */
return (role->state & StateLateFrame) != 0;
}
static void
NoteFrame (FrameMode mode, uint64_t id, void *data)
{
XdgRole *role;
Bool predict_refresh, urgent;
role = data;
switch (mode)
{
case ModeStarted:
/* Record this frame counter as the pending frame. */
role->pending_frame = id;
if (!(role->state & StateFrameStarted))
{
/* Update whether or not frame refresh prediction is to be
used. */
predict_refresh = UpdateFrameRefreshPrediction (role);
/* A rule of thumb is to never let the compositing manager
read or try to scan out incomplete buffer contents.
Thus, if the client asked for async presentation, Commit
will still wait for the compositor to finish drawing the
last frame.
In addition, how subsurfaces fit into all of this is
unclear. At present, the presentation hint of
subsurfaces is simply discarded, and the hint of the
topmost surface used instead. In addition, asynchronous
subsurfaces will prevent async presentation from taking
place at all. */
urgent = (WasFrameQueued (role)
&& !predict_refresh
&& role->role.surface
&& (role->role.surface->current_state.presentation_hint
== PresentationHintAsync));
if (XLFrameClockStartFrame (role->clock, urgent))
role->state |= StateFrameStarted;
}
break;
case ModeNotifyDisablePresent:
/* The subcompositor will draw to the frame directly, so make
the compositing manager redirect the frame again. */
if (role->state & StateAllowUnredirection)
{
role->state &= ~StateAllowUnredirection;
WriteRedirectProperty (role);
}
break;
case ModePresented:
case ModeComplete:
/* The frame was completed. */
if (id == role->pending_frame)
{
/* End the frame. */
XLFrameClockEndFrame (role->clock);
/* No frame was started clock-side for this frame. That
means programs waiting for frame callbacks will not get
any, so the frame callbacks must be run by hand. */
if (!(role->state & StateFrameStarted)
|| !IsRoleMapped (role))
{
if (MaybeRunLateFrame (role))
return;
RunFrameCallbacksConditionally (role);
}
/* Clear the frame completed flag. */
role->state &= ~StateFrameStarted;
if (mode == ModePresented
&& renderer_flags & SupportsDirectPresent)
{
/* Since a presentation was successful, assume future
frames will be presented as well. In that case, let
the compositing manager unredirect the window, so
buffers can be directly flipped to the screen. */
if (!(role->state & StateAllowUnredirection))
{
role->state |= StateAllowUnredirection;
WriteRedirectProperty (role);
}
}
}
}
}
static void
ResizeForMap (XdgRole *role)
{
@ -1373,21 +1148,52 @@ Rescale (Surface *surface, Role *role)
}
static void
HandleFreeze (void *data)
HandleResize (void *data, Bool only_frame)
{
XdgRole *role;
role = data;
if (only_frame)
{
SyncHelperCheckFrameCallback (role->sync_helper);
return;
}
/* _NET_WM_SYNC_REQUEST events should be succeeded by a
ConfigureNotify event. */
role->state |= StateWaitingForAckConfigure;
role->state |= StateWaitingForAckCommit;
/* Cancel any pending frame. Nothing should be displayed while an
ack_configure is pending. */
SyncHelperClearPendingFrame (role->sync_helper);
/* This flag means the WaitingForAckConfigure was caused by a
_NET_WM_SYNC_REQUEST, and the following ConfigureNotify event
might not lead to a configure event being sent. */
role->state |= StateMaybeConfigure;
/* If a freeze comes between commit and configure, then clients will
hang indefinitely waiting for _NET_WM_FRAME_DRAWN. Make the sync
helper check for this situation. */
SyncHelperCheckFrameCallback (role->sync_helper);
#ifdef DEBUG_GEOMETRY_CALCULATION
fprintf (stderr, "Waiting for ack_configure (?)...\n");
#endif
}
static Bool
CheckFastForward (void *data)
{
XdgRole *role;
/* Return whether or not it is ok to fast forward the frame
counter while ending a frame. */
role = data;
return !(role->state & StateWaitingForAckCommit);
}
static void
@ -1445,6 +1251,15 @@ Activate (Surface *surface, Role *role, int deviceid,
activator_surface);
}
static void
HandleFrameCallback (void *data, uint32_t frame_time)
{
XdgRole *role;
role = data;
RunFrameCallbacksConditionally (role, frame_time);
}
void
XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
uint32_t id, struct wl_resource *surface_resource)
@ -1538,8 +1353,13 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
RenderSetClient (role->target, client);
role->subcompositor = MakeSubcompositor ();
role->clock = XLMakeFrameClockForWindow (role->window);
XLFrameClockSetFreezeCallback (role->clock, HandleFreeze, role);
role->sync_helper = MakeSyncHelper (role->subcompositor,
role->window,
role->target,
HandleFrameCallback,
&role->role);
SyncHelperSetResizeCallback (role->sync_helper, HandleResize,
CheckFastForward);
SubcompositorSetTarget (role->subcompositor, &role->target);
SubcompositorSetInputCallback (role->subcompositor,
@ -1548,8 +1368,6 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
OpaqueRegionChanged, role);
SubcompositorSetBoundsCallback (role->subcompositor,
NoteBounds, role);
SubcompositorSetNoteFrameCallback (role->subcompositor,
NoteFrame, role);
XLSelectStandardEvents (role->window);
XLMakeAssoc (surfaces, role->window, role);
@ -1557,9 +1375,6 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
If it does, frame synchronization will not work. */
WriteRedirectProperty (role);
/* Initialize frame callbacks. */
XLFrameClockAfterFrame (role->clock, AfterFrame, role);
if (!XLSurfaceAttachRole (surface, &role->role))
abort ();
@ -1626,6 +1441,10 @@ XLXdgRoleSendConfigure (Role *role, uint32_t serial)
xdg_role->state |= StateWaitingForAckConfigure;
xdg_role->state |= StateWaitingForAckCommit;
/* Cancel any pending frame. Nothing should be displayed while an
ack_configure is pending. */
SyncHelperClearPendingFrame (xdg_role->sync_helper);
/* See the comment under XLXdgRoleSetBoundsSize. */
xdg_role->state &= ~StateTemporaryBounds;
@ -1912,7 +1731,7 @@ XLXdgRoleReconstrain (Role *role, XEvent *event)
/* If event is a configure event, tell the frame clock about it. */
if (event->type == ConfigureNotify)
XLFrameClockNoteConfigure (xdg_role->clock);
SyncHelperNoteConfigureEvent (xdg_role->sync_helper);
}
void
@ -1938,15 +1757,6 @@ XLXdgRoleMoveBy (Role *role, int west, int north)
xdg_role->pending_synth_configure++;
}
FrameClock *
XLXdgRoleGetFrameClock (Role *role)
{
XdgRole *xdg_role;
xdg_role = XdgRoleFromRole (role);
return xdg_role->clock;
}
void
XLInitXdgSurfaces (void)
{
@ -2043,9 +1853,6 @@ XLXdgRoleNoteRejectedConfigure (Role *role)
xdg_role->state &= ~StateWaitingForAckConfigure;
xdg_role->state &= ~StateWaitingForAckCommit;
xdg_role->state &= ~StateMaybeConfigure;
/* Unfreeze the frame clock now. */
XLFrameClockUnfreeze (xdg_role->clock);
}
}