From f3fe6b9a4394503f18d1c602768c32ac86b153b0 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 18 Jun 2025 13:30:21 +0200 Subject: [PATCH 01/17] util/box: set dest to empty if boxes don't intersect Currently if both box_a and box_b are non-empty but do not intersect, this function does not set dest to an empty box. This contradicts the doc comments and is surprising for users. (cherry picked from commit f5e7caf59994cfa08650cade41374e23779a24a4) --- util/box.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/util/box.c b/util/box.c index 6a6becf33..a9b42579a 100644 --- a/util/box.c +++ b/util/box.c @@ -67,7 +67,12 @@ bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a, dest->width = x2 - x1; dest->height = y2 - y1; - return !wlr_box_empty(dest); + if (wlr_box_empty(dest)) { + *dest = (struct wlr_box){0}; + return false; + } + + return true; } bool wlr_box_contains_point(const struct wlr_box *box, double x, double y) { From 68dea559708e0aa8a311b1495cd40d1fcbc013ec Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 29 Jun 2025 10:57:56 +0200 Subject: [PATCH 02/17] render/egl: fix software rendering check Commit b4ce0d8b39a4 ("render/egl: accept negative DRM FD to select software rendering") added an EXT_device_drm check to figure out whether the user selected a device with a DRM FD or without one. However, for KMS-only devices, Mesa will never advertise the selected KMS node: https://gitlab.freedesktop.org/mesa/mesa/-/blob/3f1d40d230c98d4ca9d2e20b214723a7b7d265d2/src/egl/main/egldevice.c#L109 Instead, pass down a parameter to indicate whether a DRM FD was passed in. Fixes: b4ce0d8b39a4 ("render/egl: accept negative DRM FD to select software rendering") (cherry picked from commit 48bd1831feff89ac94aacc2218601c2b569ef70c) --- render/egl.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/render/egl.c b/render/egl.c index 07d3a1425..6f3e9c8ca 100644 --- a/render/egl.c +++ b/render/egl.c @@ -260,7 +260,8 @@ static struct wlr_egl *egl_create(void) { return egl; } -static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { +static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display, + bool allow_software) { egl->display = display; EGLint major, minor; @@ -326,9 +327,8 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { // The only way a non-DRM device is selected is when the user // explicitly picks software rendering - if (check_egl_ext(device_exts_str, "EGL_MESA_device_software") && - egl->exts.EXT_device_drm) { - if (env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) { + if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) { + if (allow_software || env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) { wlr_log(WLR_INFO, "Using software rendering"); } else { wlr_log(WLR_ERROR, "Software rendering detected, please use " @@ -382,7 +382,7 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { } static bool egl_init(struct wlr_egl *egl, EGLenum platform, - void *remote_display) { + void *remote_display, bool allow_software) { EGLint display_attribs[3] = {0}; size_t display_attribs_len = 0; @@ -401,7 +401,7 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, return false; } - if (!egl_init_display(egl, display)) { + if (!egl_init_display(egl, display, allow_software)) { if (egl->exts.KHR_display_reference) { eglTerminate(display); } @@ -556,6 +556,8 @@ static int open_render_node(int drm_fd) { } struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { + bool allow_software = drm_fd < 0; + struct wlr_egl *egl = egl_create(); if (egl == NULL) { wlr_log(WLR_ERROR, "Failed to create EGL context"); @@ -569,7 +571,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { */ EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd); if (egl_device != EGL_NO_DEVICE_EXT) { - if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device)) { + if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device, allow_software)) { wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_DEVICE_EXT"); return egl; } @@ -594,7 +596,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { goto error; } - if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device)) { + if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device, allow_software)) { wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR"); return egl; } @@ -633,7 +635,7 @@ struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display, return NULL; } - if (!egl_init_display(egl, display)) { + if (!egl_init_display(egl, display, true)) { free(egl); return NULL; } From 5aa8c192a51c3fd0c1c21e6c00ae04bf72ca4e8b Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 30 Jun 2025 15:45:03 +0100 Subject: [PATCH 03/17] scene: Block damage on single-pixel buffer textures We cache whether buffers are single-pixel buffers (and if so what color they are) to allow rendering optimizations. But this breaks if the client changes out the single-pixel buffer for one with a different color, because this updates the texture in-place instead of actually changing the buffer. We can fix this by blocking in-place texture updates for single pixel buffers. Original bug: https://codeberg.org/ifreund/waylock/issues/121 See also: !5092 (cherry picked from commit 58c3680d96bffa1b8f59c274050a6ecfa27e7c23) --- types/scene/surface.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index e5b3b0f7d..354b5ab92 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -96,8 +96,11 @@ static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buf return; } - assert(buffer->n_ignore_locks > 0); - buffer->n_ignore_locks--; + // If the buffer was a single-pixel buffer where we cached its color + // then it won't have been marked as damage-allowed. + if (buffer->n_ignore_locks > 0) { + buffer->n_ignore_locks--; + } } static int min(int a, int b) { @@ -165,7 +168,22 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { scene_buffer_unmark_client_buffer(scene_buffer); if (surface->buffer) { - client_buffer_mark_next_can_damage(surface->buffer); + // If we've cached the buffer's single-pixel buffer color + // then any in-place updates to the texture wouldn't be + // reflected in rendering. So only allow in-place texture + // updates if it's not a single pixel buffer. Note that we + // can't use the cached scene_buffer->is_single_pixel_buffer + // because that's only set later on. + bool is_single_pixel_buffer = false; + struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(&surface->buffer->base); + if (client_buffer != NULL && client_buffer->source != NULL) { + struct wlr_single_pixel_buffer_v1 *spb = + wlr_single_pixel_buffer_v1_try_from_buffer(client_buffer->source); + is_single_pixel_buffer = spb != NULL; + } + if (!is_single_pixel_buffer) { + client_buffer_mark_next_can_damage(surface->buffer); + } struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = wlr_linux_drm_syncobj_v1_get_surface_state(surface); From f935404e68e48d781e3419ac7dd751698d44b16a Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 29 May 2025 17:38:32 +0800 Subject: [PATCH 04/17] render/pass: Ensure the precision is consistent during comparison (cherry picked from commit a08acfcee0261ae9b084c217dd70dd52eea2904a) --- render/pass.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render/pass.c b/render/pass.c index 902228273..23bdf96dd 100644 --- a/render/pass.c +++ b/render/pass.c @@ -21,8 +21,8 @@ void wlr_render_pass_add_texture(struct wlr_render_pass *render_pass, if (!wlr_fbox_empty(&options->src_box)) { const struct wlr_fbox *box = &options->src_box; assert(box->x >= 0 && box->y >= 0 && - box->x + box->width <= options->texture->width && - box->y + box->height <= options->texture->height); + (uint32_t)(box->x + box->width) <= options->texture->width && + (uint32_t)(box->y + box->height) <= options->texture->height); } render_pass->impl->add_texture(render_pass, options); From b7205866c071fe935b2f3bdb868d254a1de3b035 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 20 May 2025 01:14:16 +0900 Subject: [PATCH 05/17] backend/libinput: don't leak udev_device (cherry picked from commit 170f7e070603f0ecdadc4527c65bc08b62073e58) --- backend/libinput/tablet_pad.c | 1 + backend/libinput/tablet_tool.c | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/libinput/tablet_pad.c b/backend/libinput/tablet_pad.c index 98b3e4f4b..2fbfb6a6c 100644 --- a/backend/libinput/tablet_pad.c +++ b/backend/libinput/tablet_pad.c @@ -104,6 +104,7 @@ void init_device_tablet_pad(struct wlr_libinput_input_device *dev) { struct udev_device *udev = libinput_device_get_udev_device(handle); char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *)); *dst = strdup(udev_device_get_syspath(udev)); + udev_device_unref(udev); int groups = libinput_device_tablet_pad_get_num_mode_groups(handle); for (int i = 0; i < groups; ++i) { diff --git a/backend/libinput/tablet_tool.c b/backend/libinput/tablet_tool.c index eec697c49..d43c6cd0a 100644 --- a/backend/libinput/tablet_tool.c +++ b/backend/libinput/tablet_tool.c @@ -37,6 +37,7 @@ void init_device_tablet(struct wlr_libinput_input_device *dev) { struct udev_device *udev = libinput_device_get_udev_device(dev->handle); char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *)); *dst = strdup(udev_device_get_syspath(udev)); + udev_device_unref(udev); wl_list_init(&dev->tablet_tools); } From 30c06024574cf9a0d39e3b5830c45d593e49e23e Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Thu, 10 Jul 2025 19:46:17 +0200 Subject: [PATCH 06/17] transient_seat: initialize seat destroy listener This fixes a `wl_list_remove()` from an uninitialized listener when using `wlr_transient_seat_v1_deny()` in a `create_seat` handler. (cherry picked from commit c39b3ce7a3c4380d7a77f4cb97b3604c227c04d0) --- types/wlr_transient_seat_v1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/types/wlr_transient_seat_v1.c b/types/wlr_transient_seat_v1.c index af8f87ccf..d99ee6edb 100644 --- a/types/wlr_transient_seat_v1.c +++ b/types/wlr_transient_seat_v1.c @@ -64,6 +64,7 @@ static void manager_create_transient_seat(struct wl_client *client, wl_resource_set_implementation(seat->resource, &transient_seat_impl, seat, transient_seat_handle_resource_destroy); + wl_list_init(&seat->seat_destroy.link); wl_signal_emit_mutable(&manager->events.create_seat, seat); return; From 52e1ad01e3074b7022ac776faa94ca6e307ae4c7 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Wed, 23 Jul 2025 15:05:54 +0000 Subject: [PATCH 07/17] ext-image-capture-source: output: Apply transform to cursor The cursor can be expected to also be transformed if the output is transformed. (cherry picked from commit 80c7e0f772e38f56376e7953b148b480ca49ef3a) --- types/ext_image_capture_source_v1/output.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index e5ef78f93..e661aad3a 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -283,7 +283,8 @@ static void output_cursor_source_copy_frame(struct wlr_ext_image_capture_source_ struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_ext_image_copy_capture_frame_v1_ready(frame, WL_OUTPUT_TRANSFORM_NORMAL, &now); + wlr_ext_image_copy_capture_frame_v1_ready(frame, + cursor_source->output->transform, &now); } static const struct wlr_ext_image_capture_source_v1_interface output_cursor_source_impl = { From aa904ccf06eb342ec323849bec95ff362d0db9b3 Mon Sep 17 00:00:00 2001 From: Jesper Jensen Date: Tue, 5 Aug 2025 16:16:50 +0200 Subject: [PATCH 08/17] output/cursor: Fix double cursor bug When we fail to render the cursor (in my case because the cursor is too large) we bail out of the output_cursor_attempt_hardware function. This causes output_cursor_set_texture to clean up after us, but we've already cleared the hardware_cursor, and so output_disable_hardware_cursor thinks we don't have a hardware cursor to disable. We shouldn't modify the hardware_cursor variable before we've successfully changed the hardware cursor, this way the caller can clean up after us like it expect to. This was brought up by an actual bug when playing the game Kaizen. Which uses oddly sized cursors, that fell back to software cursors for me, and left the hardware cursor hanging around. This change has been tested to fix that. During the testing of this change, I have noticed that the previous code worked fine the first time the cursor was switch to software. It only failed on subsequent attempts. I haven't figured out why that is. (cherry picked from commit 07e92fb86816783acb5a08d628b961398216ab8e) --- types/output/cursor.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index 154b91120..b3ec152ce 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -298,8 +298,6 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { return false; } - output->hardware_cursor = NULL; - struct wlr_texture *texture = cursor->texture; // If the cursor was hidden or was a software cursor, the hardware From c1938f79c3448350436784bac602952ee8f6717a Mon Sep 17 00:00:00 2001 From: liupeng Date: Wed, 23 Jul 2025 10:53:42 +0800 Subject: [PATCH 09/17] cursor: update output cursor even if output is disabled During suspend, we first disable output and then remove the input device. This causes cursor->state->surface released while cursor->texture leaves. Which leads to use-after-free after resume. (cherry picked from commit be5e2662113ca40a21f48a5af35a219f50f28794) --- types/wlr_cursor.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 44b74e926..4eeb63cec 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -530,10 +530,6 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ struct wlr_cursor *cur = output_cursor->cursor; struct wlr_output *output = output_cursor->output_cursor->output; - if (!output->enabled) { - return; - } - cursor_output_cursor_reset_image(output_cursor); if (cur->state->buffer != NULL) { From 3118ca5c3ede0bb6f3dd55209dc3c61e761210cb Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 4 Jul 2025 15:34:14 -0400 Subject: [PATCH 10/17] drm-lease-v1: remove connector active_lease & lease connectors Upon leasing, the wlr_drm_lease_connector_v1 will be automatically clean up by the wlr_output destroy handler. There is no need for the wlr_drm_lease_manager to keep track of leased connectors. (cherry picked from commit 0166fd9eb778761295ea14fdff0515ada1a1cb17) --- include/wlr/types/wlr_drm_lease_v1.h | 5 ---- types/wlr_drm_lease_v1.c | 37 ---------------------------- 2 files changed, 42 deletions(-) diff --git a/include/wlr/types/wlr_drm_lease_v1.h b/include/wlr/types/wlr_drm_lease_v1.h index 752f6efa7..b29a5f6dc 100644 --- a/include/wlr/types/wlr_drm_lease_v1.h +++ b/include/wlr/types/wlr_drm_lease_v1.h @@ -62,8 +62,6 @@ struct wlr_drm_lease_connector_v1 { struct wlr_output *output; struct wlr_drm_lease_device_v1 *device; - /** NULL if no client is currently leasing this connector */ - struct wlr_drm_lease_v1 *active_lease; struct wl_list link; // wlr_drm_lease_device_v1.connectors @@ -93,9 +91,6 @@ struct wlr_drm_lease_v1 { struct wlr_drm_lease_device_v1 *device; - struct wlr_drm_lease_connector_v1 **connectors; - size_t n_connectors; - struct wl_list link; // wlr_drm_lease_device_v1.leases void *data; diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index c64bb0896..892a401b3 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -68,10 +68,6 @@ static void drm_lease_connector_v1_destroy( wlr_log(WLR_DEBUG, "Destroying connector %s", connector->output->name); - if (connector->active_lease) { - wlr_drm_lease_terminate(connector->active_lease->drm_lease); - } - struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &connector->resources) { wp_drm_lease_connector_v1_send_withdrawn(resource); @@ -140,14 +136,9 @@ static void lease_handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&lease->destroy.link); - for (size_t i = 0; i < lease->n_connectors; ++i) { - lease->connectors[i]->active_lease = NULL; - } - wl_list_remove(&lease->link); wl_resource_set_user_data(lease->resource, NULL); - free(lease->connectors); free(lease); } @@ -180,20 +171,6 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( return NULL; } - lease->connectors = calloc(request->n_connectors, sizeof(*lease->connectors)); - if (!lease->connectors) { - wlr_log(WLR_ERROR, "Failed to allocate lease connectors list"); - close(fd); - wp_drm_lease_v1_send_finished(lease->resource); - free(lease); - return NULL; - } - lease->n_connectors = request->n_connectors; - for (size_t i = 0; i < request->n_connectors; ++i) { - lease->connectors[i] = request->connectors[i]; - lease->connectors[i]->active_lease = lease; - } - lease->destroy.notify = lease_handle_destroy; wl_signal_add(&lease->drm_lease->events.destroy, &lease->destroy); @@ -338,16 +315,6 @@ static void drm_lease_request_v1_handle_submit( return; } - for (size_t i = 0; i < request->n_connectors; ++i) { - struct wlr_drm_lease_connector_v1 *conn = request->connectors[i]; - if (conn->active_lease) { - wlr_log(WLR_ERROR, "Failed to create lease, connector %s has " - "already been leased", conn->output->name); - wp_drm_lease_v1_send_finished(lease_resource); - return; - } - } - request->lease_resource = lease_resource; wl_signal_emit_mutable(&request->device->manager->events.request, @@ -440,10 +407,6 @@ static struct wp_drm_lease_connector_v1_interface lease_connector_impl = { static void drm_lease_connector_v1_send_to_client( struct wlr_drm_lease_connector_v1 *connector, struct wl_resource *resource) { - if (connector->active_lease) { - return; - } - struct wl_client *client = wl_resource_get_client(resource); uint32_t version = wl_resource_get_version(resource); From 907938049880011bbff45a614a4f23009e264175 Mon Sep 17 00:00:00 2001 From: liupeng Date: Sat, 30 Aug 2025 14:42:14 +0800 Subject: [PATCH 11/17] drm_lease_v1: initialize device resource link during abnormal exit Signed-off-by: liupeng (cherry picked from commit 5e5842cb1a8e11a7f274638e124ca473025734bc) --- types/wlr_drm_lease_v1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index 892a401b3..14846f21d 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -453,10 +453,12 @@ static void lease_device_bind(struct wl_client *wl_client, void *data, if (!device) { wlr_log(WLR_DEBUG, "Failed to bind lease device, " "the wlr_drm_lease_device_v1 has been destroyed"); + wl_list_init(wl_resource_get_link(device_resource)); return; } wl_resource_set_user_data(device_resource, device); + wl_list_insert(&device->resources, wl_resource_get_link(device_resource)); int fd = wlr_drm_backend_get_non_master_fd(device->backend); if (fd < 0) { @@ -468,8 +470,6 @@ static void lease_device_bind(struct wl_client *wl_client, void *data, wp_drm_lease_device_v1_send_drm_fd(device_resource, fd); close(fd); - wl_list_insert(&device->resources, wl_resource_get_link(device_resource)); - struct wlr_drm_lease_connector_v1 *connector; wl_list_for_each(connector, &device->connectors, link) { drm_lease_connector_v1_send_to_client(connector, device_resource); From 73aa61686fa75e8b9e5685fbac81b88130bcb441 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 7 Aug 2025 11:56:49 +0200 Subject: [PATCH 12/17] output/cursor: fix missing second cursor When attaching more than one cursor to wlr_output, the first one will pick the output's hardware cursor, then for the second one output_set_hardware_cursor() would fail (since the hardware cursor was already taken), but we still ended up resetting the current hardware cursor (by calling output_disable_hardware_cursor() below). As a result only the second cursor would be displayed. To fix this, move the current hardware cursor check to the caller. Fixes: 510664e79bfc ("output: disable hardware cursor when falling back to software") (cherry picked from commit fd069ad4f2793c812bd47e40f52f92eb8af7ca34) --- types/output/cursor.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index b3ec152ce..70647afb7 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -288,13 +288,7 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { struct wlr_output *output = cursor->output; - if (!output->impl->set_cursor || - output->software_cursor_locks > 0) { - return false; - } - - struct wlr_output_cursor *hwcur = output->hardware_cursor; - if (hwcur != NULL && hwcur != cursor) { + if (!output->impl->set_cursor || output->software_cursor_locks > 0) { return false; } @@ -422,12 +416,15 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, wl_list_init(&cursor->renderer_destroy.link); } - if (output_cursor_attempt_hardware(cursor)) { - return true; + if (output->hardware_cursor == NULL || output->hardware_cursor == cursor) { + if (output_cursor_attempt_hardware(cursor)) { + return true; + } + + wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name); + output_disable_hardware_cursor(output); } - wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name); - output_disable_hardware_cursor(output); output_cursor_damage_whole(cursor); return true; } From bb5180ce9e34815bc925245dc83eeaf3372fde89 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 3 Aug 2025 17:25:05 +0200 Subject: [PATCH 13/17] scene/surface: simplify single-pixel-buffer check in surface_reconfigure() No need to call wlr_client_buffer_get() on wlr_client_buffer.base: we're already manipulating a wlr_client_buffer. (cherry picked from commit b62c6878e116015d34827f66a8c4fee986e4ebfc) --- types/scene/surface.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index 354b5ab92..47977bf4b 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -175,10 +175,9 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { // can't use the cached scene_buffer->is_single_pixel_buffer // because that's only set later on. bool is_single_pixel_buffer = false; - struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(&surface->buffer->base); - if (client_buffer != NULL && client_buffer->source != NULL) { + if (surface->buffer->source != NULL) { struct wlr_single_pixel_buffer_v1 *spb = - wlr_single_pixel_buffer_v1_try_from_buffer(client_buffer->source); + wlr_single_pixel_buffer_v1_try_from_buffer(surface->buffer->source); is_single_pixel_buffer = spb != NULL; } if (!is_single_pixel_buffer) { From d092e40dec35ce501c6b7809bec4e1b5e08e4688 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 3 Aug 2025 17:28:13 +0200 Subject: [PATCH 14/17] scene/surface: fix NULL deref when source buffer is destroyed Fixes the following crash, witnessed after a GPU reset: #0 0x00007fba9a32774c n/a (libc.so.6 + 0x9774c) #1 0x00007fba9a2cddc0 raise (libc.so.6 + 0x3ddc0) #2 0x00007fba9a2b557a abort (libc.so.6 + 0x2557a) #3 0x00007fba9a2b54e3 n/a (libc.so.6 + 0x254e3) #4 0x00007fba9a53fb78 wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer (libwlroots-0.20.so + 0x26b78) #5 0x00007fba9a590846 surface_reconfigure (libwlroots-0.20.so + 0x77846) #6 0x00007fba9a590cbb scene_surface_set_clip (libwlroots-0.20.so + 0x77cbb) #7 0x00007fba9a590efa subsurface_tree_set_clip (libwlroots-0.20.so + 0x77efa) #8 0x00007fba9a590f1f subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f1f) #9 0x00007fba9a590f1f subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f1f) #10 0x00007fba9a590f8d wlr_scene_subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f8d) Reported-by: Hubert Hirtz (cherry picked from commit bd566225eacdda2b72b967fb5168314924871052) --- types/scene/surface.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index 47977bf4b..f0651bc38 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -203,7 +203,8 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { &surface->buffer->base, &options); if (syncobj_surface_state != NULL && - (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { + (surface->current.committed & WLR_SURFACE_STATE_BUFFER) && + surface->buffer->source != NULL) { wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, surface->buffer->source); } From d4009183a13511d336255c78f04a7d539f082770 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 3 Aug 2025 17:30:07 +0200 Subject: [PATCH 15/17] cursor: use source buffer to signal release timeline point Same as 128cd07e9156 ("scene/surface: use source buffer to signal release timeline point"), but for the cursor. (cherry picked from commit 462046ffdcdaacfc38ac606ed74397c360e23606) --- types/wlr_cursor.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 4eeb63cec..407d30620 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -585,10 +585,11 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ &src_box, dst_width, dst_height, surface->current.transform, hotspot_x, hotspot_y, wait_timeline, wait_point); - if (syncobj_surface_state != NULL && surface->buffer != NULL && + if (syncobj_surface_state != NULL && + surface->buffer != NULL && surface->buffer->source != NULL && (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, - &surface->buffer->base); + surface->buffer->source); } if (output_cursor->output_cursor->visible) { From 7a5278892979eaa86df29e77621c61ab94eca7da Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 8 Sep 2025 15:40:55 +0200 Subject: [PATCH 16/17] render/vulkan: Handle multi-descriptor sets A combined image sampler may need several descriptors in a descriptor set. We are not currently checking how many descriptors are required, nor is it presumably guaranteed that such multi-descriptor allocation will not fail due to fragmentation. If the pool free counter is not zero, try to allocate but continue with the next pool and fall back to creating a new pool if the allocation failed. Fixes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/4010 (cherry picked from commit dd7f5431891cad6b3980c8a7d5fdc1c1f668c1b1) --- render/vulkan/renderer.c | 109 ++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index b2feca013..1a696734a 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -66,59 +66,72 @@ static struct wlr_vk_descriptor_pool *alloc_ds( struct wl_list *pool_list, size_t *last_pool_size) { VkResult res; - bool found = false; - struct wlr_vk_descriptor_pool *pool; - wl_list_for_each(pool, pool_list, link) { - if (pool->free > 0) { - found = true; - break; - } - } - - if (!found) { // create new pool - pool = calloc(1, sizeof(*pool)); - if (!pool) { - wlr_log_errno(WLR_ERROR, "allocation failed"); - return NULL; - } - - size_t count = 2 * (*last_pool_size); - if (!count) { - count = start_descriptor_pool_size; - } - - pool->free = count; - VkDescriptorPoolSize pool_size = { - .descriptorCount = count, - .type = type, - }; - - VkDescriptorPoolCreateInfo dpool_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = count, - .poolSizeCount = 1, - .pPoolSizes = &pool_size, - .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, - }; - - res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL, - &pool->pool); - if (res != VK_SUCCESS) { - wlr_vk_error("vkCreateDescriptorPool", res); - free(pool); - return NULL; - } - - *last_pool_size = count; - wl_list_insert(pool_list, &pool->link); - } - VkDescriptorSetAllocateInfo ds_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .descriptorSetCount = 1, .pSetLayouts = layout, - .descriptorPool = pool->pool, }; + + struct wlr_vk_descriptor_pool *pool; + wl_list_for_each(pool, pool_list, link) { + if (pool->free > 0) { + ds_info.descriptorPool = pool->pool; + res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds); + switch (res) { + case VK_ERROR_FRAGMENTED_POOL: + case VK_ERROR_OUT_OF_POOL_MEMORY: + // Descriptor sets with more than one descriptor can cause us + // to run out of pool memory early or lead to fragmentation + // that makes the pool unable to service our allocation + // request. Try the next pool or allocate a new one. + continue; + case VK_SUCCESS: + --pool->free; + return pool; + default: + wlr_vk_error("vkAllocateDescriptorSets", res); + return NULL; + } + } + } + + pool = calloc(1, sizeof(*pool)); + if (!pool) { + wlr_log_errno(WLR_ERROR, "allocation failed"); + return NULL; + } + + size_t count = 2 * (*last_pool_size); + if (!count) { + count = start_descriptor_pool_size; + } + + pool->free = count; + VkDescriptorPoolSize pool_size = { + .descriptorCount = count, + .type = type, + }; + + VkDescriptorPoolCreateInfo dpool_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = count, + .poolSizeCount = 1, + .pPoolSizes = &pool_size, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + }; + + res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL, + &pool->pool); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateDescriptorPool", res); + free(pool); + return NULL; + } + + *last_pool_size = count; + wl_list_insert(pool_list, &pool->link); + + ds_info.descriptorPool = pool->pool; res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds); if (res != VK_SUCCESS) { wlr_vk_error("vkAllocateDescriptorSets", res); From 9b42c1901d967a6a05209de741131053722e9be1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 21 Sep 2025 23:03:29 +0200 Subject: [PATCH 17/17] build: bump version to 0.19.1 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 016e59842..24d812dc6 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.19.0', + version: '0.19.1', license: 'MIT', meson_version: '>=1.3', default_options: [