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); } }