diff --git a/12to11.man b/12to11.man
index 54e0c0d..eb788fa 100644
--- a/12to11.man
+++ b/12to11.man
@@ -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
diff --git a/Imakefile b/Imakefile
index 06fae2e..acea8ca 100644
--- a/Imakefile
+++ b/Imakefile
@@ -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
diff --git a/compositor.h b/compositor.h
index 118eb5b..a3e5a93 100644
--- a/compositor.h
+++ b/compositor.h
@@ -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])
diff --git a/frame_clock.c b/frame_clock.c
index 657dc22..d18a19e 100644
--- a/frame_clock.c
+++ b/frame_clock.c
@@ -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
diff --git a/icon_surface.c b/icon_surface.c
index 035fb4b..d2f349b 100644
--- a/icon_surface.c
+++ b/icon_surface.c
@@ -29,9 +29,10 @@ along with 12to11. If not, see . */
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;
}
diff --git a/picture_renderer.c b/picture_renderer.c
index 86b08f4..c2d5e14 100644
--- a/picture_renderer.c
+++ b/picture_renderer.c
@@ -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;
diff --git a/renderer.c b/renderer.c
index d13eb28..d719985 100644
--- a/renderer.c
+++ b/renderer.c
@@ -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)
{
diff --git a/subcompositor.c b/subcompositor.c
index cde4ab3..17c3dd0 100644
--- a/subcompositor.c
+++ b/subcompositor.c
@@ -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
diff --git a/test.c b/test.c
index 7f07b2e..3ffa684 100644
--- a/test.c
+++ b/test.c
@@ -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;
diff --git a/xdg_popup.c b/xdg_popup.c
index 3b678ca..d4572a2 100644
--- a/xdg_popup.c
+++ b/xdg_popup.c
@@ -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;
}
diff --git a/xdg_surface.c b/xdg_surface.c
index 0de8d46..6d7414d 100644
--- a/xdg_surface.c
+++ b/xdg_surface.c
@@ -36,17 +36,13 @@ along with 12to11. If not, see . */
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);
}
}