xserver: Branch 'master' - 23 commits

Adam Jackson ajax at kemper.freedesktop.org
Wed Mar 28 18:40:01 UTC 2018


 hw/xwayland/Makefile.am        |    1 
 hw/xwayland/meson.build        |    1 
 hw/xwayland/xwayland-glamor.c  |   23 
 hw/xwayland/xwayland-present.c |  521 ++++++++++++++++++++
 hw/xwayland/xwayland.c         |   27 +
 hw/xwayland/xwayland.h         |   40 +
 present/Makefile.am            |    6 
 present/meson.build            |    4 
 present/present.c              | 1023 ++---------------------------------------
 present/present.h              |   52 ++
 present/present_execute.c      |  122 ++++
 present/present_priv.h         |  238 ++++++++-
 present/present_scmd.c         |  840 +++++++++++++++++++++++++++++++++
 present/present_screen.c       |  114 +++-
 present/present_vblank.c       |  203 ++++++++
 present/present_wnmd.c         |  703 ++++++++++++++++++++++++++++
 16 files changed, 2907 insertions(+), 1011 deletions(-)

New commits:
commit be087778a0eae3093ffdbba3ff7c9f3863d8e1d4
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:57 2018 +0100

    xwayland: Activate Present flips in rootless mode with Glamor
    
    Link the newly introduced support for Present flips. For now flips can only
    be used in rootless mode together with Glamor.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index f3fd49764..e65d8b7e7 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -522,6 +522,15 @@ xwl_realize_window(WindowPtr window)
         wl_region_destroy(region);
     }
 
+    if (xwl_screen->present) {
+        xwl_window->present_crtc_fake = RRCrtcCreate(xwl_screen->screen, xwl_window);
+        xwl_window->present_msc = 1;
+        xwl_window->present_ust = GetTimeInMicros();
+
+        xorg_list_init(&xwl_window->present_event_list);
+        xorg_list_init(&xwl_window->present_release_queue);
+    }
+
     wl_display_flush(xwl_screen->display);
 
     send_surface_id_event(xwl_window);
@@ -587,6 +596,10 @@ xwl_unrealize_window(WindowPtr window)
 
     compUnredirectWindow(serverClient, window, CompositeRedirectManual);
 
+    if (xwl_screen->present)
+        /* Always cleanup Present (Present might have been active on child window) */
+        xwl_present_cleanup(window);
+
     screen->UnrealizeWindow = xwl_screen->UnrealizeWindow;
     ret = (*screen->UnrealizeWindow) (window);
     xwl_screen->UnrealizeWindow = screen->UnrealizeWindow;
@@ -605,6 +618,9 @@ xwl_unrealize_window(WindowPtr window)
     if (xwl_window->frame_callback)
         wl_callback_destroy(xwl_window->frame_callback);
 
+    if (xwl_window->present_crtc_fake)
+        RRCrtcDestroy(xwl_window->present_crtc_fake);
+
     free(xwl_window);
     dixSetPrivate(&window->devPrivates, &xwl_window_private_key, NULL);
 
@@ -690,6 +706,9 @@ xwl_screen_post_damage(struct xwl_screen *xwl_screen)
 
     xorg_list_for_each_entry_safe(xwl_window, next_xwl_window,
                                   &xwl_screen->damage_window_list, link_damage) {
+        /* Present on the main surface. So don't commit here as well. */
+        if (xwl_window->present_window)
+            continue;
         /* If we're waiting on a frame callback from the server,
          * don't attach a new buffer. */
         if (xwl_window->frame_callback)
@@ -1033,6 +1052,9 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
     }
 #endif
 
+    if (xwl_screen->glamor && xwl_screen->rootless)
+        xwl_screen->present = xwl_present_init(pScreen);
+
     if (!xwl_screen->glamor) {
         xwl_screen->CreateScreenResources = pScreen->CreateScreenResources;
         pScreen->CreateScreenResources = xwl_shm_create_screen_resources;
commit 07750ff3c084c6549a5612d1f935a9a3ab3df67c
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:55 2018 +0100

    xwayland: Implement queuing present vblanks
    
    Queue present events to msc values. Fake msc events with a refresh rate of
    about 60fps when flips are not possible. When flips are executed rely on
    frame callbacks with a slow updating timer as fallback.
    
    This is important for applications, that want to limit their framerate.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index a8cc02449..f403ff701 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -27,6 +27,13 @@
 
 #include <present.h>
 
+/*
+ * When not flipping let Present copy with 60fps.
+ * When flipping wait on frame_callback, otherwise
+ * the surface is not visible, in this case update
+ * with long interval.
+ */
+#define TIMER_LEN_COPY      17  // ~60fps
 #define TIMER_LEN_FLIP    1000  // 1fps
 
 static void
@@ -41,13 +48,23 @@ xwl_present_timer_callback(OsTimerPtr timer,
                            CARD32 time,
                            void *arg);
 
+static inline Bool
+xwl_present_has_events(struct xwl_window *xwl_window)
+{
+    return !xorg_list_is_empty(&xwl_window->present_event_list) ||
+           !xorg_list_is_empty(&xwl_window->present_release_queue);
+}
+
 static void
 xwl_present_reset_timer(struct xwl_window *xwl_window)
 {
-    if ( !xorg_list_is_empty(&xwl_window->present_release_queue) ) {
+    if (xwl_present_has_events(xwl_window)) {
+        uint32_t timer_len = xwl_window->present_window ? TIMER_LEN_FLIP :
+                                                          TIMER_LEN_COPY;
+
         xwl_window->present_timer = TimerSet(xwl_window->present_timer,
                                              0,
-                                             TIMER_LEN_FLIP,
+                                             timer_len,
                                              &xwl_present_timer_callback,
                                              xwl_window);
     } else {
@@ -72,6 +89,14 @@ xwl_present_cleanup(WindowPtr window)
         xwl_window->present_window = NULL;
     }
 
+    /* Clear remaining events */
+    xorg_list_for_each_entry_safe(event, tmp, &xwl_window->present_event_list, list) {
+        if (event->present_window == window) {
+            xorg_list_del(&event->list);
+            free(event);
+        }
+    }
+
     /* Clear remaining buffer releases and inform Present about free ressources */
     xorg_list_for_each_entry_safe(event, tmp, &xwl_window->present_release_queue, list) {
         if (event->present_window == window) {
@@ -81,7 +106,7 @@ xwl_present_cleanup(WindowPtr window)
     }
 
     /* Clear timer */
-    if ( xorg_list_is_empty(&xwl_window->present_release_queue) )
+    if (!xwl_present_has_events(xwl_window))
         xwl_present_free_timer(xwl_window);
 }
 
@@ -122,6 +147,25 @@ static const struct wl_buffer_listener xwl_present_release_listener = {
     xwl_present_buffer_release
 };
 
+static void
+xwl_present_events_notify(struct xwl_window *xwl_window)
+{
+    uint64_t                    msc = xwl_window->present_msc;
+    struct xwl_present_event    *event, *tmp;
+
+    xorg_list_for_each_entry_safe(event, tmp,
+                                  &xwl_window->present_event_list,
+                                  list) {
+        if (event->target_msc <= msc) {
+            present_wnmd_event_notify(event->present_window,
+                                      event->event_id,
+                                      xwl_window->present_ust,
+                                      msc);
+            xwl_present_free_event(event);
+        }
+    }
+}
+
 CARD32
 xwl_present_timer_callback(OsTimerPtr timer,
                            CARD32 time,
@@ -133,9 +177,12 @@ xwl_present_timer_callback(OsTimerPtr timer,
     xwl_window->present_msc++;
     xwl_window->present_ust = GetTimeInMicros();
 
-    if ( !xorg_list_is_empty(&xwl_window->present_release_queue) ) {
+    xwl_present_events_notify(xwl_window);
+
+    if (xwl_present_has_events(xwl_window)) {
         /* Still events, restart timer */
-        return TIMER_LEN_FLIP;
+        return xwl_window->present_window ? TIMER_LEN_FLIP :
+                                            TIMER_LEN_COPY;
     } else {
         /* No more events, do not restart timer and delete it instead */
         xwl_present_free_timer(xwl_window);
@@ -161,6 +208,8 @@ xwl_present_frame_callback(void *data,
     xwl_window->present_msc++;
     xwl_window->present_ust = GetTimeInMicros();
 
+    xwl_present_events_notify(xwl_window);
+
     /* we do not need the timer anymore for this frame,
      * reset it for potentially the next one
      */
@@ -248,9 +297,34 @@ xwl_present_queue_vblank(WindowPtr present_window,
                          uint64_t event_id,
                          uint64_t msc)
 {
-    /* Not yet implemented
-     */
-    return BadRequest;
+    struct xwl_window *xwl_window = xwl_window_of_top(present_window);
+    struct xwl_present_event *event;
+
+    if (!xwl_window)
+        return BadMatch;
+
+    if (xwl_window->present_crtc_fake != crtc)
+        return BadRequest;
+
+    if (xwl_window->present_window &&
+            xwl_window->present_window != present_window)
+        return BadMatch;
+
+    event = malloc(sizeof *event);
+    if (!event)
+        return BadAlloc;
+
+    event->event_id = event_id;
+    event->present_window = present_window;
+    event->xwl_window = xwl_window;
+    event->target_msc = msc;
+
+    xorg_list_append(&event->list, &xwl_window->present_event_list);
+
+    if (!xwl_window->present_timer)
+        xwl_present_reset_timer(xwl_window);
+
+    return Success;
 }
 
 /*
@@ -264,11 +338,19 @@ xwl_present_abort_vblank(WindowPtr present_window,
                          uint64_t msc)
 {
     struct xwl_window *xwl_window = xwl_window_of_top(present_window);
-    struct xwl_present_event *event;
+    struct xwl_present_event *event, *tmp;
 
     if (!xwl_window)
         return;
 
+    xorg_list_for_each_entry_safe(event, tmp, &xwl_window->present_event_list, list) {
+        if (event->event_id == event_id) {
+            xorg_list_del(&event->list);
+            free(event);
+            return;
+        }
+    }
+
     xorg_list_for_each_entry(event, &xwl_window->present_release_queue, list) {
         if (event->event_id == event_id) {
             event->abort = TRUE;
@@ -411,6 +493,9 @@ xwl_present_flips_stop(WindowPtr window)
     assert(xwl_window->present_window == window);
 
     xwl_window->present_window = NULL;
+
+    /* Change back to the fast refresh rate */
+    xwl_present_reset_timer(xwl_window);
 }
 
 static present_wnmd_info_rec xwl_present_info = {
diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h
index e6cec18b8..a65559374 100644
--- a/hw/xwayland/xwayland.h
+++ b/hw/xwayland/xwayland.h
@@ -140,6 +140,8 @@ struct xwl_window {
 
     struct wl_callback *present_frame_callback;
     struct wl_callback *present_sync_callback;
+
+    struct xorg_list present_event_list;
     struct xorg_list present_release_queue;
 };
 
commit 86df366973de1c10da5fbdc57d1ff12b681c321f
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:54 2018 +0100

    xwayland: Add fallback timer for msc counting
    
    When the compositor is not sending frame callbacks while we still wait
    on buffer release events fake a continuous msc counter with a timer.
    
    Having this timer is a prerequisite for queuing events.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index f41e864b3..a8cc02449 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -27,6 +27,34 @@
 
 #include <present.h>
 
+#define TIMER_LEN_FLIP    1000  // 1fps
+
+static void
+xwl_present_free_timer(struct xwl_window *xwl_window)
+{
+    TimerFree(xwl_window->present_timer);
+    xwl_window->present_timer = NULL;
+}
+
+static CARD32
+xwl_present_timer_callback(OsTimerPtr timer,
+                           CARD32 time,
+                           void *arg);
+
+static void
+xwl_present_reset_timer(struct xwl_window *xwl_window)
+{
+    if ( !xorg_list_is_empty(&xwl_window->present_release_queue) ) {
+        xwl_window->present_timer = TimerSet(xwl_window->present_timer,
+                                             0,
+                                             TIMER_LEN_FLIP,
+                                             &xwl_present_timer_callback,
+                                             xwl_window);
+    } else {
+        xwl_present_free_timer(xwl_window);
+    }
+}
+
 void
 xwl_present_cleanup(WindowPtr window)
 {
@@ -51,6 +79,10 @@ xwl_present_cleanup(WindowPtr window)
             event->abort = TRUE;
         }
     }
+
+    /* Clear timer */
+    if ( xorg_list_is_empty(&xwl_window->present_release_queue) )
+        xwl_present_free_timer(xwl_window);
 }
 
 static void
@@ -90,6 +122,27 @@ static const struct wl_buffer_listener xwl_present_release_listener = {
     xwl_present_buffer_release
 };
 
+CARD32
+xwl_present_timer_callback(OsTimerPtr timer,
+                           CARD32 time,
+                           void *arg)
+{
+    struct xwl_window *xwl_window = arg;
+
+    xwl_window->present_timer_firing = TRUE;
+    xwl_window->present_msc++;
+    xwl_window->present_ust = GetTimeInMicros();
+
+    if ( !xorg_list_is_empty(&xwl_window->present_release_queue) ) {
+        /* Still events, restart timer */
+        return TIMER_LEN_FLIP;
+    } else {
+        /* No more events, do not restart timer and delete it instead */
+        xwl_present_free_timer(xwl_window);
+        return 0;
+    }
+}
+
 static void
 xwl_present_frame_callback(void *data,
                struct wl_callback *callback,
@@ -100,8 +153,18 @@ xwl_present_frame_callback(void *data,
     wl_callback_destroy(xwl_window->present_frame_callback);
     xwl_window->present_frame_callback = NULL;
 
+    if (xwl_window->present_timer_firing) {
+        /* If the timer is firing, this frame callback is too late */
+        return;
+    }
+
     xwl_window->present_msc++;
     xwl_window->present_ust = GetTimeInMicros();
+
+    /* we do not need the timer anymore for this frame,
+     * reset it for potentially the next one
+     */
+    xwl_present_reset_timer(xwl_window);
 }
 
 static const struct wl_callback_listener xwl_present_frame_listener = {
@@ -308,6 +371,13 @@ xwl_present_flip(WindowPtr present_window,
     /* We can flip directly to the main surface (full screen window without clips) */
     wl_surface_attach(xwl_window->surface, buffer, 0, 0);
 
+    if (!xwl_window->present_timer ||
+            xwl_window->present_timer_firing) {
+        /* Realign timer */
+        xwl_window->present_timer_firing = FALSE;
+        xwl_present_reset_timer(xwl_window);
+    }
+
     if (!xwl_window->present_frame_callback) {
         xwl_window->present_frame_callback = wl_surface_frame(xwl_window->surface);
         wl_callback_add_listener(xwl_window->present_frame_callback,
diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h
index dd7d1c685..e6cec18b8 100644
--- a/hw/xwayland/xwayland.h
+++ b/hw/xwayland/xwayland.h
@@ -135,6 +135,9 @@ struct xwl_window {
     uint64_t present_msc;
     uint64_t present_ust;
 
+    OsTimerPtr present_timer;
+    Bool present_timer_firing;
+
     struct wl_callback *present_frame_callback;
     struct wl_callback *present_sync_callback;
     struct xorg_list present_release_queue;
commit 0fb2cca193e60b731c8e75a2a7e795477fb5fd8f
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:53 2018 +0100

    xwayland: Preliminary support for Present's new window flip mode
    
    Introduce support for Present's window flip mode. The support is not yet
    complete, but works reasonable well for the most important use case, that
    is fullscreen applications.
    
    We take a Present flip and if the xwl_window->window has the same dimensions
    as the presenting window, the flip is represented by a wl_buffer and attached
    to the main wl_surface of the xwl_window.
    
    After commit we are listening for the sync callback in order to tell Present,
    that the pixmap flip is not longer pending, for the frame callback in order
    to update the msc counter and for the buffer release callback in order to tell
    Present that the pixmap is idle again.
    
    The following functionality is missing from this patch:
    * (slowed down) flips in case the compositor is not sending frame callbacks,
    * queuing events to MSC times,
    * per window flips for child windows with smaller size than the xwl_window.
    
    To make use of this functionality Xwayland must run rootless and with
    Glamor/GBM.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/Makefile.am b/hw/xwayland/Makefile.am
index f44a7ded3..3378a6656 100644
--- a/hw/xwayland/Makefile.am
+++ b/hw/xwayland/Makefile.am
@@ -11,6 +11,7 @@ Xwayland_CFLAGS =				\
 
 Xwayland_SOURCES =				\
 	xwayland.c				\
+	xwayland-present.c			\
 	xwayland-input.c			\
 	xwayland-cursor.c			\
 	xwayland-shm.c				\
diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build
index 7e24c5d63..17f52b4ba 100644
--- a/hw/xwayland/meson.build
+++ b/hw/xwayland/meson.build
@@ -1,5 +1,6 @@
 srcs = [
     'xwayland.c',
+    'xwayland-present.c',
     'xwayland-input.c',
     'xwayland-cursor.c',
     'xwayland-shm.c',
diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
new file mode 100644
index 000000000..f41e864b3
--- /dev/null
+++ b/hw/xwayland/xwayland-present.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright © 2018 Roman Gilg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "xwayland.h"
+
+#include <present.h>
+
+void
+xwl_present_cleanup(WindowPtr window)
+{
+    struct xwl_window           *xwl_window = xwl_window_of_top(window);
+    struct xwl_present_event    *event, *tmp;
+
+    if (!xwl_window)
+        return;
+
+    if (xwl_window->present_window == window) {
+        if (xwl_window->present_frame_callback) {
+            wl_callback_destroy(xwl_window->present_frame_callback);
+            xwl_window->present_frame_callback = NULL;
+        }
+        xwl_window->present_window = NULL;
+    }
+
+    /* Clear remaining buffer releases and inform Present about free ressources */
+    xorg_list_for_each_entry_safe(event, tmp, &xwl_window->present_release_queue, list) {
+        if (event->present_window == window) {
+            xorg_list_del(&event->list);
+            event->abort = TRUE;
+        }
+    }
+}
+
+static void
+xwl_present_free_event(struct xwl_present_event *event)
+{
+    xorg_list_del(&event->list);
+    free(event);
+}
+
+static void
+xwl_present_buffer_release(void *data, struct wl_buffer *buffer)
+{
+    struct xwl_present_event *event = data;
+
+    if (!event)
+        return;
+    wl_buffer_set_user_data(buffer, NULL);
+
+    event->buffer_released = TRUE;
+
+    if (event->abort) {
+        if (!event->pending)
+            xwl_present_free_event(event);
+        return;
+    }
+
+    if (!event->pending) {
+        present_wnmd_event_notify(event->present_window,
+                                  event->event_id,
+                                  event->xwl_window->present_ust,
+                                  event->xwl_window->present_msc);
+        xwl_present_free_event(event);
+    }
+}
+
+static const struct wl_buffer_listener xwl_present_release_listener = {
+    xwl_present_buffer_release
+};
+
+static void
+xwl_present_frame_callback(void *data,
+               struct wl_callback *callback,
+               uint32_t time)
+{
+    struct xwl_window *xwl_window = data;
+
+    wl_callback_destroy(xwl_window->present_frame_callback);
+    xwl_window->present_frame_callback = NULL;
+
+    xwl_window->present_msc++;
+    xwl_window->present_ust = GetTimeInMicros();
+}
+
+static const struct wl_callback_listener xwl_present_frame_listener = {
+    xwl_present_frame_callback
+};
+
+static void
+xwl_present_sync_callback(void *data,
+               struct wl_callback *callback,
+               uint32_t time)
+{
+    struct xwl_present_event *event = data;
+    struct xwl_window *xwl_window = event->xwl_window;
+
+    event->pending = FALSE;
+
+    if (event->abort) {
+        /* Event might have been aborted */
+        if (event->buffer_released)
+            /* Buffer was already released, cleanup now */
+            xwl_present_free_event(event);
+        return;
+    }
+
+    present_wnmd_event_notify(event->present_window,
+                              event->event_id,
+                              xwl_window->present_ust,
+                              xwl_window->present_msc);
+
+    if (event->buffer_released)
+        /* If the buffer was already released, send the event now again */
+        present_wnmd_event_notify(event->present_window,
+                                  event->event_id,
+                                  xwl_window->present_ust,
+                                  xwl_window->present_msc);
+}
+
+static const struct wl_callback_listener xwl_present_sync_listener = {
+    xwl_present_sync_callback
+};
+
+static RRCrtcPtr
+xwl_present_get_crtc(WindowPtr present_window)
+{
+    struct xwl_window *xwl_window = xwl_window_of_top(present_window);
+    if (xwl_window == NULL)
+        return NULL;
+
+    return xwl_window->present_crtc_fake;
+}
+
+static int
+xwl_present_get_ust_msc(WindowPtr present_window, uint64_t *ust, uint64_t *msc)
+{
+    struct xwl_window *xwl_window = xwl_window_of_top(present_window);
+    if (!xwl_window)
+        return BadAlloc;
+    *ust = xwl_window->present_ust;
+    *msc = xwl_window->present_msc;
+
+    return Success;
+}
+
+static void
+xwl_present_set_present_window(struct xwl_window *xwl_window,
+                               WindowPtr present_window)
+{
+    if (xwl_window->present_window)
+        return;
+
+    xwl_window->present_window = present_window;
+}
+
+/*
+ * Queue an event to report back to the Present extension when the specified
+ * MSC has past
+ */
+static int
+xwl_present_queue_vblank(WindowPtr present_window,
+                         RRCrtcPtr crtc,
+                         uint64_t event_id,
+                         uint64_t msc)
+{
+    /* Not yet implemented
+     */
+    return BadRequest;
+}
+
+/*
+ * Remove a pending vblank event so that it is not reported
+ * to the extension
+ */
+static void
+xwl_present_abort_vblank(WindowPtr present_window,
+                         RRCrtcPtr crtc,
+                         uint64_t event_id,
+                         uint64_t msc)
+{
+    struct xwl_window *xwl_window = xwl_window_of_top(present_window);
+    struct xwl_present_event *event;
+
+    if (!xwl_window)
+        return;
+
+    xorg_list_for_each_entry(event, &xwl_window->present_release_queue, list) {
+        if (event->event_id == event_id) {
+            event->abort = TRUE;
+            return;
+        }
+    }
+}
+
+static void
+xwl_present_flush(WindowPtr window)
+{
+    /* Only called when a Pixmap is copied instead of flipped,
+     * but in this case we wait on the next block_handler.
+     */
+}
+
+static Bool
+xwl_present_check_flip2(RRCrtcPtr crtc,
+                        WindowPtr present_window,
+                        PixmapPtr pixmap,
+                        Bool sync_flip,
+                        PresentFlipReason *reason)
+{
+    struct xwl_window *xwl_window = xwl_window_of_top(present_window);
+
+    if (!xwl_window)
+        return FALSE;
+
+    /*
+     * Do not flip if there is already another child window doing flips.
+     */
+    if (xwl_window->present_window &&
+            xwl_window->present_window != present_window)
+        return FALSE;
+
+    if (!xwl_window->present_crtc_fake)
+        return FALSE;
+    /*
+     * Make sure the client doesn't try to flip to another crtc
+     * than the one created for 'xwl_window'.
+     */
+    if (xwl_window->present_crtc_fake != crtc)
+        return FALSE;
+
+    /*
+     * We currently only allow flips of windows, that have the same
+     * dimensions as their xwl_window parent window. For the case of
+     * different sizes subsurfaces are presumably the way forward.
+     */
+    if (!RegionEqual(&xwl_window->window->winSize, &present_window->winSize))
+        return FALSE;
+
+    return TRUE;
+}
+
+static Bool
+xwl_present_flip(WindowPtr present_window,
+                 RRCrtcPtr crtc,
+                 uint64_t event_id,
+                 uint64_t target_msc,
+                 PixmapPtr pixmap,
+                 Bool sync_flip,
+                 RegionPtr damage)
+{
+    struct xwl_window           *xwl_window = xwl_window_of_top(present_window);
+    BoxPtr                      present_box, damage_box;
+    Bool                        buffer_created;
+    struct wl_buffer            *buffer;
+    struct xwl_present_event    *event;
+
+    present_box = RegionExtents(&present_window->winSize);
+    damage_box = RegionExtents(damage);
+
+    xwl_present_set_present_window(xwl_window, present_window);
+
+    event = malloc(sizeof *event);
+    if (!event)
+        return FALSE;
+
+    buffer = xwl_glamor_pixmap_get_wl_buffer(pixmap,
+                                             present_box->x2 - present_box->x1,
+                                             present_box->y2 - present_box->y1,
+                                             &buffer_created);
+
+    event->event_id = event_id;
+    event->present_window = present_window;
+    event->xwl_window = xwl_window;
+    event->buffer = buffer;
+    event->target_msc = xwl_window->present_msc;
+    event->pending = TRUE;
+    event->abort = FALSE;
+    event->buffer_released = FALSE;
+
+    xorg_list_add(&event->list, &xwl_window->present_release_queue);
+
+    if (buffer_created)
+        wl_buffer_add_listener(buffer, &xwl_present_release_listener, NULL);
+    wl_buffer_set_user_data(buffer, event);
+
+    /* We can flip directly to the main surface (full screen window without clips) */
+    wl_surface_attach(xwl_window->surface, buffer, 0, 0);
+
+    if (!xwl_window->present_frame_callback) {
+        xwl_window->present_frame_callback = wl_surface_frame(xwl_window->surface);
+        wl_callback_add_listener(xwl_window->present_frame_callback,
+                                 &xwl_present_frame_listener,
+                                 xwl_window);
+    }
+
+    wl_surface_damage(xwl_window->surface, 0, 0,
+                      damage_box->x2 - damage_box->x1,
+                      damage_box->y2 - damage_box->y1);
+
+    wl_surface_commit(xwl_window->surface);
+
+    xwl_window->present_sync_callback = wl_display_sync(xwl_window->xwl_screen->display);
+    wl_callback_add_listener(xwl_window->present_sync_callback,
+                             &xwl_present_sync_listener,
+                             event);
+
+    wl_display_flush(xwl_window->xwl_screen->display);
+    return TRUE;
+}
+
+static void
+xwl_present_flips_stop(WindowPtr window)
+{
+    struct xwl_window *xwl_window = xwl_window_of_top(window);
+
+    if (!xwl_window)
+        return;
+
+    assert(xwl_window->present_window == window);
+
+    xwl_window->present_window = NULL;
+}
+
+static present_wnmd_info_rec xwl_present_info = {
+    .version = PRESENT_SCREEN_INFO_VERSION,
+    .get_crtc = xwl_present_get_crtc,
+
+    .get_ust_msc = xwl_present_get_ust_msc,
+    .queue_vblank = xwl_present_queue_vblank,
+    .abort_vblank = xwl_present_abort_vblank,
+
+    .flush = xwl_present_flush,
+
+    .capabilities = PresentCapabilityAsync,
+    .check_flip2 = xwl_present_check_flip2,
+    .flip = xwl_present_flip,
+    .flips_stop = xwl_present_flips_stop
+};
+
+Bool
+xwl_present_init(ScreenPtr screen)
+{
+    return present_wnmd_screen_init(screen, &xwl_present_info);
+}
diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h
index 49fb5ba28..dd7d1c685 100644
--- a/hw/xwayland/xwayland.h
+++ b/hw/xwayland/xwayland.h
@@ -68,6 +68,7 @@ struct xwl_screen {
     int listen_fd_count;
     int rootless;
     int glamor;
+    int present;
 
     CreateScreenResourcesProcPtr CreateScreenResources;
     CloseScreenProcPtr CloseScreen;
@@ -126,6 +127,32 @@ struct xwl_window {
     struct xorg_list link_damage;
     struct wl_callback *frame_callback;
     Bool allow_commits;
+
+    /* present */
+    RRCrtcPtr present_crtc_fake;
+    struct xorg_list present_link;
+    WindowPtr present_window;
+    uint64_t present_msc;
+    uint64_t present_ust;
+
+    struct wl_callback *present_frame_callback;
+    struct wl_callback *present_sync_callback;
+    struct xorg_list present_release_queue;
+};
+
+struct xwl_present_event {
+    uint64_t event_id;
+    uint64_t target_msc;
+
+    Bool abort;
+    Bool pending;
+    Bool buffer_released;
+
+    WindowPtr present_window;
+    struct xwl_window *xwl_window;
+    struct wl_buffer *buffer;
+
+    struct xorg_list list;
 };
 
 #define MODIFIER_META 0x01
@@ -346,6 +373,9 @@ struct wl_buffer *xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap,
                                                   unsigned short height,
                                                   Bool *created);
 
+Bool xwl_present_init(ScreenPtr screen);
+void xwl_present_cleanup(WindowPtr window);
+
 void xwl_screen_release_tablet_manager(struct xwl_screen *xwl_screen);
 
 void xwl_output_get_xdg_output(struct xwl_output *xwl_output);
commit 8fba2a03f1410f3bc7504e218ac1e5c964279ea2
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:52 2018 +0100

    xwayland: Add arguments to glamor_pixmap_get_wl_buffer
    
    Add arguments to give the caller more information and control
    over the creation of a wl_buffer with GBM, in particular let
    the caller determine the size of the buffer.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index 4de3beb9d..7e9815626 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -158,7 +158,10 @@ xwl_glamor_create_pixmap_for_bo(ScreenPtr screen, struct gbm_bo *bo, int depth)
 }
 
 struct wl_buffer *
-xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap)
+xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap,
+                                unsigned short width,
+                                unsigned short height,
+                                Bool *created)
 {
     struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
     struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
@@ -169,8 +172,16 @@ xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap)
     uint64_t modifier;
     int i;
 
-    if (xwl_pixmap->buffer)
+    if (xwl_pixmap->buffer) {
+        /* Buffer already exists. Return it and inform caller if interested. */
+        if (created)
+            *created = FALSE;
         return xwl_pixmap->buffer;
+    }
+
+    /* Buffer does not exist yet. Create now and inform caller if interested. */
+    if (created)
+        *created = TRUE;
 
     if (!xwl_pixmap->bo)
        return NULL;
@@ -205,16 +216,16 @@ xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap)
 
         xwl_pixmap->buffer =
            zwp_linux_buffer_params_v1_create_immed(params,
-                                                   pixmap->drawable.width,
-                                                   pixmap->drawable.height,
+                                                   width,
+                                                   height,
                                                    wl_drm_format_for_depth(pixmap->drawable.depth),
                                                    0);
         zwp_linux_buffer_params_v1_destroy(params);
     } else if (num_planes == 1) {
         xwl_pixmap->buffer =
             wl_drm_create_prime_buffer(xwl_screen->drm, prime_fd,
-                                       pixmap->drawable.width,
-                                       pixmap->drawable.height,
+                                       width,
+                                       height,
                                        wl_drm_format_for_depth(pixmap->drawable.depth),
                                        0, gbm_bo_get_stride(xwl_pixmap->bo),
                                        0, 0,
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index d6a2887ac..f3fd49764 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -649,7 +649,10 @@ xwl_window_post_damage(struct xwl_window *xwl_window)
 
 #ifdef GLAMOR_HAS_GBM
     if (xwl_screen->glamor)
-        buffer = xwl_glamor_pixmap_get_wl_buffer(pixmap);
+        buffer = xwl_glamor_pixmap_get_wl_buffer(pixmap,
+                                                 pixmap->drawable.width,
+                                                 pixmap->drawable.height,
+                                                 NULL);
     else
 #endif
         buffer = xwl_shm_pixmap_get_wl_buffer(pixmap);
diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h
index 36ebeaa83..49fb5ba28 100644
--- a/hw/xwayland/xwayland.h
+++ b/hw/xwayland/xwayland.h
@@ -341,7 +341,10 @@ Bool xwl_screen_set_drm_interface(struct xwl_screen *xwl_screen,
                                   uint32_t id, uint32_t version);
 Bool xwl_screen_set_dmabuf_interface(struct xwl_screen *xwl_screen,
                                      uint32_t id, uint32_t version);
-struct wl_buffer *xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap);
+struct wl_buffer *xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap,
+                                                  unsigned short width,
+                                                  unsigned short height,
+                                                  Bool *created);
 
 void xwl_screen_release_tablet_manager(struct xwl_screen *xwl_screen);
 
commit 902429f077325b98e30ede2710bd7a88440d2937
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:51 2018 +0100

    present: Add exported init function of window flip mode
    
    Allow drivers now to initialize window flip mode.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present.h b/present/present.h
index affb75f35..3d0b9722b 100644
--- a/present/present.h
+++ b/present/present.h
@@ -174,6 +174,8 @@ present_event_abandon(RRCrtcPtr crtc);
 
 extern _X_EXPORT Bool
 present_screen_init(ScreenPtr screen, present_screen_info_ptr info);
+extern _X_EXPORT Bool
+present_wnmd_screen_init(ScreenPtr screen, present_wnmd_info_ptr info);
 
 typedef void (*present_complete_notify_proc)(WindowPtr window,
                                              CARD8 kind,
diff --git a/present/present_screen.c b/present/present_screen.c
index 8ad14ad17..c7e37c5fd 100644
--- a/present/present_screen.c
+++ b/present/present_screen.c
@@ -239,7 +239,28 @@ present_screen_priv_init(ScreenPtr screen)
 }
 
 /*
- * Initialize a screen for use with present
+ * Initialize a screen for use with present in window flip mode (wnmd)
+ */
+int
+present_wnmd_screen_init(ScreenPtr screen, present_wnmd_info_ptr info)
+{
+    if (!present_screen_register_priv_keys())
+        return FALSE;
+
+    if (!present_screen_priv(screen)) {
+        present_screen_priv_ptr screen_priv = present_screen_priv_init(screen);
+        if (!screen_priv)
+            return FALSE;
+
+        screen_priv->wnmd_info = info;
+        present_wnmd_init_mode_hooks(screen_priv);
+    }
+
+    return TRUE;
+}
+
+/*
+ * Initialize a screen for use with present in default screen flip mode (scmd)
  */
 int
 present_screen_init(ScreenPtr screen, present_screen_info_ptr info)
commit a337949f99bc473ea0ae0af64736eae3d5b39399
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:50 2018 +0100

    present: Add cleanups for window flip mode
    
    Make sure that vblanks and windows get cleaned up correctly
    in window flip mode.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present_screen.c b/present/present_screen.c
index b0cae0fd1..8ad14ad17 100644
--- a/present/present_screen.c
+++ b/present/present_screen.c
@@ -109,6 +109,32 @@ present_clear_window_flip(WindowPtr window)
     }
 }
 
+static void
+present_wnmd_clear_window_flip(WindowPtr window)
+{
+    present_window_priv_ptr     window_priv = present_window_priv(window);
+    present_vblank_ptr          vblank, tmp;
+
+    if (window_priv->flip_pending) {
+        present_wnmd_set_abort_flip(window);
+        window_priv->flip_pending->window = NULL;
+    }
+
+    xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->idle_queue, event_queue) {
+        present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+        /* The pixmap will be destroyed by freeing the window resources. */
+        vblank->pixmap = NULL;
+        present_vblank_destroy(vblank);
+    }
+
+    vblank = window_priv->flip_active;
+    if (vblank) {
+        present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+        present_vblank_destroy(vblank);
+    }
+    window_priv->flip_active = NULL;
+}
+
 /*
  * Hook the close window function to clean up our window private
  */
@@ -124,7 +150,12 @@ present_destroy_window(WindowPtr window)
         present_clear_window_notifies(window);
         present_free_events(window);
         present_free_window_vblank(window);
-        present_clear_window_flip(window);
+
+        if (screen_priv->wnmd_info)
+            present_wnmd_clear_window_flip(window);
+        else
+            present_clear_window_flip(window);
+
         free(window_priv);
     }
     unwrap(screen_priv, screen, DestroyWindow);
diff --git a/present/present_vblank.c b/present/present_vblank.c
index 7662c4dd9..f93a1afa9 100644
--- a/present/present_vblank.c
+++ b/present/present_vblank.c
@@ -172,6 +172,8 @@ present_vblank_destroy(present_vblank_ptr vblank)
 {
     /* Remove vblank from window and screen lists */
     xorg_list_del(&vblank->window_list);
+    /* Also make sure vblank is removed from event queue (wnmd) */
+    xorg_list_del(&vblank->event_queue);
 
     DebugPresent(("\td %lld %p %8lld: %08lx -> %08lx\n",
                   vblank->event_id, vblank, vblank->target_msc,
commit 66a5c0bccb222ad8b9b57b10490c3041e1b3f05e
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:49 2018 +0100

    present: In window flip mode report damage on flip to driver
    
    Calculate damage before trying to flip and report it to the driver.
    This allows drivers to optimize their rendering.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present.h b/present/present.h
index 0ea0a0eb6..affb75f35 100644
--- a/present/present.h
+++ b/present/present.h
@@ -105,7 +105,8 @@ typedef Bool (*present_wnmd_flip_ptr) (WindowPtr window,
                                        uint64_t event_id,
                                        uint64_t target_msc,
                                        PixmapPtr pixmap,
-                                       Bool sync_flip);
+                                       Bool sync_flip,
+                                       RegionPtr damage);
 
 /* "unflip" back to the regular screen scanout buffer
  *
diff --git a/present/present_wnmd.c b/present/present_wnmd.c
index c1f879cac..72bda2fbf 100644
--- a/present/present_wnmd.c
+++ b/present/present_wnmd.c
@@ -350,7 +350,8 @@ present_wnmd_flip(WindowPtr window,
                   uint64_t event_id,
                   uint64_t target_msc,
                   PixmapPtr pixmap,
-                  Bool sync_flip)
+                  Bool sync_flip,
+                  RegionPtr damage)
 {
     ScreenPtr                   screen = crtc->pScreen;
     present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
@@ -360,7 +361,8 @@ present_wnmd_flip(WindowPtr window,
                                             event_id,
                                             target_msc,
                                             pixmap,
-                                            sync_flip);
+                                            sync_flip,
+                                            damage);
 }
 
 static void
@@ -449,12 +451,19 @@ present_wnmd_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
              */
             xorg_list_add(&vblank->event_queue, &window_priv->flip_queue);
 
+            /* Set update region as damaged */
+            if (vblank->update) {
+                damage = vblank->update;
+                RegionIntersect(damage, damage, &window->clipList);
+            } else
+                damage = &window->clipList;
+
             /* Try to flip - the vblank is now pending
              */
             window_priv->flip_pending = vblank;
             // ask the driver
             if (present_wnmd_flip(vblank->window, vblank->crtc, vblank->event_id,
-                                     vblank->target_msc, vblank->pixmap, vblank->sync_flip)) {
+                                     vblank->target_msc, vblank->pixmap, vblank->sync_flip, damage)) {
                 ScreenPtr screen = window->drawable.pScreen;
                 WindowPtr toplvl_window = present_wnmd_toplvl_pixmap_window(vblank->window);
                 PixmapPtr old_pixmap = screen->GetWindowPixmap(window);
@@ -464,14 +473,7 @@ present_wnmd_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
                 vblank->pixmap->refcnt++;
                 dixDestroyPixmap(old_pixmap, old_pixmap->drawable.id);
 
-                /* Report update region as damaged
-                 */
-                if (vblank->update) {
-                    damage = vblank->update;
-                    RegionIntersect(damage, damage, &window->clipList);
-                } else
-                    damage = &window->clipList;
-
+                /* Report damage */
                 DamageDamageRegion(&vblank->window->drawable, damage);
                 return;
             }
commit 029608dd80204ac96423ef79ec46c1a18bbdd5ff
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:48 2018 +0100

    present: Add window flip mode
    
    In contrast to screen flip mode this mode:
    * supports flips per windows (these windows currently need to have the same
      size as their parent windows with the same pixmap),
    * sends pixmap idle signals to the client only after the driver has given
      an additional event notify.
    
    This patch only introduces the new mode as a stub. It additionally needs a
    driver hook, such that it can get initialized and appropriate cleanup
    functions.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/Makefile.am b/present/Makefile.am
index 55ad2950e..542f4ca9c 100644
--- a/present/Makefile.am
+++ b/present/Makefile.am
@@ -15,6 +15,7 @@ libpresent_la_SOURCES = \
 	present_request.c \
 	present_scmd.c \
 	present_screen.c \
-	present_vblank.c
+	present_vblank.c \
+	present_wnmd.c
 
 sdk_HEADERS = present.h presentext.h
diff --git a/present/meson.build b/present/meson.build
index 84bea9ba4..1bd3d381e 100644
--- a/present/meson.build
+++ b/present/meson.build
@@ -9,6 +9,7 @@ srcs_present = [
     'present_scmd.c',
     'present_screen.c',
     'present_vblank.c',
+    'present_wnmd.c',
 ]
 
 libxserver_present = static_library('libxserver_present',
diff --git a/present/present.h b/present/present.h
index 8224322b3..0ea0a0eb6 100644
--- a/present/present.h
+++ b/present/present.h
@@ -159,6 +159,12 @@ typedef struct present_wnmd_info {
  */
 extern _X_EXPORT void
 present_event_notify(uint64_t event_id, uint64_t ust, uint64_t msc);
+/*
+ * Called when 'event_id' occurs for 'window'.
+ * 'ust' and 'msc' indicate when the event actually happened
+ */
+extern _X_EXPORT void
+present_wnmd_event_notify(WindowPtr window, uint64_t event_id, uint64_t ust, uint64_t msc);
 
 /* 'crtc' has been turned off, so any pending events will never occur.
  */
diff --git a/present/present_priv.h b/present/present_priv.h
index 375a100b6..f62456755 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -477,4 +477,13 @@ present_vblank_scrap(present_vblank_ptr vblank);
 void
 present_vblank_destroy(present_vblank_ptr vblank);
 
+/*
+ * present_wnmd.c
+ */
+void
+present_wnmd_set_abort_flip(WindowPtr window);
+
+void
+present_wnmd_init_mode_hooks(present_screen_priv_ptr screen_priv);
+
 #endif /*  _PRESENT_PRIV_H_ */
diff --git a/present/present_wnmd.c b/present/present_wnmd.c
new file mode 100644
index 000000000..c1f879cac
--- /dev/null
+++ b/present/present_wnmd.c
@@ -0,0 +1,701 @@
+/*
+ * Copyright © 2018 Roman Gilg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include "present_priv.h"
+
+/*
+ * Window flip mode
+ *
+ * Provides per-window flips. Flips can be processed on windows that
+ * have the same size as their parents, which they share their pixmap with.
+ *
+ * A flip still requires a copy currently, since the original pixmap needs
+ * to be updated with the new pixmap content. Just a flip of all windows
+ * to the new pixmap is diffcult, because the original pixmap might not be
+ * controlled by the Xserver.
+ *
+ */
+
+static void
+present_wnmd_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc);
+
+static void
+present_wnmd_create_event_id(present_window_priv_ptr window_priv, present_vblank_ptr vblank)
+{
+    vblank->event_id = ++window_priv->event_id;
+}
+
+static uint32_t
+present_wnmd_query_capabilities(present_screen_priv_ptr screen_priv)
+{
+    return screen_priv->wnmd_info->capabilities;
+}
+
+static RRCrtcPtr
+present_wnmd_get_crtc(present_screen_priv_ptr screen_priv, WindowPtr window)
+{
+    return (*screen_priv->wnmd_info->get_crtc)(window);
+}
+
+static int
+present_wnmd_get_ust_msc(ScreenPtr screen, WindowPtr window, uint64_t *ust, uint64_t *msc)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+    return (*screen_priv->wnmd_info->get_ust_msc)(window, ust, msc);
+}
+
+/*
+ * When the wait fence or previous flip is completed, it's time
+ * to re-try the request
+ */
+static void
+present_wnmd_re_execute(present_vblank_ptr vblank)
+{
+    uint64_t ust = 0, crtc_msc = 0;
+
+    (void) present_wnmd_get_ust_msc(vblank->screen, vblank->window, &ust, &crtc_msc);
+    present_wnmd_execute(vblank, ust, crtc_msc);
+}
+
+static void
+present_wnmd_flip_try_ready(WindowPtr window)
+{
+    present_window_priv_ptr window_priv = present_window_priv(window);
+    present_vblank_ptr      vblank;
+
+    xorg_list_for_each_entry(vblank, &window_priv->flip_queue, event_queue) {
+        if (vblank->queued) {
+            present_wnmd_re_execute(vblank);
+            return;
+        }
+    }
+}
+
+static void
+present_wnmd_free_idle_vblank(present_vblank_ptr vblank)
+{
+    present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+    present_vblank_destroy(vblank);
+}
+
+/*
+ * Free any left over idle vblanks
+ */
+static void
+present_wnmd_free_idle_vblanks(WindowPtr window)
+{
+    present_window_priv_ptr         window_priv = present_window_priv(window);
+    present_vblank_ptr              vblank, tmp;
+
+    xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->idle_queue, event_queue) {
+        present_wnmd_free_idle_vblank(vblank);
+    }
+
+    if (window_priv->flip_active) {
+        present_wnmd_free_idle_vblank(window_priv->flip_active);
+        window_priv->flip_active = NULL;
+    }
+}
+
+static WindowPtr
+present_wnmd_toplvl_pixmap_window(WindowPtr window)
+{
+    ScreenPtr       screen = window->drawable.pScreen;
+    PixmapPtr       pixmap = (*screen->GetWindowPixmap)(window);
+    WindowPtr       w = window;
+    WindowPtr       next_w;
+
+    while(w->parent) {
+        next_w = w->parent;
+        if ( (*screen->GetWindowPixmap)(next_w) != pixmap) {
+            break;
+        }
+        w = next_w;
+    }
+    return w;
+}
+
+void
+present_wnmd_set_abort_flip(WindowPtr window)
+{
+    present_window_priv_ptr window_priv = present_window_priv(window);
+
+    if (!window_priv->flip_pending->abort_flip) {
+        window_priv->flip_pending->abort_flip = TRUE;
+    }
+}
+
+static void
+present_wnmd_flips_stop(WindowPtr window)
+{
+    present_window_priv_ptr window_priv = present_window_priv(window);
+    present_screen_priv_ptr screen_priv = present_screen_priv(window->drawable.pScreen);
+
+    assert (!window_priv->flip_pending);
+
+    (*screen_priv->wnmd_info->flips_stop) (window);
+
+    present_wnmd_free_idle_vblanks(window_priv->window);
+    present_wnmd_flip_try_ready(window_priv->window);
+}
+
+static void
+present_wnmd_flip_notify(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
+{
+    WindowPtr                   window = vblank->window;
+    present_window_priv_ptr     window_priv = present_window_priv(window);
+
+    DebugPresent(("\tn %lld %p %8lld: %08lx -> %08lx\n",
+                  vblank->event_id, vblank, vblank->target_msc,
+                  vblank->pixmap ? vblank->pixmap->drawable.id : 0,
+                  vblank->window ? vblank->window->drawable.id : 0));
+
+    assert (vblank == window_priv->flip_pending);
+
+    xorg_list_del(&vblank->event_queue);
+
+    if (window_priv->flip_active) {
+        if (window_priv->flip_active->flip_idler)
+            present_wnmd_free_idle_vblank(window_priv->flip_active);
+        else
+            /* Put the previous flip in the idle_queue and wait for further notice from DDX */
+            xorg_list_append(&window_priv->flip_active->event_queue, &window_priv->idle_queue);
+    }
+
+    window_priv->flip_active = vblank;
+    window_priv->flip_pending = NULL;
+
+    if (vblank->abort_flip)
+        present_wnmd_flips_stop(window);
+
+    present_vblank_notify(vblank, PresentCompleteKindPixmap, PresentCompleteModeFlip, ust, crtc_msc);
+    present_wnmd_flip_try_ready(window);
+}
+
+void
+present_wnmd_event_notify(WindowPtr window, uint64_t event_id, uint64_t ust, uint64_t msc)
+{
+    present_window_priv_ptr     window_priv = present_window_priv(window);
+    present_vblank_ptr          vblank;
+
+    if (!window_priv)
+        return;
+    if (!event_id)
+        return;
+
+    if (window_priv->flip_active && window_priv->flip_active->event_id == event_id) {
+        /* Notify for active flip, means it is allowed to become idle */
+        window_priv->flip_active->flip_idler = TRUE;
+        return;
+    }
+
+    DebugPresent(("\te %lld ust %lld msc %lld\n", event_id, ust, msc));
+    xorg_list_for_each_entry(vblank, &window_priv->exec_queue, event_queue) {
+        if (event_id == vblank->event_id) {
+            present_wnmd_execute(vblank, ust, msc);
+            return;
+        }
+    }
+    xorg_list_for_each_entry(vblank, &window_priv->flip_queue, event_queue) {
+        if (vblank->event_id == event_id) {
+            if (vblank->queued) {
+                present_wnmd_execute(vblank, ust, msc);
+            } else {
+                assert(vblank->window);
+                present_wnmd_flip_notify(vblank, ust, msc);
+            }
+            return;
+        }
+    }
+
+    xorg_list_for_each_entry(vblank, &window_priv->idle_queue, event_queue) {
+        if (vblank->event_id == event_id) {
+            present_wnmd_free_idle_vblank(vblank);
+            return;
+        }
+    }
+}
+
+static Bool
+present_wnmd_check_flip(RRCrtcPtr           crtc,
+                        WindowPtr           window,
+                        PixmapPtr           pixmap,
+                        Bool                sync_flip,
+                        RegionPtr           valid,
+                        int16_t             x_off,
+                        int16_t             y_off,
+                        PresentFlipReason   *reason)
+{
+    ScreenPtr               screen = window->drawable.pScreen;
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+    WindowPtr               toplvl_window = present_wnmd_toplvl_pixmap_window(window);
+
+    if (reason)
+        *reason = PRESENT_FLIP_REASON_UNKNOWN;
+
+    if (!screen_priv)
+        return FALSE;
+
+    if (!screen_priv->wnmd_info)
+        return FALSE;
+
+    if (!crtc)
+        return FALSE;
+
+    /* Check to see if the driver supports flips at all */
+    if (!screen_priv->wnmd_info->flip)
+        return FALSE;
+
+    /* Don't flip redirected windows */
+    if (window->redirectDraw != RedirectDrawNone)
+        return FALSE;
+
+    /* Source pixmap must align with window exactly */
+    if (x_off || y_off)
+        return FALSE;
+
+    // TODO: Check for valid region?
+
+    /* Flip pixmap must have same dimensions as window */
+    if (window->drawable.width != pixmap->drawable.width ||
+            window->drawable.height != pixmap->drawable.height)
+        return FALSE;
+
+    /* Window must be same region as toplevel window */
+    if ( !RegionEqual(&window->winSize, &toplvl_window->winSize) )
+        return FALSE;
+
+    /* Ask the driver for permission */
+    if (screen_priv->wnmd_info->check_flip2) {
+        if (!(*screen_priv->wnmd_info->check_flip2) (crtc, window, pixmap, sync_flip, reason)) {
+            DebugPresent(("\td %08lx -> %08lx\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+/*
+ * 'window' is being reconfigured. Check to see if it is involved
+ * in flipping and clean up as necessary.
+ */
+static void
+present_wnmd_check_flip_window (WindowPtr window)
+{
+    present_window_priv_ptr window_priv = present_window_priv(window);
+    present_vblank_ptr      flip_pending;
+    present_vblank_ptr      flip_active;
+    present_vblank_ptr      vblank;
+    PresentFlipReason       reason;
+
+    /* If this window hasn't ever been used with Present, it can't be
+     * flipping
+     */
+    if (!window_priv)
+        return;
+
+    flip_pending = window_priv->flip_pending;
+    flip_active = window_priv->flip_active;
+
+    if (flip_pending) {
+        if (!present_wnmd_check_flip(flip_pending->crtc, flip_pending->window, flip_pending->pixmap,
+                                flip_pending->sync_flip, NULL, 0, 0, NULL))
+            present_wnmd_set_abort_flip(window);
+    } else if (flip_active) {
+        if (!present_wnmd_check_flip(flip_active->crtc, flip_active->window, flip_active->pixmap,
+                                     flip_active->sync_flip, NULL, 0, 0, NULL))
+            present_wnmd_flips_stop(window);
+    }
+
+    /* Now check any queued vblanks */
+    xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
+        if (vblank->queued && vblank->flip &&
+                !present_wnmd_check_flip(vblank->crtc, window, vblank->pixmap,
+                                         vblank->sync_flip, NULL, 0, 0, &reason)) {
+            vblank->flip = FALSE;
+            vblank->reason = reason;
+            if (vblank->sync_flip)
+                vblank->requeue = TRUE;
+        }
+    }
+}
+
+static Bool
+present_wnmd_flip(WindowPtr window,
+                  RRCrtcPtr crtc,
+                  uint64_t event_id,
+                  uint64_t target_msc,
+                  PixmapPtr pixmap,
+                  Bool sync_flip)
+{
+    ScreenPtr                   screen = crtc->pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    return (*screen_priv->wnmd_info->flip) (window,
+                                            crtc,
+                                            event_id,
+                                            target_msc,
+                                            pixmap,
+                                            sync_flip);
+}
+
+static void
+present_wnmd_cancel_flip(WindowPtr window)
+{
+    present_window_priv_ptr window_priv = present_window_priv(window);
+
+    if (window_priv->flip_pending)
+        present_wnmd_set_abort_flip(window);
+    else if (window_priv->flip_active)
+        present_wnmd_flips_stop(window);
+}
+
+static Bool
+present_wnmd_can_window_flip(WindowPtr window)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+    WindowPtr                   toplvl_window = present_wnmd_toplvl_pixmap_window(window);
+
+    if (!screen_priv)
+        return FALSE;
+
+    if (!screen_priv->wnmd_info)
+        return FALSE;
+
+    /* Check to see if the driver supports flips at all */
+    if (!screen_priv->wnmd_info->flip)
+        return FALSE;
+
+    /* Don't flip redirected windows */
+    if (window->redirectDraw != RedirectDrawNone)
+        return FALSE;
+
+    /* Window must be same region as toplevel window */
+    if ( !RegionEqual(&window->winSize, &toplvl_window->winSize) )
+        return FALSE;
+
+    return TRUE;
+}
+
+/*
+ * Once the required MSC has been reached, execute the pending request.
+ *
+ * For requests to actually present something, either blt contents to
+ * the window pixmap or queue a window buffer swap on the backend.
+ *
+ * For requests to just get the current MSC/UST combo, skip that part and
+ * go straight to event delivery.
+ */
+static void
+present_wnmd_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
+{
+    WindowPtr               window = vblank->window;
+    present_window_priv_ptr window_priv = present_window_priv(window);
+
+    if (present_execute_wait(vblank, crtc_msc))
+        return;
+
+    if (vblank->flip && vblank->pixmap && vblank->window) {
+        if (window_priv->flip_pending) {
+            DebugPresent(("\tr %lld %p (pending %p)\n",
+                          vblank->event_id, vblank,
+                          window_priv->flip_pending));
+            xorg_list_del(&vblank->event_queue);
+            xorg_list_append(&vblank->event_queue, &window_priv->flip_queue);
+            vblank->flip_ready = TRUE;
+            return;
+        }
+    }
+
+    xorg_list_del(&vblank->event_queue);
+    xorg_list_del(&vblank->window_list);
+    vblank->queued = FALSE;
+
+    if (vblank->pixmap && vblank->window) {
+
+        if (vblank->flip) {
+            RegionPtr damage;
+
+            DebugPresent(("\tf %lld %p %8lld: %08lx -> %08lx\n",
+                          vblank->event_id, vblank, crtc_msc,
+                          vblank->pixmap->drawable.id, vblank->window->drawable.id));
+
+            /* Prepare to flip by placing it in the flip queue
+             */
+            xorg_list_add(&vblank->event_queue, &window_priv->flip_queue);
+
+            /* Try to flip - the vblank is now pending
+             */
+            window_priv->flip_pending = vblank;
+            // ask the driver
+            if (present_wnmd_flip(vblank->window, vblank->crtc, vblank->event_id,
+                                     vblank->target_msc, vblank->pixmap, vblank->sync_flip)) {
+                ScreenPtr screen = window->drawable.pScreen;
+                WindowPtr toplvl_window = present_wnmd_toplvl_pixmap_window(vblank->window);
+                PixmapPtr old_pixmap = screen->GetWindowPixmap(window);
+
+                /* Replace window pixmap with flip pixmap */
+                present_set_tree_pixmap(toplvl_window, old_pixmap, vblank->pixmap);
+                vblank->pixmap->refcnt++;
+                dixDestroyPixmap(old_pixmap, old_pixmap->drawable.id);
+
+                /* Report update region as damaged
+                 */
+                if (vblank->update) {
+                    damage = vblank->update;
+                    RegionIntersect(damage, damage, &window->clipList);
+                } else
+                    damage = &window->clipList;
+
+                DamageDamageRegion(&vblank->window->drawable, damage);
+                return;
+            }
+
+            xorg_list_del(&vblank->event_queue);
+            /* Flip failed. Clear the flip_pending field
+              */
+            window_priv->flip_pending = NULL;
+            vblank->flip = FALSE;
+        }
+        DebugPresent(("\tc %p %8lld: %08lx -> %08lx\n", vblank, crtc_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id));
+
+        present_wnmd_cancel_flip(window);
+
+        present_execute_copy(vblank, crtc_msc);
+
+        if (vblank->queued) {
+            xorg_list_add(&vblank->event_queue, &window_priv->exec_queue);
+            xorg_list_append(&vblank->window_list, &window_priv->vblank);
+
+            return;
+        }
+    }
+
+    present_execute_post(vblank, ust, crtc_msc);
+}
+
+static int
+present_wnmd_queue_vblank(ScreenPtr screen,
+                             WindowPtr window,
+                             RRCrtcPtr crtc,
+                             uint64_t event_id,
+                             uint64_t msc)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+    return (*screen_priv->wnmd_info->queue_vblank) (window, crtc, event_id, msc);
+}
+
+static uint64_t
+present_wnmd_window_to_crtc_msc(WindowPtr window, RRCrtcPtr crtc, uint64_t window_msc, uint64_t new_msc)
+{
+    present_window_priv_ptr window_priv = present_get_window_priv(window, TRUE);
+
+    if (crtc != window_priv->crtc) {
+        uint64_t old_ust, old_msc;
+
+        if (window_priv->crtc == PresentCrtcNeverSet) {
+            window_priv->msc_offset = 0;
+        } else {
+            /* The old CRTC may have been turned off, in which case
+             * we'll just use whatever previous MSC we'd seen from this CRTC
+             */
+
+            if (present_wnmd_get_ust_msc(window->drawable.pScreen, window, &old_ust, &old_msc) != Success)
+                old_msc = window_priv->msc;
+
+            window_priv->msc_offset += new_msc - old_msc;
+        }
+        window_priv->crtc = crtc;
+    }
+
+    return window_msc + window_priv->msc_offset;
+}
+
+static int
+present_wnmd_pixmap(WindowPtr window,
+                    PixmapPtr pixmap,
+                    CARD32 serial,
+                    RegionPtr valid,
+                    RegionPtr update,
+                    int16_t x_off,
+                    int16_t y_off,
+                    RRCrtcPtr target_crtc,
+                    SyncFence *wait_fence,
+                    SyncFence *idle_fence,
+                    uint32_t options,
+                    uint64_t window_msc,
+                    uint64_t divisor,
+                    uint64_t remainder,
+                    present_notify_ptr notifies,
+                    int num_notifies)
+{
+    uint64_t                    ust = 0;
+    uint64_t                    target_msc;
+    uint64_t                    crtc_msc = 0;
+    int                         ret;
+    present_vblank_ptr          vblank, tmp;
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (!window_priv)
+        return BadAlloc;
+
+    target_crtc = present_wnmd_get_crtc(screen_priv, window);
+
+    ret = present_wnmd_get_ust_msc(screen, window, &ust, &crtc_msc);
+
+    target_msc = present_wnmd_window_to_crtc_msc(window, target_crtc, window_msc, crtc_msc);
+
+    if (ret == Success) {
+        /* Stash the current MSC away in case we need it later
+         */
+        window_priv->msc = crtc_msc;
+    }
+
+    present_adjust_timings(options,
+                           &crtc_msc,
+                           &target_msc,
+                           divisor,
+                           remainder);
+
+    /*
+     * Look for a matching presentation already on the list...
+     */
+
+    if (!update && pixmap) {
+        xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->vblank, window_list) {
+
+            if (!vblank->pixmap)
+                continue;
+
+            if (!vblank->queued)
+                continue;
+
+            if (vblank->target_msc != target_msc)
+                continue;
+
+            present_vblank_scrap(vblank);
+            if (vblank->flip_ready)
+                present_wnmd_re_execute(vblank);
+        }
+    }
+
+    vblank = present_vblank_create(window,
+                                   pixmap,
+                                   serial,
+                                   valid,
+                                   update,
+                                   x_off,
+                                   y_off,
+                                   target_crtc,
+                                   wait_fence,
+                                   idle_fence,
+                                   options,
+                                   &screen_priv->wnmd_info->capabilities,
+                                   notifies,
+                                   num_notifies,
+                                   &target_msc,
+                                   crtc_msc);
+    if (!vblank)
+        return BadAlloc;
+
+    xorg_list_append(&vblank->event_queue, &window_priv->exec_queue);
+    vblank->queued = TRUE;
+    if (crtc_msc < target_msc) {
+        if (present_wnmd_queue_vblank(screen, window, target_crtc, vblank->event_id, target_msc) == Success) {
+            return Success;
+        }
+        DebugPresent(("present_queue_vblank failed\n"));
+    }
+
+    present_wnmd_execute(vblank, ust, crtc_msc);
+    return Success;
+}
+
+static void
+present_wnmd_abort_vblank(ScreenPtr screen, WindowPtr window, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+    present_window_priv_ptr window_priv = present_window_priv(window);
+    present_vblank_ptr      vblank;
+
+    assert(crtc);
+
+    (*screen_priv->wnmd_info->abort_vblank) (window, crtc, event_id, msc);
+
+    xorg_list_for_each_entry(vblank, &window_priv->exec_queue, event_queue) {
+        if (vblank->event_id == event_id) {
+            xorg_list_del(&vblank->event_queue);
+            vblank->queued = FALSE;
+            return;
+        }
+    }
+    xorg_list_for_each_entry(vblank, &window_priv->flip_queue, event_queue) {
+        if (vblank->event_id == event_id) {
+            xorg_list_del(&vblank->event_queue);
+            vblank->queued = FALSE;
+            return;
+        }
+    }
+}
+
+static void
+present_wnmd_flip_destroy(ScreenPtr screen)
+{
+    /* Cleanup done on window destruction */
+}
+
+static void
+present_wnmd_flush(WindowPtr window)
+{
+    ScreenPtr               screen = window->drawable.pScreen;
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    (*screen_priv->wnmd_info->flush) (window);
+}
+
+void
+present_wnmd_init_mode_hooks(present_screen_priv_ptr screen_priv)
+{
+    screen_priv->query_capabilities =   &present_wnmd_query_capabilities;
+    screen_priv->get_crtc           =   &present_wnmd_get_crtc;
+
+    screen_priv->check_flip         =   &present_wnmd_check_flip;
+    screen_priv->check_flip_window  =   &present_wnmd_check_flip_window;
+    screen_priv->can_window_flip    =   &present_wnmd_can_window_flip;
+
+    screen_priv->present_pixmap     =   &present_wnmd_pixmap;
+    screen_priv->create_event_id    =   &present_wnmd_create_event_id;
+    screen_priv->queue_vblank       =   &present_wnmd_queue_vblank;
+    screen_priv->flush              =   &present_wnmd_flush;
+    screen_priv->re_execute         =   &present_wnmd_re_execute;
+
+    screen_priv->abort_vblank       =   &present_wnmd_abort_vblank;
+    screen_priv->flip_destroy       =   &present_wnmd_flip_destroy;
+}
commit 8d370fcdcaed210d9f4afc1650aa8b161c7fbb44
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:47 2018 +0100

    present: Add driver facing window flip mode hooks
    
    To enable special functionality of window flips introduce for window flips
    a separate set of driver facing function hooks.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present.h b/present/present.h
index ade838bda..8224322b3 100644
--- a/present/present.h
+++ b/present/present.h
@@ -41,6 +41,7 @@ typedef RRCrtcPtr (*present_get_crtc_ptr) (WindowPtr window);
 /* Return the current ust/msc for 'crtc'
  */
 typedef int (*present_get_ust_msc_ptr) (RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc);
+typedef int (*present_wnmd_get_ust_msc_ptr) (WindowPtr window, uint64_t *ust, uint64_t *msc);
 
 /* Queue callback on 'crtc' for time 'msc'. Call present_event_notify with 'event_id'
  * at or after 'msc'. Return false if it didn't happen (which might occur if 'crtc'
@@ -49,12 +50,20 @@ typedef int (*present_get_ust_msc_ptr) (RRCrtcPtr crtc, uint64_t *ust, uint64_t
 typedef Bool (*present_queue_vblank_ptr) (RRCrtcPtr crtc,
                                           uint64_t event_id,
                                           uint64_t msc);
+typedef Bool (*present_wnmd_queue_vblank_ptr) (WindowPtr window,
+                                               RRCrtcPtr crtc,
+                                               uint64_t event_id,
+                                               uint64_t msc);
 
 /* Abort pending vblank. The extension is no longer interested in
  * 'event_id' which was to be notified at 'msc'. If possible, the
  * driver is free to de-queue the notification.
  */
 typedef void (*present_abort_vblank_ptr) (RRCrtcPtr crtc, uint64_t event_id, uint64_t msc);
+typedef void (*present_wnmd_abort_vblank_ptr) (WindowPtr window,
+                                               RRCrtcPtr crtc,
+                                               uint64_t event_id,
+                                               uint64_t msc);
 
 /* Flush pending drawing on 'window' to the hardware.
  */
@@ -84,6 +93,19 @@ typedef Bool (*present_flip_ptr) (RRCrtcPtr crtc,
                                   uint64_t target_msc,
                                   PixmapPtr pixmap,
                                   Bool sync_flip);
+/* Flip pixmap for window, return false if it didn't happen.
+ *
+ * Like present_flip_ptr, additionaly with:
+ *
+ * 'window' used for synchronization.
+ *
+ */
+typedef Bool (*present_wnmd_flip_ptr) (WindowPtr window,
+                                       RRCrtcPtr crtc,
+                                       uint64_t event_id,
+                                       uint64_t target_msc,
+                                       PixmapPtr pixmap,
+                                       Bool sync_flip);
 
 /* "unflip" back to the regular screen scanout buffer
  *
@@ -92,6 +114,12 @@ typedef Bool (*present_flip_ptr) (RRCrtcPtr crtc,
 typedef void (*present_unflip_ptr) (ScreenPtr screen,
                                     uint64_t event_id);
 
+/* Doing flips has been discontinued.
+ *
+ * Inform driver for potential cleanup on its side.
+ */
+typedef void (*present_wnmd_flips_stop_ptr) (WindowPtr window);
+
 #define PRESENT_SCREEN_INFO_VERSION        1
 
 typedef struct present_screen_info {
@@ -110,6 +138,21 @@ typedef struct present_screen_info {
 
 } present_screen_info_rec, *present_screen_info_ptr;
 
+typedef struct present_wnmd_info {
+    uint32_t                            version;
+
+    present_get_crtc_ptr                get_crtc;
+    present_wnmd_get_ust_msc_ptr        get_ust_msc;
+    present_wnmd_queue_vblank_ptr       queue_vblank;
+    present_wnmd_abort_vblank_ptr       abort_vblank;
+    present_flush_ptr                   flush;
+    uint32_t                            capabilities;
+    present_check_flip2_ptr             check_flip2;
+    present_wnmd_flip_ptr               flip;
+    present_wnmd_flips_stop_ptr         flips_stop;
+
+} present_wnmd_info_rec, *present_wnmd_info_ptr;
+
 /*
  * Called when 'event_id' occurs. 'ust' and 'msc' indicate when the
  * event actually happened
diff --git a/present/present_priv.h b/present/present_priv.h
index f69747191..375a100b6 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -162,6 +162,7 @@ struct present_screen_priv {
     Bool                        flip_sync;
 
     present_screen_info_ptr     info;
+    present_wnmd_info_ptr       wnmd_info;
 
     /* Mode hooks */
     present_priv_query_capabilities_ptr query_capabilities;
commit 7b071b4e440313254398f06eb59b1596a6d3e8fe
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:46 2018 +0100

    present: Adapt flip mode API hooks for window flip mode
    
    Flipping pixmaps per window needs additional arguments in the
    flip mode API. Add these as preperation for window flip mode.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present_execute.c b/present/present_execute.c
index c9b5678c5..8d1ef4a8c 100644
--- a/present/present_execute.c
+++ b/present/present_execute.c
@@ -52,6 +52,7 @@ present_execute_wait(present_vblank_ptr vblank, uint64_t crtc_msc)
         vblank->requeue = FALSE;
         if (msc_is_after(vblank->target_msc, crtc_msc) &&
             Success == screen_priv->queue_vblank(screen,
+                                                 window,
                                                  vblank->crtc,
                                                  vblank->event_id,
                                                  vblank->target_msc))
@@ -77,6 +78,7 @@ present_execute_copy(present_vblank_ptr vblank, uint64_t crtc_msc)
     /* If present_flip failed, we may have to requeue for the target MSC */
     if (vblank->target_msc == crtc_msc + 1 &&
         Success == screen_priv->queue_vblank(screen,
+                                             window,
                                              vblank->crtc,
                                              vblank->event_id,
                                              vblank->target_msc)) {
diff --git a/present/present_priv.h b/present/present_priv.h
index 5e1bc3c3f..f69747191 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -87,6 +87,7 @@ struct present_vblank {
 };
 
 typedef struct present_screen_priv present_screen_priv_rec, *present_screen_priv_ptr;
+typedef struct present_window_priv present_window_priv_rec, *present_window_priv_ptr;
 
 /*
  * Mode hooks
@@ -123,9 +124,11 @@ typedef int (*present_priv_pixmap_ptr)(WindowPtr window,
                                        present_notify_ptr notifies,
                                        int num_notifies);
 
-typedef void (*present_priv_create_event_id_ptr)(present_vblank_ptr vblank);
+typedef void (*present_priv_create_event_id_ptr)(present_window_priv_ptr window_priv,
+                                                 present_vblank_ptr vblank);
 
 typedef int (*present_priv_queue_vblank_ptr)(ScreenPtr screen,
+                                             WindowPtr window,
                                              RRCrtcPtr crtc,
                                              uint64_t event_id,
                                              uint64_t msc);
@@ -133,6 +136,7 @@ typedef void (*present_priv_flush_ptr)(WindowPtr window);
 typedef void (*present_priv_re_execute_ptr)(present_vblank_ptr vblank);
 
 typedef void (*present_priv_abort_vblank_ptr)(ScreenPtr screen,
+                                              WindowPtr window,
                                               RRCrtcPtr crtc,
                                               uint64_t event_id,
                                               uint64_t msc);
@@ -206,7 +210,7 @@ typedef struct present_event {
     int mask;
 } present_event_rec;
 
-typedef struct present_window_priv {
+struct present_window_priv {
     WindowPtr              window;
     present_event_ptr      events;
     RRCrtcPtr              crtc;        /* Last reported CRTC from get_ust_msc */
@@ -223,7 +227,7 @@ typedef struct present_window_priv {
 
     present_vblank_ptr     flip_pending;
     present_vblank_ptr     flip_active;
-} present_window_priv_rec, *present_window_priv_ptr;
+};
 
 #define PresentCrtcNeverSet     ((RRCrtcPtr) 1)
 
diff --git a/present/present_scmd.c b/present/present_scmd.c
index 18ce619a6..0803a0c0b 100644
--- a/present/present_scmd.c
+++ b/present/present_scmd.c
@@ -47,7 +47,8 @@ static void
 present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc);
 
 static void
-present_scmd_create_event_id(present_vblank_ptr vblank)
+present_scmd_create_event_id(present_window_priv_ptr window_priv,
+                             present_vblank_ptr vblank)
 {
     vblank->event_id = ++present_event_id;
 }
@@ -204,6 +205,7 @@ present_flush(WindowPtr window)
 
 static int
 present_queue_vblank(ScreenPtr screen,
+                     WindowPtr window,
                      RRCrtcPtr crtc,
                      uint64_t event_id,
                      uint64_t msc)
@@ -751,7 +753,7 @@ present_scmd_pixmap(WindowPtr window,
     xorg_list_append(&vblank->event_queue, &present_exec_queue);
     vblank->queued = TRUE;
     if (msc_is_after(target_msc, crtc_msc)) {
-        ret = present_queue_vblank(screen, target_crtc, vblank->event_id, target_msc);
+        ret = present_queue_vblank(screen, window, target_crtc, vblank->event_id, target_msc);
         if (ret == Success)
             return Success;
 
@@ -764,7 +766,7 @@ present_scmd_pixmap(WindowPtr window,
 }
 
 static void
-present_scmd_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
+present_scmd_abort_vblank(ScreenPtr screen, WindowPtr window, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
 {
     present_vblank_ptr  vblank;
 
diff --git a/present/present_screen.c b/present/present_screen.c
index d6c9a5e39..b0cae0fd1 100644
--- a/present/present_screen.c
+++ b/present/present_screen.c
@@ -84,7 +84,7 @@ present_free_window_vblank(WindowPtr window)
     present_vblank_ptr          vblank, tmp;
 
     xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->vblank, window_list) {
-        screen_priv->abort_vblank(window->drawable.pScreen, vblank->crtc, vblank->event_id, vblank->target_msc);
+        screen_priv->abort_vblank(window->drawable.pScreen, window, vblank->crtc, vblank->event_id, vblank->target_msc);
         present_vblank_destroy(vblank);
     }
 }
diff --git a/present/present_vblank.c b/present/present_vblank.c
index fac639b22..7662c4dd9 100644
--- a/present/present_vblank.c
+++ b/present/present_vblank.c
@@ -77,7 +77,7 @@ present_vblank_create(WindowPtr window,
     vblank->window = window;
     vblank->pixmap = pixmap;
 
-    screen_priv->create_event_id(vblank);
+    screen_priv->create_event_id(window_priv, vblank);
 
     if (pixmap) {
         vblank->kind = PresentCompleteKindPixmap;
commit 92b91b8cf34a38de39281044d8441b6cabe87a85
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:45 2018 +0100

    present: Add flip_idler vblank property
    
    Introduce vblank property for flip modes, that demand explicite
    allowance by the driver for vblanks to become idle.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present_priv.h b/present/present_priv.h
index 3fba10d73..5e1bc3c3f 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -79,6 +79,7 @@ struct present_vblank {
     Bool                requeue;        /* on queue, but target_msc has changed */
     Bool                flip;           /* planning on using flip */
     Bool                flip_ready;     /* wants to flip, but waiting for previous flip or unflip */
+    Bool                flip_idler;     /* driver explicitly permitted idling */
     Bool                sync_flip;      /* do flip synchronous to vblank */
     Bool                abort_flip;     /* aborting this flip */
     PresentFlipReason   reason;         /* reason for which flip is not possible */
diff --git a/present/present_vblank.c b/present/present_vblank.c
index 6265dffa5..fac639b22 100644
--- a/present/present_vblank.c
+++ b/present/present_vblank.c
@@ -106,6 +106,7 @@ present_vblank_create(WindowPtr window,
     vblank->notifies = notifies;
     vblank->num_notifies = num_notifies;
     vblank->has_suboptimal = (options & PresentOptionSuboptimal);
+    vblank->flip_idler = FALSE;
 
     if (pixmap != NULL &&
         !(options & PresentOptionCopy) &&
commit 84e47f3fe68f05f7b0b762e96acd4c95fa8000ca
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:44 2018 +0100

    present: Add present_window_priv properties for window flip mode
    
    For window flip mode data about flips needs to be stored per window.
    Add properties to 'present_window_priv' and initialize them on creation.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present_priv.h b/present/present_priv.h
index ba607e279..3fba10d73 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -206,12 +206,22 @@ typedef struct present_event {
 } present_event_rec;
 
 typedef struct present_window_priv {
+    WindowPtr              window;
     present_event_ptr      events;
     RRCrtcPtr              crtc;        /* Last reported CRTC from get_ust_msc */
     uint64_t               msc_offset;
     uint64_t               msc;         /* Last reported MSC from the current crtc */
     struct xorg_list       vblank;
     struct xorg_list       notifies;
+
+    /* Used for window flips */
+    uint64_t               event_id;
+    struct xorg_list       exec_queue;
+    struct xorg_list       flip_queue;
+    struct xorg_list       idle_queue;
+
+    present_vblank_ptr     flip_pending;
+    present_vblank_ptr     flip_active;
 } present_window_priv_rec, *present_window_priv_ptr;
 
 #define PresentCrtcNeverSet     ((RRCrtcPtr) 1)
diff --git a/present/present_screen.c b/present/present_screen.c
index 868eaf85b..d6c9a5e39 100644
--- a/present/present_screen.c
+++ b/present/present_screen.c
@@ -45,6 +45,12 @@ present_get_window_priv(WindowPtr window, Bool create)
         return NULL;
     xorg_list_init(&window_priv->vblank);
     xorg_list_init(&window_priv->notifies);
+
+    xorg_list_init(&window_priv->exec_queue);
+    xorg_list_init(&window_priv->flip_queue);
+    xorg_list_init(&window_priv->idle_queue);
+
+    window_priv->window = window;
     window_priv->crtc = PresentCrtcNeverSet;
     dixSetPrivate(&window->devPrivates, &present_window_private_key, window_priv);
     return window_priv;
commit 3aaaac0be573fb09a206966075d81ebe0510ca23
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:43 2018 +0100

    present: Refactor present_screen_init
    
    To initialize easily different flip modes, refactor
    'present_screen_init'.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present_screen.c b/present/present_screen.c
index 98f701ab8..868eaf85b 100644
--- a/present/present_screen.c
+++ b/present/present_screen.c
@@ -170,32 +170,52 @@ present_clip_notify(WindowPtr window, int dx, int dy)
     wrap(screen_priv, screen, ClipNotify, present_clip_notify);
 }
 
+static Bool
+present_screen_register_priv_keys(void)
+{
+    if (!dixRegisterPrivateKey(&present_screen_private_key, PRIVATE_SCREEN, 0))
+        return FALSE;
+
+    if (!dixRegisterPrivateKey(&present_window_private_key, PRIVATE_WINDOW, 0))
+        return FALSE;
+
+    return TRUE;
+}
+
+static present_screen_priv_ptr
+present_screen_priv_init(ScreenPtr screen)
+{
+    present_screen_priv_ptr screen_priv;
+
+    screen_priv = calloc(1, sizeof (present_screen_priv_rec));
+    if (!screen_priv)
+        return NULL;
+
+    wrap(screen_priv, screen, CloseScreen, present_close_screen);
+    wrap(screen_priv, screen, DestroyWindow, present_destroy_window);
+    wrap(screen_priv, screen, ConfigNotify, present_config_notify);
+    wrap(screen_priv, screen, ClipNotify, present_clip_notify);
+
+    dixSetPrivate(&screen->devPrivates, &present_screen_private_key, screen_priv);
+
+    return screen_priv;
+}
+
 /*
  * Initialize a screen for use with present
  */
 int
 present_screen_init(ScreenPtr screen, present_screen_info_ptr info)
 {
-    if (!dixRegisterPrivateKey(&present_screen_private_key, PRIVATE_SCREEN, 0))
-        return FALSE;
-
-    if (!dixRegisterPrivateKey(&present_window_private_key, PRIVATE_WINDOW, 0))
+    if (!present_screen_register_priv_keys())
         return FALSE;
 
     if (!present_screen_priv(screen)) {
-        present_screen_priv_ptr screen_priv = calloc(1, sizeof (present_screen_priv_rec));
+        present_screen_priv_ptr screen_priv = present_screen_priv_init(screen);
         if (!screen_priv)
             return FALSE;
 
-        wrap(screen_priv, screen, CloseScreen, present_close_screen);
-        wrap(screen_priv, screen, DestroyWindow, present_destroy_window);
-        wrap(screen_priv, screen, ConfigNotify, present_config_notify);
-        wrap(screen_priv, screen, ClipNotify, present_clip_notify);
-
         screen_priv->info = info;
-
-        dixSetPrivate(&screen->devPrivates, &present_screen_private_key, screen_priv);
-
         present_scmd_init_mode_hooks(screen_priv);
 
         present_fake_screen_init(screen);
commit 6d813bbd5ea0fc38a8114c08368a7954eeb2ef37
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:42 2018 +0100

    present: Add more hooks to internal flip mode API
    
    Add hooks to query caps, get crtcs, abort vblanks and destroy
    a flip.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present.c b/present/present.c
index 8ba864772..37cbf0720 100644
--- a/present/present.c
+++ b/present/present.c
@@ -38,6 +38,34 @@ msc_is_equal_or_after(uint64_t test, uint64_t reference)
     return (int64_t)(test - reference) >= 0;
 }
 
+uint32_t
+present_query_capabilities(RRCrtcPtr crtc)
+{
+    present_screen_priv_ptr screen_priv;
+
+    if (!crtc)
+        return 0;
+
+    screen_priv = present_screen_priv(crtc->pScreen);
+
+    if (!screen_priv)
+        return 0;
+
+    return screen_priv->query_capabilities(screen_priv);
+}
+
+RRCrtcPtr
+present_get_crtc(WindowPtr window)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (!screen_priv)
+        return NULL;
+
+    return screen_priv->get_crtc(screen_priv, window);
+}
+
 /*
  * Copies the update region from a pixmap to the target drawable
  */
diff --git a/present/present_priv.h b/present/present_priv.h
index 261f0e4fa..ba607e279 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -85,9 +85,15 @@ struct present_vblank {
     Bool                has_suboptimal; /* whether client can support SuboptimalCopy mode */
 };
 
+typedef struct present_screen_priv present_screen_priv_rec, *present_screen_priv_ptr;
+
 /*
  * Mode hooks
  */
+typedef uint32_t (*present_priv_query_capabilities_ptr)(present_screen_priv_ptr screen_priv);
+typedef RRCrtcPtr (*present_priv_get_crtc_ptr)(present_screen_priv_ptr screen_priv,
+                                               WindowPtr window);
+
 typedef Bool (*present_priv_check_flip_ptr)(RRCrtcPtr crtc,
                                             WindowPtr window,
                                             PixmapPtr pixmap,
@@ -125,7 +131,13 @@ typedef int (*present_priv_queue_vblank_ptr)(ScreenPtr screen,
 typedef void (*present_priv_flush_ptr)(WindowPtr window);
 typedef void (*present_priv_re_execute_ptr)(present_vblank_ptr vblank);
 
-typedef struct present_screen_priv {
+typedef void (*present_priv_abort_vblank_ptr)(ScreenPtr screen,
+                                              RRCrtcPtr crtc,
+                                              uint64_t event_id,
+                                              uint64_t msc);
+typedef void (*present_priv_flip_destroy_ptr)(ScreenPtr screen);
+
+struct present_screen_priv {
     CloseScreenProcPtr          CloseScreen;
     ConfigNotifyProcPtr         ConfigNotify;
     DestroyWindowProcPtr        DestroyWindow;
@@ -147,6 +159,9 @@ typedef struct present_screen_priv {
     present_screen_info_ptr     info;
 
     /* Mode hooks */
+    present_priv_query_capabilities_ptr query_capabilities;
+    present_priv_get_crtc_ptr           get_crtc;
+
     present_priv_check_flip_ptr         check_flip;
     present_priv_check_flip_window_ptr  check_flip_window;
     present_priv_can_window_flip_ptr    can_window_flip;
@@ -158,7 +173,9 @@ typedef struct present_screen_priv {
     present_priv_flush_ptr              flush;
     present_priv_re_execute_ptr         re_execute;
 
-} present_screen_priv_rec, *present_screen_priv_ptr;
+    present_priv_abort_vblank_ptr       abort_vblank;
+    present_priv_flip_destroy_ptr       flip_destroy;
+};
 
 #define wrap(priv,real,mem,func) {\
     priv->mem = real->mem; \
@@ -224,6 +241,12 @@ msc_is_after(uint64_t test, uint64_t reference)
 /*
  * present.c
  */
+uint32_t
+present_query_capabilities(RRCrtcPtr crtc);
+
+RRCrtcPtr
+present_get_crtc(WindowPtr window);
+
 void
 present_copy_region(DrawablePtr drawable,
                     PixmapPtr pixmap,
@@ -398,12 +421,6 @@ present_restore_screen_pixmap(ScreenPtr screen);
 void
 present_set_abort_flip(ScreenPtr screen);
 
-RRCrtcPtr
-present_get_crtc(WindowPtr window);
-
-uint32_t
-present_query_capabilities(RRCrtcPtr crtc);
-
 Bool
 present_init(void);
 
diff --git a/present/present_scmd.c b/present/present_scmd.c
index 71c8fb9cc..18ce619a6 100644
--- a/present/present_scmd.c
+++ b/present/present_scmd.c
@@ -158,34 +158,18 @@ present_flip(RRCrtcPtr crtc,
     return (*screen_priv->info->flip) (crtc, event_id, target_msc, pixmap, sync_flip);
 }
 
-RRCrtcPtr
-present_get_crtc(WindowPtr window)
+static RRCrtcPtr
+present_scmd_get_crtc(present_screen_priv_ptr screen_priv, WindowPtr window)
 {
-    ScreenPtr                   screen = window->drawable.pScreen;
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-
-    if (!screen_priv)
-        return NULL;
-
     if (!screen_priv->info)
         return NULL;
 
     return (*screen_priv->info->get_crtc)(window);
 }
 
-uint32_t
-present_query_capabilities(RRCrtcPtr crtc)
+static uint32_t
+present_scmd_query_capabilities(present_screen_priv_ptr screen_priv)
 {
-    present_screen_priv_ptr     screen_priv;
-
-    if (!crtc)
-        return 0;
-
-    screen_priv = present_screen_priv(crtc->pScreen);
-
-    if (!screen_priv)
-        return 0;
-
     if (!screen_priv->info)
         return 0;
 
@@ -779,8 +763,8 @@ present_scmd_pixmap(WindowPtr window,
     return Success;
 }
 
-void
-present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
+static void
+present_scmd_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
 {
     present_vblank_ptr  vblank;
 
@@ -810,8 +794,8 @@ present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64
     }
 }
 
-void
-present_flip_destroy(ScreenPtr screen)
+static void
+present_scmd_flip_destroy(ScreenPtr screen)
 {
     present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
 
@@ -826,6 +810,9 @@ present_flip_destroy(ScreenPtr screen)
 void
 present_scmd_init_mode_hooks(present_screen_priv_ptr screen_priv)
 {
+    screen_priv->query_capabilities =   &present_scmd_query_capabilities;
+    screen_priv->get_crtc           =   &present_scmd_get_crtc;
+
     screen_priv->check_flip         =   &present_check_flip;
     screen_priv->check_flip_window  =   &present_check_flip_window;
     screen_priv->can_window_flip    =   &present_scmd_can_window_flip;
@@ -836,6 +823,9 @@ present_scmd_init_mode_hooks(present_screen_priv_ptr screen_priv)
     screen_priv->queue_vblank       =   &present_queue_vblank;
     screen_priv->flush              =   &present_flush;
     screen_priv->re_execute         =   &present_re_execute;
+
+    screen_priv->abort_vblank       =   &present_scmd_abort_vblank;
+    screen_priv->flip_destroy       =   &present_scmd_flip_destroy;
 }
 
 Bool
diff --git a/present/present_screen.c b/present/present_screen.c
index 454cc533c..98f701ab8 100644
--- a/present/present_screen.c
+++ b/present/present_screen.c
@@ -58,7 +58,7 @@ present_close_screen(ScreenPtr screen)
 {
     present_screen_priv_ptr screen_priv = present_screen_priv(screen);
 
-    present_flip_destroy(screen);
+    screen_priv->flip_destroy(screen);
 
     unwrap(screen_priv, screen, CloseScreen);
     (*screen->CloseScreen) (screen);
@@ -72,11 +72,13 @@ present_close_screen(ScreenPtr screen)
 static void
 present_free_window_vblank(WindowPtr window)
 {
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
     present_window_priv_ptr     window_priv = present_window_priv(window);
     present_vblank_ptr          vblank, tmp;
 
     xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->vblank, window_list) {
-        present_abort_vblank(window->drawable.pScreen, vblank->crtc, vblank->event_id, vblank->target_msc);
+        screen_priv->abort_vblank(window->drawable.pScreen, vblank->crtc, vblank->event_id, vblank->target_msc);
         present_vblank_destroy(vblank);
     }
 }
commit 6a338b5959ca5a9e5260d71b6a739a5c672d77e7
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:41 2018 +0100

    present: Move timings adjustment in common part of flip mode API
    
    To reduce future code duplication refactor timings adjustment out
    as a separate function.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present.c b/present/present.c
index da4549197..8ba864772 100644
--- a/present/present.c
+++ b/present/present.c
@@ -28,6 +28,17 @@
 #include <gcstruct.h>
 
 /*
+ * Returns:
+ * TRUE if the first MSC value is equal to or after the second one
+ * FALSE if the first MSC value is before the second one
+ */
+static Bool
+msc_is_equal_or_after(uint64_t test, uint64_t reference)
+{
+    return (int64_t)(test - reference) >= 0;
+}
+
+/*
  * Copies the update region from a pixmap to the target drawable
  */
 void
@@ -118,6 +129,33 @@ present_can_window_flip(WindowPtr window)
     return screen_priv->can_window_flip(window);
 }
 
+void
+present_adjust_timings(uint32_t options,
+                       uint64_t *crtc_msc,
+                       uint64_t *target_msc,
+                       uint64_t divisor,
+                       uint64_t remainder)
+{
+    /* Adjust target_msc to match modulus
+     */
+    if (msc_is_equal_or_after(*crtc_msc, *target_msc)) {
+        if (divisor != 0) {
+            *target_msc = *crtc_msc - (*crtc_msc % divisor) + remainder;
+            if (options & PresentOptionAsync) {
+                if (msc_is_after(*crtc_msc, *target_msc))
+                    *target_msc += divisor;
+            } else {
+                if (msc_is_equal_or_after(*crtc_msc, *target_msc))
+                    *target_msc += divisor;
+            }
+        } else {
+            *target_msc = *crtc_msc;
+            if (!(options & PresentOptionAsync))
+                (*target_msc)++;
+        }
+    }
+}
+
 int
 present_pixmap(WindowPtr window,
                PixmapPtr pixmap,
diff --git a/present/present_priv.h b/present/present_priv.h
index aee3e57d2..261f0e4fa 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -239,6 +239,13 @@ present_set_tree_pixmap(WindowPtr window,
                         PixmapPtr expected,
                         PixmapPtr pixmap);
 
+void
+present_adjust_timings(uint32_t options,
+                       uint64_t *crtc_msc,
+                       uint64_t *target_msc,
+                       uint64_t divisor,
+                       uint64_t remainder);
+
 int
 present_pixmap(WindowPtr window,
                PixmapPtr pixmap,
diff --git a/present/present_scmd.c b/present/present_scmd.c
index 7fd27ac9d..71c8fb9cc 100644
--- a/present/present_scmd.c
+++ b/present/present_scmd.c
@@ -46,17 +46,6 @@ static struct xorg_list present_flip_queue;
 static void
 present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc);
 
-/*
- * Returns:
- * TRUE if the first MSC value is equal to or after the second one
- * FALSE if the first MSC value is before the second one
- */
-static Bool
-msc_is_equal_or_after(uint64_t test, uint64_t reference)
-{
-    return (int64_t)(test - reference) >= 0;
-}
-
 static void
 present_scmd_create_event_id(present_vblank_ptr vblank)
 {
@@ -714,24 +703,11 @@ present_scmd_pixmap(WindowPtr window,
         window_priv->msc = crtc_msc;
     }
 
-    /* Adjust target_msc to match modulus
-     */
-    if (msc_is_equal_or_after(crtc_msc, target_msc)) {
-        if (divisor != 0) {
-            target_msc = crtc_msc - (crtc_msc % divisor) + remainder;
-            if (options & PresentOptionAsync) {
-                if (msc_is_after(crtc_msc, target_msc))
-                    target_msc += divisor;
-            } else {
-                if (msc_is_equal_or_after(crtc_msc, target_msc))
-                    target_msc += divisor;
-            }
-        } else {
-            target_msc = crtc_msc;
-            if (!(options & PresentOptionAsync))
-                target_msc++;
-        }
-    }
+    present_adjust_timings(options,
+                           &crtc_msc,
+                           &target_msc,
+                           divisor,
+                           remainder);
 
     /*
      * Look for a matching presentation already on the list and
commit 84112a1d0b221c00d7d3c23fd5b97687e6e3749a
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:40 2018 +0100

    present: Add flip mode API hook for present_can_window_flip
    
    Flip modes can now have different implementations of
    present_can_window_flip.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present.c b/present/present.c
index ddd9c1cc3..da4549197 100644
--- a/present/present.c
+++ b/present/present.c
@@ -109,6 +109,15 @@ present_set_tree_pixmap(WindowPtr window,
     TraverseTree(window, present_set_tree_pixmap_visit, &visit);
 }
 
+Bool
+present_can_window_flip(WindowPtr window)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    return screen_priv->can_window_flip(window);
+}
+
 int
 present_pixmap(WindowPtr window,
                PixmapPtr pixmap,
diff --git a/present/present_priv.h b/present/present_priv.h
index 27b80483e..aee3e57d2 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -97,6 +97,7 @@ typedef Bool (*present_priv_check_flip_ptr)(RRCrtcPtr crtc,
                                             int16_t y_off,
                                             PresentFlipReason *reason);
 typedef void (*present_priv_check_flip_window_ptr)(WindowPtr window);
+typedef Bool (*present_priv_can_window_flip_ptr)(WindowPtr window);
 
 typedef int (*present_priv_pixmap_ptr)(WindowPtr window,
                                        PixmapPtr pixmap,
@@ -148,6 +149,7 @@ typedef struct present_screen_priv {
     /* Mode hooks */
     present_priv_check_flip_ptr         check_flip;
     present_priv_check_flip_window_ptr  check_flip_window;
+    present_priv_can_window_flip_ptr    can_window_flip;
 
     present_priv_pixmap_ptr             present_pixmap;
     present_priv_create_event_id_ptr    create_event_id;
diff --git a/present/present_scmd.c b/present/present_scmd.c
index 8ba4b2e80..7fd27ac9d 100644
--- a/present/present_scmd.c
+++ b/present/present_scmd.c
@@ -512,8 +512,8 @@ present_check_flip_window (WindowPtr window)
     }
 }
 
-Bool
-present_can_window_flip(WindowPtr window)
+static Bool
+present_scmd_can_window_flip(WindowPtr window)
 {
     ScreenPtr                   screen = window->drawable.pScreen;
     PixmapPtr                   window_pixmap;
@@ -852,6 +852,7 @@ present_scmd_init_mode_hooks(present_screen_priv_ptr screen_priv)
 {
     screen_priv->check_flip         =   &present_check_flip;
     screen_priv->check_flip_window  =   &present_check_flip_window;
+    screen_priv->can_window_flip    =   &present_scmd_can_window_flip;
 
     screen_priv->present_pixmap     =   &present_scmd_pixmap;
     screen_priv->create_event_id    =   &present_scmd_create_event_id;
commit 1db7cf0429eabf33f8e2b55a15db4d1f87e1fb95
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:39 2018 +0100

    present: Add flip mode API hook for present_pixmap
    
    Make present_pixmap a common function callable by any
    flip mode.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present.c b/present/present.c
index ec295335c..ddd9c1cc3 100644
--- a/present/present.c
+++ b/present/present.c
@@ -108,3 +108,60 @@ present_set_tree_pixmap(WindowPtr window,
         return;
     TraverseTree(window, present_set_tree_pixmap_visit, &visit);
 }
+
+int
+present_pixmap(WindowPtr window,
+               PixmapPtr pixmap,
+               CARD32 serial,
+               RegionPtr valid,
+               RegionPtr update,
+               int16_t x_off,
+               int16_t y_off,
+               RRCrtcPtr target_crtc,
+               SyncFence *wait_fence,
+               SyncFence *idle_fence,
+               uint32_t options,
+               uint64_t window_msc,
+               uint64_t divisor,
+               uint64_t remainder,
+               present_notify_ptr notifies,
+               int num_notifies)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    return screen_priv->present_pixmap(window,
+                                       pixmap,
+                                       serial,
+                                       valid,
+                                       update,
+                                       x_off,
+                                       y_off,
+                                       target_crtc,
+                                       wait_fence,
+                                       idle_fence,
+                                       options,
+                                       window_msc,
+                                       divisor,
+                                       remainder,
+                                       notifies,
+                                       num_notifies);
+}
+
+int
+present_notify_msc(WindowPtr window,
+                   CARD32 serial,
+                   uint64_t target_msc,
+                   uint64_t divisor,
+                   uint64_t remainder)
+{
+    return present_pixmap(window,
+                          NULL,
+                          serial,
+                          NULL, NULL,
+                          0, 0,
+                          NULL,
+                          NULL, NULL,
+                          divisor == 0 ? PresentOptionAsync : 0,
+                          target_msc, divisor, remainder, NULL, 0);
+}
diff --git a/present/present_priv.h b/present/present_priv.h
index 05c793d21..27b80483e 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -98,6 +98,23 @@ typedef Bool (*present_priv_check_flip_ptr)(RRCrtcPtr crtc,
                                             PresentFlipReason *reason);
 typedef void (*present_priv_check_flip_window_ptr)(WindowPtr window);
 
+typedef int (*present_priv_pixmap_ptr)(WindowPtr window,
+                                       PixmapPtr pixmap,
+                                       CARD32 serial,
+                                       RegionPtr valid,
+                                       RegionPtr update,
+                                       int16_t x_off,
+                                       int16_t y_off,
+                                       RRCrtcPtr target_crtc,
+                                       SyncFence *wait_fence,
+                                       SyncFence *idle_fence,
+                                       uint32_t options,
+                                       uint64_t window_msc,
+                                       uint64_t divisor,
+                                       uint64_t remainder,
+                                       present_notify_ptr notifies,
+                                       int num_notifies);
+
 typedef void (*present_priv_create_event_id_ptr)(present_vblank_ptr vblank);
 
 typedef int (*present_priv_queue_vblank_ptr)(ScreenPtr screen,
@@ -132,6 +149,7 @@ typedef struct present_screen_priv {
     present_priv_check_flip_ptr         check_flip;
     present_priv_check_flip_window_ptr  check_flip_window;
 
+    present_priv_pixmap_ptr             present_pixmap;
     present_priv_create_event_id_ptr    create_event_id;
 
     present_priv_queue_vblank_ptr       queue_vblank;
@@ -219,6 +237,31 @@ present_set_tree_pixmap(WindowPtr window,
                         PixmapPtr expected,
                         PixmapPtr pixmap);
 
+int
+present_pixmap(WindowPtr window,
+               PixmapPtr pixmap,
+               CARD32 serial,
+               RegionPtr valid,
+               RegionPtr update,
+               int16_t x_off,
+               int16_t y_off,
+               RRCrtcPtr target_crtc,
+               SyncFence *wait_fence,
+               SyncFence *idle_fence,
+               uint32_t options,
+               uint64_t target_msc,
+               uint64_t divisor,
+               uint64_t remainder,
+               present_notify_ptr notifies,
+               int num_notifies);
+
+int
+present_notify_msc(WindowPtr window,
+                   CARD32 serial,
+                   uint64_t target_msc,
+                   uint64_t divisor,
+                   uint64_t remainder);
+
 /*
  * present_event.c
  */
@@ -334,31 +377,6 @@ sproc_present_dispatch(ClientPtr client);
 /*
  * present_scmd.c
  */
-int
-present_pixmap(WindowPtr window,
-               PixmapPtr pixmap,
-               CARD32 serial,
-               RegionPtr valid,
-               RegionPtr update,
-               int16_t x_off,
-               int16_t y_off,
-               RRCrtcPtr target_crtc,
-               SyncFence *wait_fence,
-               SyncFence *idle_fence,
-               uint32_t options,
-               uint64_t target_msc,
-               uint64_t divisor,
-               uint64_t remainder,
-               present_notify_ptr notifies,
-               int num_notifies);
-
-int
-present_notify_msc(WindowPtr window,
-                   CARD32 serial,
-                   uint64_t target_msc,
-                   uint64_t divisor,
-                   uint64_t remainder);
-
 void
 present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc);
 
diff --git a/present/present_scmd.c b/present/present_scmd.c
index 4582887d7..8ba4b2e80 100644
--- a/present/present_scmd.c
+++ b/present/present_scmd.c
@@ -662,23 +662,23 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
     present_execute_post(vblank, ust, crtc_msc);
 }
 
-int
-present_pixmap(WindowPtr window,
-               PixmapPtr pixmap,
-               CARD32 serial,
-               RegionPtr valid,
-               RegionPtr update,
-               int16_t x_off,
-               int16_t y_off,
-               RRCrtcPtr target_crtc,
-               SyncFence *wait_fence,
-               SyncFence *idle_fence,
-               uint32_t options,
-               uint64_t window_msc,
-               uint64_t divisor,
-               uint64_t remainder,
-               present_notify_ptr notifies,
-               int num_notifies)
+static int
+present_scmd_pixmap(WindowPtr window,
+                    PixmapPtr pixmap,
+                    CARD32 serial,
+                    RegionPtr valid,
+                    RegionPtr update,
+                    int16_t x_off,
+                    int16_t y_off,
+                    RRCrtcPtr target_crtc,
+                    SyncFence *wait_fence,
+                    SyncFence *idle_fence,
+                    uint32_t options,
+                    uint64_t window_msc,
+                    uint64_t divisor,
+                    uint64_t remainder,
+                    present_notify_ptr notifies,
+                    int num_notifies)
 {
     uint64_t                    ust = 0;
     uint64_t                    target_msc;
@@ -834,24 +834,6 @@ present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64
     }
 }
 
-int
-present_notify_msc(WindowPtr window,
-                   CARD32 serial,
-                   uint64_t target_msc,
-                   uint64_t divisor,
-                   uint64_t remainder)
-{
-    return present_pixmap(window,
-                          NULL,
-                          serial,
-                          NULL, NULL,
-                          0, 0,
-                          NULL,
-                          NULL, NULL,
-                          divisor == 0 ? PresentOptionAsync : 0,
-                          target_msc, divisor, remainder, NULL, 0);
-}
-
 void
 present_flip_destroy(ScreenPtr screen)
 {
@@ -871,7 +853,9 @@ present_scmd_init_mode_hooks(present_screen_priv_ptr screen_priv)
     screen_priv->check_flip         =   &present_check_flip;
     screen_priv->check_flip_window  =   &present_check_flip_window;
 
+    screen_priv->present_pixmap     =   &present_scmd_pixmap;
     screen_priv->create_event_id    =   &present_scmd_create_event_id;
+
     screen_priv->queue_vblank       =   &present_queue_vblank;
     screen_priv->flush              =   &present_flush;
     screen_priv->re_execute         =   &present_re_execute;
commit 679ffbf5f39822ea508e50f1b7c92a2a9e79f7bb
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:38 2018 +0100

    present: Refactor execute in separate file
    
    To be shared by multiple flip modes, refactor execute functionality,
    such that logical chunks can go in new separate file.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/Makefile.am b/present/Makefile.am
index 3d3d38d76..55ad2950e 100644
--- a/present/Makefile.am
+++ b/present/Makefile.am
@@ -7,6 +7,7 @@ libpresent_la_SOURCES = \
 	present.h \
 	present.c \
 	present_event.c \
+	present_execute.c \
 	present_fake.c \
 	present_fence.c \
 	present_notify.c \
diff --git a/present/meson.build b/present/meson.build
index d22cd09a7..84bea9ba4 100644
--- a/present/meson.build
+++ b/present/meson.build
@@ -1,6 +1,7 @@
 srcs_present = [
     'present.c',
     'present_event.c',
+    'present_execute.c',
     'present_fake.c',
     'present_fence.c',
     'present_notify.c',
diff --git a/present/present_execute.c b/present/present_execute.c
new file mode 100644
index 000000000..c9b5678c5
--- /dev/null
+++ b/present/present_execute.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include "present_priv.h"
+
+/*
+ * Called when the wait fence is triggered; just gets the current msc/ust and
+ * calls the proper execute again. That will re-check the fence and pend the
+ * request again if it's still not actually ready
+ */
+static void
+present_wait_fence_triggered(void *param)
+{
+    present_vblank_ptr      vblank = param;
+    ScreenPtr               screen = vblank->screen;
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    screen_priv->re_execute(vblank);
+}
+
+Bool
+present_execute_wait(present_vblank_ptr vblank, uint64_t crtc_msc)
+{
+    WindowPtr                   window = vblank->window;
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    if (vblank->requeue) {
+        vblank->requeue = FALSE;
+        if (msc_is_after(vblank->target_msc, crtc_msc) &&
+            Success == screen_priv->queue_vblank(screen,
+                                                 vblank->crtc,
+                                                 vblank->event_id,
+                                                 vblank->target_msc))
+            return TRUE;
+    }
+
+    if (vblank->wait_fence) {
+        if (!present_fence_check_triggered(vblank->wait_fence)) {
+            present_fence_set_callback(vblank->wait_fence, present_wait_fence_triggered, vblank);
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+void
+present_execute_copy(present_vblank_ptr vblank, uint64_t crtc_msc)
+{
+    WindowPtr                   window = vblank->window;
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    /* If present_flip failed, we may have to requeue for the target MSC */
+    if (vblank->target_msc == crtc_msc + 1 &&
+        Success == screen_priv->queue_vblank(screen,
+                                             vblank->crtc,
+                                             vblank->event_id,
+                                             vblank->target_msc)) {
+        vblank->queued = TRUE;
+        return;
+    }
+
+    present_copy_region(&window->drawable, vblank->pixmap, vblank->update, vblank->x_off, vblank->y_off);
+
+    /* present_copy_region sticks the region into a scratch GC,
+     * which is then freed, freeing the region
+     */
+    vblank->update = NULL;
+    screen_priv->flush(window);
+
+    present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+}
+
+void
+present_execute_post(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
+{
+    uint8_t mode;
+
+    /* Compute correct CompleteMode
+     */
+    if (vblank->kind == PresentCompleteKindPixmap) {
+        if (vblank->pixmap && vblank->window) {
+            if (vblank->has_suboptimal && vblank->reason == PRESENT_FLIP_REASON_BUFFER_FORMAT)
+                mode = PresentCompleteModeSuboptimalCopy;
+            else
+                mode = PresentCompleteModeCopy;
+        } else {
+            mode = PresentCompleteModeSkip;
+        }
+    }
+    else
+        mode = PresentCompleteModeCopy;
+
+    present_vblank_notify(vblank, vblank->kind, mode, ust, crtc_msc);
+    present_vblank_destroy(vblank);
+}
diff --git a/present/present_priv.h b/present/present_priv.h
index 971bce505..05c793d21 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -245,6 +245,18 @@ Bool
 present_event_init(void);
 
 /*
+ * present_execute.c
+ */
+Bool
+present_execute_wait(present_vblank_ptr vblank, uint64_t crtc_msc);
+
+void
+present_execute_copy(present_vblank_ptr vblank, uint64_t crtc_msc);
+
+void
+present_execute_post(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc);
+
+/*
  * present_fake.c
  */
 int
diff --git a/present/present_scmd.c b/present/present_scmd.c
index 0376b4e75..4582887d7 100644
--- a/present/present_scmd.c
+++ b/present/present_scmd.c
@@ -551,18 +551,6 @@ present_can_window_flip(WindowPtr window)
 }
 
 /*
- * Called when the wait fence is triggered; just gets the current msc/ust and
- * calls present_execute again. That will re-check the fence and pend the
- * request again if it's still not actually ready
- */
-static void
-present_wait_fence_triggered(void *param)
-{
-    present_vblank_ptr  vblank = param;
-    present_re_execute(vblank);
-}
-
-/*
  * Once the required MSC has been reached, execute the pending request.
  *
  * For requests to actually present something, either blt contents to
@@ -578,24 +566,9 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
     WindowPtr                   window = vblank->window;
     ScreenPtr                   screen = window->drawable.pScreen;
     present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-    uint8_t                     mode;
-
-    if (vblank->requeue) {
-        vblank->requeue = FALSE;
-        if (msc_is_after(vblank->target_msc, crtc_msc) &&
-            Success == present_queue_vblank(screen,
-                                            vblank->crtc,
-                                            vblank->event_id,
-                                            vblank->target_msc))
-            return;
-    }
 
-    if (vblank->wait_fence) {
-        if (!present_fence_check_triggered(vblank->wait_fence)) {
-            present_fence_set_callback(vblank->wait_fence, present_wait_fence_triggered, vblank);
-            return;
-        }
-    }
+    if (present_execute_wait(vblank, crtc_msc))
+        return;
 
     if (vblank->flip && vblank->pixmap && vblank->window) {
         if (screen_priv->flip_pending || screen_priv->unflip_event_id) {
@@ -676,48 +649,17 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
                 present_unflip(screen);
         }
 
-        /* If present_flip failed, we may have to requeue for the target MSC */
-        if (vblank->target_msc == crtc_msc + 1 &&
-            Success == present_queue_vblank(screen,
-                                            vblank->crtc,
-                                            vblank->event_id,
-                                            vblank->target_msc)) {
+        present_execute_copy(vblank, crtc_msc);
+
+        if (vblank->queued) {
             xorg_list_add(&vblank->event_queue, &present_exec_queue);
             xorg_list_append(&vblank->window_list,
                              &present_get_window_priv(window, TRUE)->vblank);
-            vblank->queued = TRUE;
             return;
         }
-
-        present_copy_region(&window->drawable, vblank->pixmap, vblank->update, vblank->x_off, vblank->y_off);
-
-        /* present_copy_region sticks the region into a scratch GC,
-         * which is then freed, freeing the region
-         */
-        vblank->update = NULL;
-        present_flush(window);
-
-        present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
     }
 
-    /* Compute correct CompleteMode
-     */
-    if (vblank->kind == PresentCompleteKindPixmap) {
-        if (vblank->pixmap && vblank->window) {
-            if (vblank->has_suboptimal && vblank->reason == PRESENT_FLIP_REASON_BUFFER_FORMAT)
-                mode = PresentCompleteModeSuboptimalCopy;
-            else
-                mode = PresentCompleteModeCopy;
-        } else {
-            mode = PresentCompleteModeSkip;
-        }
-    }
-    else
-        mode = PresentCompleteModeCopy;
-
-
-    present_vblank_notify(vblank, vblank->kind, mode, ust, crtc_msc);
-    present_vblank_destroy(vblank);
+    present_execute_post(vblank, ust, crtc_msc);
 }
 
 int
commit 1e7d8902bfe7cfb79c41b14fc6b50bcbe4f7c800
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:37 2018 +0100

    present: Add flip mode API hooks for several functions
    
    Add 'queue_vblank', 'flush' and 're_execute' hooks, that
    are supposed to be shared with other flip modes.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present_priv.h b/present/present_priv.h
index dc6654e77..971bce505 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -100,6 +100,13 @@ typedef void (*present_priv_check_flip_window_ptr)(WindowPtr window);
 
 typedef void (*present_priv_create_event_id_ptr)(present_vblank_ptr vblank);
 
+typedef int (*present_priv_queue_vblank_ptr)(ScreenPtr screen,
+                                             RRCrtcPtr crtc,
+                                             uint64_t event_id,
+                                             uint64_t msc);
+typedef void (*present_priv_flush_ptr)(WindowPtr window);
+typedef void (*present_priv_re_execute_ptr)(present_vblank_ptr vblank);
+
 typedef struct present_screen_priv {
     CloseScreenProcPtr          CloseScreen;
     ConfigNotifyProcPtr         ConfigNotify;
@@ -126,6 +133,11 @@ typedef struct present_screen_priv {
     present_priv_check_flip_window_ptr  check_flip_window;
 
     present_priv_create_event_id_ptr    create_event_id;
+
+    present_priv_queue_vblank_ptr       queue_vblank;
+    present_priv_flush_ptr              flush;
+    present_priv_re_execute_ptr         re_execute;
+
 } present_screen_priv_rec, *present_screen_priv_ptr;
 
 #define wrap(priv,real,mem,func) {\
diff --git a/present/present_scmd.c b/present/present_scmd.c
index 1e11d3505..0376b4e75 100644
--- a/present/present_scmd.c
+++ b/present/present_scmd.c
@@ -930,6 +930,9 @@ present_scmd_init_mode_hooks(present_screen_priv_ptr screen_priv)
     screen_priv->check_flip_window  =   &present_check_flip_window;
 
     screen_priv->create_event_id    =   &present_scmd_create_event_id;
+    screen_priv->queue_vblank       =   &present_queue_vblank;
+    screen_priv->flush              =   &present_flush;
+    screen_priv->re_execute         =   &present_re_execute;
 }
 
 Bool
commit 5365ece70a75a05df3d6351767d19c3edcf0305d
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:36 2018 +0100

    present: Move vblank functionality in seperate file
    
    With the new internal flip mode API move vblank creation
    and so on into a seperate file, such that it can be shared
    between flip modes.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/Makefile.am b/present/Makefile.am
index 3b458fdbc..3d3d38d76 100644
--- a/present/Makefile.am
+++ b/present/Makefile.am
@@ -13,6 +13,7 @@ libpresent_la_SOURCES = \
 	present_priv.h \
 	present_request.c \
 	present_scmd.c \
-	present_screen.c
+	present_screen.c \
+	present_vblank.c
 
 sdk_HEADERS = present.h presentext.h
diff --git a/present/meson.build b/present/meson.build
index 859a99152..d22cd09a7 100644
--- a/present/meson.build
+++ b/present/meson.build
@@ -7,6 +7,7 @@ srcs_present = [
     'present_request.c',
     'present_scmd.c',
     'present_screen.c',
+    'present_vblank.c',
 ]
 
 libxserver_present = static_library('libxserver_present',
diff --git a/present/present_priv.h b/present/present_priv.h
index 8908061c0..dc6654e77 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -179,6 +179,17 @@ present_window_priv_ptr
 present_get_window_priv(WindowPtr window, Bool create);
 
 /*
+ * Returns:
+ * TRUE if the first MSC value is after the second one
+ * FALSE if the first MSC value is equal to or before the second one
+ */
+static inline Bool
+msc_is_after(uint64_t test, uint64_t reference)
+{
+    return (int64_t)(test - reference) > 0;
+}
+
+/*
  * present.c
  */
 void
@@ -328,9 +339,6 @@ void
 present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc);
 
 void
-present_vblank_destroy(present_vblank_ptr vblank);
-
-void
 present_flip_destroy(ScreenPtr screen);
 
 void
@@ -355,4 +363,34 @@ present_scmd_init_mode_hooks(present_screen_priv_ptr screen_priv);
  * present_screen.c
  */
 
+/*
+ * present_vblank.c
+ */
+void
+present_vblank_notify(present_vblank_ptr vblank, CARD8 kind, CARD8 mode, uint64_t ust, uint64_t crtc_msc);
+
+present_vblank_ptr
+present_vblank_create(WindowPtr window,
+                      PixmapPtr pixmap,
+                      CARD32 serial,
+                      RegionPtr valid,
+                      RegionPtr update,
+                      int16_t x_off,
+                      int16_t y_off,
+                      RRCrtcPtr target_crtc,
+                      SyncFence *wait_fence,
+                      SyncFence *idle_fence,
+                      uint32_t options,
+                      const uint32_t *capabilities,
+                      present_notify_ptr notifies,
+                      int num_notifies,
+                      uint64_t *target_msc,
+                      uint64_t crtc_msc);
+
+void
+present_vblank_scrap(present_vblank_ptr vblank);
+
+void
+present_vblank_destroy(present_vblank_ptr vblank);
+
 #endif /*  _PRESENT_PRIV_H_ */
diff --git a/present/present_scmd.c b/present/present_scmd.c
index 17a1758d8..1e11d3505 100644
--- a/present/present_scmd.c
+++ b/present/present_scmd.c
@@ -48,17 +48,6 @@ present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc);
 
 /*
  * Returns:
- * TRUE if the first MSC value is after the second one
- * FALSE if the first MSC value is equal to or before the second one
- */
-static Bool
-msc_is_after(uint64_t test, uint64_t reference)
-{
-    return (int64_t)(test - reference) > 0;
-}
-
-/*
- * Returns:
  * TRUE if the first MSC value is equal to or after the second one
  * FALSE if the first MSC value is before the second one
  */
@@ -180,22 +169,6 @@ present_flip(RRCrtcPtr crtc,
     return (*screen_priv->info->flip) (crtc, event_id, target_msc, pixmap, sync_flip);
 }
 
-static void
-present_vblank_notify(present_vblank_ptr vblank, CARD8 kind, CARD8 mode, uint64_t ust, uint64_t crtc_msc)
-{
-    int         n;
-
-    if (vblank->window)
-        present_send_complete_notify(vblank->window, kind, mode, vblank->serial, ust, crtc_msc - vblank->msc_offset);
-    for (n = 0; n < vblank->num_notifies; n++) {
-        WindowPtr   window = vblank->notifies[n].window;
-        CARD32      serial = vblank->notifies[n].serial;
-
-        if (window)
-            present_send_complete_notify(window, kind, mode, serial, ust, crtc_msc - vblank->msc_offset);
-    }
-}
-
 RRCrtcPtr
 present_get_crtc(WindowPtr window)
 {
@@ -773,7 +746,6 @@ present_pixmap(WindowPtr window,
     ScreenPtr                   screen = window->drawable.pScreen;
     present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
     present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-    PresentFlipReason           reason = PRESENT_FLIP_REASON_UNKNOWN;
 
     if (!window_priv)
         return BadAlloc;
@@ -854,81 +826,26 @@ present_pixmap(WindowPtr window,
         }
     }
 
-    vblank = calloc (1, sizeof (present_vblank_rec));
+    vblank = present_vblank_create(window,
+                                   pixmap,
+                                   serial,
+                                   valid,
+                                   update,
+                                   x_off,
+                                   y_off,
+                                   target_crtc,
+                                   wait_fence,
+                                   idle_fence,
+                                   options,
+                                   screen_priv->info ? &screen_priv->info->capabilities : NULL,
+                                   notifies,
+                                   num_notifies,
+                                   &target_msc,
+                                   crtc_msc);
+
     if (!vblank)
         return BadAlloc;
 
-    xorg_list_append(&vblank->window_list, &window_priv->vblank);
-    xorg_list_init(&vblank->event_queue);
-
-    vblank->screen = screen;
-    vblank->window = window;
-    vblank->pixmap = pixmap;
-    present_scmd_create_event_id(vblank);
-
-    if (pixmap) {
-        vblank->kind = PresentCompleteKindPixmap;
-        pixmap->refcnt++;
-    } else
-        vblank->kind = PresentCompleteKindNotifyMSC;
-
-    vblank->serial = serial;
-
-    if (valid) {
-        vblank->valid = RegionDuplicate(valid);
-        if (!vblank->valid)
-            goto no_mem;
-    }
-    if (update) {
-        vblank->update = RegionDuplicate(update);
-        if (!vblank->update)
-            goto no_mem;
-    }
-
-    vblank->x_off = x_off;
-    vblank->y_off = y_off;
-    vblank->target_msc = target_msc;
-    vblank->crtc = target_crtc;
-    vblank->msc_offset = window_priv->msc_offset;
-    vblank->notifies = notifies;
-    vblank->num_notifies = num_notifies;
-    vblank->has_suboptimal = (options & PresentOptionSuboptimal);
-
-    if (pixmap != NULL &&
-        !(options & PresentOptionCopy) &&
-        screen_priv->info) {
-        if (msc_is_after(target_msc, crtc_msc) &&
-            present_check_flip (target_crtc, window, pixmap, TRUE, valid, x_off, y_off, &reason))
-        {
-            vblank->flip = TRUE;
-            vblank->sync_flip = TRUE;
-            target_msc--;
-        } else if ((screen_priv->info->capabilities & PresentCapabilityAsync) &&
-            present_check_flip (target_crtc, window, pixmap, FALSE, valid, x_off, y_off, &reason))
-        {
-            vblank->flip = TRUE;
-        }
-    }
-    vblank->reason = reason;
-
-    if (wait_fence) {
-        vblank->wait_fence = present_fence_create(wait_fence);
-        if (!vblank->wait_fence)
-            goto no_mem;
-    }
-
-    if (idle_fence) {
-        vblank->idle_fence = present_fence_create(idle_fence);
-        if (!vblank->idle_fence)
-            goto no_mem;
-    }
-
-    if (pixmap)
-        DebugPresent(("q %lld %p %8lld: %08lx -> %08lx (crtc %p) flip %d vsync %d serial %d\n",
-                      vblank->event_id, vblank, target_msc,
-                      vblank->pixmap->drawable.id, vblank->window->drawable.id,
-                      target_crtc, vblank->flip, vblank->sync_flip, vblank->serial));
-
     xorg_list_append(&vblank->event_queue, &present_exec_queue);
     vblank->queued = TRUE;
     if (msc_is_after(target_msc, crtc_msc)) {
@@ -942,12 +859,6 @@ present_pixmap(WindowPtr window,
     present_execute(vblank, ust, crtc_msc);
 
     return Success;
-
-no_mem:
-    ret = BadAlloc;
-    vblank->notifies = NULL;
-    present_vblank_destroy(vblank);
-    return ret;
 }
 
 void
@@ -1013,39 +924,6 @@ present_flip_destroy(ScreenPtr screen)
 }
 
 void
-present_vblank_destroy(present_vblank_ptr vblank)
-{
-    /* Remove vblank from window and screen lists */
-    xorg_list_del(&vblank->window_list);
-
-    DebugPresent(("\td %lld %p %8lld: %08lx -> %08lx\n",
-                  vblank->event_id, vblank, vblank->target_msc,
-                  vblank->pixmap ? vblank->pixmap->drawable.id : 0,
-                  vblank->window ? vblank->window->drawable.id : 0));
-
-    /* Drop pixmap reference */
-    if (vblank->pixmap)
-        dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
-
-    /* Free regions */
-    if (vblank->valid)
-        RegionDestroy(vblank->valid);
-    if (vblank->update)
-        RegionDestroy(vblank->update);
-
-    if (vblank->wait_fence)
-        present_fence_destroy(vblank->wait_fence);
-
-    if (vblank->idle_fence)
-        present_fence_destroy(vblank->idle_fence);
-
-    if (vblank->notifies)
-        present_destroy_notifies(vblank->notifies, vblank->num_notifies);
-
-    free(vblank);
-}
-
-void
 present_scmd_init_mode_hooks(present_screen_priv_ptr screen_priv)
 {
     screen_priv->check_flip         =   &present_check_flip;
diff --git a/present/present_vblank.c b/present/present_vblank.c
new file mode 100644
index 000000000..6265dffa5
--- /dev/null
+++ b/present/present_vblank.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include "present_priv.h"
+
+void
+present_vblank_notify(present_vblank_ptr vblank, CARD8 kind, CARD8 mode, uint64_t ust, uint64_t crtc_msc)
+{
+    int n;
+
+    if (vblank->window)
+        present_send_complete_notify(vblank->window, kind, mode, vblank->serial, ust, crtc_msc - vblank->msc_offset);
+    for (n = 0; n < vblank->num_notifies; n++) {
+        WindowPtr   window = vblank->notifies[n].window;
+        CARD32      serial = vblank->notifies[n].serial;
+
+        if (window)
+            present_send_complete_notify(window, kind, mode, serial, ust, crtc_msc - vblank->msc_offset);
+    }
+}
+
+present_vblank_ptr
+present_vblank_create(WindowPtr window,
+                      PixmapPtr pixmap,
+                      CARD32 serial,
+                      RegionPtr valid,
+                      RegionPtr update,
+                      int16_t x_off,
+                      int16_t y_off,
+                      RRCrtcPtr target_crtc,
+                      SyncFence *wait_fence,
+                      SyncFence *idle_fence,
+                      uint32_t options,
+                      const uint32_t *capabilities,
+                      present_notify_ptr notifies,
+                      int num_notifies,
+                      uint64_t *target_msc,
+                      uint64_t crtc_msc)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+    present_vblank_ptr          vblank;
+    PresentFlipReason           reason = PRESENT_FLIP_REASON_UNKNOWN;
+
+    vblank = calloc (1, sizeof (present_vblank_rec));
+    if (!vblank)
+        return NULL;
+
+    xorg_list_append(&vblank->window_list, &window_priv->vblank);
+    xorg_list_init(&vblank->event_queue);
+
+    vblank->screen = screen;
+    vblank->window = window;
+    vblank->pixmap = pixmap;
+
+    screen_priv->create_event_id(vblank);
+
+    if (pixmap) {
+        vblank->kind = PresentCompleteKindPixmap;
+        pixmap->refcnt++;
+    } else
+        vblank->kind = PresentCompleteKindNotifyMSC;
+
+    vblank->serial = serial;
+
+    if (valid) {
+        vblank->valid = RegionDuplicate(valid);
+        if (!vblank->valid)
+            goto no_mem;
+    }
+    if (update) {
+        vblank->update = RegionDuplicate(update);
+        if (!vblank->update)
+            goto no_mem;
+    }
+
+    vblank->x_off = x_off;
+    vblank->y_off = y_off;
+    vblank->target_msc = *target_msc;
+    vblank->crtc = target_crtc;
+    vblank->msc_offset = window_priv->msc_offset;
+    vblank->notifies = notifies;
+    vblank->num_notifies = num_notifies;
+    vblank->has_suboptimal = (options & PresentOptionSuboptimal);
+
+    if (pixmap != NULL &&
+        !(options & PresentOptionCopy) &&
+        capabilities) {
+        if (msc_is_after(*target_msc, crtc_msc) &&
+            screen_priv->check_flip (target_crtc, window, pixmap, TRUE, valid, x_off, y_off, &reason))
+        {
+            vblank->flip = TRUE;
+            vblank->sync_flip = TRUE;
+            *target_msc = *target_msc - 1;
+        } else if ((*capabilities & PresentCapabilityAsync) &&
+            screen_priv->check_flip (target_crtc, window, pixmap, FALSE, valid, x_off, y_off, &reason))
+        {
+            vblank->flip = TRUE;
+        }
+    }
+    vblank->reason = reason;
+
+    if (wait_fence) {
+        vblank->wait_fence = present_fence_create(wait_fence);
+        if (!vblank->wait_fence)
+            goto no_mem;
+    }
+
+    if (idle_fence) {
+        vblank->idle_fence = present_fence_create(idle_fence);
+        if (!vblank->idle_fence)
+            goto no_mem;
+    }
+
+    if (pixmap)
+        DebugPresent(("q %lld %p %8lld: %08lx -> %08lx (crtc %p) flip %d vsync %d serial %d\n",
+                      vblank->event_id, vblank, *target_msc,
+                      vblank->pixmap->drawable.id, vblank->window->drawable.id,
+                      target_crtc, vblank->flip, vblank->sync_flip, vblank->serial));
+    return vblank;
+
+no_mem:
+    vblank->notifies = NULL;
+    present_vblank_destroy(vblank);
+    return NULL;
+}
+
+void
+present_vblank_scrap(present_vblank_ptr vblank)
+{
+    DebugPresent(("\tx %lld %p %8lld: %08lx -> %08lx (crtc %p)\n",
+                  vblank->event_id, vblank, vblank->target_msc,
+                  vblank->pixmap->drawable.id, vblank->window->drawable.id,
+                  vblank->crtc));
+
+    present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+    present_fence_destroy(vblank->idle_fence);
+    dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
+
+    vblank->pixmap = NULL;
+    vblank->idle_fence = NULL;
+    vblank->flip = FALSE;
+}
+
+void
+present_vblank_destroy(present_vblank_ptr vblank)
+{
+    /* Remove vblank from window and screen lists */
+    xorg_list_del(&vblank->window_list);
+
+    DebugPresent(("\td %lld %p %8lld: %08lx -> %08lx\n",
+                  vblank->event_id, vblank, vblank->target_msc,
+                  vblank->pixmap ? vblank->pixmap->drawable.id : 0,
+                  vblank->window ? vblank->window->drawable.id : 0));
+
+    /* Drop pixmap reference */
+    if (vblank->pixmap)
+        dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
+
+    /* Free regions */
+    if (vblank->valid)
+        RegionDestroy(vblank->valid);
+    if (vblank->update)
+        RegionDestroy(vblank->update);
+
+    if (vblank->wait_fence)
+        present_fence_destroy(vblank->wait_fence);
+
+    if (vblank->idle_fence)
+        present_fence_destroy(vblank->idle_fence);
+
+    if (vblank->notifies)
+        present_destroy_notifies(vblank->notifies, vblank->num_notifies);
+
+    free(vblank);
+}
commit c5c50c6db1e71e976596750277b1a618704c04aa
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:35 2018 +0100

    present: Preliminary internal flip mode API
    
    Add some basic function hooks to our future present-internal flip mode API,
    that will allow us to share functionality in between modes and move more code
    in separate files.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/present_priv.h b/present/present_priv.h
index 66539e183..8908061c0 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -85,6 +85,21 @@ struct present_vblank {
     Bool                has_suboptimal; /* whether client can support SuboptimalCopy mode */
 };
 
+/*
+ * Mode hooks
+ */
+typedef Bool (*present_priv_check_flip_ptr)(RRCrtcPtr crtc,
+                                            WindowPtr window,
+                                            PixmapPtr pixmap,
+                                            Bool sync_flip,
+                                            RegionPtr valid,
+                                            int16_t x_off,
+                                            int16_t y_off,
+                                            PresentFlipReason *reason);
+typedef void (*present_priv_check_flip_window_ptr)(WindowPtr window);
+
+typedef void (*present_priv_create_event_id_ptr)(present_vblank_ptr vblank);
+
 typedef struct present_screen_priv {
     CloseScreenProcPtr          CloseScreen;
     ConfigNotifyProcPtr         ConfigNotify;
@@ -105,6 +120,12 @@ typedef struct present_screen_priv {
     Bool                        flip_sync;
 
     present_screen_info_ptr     info;
+
+    /* Mode hooks */
+    present_priv_check_flip_ptr         check_flip;
+    present_priv_check_flip_window_ptr  check_flip_window;
+
+    present_priv_create_event_id_ptr    create_event_id;
 } present_screen_priv_rec, *present_screen_priv_ptr;
 
 #define wrap(priv,real,mem,func) {\
@@ -318,9 +339,6 @@ present_restore_screen_pixmap(ScreenPtr screen);
 void
 present_set_abort_flip(ScreenPtr screen);
 
-void
-present_check_flip_window(WindowPtr window);
-
 RRCrtcPtr
 present_get_crtc(WindowPtr window);
 
@@ -330,6 +348,9 @@ present_query_capabilities(RRCrtcPtr crtc);
 Bool
 present_init(void);
 
+void
+present_scmd_init_mode_hooks(present_screen_priv_ptr screen_priv);
+
 /*
  * present_screen.c
  */
diff --git a/present/present_scmd.c b/present/present_scmd.c
index bd92b2760..17a1758d8 100644
--- a/present/present_scmd.c
+++ b/present/present_scmd.c
@@ -68,6 +68,12 @@ msc_is_equal_or_after(uint64_t test, uint64_t reference)
     return (int64_t)(test - reference) >= 0;
 }
 
+static void
+present_scmd_create_event_id(present_vblank_ptr vblank)
+{
+    vblank->event_id = ++present_event_id;
+}
+
 static inline PixmapPtr
 present_flip_pending_pixmap(ScreenPtr screen)
 {
@@ -484,7 +490,7 @@ present_event_notify(uint64_t event_id, uint64_t ust, uint64_t msc)
  * 'window' is being reconfigured. Check to see if it is involved
  * in flipping and clean up as necessary
  */
-void
+static void
 present_check_flip_window (WindowPtr window)
 {
     ScreenPtr                   screen = window->drawable.pScreen;
@@ -858,7 +864,8 @@ present_pixmap(WindowPtr window,
     vblank->screen = screen;
     vblank->window = window;
     vblank->pixmap = pixmap;
-    vblank->event_id = ++present_event_id;
+    present_scmd_create_event_id(vblank);
+
     if (pixmap) {
         vblank->kind = PresentCompleteKindPixmap;
         pixmap->refcnt++;
@@ -1038,6 +1045,15 @@ present_vblank_destroy(present_vblank_ptr vblank)
     free(vblank);
 }
 
+void
+present_scmd_init_mode_hooks(present_screen_priv_ptr screen_priv)
+{
+    screen_priv->check_flip         =   &present_check_flip;
+    screen_priv->check_flip_window  =   &present_check_flip_window;
+
+    screen_priv->create_event_id    =   &present_scmd_create_event_id;
+}
+
 Bool
 present_init(void)
 {
diff --git a/present/present_screen.c b/present/present_screen.c
index 9d03c8aa4..454cc533c 100644
--- a/present/present_screen.c
+++ b/present/present_screen.c
@@ -161,7 +161,7 @@ present_clip_notify(WindowPtr window, int dx, int dy)
     ScreenPtr screen = window->drawable.pScreen;
     present_screen_priv_ptr screen_priv = present_screen_priv(screen);
 
-    present_check_flip_window(window);
+    screen_priv->check_flip_window(window);
     unwrap(screen_priv, screen, ClipNotify)
     if (screen->ClipNotify)
         screen->ClipNotify (window, dx, dy);
@@ -194,6 +194,8 @@ present_screen_init(ScreenPtr screen, present_screen_info_ptr info)
 
         dixSetPrivate(&screen->devPrivates, &present_screen_private_key, screen_priv);
 
+        present_scmd_init_mode_hooks(screen_priv);
+
         present_fake_screen_init(screen);
     }
 
commit dda7efec36b495e100e670e057d7ef5022ef3976
Author: Roman Gilg <subdiff at gmail.com>
Date:   Tue Mar 13 16:00:34 2018 +0100

    present: Move screen flip functionality in separate file
    
    As a preperation for future flip mode alternatives move most of the
    functionality from 'present.c' into a separate file.
    
    Leave some functions needed by future other flip modes in 'present.c'.
    
    Signed-off-by: Roman Gilg <subdiff at gmail.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/present/Makefile.am b/present/Makefile.am
index 7fea6699f..3b458fdbc 100644
--- a/present/Makefile.am
+++ b/present/Makefile.am
@@ -12,6 +12,7 @@ libpresent_la_SOURCES = \
 	present_notify.c \
 	present_priv.h \
 	present_request.c \
+	present_scmd.c \
 	present_screen.c
 
 sdk_HEADERS = present.h presentext.h
diff --git a/present/meson.build b/present/meson.build
index cf725302a..859a99152 100644
--- a/present/meson.build
+++ b/present/meson.build
@@ -5,6 +5,7 @@ srcs_present = [
     'present_fence.c',
     'present_notify.c',
     'present_request.c',
+    'present_scmd.c',
     'present_screen.c',
 ]
 
diff --git a/present/present.c b/present/present.c
index 080cafcba..ec295335c 100644
--- a/present/present.c
+++ b/present/present.c
@@ -26,51 +26,11 @@
 
 #include "present_priv.h"
 #include <gcstruct.h>
-#include <misync.h>
-#include <misyncstr.h>
-#ifdef MONOTONIC_CLOCK
-#include <time.h>
-#endif
-
-static uint64_t         present_event_id;
-static struct xorg_list present_exec_queue;
-static struct xorg_list present_flip_queue;
-
-#if 0
-#define DebugPresent(x) ErrorF x
-#else
-#define DebugPresent(x)
-#endif
-
-static void
-present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc);
-
-/*
- * Returns:
- * TRUE if the first MSC value is after the second one
- * FALSE if the first MSC value is equal to or before the second one
- */
-static Bool
-msc_is_after(uint64_t test, uint64_t reference)
-{
-    return (int64_t)(test - reference) > 0;
-}
-
-/*
- * Returns:
- * TRUE if the first MSC value is equal to or after the second one
- * FALSE if the first MSC value is before the second one
- */
-static Bool
-msc_is_equal_or_after(uint64_t test, uint64_t reference)
-{
-    return (int64_t)(test - reference) >= 0;
-}
 
 /*
  * Copies the update region from a pixmap to the target drawable
  */
-static void
+void
 present_copy_region(DrawablePtr drawable,
                     PixmapPtr pixmap,
                     RegionPtr update,
@@ -103,129 +63,7 @@ present_copy_region(DrawablePtr drawable,
     FreeScratchGC(gc);
 }
 
-static inline PixmapPtr
-present_flip_pending_pixmap(ScreenPtr screen)
-{
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-
-    if (!screen_priv)
-        return NULL;
-
-    if (!screen_priv->flip_pending)
-        return NULL;
-
-    return screen_priv->flip_pending->pixmap;
-}
-
-static Bool
-present_check_flip(RRCrtcPtr            crtc,
-                   WindowPtr            window,
-                   PixmapPtr            pixmap,
-                   Bool                 sync_flip,
-                   RegionPtr            valid,
-                   int16_t              x_off,
-                   int16_t              y_off,
-                   PresentFlipReason   *reason)
-{
-    ScreenPtr                   screen = window->drawable.pScreen;
-    PixmapPtr                   window_pixmap;
-    WindowPtr                   root = screen->root;
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-
-    if (reason)
-        *reason = PRESENT_FLIP_REASON_UNKNOWN;
-
-    if (!screen_priv)
-        return FALSE;
-
-    if (!screen_priv->info)
-        return FALSE;
-
-    if (!crtc)
-        return FALSE;
-
-    /* Check to see if the driver supports flips at all */
-    if (!screen_priv->info->flip)
-        return FALSE;
-
-    /* Make sure the window hasn't been redirected with Composite */
-    window_pixmap = screen->GetWindowPixmap(window);
-    if (window_pixmap != screen->GetScreenPixmap(screen) &&
-        window_pixmap != screen_priv->flip_pixmap &&
-        window_pixmap != present_flip_pending_pixmap(screen))
-        return FALSE;
-
-    /* Check for full-screen window */
-    if (!RegionEqual(&window->clipList, &root->winSize)) {
-        return FALSE;
-    }
-
-    /* Source pixmap must align with window exactly */
-    if (x_off || y_off) {
-        return FALSE;
-    }
-
-    /* Make sure the area marked as valid fills the screen */
-    if (valid && !RegionEqual(valid, &root->winSize)) {
-        return FALSE;
-    }
-
-    /* Does the window match the pixmap exactly? */
-    if (window->drawable.x != 0 || window->drawable.y != 0 ||
-#ifdef COMPOSITE
-        window->drawable.x != pixmap->screen_x || window->drawable.y != pixmap->screen_y ||
-#endif
-        window->drawable.width != pixmap->drawable.width ||
-        window->drawable.height != pixmap->drawable.height) {
-        return FALSE;
-    }
-
-    /* Ask the driver for permission */
-    if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
-        if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, reason)) {
-            DebugPresent(("\td %08lx -> %08lx\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
-            return FALSE;
-        }
-    } else if (screen_priv->info->check_flip) {
-        if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
-            DebugPresent(("\td %08lx -> %08lx\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
-            return FALSE;
-        }
-    }
-
-    return TRUE;
-}
-
-static Bool
-present_flip(RRCrtcPtr crtc,
-             uint64_t event_id,
-             uint64_t target_msc,
-             PixmapPtr pixmap,
-             Bool sync_flip)
-{
-    ScreenPtr                   screen = crtc->pScreen;
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-
-    return (*screen_priv->info->flip) (crtc, event_id, target_msc, pixmap, sync_flip);
-}
-
-static void
-present_vblank_notify(present_vblank_ptr vblank, CARD8 kind, CARD8 mode, uint64_t ust, uint64_t crtc_msc)
-{
-    int         n;
-
-    if (vblank->window)
-        present_send_complete_notify(vblank->window, kind, mode, vblank->serial, ust, crtc_msc - vblank->msc_offset);
-    for (n = 0; n < vblank->num_notifies; n++) {
-        WindowPtr   window = vblank->notifies[n].window;
-        CARD32      serial = vblank->notifies[n].serial;
-
-        if (window)
-            present_send_complete_notify(window, kind, mode, serial, ust, crtc_msc - vblank->msc_offset);
-    }
-}
-
-static void
+void
 present_pixmap_idle(PixmapPtr pixmap, WindowPtr window, CARD32 serial, struct present_fence *present_fence)
 {
     if (present_fence)
@@ -236,157 +74,6 @@ present_pixmap_idle(PixmapPtr pixmap, WindowPtr window, CARD32 serial, struct pr
     }
 }
 
-RRCrtcPtr
-present_get_crtc(WindowPtr window)
-{
-    ScreenPtr                   screen = window->drawable.pScreen;
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-
-    if (!screen_priv)
-        return NULL;
-
-    if (!screen_priv->info)
-        return NULL;
-
-    return (*screen_priv->info->get_crtc)(window);
-}
-
-uint32_t
-present_query_capabilities(RRCrtcPtr crtc)
-{
-    present_screen_priv_ptr     screen_priv;
-
-    if (!crtc)
-        return 0;
-
-    screen_priv = present_screen_priv(crtc->pScreen);
-
-    if (!screen_priv)
-        return 0;
-
-    if (!screen_priv->info)
-        return 0;
-
-    return screen_priv->info->capabilities;
-}
-
-static int
-present_get_ust_msc(ScreenPtr screen, RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc)
-{
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-
-    if (crtc == NULL)
-        return present_fake_get_ust_msc(screen, ust, msc);
-    else
-        return (*screen_priv->info->get_ust_msc)(crtc, ust, msc);
-}
-
-static void
-present_flush(WindowPtr window)
-{
-    ScreenPtr                   screen = window->drawable.pScreen;
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-
-    if (!screen_priv)
-        return;
-
-    if (!screen_priv->info)
-        return;
-
-    (*screen_priv->info->flush) (window);
-}
-
-static int
-present_queue_vblank(ScreenPtr screen,
-                     RRCrtcPtr crtc,
-                     uint64_t event_id,
-                     uint64_t msc)
-{
-    Bool                        ret;
-
-    if (crtc == NULL)
-        ret = present_fake_queue_vblank(screen, event_id, msc);
-    else
-    {
-        present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-        ret = (*screen_priv->info->queue_vblank) (crtc, event_id, msc);
-    }
-    return ret;
-}
-
-static uint64_t
-present_window_to_crtc_msc(WindowPtr window, RRCrtcPtr crtc, uint64_t window_msc, uint64_t new_msc)
-{
-    present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
-
-    if (crtc != window_priv->crtc) {
-        uint64_t        old_ust, old_msc;
-
-        if (window_priv->crtc == PresentCrtcNeverSet) {
-            window_priv->msc_offset = 0;
-        } else {
-            /* The old CRTC may have been turned off, in which case
-             * we'll just use whatever previous MSC we'd seen from this CRTC
-             */
-
-            if (present_get_ust_msc(window->drawable.pScreen, window_priv->crtc, &old_ust, &old_msc) != Success)
-                old_msc = window_priv->msc;
-
-            window_priv->msc_offset += new_msc - old_msc;
-        }
-        window_priv->crtc = crtc;
-    }
-
-    return window_msc + window_priv->msc_offset;
-}
-
-/*
- * When the wait fence or previous flip is completed, it's time
- * to re-try the request
- */
-static void
-present_re_execute(present_vblank_ptr vblank)
-{
-    uint64_t            ust = 0, crtc_msc = 0;
-
-    if (vblank->crtc)
-        (void) present_get_ust_msc(vblank->screen, vblank->crtc, &ust, &crtc_msc);
-
-    present_execute(vblank, ust, crtc_msc);
-}
-
-static void
-present_flip_try_ready(ScreenPtr screen)
-{
-    present_vblank_ptr  vblank;
-
-    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
-        if (vblank->queued) {
-            present_re_execute(vblank);
-            return;
-        }
-    }
-}
-
-static void
-present_flip_idle(ScreenPtr screen)
-{
-    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
-
-    if (screen_priv->flip_pixmap) {
-        present_pixmap_idle(screen_priv->flip_pixmap, screen_priv->flip_window,
-                            screen_priv->flip_serial, screen_priv->flip_idle_fence);
-        if (screen_priv->flip_idle_fence)
-            present_fence_destroy(screen_priv->flip_idle_fence);
-        dixDestroyPixmap(screen_priv->flip_pixmap, screen_priv->flip_pixmap->drawable.id);
-        screen_priv->flip_crtc = NULL;
-        screen_priv->flip_window = NULL;
-        screen_priv->flip_serial = 0;
-        screen_priv->flip_pixmap = NULL;
-        screen_priv->flip_idle_fence = NULL;
-    }
-}
-
 struct pixmap_visit {
     PixmapPtr   old;
     PixmapPtr   new;
@@ -404,7 +91,7 @@ present_set_tree_pixmap_visit(WindowPtr window, void *data)
     return WT_WALKCHILDREN;
 }
 
-static void
+void
 present_set_tree_pixmap(WindowPtr window,
                         PixmapPtr expected,
                         PixmapPtr pixmap)
@@ -421,709 +108,3 @@ present_set_tree_pixmap(WindowPtr window,
         return;
     TraverseTree(window, present_set_tree_pixmap_visit, &visit);
 }
-
-void
-present_restore_screen_pixmap(ScreenPtr screen)
-{
-    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
-    PixmapPtr screen_pixmap = (*screen->GetScreenPixmap)(screen);
-    PixmapPtr flip_pixmap;
-    WindowPtr flip_window;
-
-    if (screen_priv->flip_pending) {
-        flip_window = screen_priv->flip_pending->window;
-        flip_pixmap = screen_priv->flip_pending->pixmap;
-    } else {
-        flip_window = screen_priv->flip_window;
-        flip_pixmap = screen_priv->flip_pixmap;
-    }
-
-    assert (flip_pixmap);
-
-    /* Update the screen pixmap with the current flip pixmap contents
-     * Only do this the first time for a particular unflip operation, or
-     * we'll probably scribble over other windows
-     */
-    if (screen->root && screen->GetWindowPixmap(screen->root) == flip_pixmap)
-        present_copy_region(&screen_pixmap->drawable, flip_pixmap, NULL, 0, 0);
-
-    /* Switch back to using the screen pixmap now to avoid
-     * 2D applications drawing to the wrong pixmap.
-     */
-    if (flip_window)
-        present_set_tree_pixmap(flip_window, flip_pixmap, screen_pixmap);
-    if (screen->root)
-        present_set_tree_pixmap(screen->root, NULL, screen_pixmap);
-}
-
-void
-present_set_abort_flip(ScreenPtr screen)
-{
-    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
-
-    if (!screen_priv->flip_pending->abort_flip) {
-        present_restore_screen_pixmap(screen);
-        screen_priv->flip_pending->abort_flip = TRUE;
-    }
-}
-
-static void
-present_unflip(ScreenPtr screen)
-{
-    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
-
-    assert (!screen_priv->unflip_event_id);
-    assert (!screen_priv->flip_pending);
-
-    present_restore_screen_pixmap(screen);
-
-    screen_priv->unflip_event_id = ++present_event_id;
-    DebugPresent(("u %lld\n", screen_priv->unflip_event_id));
-    (*screen_priv->info->unflip) (screen, screen_priv->unflip_event_id);
-}
-
-static void
-present_flip_notify(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
-{
-    ScreenPtr                   screen = vblank->screen;
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-
-    DebugPresent(("\tn %lld %p %8lld: %08lx -> %08lx\n",
-                  vblank->event_id, vblank, vblank->target_msc,
-                  vblank->pixmap ? vblank->pixmap->drawable.id : 0,
-                  vblank->window ? vblank->window->drawable.id : 0));
-
-    assert (vblank == screen_priv->flip_pending);
-
-    present_flip_idle(screen);
-
-    xorg_list_del(&vblank->event_queue);
-
-    /* Transfer reference for pixmap and fence from vblank to screen_priv */
-    screen_priv->flip_crtc = vblank->crtc;
-    screen_priv->flip_window = vblank->window;
-    screen_priv->flip_serial = vblank->serial;
-    screen_priv->flip_pixmap = vblank->pixmap;
-    screen_priv->flip_sync = vblank->sync_flip;
-    screen_priv->flip_idle_fence = vblank->idle_fence;
-
-    vblank->pixmap = NULL;
-    vblank->idle_fence = NULL;
-
-    screen_priv->flip_pending = NULL;
-
-    if (vblank->abort_flip)
-        present_unflip(screen);
-
-    present_vblank_notify(vblank, PresentCompleteKindPixmap, PresentCompleteModeFlip, ust, crtc_msc);
-    present_vblank_destroy(vblank);
-
-    present_flip_try_ready(screen);
-}
-
-void
-present_event_notify(uint64_t event_id, uint64_t ust, uint64_t msc)
-{
-    present_vblank_ptr  vblank;
-    int                 s;
-
-    if (!event_id)
-        return;
-    DebugPresent(("\te %lld ust %lld msc %lld\n", event_id, ust, msc));
-    xorg_list_for_each_entry(vblank, &present_exec_queue, event_queue) {
-        int64_t match = event_id - vblank->event_id;
-        if (match == 0) {
-            present_execute(vblank, ust, msc);
-            return;
-        }
-    }
-    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
-        if (vblank->event_id == event_id) {
-            if (vblank->queued)
-                present_execute(vblank, ust, msc);
-            else
-                present_flip_notify(vblank, ust, msc);
-            return;
-        }
-    }
-
-    for (s = 0; s < screenInfo.numScreens; s++) {
-        ScreenPtr               screen = screenInfo.screens[s];
-        present_screen_priv_ptr screen_priv = present_screen_priv(screen);
-
-        if (event_id == screen_priv->unflip_event_id) {
-            DebugPresent(("\tun %lld\n", event_id));
-            screen_priv->unflip_event_id = 0;
-            present_flip_idle(screen);
-            present_flip_try_ready(screen);
-            return;
-        }
-    }
-}
-
-/*
- * 'window' is being reconfigured. Check to see if it is involved
- * in flipping and clean up as necessary
- */
-void
-present_check_flip_window (WindowPtr window)
-{
-    ScreenPtr                   screen = window->drawable.pScreen;
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-    present_window_priv_ptr     window_priv = present_window_priv(window);
-    present_vblank_ptr          flip_pending = screen_priv->flip_pending;
-    present_vblank_ptr          vblank;
-    PresentFlipReason           reason;
-
-    /* If this window hasn't ever been used with Present, it can't be
-     * flipping
-     */
-    if (!window_priv)
-        return;
-
-    if (screen_priv->unflip_event_id)
-        return;
-
-    if (flip_pending) {
-        /*
-         * Check pending flip
-         */
-        if (flip_pending->window == window) {
-            if (!present_check_flip(flip_pending->crtc, window, flip_pending->pixmap,
-                                    flip_pending->sync_flip, NULL, 0, 0, NULL))
-                present_set_abort_flip(screen);
-        }
-    } else {
-        /*
-         * Check current flip
-         */
-        if (window == screen_priv->flip_window) {
-            if (!present_check_flip(screen_priv->flip_crtc, window, screen_priv->flip_pixmap, screen_priv->flip_sync, NULL, 0, 0, NULL))
-                present_unflip(screen);
-        }
-    }
-
-    /* Now check any queued vblanks */
-    xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
-        if (vblank->queued && vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, vblank->sync_flip, NULL, 0, 0, &reason)) {
-            vblank->flip = FALSE;
-            vblank->reason = reason;
-            if (vblank->sync_flip)
-                vblank->requeue = TRUE;
-        }
-    }
-}
-
-Bool
-present_can_window_flip(WindowPtr window)
-{
-    ScreenPtr                   screen = window->drawable.pScreen;
-    PixmapPtr                   window_pixmap;
-    WindowPtr                   root = screen->root;
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-
-    if (!screen_priv)
-        return FALSE;
-
-    if (!screen_priv->info)
-        return FALSE;
-
-    /* Check to see if the driver supports flips at all */
-    if (!screen_priv->info->flip)
-        return FALSE;
-
-    /* Make sure the window hasn't been redirected with Composite */
-    window_pixmap = screen->GetWindowPixmap(window);
-    if (window_pixmap != screen->GetScreenPixmap(screen) &&
-        window_pixmap != screen_priv->flip_pixmap &&
-        window_pixmap != present_flip_pending_pixmap(screen))
-        return FALSE;
-
-    /* Check for full-screen window */
-    if (!RegionEqual(&window->clipList, &root->winSize)) {
-        return FALSE;
-    }
-
-    /* Does the window match the pixmap exactly? */
-    if (window->drawable.x != 0 || window->drawable.y != 0) {
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-/*
- * Called when the wait fence is triggered; just gets the current msc/ust and
- * calls present_execute again. That will re-check the fence and pend the
- * request again if it's still not actually ready
- */
-static void
-present_wait_fence_triggered(void *param)
-{
-    present_vblank_ptr  vblank = param;
-    present_re_execute(vblank);
-}
-
-/*
- * Once the required MSC has been reached, execute the pending request.
- *
- * For requests to actually present something, either blt contents to
- * the screen or queue a frame buffer swap.
- *
- * For requests to just get the current MSC/UST combo, skip that part and
- * go straight to event delivery
- */
-
-static void
-present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
-{
-    WindowPtr                   window = vblank->window;
-    ScreenPtr                   screen = window->drawable.pScreen;
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-    uint8_t                     mode;
-
-    if (vblank->requeue) {
-        vblank->requeue = FALSE;
-        if (msc_is_after(vblank->target_msc, crtc_msc) &&
-            Success == present_queue_vblank(screen,
-                                            vblank->crtc,
-                                            vblank->event_id,
-                                            vblank->target_msc))
-            return;
-    }
-
-    if (vblank->wait_fence) {
-        if (!present_fence_check_triggered(vblank->wait_fence)) {
-            present_fence_set_callback(vblank->wait_fence, present_wait_fence_triggered, vblank);
-            return;
-        }
-    }
-
-    if (vblank->flip && vblank->pixmap && vblank->window) {
-        if (screen_priv->flip_pending || screen_priv->unflip_event_id) {
-            DebugPresent(("\tr %lld %p (pending %p unflip %lld)\n",
-                          vblank->event_id, vblank,
-                          screen_priv->flip_pending, screen_priv->unflip_event_id));
-            xorg_list_del(&vblank->event_queue);
-            xorg_list_append(&vblank->event_queue, &present_flip_queue);
-            vblank->flip_ready = TRUE;
-            return;
-        }
-    }
-
-    xorg_list_del(&vblank->event_queue);
-    xorg_list_del(&vblank->window_list);
-    vblank->queued = FALSE;
-
-    if (vblank->pixmap && vblank->window) {
-
-        if (vblank->flip) {
-
-            DebugPresent(("\tf %lld %p %8lld: %08lx -> %08lx\n",
-                          vblank->event_id, vblank, crtc_msc,
-                          vblank->pixmap->drawable.id, vblank->window->drawable.id));
-
-            /* Prepare to flip by placing it in the flip queue and
-             * and sticking it into the flip_pending field
-             */
-            screen_priv->flip_pending = vblank;
-
-            xorg_list_add(&vblank->event_queue, &present_flip_queue);
-            /* Try to flip
-             */
-            if (present_flip(vblank->crtc, vblank->event_id, vblank->target_msc, vblank->pixmap, vblank->sync_flip)) {
-                RegionPtr damage;
-
-                /* Fix window pixmaps:
-                 *  1) Restore previous flip window pixmap
-                 *  2) Set current flip window pixmap to the new pixmap
-                 */
-                if (screen_priv->flip_window && screen_priv->flip_window != window)
-                    present_set_tree_pixmap(screen_priv->flip_window,
-                                            screen_priv->flip_pixmap,
-                                            (*screen->GetScreenPixmap)(screen));
-                present_set_tree_pixmap(vblank->window, NULL, vblank->pixmap);
-                present_set_tree_pixmap(screen->root, NULL, vblank->pixmap);
-
-                /* Report update region as damaged
-                 */
-                if (vblank->update) {
-                    damage = vblank->update;
-                    RegionIntersect(damage, damage, &window->clipList);
-                } else
-                    damage = &window->clipList;
-
-                DamageDamageRegion(&vblank->window->drawable, damage);
-                return;
-            }
-
-            xorg_list_del(&vblank->event_queue);
-            /* Oops, flip failed. Clear the flip_pending field
-              */
-            screen_priv->flip_pending = NULL;
-            vblank->flip = FALSE;
-        }
-        DebugPresent(("\tc %p %8lld: %08lx -> %08lx\n", vblank, crtc_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id));
-        if (screen_priv->flip_pending) {
-
-            /* Check pending flip
-             */
-            if (window == screen_priv->flip_pending->window)
-                present_set_abort_flip(screen);
-        } else if (!screen_priv->unflip_event_id) {
-
-            /* Check current flip
-             */
-            if (window == screen_priv->flip_window)
-                present_unflip(screen);
-        }
-
-        /* If present_flip failed, we may have to requeue for the target MSC */
-        if (vblank->target_msc == crtc_msc + 1 &&
-            Success == present_queue_vblank(screen,
-                                            vblank->crtc,
-                                            vblank->event_id,
-                                            vblank->target_msc)) {
-            xorg_list_add(&vblank->event_queue, &present_exec_queue);
-            xorg_list_append(&vblank->window_list,
-                             &present_get_window_priv(window, TRUE)->vblank);
-            vblank->queued = TRUE;
-            return;
-        }
-
-        present_copy_region(&window->drawable, vblank->pixmap, vblank->update, vblank->x_off, vblank->y_off);
-
-        /* present_copy_region sticks the region into a scratch GC,
-         * which is then freed, freeing the region
-         */
-        vblank->update = NULL;
-        present_flush(window);
-
-        present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
-    }
-
-    /* Compute correct CompleteMode
-     */
-    if (vblank->kind == PresentCompleteKindPixmap) {
-        if (vblank->pixmap && vblank->window) {
-            if (vblank->has_suboptimal && vblank->reason == PRESENT_FLIP_REASON_BUFFER_FORMAT)
-                mode = PresentCompleteModeSuboptimalCopy;
-            else
-                mode = PresentCompleteModeCopy;
-        } else {
-            mode = PresentCompleteModeSkip;
-        }
-    }
-    else
-        mode = PresentCompleteModeCopy;
-
-
-    present_vblank_notify(vblank, vblank->kind, mode, ust, crtc_msc);
-    present_vblank_destroy(vblank);
-}
-
-int
-present_pixmap(WindowPtr window,
-               PixmapPtr pixmap,
-               CARD32 serial,
-               RegionPtr valid,
-               RegionPtr update,
-               int16_t x_off,
-               int16_t y_off,
-               RRCrtcPtr target_crtc,
-               SyncFence *wait_fence,
-               SyncFence *idle_fence,
-               uint32_t options,
-               uint64_t window_msc,
-               uint64_t divisor,
-               uint64_t remainder,
-               present_notify_ptr notifies,
-               int num_notifies)
-{
-    uint64_t                    ust = 0;
-    uint64_t                    target_msc;
-    uint64_t                    crtc_msc = 0;
-    int                         ret;
-    present_vblank_ptr          vblank, tmp;
-    ScreenPtr                   screen = window->drawable.pScreen;
-    present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-    PresentFlipReason           reason = PRESENT_FLIP_REASON_UNKNOWN;
-
-    if (!window_priv)
-        return BadAlloc;
-
-    if (!screen_priv || !screen_priv->info)
-        target_crtc = NULL;
-    else if (!target_crtc) {
-        /* Update the CRTC if we have a pixmap or we don't have a CRTC
-         */
-        if (!pixmap)
-            target_crtc = window_priv->crtc;
-
-        if (!target_crtc || target_crtc == PresentCrtcNeverSet)
-            target_crtc = present_get_crtc(window);
-    }
-
-    ret = present_get_ust_msc(screen, target_crtc, &ust, &crtc_msc);
-
-    target_msc = present_window_to_crtc_msc(window, target_crtc, window_msc, crtc_msc);
-
-    if (ret == Success) {
-        /* Stash the current MSC away in case we need it later
-         */
-        window_priv->msc = crtc_msc;
-    }
-
-    /* Adjust target_msc to match modulus
-     */
-    if (msc_is_equal_or_after(crtc_msc, target_msc)) {
-        if (divisor != 0) {
-            target_msc = crtc_msc - (crtc_msc % divisor) + remainder;
-            if (options & PresentOptionAsync) {
-                if (msc_is_after(crtc_msc, target_msc))
-                    target_msc += divisor;
-            } else {
-                if (msc_is_equal_or_after(crtc_msc, target_msc))
-                    target_msc += divisor;
-            }
-        } else {
-            target_msc = crtc_msc;
-            if (!(options & PresentOptionAsync))
-                target_msc++;
-        }
-    }
-
-    /*
-     * Look for a matching presentation already on the list and
-     * don't bother doing the previous one if this one will overwrite it
-     * in the same frame
-     */
-
-    if (!update && pixmap) {
-        xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->vblank, window_list) {
-
-            if (!vblank->pixmap)
-                continue;
-
-            if (!vblank->queued)
-                continue;
-
-            if (vblank->crtc != target_crtc || vblank->target_msc != target_msc)
-                continue;
-
-            DebugPresent(("\tx %lld %p %8lld: %08lx -> %08lx (crtc %p)\n",
-                          vblank->event_id, vblank, vblank->target_msc,
-                          vblank->pixmap->drawable.id, vblank->window->drawable.id,
-                          vblank->crtc));
-
-            present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
-            present_fence_destroy(vblank->idle_fence);
-            dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
-
-            vblank->pixmap = NULL;
-            vblank->idle_fence = NULL;
-            vblank->flip = FALSE;
-            if (vblank->flip_ready)
-                present_re_execute(vblank);
-        }
-    }
-
-    vblank = calloc (1, sizeof (present_vblank_rec));
-    if (!vblank)
-        return BadAlloc;
-
-    xorg_list_append(&vblank->window_list, &window_priv->vblank);
-    xorg_list_init(&vblank->event_queue);
-
-    vblank->screen = screen;
-    vblank->window = window;
-    vblank->pixmap = pixmap;
-    vblank->event_id = ++present_event_id;
-    if (pixmap) {
-        vblank->kind = PresentCompleteKindPixmap;
-        pixmap->refcnt++;
-    } else
-        vblank->kind = PresentCompleteKindNotifyMSC;
-
-    vblank->serial = serial;
-
-    if (valid) {
-        vblank->valid = RegionDuplicate(valid);
-        if (!vblank->valid)
-            goto no_mem;
-    }
-    if (update) {
-        vblank->update = RegionDuplicate(update);
-        if (!vblank->update)
-            goto no_mem;
-    }
-
-    vblank->x_off = x_off;
-    vblank->y_off = y_off;
-    vblank->target_msc = target_msc;
-    vblank->crtc = target_crtc;
-    vblank->msc_offset = window_priv->msc_offset;
-    vblank->notifies = notifies;
-    vblank->num_notifies = num_notifies;
-    vblank->has_suboptimal = (options & PresentOptionSuboptimal);
-
-    if (pixmap != NULL &&
-        !(options & PresentOptionCopy) &&
-        screen_priv->info) {
-        if (msc_is_after(target_msc, crtc_msc) &&
-            present_check_flip (target_crtc, window, pixmap, TRUE, valid, x_off, y_off, &reason))
-        {
-            vblank->flip = TRUE;
-            vblank->sync_flip = TRUE;
-            target_msc--;
-        } else if ((screen_priv->info->capabilities & PresentCapabilityAsync) &&
-            present_check_flip (target_crtc, window, pixmap, FALSE, valid, x_off, y_off, &reason))
-        {
-            vblank->flip = TRUE;
-        }
-    }
-    vblank->reason = reason;
-
-    if (wait_fence) {
-        vblank->wait_fence = present_fence_create(wait_fence);
-        if (!vblank->wait_fence)
-            goto no_mem;
-    }
-
-    if (idle_fence) {
-        vblank->idle_fence = present_fence_create(idle_fence);
-        if (!vblank->idle_fence)
-            goto no_mem;
-    }
-
-    if (pixmap)
-        DebugPresent(("q %lld %p %8lld: %08lx -> %08lx (crtc %p) flip %d vsync %d serial %d\n",
-                      vblank->event_id, vblank, target_msc,
-                      vblank->pixmap->drawable.id, vblank->window->drawable.id,
-                      target_crtc, vblank->flip, vblank->sync_flip, vblank->serial));
-
-    xorg_list_append(&vblank->event_queue, &present_exec_queue);
-    vblank->queued = TRUE;
-    if (msc_is_after(target_msc, crtc_msc)) {
-        ret = present_queue_vblank(screen, target_crtc, vblank->event_id, target_msc);
-        if (ret == Success)
-            return Success;
-
-        DebugPresent(("present_queue_vblank failed\n"));
-    }
-
-    present_execute(vblank, ust, crtc_msc);
-
-    return Success;
-
-no_mem:
-    ret = BadAlloc;
-    vblank->notifies = NULL;
-    present_vblank_destroy(vblank);
-    return ret;
-}
-
-void
-present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
-{
-    present_vblank_ptr  vblank;
-
-    if (crtc == NULL)
-        present_fake_abort_vblank(screen, event_id, msc);
-    else
-    {
-        present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-
-        (*screen_priv->info->abort_vblank) (crtc, event_id, msc);
-    }
-
-    xorg_list_for_each_entry(vblank, &present_exec_queue, event_queue) {
-        int64_t match = event_id - vblank->event_id;
-        if (match == 0) {
-            xorg_list_del(&vblank->event_queue);
-            vblank->queued = FALSE;
-            return;
-        }
-    }
-    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
-        if (vblank->event_id == event_id) {
-            xorg_list_del(&vblank->event_queue);
-            vblank->queued = FALSE;
-            return;
-        }
-    }
-}
-
-int
-present_notify_msc(WindowPtr window,
-                   CARD32 serial,
-                   uint64_t target_msc,
-                   uint64_t divisor,
-                   uint64_t remainder)
-{
-    return present_pixmap(window,
-                          NULL,
-                          serial,
-                          NULL, NULL,
-                          0, 0,
-                          NULL,
-                          NULL, NULL,
-                          divisor == 0 ? PresentOptionAsync : 0,
-                          target_msc, divisor, remainder, NULL, 0);
-}
-
-void
-present_flip_destroy(ScreenPtr screen)
-{
-    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
-
-    /* Reset window pixmaps back to the screen pixmap */
-    if (screen_priv->flip_pending)
-        present_set_abort_flip(screen);
-
-    /* Drop reference to any pending flip or unflip pixmaps. */
-    present_flip_idle(screen);
-}
-
-void
-present_vblank_destroy(present_vblank_ptr vblank)
-{
-    /* Remove vblank from window and screen lists */
-    xorg_list_del(&vblank->window_list);
-
-    DebugPresent(("\td %lld %p %8lld: %08lx -> %08lx\n",
-                  vblank->event_id, vblank, vblank->target_msc,
-                  vblank->pixmap ? vblank->pixmap->drawable.id : 0,
-                  vblank->window ? vblank->window->drawable.id : 0));
-
-    /* Drop pixmap reference */
-    if (vblank->pixmap)
-        dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
-
-    /* Free regions */
-    if (vblank->valid)
-        RegionDestroy(vblank->valid);
-    if (vblank->update)
-        RegionDestroy(vblank->update);
-
-    if (vblank->wait_fence)
-        present_fence_destroy(vblank->wait_fence);
-
-    if (vblank->idle_fence)
-        present_fence_destroy(vblank->idle_fence);
-
-    if (vblank->notifies)
-        present_destroy_notifies(vblank->notifies, vblank->num_notifies);
-
-    free(vblank);
-}
-
-Bool
-present_init(void)
-{
-    xorg_list_init(&present_exec_queue);
-    xorg_list_init(&present_flip_queue);
-    present_fake_queue_init();
-    return TRUE;
-}
diff --git a/present/present_priv.h b/present/present_priv.h
index 6980ddd11..66539e183 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -35,6 +35,12 @@
 #include <xfixes.h>
 #include <randrstr.h>
 
+#if 0
+#define DebugPresent(x) ErrorF x
+#else
+#define DebugPresent(x)
+#endif
+
 extern int present_request;
 
 extern DevPrivateKeyRec present_screen_private_key;
@@ -154,57 +160,20 @@ present_get_window_priv(WindowPtr window, Bool create);
 /*
  * present.c
  */
-int
-present_pixmap(WindowPtr window,
-               PixmapPtr pixmap,
-               CARD32 serial,
-               RegionPtr valid,
-               RegionPtr update,
-               int16_t x_off,
-               int16_t y_off,
-               RRCrtcPtr target_crtc,
-               SyncFence *wait_fence,
-               SyncFence *idle_fence,
-               uint32_t options,
-               uint64_t target_msc,
-               uint64_t divisor,
-               uint64_t remainder,
-               present_notify_ptr notifies,
-               int num_notifies);
-
-int
-present_notify_msc(WindowPtr window,
-                   CARD32 serial,
-                   uint64_t target_msc,
-                   uint64_t divisor,
-                   uint64_t remainder);
-
 void
-present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc);
+present_copy_region(DrawablePtr drawable,
+                    PixmapPtr pixmap,
+                    RegionPtr update,
+                    int16_t x_off,
+                    int16_t y_off);
 
 void
-present_vblank_destroy(present_vblank_ptr vblank);
+present_pixmap_idle(PixmapPtr pixmap, WindowPtr window, CARD32 serial, struct present_fence *present_fence);
 
 void
-present_flip_destroy(ScreenPtr screen);
-
-void
-present_restore_screen_pixmap(ScreenPtr screen);
-
-void
-present_set_abort_flip(ScreenPtr screen);
-
-void
-present_check_flip_window(WindowPtr window);
-
-RRCrtcPtr
-present_get_crtc(WindowPtr window);
-
-uint32_t
-present_query_capabilities(RRCrtcPtr crtc);
-
-Bool
-present_init(void);
+present_set_tree_pixmap(WindowPtr window,
+                        PixmapPtr expected,
+                        PixmapPtr pixmap);
 
 /*
  * present_event.c
@@ -307,6 +276,61 @@ int
 sproc_present_dispatch(ClientPtr client);
 
 /*
+ * present_scmd.c
+ */
+int
+present_pixmap(WindowPtr window,
+               PixmapPtr pixmap,
+               CARD32 serial,
+               RegionPtr valid,
+               RegionPtr update,
+               int16_t x_off,
+               int16_t y_off,
+               RRCrtcPtr target_crtc,
+               SyncFence *wait_fence,
+               SyncFence *idle_fence,
+               uint32_t options,
+               uint64_t target_msc,
+               uint64_t divisor,
+               uint64_t remainder,
+               present_notify_ptr notifies,
+               int num_notifies);
+
+int
+present_notify_msc(WindowPtr window,
+                   CARD32 serial,
+                   uint64_t target_msc,
+                   uint64_t divisor,
+                   uint64_t remainder);
+
+void
+present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc);
+
+void
+present_vblank_destroy(present_vblank_ptr vblank);
+
+void
+present_flip_destroy(ScreenPtr screen);
+
+void
+present_restore_screen_pixmap(ScreenPtr screen);
+
+void
+present_set_abort_flip(ScreenPtr screen);
+
+void
+present_check_flip_window(WindowPtr window);
+
+RRCrtcPtr
+present_get_crtc(WindowPtr window);
+
+uint32_t
+present_query_capabilities(RRCrtcPtr crtc);
+
+Bool
+present_init(void);
+
+/*
  * present_screen.c
  */
 
diff --git a/present/present_scmd.c b/present/present_scmd.c
new file mode 100644
index 000000000..bd92b2760
--- /dev/null
+++ b/present/present_scmd.c
@@ -0,0 +1,1048 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include "present_priv.h"
+#include <misync.h>
+#include <misyncstr.h>
+#ifdef MONOTONIC_CLOCK
+#include <time.h>
+#endif
+
+/*
+ * Screen flip mode
+ *
+ * Provides the default mode for drivers, that do not
+ * support flips and the full screen flip mode.
+ *
+ */
+
+static uint64_t         present_event_id;
+static struct xorg_list present_exec_queue;
+static struct xorg_list present_flip_queue;
+
+static void
+present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc);
+
+/*
+ * Returns:
+ * TRUE if the first MSC value is after the second one
+ * FALSE if the first MSC value is equal to or before the second one
+ */
+static Bool
+msc_is_after(uint64_t test, uint64_t reference)
+{
+    return (int64_t)(test - reference) > 0;
+}
+
+/*
+ * Returns:
+ * TRUE if the first MSC value is equal to or after the second one
+ * FALSE if the first MSC value is before the second one
+ */
+static Bool
+msc_is_equal_or_after(uint64_t test, uint64_t reference)
+{
+    return (int64_t)(test - reference) >= 0;
+}
+
+static inline PixmapPtr
+present_flip_pending_pixmap(ScreenPtr screen)
+{
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (!screen_priv)
+        return NULL;
+
+    if (!screen_priv->flip_pending)
+        return NULL;
+
+    return screen_priv->flip_pending->pixmap;
+}
+
+static Bool
+present_check_flip(RRCrtcPtr            crtc,
+                   WindowPtr            window,
+                   PixmapPtr            pixmap,
+                   Bool                 sync_flip,
+                   RegionPtr            valid,
+                   int16_t              x_off,
+                   int16_t              y_off,
+                   PresentFlipReason   *reason)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    PixmapPtr                   window_pixmap;
+    WindowPtr                   root = screen->root;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (reason)
+        *reason = PRESENT_FLIP_REASON_UNKNOWN;
+
+    if (!screen_priv)
+        return FALSE;
+
+    if (!screen_priv->info)
+        return FALSE;
+
+    if (!crtc)
+        return FALSE;
+
+    /* Check to see if the driver supports flips at all */
+    if (!screen_priv->info->flip)
+        return FALSE;
+
+    /* Make sure the window hasn't been redirected with Composite */
+    window_pixmap = screen->GetWindowPixmap(window);
+    if (window_pixmap != screen->GetScreenPixmap(screen) &&
+        window_pixmap != screen_priv->flip_pixmap &&
+        window_pixmap != present_flip_pending_pixmap(screen))
+        return FALSE;
+
+    /* Check for full-screen window */
+    if (!RegionEqual(&window->clipList, &root->winSize)) {
+        return FALSE;
+    }
+
+    /* Source pixmap must align with window exactly */
+    if (x_off || y_off) {
+        return FALSE;
+    }
+
+    /* Make sure the area marked as valid fills the screen */
+    if (valid && !RegionEqual(valid, &root->winSize)) {
+        return FALSE;
+    }
+
+    /* Does the window match the pixmap exactly? */
+    if (window->drawable.x != 0 || window->drawable.y != 0 ||
+#ifdef COMPOSITE
+        window->drawable.x != pixmap->screen_x || window->drawable.y != pixmap->screen_y ||
+#endif
+        window->drawable.width != pixmap->drawable.width ||
+        window->drawable.height != pixmap->drawable.height) {
+        return FALSE;
+    }
+
+    /* Ask the driver for permission */
+    if (screen_priv->info->version >= 1 && screen_priv->info->check_flip2) {
+        if (!(*screen_priv->info->check_flip2) (crtc, window, pixmap, sync_flip, reason)) {
+            DebugPresent(("\td %08lx -> %08lx\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
+            return FALSE;
+        }
+    } else if (screen_priv->info->check_flip) {
+        if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
+            DebugPresent(("\td %08lx -> %08lx\n", window->drawable.id, pixmap ? pixmap->drawable.id : 0));
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static Bool
+present_flip(RRCrtcPtr crtc,
+             uint64_t event_id,
+             uint64_t target_msc,
+             PixmapPtr pixmap,
+             Bool sync_flip)
+{
+    ScreenPtr                   screen = crtc->pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    return (*screen_priv->info->flip) (crtc, event_id, target_msc, pixmap, sync_flip);
+}
+
+static void
+present_vblank_notify(present_vblank_ptr vblank, CARD8 kind, CARD8 mode, uint64_t ust, uint64_t crtc_msc)
+{
+    int         n;
+
+    if (vblank->window)
+        present_send_complete_notify(vblank->window, kind, mode, vblank->serial, ust, crtc_msc - vblank->msc_offset);
+    for (n = 0; n < vblank->num_notifies; n++) {
+        WindowPtr   window = vblank->notifies[n].window;
+        CARD32      serial = vblank->notifies[n].serial;
+
+        if (window)
+            present_send_complete_notify(window, kind, mode, serial, ust, crtc_msc - vblank->msc_offset);
+    }
+}
+
+RRCrtcPtr
+present_get_crtc(WindowPtr window)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (!screen_priv)
+        return NULL;
+
+    if (!screen_priv->info)
+        return NULL;
+
+    return (*screen_priv->info->get_crtc)(window);
+}
+
+uint32_t
+present_query_capabilities(RRCrtcPtr crtc)
+{
+    present_screen_priv_ptr     screen_priv;
+
+    if (!crtc)
+        return 0;
+
+    screen_priv = present_screen_priv(crtc->pScreen);
+
+    if (!screen_priv)
+        return 0;
+
+    if (!screen_priv->info)
+        return 0;
+
+    return screen_priv->info->capabilities;
+}
+
+static int
+present_get_ust_msc(ScreenPtr screen, RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc)
+{
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (crtc == NULL)
+        return present_fake_get_ust_msc(screen, ust, msc);
+    else
+        return (*screen_priv->info->get_ust_msc)(crtc, ust, msc);
+}
+
+static void
+present_flush(WindowPtr window)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (!screen_priv)
+        return;
+
+    if (!screen_priv->info)
+        return;
+
+    (*screen_priv->info->flush) (window);
+}
+
+static int
+present_queue_vblank(ScreenPtr screen,
+                     RRCrtcPtr crtc,
+                     uint64_t event_id,
+                     uint64_t msc)
+{
+    Bool                        ret;
+
+    if (crtc == NULL)
+        ret = present_fake_queue_vblank(screen, event_id, msc);
+    else
+    {
+        present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+        ret = (*screen_priv->info->queue_vblank) (crtc, event_id, msc);
+    }
+    return ret;
+}
+
+static uint64_t
+present_window_to_crtc_msc(WindowPtr window, RRCrtcPtr crtc, uint64_t window_msc, uint64_t new_msc)
+{
+    present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
+
+    if (crtc != window_priv->crtc) {
+        uint64_t        old_ust, old_msc;
+
+        if (window_priv->crtc == PresentCrtcNeverSet) {
+            window_priv->msc_offset = 0;
+        } else {
+            /* The old CRTC may have been turned off, in which case
+             * we'll just use whatever previous MSC we'd seen from this CRTC
+             */
+
+            if (present_get_ust_msc(window->drawable.pScreen, window_priv->crtc, &old_ust, &old_msc) != Success)
+                old_msc = window_priv->msc;
+
+            window_priv->msc_offset += new_msc - old_msc;
+        }
+        window_priv->crtc = crtc;
+    }
+
+    return window_msc + window_priv->msc_offset;
+}
+
+/*
+ * When the wait fence or previous flip is completed, it's time
+ * to re-try the request
+ */
+static void
+present_re_execute(present_vblank_ptr vblank)
+{
+    uint64_t            ust = 0, crtc_msc = 0;
+
+    if (vblank->crtc)
+        (void) present_get_ust_msc(vblank->screen, vblank->crtc, &ust, &crtc_msc);
+
+    present_execute(vblank, ust, crtc_msc);
+}
+
+static void
+present_flip_try_ready(ScreenPtr screen)
+{
+    present_vblank_ptr  vblank;
+
+    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
+        if (vblank->queued) {
+            present_re_execute(vblank);
+            return;
+        }
+    }
+}
+
+static void
+present_flip_idle(ScreenPtr screen)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    if (screen_priv->flip_pixmap) {
+        present_pixmap_idle(screen_priv->flip_pixmap, screen_priv->flip_window,
+                            screen_priv->flip_serial, screen_priv->flip_idle_fence);
+        if (screen_priv->flip_idle_fence)
+            present_fence_destroy(screen_priv->flip_idle_fence);
+        dixDestroyPixmap(screen_priv->flip_pixmap, screen_priv->flip_pixmap->drawable.id);
+        screen_priv->flip_crtc = NULL;
+        screen_priv->flip_window = NULL;
+        screen_priv->flip_serial = 0;
+        screen_priv->flip_pixmap = NULL;
+        screen_priv->flip_idle_fence = NULL;
+    }
+}
+
+void
+present_restore_screen_pixmap(ScreenPtr screen)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+    PixmapPtr screen_pixmap = (*screen->GetScreenPixmap)(screen);
+    PixmapPtr flip_pixmap;
+    WindowPtr flip_window;
+
+    if (screen_priv->flip_pending) {
+        flip_window = screen_priv->flip_pending->window;
+        flip_pixmap = screen_priv->flip_pending->pixmap;
+    } else {
+        flip_window = screen_priv->flip_window;
+        flip_pixmap = screen_priv->flip_pixmap;
+    }
+
+    assert (flip_pixmap);
+
+    /* Update the screen pixmap with the current flip pixmap contents
+     * Only do this the first time for a particular unflip operation, or
+     * we'll probably scribble over other windows
+     */
+    if (screen->root && screen->GetWindowPixmap(screen->root) == flip_pixmap)
+        present_copy_region(&screen_pixmap->drawable, flip_pixmap, NULL, 0, 0);
+
+    /* Switch back to using the screen pixmap now to avoid
+     * 2D applications drawing to the wrong pixmap.
+     */
+    if (flip_window)
+        present_set_tree_pixmap(flip_window, flip_pixmap, screen_pixmap);
+    if (screen->root)
+        present_set_tree_pixmap(screen->root, NULL, screen_pixmap);
+}
+
+void
+present_set_abort_flip(ScreenPtr screen)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    if (!screen_priv->flip_pending->abort_flip) {
+        present_restore_screen_pixmap(screen);
+        screen_priv->flip_pending->abort_flip = TRUE;
+    }
+}
+
+static void
+present_unflip(ScreenPtr screen)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    assert (!screen_priv->unflip_event_id);
+    assert (!screen_priv->flip_pending);
+
+    present_restore_screen_pixmap(screen);
+
+    screen_priv->unflip_event_id = ++present_event_id;
+    DebugPresent(("u %lld\n", screen_priv->unflip_event_id));
+    (*screen_priv->info->unflip) (screen, screen_priv->unflip_event_id);
+}
+
+static void
+present_flip_notify(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
+{
+    ScreenPtr                   screen = vblank->screen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    DebugPresent(("\tn %lld %p %8lld: %08lx -> %08lx\n",
+                  vblank->event_id, vblank, vblank->target_msc,
+                  vblank->pixmap ? vblank->pixmap->drawable.id : 0,
+                  vblank->window ? vblank->window->drawable.id : 0));
+
+    assert (vblank == screen_priv->flip_pending);
+
+    present_flip_idle(screen);
+
+    xorg_list_del(&vblank->event_queue);
+
+    /* Transfer reference for pixmap and fence from vblank to screen_priv */
+    screen_priv->flip_crtc = vblank->crtc;
+    screen_priv->flip_window = vblank->window;
+    screen_priv->flip_serial = vblank->serial;
+    screen_priv->flip_pixmap = vblank->pixmap;
+    screen_priv->flip_sync = vblank->sync_flip;
+    screen_priv->flip_idle_fence = vblank->idle_fence;
+
+    vblank->pixmap = NULL;
+    vblank->idle_fence = NULL;
+
+    screen_priv->flip_pending = NULL;
+
+    if (vblank->abort_flip)
+        present_unflip(screen);
+
+    present_vblank_notify(vblank, PresentCompleteKindPixmap, PresentCompleteModeFlip, ust, crtc_msc);
+    present_vblank_destroy(vblank);
+
+    present_flip_try_ready(screen);
+}
+
+void
+present_event_notify(uint64_t event_id, uint64_t ust, uint64_t msc)
+{
+    present_vblank_ptr  vblank;
+    int                 s;
+
+    if (!event_id)
+        return;
+    DebugPresent(("\te %lld ust %lld msc %lld\n", event_id, ust, msc));
+    xorg_list_for_each_entry(vblank, &present_exec_queue, event_queue) {
+        int64_t match = event_id - vblank->event_id;
+        if (match == 0) {
+            present_execute(vblank, ust, msc);
+            return;
+        }
+    }
+    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
+        if (vblank->event_id == event_id) {
+            if (vblank->queued)
+                present_execute(vblank, ust, msc);
+            else
+                present_flip_notify(vblank, ust, msc);
+            return;
+        }
+    }
+
+    for (s = 0; s < screenInfo.numScreens; s++) {
+        ScreenPtr               screen = screenInfo.screens[s];
+        present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+        if (event_id == screen_priv->unflip_event_id) {
+            DebugPresent(("\tun %lld\n", event_id));
+            screen_priv->unflip_event_id = 0;
+            present_flip_idle(screen);
+            present_flip_try_ready(screen);
+            return;
+        }
+    }
+}
+
+/*
+ * 'window' is being reconfigured. Check to see if it is involved
+ * in flipping and clean up as necessary
+ */
+void
+present_check_flip_window (WindowPtr window)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+    present_window_priv_ptr     window_priv = present_window_priv(window);
+    present_vblank_ptr          flip_pending = screen_priv->flip_pending;
+    present_vblank_ptr          vblank;
+    PresentFlipReason           reason;
+
+    /* If this window hasn't ever been used with Present, it can't be
+     * flipping
+     */
+    if (!window_priv)
+        return;
+
+    if (screen_priv->unflip_event_id)
+        return;
+
+    if (flip_pending) {
+        /*
+         * Check pending flip
+         */
+        if (flip_pending->window == window) {
+            if (!present_check_flip(flip_pending->crtc, window, flip_pending->pixmap,
+                                    flip_pending->sync_flip, NULL, 0, 0, NULL))
+                present_set_abort_flip(screen);
+        }
+    } else {
+        /*
+         * Check current flip
+         */
+        if (window == screen_priv->flip_window) {
+            if (!present_check_flip(screen_priv->flip_crtc, window, screen_priv->flip_pixmap, screen_priv->flip_sync, NULL, 0, 0, NULL))
+                present_unflip(screen);
+        }
+    }
+
+    /* Now check any queued vblanks */
+    xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
+        if (vblank->queued && vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, vblank->sync_flip, NULL, 0, 0, &reason)) {
+            vblank->flip = FALSE;
+            vblank->reason = reason;
+            if (vblank->sync_flip)
+                vblank->requeue = TRUE;
+        }
+    }
+}
+
+Bool
+present_can_window_flip(WindowPtr window)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    PixmapPtr                   window_pixmap;
+    WindowPtr                   root = screen->root;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (!screen_priv)
+        return FALSE;
+
+    if (!screen_priv->info)
+        return FALSE;
+
+    /* Check to see if the driver supports flips at all */
+    if (!screen_priv->info->flip)
+        return FALSE;
+
+    /* Make sure the window hasn't been redirected with Composite */
+    window_pixmap = screen->GetWindowPixmap(window);
+    if (window_pixmap != screen->GetScreenPixmap(screen) &&
+        window_pixmap != screen_priv->flip_pixmap &&
+        window_pixmap != present_flip_pending_pixmap(screen))
+        return FALSE;
+
+    /* Check for full-screen window */
+    if (!RegionEqual(&window->clipList, &root->winSize)) {
+        return FALSE;
+    }
+
+    /* Does the window match the pixmap exactly? */
+    if (window->drawable.x != 0 || window->drawable.y != 0) {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+ * Called when the wait fence is triggered; just gets the current msc/ust and
+ * calls present_execute again. That will re-check the fence and pend the
+ * request again if it's still not actually ready
+ */
+static void
+present_wait_fence_triggered(void *param)
+{
+    present_vblank_ptr  vblank = param;
+    present_re_execute(vblank);
+}
+
+/*
+ * Once the required MSC has been reached, execute the pending request.
+ *
+ * For requests to actually present something, either blt contents to
+ * the screen or queue a frame buffer swap.
+ *
+ * For requests to just get the current MSC/UST combo, skip that part and
+ * go straight to event delivery
+ */
+
+static void
+present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
+{
+    WindowPtr                   window = vblank->window;
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+    uint8_t                     mode;
+
+    if (vblank->requeue) {
+        vblank->requeue = FALSE;
+        if (msc_is_after(vblank->target_msc, crtc_msc) &&
+            Success == present_queue_vblank(screen,
+                                            vblank->crtc,
+                                            vblank->event_id,
+                                            vblank->target_msc))
+            return;
+    }
+
+    if (vblank->wait_fence) {
+        if (!present_fence_check_triggered(vblank->wait_fence)) {
+            present_fence_set_callback(vblank->wait_fence, present_wait_fence_triggered, vblank);
+            return;
+        }
+    }
+
+    if (vblank->flip && vblank->pixmap && vblank->window) {
+        if (screen_priv->flip_pending || screen_priv->unflip_event_id) {
+            DebugPresent(("\tr %lld %p (pending %p unflip %lld)\n",
+                          vblank->event_id, vblank,
+                          screen_priv->flip_pending, screen_priv->unflip_event_id));
+            xorg_list_del(&vblank->event_queue);
+            xorg_list_append(&vblank->event_queue, &present_flip_queue);
+            vblank->flip_ready = TRUE;
+            return;
+        }
+    }
+
+    xorg_list_del(&vblank->event_queue);
+    xorg_list_del(&vblank->window_list);
+    vblank->queued = FALSE;
+
+    if (vblank->pixmap && vblank->window) {
+
+        if (vblank->flip) {
+
+            DebugPresent(("\tf %lld %p %8lld: %08lx -> %08lx\n",
+                          vblank->event_id, vblank, crtc_msc,
+                          vblank->pixmap->drawable.id, vblank->window->drawable.id));
+
+            /* Prepare to flip by placing it in the flip queue and
+             * and sticking it into the flip_pending field
+             */
+            screen_priv->flip_pending = vblank;
+
+            xorg_list_add(&vblank->event_queue, &present_flip_queue);
+            /* Try to flip
+             */
+            if (present_flip(vblank->crtc, vblank->event_id, vblank->target_msc, vblank->pixmap, vblank->sync_flip)) {
+                RegionPtr damage;
+
+                /* Fix window pixmaps:
+                 *  1) Restore previous flip window pixmap
+                 *  2) Set current flip window pixmap to the new pixmap
+                 */
+                if (screen_priv->flip_window && screen_priv->flip_window != window)
+                    present_set_tree_pixmap(screen_priv->flip_window,
+                                            screen_priv->flip_pixmap,
+                                            (*screen->GetScreenPixmap)(screen));
+                present_set_tree_pixmap(vblank->window, NULL, vblank->pixmap);
+                present_set_tree_pixmap(screen->root, NULL, vblank->pixmap);
+
+                /* Report update region as damaged
+                 */
+                if (vblank->update) {
+                    damage = vblank->update;
+                    RegionIntersect(damage, damage, &window->clipList);
+                } else
+                    damage = &window->clipList;
+
+                DamageDamageRegion(&vblank->window->drawable, damage);
+                return;
+            }
+
+            xorg_list_del(&vblank->event_queue);
+            /* Oops, flip failed. Clear the flip_pending field
+              */
+            screen_priv->flip_pending = NULL;
+            vblank->flip = FALSE;
+        }
+        DebugPresent(("\tc %p %8lld: %08lx -> %08lx\n", vblank, crtc_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id));
+        if (screen_priv->flip_pending) {
+
+            /* Check pending flip
+             */
+            if (window == screen_priv->flip_pending->window)
+                present_set_abort_flip(screen);
+        } else if (!screen_priv->unflip_event_id) {
+
+            /* Check current flip
+             */
+            if (window == screen_priv->flip_window)
+                present_unflip(screen);
+        }
+
+        /* If present_flip failed, we may have to requeue for the target MSC */
+        if (vblank->target_msc == crtc_msc + 1 &&
+            Success == present_queue_vblank(screen,
+                                            vblank->crtc,
+                                            vblank->event_id,
+                                            vblank->target_msc)) {
+            xorg_list_add(&vblank->event_queue, &present_exec_queue);
+            xorg_list_append(&vblank->window_list,
+                             &present_get_window_priv(window, TRUE)->vblank);
+            vblank->queued = TRUE;
+            return;
+        }
+
+        present_copy_region(&window->drawable, vblank->pixmap, vblank->update, vblank->x_off, vblank->y_off);
+
+        /* present_copy_region sticks the region into a scratch GC,
+         * which is then freed, freeing the region
+         */
+        vblank->update = NULL;
+        present_flush(window);
+
+        present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+    }
+
+    /* Compute correct CompleteMode
+     */
+    if (vblank->kind == PresentCompleteKindPixmap) {
+        if (vblank->pixmap && vblank->window) {
+            if (vblank->has_suboptimal && vblank->reason == PRESENT_FLIP_REASON_BUFFER_FORMAT)
+                mode = PresentCompleteModeSuboptimalCopy;
+            else
+                mode = PresentCompleteModeCopy;
+        } else {
+            mode = PresentCompleteModeSkip;
+        }
+    }
+    else
+        mode = PresentCompleteModeCopy;
+
+
+    present_vblank_notify(vblank, vblank->kind, mode, ust, crtc_msc);
+    present_vblank_destroy(vblank);
+}
+
+int
+present_pixmap(WindowPtr window,
+               PixmapPtr pixmap,
+               CARD32 serial,
+               RegionPtr valid,
+               RegionPtr update,
+               int16_t x_off,
+               int16_t y_off,
+               RRCrtcPtr target_crtc,
+               SyncFence *wait_fence,
+               SyncFence *idle_fence,
+               uint32_t options,
+               uint64_t window_msc,
+               uint64_t divisor,
+               uint64_t remainder,
+               present_notify_ptr notifies,
+               int num_notifies)
+{
+    uint64_t                    ust = 0;
+    uint64_t                    target_msc;
+    uint64_t                    crtc_msc = 0;
+    int                         ret;
+    present_vblank_ptr          vblank, tmp;
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+    PresentFlipReason           reason = PRESENT_FLIP_REASON_UNKNOWN;
+
+    if (!window_priv)
+        return BadAlloc;
+
+    if (!screen_priv || !screen_priv->info)
+        target_crtc = NULL;
+    else if (!target_crtc) {
+        /* Update the CRTC if we have a pixmap or we don't have a CRTC
+         */
+        if (!pixmap)
+            target_crtc = window_priv->crtc;
+
+        if (!target_crtc || target_crtc == PresentCrtcNeverSet)
+            target_crtc = present_get_crtc(window);
+    }
+
+    ret = present_get_ust_msc(screen, target_crtc, &ust, &crtc_msc);
+
+    target_msc = present_window_to_crtc_msc(window, target_crtc, window_msc, crtc_msc);
+
+    if (ret == Success) {
+        /* Stash the current MSC away in case we need it later
+         */
+        window_priv->msc = crtc_msc;
+    }
+
+    /* Adjust target_msc to match modulus
+     */
+    if (msc_is_equal_or_after(crtc_msc, target_msc)) {
+        if (divisor != 0) {
+            target_msc = crtc_msc - (crtc_msc % divisor) + remainder;
+            if (options & PresentOptionAsync) {
+                if (msc_is_after(crtc_msc, target_msc))
+                    target_msc += divisor;
+            } else {
+                if (msc_is_equal_or_after(crtc_msc, target_msc))
+                    target_msc += divisor;
+            }
+        } else {
+            target_msc = crtc_msc;
+            if (!(options & PresentOptionAsync))
+                target_msc++;
+        }
+    }
+
+    /*
+     * Look for a matching presentation already on the list and
+     * don't bother doing the previous one if this one will overwrite it
+     * in the same frame
+     */
+
+    if (!update && pixmap) {
+        xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->vblank, window_list) {
+
+            if (!vblank->pixmap)
+                continue;
+
+            if (!vblank->queued)
+                continue;
+
+            if (vblank->crtc != target_crtc || vblank->target_msc != target_msc)
+                continue;
+
+            DebugPresent(("\tx %lld %p %8lld: %08lx -> %08lx (crtc %p)\n",
+                          vblank->event_id, vblank, vblank->target_msc,
+                          vblank->pixmap->drawable.id, vblank->window->drawable.id,
+                          vblank->crtc));
+
+            present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+            present_fence_destroy(vblank->idle_fence);
+            dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
+
+            vblank->pixmap = NULL;
+            vblank->idle_fence = NULL;
+            vblank->flip = FALSE;
+            if (vblank->flip_ready)
+                present_re_execute(vblank);
+        }
+    }
+
+    vblank = calloc (1, sizeof (present_vblank_rec));
+    if (!vblank)
+        return BadAlloc;
+
+    xorg_list_append(&vblank->window_list, &window_priv->vblank);
+    xorg_list_init(&vblank->event_queue);
+
+    vblank->screen = screen;
+    vblank->window = window;
+    vblank->pixmap = pixmap;
+    vblank->event_id = ++present_event_id;
+    if (pixmap) {
+        vblank->kind = PresentCompleteKindPixmap;
+        pixmap->refcnt++;
+    } else
+        vblank->kind = PresentCompleteKindNotifyMSC;
+
+    vblank->serial = serial;
+
+    if (valid) {
+        vblank->valid = RegionDuplicate(valid);
+        if (!vblank->valid)
+            goto no_mem;
+    }
+    if (update) {
+        vblank->update = RegionDuplicate(update);
+        if (!vblank->update)
+            goto no_mem;
+    }
+
+    vblank->x_off = x_off;
+    vblank->y_off = y_off;
+    vblank->target_msc = target_msc;
+    vblank->crtc = target_crtc;
+    vblank->msc_offset = window_priv->msc_offset;
+    vblank->notifies = notifies;
+    vblank->num_notifies = num_notifies;
+    vblank->has_suboptimal = (options & PresentOptionSuboptimal);
+
+    if (pixmap != NULL &&
+        !(options & PresentOptionCopy) &&
+        screen_priv->info) {
+        if (msc_is_after(target_msc, crtc_msc) &&
+            present_check_flip (target_crtc, window, pixmap, TRUE, valid, x_off, y_off, &reason))
+        {
+            vblank->flip = TRUE;
+            vblank->sync_flip = TRUE;
+            target_msc--;
+        } else if ((screen_priv->info->capabilities & PresentCapabilityAsync) &&
+            present_check_flip (target_crtc, window, pixmap, FALSE, valid, x_off, y_off, &reason))
+        {
+            vblank->flip = TRUE;
+        }
+    }
+    vblank->reason = reason;
+
+    if (wait_fence) {
+        vblank->wait_fence = present_fence_create(wait_fence);
+        if (!vblank->wait_fence)
+            goto no_mem;
+    }
+
+    if (idle_fence) {
+        vblank->idle_fence = present_fence_create(idle_fence);
+        if (!vblank->idle_fence)
+            goto no_mem;
+    }
+
+    if (pixmap)
+        DebugPresent(("q %lld %p %8lld: %08lx -> %08lx (crtc %p) flip %d vsync %d serial %d\n",
+                      vblank->event_id, vblank, target_msc,
+                      vblank->pixmap->drawable.id, vblank->window->drawable.id,
+                      target_crtc, vblank->flip, vblank->sync_flip, vblank->serial));
+
+    xorg_list_append(&vblank->event_queue, &present_exec_queue);
+    vblank->queued = TRUE;
+    if (msc_is_after(target_msc, crtc_msc)) {
+        ret = present_queue_vblank(screen, target_crtc, vblank->event_id, target_msc);
+        if (ret == Success)
+            return Success;
+
+        DebugPresent(("present_queue_vblank failed\n"));
+    }
+
+    present_execute(vblank, ust, crtc_msc);
+
+    return Success;
+
+no_mem:
+    ret = BadAlloc;
+    vblank->notifies = NULL;
+    present_vblank_destroy(vblank);
+    return ret;
+}
+
+void
+present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
+{
+    present_vblank_ptr  vblank;
+
+    if (crtc == NULL)
+        present_fake_abort_vblank(screen, event_id, msc);
+    else
+    {
+        present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+        (*screen_priv->info->abort_vblank) (crtc, event_id, msc);
+    }
+
+    xorg_list_for_each_entry(vblank, &present_exec_queue, event_queue) {
+        int64_t match = event_id - vblank->event_id;
+        if (match == 0) {
+            xorg_list_del(&vblank->event_queue);
+            vblank->queued = FALSE;
+            return;
+        }
+    }
+    xorg_list_for_each_entry(vblank, &present_flip_queue, event_queue) {
+        if (vblank->event_id == event_id) {
+            xorg_list_del(&vblank->event_queue);
+            vblank->queued = FALSE;
+            return;
+        }
+    }
+}
+
+int
+present_notify_msc(WindowPtr window,
+                   CARD32 serial,
+                   uint64_t target_msc,
+                   uint64_t divisor,
+                   uint64_t remainder)
+{
+    return present_pixmap(window,
+                          NULL,
+                          serial,
+                          NULL, NULL,
+                          0, 0,
+                          NULL,
+                          NULL, NULL,
+                          divisor == 0 ? PresentOptionAsync : 0,
+                          target_msc, divisor, remainder, NULL, 0);
+}
+
+void
+present_flip_destroy(ScreenPtr screen)
+{
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    /* Reset window pixmaps back to the screen pixmap */
+    if (screen_priv->flip_pending)
+        present_set_abort_flip(screen);
+
+    /* Drop reference to any pending flip or unflip pixmaps. */
+    present_flip_idle(screen);
+}
+
+void
+present_vblank_destroy(present_vblank_ptr vblank)
+{
+    /* Remove vblank from window and screen lists */
+    xorg_list_del(&vblank->window_list);
+
+    DebugPresent(("\td %lld %p %8lld: %08lx -> %08lx\n",
+                  vblank->event_id, vblank, vblank->target_msc,
+                  vblank->pixmap ? vblank->pixmap->drawable.id : 0,
+                  vblank->window ? vblank->window->drawable.id : 0));
+
+    /* Drop pixmap reference */
+    if (vblank->pixmap)
+        dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
+
+    /* Free regions */
+    if (vblank->valid)
+        RegionDestroy(vblank->valid);
+    if (vblank->update)
+        RegionDestroy(vblank->update);
+
+    if (vblank->wait_fence)
+        present_fence_destroy(vblank->wait_fence);
+
+    if (vblank->idle_fence)
+        present_fence_destroy(vblank->idle_fence);
+
+    if (vblank->notifies)
+        present_destroy_notifies(vblank->notifies, vblank->num_notifies);
+
+    free(vblank);
+}
+
+Bool
+present_init(void)
+{
+    xorg_list_init(&present_exec_queue);
+    xorg_list_init(&present_flip_queue);
+    present_fake_queue_init();
+    return TRUE;
+}


More information about the xorg-commit mailing list