diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h index 04c9d4840db..bd84042214d 100644 --- a/src/egl/drivers/dri2/egl_dri2.h +++ b/src/egl/drivers/dri2/egl_dri2.h @@ -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 diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c index af4fe2cd513..04aedc48414 100644 --- a/src/egl/drivers/dri2/platform_wayland.c +++ b/src/egl/drivers/dri2/platform_wayland.c @@ -37,6 +37,7 @@ #include #include #include "drm-uapi/drm_fourcc.h" +#include "util/u_dynarray.h" #include #include #include @@ -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) diff --git a/src/loader/loader.c b/src/loader/loader.c index 0567beb3dee..5827fe0b2b2 100644 --- a/src/loader/loader.c +++ b/src/loader/loader.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #ifdef MAJOR_IN_MKDEV #include @@ -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 diff --git a/src/loader/loader.h b/src/loader/loader.h index 6748cb5269d..81dc60d7d23 100644 --- a/src/loader/loader.h +++ b/src/loader/loader.h @@ -30,6 +30,7 @@ #include #include #include +#include #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. */