diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build index cd608e1e1..10a884e8b 100644 --- a/hw/xwayland/meson.build +++ b/hw/xwayland/meson.build @@ -104,7 +104,11 @@ endif xwayland_glamor = [] eglstream_srcs = [] if build_xwayland_glamor - srcs += 'xwayland-glamor.c' + srcs += [ + 'xwayland-glamor.c', + 'xwayland-dmabuf.h', + 'xwayland-dmabuf.c' + ] if gbm_dep.found() srcs += [ 'xwayland-glamor-gbm.c', diff --git a/hw/xwayland/xwayland-dmabuf.c b/hw/xwayland/xwayland-dmabuf.c new file mode 100644 index 000000000..7617a0e5f --- /dev/null +++ b/hw/xwayland/xwayland-dmabuf.c @@ -0,0 +1,804 @@ +/* + * Copyright © 2011-2014 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of the + * copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include + +#include + +#include +#include + +#include "xwayland-dmabuf.h" +#include "xwayland-screen.h" +#include "xwayland-types.h" +#include "xwayland-window-buffers.h" + +#include "drm-client-protocol.h" +#include "linux-dmabuf-unstable-v1-client-protocol.h" + +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); + drmFreeDevice(&dev_formats->drm_dev); +} + +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; +} + +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 < num_formats; i++) { + if (formats[i].format == format) { + xwl_format = &formats[i]; + break; + } + } + + if (xwl_format) { + for (i = 0; i < xwl_format->num_modifiers; i++) { + if (xwl_format->modifiers[i] == modifier) { + return TRUE; + } + } + } + + return FALSE; +} + +static Bool +xwl_feedback_is_modifier_supported(struct xwl_dmabuf_feedback *xwl_feedback, + uint32_t format, uint64_t modifier, + int supports_scanout) +{ + for (int i = 0; i < xwl_feedback->dev_formats_len; i++) { + struct xwl_device_formats *dev_formats = &xwl_feedback->dev_formats[i]; + + if (supports_scanout && !dev_formats->supports_scanout) + continue; + + 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, FALSE)) + 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, FALSE)) + return TRUE; + } + + return FALSE; +} + +uint32_t +wl_drm_format_for_depth(int depth) +{ + switch (depth) { + case 15: + return WL_DRM_FORMAT_XRGB1555; + case 16: + return WL_DRM_FORMAT_RGB565; + case 24: + return WL_DRM_FORMAT_XRGB8888; + case 30: + return WL_DRM_FORMAT_ARGB2101010; + default: + ErrorF("unexpected depth: %d\n", depth); + case 32: + return WL_DRM_FORMAT_ARGB8888; + } +} + +static drmDevice * +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; +} + +static Bool +xwl_dmabuf_get_formats(struct xwl_format *format_array, int format_array_len, + CARD32 *num_formats, CARD32 **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; +} + +static Bool +xwl_dmabuf_get_formats_for_device(struct xwl_dmabuf_feedback *xwl_feedback, drmDevice *device, + CARD32 *num_formats, CARD32 **formats) +{ + CARD32 *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 (drmDevicesEqual(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) +{ + struct xwl_screen *xwl_screen = xwl_screen_get(screen); + + /* Explicitly zero the count as the caller may ignore the return value */ + *num_formats = 0; + + if (!xwl_screen->dmabuf) + return FALSE; + + if (xwl_screen->dmabuf_protocol_version >= 4) { + drmDevice *main_dev = xwl_screen_get_main_dev(xwl_screen); + + return xwl_dmabuf_get_formats_for_device(&xwl_screen->default_feedback, main_dev, + num_formats, formats); + } + + return xwl_dmabuf_get_formats(xwl_screen->formats, xwl_screen->num_formats, + num_formats, formats); +} + +static Bool +xwl_dmabuf_get_modifiers_for_format(struct xwl_format *format_array, int num_formats, + uint32_t format, uint32_t *num_modifiers, + uint64_t **modifiers) +{ + struct xwl_format *xwl_format = NULL; + int i; + + *num_modifiers = 0; + *modifiers = NULL; + + if (num_formats == 0) + return TRUE; + + for (i = 0; i < num_formats; i++) { + if (format_array[i].format == format) { + xwl_format = &format_array[i]; + break; + } + } + + if (!xwl_format || + (xwl_format->num_modifiers == 1 && + xwl_format->modifiers[0] == DRM_FORMAT_MOD_INVALID)) + return FALSE; + + *modifiers = calloc(xwl_format->num_modifiers, sizeof(uint64_t)); + if (*modifiers == NULL) + return FALSE; + + for (i = 0; i < xwl_format->num_modifiers; i++) + (*modifiers)[i] = xwl_format->modifiers[i]; + *num_modifiers = xwl_format->num_modifiers; + + return TRUE; +} + +static Bool +xwl_dmabuf_get_modifiers_for_device(struct xwl_dmabuf_feedback *feedback, + drmDevice *device, + uint32_t format, uint32_t *num_modifiers, + uint64_t **modifiers, + Bool *supports_scanout) +{ + /* Now try to find a matching set of tranches for the window's device */ + for (int i = 0; i < feedback->dev_formats_len; i++) { + struct xwl_device_formats *dev_formats = &feedback->dev_formats[i]; + + if (drmDevicesEqual(dev_formats->drm_dev, device) && + xwl_dmabuf_get_modifiers_for_format(dev_formats->formats, + dev_formats->num_formats, + format, num_modifiers, modifiers)) { + if (supports_scanout) + *supports_scanout = !!dev_formats->supports_scanout; + return TRUE; + } + } + + return FALSE; +} + +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); + drmDevice *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_dmabuf_get_modifiers_for_device(&xwl_screen->default_feedback, main_dev, + format, num_modifiers, modifiers, NULL); + } else { + return xwl_dmabuf_get_modifiers_for_format(xwl_screen->formats, xwl_screen->num_formats, + format, num_modifiers, modifiers); + } +} + +Bool +xwl_glamor_get_drawable_modifiers_and_scanout(DrawablePtr drawable, + uint32_t format, + uint32_t *num_modifiers, + uint64_t **modifiers, + Bool *supports_scanout) +{ + struct xwl_screen *xwl_screen = xwl_screen_get(drawable->pScreen); + struct xwl_window *xwl_window; + drmDevice *main_dev; + + *num_modifiers = 0; + *modifiers = NULL; + if (supports_scanout) + *supports_scanout = FALSE; + + /* 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_dmabuf_get_modifiers_for_device(&xwl_window->feedback, main_dev, + format, num_modifiers, modifiers, + supports_scanout); + +} + +Bool +xwl_glamor_get_drawable_modifiers(DrawablePtr drawable, uint32_t format, + uint32_t *num_modifiers, uint64_t **modifiers) +{ + return xwl_glamor_get_drawable_modifiers_and_scanout(drawable, + format, num_modifiers, + modifiers, NULL); + +} + +static void +xwl_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *dmabuf, + uint32_t format) +{ +} + +static void +xwl_add_format_and_mod_to_list(struct xwl_format **formats, + uint32_t *num_formats, + uint32_t format, + uint64_t modifier) +{ + struct xwl_format *xwl_format = NULL; + int i; + + for (i = 0; i < *num_formats; i++) { + if ((*formats)[i].format == format) { + xwl_format = &(*formats)[i]; + break; + } + } + + if (xwl_format == NULL) { + (*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 = 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 = { + .format = xwl_dmabuf_handle_format, + .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; + dev_t devid; + + xwl_check_reset_tranche_info(xwl_feedback); + + assert(dev->size == sizeof(dev_t)); + memcpy(&devid, dev->data, sizeof(dev_t)); + + if (drmGetDeviceFromDevId(devid, 0, &xwl_feedback->main_dev) != 0) + ErrorF("linux_dmabuf_feedback.main_device: Failed to fetch DRM device\n"); +} + +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; + dev_t devid; + + xwl_check_reset_tranche_info(xwl_feedback); + + assert(dev->size == sizeof(dev_t)); + memcpy(&devid, dev->data, sizeof(dev_t)); + + if (drmGetDeviceFromDevId(devid, 0, &xwl_feedback->tmp_tranche.drm_dev) != 0) + ErrorF("linux_dmabuf_feedback.tranche_target_device: Failed to fetch DRM device\n"); +} + +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 + */ + + if (xwl_feedback->tmp_tranche.drm_dev == NULL) { + xwl_device_formats_destroy(&xwl_feedback->tmp_tranche); + goto out; + } + + /* + * 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)); + } + +out: + /* 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; + xwl_feedback->unprocessed_feedback_pending = 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_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, supported_version); + 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; +} + +static void +xwl_window_dmabuf_feedback_main_device(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, + struct wl_array *dev) +{ + struct xwl_window *xwl_window = data; + + xwl_dmabuf_feedback_main_device(&xwl_window->feedback, dmabuf_feedback, dev); +} + +static void +xwl_window_dmabuf_feedback_tranche_target_device(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, + struct wl_array *dev) +{ + struct xwl_window *xwl_window = data; + + xwl_dmabuf_feedback_tranche_target_device(&xwl_window->feedback, dmabuf_feedback, dev); +} + +static void +xwl_window_dmabuf_feedback_tranche_flags(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, + uint32_t flags) +{ + struct xwl_window *xwl_window = data; + + xwl_dmabuf_feedback_tranche_flags(&xwl_window->feedback, dmabuf_feedback, flags); +} + +static void +xwl_window_dmabuf_feedback_tranche_formats(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, + struct wl_array *indices) +{ + struct xwl_window *xwl_window = data; + + xwl_dmabuf_feedback_tranche_formats(&xwl_window->feedback, dmabuf_feedback, indices); +} + +static void +xwl_window_dmabuf_feedback_tranche_done(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) +{ + struct xwl_window *xwl_window = data; + + xwl_dmabuf_feedback_tranche_done(&xwl_window->feedback, dmabuf_feedback); +} + +static void +xwl_window_dmabuf_feedback_done(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) +{ + struct xwl_window *xwl_window = data; + uint32_t format = wl_drm_format_for_depth(xwl_window->window->drawable.depth); + + xwl_dmabuf_feedback_done(&xwl_window->feedback, dmabuf_feedback); + + xwl_window->has_implicit_scanout_support = + xwl_feedback_is_modifier_supported(&xwl_window->feedback, format, + DRM_FORMAT_MOD_INVALID, TRUE); + DebugF("XWAYLAND: Window 0x%x can%s get implicit scanout support\n", + xwl_window->window->drawable.id, + xwl_window->has_implicit_scanout_support ? "" : "not"); + + /* If the linux-dmabuf v4 per-surface feedback changed, make sure the + * window buffers get re-created with appropriate parameters. + */ + xwl_window_buffers_dispose(xwl_window); + xwl_window_realloc_pixmap(xwl_window); +} + +static void +xwl_window_dmabuf_feedback_format_table(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, + int32_t fd, uint32_t size) +{ + struct xwl_window *xwl_window = data; + + xwl_dmabuf_feedback_format_table(&xwl_window->feedback, dmabuf_feedback, fd, size); +} + +static const struct zwp_linux_dmabuf_feedback_v1_listener xwl_window_dmabuf_feedback_listener = { + .done = xwl_window_dmabuf_feedback_done, + .format_table = xwl_window_dmabuf_feedback_format_table, + .main_device = xwl_window_dmabuf_feedback_main_device, + .tranche_done = xwl_window_dmabuf_feedback_tranche_done, + .tranche_target_device = xwl_window_dmabuf_feedback_tranche_target_device, + .tranche_formats = xwl_window_dmabuf_feedback_tranche_formats, + .tranche_flags = xwl_window_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_window_dmabuf_feedback_listener, + xwl_window); + + return TRUE; +} diff --git a/hw/xwayland/xwayland-dmabuf.h b/hw/xwayland/xwayland-dmabuf.h new file mode 100644 index 000000000..ca0dcd706 --- /dev/null +++ b/hw/xwayland/xwayland-dmabuf.h @@ -0,0 +1,115 @@ +/* + * Copyright © 2011-2014 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of the + * copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef XWAYLAND_DMABUF_H +#define XWAYLAND_DMABUF_H + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "xwayland-types.h" + +struct xwl_format { + uint32_t format; + int num_modifiers; + uint64_t *modifiers; +}; + +struct xwl_format_table_entry { + uint32_t format; + uint32_t pad; + uint64_t modifier; +}; + +struct xwl_device_formats { + drmDevice *drm_dev; + uint32_t num_formats; + struct xwl_format *formats; + Bool supports_scanout; +}; + +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; + drmDevice *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; + /* + * 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; +}; + +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); + +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); +Bool xwl_glamor_is_modifier_supported(struct xwl_screen *xwl_screen, + uint32_t format, uint64_t modifier); +uint32_t wl_drm_format_for_depth(int depth); +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_and_scanout(DrawablePtr drawable, + uint32_t format, + uint32_t *num_modifiers, + uint64_t **modifiers, + Bool *supports_scanout); +Bool xwl_glamor_get_drawable_modifiers(DrawablePtr drawable, uint32_t format, + uint32_t *num_modifiers, uint64_t **modifiers); + +#endif /* XWAYLAND_DMABUF_H */ diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c index 28f848a3d..73ac9df49 100644 --- a/hw/xwayland/xwayland-glamor.c +++ b/hw/xwayland/xwayland-glamor.c @@ -40,9 +40,8 @@ #endif #include "linux-dmabuf-unstable-v1-client-protocol.h" -#include "drm-client-protocol.h" -#include +#include "xwayland-dmabuf.h" #include "xwayland-glamor.h" #include "xwayland-screen.h" #include "xwayland-window.h" @@ -104,733 +103,6 @@ xwl_glamor_check_flip(WindowPtr present_window, PixmapPtr pixmap) return TRUE; } -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 < num_formats; i++) { - if (formats[i].format == format) { - xwl_format = &formats[i]; - break; - } - } - - if (xwl_format) { - for (i = 0; i < xwl_format->num_modifiers; i++) { - if (xwl_format->modifiers[i] == modifier) { - return TRUE; - } - } - } - - return FALSE; -} - -static Bool -xwl_feedback_is_modifier_supported(struct xwl_dmabuf_feedback *xwl_feedback, - uint32_t format, uint64_t modifier, - int supports_scanout) -{ - for (int i = 0; i < xwl_feedback->dev_formats_len; i++) { - struct xwl_device_formats *dev_formats = &xwl_feedback->dev_formats[i]; - - if (supports_scanout && !dev_formats->supports_scanout) - continue; - - 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, FALSE)) - 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, FALSE)) - return TRUE; - } - - return FALSE; -} - -uint32_t -wl_drm_format_for_depth(int depth) -{ - switch (depth) { - case 15: - return WL_DRM_FORMAT_XRGB1555; - case 16: - return WL_DRM_FORMAT_RGB565; - case 24: - return WL_DRM_FORMAT_XRGB8888; - case 30: - return WL_DRM_FORMAT_ARGB2101010; - default: - ErrorF("unexpected depth: %d\n", depth); - case 32: - return WL_DRM_FORMAT_ARGB8888; - } -} - -static drmDevice * -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; -} - -static Bool -xwl_get_formats(struct xwl_format *format_array, int format_array_len, - CARD32 *num_formats, CARD32 **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; -} - -static Bool -xwl_get_formats_for_device(struct xwl_dmabuf_feedback *xwl_feedback, drmDevice *device, - CARD32 *num_formats, CARD32 **formats) -{ - CARD32 *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 (drmDevicesEqual(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) -{ - struct xwl_screen *xwl_screen = xwl_screen_get(screen); - - /* Explicitly zero the count as the caller may ignore the return value */ - *num_formats = 0; - - if (!xwl_screen->dmabuf) - return FALSE; - - if (xwl_screen->dmabuf_protocol_version >= 4) { - drmDevice *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); -} - -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_format *xwl_format = NULL; - int i; - - *num_modifiers = 0; - *modifiers = NULL; - - if (num_formats == 0) - return TRUE; - - for (i = 0; i < num_formats; i++) { - if (format_array[i].format == format) { - xwl_format = &format_array[i]; - break; - } - } - - if (!xwl_format || - (xwl_format->num_modifiers == 1 && - xwl_format->modifiers[0] == DRM_FORMAT_MOD_INVALID)) - return FALSE; - - *modifiers = calloc(xwl_format->num_modifiers, sizeof(uint64_t)); - if (*modifiers == NULL) - return FALSE; - - for (i = 0; i < xwl_format->num_modifiers; i++) - (*modifiers)[i] = xwl_format->modifiers[i]; - *num_modifiers = xwl_format->num_modifiers; - - return TRUE; -} - -static Bool -xwl_get_modifiers_for_device(struct xwl_dmabuf_feedback *feedback, drmDevice *device, - uint32_t format, uint32_t *num_modifiers, - uint64_t **modifiers, - Bool *supports_scanout) -{ - /* Now try to find a matching set of tranches for the window's device */ - for (int i = 0; i < feedback->dev_formats_len; i++) { - struct xwl_device_formats *dev_formats = &feedback->dev_formats[i]; - - if (drmDevicesEqual(dev_formats->drm_dev, device) && - xwl_get_modifiers_for_format(dev_formats->formats, dev_formats->num_formats, - format, num_modifiers, modifiers)) { - if (supports_scanout) - *supports_scanout = !!dev_formats->supports_scanout; - return TRUE; - } - } - - return FALSE; -} - -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); - drmDevice *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, - NULL); - } else { - return xwl_get_modifiers_for_format(xwl_screen->formats, xwl_screen->num_formats, - format, num_modifiers, modifiers); - } -} - -Bool -xwl_glamor_get_drawable_modifiers_and_scanout(DrawablePtr drawable, - uint32_t format, - uint32_t *num_modifiers, - uint64_t **modifiers, - Bool *supports_scanout) -{ - struct xwl_screen *xwl_screen = xwl_screen_get(drawable->pScreen); - struct xwl_window *xwl_window; - drmDevice *main_dev; - - *num_modifiers = 0; - *modifiers = NULL; - if (supports_scanout) - *supports_scanout = FALSE; - - /* 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, - supports_scanout); - -} - -Bool -xwl_glamor_get_drawable_modifiers(DrawablePtr drawable, uint32_t format, - uint32_t *num_modifiers, uint64_t **modifiers) -{ - return xwl_glamor_get_drawable_modifiers_and_scanout(drawable, - format, num_modifiers, - modifiers, NULL); - -} - -static void -xwl_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *dmabuf, - uint32_t format) -{ -} - -static void -xwl_add_format_and_mod_to_list(struct xwl_format **formats, - uint32_t *num_formats, - uint32_t format, - uint64_t modifier) -{ - struct xwl_format *xwl_format = NULL; - int i; - - for (i = 0; i < *num_formats; i++) { - if ((*formats)[i].format == format) { - xwl_format = &(*formats)[i]; - break; - } - } - - if (xwl_format == NULL) { - (*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 = 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 = { - .format = xwl_dmabuf_handle_format, - .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; - dev_t devid; - - xwl_check_reset_tranche_info(xwl_feedback); - - assert(dev->size == sizeof(dev_t)); - memcpy(&devid, dev->data, sizeof(dev_t)); - - if (drmGetDeviceFromDevId(devid, 0, &xwl_feedback->main_dev) != 0) - ErrorF("linux_dmabuf_feedback.main_device: Failed to fetch DRM device\n"); -} - -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; - dev_t devid; - - xwl_check_reset_tranche_info(xwl_feedback); - - assert(dev->size == sizeof(dev_t)); - memcpy(&devid, dev->data, sizeof(dev_t)); - - if (drmGetDeviceFromDevId(devid, 0, &xwl_feedback->tmp_tranche.drm_dev) != 0) - ErrorF("linux_dmabuf_feedback.tranche_target_device: Failed to fetch DRM device\n"); -} - -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 - */ - - if (xwl_feedback->tmp_tranche.drm_dev == NULL) { - xwl_device_formats_destroy(&xwl_feedback->tmp_tranche); - goto out; - } - - /* - * 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)); - } - -out: - /* 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; - xwl_feedback->unprocessed_feedback_pending = 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_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, supported_version); - 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; -} - -static void -xwl_window_dmabuf_feedback_main_device(void *data, - struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, - struct wl_array *dev) -{ - struct xwl_window *xwl_window = data; - - xwl_dmabuf_feedback_main_device(&xwl_window->feedback, dmabuf_feedback, dev); -} - -static void -xwl_window_dmabuf_feedback_tranche_target_device(void *data, - struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, - struct wl_array *dev) -{ - struct xwl_window *xwl_window = data; - - xwl_dmabuf_feedback_tranche_target_device(&xwl_window->feedback, dmabuf_feedback, dev); -} - -static void -xwl_window_dmabuf_feedback_tranche_flags(void *data, - struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, - uint32_t flags) -{ - struct xwl_window *xwl_window = data; - - xwl_dmabuf_feedback_tranche_flags(&xwl_window->feedback, dmabuf_feedback, flags); -} - -static void -xwl_window_dmabuf_feedback_tranche_formats(void *data, - struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, - struct wl_array *indices) -{ - struct xwl_window *xwl_window = data; - - xwl_dmabuf_feedback_tranche_formats(&xwl_window->feedback, dmabuf_feedback, indices); -} - -static void -xwl_window_dmabuf_feedback_tranche_done(void *data, - struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) -{ - struct xwl_window *xwl_window = data; - - xwl_dmabuf_feedback_tranche_done(&xwl_window->feedback, dmabuf_feedback); -} - -static void -xwl_window_dmabuf_feedback_done(void *data, - struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) -{ - struct xwl_window *xwl_window = data; - uint32_t format = wl_drm_format_for_depth(xwl_window->window->drawable.depth); - - xwl_dmabuf_feedback_done(&xwl_window->feedback, dmabuf_feedback); - - xwl_window->has_implicit_scanout_support = - xwl_feedback_is_modifier_supported(&xwl_window->feedback, format, - DRM_FORMAT_MOD_INVALID, TRUE); - DebugF("XWAYLAND: Window 0x%x can%s get implicit scanout support\n", - xwl_window->window->drawable.id, - xwl_window->has_implicit_scanout_support ? "" : "not"); - - /* If the linux-dmabuf v4 per-surface feedback changed, make sure the - * window buffers get re-created with appropriate parameters. - */ - xwl_window_buffers_dispose(xwl_window); - xwl_window_realloc_pixmap(xwl_window); -} - -static void -xwl_window_dmabuf_feedback_format_table(void *data, - struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, - int32_t fd, uint32_t size) -{ - struct xwl_window *xwl_window = data; - - xwl_dmabuf_feedback_format_table(&xwl_window->feedback, dmabuf_feedback, fd, size); -} - -static const struct zwp_linux_dmabuf_feedback_v1_listener xwl_window_dmabuf_feedback_listener = { - .done = xwl_window_dmabuf_feedback_done, - .format_table = xwl_window_dmabuf_feedback_format_table, - .main_device = xwl_window_dmabuf_feedback_main_device, - .tranche_done = xwl_window_dmabuf_feedback_tranche_done, - .tranche_target_device = xwl_window_dmabuf_feedback_tranche_target_device, - .tranche_formats = xwl_window_dmabuf_feedback_tranche_formats, - .tranche_flags = xwl_window_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_window_dmabuf_feedback_listener, - xwl_window); - - return TRUE; -} - void xwl_glamor_init_wl_registry(struct xwl_screen *xwl_screen, struct wl_registry *registry, diff --git a/hw/xwayland/xwayland-glamor.h b/hw/xwayland/xwayland-glamor.h index 5513230ff..cadc62f6f 100644 --- a/hw/xwayland/xwayland-glamor.h +++ b/hw/xwayland/xwayland-glamor.h @@ -126,9 +126,6 @@ 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); void xwl_glamor_init_wl_registry(struct xwl_screen *xwl_screen, struct wl_registry *registry, @@ -142,20 +139,6 @@ Bool xwl_glamor_allow_commits(struct xwl_window *xwl_window); void xwl_glamor_egl_make_current(struct xwl_screen *xwl_screen); Bool xwl_glamor_needs_buffer_flush(struct xwl_screen *xwl_screen); Bool xwl_glamor_needs_n_buffering(struct xwl_screen *xwl_screen); -Bool xwl_glamor_is_modifier_supported(struct xwl_screen *xwl_screen, - uint32_t format, uint64_t modifier); -uint32_t wl_drm_format_for_depth(int depth); -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_and_scanout(DrawablePtr drawable, - uint32_t format, - uint32_t *num_modifiers, - uint64_t **modifiers, - Bool *supports_scanout); -Bool xwl_glamor_get_drawable_modifiers(DrawablePtr drawable, uint32_t format, - uint32_t *num_modifiers, uint64_t **modifiers); Bool xwl_glamor_check_flip(WindowPtr present_window, PixmapPtr pixmap); PixmapPtr xwl_glamor_create_pixmap_for_window (struct xwl_window *xwl_window); diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c index 5c71b55e2..d40fc270a 100644 --- a/hw/xwayland/xwayland-screen.c +++ b/hw/xwayland/xwayland-screen.c @@ -219,9 +219,9 @@ xwl_close_screen(ScreenPtr screen) struct xwl_output *xwl_output, *next_xwl_output; struct xwl_seat *xwl_seat, *next_xwl_seat; struct xwl_wl_surface *xwl_wl_surface, *xwl_wl_surface_next; - +#ifdef XWL_HAS_GLAMOR xwl_dmabuf_feedback_destroy(&xwl_screen->default_feedback); - +#endif DeleteCallback(&PropertyStateCallback, xwl_property_callback, screen); XaceDeleteCallback(XACE_PROPERTY_ACCESS, xwl_access_property_callback, screen); diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h index 993b3cb12..63740a8e2 100644 --- a/hw/xwayland/xwayland-screen.h +++ b/hw/xwayland/xwayland-screen.h @@ -38,17 +38,12 @@ #include "xwayland-output.h" #include "xwayland-glamor.h" #include "xwayland-drm-lease.h" +#include "xwayland-dmabuf.h" #ifdef XWL_HAS_LIBDECOR #include #endif -struct xwl_format { - uint32_t format; - int num_modifiers; - uint64_t *modifiers; -}; - struct xwl_screen { int width; int height; diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c index c8b62db6e..fda98fa18 100644 --- a/hw/xwayland/xwayland-window.c +++ b/hw/xwayland/xwayland-window.c @@ -45,6 +45,7 @@ #include "xwayland-window.h" #include "xwayland-window-buffers.h" #include "xwayland-shm.h" +#include "xwayland-dmabuf.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" #include "tearing-control-v1-client-protocol.h" @@ -1097,43 +1098,6 @@ 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); - drmFreeDevice(&dev_formats->drm_dev); -} - -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) { @@ -1175,11 +1139,14 @@ xwl_unrealize_window(WindowPtr window) if (xwl_window_has_viewport_enabled(xwl_window)) xwl_window_disable_viewport(xwl_window); - +#ifdef XWL_HAS_GLAMOR xwl_dmabuf_feedback_destroy(&xwl_window->feedback); +#ifdef GLAMOR_HAS_GBM if (xwl_window->xwl_screen->present) xwl_present_for_each_frame_callback(xwl_window, xwl_present_unrealize_window); +#endif /* GLAMOR_HAS_GBM */ +#endif /* XWL_HAS_GLAMOR */ if (xwl_window->tearing_control) wp_tearing_control_v1_destroy(xwl_window->tearing_control); diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h index 03d3fed14..7c70bb9ed 100644 --- a/hw/xwayland/xwayland-window.h +++ b/hw/xwayland/xwayland-window.h @@ -41,6 +41,7 @@ #include #include "xwayland-types.h" +#include "xwayland-dmabuf.h" struct xwl_wl_surface { OsTimerPtr wl_surface_destroy_timer; @@ -48,51 +49,6 @@ 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 { - drmDevice *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; - drmDevice *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; - /* - * 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 { struct xwl_screen *xwl_screen; struct wl_surface *surface; @@ -155,8 +111,4 @@ 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 */