xserver: Branch 'master' - 9 commits

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Fri Jan 20 17:58:23 UTC 2023


 dri3/dri3_screen.c                      |   56 ---
 hw/xwayland/xwayland-glamor-eglstream.c |    3 
 hw/xwayland/xwayland-glamor-gbm.c       |   19 +
 hw/xwayland/xwayland-glamor.c           |  523 +++++++++++++++++++++++++++++---
 hw/xwayland/xwayland-glamor.h           |    8 
 hw/xwayland/xwayland-present.c          |   29 +
 hw/xwayland/xwayland-screen.c           |    2 
 hw/xwayland/xwayland-screen.h           |    3 
 hw/xwayland/xwayland-window.c           |   48 ++
 hw/xwayland/xwayland-window.h           |   51 +++
 10 files changed, 646 insertions(+), 96 deletions(-)

New commits:
commit d67383a695f40ca5470ae2392233e1f5fc012b0c
Author: Austin Shafer <ashafer at nvidia.com>
Date:   Tue Dec 20 12:23:02 2022 +0100

    xwayland: Send PresentCompleteModeSuboptimalCopy if dmabuf feedback was resent
    
    If the dmabuf protocol's feedback object gave us a new list of
    modifiers, send PresentCompleteModeSuboptimalCopy to the client
    to inform them that they need to call GetSupportedModifiers.
    
    Reviewed-by: Michel Dänzer <mdaenzer at redhat.com>

diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index fdec127d8..6c7784efb 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -608,6 +608,7 @@ xwl_dmabuf_feedback_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf
     struct xwl_dmabuf_feedback *xwl_feedback = data;
 
     xwl_feedback->feedback_done = true;
+    xwl_feedback->unprocessed_feedback_pending = true;
 }
 
 static void
diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index 99e476b2f..2c0e1a05c 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -296,6 +296,7 @@ xwl_present_flip_notify_vblank(present_vblank_ptr vblank, uint64_t ust, uint64_t
 {
     WindowPtr                   window = vblank->window;
     struct xwl_present_window *xwl_present_window = xwl_present_window_priv(window);
+    uint8_t mode = PresentCompleteModeFlip;
 
     DebugPresent(("\tn %" PRIu64 " %p %" PRIu64 " %" PRIu64 ": %08" PRIx32 " -> %08" PRIx32 "\n",
                   vblank->event_id, vblank, vblank->exec_msc, vblank->target_msc,
@@ -321,7 +322,10 @@ xwl_present_flip_notify_vblank(present_vblank_ptr vblank, uint64_t ust, uint64_t
 
     xwl_present_window->flip_active = vblank;
 
-    present_vblank_notify(vblank, PresentCompleteKindPixmap, PresentCompleteModeFlip, ust, crtc_msc);
+    if (vblank->reason == PRESENT_FLIP_REASON_BUFFER_FORMAT)
+        mode = PresentCompleteModeSuboptimalCopy;
+
+    present_vblank_notify(vblank, PresentCompleteKindPixmap, mode, ust, crtc_msc);
 
     if (vblank->abort_flip)
         xwl_present_flips_stop(window);
@@ -559,6 +563,27 @@ xwl_present_flush(WindowPtr window)
     glamor_block_handler(window->drawable.pScreen);
 }
 
+static void
+xwl_present_maybe_set_reason(struct xwl_window *xwl_window, PresentFlipReason *reason)
+{
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+
+    if (!reason || xwl_screen->dmabuf_protocol_version < 4)
+        return;
+
+    if (xwl_window->feedback.unprocessed_feedback_pending) {
+        xwl_window->feedback.unprocessed_feedback_pending = 0;
+
+        *reason = PRESENT_FLIP_REASON_BUFFER_FORMAT;
+    }
+
+    if (xwl_screen->default_feedback.unprocessed_feedback_pending) {
+        xwl_screen->default_feedback.unprocessed_feedback_pending = 0;
+
+        *reason = PRESENT_FLIP_REASON_BUFFER_FORMAT;
+    }
+}
+
 static Bool
 xwl_present_check_flip(RRCrtcPtr crtc,
                        WindowPtr present_window,
@@ -579,6 +604,8 @@ xwl_present_check_flip(RRCrtcPtr crtc,
     if (!xwl_window)
         return FALSE;
 
+    xwl_present_maybe_set_reason(xwl_window, reason);
+
     if (!crtc)
         return FALSE;
 
diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h
index 88ea6011c..2e8313f56 100644
--- a/hw/xwayland/xwayland-window.h
+++ b/hw/xwayland/xwayland-window.h
@@ -80,6 +80,13 @@ struct xwl_dmabuf_feedback {
     int feedback_done;
     int dev_formats_len;
     struct xwl_device_formats *dev_formats;
+    /*
+     * This flag is used to identify if the feedback
+     * has been resent. If this is true, then the xwayland
+     * clients need to be sent PresentCompleteModeSuboptimalCopy
+     * to tell them to re-request modifiers.
+     */
+    int unprocessed_feedback_pending;
 };
 
 struct xwl_window {
commit 9865a2321f96966f1314aab8349392439f2e3e09
Author: Austin Shafer <ashafer at nvidia.com>
Date:   Tue Dec 20 12:22:53 2022 +0100

    dri3: Don't compute intersection with drawable modifiers
    
    In dri3_get_supported_modifiers we were previously intersecting
    the drawable mods and the screen mods. This meant all screen mods
    were returned, but the ones compatible with the drawable were
    returned in the drawable mods list and the rest are returned in
    the screen mods list.
    
    This is a problem with linux_dmabuf v4 since the drawable mods may
    contain different mods not found in the screen mods (such as scanout
    entries). This change removes the intersection, and just returns
    the drawable/screen mod lists directly.
    
    Reviewed-by: Michel Dänzer <mdaenzer at redhat.com>

diff --git a/dri3/dri3_screen.c b/dri3/dri3_screen.c
index 3c7e5bf60..bc96e5339 100644
--- a/dri3/dri3_screen.c
+++ b/dri3/dri3_screen.c
@@ -211,18 +211,17 @@ cache_formats_and_modifiers(ScreenPtr screen)
 int
 dri3_get_supported_modifiers(ScreenPtr screen, DrawablePtr drawable,
                              CARD8 depth, CARD8 bpp,
-                             CARD32 *num_intersect_modifiers,
-                             CARD64 **intersect_modifiers,
+                             CARD32 *num_drawable_modifiers,
+                             CARD64 **drawable_modifiers,
                              CARD32 *num_screen_modifiers,
                              CARD64 **screen_modifiers)
 {
     dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
     const dri3_screen_info_rec *info = ds->info;
-    int                         i, j;
+    int                         i;
     int                         ret;
     uint32_t                    num_drawable_mods;
     uint64_t                   *drawable_mods;
-    CARD64                     *intersect_mods = NULL;
     CARD64                     *screen_mods = NULL;
     CARD32                      format;
     dri3_dmabuf_format_ptr      screen_format = NULL;
@@ -248,10 +247,15 @@ dri3_get_supported_modifiers(ScreenPtr screen, DrawablePtr drawable,
 
     if (screen_format->num_modifiers == 0) {
         *num_screen_modifiers = 0;
-        *num_intersect_modifiers = 0;
+        *num_drawable_modifiers = 0;
         return Success;
     }
 
+    /* copy the screen mods so we can return an owned allocation */
+    screen_mods = xnfalloc(screen_format->num_modifiers * sizeof(CARD64));
+    memcpy(screen_mods, screen_format->modifiers,
+           screen_format->num_modifiers * sizeof(CARD64));
+
     if (!info->get_drawable_modifiers ||
         !info->get_drawable_modifiers(drawable, format,
                                       &num_drawable_mods,
@@ -260,47 +264,11 @@ dri3_get_supported_modifiers(ScreenPtr screen, DrawablePtr drawable,
         drawable_mods = NULL;
     }
 
-    /* We're allocating slightly more memory than necessary but it reduces
-     * the complexity of finding the intersection set.
-     */
-    screen_mods = malloc(screen_format->num_modifiers * sizeof(CARD64));
-    if (!screen_mods)
-        return BadAlloc;
-    if (num_drawable_mods > 0) {
-        intersect_mods = malloc(screen_format->num_modifiers * sizeof(CARD64));
-        if (!intersect_mods) {
-            free(screen_mods);
-            return BadAlloc;
-        }
-    }
-
-    *num_screen_modifiers = 0;
-    *num_intersect_modifiers = 0;
-    for (i = 0; i < screen_format->num_modifiers; i++) {
-        CARD64 modifier = screen_format->modifiers[i];
-        Bool intersect = FALSE;
-
-        for (j = 0; j < num_drawable_mods; j++) {
-            if (drawable_mods[j] == modifier) {
-                intersect = TRUE;
-                break;
-            }
-        }
-
-        if (intersect) {
-            intersect_mods[*num_intersect_modifiers] = modifier;
-            *num_intersect_modifiers += 1;
-        } else {
-            screen_mods[*num_screen_modifiers] = modifier;
-            *num_screen_modifiers += 1;
-        }
-    }
-
-    assert(*num_intersect_modifiers + *num_screen_modifiers == screen_format->num_modifiers);
+    *num_drawable_modifiers = num_drawable_mods;
+    *drawable_modifiers = drawable_mods;
 
-    *intersect_modifiers = intersect_mods;
+    *num_screen_modifiers = screen_format->num_modifiers;
     *screen_modifiers = screen_mods;
-    free(drawable_mods);
 
     return Success;
 }
commit f0415beb9f65f547435d2122b27881b3fbdfa61e
Author: Austin Shafer <ashafer at nvidia.com>
Date:   Tue Dec 20 12:22:45 2022 +0100

    xwayland: Add proper support for telling if a format/mod is supported
    
    This adds to xwl_glamor_is_modifier_supported, where if feedback
    is in use we will check that the format/mod is allowed in any
    device advertised by the compositor.
    
    Reviewed-by: Michel Dänzer <mdaenzer at redhat.com>
    
    [ Michel Dänzer:
    * Move dev_formats declaration to where it's used in
      xwl_feedback_is_modifier_supported
    * Add curly braces around multi-line statement in
      xwl_glamor_is_modifier_supported ]

diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index ad52afd20..fdec127d8 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -97,16 +97,16 @@ xwl_glamor_check_flip(PixmapPtr pixmap)
     return TRUE;
 }
 
-Bool
-xwl_glamor_is_modifier_supported(struct xwl_screen *xwl_screen,
-                                 uint32_t format, uint64_t modifier)
+static Bool
+xwl_glamor_is_modifier_supported_in_formats(struct xwl_format *formats, int num_formats,
+                                            uint32_t format, uint64_t modifier)
 {
     struct xwl_format *xwl_format = NULL;
     int i;
 
-    for (i = 0; i < xwl_screen->num_formats; i++) {
-        if (xwl_screen->formats[i].format == format) {
-            xwl_format = &xwl_screen->formats[i];
+    for (i = 0; i < num_formats; i++) {
+        if (formats[i].format == format) {
+            xwl_format = &formats[i];
             break;
         }
     }
@@ -122,6 +122,52 @@ xwl_glamor_is_modifier_supported(struct xwl_screen *xwl_screen,
     return FALSE;
 }
 
+static Bool
+xwl_feedback_is_modifier_supported(struct xwl_dmabuf_feedback *xwl_feedback,
+                                   uint32_t format, uint64_t modifier)
+{
+    for (int i = 0; i < xwl_feedback->dev_formats_len; i++) {
+        struct xwl_device_formats *dev_formats = &xwl_feedback->dev_formats[i];
+
+        if (xwl_glamor_is_modifier_supported_in_formats(dev_formats->formats,
+                                                        dev_formats->num_formats,
+                                                        format, modifier))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+
+Bool
+xwl_glamor_is_modifier_supported(struct xwl_screen *xwl_screen,
+                                 uint32_t format, uint64_t modifier)
+{
+    struct xwl_window *xwl_window;
+
+    /*
+     * If we are using dmabuf v4, then we need to check in the main
+     * device and per-window format lists. For older protocol
+     * versions we can just check the list returned by the dmabuf.modifier
+     * events in xwl_screen
+     */
+    if (xwl_screen->dmabuf_protocol_version < 4) {
+        return xwl_glamor_is_modifier_supported_in_formats(xwl_screen->formats,
+                                                           xwl_screen->num_formats,
+                                                           format, modifier);
+    }
+
+    if (xwl_feedback_is_modifier_supported(&xwl_screen->default_feedback, format, modifier))
+        return TRUE;
+
+    xorg_list_for_each_entry(xwl_window, &xwl_screen->window_list, link_window) {
+        if (xwl_feedback_is_modifier_supported(&xwl_window->feedback, format, modifier))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
 uint32_t
 wl_drm_format_for_depth(int depth)
 {
commit d61eb4dd98e3192b35c61ecf46ef1c1053c528db
Author: Austin Shafer <ashafer at nvidia.com>
Date:   Thu Dec 2 12:54:23 2021 -0500

    xwayland: Return default feedback in xwl_screen
    
    If protocol version 4 of linux_dmabuf is in use, then the compositor
    may not return anything with the modifiers event. We instead
    will return the formats/mods reported for the main device.
    
    Reviewed-by: Michel Dänzer <mdaenzer at redhat.com>
    
    [ Michel Dänzer:
    * Move main_dev declaration to where it's used in
      xwl_glamor_get_formats
    * Add empty line between variable declaration and comment ]

diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index 988ab33ff..ad52afd20 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -175,6 +175,45 @@ xwl_get_formats(struct xwl_format *format_array, int format_array_len,
     return TRUE;
 }
 
+static Bool
+xwl_get_formats_for_device(struct xwl_dmabuf_feedback *xwl_feedback, dev_t device,
+                           uint32_t *num_formats, uint32_t **formats)
+{
+    uint32_t *ret = NULL;
+    uint32_t count = 0;
+
+    /* go through all matching sets of tranches for the window's device */
+    for (int i = 0; i < xwl_feedback->dev_formats_len; i++) {
+        if (xwl_feedback->dev_formats[i].drm_dev == device) {
+            struct xwl_device_formats *dev_formats = &xwl_feedback->dev_formats[i];
+
+            /* Append the formats from this tranche to the list */
+            ret = xnfreallocarray(ret, count + dev_formats->num_formats, sizeof(CARD32));
+
+            for (int j = 0; j < dev_formats->num_formats; j++) {
+                bool found = false;
+
+                /* Check if this format is already present in the list */
+                for (int k = 0; k < count; k++) {
+                    if (ret[k] == dev_formats->formats[j].format) {
+                        found = true;
+                        break;
+                    }
+                }
+
+                /* If this format has not yet been added, do so now */
+                if (!found)
+                    ret[count++] = dev_formats->formats[j].format;
+            }
+        }
+    }
+
+    *num_formats = count;
+    *formats = ret;
+
+    return TRUE;
+}
+
 Bool
 xwl_glamor_get_formats(ScreenPtr screen,
                        CARD32 *num_formats, CARD32 **formats)
@@ -187,6 +226,13 @@ xwl_glamor_get_formats(ScreenPtr screen,
     if (!xwl_screen->dmabuf)
         return FALSE;
 
+    if (xwl_screen->dmabuf_protocol_version >= 4) {
+        dev_t main_dev = xwl_screen_get_main_dev(xwl_screen);
+
+        return xwl_get_formats_for_device(&xwl_screen->default_feedback, main_dev,
+                                          num_formats, formats);
+    }
+
     return xwl_get_formats(xwl_screen->formats, xwl_screen->num_formats,
                            num_formats, formats);
 }
@@ -232,19 +278,17 @@ xwl_get_modifiers_for_device(struct xwl_dmabuf_feedback *feedback, dev_t device,
                              uint32_t format, uint32_t *num_modifiers,
                              uint64_t **modifiers)
 {
-    struct xwl_device_formats *dev_formats = NULL;
-
     /* Now try to find a matching set of tranches for the window's device */
     for (int i = 0; i < feedback->dev_formats_len; i++) {
-        if (feedback->dev_formats[i].drm_dev == device)
-            dev_formats = &feedback->dev_formats[i];
-    }
+        struct xwl_device_formats *dev_formats = &feedback->dev_formats[i];
 
-    if (!dev_formats)
-        return FALSE;
+        if (dev_formats->drm_dev == device &&
+            xwl_get_modifiers_for_format(dev_formats->formats, dev_formats->num_formats,
+                                         format, num_modifiers, modifiers))
+            return TRUE;
+    }
 
-    return xwl_get_modifiers_for_format(dev_formats->formats, dev_formats->num_formats,
-                                        format, num_modifiers, modifiers);
+    return FALSE;
 }
 
 Bool
@@ -587,6 +631,18 @@ xwl_screen_set_dmabuf_interface(struct xwl_screen *xwl_screen,
     xwl_screen->dmabuf_protocol_version = supported_version;
     zwp_linux_dmabuf_v1_add_listener(xwl_screen->dmabuf, &xwl_dmabuf_listener, xwl_screen);
 
+    /* If the compositor supports it, request the default feedback hints */
+    if (version >= 4) {
+        xwl_screen->default_feedback.dmabuf_feedback =
+            zwp_linux_dmabuf_v1_get_default_feedback(xwl_screen->dmabuf);
+        if (!xwl_screen->default_feedback.dmabuf_feedback)
+            return FALSE;
+
+        zwp_linux_dmabuf_feedback_v1_add_listener(xwl_screen->default_feedback.dmabuf_feedback,
+                                                  &xwl_dmabuf_feedback_listener,
+                                                  &xwl_screen->default_feedback);
+    }
+
     return TRUE;
 }
 
commit 43b35b4efeddac3df180d38b3c4967a15de9e639
Author: Austin Shafer <ashafer at nvidia.com>
Date:   Tue Dec 20 12:15:41 2022 +0100

    xwayland: Make helper for returning a list of formats
    
    This adds xwl_get_formats
    
    Reviewed-by: Michel Dänzer <mdaenzer at redhat.com>
    
    [ Michel Dänzer:
    * Remove unused variable i from xwl_glamor_get_formats ]

diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index 1c7845ee6..988ab33ff 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -154,12 +154,32 @@ xwl_screen_get_main_dev(struct xwl_screen *xwl_screen)
         return xwl_screen->default_feedback.main_dev;
 }
 
+static Bool
+xwl_get_formats(struct xwl_format *format_array, int format_array_len,
+               uint32_t *num_formats, uint32_t **formats)
+{
+    *num_formats = 0;
+    *formats = NULL;
+
+    if (format_array_len == 0)
+       return TRUE;
+
+    *formats = calloc(format_array_len, sizeof(CARD32));
+    if (*formats == NULL)
+        return FALSE;
+
+    for (int i = 0; i < format_array_len; i++)
+       (*formats)[i] = format_array[i].format;
+    *num_formats = format_array_len;
+
+    return TRUE;
+}
+
 Bool
 xwl_glamor_get_formats(ScreenPtr screen,
                        CARD32 *num_formats, CARD32 **formats)
 {
     struct xwl_screen *xwl_screen = xwl_screen_get(screen);
-    int i;
 
     /* Explicitly zero the count as the caller may ignore the return value */
     *num_formats = 0;
@@ -167,18 +187,8 @@ xwl_glamor_get_formats(ScreenPtr screen,
     if (!xwl_screen->dmabuf)
         return FALSE;
 
-    if (xwl_screen->num_formats == 0)
-       return TRUE;
-
-    *formats = calloc(xwl_screen->num_formats, sizeof(CARD32));
-    if (*formats == NULL)
-        return FALSE;
-
-    for (i = 0; i < xwl_screen->num_formats; i++)
-       (*formats)[i] = xwl_screen->formats[i].format;
-    *num_formats = xwl_screen->num_formats;
-
-    return TRUE;
+    return xwl_get_formats(xwl_screen->formats, xwl_screen->num_formats,
+                           num_formats, formats);
 }
 
 static Bool
commit d2e107b260a87e577827ef02c4970a33cb95cc92
Author: Austin Shafer <ashafer at nvidia.com>
Date:   Tue Dec 20 12:15:34 2022 +0100

    xwayland: Add get_drawable_modifiers implementation
    
    This reads from the format list, which is not yet filled in.
    
    Reviewed-by: Michel Dänzer <mdaenzer at redhat.com>

diff --git a/hw/xwayland/xwayland-glamor-eglstream.c b/hw/xwayland/xwayland-glamor-eglstream.c
index 04541bfc9..c911ed987 100644
--- a/hw/xwayland/xwayland-glamor-eglstream.c
+++ b/hw/xwayland/xwayland-glamor-eglstream.c
@@ -932,7 +932,7 @@ static const dri3_screen_info_rec xwl_dri3_info = {
     .open_client = xwl_dri3_open_client,
     .get_formats = xwl_glamor_get_formats,
     .get_modifiers = xwl_glamor_get_modifiers,
-    .get_drawable_modifiers = glamor_get_drawable_modifiers,
+    .get_drawable_modifiers = xwl_glamor_get_drawable_modifiers,
 };
 
 static Bool
diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c
index a29105f4d..205d2feb3 100644
--- a/hw/xwayland/xwayland-glamor-gbm.c
+++ b/hw/xwayland/xwayland-glamor-gbm.c
@@ -716,7 +716,7 @@ static const dri3_screen_info_rec xwl_dri3_info = {
     .open_client = xwl_dri3_open_client,
     .get_formats = xwl_glamor_get_formats,
     .get_modifiers = xwl_glamor_get_modifiers,
-    .get_drawable_modifiers = glamor_get_drawable_modifiers,
+    .get_drawable_modifiers = xwl_glamor_get_drawable_modifiers,
 };
 
 static const char *
diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index 2f0bc5275..1c7845ee6 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -141,6 +141,19 @@ wl_drm_format_for_depth(int depth)
     }
 }
 
+static dev_t
+xwl_screen_get_main_dev(struct xwl_screen *xwl_screen)
+{
+    /*
+     * If we have gbm then get our main device from it. Otherwise use what
+     * the compositor told us.
+     */
+    if (xwl_screen->gbm_backend.is_available)
+        return xwl_screen->gbm_backend.get_main_device(xwl_screen);
+    else
+        return xwl_screen->default_feedback.main_dev;
+}
+
 Bool
 xwl_glamor_get_formats(ScreenPtr screen,
                        CARD32 *num_formats, CARD32 **formats)
@@ -168,26 +181,22 @@ xwl_glamor_get_formats(ScreenPtr screen,
     return TRUE;
 }
 
-Bool
-xwl_glamor_get_modifiers(ScreenPtr screen, uint32_t format,
-                         uint32_t *num_modifiers, uint64_t **modifiers)
+static Bool
+xwl_get_modifiers_for_format(struct xwl_format *format_array, int num_formats,
+                             uint32_t format, uint32_t *num_modifiers, uint64_t **modifiers)
 {
-    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
     struct xwl_format *xwl_format = NULL;
     int i;
 
-    /* Explicitly zero the count as the caller may ignore the return value */
     *num_modifiers = 0;
+    *modifiers = NULL;
 
-    if (!xwl_screen->dmabuf)
-        return FALSE;
-
-    if (xwl_screen->num_formats == 0)
+    if (num_formats == 0)
        return TRUE;
 
-    for (i = 0; i < xwl_screen->num_formats; i++) {
-       if (xwl_screen->formats[i].format == format) {
-          xwl_format = &xwl_screen->formats[i];
+    for (i = 0; i < num_formats; i++) {
+       if (format_array[i].format == format) {
+          xwl_format = &format_array[i];
           break;
        }
     }
@@ -208,6 +217,82 @@ xwl_glamor_get_modifiers(ScreenPtr screen, uint32_t format,
     return TRUE;
 }
 
+static Bool
+xwl_get_modifiers_for_device(struct xwl_dmabuf_feedback *feedback, dev_t device,
+                             uint32_t format, uint32_t *num_modifiers,
+                             uint64_t **modifiers)
+{
+    struct xwl_device_formats *dev_formats = NULL;
+
+    /* Now try to find a matching set of tranches for the window's device */
+    for (int i = 0; i < feedback->dev_formats_len; i++) {
+        if (feedback->dev_formats[i].drm_dev == device)
+            dev_formats = &feedback->dev_formats[i];
+    }
+
+    if (!dev_formats)
+        return FALSE;
+
+    return xwl_get_modifiers_for_format(dev_formats->formats, dev_formats->num_formats,
+                                        format, num_modifiers, modifiers);
+}
+
+Bool
+xwl_glamor_get_modifiers(ScreenPtr screen, uint32_t format,
+                         uint32_t *num_modifiers, uint64_t **modifiers)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+    dev_t main_dev;
+
+    /* Explicitly zero the count as the caller may ignore the return value */
+    *num_modifiers = 0;
+    *modifiers = NULL;
+
+    if (!xwl_screen->dmabuf)
+        return FALSE;
+
+    if (xwl_screen->dmabuf_protocol_version >= 4) {
+        main_dev = xwl_screen_get_main_dev(xwl_screen);
+
+        return xwl_get_modifiers_for_device(&xwl_screen->default_feedback, main_dev,
+                                            format, num_modifiers, modifiers);
+    } else {
+        return xwl_get_modifiers_for_format(xwl_screen->formats, xwl_screen->num_formats,
+                                            format, num_modifiers, modifiers);
+    }
+}
+
+Bool
+xwl_glamor_get_drawable_modifiers(DrawablePtr drawable, uint32_t format,
+                                  uint32_t *num_modifiers, uint64_t **modifiers)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(drawable->pScreen);
+    struct xwl_window *xwl_window;
+    dev_t main_dev;
+
+    *num_modifiers = 0;
+    *modifiers = NULL;
+
+    /* We can only return per-drawable modifiers if the compositor supports feedback */
+    if (xwl_screen->dmabuf_protocol_version < 4)
+        return TRUE;
+
+    if (drawable->type != DRAWABLE_WINDOW || !xwl_screen->dmabuf)
+        return FALSE;
+
+    xwl_window = xwl_window_from_window((WindowPtr)drawable);
+
+    /* couldn't find drawable for window */
+    if (!xwl_window)
+        return FALSE;
+
+    main_dev = xwl_screen_get_main_dev(xwl_screen);
+
+    return xwl_get_modifiers_for_device(&xwl_window->feedback, main_dev,
+                                        format, num_modifiers, modifiers);
+
+}
+
 static void
 xwl_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
                          uint32_t format)
diff --git a/hw/xwayland/xwayland-glamor.h b/hw/xwayland/xwayland-glamor.h
index 216b4bf17..ed9ec40de 100644
--- a/hw/xwayland/xwayland-glamor.h
+++ b/hw/xwayland/xwayland-glamor.h
@@ -136,6 +136,8 @@ Bool xwl_glamor_get_formats(ScreenPtr screen,
                             CARD32 *num_formats, CARD32 **formats);
 Bool xwl_glamor_get_modifiers(ScreenPtr screen, uint32_t format,
                               uint32_t *num_modifiers, uint64_t **modifiers);
+Bool xwl_glamor_get_drawable_modifiers(DrawablePtr drawable, uint32_t format,
+                                       uint32_t *num_modifiers, uint64_t **modifiers);
 Bool xwl_glamor_check_flip(PixmapPtr pixmap);
 
 #ifdef XV
diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h
index 740e62455..88ea6011c 100644
--- a/hw/xwayland/xwayland-window.h
+++ b/hw/xwayland/xwayland-window.h
@@ -34,6 +34,7 @@
 #include <dix.h>
 #include <propertyst.h>
 #include <validate.h>
+#include <wayland-util.h>
 
 #include "xwayland-types.h"
 
commit 9f348077521a28272d79117eb06476f666419f32
Author: Austin Shafer <ashafer at nvidia.com>
Date:   Tue Dec 20 12:15:26 2022 +0100

    xwayland: Add get_main_device helper to GBM
    
    Reviewed-by: Michel Dänzer <mdaenzer at redhat.com>

diff --git a/hw/xwayland/xwayland-glamor-eglstream.c b/hw/xwayland/xwayland-glamor-eglstream.c
index 9f95e6c14..04541bfc9 100644
--- a/hw/xwayland/xwayland-glamor-eglstream.c
+++ b/hw/xwayland/xwayland-glamor-eglstream.c
@@ -1118,6 +1118,7 @@ xwl_glamor_init_eglstream(struct xwl_screen *xwl_screen)
     xwl_screen->eglstream_backend.post_damage = xwl_glamor_eglstream_post_damage;
     xwl_screen->eglstream_backend.allow_commits = xwl_glamor_eglstream_allow_commits;
     xwl_screen->eglstream_backend.check_flip = xwl_glamor_eglstream_check_flip;
+    xwl_screen->eglstream_backend.get_main_device = NULL;
     xwl_screen->eglstream_backend.is_available = TRUE;
     xwl_screen->eglstream_backend.backend_flags = XWL_EGL_BACKEND_NO_FLAG;
 }
diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c
index 93c15cac9..a29105f4d 100644
--- a/hw/xwayland/xwayland-glamor-gbm.c
+++ b/hw/xwayland/xwayland-glamor-gbm.c
@@ -53,6 +53,7 @@
 #include "linux-dmabuf-unstable-v1-client-protocol.h"
 
 struct xwl_gbm_private {
+    dev_t device;
     char *device_name;
     struct gbm_device *gbm;
     struct wl_drm *drm;
@@ -783,6 +784,7 @@ xwl_drm_handle_device(void *data, struct wl_drm *drm, const char *device)
    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
    drm_magic_t magic;
    char *render_node_path = NULL;
+   struct stat stat;
 
    if (!is_device_path_render_node(device))
        render_node_path = get_render_node_path(device);
@@ -805,6 +807,13 @@ xwl_drm_handle_device(void *data, struct wl_drm *drm, const char *device)
        return;
    }
 
+   if (fstat(xwl_gbm->drm_fd, &stat)) {
+       ErrorF("wayland-egl: Could not stat file %s (%s)\n",
+              xwl_gbm->device_name, strerror(errno));
+       return;
+   }
+   xwl_gbm->device = stat.st_rdev;
+
    if (drmGetNodeTypeFromFd(xwl_gbm->drm_fd) == DRM_NODE_RENDER) {
        xwl_gbm->fd_render_node = 1;
        xwl_screen->expecting_event--;
@@ -1102,6 +1111,13 @@ error:
     return FALSE;
 }
 
+static dev_t xwl_gbm_get_main_device(struct xwl_screen *xwl_screen)
+{
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+
+    return xwl_gbm->device;
+}
+
 void
 xwl_glamor_init_gbm(struct xwl_screen *xwl_screen)
 {
@@ -1130,6 +1146,7 @@ xwl_glamor_init_gbm(struct xwl_screen *xwl_screen)
     xwl_screen->gbm_backend.init_screen = xwl_glamor_gbm_init_screen;
     xwl_screen->gbm_backend.get_wl_buffer_for_pixmap = xwl_glamor_gbm_get_wl_buffer_for_pixmap;
     xwl_screen->gbm_backend.check_flip = NULL;
+    xwl_screen->gbm_backend.get_main_device = xwl_gbm_get_main_device;
     xwl_screen->gbm_backend.is_available = TRUE;
     xwl_screen->gbm_backend.backend_flags = XWL_EGL_BACKEND_NEEDS_BUFFER_FLUSH |
                                             XWL_EGL_BACKEND_NEEDS_N_BUFFERING;
diff --git a/hw/xwayland/xwayland-glamor.h b/hw/xwayland/xwayland-glamor.h
index 5c3661de3..216b4bf17 100644
--- a/hw/xwayland/xwayland-glamor.h
+++ b/hw/xwayland/xwayland-glamor.h
@@ -96,6 +96,11 @@ struct xwl_egl_backend {
      * presented by xwl_present_flip. If not implemented, assumed TRUE.
      */
     Bool (*check_flip)(PixmapPtr pixmap);
+
+    /* Called to get the dev_t of the primary GPU that this backend
+     * is set up on.
+     */
+    dev_t (*get_main_device)(struct xwl_screen *xwl_screen);
 };
 
 #ifdef XWL_HAS_GLAMOR
commit bddfe190de4dea9ca2d9cdaca5ed98caacbd014c
Author: Austin Shafer <ashafer at nvidia.com>
Date:   Tue Dec 20 12:15:15 2022 +0100

    xwayland: Implement linux_dmabuf_feedback event handlers
    
    Reviewed-by: Michel Dänzer <mdaenzer at redhat.com>
    
    [ Michel Dänzer:
    * Sort protocol #includes lexically.
    * memcpy to &xwl_feedback->main_dev directly in
      xwl_dmabuf_feedback_main_device. ]

diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index 326708f9a..2f0bc5275 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -44,6 +44,8 @@
 #include "xwayland-screen.h"
 #include "xwayland-window.h"
 
+#include <sys/mman.h>
+
 static void
 glamor_egl_make_current(struct glamor_context *glamor_ctx)
 {
@@ -266,15 +268,228 @@ static const struct zwp_linux_dmabuf_v1_listener xwl_dmabuf_listener = {
     .modifier = xwl_dmabuf_handle_modifier
 };
 
+/*
+ * We need to check if the compositor is resending all of the tranche
+ * information. Each tranche event will call this method to see
+ * if the existing format info should be cleared before refilling.
+ */
+static void
+xwl_check_reset_tranche_info(struct xwl_dmabuf_feedback *xwl_feedback)
+{
+    if (!xwl_feedback->feedback_done)
+        return;
+
+    xwl_feedback->feedback_done = false;
+
+    xwl_dmabuf_feedback_clear_dev_formats(xwl_feedback);
+}
+
+static void
+xwl_dmabuf_feedback_main_device(void *data,
+                                struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback,
+                                struct wl_array *dev)
+{
+    struct xwl_dmabuf_feedback *xwl_feedback = data;
+
+    xwl_check_reset_tranche_info(xwl_feedback);
+
+    assert(dev->size == sizeof(dev_t));
+    memcpy(&xwl_feedback->main_dev, dev->data, sizeof(dev_t));
+}
+
+static void
+xwl_dmabuf_feedback_tranche_target_device(void *data,
+                                          struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback,
+                                          struct wl_array *dev)
+{
+    struct xwl_dmabuf_feedback *xwl_feedback = data;
+
+    xwl_check_reset_tranche_info(xwl_feedback);
+
+    assert(dev->size == sizeof(dev_t));
+    memcpy(&xwl_feedback->tmp_tranche.drm_dev, dev->data, sizeof(dev_t));
+}
+
+static void
+xwl_dmabuf_feedback_tranche_flags(void *data,
+                                  struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback,
+                                  uint32_t flags)
+{
+    struct xwl_dmabuf_feedback *xwl_feedback = data;
+
+    xwl_check_reset_tranche_info(xwl_feedback);
+
+    if (flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT)
+        xwl_feedback->tmp_tranche.supports_scanout = true;
+}
+
+static void
+xwl_dmabuf_feedback_tranche_formats(void *data,
+                                    struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback,
+                                    struct wl_array *indices)
+{
+    struct xwl_dmabuf_feedback *xwl_feedback = data;
+    struct xwl_device_formats *tranche = &xwl_feedback->tmp_tranche;
+    uint16_t *index;
+
+    xwl_check_reset_tranche_info(xwl_feedback);
+
+    wl_array_for_each(index, indices) {
+        if (*index >= xwl_feedback->format_table.len) {
+            ErrorF("linux_dmabuf_feedback.tranche_formats: Index given to us by the compositor"
+                   " is too large to fit in the format table\n");
+            continue;
+        }
+
+        /* Look up this format/mod in the format table */
+        struct xwl_format_table_entry *entry = &xwl_feedback->format_table.entry[*index];
+
+        /* Add it to the in-progress tranche */
+        xwl_add_format_and_mod_to_list(&tranche->formats, &tranche->num_formats,
+                                       entry->format,
+                                       entry->modifier);
+    }
+}
+
+static void
+xwl_append_to_tranche(struct xwl_device_formats *dst, struct xwl_device_formats *src)
+{
+    struct xwl_format *format;
+
+    for (int i = 0; i < src->num_formats; i++) {
+        format = &src->formats[i];
+
+        for (int j = 0; j < format->num_modifiers; j++)
+            xwl_add_format_and_mod_to_list(&dst->formats, &dst->num_formats,
+                                           format->format,
+                                           format->modifiers[j]);
+    }
+}
+
+static void
+xwl_dmabuf_feedback_tranche_done(void *data,
+                                 struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback)
+{
+    struct xwl_dmabuf_feedback *xwl_feedback = data;
+    struct xwl_device_formats *tranche;
+    int appended = false;
+
+    /*
+     * No need to call xwl_check_reset_tranche_info, the other events should have been
+     * triggered first
+     */
+
+    /*
+     * First check if there is an existing tranche for this device+flags combo. We
+     * will combine it with this tranche, since we can only send one modifier list
+     * in DRI3 but the compositor may report multiple tranches per device (KDE
+     * does this)
+     */
+    for (int i = 0; i < xwl_feedback->dev_formats_len; i++) {
+        tranche = &xwl_feedback->dev_formats[i];
+        if (tranche->drm_dev == xwl_feedback->tmp_tranche.drm_dev &&
+            tranche->supports_scanout == xwl_feedback->tmp_tranche.supports_scanout) {
+            appended = true;
+
+            /* Add all format/mods to this tranche */
+            xwl_append_to_tranche(tranche, &xwl_feedback->tmp_tranche);
+
+            /* Now free our temp tranche's allocations */
+            xwl_device_formats_destroy(&xwl_feedback->tmp_tranche);
+
+            break;
+        }
+    }
+
+    if (!appended) {
+        xwl_feedback->dev_formats_len++;
+        xwl_feedback->dev_formats = xnfrealloc(xwl_feedback->dev_formats,
+                                               sizeof(struct xwl_device_formats) *
+                                               xwl_feedback->dev_formats_len);
+
+        /* copy the temporary tranche into the official array */
+        memcpy(&xwl_feedback->dev_formats[xwl_feedback->dev_formats_len - 1],
+               &xwl_feedback->tmp_tranche,
+               sizeof(struct xwl_device_formats));
+    }
+
+    /* reset the tranche */
+    memset(&xwl_feedback->tmp_tranche, 0, sizeof(struct xwl_device_formats));
+}
+
+static void
+xwl_dmabuf_feedback_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback)
+{
+    struct xwl_dmabuf_feedback *xwl_feedback = data;
+
+    xwl_feedback->feedback_done = true;
+}
+
+static void
+xwl_dmabuf_feedback_format_table(void *data,
+                                 struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1,
+                                 int32_t fd, uint32_t size)
+{
+    struct xwl_dmabuf_feedback *xwl_feedback = data;
+    /* Unmap the old table */
+    if (xwl_feedback->format_table.entry) {
+        munmap(xwl_feedback->format_table.entry,
+               xwl_feedback->format_table.len * sizeof(struct xwl_format_table_entry));
+    }
+
+    assert(size % sizeof(struct xwl_format_table_entry) == 0);
+    xwl_feedback->format_table.len = size / sizeof(struct xwl_format_table_entry);
+    xwl_feedback->format_table.entry = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+    close(fd);
+
+    if (xwl_feedback->format_table.entry == MAP_FAILED) {
+        ErrorF("linux_dmabuf_feedback.format_table: Could not map the format"
+               " table: Compositor bug or out of resources\n");
+        xwl_feedback->format_table.len = 0;
+    }
+}
+
+static const struct zwp_linux_dmabuf_feedback_v1_listener xwl_dmabuf_feedback_listener = {
+    .done = xwl_dmabuf_feedback_done,
+    .format_table = xwl_dmabuf_feedback_format_table,
+    .main_device = xwl_dmabuf_feedback_main_device,
+    .tranche_done = xwl_dmabuf_feedback_tranche_done,
+    .tranche_target_device = xwl_dmabuf_feedback_tranche_target_device,
+    .tranche_formats = xwl_dmabuf_feedback_tranche_formats,
+    .tranche_flags = xwl_dmabuf_feedback_tranche_flags,
+};
+
+Bool
+xwl_dmabuf_setup_feedback_for_window(struct xwl_window *xwl_window)
+{
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+
+    xwl_window->feedback.dmabuf_feedback =
+        zwp_linux_dmabuf_v1_get_surface_feedback(xwl_screen->dmabuf, xwl_window->surface);
+
+    if (!xwl_window->feedback.dmabuf_feedback)
+        return FALSE;
+
+    zwp_linux_dmabuf_feedback_v1_add_listener(xwl_window->feedback.dmabuf_feedback,
+                                              &xwl_dmabuf_feedback_listener,
+                                              &xwl_window->feedback);
+
+    return TRUE;
+}
+
 Bool
 xwl_screen_set_dmabuf_interface(struct xwl_screen *xwl_screen,
                                 uint32_t id, uint32_t version)
 {
+    /* We either support versions 3 or 4. 4 is needed for dmabuf feedback */
+    int supported_version = version >= 4 ? 4 : 3;
+
     if (version < 3)
         return FALSE;
 
     xwl_screen->dmabuf =
-        wl_registry_bind(xwl_screen->registry, id, &zwp_linux_dmabuf_v1_interface, 3);
+        wl_registry_bind(xwl_screen->registry, id, &zwp_linux_dmabuf_v1_interface, supported_version);
+    xwl_screen->dmabuf_protocol_version = supported_version;
     zwp_linux_dmabuf_v1_add_listener(xwl_screen->dmabuf, &xwl_dmabuf_listener, xwl_screen);
 
     return TRUE;
diff --git a/hw/xwayland/xwayland-glamor.h b/hw/xwayland/xwayland-glamor.h
index cf3c4fba3..5c3661de3 100644
--- a/hw/xwayland/xwayland-glamor.h
+++ b/hw/xwayland/xwayland-glamor.h
@@ -108,6 +108,7 @@ Bool xwl_glamor_init(struct xwl_screen *xwl_screen);
 
 Bool xwl_screen_set_drm_interface(struct xwl_screen *xwl_screen,
                                   uint32_t id, uint32_t version);
+Bool xwl_dmabuf_setup_feedback_for_window(struct xwl_window *xwl_window);
 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);
diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c
index 0e38c1cdf..a880406c9 100644
--- a/hw/xwayland/xwayland-screen.c
+++ b/hw/xwayland/xwayland-screen.c
@@ -185,6 +185,8 @@ xwl_close_screen(ScreenPtr screen)
     struct xwl_seat *xwl_seat, *next_xwl_seat;
     struct xwl_wl_surface *xwl_wl_surface, *xwl_wl_surface_next;
 
+    xwl_dmabuf_feedback_destroy(&xwl_screen->default_feedback);
+
     DeleteCallback(&PropertyStateCallback, xwl_property_callback, screen);
 
     xorg_list_for_each_entry_safe(xwl_output, next_xwl_output,
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index 8ccfc9b4d..fadd0526e 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -34,6 +34,7 @@
 #include <dix.h>
 
 #include "xwayland-types.h"
+#include "xwayland-window.h"
 #include "xwayland-output.h"
 #include "xwayland-glamor.h"
 #include "xwayland-drm-lease.h"
@@ -101,6 +102,8 @@ struct xwl_screen {
     struct zwp_keyboard_shortcuts_inhibit_manager_v1 *shortcuts_inhibit_manager;
     struct zwp_keyboard_shortcuts_inhibitor_v1 *shortcuts_inhibit;
     struct zwp_linux_dmabuf_v1 *dmabuf;
+    int dmabuf_protocol_version;
+    struct xwl_dmabuf_feedback default_feedback;
     struct zxdg_output_manager_v1 *xdg_output_manager;
     struct wp_viewporter *viewporter;
     struct xwayland_shell_v1 *xwayland_shell;
diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index 5c59caaab..941c1b01b 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -27,6 +27,8 @@
 #include <dix-config.h>
 #endif
 
+#include <sys/mman.h>
+
 #include <X11/X.h>
 #include <X11/Xatom.h>
 
@@ -43,6 +45,7 @@
 #include "xwayland-window-buffers.h"
 #include "xwayland-shm.h"
 
+#include "linux-dmabuf-unstable-v1-client-protocol.h"
 #include "viewporter-client-protocol.h"
 #include "xdg-shell-client-protocol.h"
 #include "xwayland-shell-v1-client-protocol.h"
@@ -835,6 +838,13 @@ ensure_surface_for_window(WindowPtr window)
     if (!xwl_screen->rootless && !xwl_create_root_surface(xwl_window))
         goto err;
 
+#ifdef XWL_HAS_GLAMOR
+    if (xwl_screen->dmabuf_protocol_version >= 4)
+        xwl_dmabuf_setup_feedback_for_window(xwl_window);
+#endif
+
+    wl_display_flush(xwl_screen->display);
+
     send_surface_id_event(xwl_window);
 
     wl_surface_set_user_data(xwl_window->surface, xwl_window);
@@ -990,6 +1000,42 @@ release_wl_surface_for_window(struct xwl_window *xwl_window)
         release_wl_surface_for_window_legacy_delay(xwl_window);
 }
 
+void
+xwl_device_formats_destroy(struct xwl_device_formats *dev_formats)
+{
+    for (int j = 0; j < dev_formats->num_formats; j++)
+        free(dev_formats->formats[j].modifiers);
+    free(dev_formats->formats);
+}
+
+void
+xwl_dmabuf_feedback_clear_dev_formats(struct xwl_dmabuf_feedback *xwl_feedback)
+{
+    if (xwl_feedback->dev_formats_len == 0)
+        return;
+
+    for (int i = 0; i < xwl_feedback->dev_formats_len; i++) {
+        struct xwl_device_formats *dev_format = &xwl_feedback->dev_formats[i];
+        xwl_device_formats_destroy(dev_format);
+    }
+    free(xwl_feedback->dev_formats);
+    xwl_feedback->dev_formats = NULL;
+    xwl_feedback->dev_formats_len = 0;
+}
+
+void
+xwl_dmabuf_feedback_destroy(struct xwl_dmabuf_feedback *xwl_feedback)
+{
+    munmap(xwl_feedback->format_table.entry,
+           xwl_feedback->format_table.len * sizeof(struct xwl_format_table_entry));
+    xwl_dmabuf_feedback_clear_dev_formats(xwl_feedback);
+
+    if (xwl_feedback->dmabuf_feedback)
+        zwp_linux_dmabuf_feedback_v1_destroy(xwl_feedback->dmabuf_feedback);
+
+    xwl_feedback->dmabuf_feedback = NULL;
+}
+
 Bool
 xwl_unrealize_window(WindowPtr window)
 {
@@ -1032,6 +1078,8 @@ xwl_unrealize_window(WindowPtr window)
     if (xwl_window_has_viewport_enabled(xwl_window))
         xwl_window_disable_viewport(xwl_window);
 
+    xwl_dmabuf_feedback_destroy(&xwl_window->feedback);
+
 #ifdef GLAMOR_HAS_GBM
     if (xwl_screen->present) {
         struct xwl_present_window *xwl_present_window, *tmp;
diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h
index fdeb80c7a..740e62455 100644
--- a/hw/xwayland/xwayland-window.h
+++ b/hw/xwayland/xwayland-window.h
@@ -43,6 +43,44 @@ struct xwl_wl_surface {
     struct xorg_list link;
 };
 
+struct xwl_format_table_entry {
+    uint32_t format;
+    uint32_t pad;
+    uint64_t modifier;
+};
+
+struct xwl_device_formats {
+    dev_t drm_dev;
+    int supports_scanout;
+    uint32_t num_formats;
+    struct xwl_format *formats;
+};
+
+struct xwl_format_table {
+    /* This is mmapped from the fd given to us by the compositor */
+    int len;
+    struct xwl_format_table_entry *entry;
+};
+
+/*
+ * Helper struct for sharing dmabuf feedback logic between
+ * a screen and a window. The screen will get the default
+ * feedback, and a window will get a per-surface feedback.
+ */
+struct xwl_dmabuf_feedback {
+    struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback;
+    struct xwl_format_table format_table;
+    dev_t main_dev;
+    /*
+     * This will be filled in during wl events and copied to
+     * dev_formats on dmabuf_feedback.tranche_done
+     */
+    struct xwl_device_formats tmp_tranche;
+    int feedback_done;
+    int dev_formats_len;
+    struct xwl_device_formats *dev_formats;
+};
+
 struct xwl_window {
     struct xwl_screen *xwl_screen;
     struct wl_surface *surface;
@@ -68,6 +106,7 @@ struct xwl_window {
     struct libdecor_frame *libdecor_frame;
 #endif
     struct xwayland_surface_v1 *xwayland_surface;
+    struct xwl_dmabuf_feedback feedback;
 };
 
 struct xwl_window *xwl_window_get(WindowPtr window);
@@ -102,4 +141,8 @@ void xwl_window_surface_do_destroy(struct xwl_wl_surface *xwl_wl_surface);
 
 Bool xwl_window_init(void);
 
+void xwl_dmabuf_feedback_destroy(struct xwl_dmabuf_feedback *xwl_feedback);
+void xwl_dmabuf_feedback_clear_dev_formats(struct xwl_dmabuf_feedback *xwl_feedback);
+void xwl_device_formats_destroy(struct xwl_device_formats *dev_formats);
+
 #endif /* XWAYLAND_WINDOW_H */
commit 2930eb113b81bdef57fc137e518dff9677478331
Author: Austin Shafer <ashafer at nvidia.com>
Date:   Tue Dec 20 12:15:08 2022 +0100

    xwayland: Move xwl_format array management to its own function
    
    This creates xwl_add_format_and_mod_to_list, which is a helper
    that adds a format/mod combo to a xwl_format* list. This will
    be used by both the modifier event handling and the tranche
    format handling.
    
    Reviewed-by: Michel Dänzer <mdaenzer at redhat.com>

diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index 24620605d..326708f9a 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -213,40 +213,52 @@ xwl_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
 }
 
 static void
-xwl_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
-                           uint32_t format, uint32_t modifier_hi,
-                           uint32_t modifier_lo)
+xwl_add_format_and_mod_to_list(struct xwl_format **formats,
+                               uint32_t *num_formats,
+                               uint32_t format,
+                               uint64_t modifier)
 {
-    struct xwl_screen *xwl_screen = data;
     struct xwl_format *xwl_format = NULL;
     int i;
 
-    for (i = 0; i < xwl_screen->num_formats; i++) {
-        if (xwl_screen->formats[i].format == format) {
-            xwl_format = &xwl_screen->formats[i];
+    for (i = 0; i < *num_formats; i++) {
+        if ((*formats)[i].format == format) {
+            xwl_format = &(*formats)[i];
             break;
         }
     }
 
     if (xwl_format == NULL) {
-        xwl_screen->num_formats++;
-        xwl_screen->formats = realloc(xwl_screen->formats,
-                                      xwl_screen->num_formats * sizeof(*xwl_format));
-        if (!xwl_screen->formats)
-            return;
-        xwl_format = &xwl_screen->formats[xwl_screen->num_formats - 1];
+        (*num_formats)++;
+        *formats = xnfrealloc(*formats, *num_formats * sizeof(*xwl_format));
+        xwl_format = &(*formats)[*num_formats - 1];
         xwl_format->format = format;
         xwl_format->num_modifiers = 0;
         xwl_format->modifiers = NULL;
     }
 
+    for (i = 0; i < xwl_format->num_modifiers; i++) {
+        /* don't add it if the modifier already exists */
+        if (xwl_format->modifiers[i] == modifier)
+            return;
+    }
+
     xwl_format->num_modifiers++;
-    xwl_format->modifiers = realloc(xwl_format->modifiers,
-                                    xwl_format->num_modifiers * sizeof(uint64_t));
-    if (!xwl_format->modifiers)
-        return;
-    xwl_format->modifiers[xwl_format->num_modifiers - 1]  = (uint64_t) modifier_lo;
-    xwl_format->modifiers[xwl_format->num_modifiers - 1] |= (uint64_t) modifier_hi << 32;
+    xwl_format->modifiers = xnfrealloc(xwl_format->modifiers,
+                                       xwl_format->num_modifiers * sizeof(uint64_t));
+    xwl_format->modifiers[xwl_format->num_modifiers - 1]  = modifier;
+}
+
+static void
+xwl_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
+                           uint32_t format, uint32_t modifier_hi,
+                           uint32_t modifier_lo)
+{
+    struct xwl_screen *xwl_screen = data;
+
+    xwl_add_format_and_mod_to_list(&xwl_screen->formats, &xwl_screen->num_formats,
+                                   format,
+                                   ((uint64_t)modifier_hi << 32 | (uint64_t)modifier_lo));
 }
 
 static const struct zwp_linux_dmabuf_v1_listener xwl_dmabuf_listener = {


More information about the xorg-commit mailing list