From 6c3d30c599823b5f71db00c6115a9f52e30ee163 Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Sat, 20 Jun 2026 22:00:32 +0200 Subject: [PATCH] wayland: Fix GPU acceleration --- .../native/meta-cursor-renderer-native.c | 24 +++++- src/backends/native/meta-drm-buffer-gbm.c | 85 +++++++++++++++++-- src/backends/native/meta-kms-impl-device.c | 5 ++ src/backends/native/meta-renderer-native.c | 39 +++++++-- src/meson.build | 3 + src/wayland/meta-wayland.c | 2 + 6 files changed, 145 insertions(+), 13 deletions(-) diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c index 942bfe0c8..ce4d9ff54 100644 --- a/src/backends/native/meta-cursor-renderer-native.c +++ b/src/backends/native/meta-cursor-renderer-native.c @@ -39,6 +39,7 @@ #include "backends/meta-monitor-manager-private.h" #include "backends/meta-output.h" #include "backends/native/meta-crtc-kms.h" +#include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-kms-device.h" #include "backends/native/meta-kms-update.h" #include "backends/native/meta-kms.h" @@ -98,6 +99,8 @@ typedef struct _MetaCursorRendererNativeGpuData uint64_t cursor_width; uint64_t cursor_height; + + struct gbm_device *kms_gbm_device; } MetaCursorRendererNativeGpuData; typedef enum _MetaCursorGbmBoState @@ -159,6 +162,15 @@ meta_cursor_renderer_native_gpu_data_from_gpu (MetaGpuKms *gpu_kms) quark_cursor_renderer_native_gpu_data); } +static void +cursor_renderer_native_gpu_data_free (gpointer data) +{ + MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data = data; + + g_clear_pointer (&cursor_renderer_gpu_data->kms_gbm_device, gbm_device_destroy); + g_free (cursor_renderer_gpu_data); +} + static MetaCursorRendererNativeGpuData * meta_create_cursor_renderer_native_gpu_data (MetaGpuKms *gpu_kms) { @@ -168,7 +180,7 @@ meta_create_cursor_renderer_native_gpu_data (MetaGpuKms *gpu_kms) g_object_set_qdata_full (G_OBJECT (gpu_kms), quark_cursor_renderer_native_gpu_data, cursor_renderer_gpu_data, - g_free); + cursor_renderer_native_gpu_data_free); return cursor_renderer_gpu_data; } @@ -1189,7 +1201,9 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native, return; } - gbm_device = meta_gbm_device_from_gpu (gpu_kms); + gbm_device = cursor_renderer_gpu_data->kms_gbm_device + ? cursor_renderer_gpu_data->kms_gbm_device + : meta_gbm_device_from_gpu (gpu_kms); if (gbm_device_is_format_supported (gbm_device, gbm_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) { @@ -1685,6 +1699,12 @@ init_hw_cursor_support_for_gpu (MetaGpuKms *gpu_kms) cursor_renderer_gpu_data->cursor_width = width; cursor_renderer_gpu_data->cursor_height = height; + + cursor_renderer_gpu_data->kms_gbm_device = + gbm_create_device (meta_gpu_kms_get_fd (gpu_kms)); + if (!cursor_renderer_gpu_data->kms_gbm_device) + g_warning ("Failed to create KMS GBM device for cursor allocation, " + "HW cursor may not work"); } static void diff --git a/src/backends/native/meta-drm-buffer-gbm.c b/src/backends/native/meta-drm-buffer-gbm.c index 85f5ed598..09a3d0e89 100644 --- a/src/backends/native/meta-drm-buffer-gbm.c +++ b/src/backends/native/meta-drm-buffer-gbm.c @@ -27,6 +27,9 @@ #include #include +#include +#include +#include #include #include @@ -57,6 +60,13 @@ meta_drm_buffer_gbm_get_bo (MetaDrmBufferGbm *buffer_gbm) return buffer_gbm->bo; } +static void +close_kms_handle (int kms_fd, uint32_t handle) +{ + struct drm_gem_close args = { .handle = handle }; + drmIoctl (kms_fd, DRM_IOCTL_GEM_CLOSE, &args); +} + static gboolean init_fb_id (MetaDrmBufferGbm *buffer_gbm, struct gbm_bo *bo, @@ -64,27 +74,79 @@ init_fb_id (MetaDrmBufferGbm *buffer_gbm, GError **error) { MetaGpuKmsFBArgs fb_args = { 0, }; + int gbm_fd = gbm_device_get_fd (gbm_bo_get_device (bo)); + int kms_fd = meta_gpu_kms_get_fd (buffer_gbm->gpu_kms); + gboolean need_prime = (gbm_fd != kms_fd); + uint32_t imported_handles[4] = { 0, 0, 0, 0 }; + int n_imported = 0; + int i; if (gbm_bo_get_handle_for_plane (bo, 0).s32 == -1) { /* Failed to fetch handle to plane, falling back to old method */ fb_args.strides[0] = gbm_bo_get_stride (bo); - fb_args.handles[0] = gbm_bo_get_handle (bo).u32; fb_args.offsets[0] = 0; fb_args.modifiers[0] = DRM_FORMAT_MOD_INVALID; + + if (need_prime) + { + int prime_fd = -1; + if (drmPrimeHandleToFD (gbm_fd, gbm_bo_get_handle (bo).u32, + DRM_CLOEXEC, &prime_fd) != 0 || + drmPrimeFDToHandle (kms_fd, prime_fd, &imported_handles[0]) != 0) + { + if (prime_fd >= 0) + close (prime_fd); + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), + "Failed to import GBM buffer via prime: %s", + g_strerror (errno)); + return FALSE; + } + close (prime_fd); + n_imported = 1; + fb_args.handles[0] = imported_handles[0]; + } + else + { + fb_args.handles[0] = gbm_bo_get_handle (bo).u32; + } } else { - int i; + int n_planes = gbm_bo_get_plane_count (bo); - for (i = 0; i < gbm_bo_get_plane_count (bo); i++) + for (i = 0; i < n_planes; i++) { fb_args.strides[i] = gbm_bo_get_stride_for_plane (bo, i); - fb_args.handles[i] = gbm_bo_get_handle_for_plane (bo, i).u32; fb_args.offsets[i] = gbm_bo_get_offset (bo, i); fb_args.modifiers[i] = gbm_bo_get_modifier (bo); + + if (need_prime) + { + int prime_fd = -1; + if (drmPrimeHandleToFD (gbm_fd, + gbm_bo_get_handle_for_plane (bo, i).u32, + DRM_CLOEXEC, &prime_fd) != 0 || + drmPrimeFDToHandle (kms_fd, prime_fd, + &imported_handles[i]) != 0) + { + if (prime_fd >= 0) + close (prime_fd); + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), + "Failed to import GBM buffer plane %d via prime: %s", + i, g_strerror (errno)); + goto out_close_imported; + } + close (prime_fd); + n_imported = i + 1; + fb_args.handles[i] = imported_handles[i]; + } + else + { + fb_args.handles[i] = gbm_bo_get_handle_for_plane (bo, i).u32; + } } - } + } fb_args.width = gbm_bo_get_width (bo); fb_args.height = gbm_bo_get_height (bo); @@ -94,8 +156,19 @@ init_fb_id (MetaDrmBufferGbm *buffer_gbm, use_modifiers, &fb_args, &buffer_gbm->fb_id, error)) - return FALSE; + goto out_close_imported; + + /* The kernel now holds a reference to the GEM objects via the FB; + * close our local imported handles. */ + for (i = 0; i < n_imported; i++) + close_kms_handle (kms_fd, imported_handles[i]); + return TRUE; + +out_close_imported: + for (i = 0; i < n_imported; i++) + close_kms_handle (kms_fd, imported_handles[i]); + return FALSE; } static gboolean diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c index 80d7a8b01..55aefd54f 100644 --- a/src/backends/native/meta-kms-impl-device.c +++ b/src/backends/native/meta-kms-impl-device.c @@ -417,6 +417,11 @@ meta_kms_impl_device_new (MetaKmsDevice *device, return NULL; } + ret = drmSetClientCap (fd, DRM_CLIENT_CAP_ATOMIC, 1); + if (ret != 0) + g_warning ("DRM_CLIENT_CAP_ATOMIC not supported on this kernel/device: %s", + g_strerror (-ret)); + drm_resources = drmModeGetResources (fd); if (!drm_resources) { diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index fd2272df3..b8e912046 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -102,6 +102,7 @@ typedef struct _MetaRendererNativeGpuData struct { struct gbm_device *device; + int fd; } gbm; #ifdef HAVE_EGL_DEVICE @@ -283,6 +284,8 @@ meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data meta_egl_terminate (egl, renderer_gpu_data->egl_display, NULL); g_clear_pointer (&renderer_gpu_data->gbm.device, gbm_device_destroy); + if (renderer_gpu_data->gbm.fd >= 0) + close (renderer_gpu_data->gbm.fd); g_free (renderer_gpu_data); } @@ -322,7 +325,9 @@ meta_renderer_native_get_primary_gpu (MetaRendererNative *renderer_native) static MetaRendererNativeGpuData * meta_create_renderer_native_gpu_data (MetaGpuKms *gpu_kms) { - return g_new0 (MetaRendererNativeGpuData, 1); + MetaRendererNativeGpuData *data = g_new0 (MetaRendererNativeGpuData, 1); + data->gbm.fd = -1; + return data; } static MetaEgl * @@ -3442,6 +3447,7 @@ init_secondary_gpu_data_gpu (MetaRendererNativeGpuData *renderer_gpu_data, } renderer_str = (const char *) glGetString (GL_RENDERER); + g_message ("GL renderer: %s", renderer_str); if (g_str_has_prefix (renderer_str, "llvmpipe") || g_str_has_prefix (renderer_str, "softpipe") || g_str_has_prefix (renderer_str, "swrast")) @@ -3569,12 +3575,34 @@ create_renderer_gpu_data_gbm (MetaRendererNative *renderer_native, { struct gbm_device *gbm_device; int kms_fd; + int gbm_fd = -1; + char *render_node_path; MetaRendererNativeGpuData *renderer_gpu_data; g_autoptr (GError) local_error = NULL; kms_fd = meta_gpu_kms_get_fd (gpu_kms); - gbm_device = gbm_create_device (kms_fd); + /* Open the render node directly so GBM gets a clean fd with no KMS client + * caps set on it. This matches how wlroots-based compositors initialize + * rendering and avoids hardware-specific issues with reusing the KMS fd. */ + render_node_path = drmGetRenderDeviceNameFromFd (kms_fd); + if (render_node_path) + { + do + gbm_fd = open (render_node_path, O_RDWR | O_CLOEXEC); + while (gbm_fd < 0 && errno == EINTR); + free (render_node_path); + } + + gbm_device = gbm_create_device (gbm_fd >= 0 ? gbm_fd : kms_fd); + if (!gbm_device && gbm_fd >= 0) + { + g_warning ("Failed to create gbm device from render node, retrying with primary fd"); + close (gbm_fd); + gbm_fd = -1; + gbm_device = gbm_create_device (kms_fd); + } + if (!gbm_device) { g_set_error (error, G_IO_ERROR, @@ -3586,6 +3614,7 @@ create_renderer_gpu_data_gbm (MetaRendererNative *renderer_native, renderer_gpu_data = meta_create_renderer_native_gpu_data (gpu_kms); renderer_gpu_data->renderer_native = renderer_native; renderer_gpu_data->gbm.device = gbm_device; + renderer_gpu_data->gbm.fd = gbm_fd; renderer_gpu_data->mode = META_RENDERER_NATIVE_MODE_GBM; renderer_gpu_data->egl_display = init_gbm_egl_display (renderer_native, @@ -3593,9 +3622,9 @@ create_renderer_gpu_data_gbm (MetaRendererNative *renderer_native, &local_error); if (renderer_gpu_data->egl_display == EGL_NO_DISPLAY) { - g_debug ("GBM EGL init for %s failed: %s", - meta_gpu_kms_get_file_path (gpu_kms), - local_error->message); + g_warning ("GBM EGL init for %s failed: %s", + meta_gpu_kms_get_file_path (gpu_kms), + local_error->message); init_secondary_gpu_data_cpu (renderer_gpu_data); return renderer_gpu_data; diff --git a/src/meson.build b/src/meson.build index ec51134cd..7508bfaa6 100644 --- a/src/meson.build +++ b/src/meson.build @@ -511,6 +511,8 @@ if have_wayland 'wayland/meta-wayland-data-source-primary.h', 'wayland/meta-wayland-dma-buf.c', 'wayland/meta-wayland-dma-buf.h', + 'wayland/meta-wayland-drm.c', + 'wayland/meta-wayland-drm.h', 'wayland/meta-wayland-dnd-surface.c', 'wayland/meta-wayland-dnd-surface.h', 'wayland/meta-wayland-gtk-shell.c', @@ -842,6 +844,7 @@ if have_wayland ['xdg-toplevel-icon-v1', 'private', ], ['xapp-shell', 'private', ], ['xwayland-keyboard-grab', 'unstable', 'v1', ], + ['wayland-drm', 'private', ], ] if have_wayland_eglstream wayland_eglstream_protocols_dir = wayland_eglstream_protocols_dep.get_variable(pkgconfig: 'pkgdatadir') diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index 825fe58dc..cafc78a48 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -36,6 +36,7 @@ #include "wayland/meta-wayland-cursor-shape.h" #include "wayland/meta-wayland-data-device.h" #include "wayland/meta-wayland-dma-buf.h" +#include "wayland/meta-wayland-drm.h" #include "wayland/meta-wayland-egl-stream.h" #include "wayland/meta-wayland-idle-inhibit.h" #include "wayland/meta-wayland-inhibit-shortcuts-dialog.h" @@ -444,6 +445,7 @@ meta_wayland_compositor_setup (MetaWaylandCompositor *wayland_compositor) meta_wayland_xdg_foreign_init (compositor); meta_wayland_legacy_xdg_foreign_init (compositor); meta_wayland_dma_buf_init (compositor); + meta_wayland_drm_init (compositor); meta_wayland_init_single_pixel_buffer_manager (compositor); meta_wayland_keyboard_shortcuts_inhibit_init (compositor); meta_wayland_surface_inhibit_shortcuts_dialog_init ();