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 either be \fBpicture\fP (the XRender based compositor) or \fBegl\fP
(the OpenGL ES 2.0 based compositor). (the OpenGL ES 2.0 based compositor).
.TP .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) .B wmProtocols\fP (class \fBWmProtocols\fP)
Comma-separated list of window manager protocols, similar to Comma-separated list of window manager protocols, similar to
\fBinputStyles\fP, that the protocol translator should enable or \fBinputStyles\fP, that the protocol translator should enable or

View file

@ -18,8 +18,8 @@ MakeSubdirs($(SUBDIRS))
DependSubdirs($(SUBDIRS)) DependSubdirs($(SUBDIRS))
#endif #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 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 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 GENHEADERS = transfer_atoms.h drm_modifiers.h
HEADER = $(GENHEADERS) compositor.h HEADER = $(GENHEADERS) compositor.h

View file

@ -160,6 +160,7 @@ typedef struct _ShmFormat ShmFormat;
typedef enum _Operation Operation; typedef enum _Operation Operation;
typedef enum _BufferTransform BufferTransform; typedef enum _BufferTransform BufferTransform;
typedef enum _RenderMode RenderMode;
typedef void *IdleCallbackKey; typedef void *IdleCallbackKey;
typedef void *PresentCompletionKey; typedef void *PresentCompletionKey;
@ -169,8 +170,8 @@ typedef void (*DmaBufSuccessFunc) (RenderBuffer, void *);
typedef void (*DmaBufFailureFunc) (void *); typedef void (*DmaBufFailureFunc) (void *);
typedef void (*BufferIdleFunc) (RenderBuffer, void *); typedef void (*BufferIdleFunc) (RenderBuffer, void *);
typedef void (*PresentCompletionFunc) (void *); typedef void (*PresentCompletionFunc) (void *, uint64_t, uint64_t);
typedef void (*RenderCompletionFunc) (void *); typedef void (*RenderCompletionFunc) (void *, uint64_t, uint64_t);
enum _BufferTransform enum _BufferTransform
{ {
@ -314,6 +315,14 @@ enum
SupportsDirectPresent = 1 << 4, SupportsDirectPresent = 1 << 4,
}; };
enum _RenderMode
{
/* Do not synchronize rendering. */
RenderModeAsync,
/* Synchronize rendering with the vertical refresh. */
RenderModeVsync,
};
struct _RenderFuncs struct _RenderFuncs
{ {
/* Initialize the visual and depth. */ /* Initialize the visual and depth. */
@ -325,6 +334,12 @@ struct _RenderFuncs
/* Create a rendering target for the given pixmap. */ /* Create a rendering target for the given pixmap. */
RenderTarget (*target_from_pixmap) (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 /* Set the client associated with the target. This allows some
extra pixmap memory allocation tracking to be done. */ extra pixmap memory allocation tracking to be done. */
void (*set_client) (RenderTarget, struct wl_client *); void (*set_client) (RenderTarget, struct wl_client *);
@ -423,6 +438,11 @@ struct _RenderFuncs
pixman_region32_t *, pixman_region32_t *,
PresentCompletionFunc, void *); 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. */ /* Cancel the given presentation callback. */
void (*cancel_presentation_callback) (PresentCompletionKey); void (*cancel_presentation_callback) (PresentCompletionKey);
@ -542,6 +562,7 @@ extern void InitRenderers (void);
extern RenderTarget RenderTargetFromWindow (Window, unsigned long); extern RenderTarget RenderTargetFromWindow (Window, unsigned long);
extern RenderTarget RenderTargetFromPixmap (Pixmap); extern RenderTarget RenderTargetFromPixmap (Pixmap);
extern Bool RenderSetRenderMode (RenderTarget, RenderMode, uint64_t);
extern void RenderSetClient (RenderTarget, struct wl_client *); extern void RenderSetClient (RenderTarget, struct wl_client *);
extern void RenderSetStandardEventMask (RenderTarget, unsigned long); extern void RenderSetStandardEventMask (RenderTarget, unsigned long);
extern void RenderNoteTargetSize (RenderTarget, int, int); extern void RenderNoteTargetSize (RenderTarget, int, int);
@ -568,6 +589,8 @@ extern PresentCompletionKey RenderPresentToWindow (RenderTarget, RenderBuffer,
pixman_region32_t *, pixman_region32_t *,
PresentCompletionFunc, PresentCompletionFunc,
void *); void *);
extern RenderCompletionKey RenderNotifyMsc (RenderTarget, RenderCompletionFunc,
void *);
extern void RenderCancelPresentationCallback (PresentCompletionKey); extern void RenderCancelPresentationCallback (PresentCompletionKey);
extern DrmFormat *RenderGetDrmFormats (int *); extern DrmFormat *RenderGetDrmFormats (int *);
@ -767,7 +790,6 @@ typedef enum _FrameMode FrameMode;
enum _FrameMode enum _FrameMode
{ {
ModeStarted, ModeStarted,
ModeNotifyDisablePresent,
ModeComplete, ModeComplete,
ModePresented, ModePresented,
}; };
@ -798,7 +820,8 @@ extern void SubcompositorSetBoundsCallback (Subcompositor *,
void *); void *);
extern void SubcompositorSetNoteFrameCallback (Subcompositor *, extern void SubcompositorSetNoteFrameCallback (Subcompositor *,
void (*) (FrameMode, uint64_t, void (*) (FrameMode, uint64_t,
void *), void *, uint64_t,
uint64_t),
void *); void *);
extern void SubcompositorBounds (Subcompositor *, int *, int *, int *, int *); extern void SubcompositorBounds (Subcompositor *, int *, int *, int *, int *);
extern void SubcompositorSetProjectiveTransform (Subcompositor *, int, int); extern void SubcompositorSetProjectiveTransform (Subcompositor *, int, int);
@ -1304,24 +1327,20 @@ typedef struct _FrameClock FrameClock;
extern FrameClock *XLMakeFrameClockForWindow (Window); extern FrameClock *XLMakeFrameClockForWindow (Window);
extern Bool XLFrameClockFrameInProgress (FrameClock *); extern Bool XLFrameClockFrameInProgress (FrameClock *);
extern void XLFrameClockAfterFrame (FrameClock *, void (*) (FrameClock *, extern void XLFrameClockAfterFrame (FrameClock *, void (*) (FrameClock *,
void *), void *,
uint64_t),
void *); void *);
extern void XLFrameClockHandleFrameEvent (FrameClock *, XEvent *); extern void XLFrameClockHandleFrameEvent (FrameClock *, XEvent *);
extern Bool XLFrameClockStartFrame (FrameClock *, Bool); extern Bool XLFrameClockStartFrame (FrameClock *, Bool);
extern void XLFrameClockEndFrame (FrameClock *); extern void XLFrameClockEndFrame (FrameClock *);
extern Bool XLFrameClockIsFrozen (FrameClock *);
extern Bool XLFrameClockCanBatch (FrameClock *); extern Bool XLFrameClockCanBatch (FrameClock *);
extern Bool XLFrameClockNeedConfigure (FrameClock *);
extern void XLFrameClockFreeze (FrameClock *);
extern void XLFrameClockUnfreeze (FrameClock *);
extern void XLFreeFrameClock (FrameClock *); extern void XLFreeFrameClock (FrameClock *);
extern Bool XLFrameClockSyncSupported (void); extern Bool XLFrameClockSyncSupported (void);
extern Bool XLFrameClockCanRunFrame (FrameClock *);
extern void XLFrameClockSetPredictRefresh (FrameClock *); extern void XLFrameClockSetPredictRefresh (FrameClock *);
extern void XLFrameClockDisablePredictRefresh (FrameClock *); extern void XLFrameClockDisablePredictRefresh (FrameClock *);
extern void XLFrameClockSetFreezeCallback (FrameClock *, void (*) (void *), extern void XLFrameClockSetFreezeCallback (FrameClock *, void (*) (void *,
void *); Bool),
extern uint64_t XLFrameClockGetFrameTime (FrameClock *); Bool (*) (void *), void *);
extern void XLFrameClockNoteConfigure (FrameClock *); extern void XLFrameClockNoteConfigure (FrameClock *);
extern void *XLAddCursorClockCallback (void (*) (void *, struct timespec), extern void *XLAddCursorClockCallback (void (*) (void *, struct timespec),
void *); void *);
@ -1905,6 +1924,25 @@ extern void XLInitXdgActivation (void);
extern void XLInitTearingControl (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. */ /* Utility functions that don't belong in a specific file. */
#define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0]) #define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0])

View file

@ -33,23 +33,20 @@ enum
}; };
/* Whether or not the compositor supports frame synchronization. */ /* Whether or not the compositor supports frame synchronization. */
static Bool frame_sync_supported; static Bool frame_sync_supported;
/* Timer used for cursor animations. */ /* Timer used for cursor animations. */
static Timer *cursor_clock; static Timer *cursor_clock;
/* How many cursors want cursor animations. */ /* How many cursors want cursor animations. */
static int cursor_count; static int cursor_count;
struct _FrameClockCallback struct _FrameClockCallback
{ {
/* Function called once a frame is completely written to display and /* Function called once a frame is completely written to display.
(ideally, whether or not this actually works depends on various The last arg is the time at which the frame was written, or
different factors) enters vblank. */ (uint64_t) -1. */
void (*frame) (FrameClock *, void *); void (*frame) (FrameClock *, void *, uint64_t);
/* Data that function is called with. */ /* Data that function is called with. */
void *data; void *data;
@ -60,28 +57,12 @@ struct _FrameClockCallback
struct _FrameClock 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, /* The value of the frame currently being drawn in this frame clock,
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;
/* A timer used as a fake synchronization source if frame /* List of frame clock callbacks. */
synchronization is not supported. */ FrameClockCallback callbacks;
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;
/* The wanted configure value. */ /* The wanted configure value. */
uint64_t configure_id; uint64_t configure_id;
@ -103,6 +84,28 @@ struct _FrameClock
/* The last known presentation time. */ /* The last known presentation time. */
uint64_t last_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. */ /* The refresh interval. */
uint32_t refresh_interval; uint32_t refresh_interval;
@ -114,10 +117,8 @@ struct _FrameClock
is put in place. */ is put in place. */
uint32_t got_configure_count, pending_configure_count; uint32_t got_configure_count, pending_configure_count;
/* Whether or not configury is in progress, and whether or not this /* Whether or not configury is in progress. */
is frozen, and whether or not the frame shouldn't actually be Bool need_configure;
unfrozen until EndFrame. */
Bool need_configure, frozen, frozen_until_end_frame;
/* Whether or not EndFrame was called after StartFrame. */ /* Whether or not EndFrame was called after StartFrame. */
Bool end_frame_called; Bool end_frame_called;
@ -246,9 +247,16 @@ HandleEndFrame (Timer *timer, void *data, struct timespec time)
/* Forward declarations. */ /* Forward declarations. */
static void RunFrameCallbacks (FrameClock *); static void RunFrameCallbacks (FrameClock *, uint64_t);
static Bool StartFrame (FrameClock *, Bool, Bool); static Bool StartFrame (FrameClock *, Bool, Bool);
static void
BumpFrame (FrameClock *clock)
{
StartFrame (clock, True, False);
EndFrame (clock);
}
static void static void
FreezeForValue (FrameClock *clock, uint64_t counter_value) 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 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 by the time the freeze itself. Start a new frame to bring up to
date contents to the display. */ date contents to the display. */
if (clock->pending_configure_count <= clock->got_configure_count) if (clock->pending_configure_count <= clock->got_configure_count)
need_empty_frame = True; need_empty_frame = True;
@ -282,14 +291,6 @@ FreezeForValue (FrameClock *clock, uint64_t counter_value)
EndFrame (clock); 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 /* The reason for clearing in_frame is that otherwise a future
Commit after the configuration is acknowledged will not be able Commit after the configuration is acknowledged will not be able
to start a new frame and restart the frame clock. */ 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->need_configure = True;
clock->configure_id = counter_value; clock->configure_id = counter_value;
if (need_empty_frame) if (need_empty_frame
{ /* If the surface has not yet ack_configure'd, don't fast
/* Request a new frame and don't allow starting frames until it forward the frame. The sync counter will be set at the next
finishes. See above for why. clock->in_frame is False for commit after ack_configure. */
now to really force the frame to happen. */ && 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); clock->freeze_callback (clock->freeze_callback_data,
EndFrame (clock); /* Only run the freeze callback if this
} freeze is up to date. If it is not,
else frame callbacks still have to be run,
clock->frozen = True; which is what the following parameter
means. */
need_empty_frame);
return; return;
} }
@ -410,12 +417,6 @@ PostEndFrame (FrameClock *clock)
static Bool static Bool
StartFrame (FrameClock *clock, Bool urgent, Bool predict) 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->in_frame)
{ {
if (clock->end_frame_timer if (clock->end_frame_timer
@ -487,11 +488,6 @@ StartFrame (FrameClock *clock, Bool urgent, Bool predict)
static void static void
EndFrame (FrameClock *clock) 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 /* Signal that end_frame was called and it is now safe to finish the
frame from the timer. */ frame from the timer. */
clock->end_frame_called = True; clock->end_frame_called = True;
@ -521,8 +517,10 @@ EndFrame (FrameClock *clock)
/* The frame has ended. Freeze the frame clock if there is a /* The frame has ended. Freeze the frame clock if there is a
pending sync value. */ pending sync value. */
if (clock->pending_sync_value) if (clock->pending_sync_value)
FreezeForValue (clock, clock->pending_sync_value); FreezeForValue (clock, clock->pending_sync_value);
clock->pending_sync_value = 0; clock->pending_sync_value = 0;
if (!frame_sync_supported) if (!frame_sync_supported)
@ -551,7 +549,7 @@ FreeFrameCallbacks (FrameClock *clock)
} }
static void static void
RunFrameCallbacks (FrameClock *clock) RunFrameCallbacks (FrameClock *clock, uint64_t frame_drawn_time)
{ {
FrameClockCallback *callback; FrameClockCallback *callback;
@ -559,7 +557,8 @@ RunFrameCallbacks (FrameClock *clock)
while (callback != &clock->callbacks) while (callback != &clock->callbacks)
{ {
callback->frame (clock, callback->data); callback->frame (clock, callback->data,
frame_drawn_time);
callback = callback->next; callback = callback->next;
} }
} }
@ -575,13 +574,14 @@ NoteFakeFrame (Timer *timer, void *data, struct timespec time)
&& (clock->finished_frame_id == clock->next_frame_id)) && (clock->finished_frame_id == clock->next_frame_id))
{ {
clock->in_frame = False; clock->in_frame = False;
RunFrameCallbacks (clock); RunFrameCallbacks (clock, (uint64_t) -1);
} }
} }
void void
XLFrameClockAfterFrame (FrameClock *clock, XLFrameClockAfterFrame (FrameClock *clock,
void (*frame_func) (FrameClock *, void *), void (*frame_func) (FrameClock *, void *,
uint64_t),
void *data) void *data)
{ {
FrameClockCallback *callback; FrameClockCallback *callback;
@ -613,36 +613,9 @@ XLFrameClockEndFrame (FrameClock *clock)
Bool Bool
XLFrameClockFrameInProgress (FrameClock *clock) 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; 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 void
XLFrameClockHandleFrameEvent (FrameClock *clock, XEvent *event) XLFrameClockHandleFrameEvent (FrameClock *clock, XEvent *event)
{ {
@ -671,9 +644,11 @@ XLFrameClockHandleFrameEvent (FrameClock *clock, XEvent *event)
/* Actually compute the time and save it. */ /* Actually compute the time and save it. */
clock->last_frame_time = low | (high << 32); clock->last_frame_time = low | (high << 32);
/* Run any frame callbacks, since drawing has finished. */ /* Clear the in_frame flag. */
clock->in_frame = False; 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) if (clock->frame_timings_id == -1)
{ {
@ -742,14 +717,11 @@ XLFrameClockHandleFrameEvent (FrameClock *clock, XEvent *event)
/* If a frame is in progress, postpone this frame /* If a frame is in progress, postpone this frame
synchronization message. */ synchronization message. */
if (clock->in_frame && !clock->end_frame_called) if (clock->in_frame && !clock->end_frame_called)
clock->pending_sync_value = value; clock->pending_sync_value = value;
else else
FreezeForValue (clock, value); 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; return clock;
} }
void
XLFrameClockUnfreeze (FrameClock *clock)
{
clock->frozen = False;
}
Bool
XLFrameClockNeedConfigure (FrameClock *clock)
{
return clock->need_configure;
}
Bool Bool
XLFrameClockSyncSupported (void) XLFrameClockSyncSupported (void)
{ {
return frame_sync_supported; return frame_sync_supported;
} }
Bool
XLFrameClockIsFrozen (FrameClock *clock)
{
return clock->frozen;
}
Bool Bool
XLFrameClockCanBatch (FrameClock *clock) XLFrameClockCanBatch (FrameClock *clock)
{ {
@ -884,21 +838,14 @@ XLFrameClockDisablePredictRefresh (FrameClock *clock)
} }
void void
XLFrameClockSetFreezeCallback (FrameClock *clock, void (*callback) (void *), XLFrameClockSetFreezeCallback (FrameClock *clock, void (*callback) (void *,
Bool),
Bool (*fast_forward_callback) (void *),
void *data) void *data)
{ {
clock->freeze_callback = callback; clock->freeze_callback = callback;
clock->freeze_callback_data = data; clock->freeze_callback_data = data;
} clock->can_forward_sync_counter = fast_forward_callback;
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;
} }
void void

View file

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

View file

@ -220,6 +220,9 @@ enum
struct _PictureTarget struct _PictureTarget
{ {
/* The next frame number. */
uint64_t next_msc;
/* The XID of the picture. */ /* The XID of the picture. */
Picture picture; Picture picture;
@ -265,6 +268,9 @@ struct _PictureTarget
/* List of buffers that were used in the course of an update. */ /* List of buffers that were used in the course of an update. */
XLList *buffers_used; XLList *buffers_used;
/* What rendering mode should be used. */
RenderMode render_mode;
}; };
struct _DrmFormatInfo struct _DrmFormatInfo
@ -451,9 +457,6 @@ static BufferActivityRecord all_activity;
/* List of all presentations that have not yet been completed. */ /* List of all presentations that have not yet been completed. */
static PresentCompletionCallback all_completion_callbacks; static PresentCompletionCallback all_completion_callbacks;
/* Whether or not direct presentation should be used. */
static Bool use_direct_presentation;
/* The device nodes of each provider. */ /* The device nodes of each provider. */
static dev_t *render_devices; static dev_t *render_devices;
@ -839,10 +842,18 @@ SwapBackBuffers (PictureTarget *target, pixman_region32_t *damage)
if (!present_serial) if (!present_serial)
present_serial++; present_serial++;
if (target->render_mode == RenderModeAsync)
XPresentPixmap (compositor.display, target->window, XPresentPixmap (compositor.display, target->window,
back_buffer->pixmap, present_serial, back_buffer->pixmap, present_serial,
None, region, 0, 0, None, None, fence, None, region, 0, 0, None, None, fence,
PresentOptionAsync, 0, 0, 0, NULL, 0); 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 /* Mark the back buffer as busy, and the other back buffer as having
been released. */ been released. */
@ -1024,42 +1035,6 @@ PickVisual (int *depth)
return NULL; 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 static void
AddAdditionalModifier (const char *name) AddAdditionalModifier (const char *name)
{ {
@ -1154,12 +1129,6 @@ InitAdditionalModifiers (void)
classlist[0] = resource_quark; classlist[0] = resource_quark;
classlist[2] = NULLQUARK; 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, if (XrmQGetResource (rdb, namelist, classlist,
&type, &value) &type, &value)
&& type == QString) && type == QString)
@ -1190,14 +1159,10 @@ InitRenderFuncs (void)
return False; return False;
} }
/* Figure out whether or not the user wants synchronized
presentation. */
InitSynchronizedPresentation ();
/* Find out what additional modifiers the user wants. */ /* Find out what additional modifiers the user wants. */
InitAdditionalModifiers (); InitAdditionalModifiers ();
if (use_direct_presentation) /* Add the direct presentation support flag. */
AddRenderFlag (SupportsDirectPresent); AddRenderFlag (SupportsDirectPresent);
/* Create an unmapped, InputOnly window, that is used to receive /* Create an unmapped, InputOnly window, that is used to receive
@ -1276,16 +1241,32 @@ TargetFromDrawable (Drawable drawable, Window window,
return (RenderTarget) (void *) target; return (RenderTarget) (void *) target;
} }
static RenderTarget
TargetFromWindow (Window window, unsigned long event_mask)
{
return TargetFromDrawable (window, window, event_mask);
}
static RenderTarget static RenderTarget
TargetFromPixmap (Pixmap pixmap) TargetFromPixmap (Pixmap pixmap)
{ {
return TargetFromDrawable (pixmap, None, NoEventMask); return TargetFromDrawable (pixmap, None, NoEventMask);
} }
static RenderTarget static Bool
TargetFromWindow (Window window, unsigned long event_mask) 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 static void
@ -1941,21 +1922,16 @@ PresentToWindow (RenderTarget target, RenderBuffer source,
else else
region = None; region = None;
if (use_direct_presentation) if (pict_target->render_mode == RenderModeAsync)
{ /* Present the pixmap asynchronously at an msc of 0. */
/* 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. */
XPresentPixmap (compositor.display, pict_target->window, buffer->pixmap, 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); 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) if (region)
XFixesDestroyRegion (compositor.display, region); XFixesDestroyRegion (compositor.display, region);
@ -1987,6 +1963,30 @@ PresentToWindow (RenderTarget target, RenderBuffer source,
return callback_rec; 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. */ /* Cancel the given presentation callback. */
static void static void
@ -2006,6 +2006,7 @@ static RenderFuncs picture_render_funcs =
.init_render_funcs = InitRenderFuncs, .init_render_funcs = InitRenderFuncs,
.target_from_window = TargetFromWindow, .target_from_window = TargetFromWindow,
.target_from_pixmap = TargetFromPixmap, .target_from_pixmap = TargetFromPixmap,
.set_render_mode = SetRenderMode,
.set_client = SetClient, .set_client = SetClient,
.set_standard_event_mask = SetStandardEventMask, .set_standard_event_mask = SetStandardEventMask,
.note_target_size = NoteTargetSize, .note_target_size = NoteTargetSize,
@ -2023,6 +2024,7 @@ static RenderFuncs picture_render_funcs =
.delete_fence = DeleteFence, .delete_fence = DeleteFence,
.get_finish_fence = GetFinishFence, .get_finish_fence = GetFinishFence,
.present_to_window = PresentToWindow, .present_to_window = PresentToWindow,
.notify_msc = NotifyMsc,
.cancel_presentation_callback = CancelPresentationCallback, .cancel_presentation_callback = CancelPresentationCallback,
}; };
@ -3379,7 +3381,7 @@ HandlePresentCompleteNotify (XPresentCompleteNotifyEvent *complete)
{ {
/* The presentation is complete. Run and unlink the /* The presentation is complete. Run and unlink the
callback. */ callback. */
last->function (last->data); last->function (last->data, complete->msc, complete->ust);
last->next->last = last->last; last->next->last = last->last;
last->last->next = last->next; last->last->next = last->next;

View file

@ -80,6 +80,16 @@ RenderTargetFromPixmap (Pixmap pixmap)
return render_funcs.target_from_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 void
RenderSetClient (RenderTarget target, struct wl_client *client) RenderSetClient (RenderTarget target, struct wl_client *client)
{ {
@ -215,6 +225,16 @@ RenderPresentToWindow (RenderTarget target, RenderBuffer source,
data); 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 void
RenderCancelPresentationCallback (PresentCompletionKey key) RenderCancelPresentationCallback (PresentCompletionKey key)
{ {

View file

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

View file

@ -442,7 +442,6 @@ static void
InternalReposition (XdgPopup *popup) InternalReposition (XdgPopup *popup)
{ {
int x, y, width, height; int x, y, width, height;
FrameClock *clock;
/* No parent was specified or the role is detached. */ /* No parent was specified or the role is detached. */
if (!popup->role || !popup->parent) if (!popup->role || !popup->parent)
@ -458,11 +457,6 @@ InternalReposition (XdgPopup *popup)
SendConfigure (popup, popup->pending_x, popup->pending_y, SendConfigure (popup, popup->pending_x, popup->pending_y,
width, height); 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; popup->state |= StateAckPosition;
} }

View file

@ -36,17 +36,13 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
enum enum
{ {
StatePendingFrameCallback = 1, StatePendingFrameCallback = 1,
StateLateFrame = (1 << 1),
StatePendingWindowGeometry = (1 << 2), StatePendingWindowGeometry = (1 << 2),
StateWaitingForAckConfigure = (1 << 3), StateWaitingForAckConfigure = (1 << 3),
StateWaitingForAckCommit = (1 << 4), StateWaitingForAckCommit = (1 << 4),
StateLateFrameAcked = (1 << 5), StateMaybeConfigure = (1 << 5),
StateMaybeConfigure = (1 << 6), StateDirtyFrameExtents = (1 << 6),
StateDirtyFrameExtents = (1 << 7), StateTemporaryBounds = (1 << 7),
StateTemporaryBounds = (1 << 8), StatePendingBufferRelease = (1 << 8),
StateFrameStarted = (1 << 9),
StateAllowUnredirection = (1 << 10),
StatePendingBufferRelease = (1 << 11),
}; };
typedef struct _XdgRole XdgRole; typedef struct _XdgRole XdgRole;
@ -128,8 +124,8 @@ struct _XdgRole
/* Buffer release helper. */ /* Buffer release helper. */
BufferReleaseHelper *release_helper; BufferReleaseHelper *release_helper;
/* The frame clock. */ /* The synchronization helper. */
FrameClock *clock; SyncHelper *sync_helper;
/* The pending xdg_surface state. */ /* The pending xdg_surface state. */
XdgState pending_state; XdgState pending_state;
@ -156,6 +152,9 @@ struct _XdgRole
events to wait for before ignoring those coordinates. */ events to wait for before ignoring those coordinates. */
int pending_synth_configure; int pending_synth_configure;
/* The pending frame time. */
uint32_t pending_frame_time;
/* The input region of the attached subsurface. */ /* The input region of the attached subsurface. */
pixman_region32_t input_region; pixman_region32_t input_region;
@ -244,63 +243,29 @@ FreeReconstrainCallbacks (XdgRole *role)
} }
static void 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 /* Surface can be NULL for various reasons, especially events
arriving after the shell surface is detached. */ arriving after the shell surface is detached. */
if (!surface) if (!surface)
return; return;
last_drawn_time = XLFrameClockGetFrameTime (role->clock); XLSurfaceRunFrameCallbacksMs (surface, frame_time);
if (!last_drawn_time)
{
clock_gettime (CLOCK_MONOTONIC, &time);
XLSurfaceRunFrameCallbacks (surface, time);
}
else
XLSurfaceRunFrameCallbacksMs (surface, last_drawn_time / 1000);
} }
static void static void
RunFrameCallbacksConditionally (XdgRole *role) RunFrameCallbacksConditionally (XdgRole *role, uint32_t frame_time)
{ {
if (!(role->state & StatePendingBufferRelease)) if (!(role->state & StatePendingBufferRelease))
RunFrameCallbacks (role->role.surface, role); RunFrameCallbacks (role->role.surface, role, frame_time);
else if (role->role.surface) else if (role->role.surface)
{
/* weston-simple-shm seems to assume that a frame callback can /* weston-simple-shm seems to assume that a frame callback can
only arrive after all buffers have been released. */ only arrive after all buffers have been released. */
role->state |= StatePendingFrameCallback; role->state |= StatePendingFrameCallback;
role->pending_frame_time = frame_time;
} }
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;
}
return False;
} }
static void static void
@ -319,7 +284,8 @@ AllBuffersReleased (void *data)
released. */ released. */
if (surface && role->state & StatePendingFrameCallback) if (surface && role->state & StatePendingFrameCallback)
{ {
RunFrameCallbacks (surface, role); RunFrameCallbacks (surface, role,
role->pending_frame_time);
role->state &= ~StatePendingFrameCallback; role->state &= ~StatePendingFrameCallback;
} }
@ -341,7 +307,7 @@ XLHandleXEventForXdgSurfaces (XEvent *event)
if (role) if (role)
{ {
XLFrameClockHandleFrameEvent (role->clock, event); SyncHelperHandleFrameEvent (role->sync_helper, event);
return True; return True;
} }
@ -354,10 +320,6 @@ XLHandleXEventForXdgSurfaces (XEvent *event)
if (role) 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; return True;
@ -535,11 +497,6 @@ AckConfigure (struct wl_client *client, struct wl_resource *resource,
exposed due to changes in bounds. */ exposed due to changes in bounds. */
SubcompositorGarbage (xdg_role->subcompositor); 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 #ifdef DEBUG_GEOMETRY_CALCULATION
fprintf (stderr, "Client acknowledged configuration\n"); fprintf (stderr, "Client acknowledged configuration\n");
#endif #endif
@ -560,51 +517,6 @@ static const struct xdg_surface_interface xdg_surface_impl =
.ack_configure = AckConfigure, .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 static void
Commit (Surface *surface, Role *role) Commit (Surface *surface, Role *role)
{ {
@ -627,11 +539,13 @@ Commit (Surface *surface, Role *role)
= xdg_role->pending_state.window_geometry_height; = xdg_role->pending_state.window_geometry_height;
#ifdef DEBUG_GEOMETRY_CALCULATION #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_x,
xdg_role->current_state.window_geometry_y, xdg_role->current_state.window_geometry_y,
xdg_role->current_state.window_geometry_width, 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 #endif
/* Now, clear the "pending window geometry" flag. */ /* Now, clear the "pending window geometry" flag. */
@ -657,35 +571,40 @@ Commit (Surface *surface, Role *role)
xdg_role->state &= ~StateWaitingForAckCommit; xdg_role->state &= ~StateWaitingForAckCommit;
} }
/* If the frame clock is frozen but we are no longer waiting for the if (!(xdg_role->state & StateWaitingForAckCommit))
configure event to be acknowledged by the client, unfreeze the {
frame clock. */ /* Tell the sync helper to update the frame. This will also
if (!(xdg_role->state & StateWaitingForAckConfigure)) complete any resize if necessary. */
Unfreeze (xdg_role); SyncHelperUpdate (xdg_role->sync_helper);
/* Now, check if a frame can be drawn, or schedule a frame to be /* Run the after_commit function of the role implementation,
drawn after this one completes. */ 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;
/* The frame can be drawn, so update the window contents now. */
SubcompositorUpdate (xdg_role->subcompositor);
/* Do not end frames explicitly. Instead, wait for the
NoteFrameCallback to end the frame. */
after_commit:
/* Run the after_commit function of the role implementation, which
peforms actions such as posting pending configure events for
built-in resize. */
if (xdg_role->impl->funcs.after_commit) if (xdg_role->impl->funcs.after_commit)
xdg_role->impl->funcs.after_commit (role, surface, xdg_role->impl->funcs.after_commit (role, surface,
xdg_role->impl); xdg_role->impl);
}
else
/* Now, tell the sync helper to generate a frame.
Many clients do this:
wl_surface@1.frame (new id wl_callback@2)
wl_surface@1.commit ()
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; return;
} }
@ -753,13 +672,13 @@ ReleaseBacking (XdgRole *role)
/* And the association. */ /* And the association. */
XLDeleteAssoc (surfaces, role->window); XLDeleteAssoc (surfaces, role->window);
/* Destroy the sync helper. */
FreeSyncHelper (role->sync_helper);
/* There shouldn't be any children of the subcompositor at this /* There shouldn't be any children of the subcompositor at this
point. */ point. */
SubcompositorFree (role->subcompositor); SubcompositorFree (role->subcompositor);
/* The frame clock is no longer useful. */
XLFreeFrameClock (role->clock);
/* Free the input region. */ /* Free the input region. */
pixman_region32_fini (&role->input_region); pixman_region32_fini (&role->input_region);
@ -822,19 +741,34 @@ SubsurfaceUpdate (Surface *surface, Role *role)
xdg_role = XdgRoleFromRole (role); xdg_role = XdgRoleFromRole (role);
/* If the frame clock is frozen, don't update anything. */ if (xdg_role->state & StateWaitingForAckCommit)
if (XLFrameClockIsFrozen (xdg_role->clock)) {
/* Updates are being postponed until the next commit after
ack_configure.
Now, tell the sync helper to generate a frame.
Many clients do this:
wl_surface@1.frame (new id wl_callback@2)
wl_surface@1.commit ()
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; return;
}
/* If a frame is already in progress, return, but schedule a frame /* Tell the sync helper to do an update. */
to be drawn later. */ SyncHelperUpdate (xdg_role->sync_helper);
if (!CheckFrame (xdg_role))
/* The frame cannot be drawn. */
return;
/* The frame can be drawn, so update the window contents. */
SubcompositorUpdate (xdg_role->subcompositor);
} }
static Window static Window
@ -858,48 +792,6 @@ HandleResourceDestroy (struct wl_resource *resource)
ReleaseBacking (role); 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 static void
OpaqueRegionChanged (Subcompositor *subcompositor, OpaqueRegionChanged (Subcompositor *subcompositor,
void *client_data, void *client_data,
@ -1004,7 +896,7 @@ NoteConfigure (XdgRole *role, XEvent *event)
/* Tell the frame clock how many WM-generated configure events have /* Tell the frame clock how many WM-generated configure events have
arrived. */ arrived. */
XLFrameClockNoteConfigure (role->clock); SyncHelperNoteConfigureEvent (role->sync_helper);
/* Run reconstrain callbacks. */ /* Run reconstrain callbacks. */
RunReconstrainCallbacksForXEvent (role, event); RunReconstrainCallbacksForXEvent (role, event);
@ -1040,11 +932,6 @@ NoteBounds (void *data, int min_x, int min_y,
run_reconstrain_callbacks = False; run_reconstrain_callbacks = False;
root_position_initialized = 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) if (role->state & StateWaitingForAckCommit)
/* Don't resize the window until all configure events are /* Don't resize the window until all configure events are
acknowledged. We wait for a commit on the xdg_toplevel to do acknowledged. We wait for a commit on the xdg_toplevel to do
@ -1160,11 +1047,6 @@ WriteRedirectProperty (XdgRole *role)
{ {
unsigned long bypass_compositor; 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, XChangeProperty (compositor.display, role->window,
@ -1173,113 +1055,6 @@ WriteRedirectProperty (XdgRole *role)
(unsigned char *) &bypass_compositor, 1); (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 static void
ResizeForMap (XdgRole *role) ResizeForMap (XdgRole *role)
{ {
@ -1373,21 +1148,52 @@ Rescale (Surface *surface, Role *role)
} }
static void static void
HandleFreeze (void *data) HandleResize (void *data, Bool only_frame)
{ {
XdgRole *role; XdgRole *role;
role = data; role = data;
if (only_frame)
{
SyncHelperCheckFrameCallback (role->sync_helper);
return;
}
/* _NET_WM_SYNC_REQUEST events should be succeeded by a /* _NET_WM_SYNC_REQUEST events should be succeeded by a
ConfigureNotify event. */ ConfigureNotify event. */
role->state |= StateWaitingForAckConfigure; role->state |= StateWaitingForAckConfigure;
role->state |= StateWaitingForAckCommit; 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 /* This flag means the WaitingForAckConfigure was caused by a
_NET_WM_SYNC_REQUEST, and the following ConfigureNotify event _NET_WM_SYNC_REQUEST, and the following ConfigureNotify event
might not lead to a configure event being sent. */ might not lead to a configure event being sent. */
role->state |= StateMaybeConfigure; 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 static void
@ -1445,6 +1251,15 @@ Activate (Surface *surface, Role *role, int deviceid,
activator_surface); activator_surface);
} }
static void
HandleFrameCallback (void *data, uint32_t frame_time)
{
XdgRole *role;
role = data;
RunFrameCallbacksConditionally (role, frame_time);
}
void void
XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource, XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
uint32_t id, struct wl_resource *surface_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); RenderSetClient (role->target, client);
role->subcompositor = MakeSubcompositor (); role->subcompositor = MakeSubcompositor ();
role->clock = XLMakeFrameClockForWindow (role->window); role->sync_helper = MakeSyncHelper (role->subcompositor,
XLFrameClockSetFreezeCallback (role->clock, HandleFreeze, role); role->window,
role->target,
HandleFrameCallback,
&role->role);
SyncHelperSetResizeCallback (role->sync_helper, HandleResize,
CheckFastForward);
SubcompositorSetTarget (role->subcompositor, &role->target); SubcompositorSetTarget (role->subcompositor, &role->target);
SubcompositorSetInputCallback (role->subcompositor, SubcompositorSetInputCallback (role->subcompositor,
@ -1548,8 +1368,6 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
OpaqueRegionChanged, role); OpaqueRegionChanged, role);
SubcompositorSetBoundsCallback (role->subcompositor, SubcompositorSetBoundsCallback (role->subcompositor,
NoteBounds, role); NoteBounds, role);
SubcompositorSetNoteFrameCallback (role->subcompositor,
NoteFrame, role);
XLSelectStandardEvents (role->window); XLSelectStandardEvents (role->window);
XLMakeAssoc (surfaces, role->window, role); 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. */ If it does, frame synchronization will not work. */
WriteRedirectProperty (role); WriteRedirectProperty (role);
/* Initialize frame callbacks. */
XLFrameClockAfterFrame (role->clock, AfterFrame, role);
if (!XLSurfaceAttachRole (surface, &role->role)) if (!XLSurfaceAttachRole (surface, &role->role))
abort (); abort ();
@ -1626,6 +1441,10 @@ XLXdgRoleSendConfigure (Role *role, uint32_t serial)
xdg_role->state |= StateWaitingForAckConfigure; xdg_role->state |= StateWaitingForAckConfigure;
xdg_role->state |= StateWaitingForAckCommit; 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. */ /* See the comment under XLXdgRoleSetBoundsSize. */
xdg_role->state &= ~StateTemporaryBounds; 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 is a configure event, tell the frame clock about it. */
if (event->type == ConfigureNotify) if (event->type == ConfigureNotify)
XLFrameClockNoteConfigure (xdg_role->clock); SyncHelperNoteConfigureEvent (xdg_role->sync_helper);
} }
void void
@ -1938,15 +1757,6 @@ XLXdgRoleMoveBy (Role *role, int west, int north)
xdg_role->pending_synth_configure++; xdg_role->pending_synth_configure++;
} }
FrameClock *
XLXdgRoleGetFrameClock (Role *role)
{
XdgRole *xdg_role;
xdg_role = XdgRoleFromRole (role);
return xdg_role->clock;
}
void void
XLInitXdgSurfaces (void) XLInitXdgSurfaces (void)
{ {
@ -2043,9 +1853,6 @@ XLXdgRoleNoteRejectedConfigure (Role *role)
xdg_role->state &= ~StateWaitingForAckConfigure; xdg_role->state &= ~StateWaitingForAckConfigure;
xdg_role->state &= ~StateWaitingForAckCommit; xdg_role->state &= ~StateWaitingForAckCommit;
xdg_role->state &= ~StateMaybeConfigure; xdg_role->state &= ~StateMaybeConfigure;
/* Unfreeze the frame clock now. */
XLFrameClockUnfreeze (xdg_role->clock);
} }
} }