egl/wsi/wayland: Support wl-dmabuf v6

This commit is contained in:
Victoria Brekenfeld 2024-09-28 13:09:27 +02:00
parent 62f0fd1568
commit 4266c8a449
4 changed files with 183 additions and 49 deletions

View file

@ -283,8 +283,9 @@ struct dri2_egl_display {
struct wp_presentation *wp_presentation;
struct dri2_wl_formats formats;
struct zwp_linux_dmabuf_feedback_v1 *wl_dmabuf_feedback;
struct dmabuf_feedback_format_table format_table;
struct dmabuf_feedback dmabuf_feedback;
char *device_name;
dev_t display_device_dev;
bool is_render_node;
clockid_t presentation_clock_id;
#endif

View file

@ -37,6 +37,7 @@
#include <unistd.h>
#include <xf86drm.h>
#include "drm-uapi/drm_fourcc.h"
#include "util/u_dynarray.h"
#include <sys/mman.h>
#include <vulkan/vulkan_core.h>
#include <vulkan/vulkan_wayland.h>
@ -1205,8 +1206,8 @@ create_dri_image_from_dmabuf_feedback(struct dri2_egl_surface *dri2_surf,
{
uint32_t flags;
/* We don't have valid dma-buf feedback, so return */
if (dri2_surf->dmabuf_feedback.main_device == 0)
/* We don't have any tranches for our surface dma-buf feedback, so return */
if (util_dynarray_num_elements(&dri2_surf->dmabuf_feedback.tranches, struct dmabuf_feedback_tranche) == 0)
return;
/* Iterates through the dma-buf feedback to pick a new set of modifiers. The
@ -1697,6 +1698,16 @@ create_wl_buffer(struct dri2_egl_display *dri2_dpy,
if (dri2_surf)
wl_proxy_set_queue((struct wl_proxy *)params, dri2_surf->wl_queue);
if (wl_proxy_get_version((struct wl_proxy *)dri2_dpy->wl_dmabuf) >=
ZWP_LINUX_BUFFER_PARAMS_V1_SET_SAMPLING_DEVICE_SINCE_VERSION) {
struct wl_array dev;
wl_array_init(&dev);
wl_array_add(&dev, sizeof(dev_t));
memcpy(dev.data, &dri2_dpy->display_device_dev, sizeof(dev_t));
zwp_linux_buffer_params_v1_set_sampling_device(params, &dev);
wl_array_release(&dev);
}
for (i = 0; i < num_planes; i++) {
struct dri_image *p_image;
int stride, offset;
@ -2145,26 +2156,24 @@ default_dmabuf_feedback_format_table(
int32_t fd, uint32_t size)
{
struct dri2_egl_display *dri2_dpy = data;
struct dmabuf_feedback *feedback = &dri2_dpy->dmabuf_feedback;
dri2_dpy->format_table.size = size;
dri2_dpy->format_table.data =
feedback->format_table.size = size;
feedback->format_table.data =
mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
}
static void
default_dmabuf_feedback_main_device(
void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback,
struct wl_array *device)
dri2_wl_init_display_dev(
struct dri2_egl_display *dri2_dpy,
dev_t dev)
{
struct dri2_egl_display *dri2_dpy = data;
char *node;
int fd;
dev_t dev;
/* Given the device, look for a render node and try to open it. */
memcpy(&dev, device->data, sizeof(dev));
node = loader_get_render_node(dev);
if (!node)
return;
@ -2174,6 +2183,7 @@ default_dmabuf_feedback_main_device(
return;
}
dri2_dpy->display_device_dev = dev;
dri2_dpy->device_name = node;
dri2_dpy->fd_render_gpu = fd;
#ifdef HAVE_BIND_WL_DISPLAY
@ -2181,12 +2191,27 @@ default_dmabuf_feedback_main_device(
#endif
}
static void
default_dmabuf_feedback_main_device(
void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback,
struct wl_array *device)
{
struct dri2_egl_display *dri2_dpy = data;
struct dmabuf_feedback *feedback = &dri2_dpy->dmabuf_feedback;
memcpy(&feedback->main_device, device->data, sizeof(feedback->main_device));
dri2_wl_init_display_dev(dri2_dpy, feedback->main_device);
}
static void
default_dmabuf_feedback_tranche_target_device(
void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback,
struct wl_array *device)
{
/* ignore this event */
struct dri2_egl_display *dri2_dpy = data;
struct dmabuf_feedback *feedback = &dri2_dpy->dmabuf_feedback;
memcpy(&feedback->pending_tranche.target_device, device->data, sizeof(feedback->pending_tranche.target_device));
}
static void
@ -2194,7 +2219,10 @@ default_dmabuf_feedback_tranche_flags(
void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback,
uint32_t flags)
{
/* ignore this event */
struct dri2_egl_display *dri2_dpy = data;
struct dmabuf_feedback *feedback = &dri2_dpy->dmabuf_feedback;
feedback->pending_tranche.flags = flags;
}
static void
@ -2203,18 +2231,19 @@ default_dmabuf_feedback_tranche_formats(
struct wl_array *indices)
{
struct dri2_egl_display *dri2_dpy = data;
struct dmabuf_feedback *feedback = &dri2_dpy->dmabuf_feedback;
uint64_t *modifier_ptr, modifier;
uint32_t format;
uint16_t *index;
int visual_idx;
if (dri2_dpy->format_table.data == MAP_FAILED) {
if (feedback->format_table.data == MAP_FAILED) {
_eglLog(_EGL_WARNING, "wayland-egl: we could not map the format table "
"so we won't be able to use this batch of dma-buf "
"feedback events.");
return;
}
if (dri2_dpy->format_table.data == NULL) {
if (feedback->format_table.data == NULL) {
_eglLog(_EGL_WARNING,
"wayland-egl: compositor didn't advertise a format "
"table, so we won't be able to use this batch of dma-buf "
@ -2223,8 +2252,8 @@ default_dmabuf_feedback_tranche_formats(
}
wl_array_for_each (index, indices) {
format = dri2_dpy->format_table.data[*index].format;
modifier = dri2_dpy->format_table.data[*index].modifier;
format = feedback->format_table.data[*index].format;
modifier = feedback->format_table.data[*index].modifier;
/* skip formats that we don't support */
visual_idx = dri2_wl_visual_idx_from_fourcc(format);
@ -2242,15 +2271,20 @@ static void
default_dmabuf_feedback_tranche_done(
void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback)
{
/* ignore this event */
struct dri2_egl_display *dri2_dpy = data;
struct dmabuf_feedback *feedback = &dri2_dpy->dmabuf_feedback;
/* Add tranche to array of tranches. */
util_dynarray_append(&feedback->tranches,
feedback->pending_tranche);
dmabuf_feedback_tranche_init(&feedback->pending_tranche);
}
static void
default_dmabuf_feedback_done(
void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback)
{
/* ignore this event */
}
{}
static const struct zwp_linux_dmabuf_feedback_v1_listener
dmabuf_feedback_listener = {
@ -2292,7 +2326,7 @@ registry_handle_global_drm(void *data, struct wl_registry *registry,
version >= 3) {
dri2_dpy->wl_dmabuf = wl_registry_bind(
registry, name, &zwp_linux_dmabuf_v1_interface,
MIN2(version, ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION));
MIN2(version, ZWP_LINUX_BUFFER_PARAMS_V1_SET_SAMPLING_DEVICE_SINCE_VERSION));
zwp_linux_dmabuf_v1_add_listener(dri2_dpy->wl_dmabuf, &dmabuf_listener,
dri2_dpy);
} else if (strcmp(interface, wp_presentation_interface.name) == 0) {
@ -2590,7 +2624,9 @@ dri2_initialize_wayland_drm_extensions(struct dri2_egl_display *dri2_dpy)
if (dri2_dpy->wl_dmabuf &&
zwp_linux_dmabuf_v1_get_version(dri2_dpy->wl_dmabuf) >=
ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) {
dmabuf_feedback_format_table_init(&dri2_dpy->format_table);
if (dmabuf_feedback_init(&dri2_dpy->dmabuf_feedback) < 0) {
return false;
}
dri2_dpy->wl_dmabuf_feedback =
zwp_linux_dmabuf_v1_get_default_feedback(dri2_dpy->wl_dmabuf);
zwp_linux_dmabuf_feedback_v1_add_listener(
@ -2600,11 +2636,20 @@ dri2_initialize_wayland_drm_extensions(struct dri2_egl_display *dri2_dpy)
if (roundtrip(dri2_dpy) < 0)
return false;
/* Destroy the default dma-buf feedback and the format table. */
/* Destroy the default dma-buf feedback. */
if (dri2_dpy->wl_dmabuf_feedback) {
zwp_linux_dmabuf_feedback_v1_destroy(dri2_dpy->wl_dmabuf_feedback);
dri2_dpy->wl_dmabuf_feedback = NULL;
dmabuf_feedback_format_table_fini(&dri2_dpy->format_table);
/* For dmabuf v6 we don't get a main device, use the first tranch suitable for sampling as default */
if (dri2_dpy->fd_render_gpu == -1) {
util_dynarray_foreach(&dri2_dpy->dmabuf_feedback.tranches, struct dmabuf_feedback_tranche, tranche) {
if ((tranche->flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SAMPLING) != 0) {
dri2_wl_init_display_dev(dri2_dpy, tranche->target_device);
break;
}
}
}
}
#ifdef HAVE_BIND_WL_DISPLAY
@ -2633,6 +2678,8 @@ static EGLBoolean
dri2_initialize_wayland_drm(_EGLDisplay *disp)
{
struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
dev_t preferred_dev_id = 0;
drmDevicePtr render_dev = NULL, preferred_dev = NULL, tranche_dev = NULL;
if (dri2_wl_formats_init(&dri2_dpy->formats) < 0)
goto cleanup;
@ -2669,8 +2716,43 @@ dri2_initialize_wayland_drm(_EGLDisplay *disp)
if (!dri2_initialize_wayland_drm_extensions(dri2_dpy))
goto cleanup;
loader_get_user_preferred_fd(&dri2_dpy->fd_render_gpu,
&dri2_dpy->fd_display_gpu);
if (drmGetDevice2(dri2_dpy->fd_render_gpu, 0, &render_dev) != 0)
goto cleanup;
if (loader_get_user_preferred_device(render_dev, &preferred_dev_id))
{
bool found_matching_tranche = false;
if (drmGetDeviceFromDevId(preferred_dev_id, 0, &preferred_dev) < 0) {
goto cleanup;
}
/* check if the compositor can sample from the new device */
util_dynarray_foreach(&dri2_dpy->dmabuf_feedback.tranches, struct dmabuf_feedback_tranche, tranche) {
if ((tranche->flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SAMPLING) != 0) {
if (drmGetDeviceFromDevId(tranche->target_device, 0, &tranche_dev) < 0) {
continue;
}
if (!found_matching_tranche && loader_drm_devices_match(preferred_dev, tranche_dev)) {
close(dri2_dpy->fd_render_gpu);
dri2_wl_init_display_dev(dri2_dpy, tranche->target_device);
found_matching_tranche = true;
}
drmFreeDevice(&tranche_dev);
}
}
dri2_dpy->fd_display_gpu = dri2_dpy->fd_render_gpu;
if (!found_matching_tranche) {
// fallback to copying
dri2_dpy->fd_render_gpu = loader_open_device(preferred_dev->nodes[DRM_NODE_RENDER]);
}
drmFreeDevice(&preferred_dev);
} else {
dri2_dpy->fd_display_gpu = dri2_dpy->fd_render_gpu;
}
drmFreeDevice(&render_dev);
if (dri2_dpy->fd_render_gpu != dri2_dpy->fd_display_gpu) {
free(dri2_dpy->device_name);
@ -2753,6 +2835,9 @@ dri2_initialize_wayland_drm(_EGLDisplay *disp)
return EGL_TRUE;
cleanup:
if (render_dev) drmFreeDevice(&render_dev);
if (preferred_dev) drmFreeDevice(&preferred_dev);
if (tranche_dev) drmFreeDevice(&tranche_dev);
return EGL_FALSE;
}
@ -3259,7 +3344,6 @@ dri2_initialize_wayland(_EGLDisplay *disp)
void
dri2_teardown_wayland(struct dri2_egl_display *dri2_dpy)
{
dri2_wl_formats_fini(&dri2_dpy->formats);
if (dri2_dpy->wp_presentation)
wp_presentation_destroy(dri2_dpy->wp_presentation);
#ifdef HAVE_BIND_WL_DISPLAY
@ -3268,6 +3352,7 @@ dri2_teardown_wayland(struct dri2_egl_display *dri2_dpy)
#endif
if (dri2_dpy->wl_dmabuf)
zwp_linux_dmabuf_v1_destroy(dri2_dpy->wl_dmabuf);
dmabuf_feedback_fini(&dri2_dpy->dmabuf_feedback);
if (dri2_dpy->wl_shm)
wl_shm_destroy(dri2_dpy->wl_shm);
if (dri2_dpy->wl_registry)

View file

@ -37,6 +37,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <xf86drm.h>
#include <sys/param.h>
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
@ -48,7 +49,6 @@
#include "mesa_interface.h"
#include "loader.h"
#include "util/drm_is_nouveau.h"
#include "util/libdrm.h"
#include "util/os_file.h"
#include "util/os_misc.h"
#include "util/u_debug.h"
@ -448,13 +448,20 @@ static char *drm_get_id_path_tag_for_fd(int fd)
return tag;
}
bool loader_get_user_preferred_fd(int *fd_render_gpu, int *original_fd)
bool loader_drm_devices_match(drmDevicePtr dev1, drmDevicePtr dev2)
{
char *tag = drm_construct_id_path_tag(dev1);
return drm_device_matches_tag(dev2, tag);
}
bool loader_get_user_preferred_device(drmDevicePtr default_dev, dev_t *preferred_dev_id)
{
const char *dri_prime = os_get_option("DRI_PRIME");
bool debug = debug_get_bool_option("DRI_PRIME_DEBUG", false);
char *default_tag = NULL;
drmDevicePtr devices[MAX_DRM_DEVICES];
int i, num_devices, fd = -1;
int i, num_devices = -1;
struct stat sbuf;
struct {
enum {
PRIME_IS_INTEGER,
@ -500,7 +507,7 @@ bool loader_get_user_preferred_fd(int *fd_render_gpu, int *original_fd)
}
}
default_tag = drm_get_id_path_tag_for_fd(*fd_render_gpu);
default_tag = drm_construct_id_path_tag(default_dev);
if (default_tag == NULL)
goto err;
@ -605,20 +612,58 @@ bool loader_get_user_preferred_fd(int *fd_render_gpu, int *original_fd)
log_(debug ? _LOADER_WARNING : _LOADER_INFO,
"selected (%s)\n", devices[i]->nodes[DRM_NODE_RENDER]);
fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]);
if (fd < 0) {
log_(debug ? _LOADER_WARNING : _LOADER_INFO,
"DRI_PRIME: failed to open '%s'\n",
devices[i]->nodes[DRM_NODE_RENDER]);
char *preferred_node = devices[i]->nodes[DRM_NODE_RENDER];
if (stat(preferred_node, &sbuf) != 0) {
goto err;
}
*preferred_dev_id = sbuf.st_rdev;
break;
}
drmFreeDevices(devices, num_devices);
if (i == num_devices || fd < 0)
if (i == num_devices)
goto err;
bool is_render_and_display_gpu_diff = !!strcmp(default_tag, prime.str);
free(default_tag);
free(prime.str);
return is_render_and_display_gpu_diff;
err:
log_(debug ? _LOADER_WARNING : _LOADER_INFO,
"DRI_PRIME: error. Using the default GPU\n");
free(default_tag);
free(prime.str);
no_prime_gpu_offloading:
return false;
}
bool loader_get_user_preferred_fd(int *fd_render_gpu, int *original_fd)
{
int fd = -1;
bool is_render_and_display_gpu_diff;
dev_t preferred_dev_id = 0;
drmDevicePtr render_dev, preferred_dev;
bool debug = debug_get_bool_option("DRI_PRIME_DEBUG", false);
if (drmGetDevice2(*fd_render_gpu, 0, &render_dev) != 0)
return false;
is_render_and_display_gpu_diff = loader_get_user_preferred_device(render_dev, &preferred_dev_id);
if (drmGetDeviceFromDevId(preferred_dev_id, 0, &preferred_dev) != 0) {
drmFreeDevice(&render_dev);
return false;
};
fd = loader_open_device(preferred_dev->nodes[DRM_NODE_RENDER]);
if (fd < 0) {
log_(debug ? _LOADER_WARNING : _LOADER_INFO,
"DRI_PRIME: failed to open '%s'\n",
render_dev->nodes[DRM_NODE_RENDER]);
drmFreeDevice(&render_dev);
drmFreeDevice(&preferred_dev);
return false;
}
if (original_fd) {
if (is_render_and_display_gpu_diff) {
*original_fd = *fd_render_gpu;
@ -632,18 +677,9 @@ bool loader_get_user_preferred_fd(int *fd_render_gpu, int *original_fd)
*fd_render_gpu = fd;
}
free(default_tag);
free(prime.str);
drmFreeDevice(&render_dev);
drmFreeDevice(&preferred_dev);
return is_render_and_display_gpu_diff;
err:
log_(debug ? _LOADER_WARNING : _LOADER_INFO,
"DRI_PRIME: error. Using the default GPU\n");
free(default_tag);
free(prime.str);
no_prime_gpu_offloading:
if (original_fd)
*original_fd = *fd_render_gpu;
return false;
}
static bool

View file

@ -30,6 +30,7 @@
#include <stdbool.h>
#include <sys/stat.h>
#include <stddef.h>
#include <xf86drm.h>
#include "mesa_interface.h"
#ifdef __cplusplus
@ -75,6 +76,9 @@ loader_open_driver_lib(const char *driver_name,
char *
loader_get_device_name_for_fd(int fd);
bool
loader_drm_devices_match(drmDevicePtr dev1, drmDevicePtr dev2);
/* For dri prime gpu offloading this function will take current render fd and possibly
* update it with new prime gpu offloading fd. For dri prime gpu offloading optionally
* this function can return the original fd. Also this function returns true/false based
@ -84,6 +88,14 @@ loader_get_device_name_for_fd(int fd);
bool
loader_get_user_preferred_fd(int *fd_render_gpu, int *original_fd);
/* For dri prime offloading this function will take a default device and possibly set
* the user preferred device for prime gpu offloading. This function returns true/false
* based on if the default device is different from the preferred one.
*/
bool
loader_get_user_preferred_device(drmDevicePtr default_dev, dev_t *preferred_dev_id);
/* for logging.. keep this aligned with egllog.h so we can just use
* _eglLog directly.
*/