summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAustin Shafer <ashafer@nvidia.com>2022-12-20 12:15:15 +0100
committerMichel Dänzer <michel@daenzer.net>2023-01-20 17:56:54 +0000
commitbddfe190de4dea9ca2d9cdaca5ed98caacbd014c (patch)
treecac26f5e15f8ef5b7fb85f410469e23d07ea38dd
parent2930eb113b81bdef57fc137e518dff9677478331 (diff)
downloadxserver-bddfe190de4dea9ca2d9cdaca5ed98caacbd014c.tar.gz
xwayland: Implement linux_dmabuf_feedback event handlers
Reviewed-by: Michel Dänzer <mdaenzer@redhat.com> [ Michel Dänzer: * Sort protocol #includes lexically. * memcpy to &xwl_feedback->main_dev directly in xwl_dmabuf_feedback_main_device. ]
-rw-r--r--hw/xwayland/xwayland-glamor.c217
-rw-r--r--hw/xwayland/xwayland-glamor.h1
-rw-r--r--hw/xwayland/xwayland-screen.c2
-rw-r--r--hw/xwayland/xwayland-screen.h3
-rw-r--r--hw/xwayland/xwayland-window.c48
-rw-r--r--hw/xwayland/xwayland-window.h43
6 files changed, 313 insertions, 1 deletions
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 */