From bddfe190de4dea9ca2d9cdaca5ed98caacbd014c Mon Sep 17 00:00:00 2001 From: Austin Shafer Date: Tue, 20 Dec 2022 12:15:15 +0100 Subject: xwayland: Implement linux_dmabuf_feedback event handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Michel Dänzer [ Michel Dänzer: * Sort protocol #includes lexically. * memcpy to &xwl_feedback->main_dev directly in xwl_dmabuf_feedback_main_device. ] --- hw/xwayland/xwayland-glamor.c | 217 +++++++++++++++++++++++++++++++++++++++++- hw/xwayland/xwayland-glamor.h | 1 + hw/xwayland/xwayland-screen.c | 2 + hw/xwayland/xwayland-screen.h | 3 + hw/xwayland/xwayland-window.c | 48 ++++++++++ hw/xwayland/xwayland-window.h | 43 +++++++++ 6 files changed, 313 insertions(+), 1 deletion(-) 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 + 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 #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 #endif +#include + #include #include @@ -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 */ -- cgit v1.2.1