[PATCH 4/5] xwayland: Use Present's flips per window mode

Roman Gilg subdiff at gmail.com
Tue Aug 29 15:24:31 UTC 2017


Enables in Rootless mode with Glamor the usage of the new per window
flip mode of Present.

With this patch per window flips are possible for windows, that own an
xwl_window themselves, i.e. windows, that are manually redirected or
child windows, that are region equal to their first manually redirected
parent window.

We create a fake RRCrtc for such a window, since a Wayland compositor
can receive flips and in theory also send back timing information for
every surface separately.

Currently there can always only be one Pixmap flipping window per
xwl_window. If a new child window requests Pixmap flips we always
switch to the new one after cleaning up the old one.

When the Wayland server sends out the buffer release event, Xwayland
informs Present about the Pixmap being now available again.

There is a small change to the window mode in Present as well, that now
calls flip_executed at the end of processing the flip with the
calculated damage. Xwayland then commits the new buffer.

Signed-off-by: Roman Gilg <subdiff at gmail.com>
---
 hw/xwayland/Makefile.am        |   1 +
 hw/xwayland/meson.build        |   1 +
 hw/xwayland/xwayland-glamor.c  |  17 +-
 hw/xwayland/xwayland-present.c | 342 +++++++++++++++++++++++++++++++++++++++++
 hw/xwayland/xwayland.c         |  24 ++-
 hw/xwayland/xwayland.h         |  29 +++-
 present/present.h              |   7 +
 present/present_winmode.c      |   5 +
 8 files changed, 420 insertions(+), 6 deletions(-)
 create mode 100644 hw/xwayland/xwayland-present.c

diff --git a/hw/xwayland/Makefile.am b/hw/xwayland/Makefile.am
index eda4979..9c89829 100644
--- a/hw/xwayland/Makefile.am
+++ b/hw/xwayland/Makefile.am
@@ -15,6 +15,7 @@ Xwayland_SOURCES =				\
 	xwayland-cursor.c			\
 	xwayland-shm.c				\
 	xwayland-output.c			\
+	xwayland-present.c			\
 	xwayland-cvt.c				\
 	xwayland-vidmode.c			\
 	xwayland.h				\
diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build
index b619a66..5abf2f8 100644
--- a/hw/xwayland/meson.build
+++ b/hw/xwayland/meson.build
@@ -4,6 +4,7 @@ srcs = [
     'xwayland-cursor.c',
     'xwayland-shm.c',
     'xwayland-output.c',
+    'xwayland-present.c',
     'xwayland-cvt.c',
     'xwayland-vidmode.c',
     '../../mi/miinitext.c',
diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index 8762283..967c88b 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -153,14 +153,23 @@ 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);
     int prime_fd;
 
-    if (xwl_pixmap->buffer)
+    if (xwl_pixmap->buffer) {
+        if(created)
+            *created = FALSE;
         return xwl_pixmap->buffer;
+    }
+
+    if(created)
+        *created = TRUE;
 
     prime_fd = gbm_bo_get_fd(xwl_pixmap->bo);
     if (prime_fd == -1)
@@ -168,8 +177,8 @@ xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap)
 
     xwl_pixmap->buffer =
         wl_drm_create_prime_buffer(xwl_screen->drm, prime_fd,
-                                   pixmap->drawable.width,
-                                   pixmap->drawable.height,
+                                   width,
+                                   height,
                                    drm_format_for_depth(pixmap->drawable.depth),
                                    0, gbm_bo_get_stride(xwl_pixmap->bo),
                                    0, 0,
diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
new file mode 100644
index 0000000..c10128c
--- /dev/null
+++ b/hw/xwayland/xwayland-present.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright © 2017 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>
+
+static struct xorg_list xwl_present_windows;
+
+void
+xwl_present_cleanup(WindowPtr window)
+{
+    struct xwl_window           *xwl_window = xwl_window_from_window(window);
+    struct xwl_present_event    *event, *tmp;
+
+    if (!xwl_window)
+        return;
+
+    if (!xwl_window->present_window)
+        return;
+
+    if (xwl_window->present_window != window && xwl_window->window != window)
+        /* Unrealizing a non-presenting sibling */
+        return;
+
+    /*
+     * At this point we're either:
+     * - Unflipping.
+     * - Unrealizing the presenting window 'xwl_window->present_window'
+     *   or its ancestor 'xwl_window->window'.
+     * And therefore need to cleanup.
+     */
+
+    if (xwl_window->present_frame_callback) {
+        wl_callback_destroy(xwl_window->present_frame_callback);
+        xwl_window->present_frame_callback = NULL;
+    }
+
+    /* Reset base data */
+    xorg_list_del(&xwl_window->present_link);
+    xwl_window->present_surface = NULL;
+    xwl_window->present_window = NULL;
+
+    /* Clear remaining events */
+    xorg_list_for_each_entry_safe(event, tmp, &xwl_window->present_event_list, list) {
+        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) {
+        present_winmode_event_notify(xwl_window->window, event->event_id, 0, xwl_window->present_msc);
+        xorg_list_del(&event->list);
+        free(event);
+    }
+}
+
+static void
+buffer_release(void *data, struct wl_buffer *buffer)
+{
+    WindowPtr                   present_window = wl_buffer_get_user_data(buffer);
+    struct xwl_window           *xwl_window;
+    struct xwl_present_event    *event, *tmp;
+    Bool                        found_window = FALSE;
+
+    /* Find window */
+    xorg_list_for_each_entry(xwl_window, &xwl_present_windows, present_link) {
+        if (xwl_window->present_window == present_window) {
+            found_window = TRUE;
+            break;
+        }
+    }
+
+    if (!found_window)
+        return;
+
+    xorg_list_for_each_entry_safe(event, tmp, &xwl_window->present_release_queue, list) {
+        if (event->buffer == buffer) {
+            present_winmode_event_notify(present_window, event->event_id, 0, xwl_window->present_msc);
+            xorg_list_del(&event->list);
+            free(event);
+            break;
+        }
+    }
+}
+
+static const struct wl_buffer_listener release_listener = {
+    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_winmode_event_notify(xwl_window->present_window, event->event_id, 0, msc);
+            xorg_list_del(&event->list);
+            free(event);
+        }
+    }
+}
+
+static void
+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_present_events_notify(xwl_window);
+}
+
+static const struct wl_callback_listener present_frame_listener = {
+    present_frame_callback
+};
+
+static RRCrtcPtr
+xwl_present_get_crtc(WindowPtr present_window)
+{
+    struct xwl_window *xwl_window = xwl_window_from_window(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_from_window(present_window);
+    if (!xwl_window)
+        return BadAlloc;
+    *ust = 0;
+    *msc = xwl_window->present_msc;
+
+    return Success;
+}
+
+/*
+ * 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)
+{
+    /*
+     * Queuing events doesn't work yet: There needs to be a Wayland protocol
+     * extension infroming clients about timings.
+     *
+     * See for a proposal for that:
+     * https://cgit.freedesktop.org/wayland/wayland-protocols/tree/stable/presentation-time
+     *
+     */
+    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_from_window(present_window);
+    struct xwl_present_event *event, *tmp;
+
+    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;
+        }
+    }
+}
+
+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_flip(RRCrtcPtr crtc,
+                       WindowPtr present_window,
+                       PixmapPtr pixmap,
+                       Bool sync_flip)
+{
+    struct xwl_window *xwl_window = xwl_window_from_window(present_window);
+
+    if (!xwl_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;
+    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)
+{
+    struct xwl_window           *xwl_window = xwl_window_from_window(present_window);
+    BoxPtr                      present_box;
+    Bool                        buffer_created;
+    struct wl_buffer            *buffer;
+    struct xwl_present_event    *event;
+
+    present_box = RegionExtents(&present_window->winSize);
+
+    /* We always switch to another child window, if it wants to present. */
+    if (xwl_window->present_window != present_window) {
+        if (xwl_window->present_window)
+            xwl_present_cleanup(xwl_window->present_window);
+        xwl_window->present_window = present_window;
+        xorg_list_add(&xwl_window->present_link, &xwl_present_windows);
+
+        /* We can flip directly to the main surface (full screen window without clips) */
+        xwl_window->present_surface = xwl_window->surface;
+    }
+
+    event = malloc(sizeof *event);
+    if (!event) {
+        xwl_present_cleanup(present_window);
+        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->xwl_window = xwl_window;
+    event->buffer = buffer;
+
+    xorg_list_add(&event->list, &xwl_window->present_release_queue);
+
+    if (buffer_created)
+        wl_buffer_add_listener(buffer, &release_listener, NULL);
+
+    wl_buffer_set_user_data(buffer, present_window);
+    wl_surface_attach(xwl_window->present_surface, buffer, 0, 0);
+
+    if (!xwl_window->present_frame_callback) {
+        xwl_window->present_frame_callback = wl_surface_frame(xwl_window->present_surface);
+        wl_callback_add_listener(xwl_window->present_frame_callback, &present_frame_listener, xwl_window);
+    }
+
+    return TRUE;
+}
+
+static void
+xwl_present_flip_executed(WindowPtr present_window, RRCrtcPtr crtc, uint64_t event_id, RegionPtr damage)
+{
+    struct xwl_window *xwl_window = xwl_window_from_window(present_window);
+    BoxPtr box = RegionExtents(damage);
+
+    wl_surface_damage(xwl_window->present_surface, box->x1, box->y1,
+                      box->x2 - box->x1, box->y2 - box->y1);
+
+    wl_surface_commit(xwl_window->present_surface);
+    wl_display_flush(xwl_window->xwl_screen->display);
+
+    present_winmode_event_notify(present_window, event_id, 0, xwl_window->present_msc);
+}
+
+static void
+xwl_present_unflip(WindowPtr window, uint64_t event_id)
+{
+    xwl_present_cleanup(window);
+    present_winmode_event_notify(window, event_id, 0, 0);
+}
+
+static present_winmode_screen_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_flip = xwl_present_check_flip,
+    .flip = xwl_present_flip,
+    .unflip = xwl_present_unflip,
+    .flip_executed = xwl_present_flip_executed,
+};
+
+Bool
+xwl_present_init(ScreenPtr screen)
+{
+    xorg_list_init(&xwl_present_windows);
+    return present_winmode_screen_init(screen, &xwl_present_info);
+}
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index cb929ca..2182c71 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -496,6 +496,13 @@ 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;
+        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);
@@ -555,6 +562,9 @@ xwl_unrealize_window(WindowPtr window)
         xwl_seat_clear_touch(xwl_seat, window);
     }
 
+    /* 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;
@@ -572,6 +582,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);
 
@@ -615,7 +628,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);
@@ -642,6 +658,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_surface)
+            continue;
         /* If we're waiting on a frame callback from the server,
          * don't attach a new buffer. */
         if (xwl_window->frame_callback)
@@ -976,6 +995,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;
diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h
index 6d3edf3..0969043 100644
--- a/hw/xwayland/xwayland.h
+++ b/hw/xwayland/xwayland.h
@@ -60,6 +60,7 @@ struct xwl_screen {
     int listen_fd_count;
     int rootless;
     int glamor;
+    int present;
 
     CreateScreenResourcesProcPtr CreateScreenResources;
     CloseScreenProcPtr CloseScreen;
@@ -113,6 +114,26 @@ 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;
+    struct wl_surface *present_surface;
+    uint64_t present_msc;
+    struct wl_callback *present_frame_callback;
+    struct xorg_list present_event_list;
+    struct xorg_list present_release_queue;
+};
+
+struct xwl_present_event {
+    uint64_t event_id;
+    uint64_t target_msc;
+
+    struct xwl_window *xwl_window;
+    struct wl_buffer *buffer;
+
+    struct xorg_list list;
 };
 
 #define MODIFIER_META 0x01
@@ -322,7 +343,13 @@ Bool xwl_glamor_init(struct xwl_screen *xwl_screen);
 
 Bool xwl_screen_init_glamor(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);
+
+Bool xwl_present_init(ScreenPtr screen);
+void xwl_present_cleanup(WindowPtr window);
 
 void xwl_screen_release_tablet_manager(struct xwl_screen *xwl_screen);
 
diff --git a/present/present.h b/present/present.h
index 7a0afbd..c9a744f 100644
--- a/present/present.h
+++ b/present/present.h
@@ -98,6 +98,12 @@ typedef Bool (*present_winmode_flip_ptr) (WindowPtr window,
                                           PixmapPtr pixmap,
                                           Bool sync_flip);
 
+/* Called when the flip with id 'flip_event_id' has been fully processed internally,
+ * and the Extension is waiting with regards to this flip for the information,
+ * that the flips isn't pending anymore.
+ */
+typedef void (*present_flip_executed_ptr) (WindowPtr window, RRCrtcPtr crtc, uint64_t flip_event_id, RegionPtr damage);
+
 /* "unflip" back to the regular screen scanout buffer
  *
  * present_event_notify should be called with 'event_id' when the unflip occurs.
@@ -139,6 +145,7 @@ typedef struct present_winmode_screen_info {
     uint32_t                            capabilities;
     present_check_flip_ptr              check_flip;
     present_winmode_flip_ptr            flip;
+    present_flip_executed_ptr           flip_executed;
     present_winmode_unflip_ptr          unflip;
 
 } present_winmode_screen_info_rec, *present_winmode_screen_info_ptr;
diff --git a/present/present_winmode.c b/present/present_winmode.c
index d05f86e..3027db7 100644
--- a/present/present_winmode.c
+++ b/present/present_winmode.c
@@ -363,7 +363,9 @@ static void
 present_winmode_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
 {
     WindowPtr               window = vblank->window;
+    ScreenPtr               screen = window->drawable.pScreen;
     present_window_priv_ptr window_priv = present_window_priv(window);
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
 
     if (present_execute_wait(vblank, crtc_msc))
         return;
@@ -419,6 +421,9 @@ present_winmode_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_m
                     damage = &window->clipList;
 
                 DamageDamageRegion(&vblank->window->drawable, damage);
+                if (*screen_priv->winmode_info->flip_executed)
+                    (*screen_priv->winmode_info->flip_executed) (vblank->window, vblank->crtc, vblank->event_id, damage);
+
 
                 return;
             }
-- 
2.7.4



More information about the xorg-devel mailing list