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. ]
This commit is contained in:
Austin Shafer 2022-12-20 12:15:15 +01:00 committed by Michel Dänzer
parent 2930eb113b
commit bddfe190de
6 changed files with 313 additions and 1 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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 */