From 1152c53e58440c16a6c3b1078e6c0b977e4fb29e Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Tue, 10 Mar 2026 20:37:41 -0300 Subject: [PATCH] drm: handle client buffer destroyed while writeback scheduled Currently when a client buffer gets destroyed, the output capture task gets destroyed with weston_capture_task_buffer_destroy_handler(). The problem is that we may have a writeback task scheduled, so wb_state->ct would be pointing to a ct that has already been retired and destroyed. In this commit we start handling this case. Signed-off-by: Leandro Ribeiro --- libweston/backend-drm/drm-internal.h | 1 + libweston/backend-drm/drm.c | 49 ++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 07b00d803..d87e4883c 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -514,6 +514,7 @@ struct drm_writeback_state { enum writeback_screenshot_state state; struct weston_capture_task *ct; + struct wl_listener buffer_destroy_listener; struct drm_fb *fb; int32_t out_fence_fd; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 0e753b4f7..20c7e3251 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -730,9 +730,28 @@ drm_writeback_state_free(struct drm_writeback_state *state) drm_fb_unref(*fb); wl_array_release(&state->referenced_fbs); + wl_list_remove(&state->buffer_destroy_listener.link); + free(state); } +static void +drm_writeback_state_buffer_destroy_handler(struct wl_listener *listener, void *data) +{ + struct drm_writeback_state *state = + container_of(listener, struct drm_writeback_state, + buffer_destroy_listener); + + /** + * Client buffer destroyed while the wb job was scheduled. + * weston_capture_task_buffer_destroy_handler() retires the capture task + * when the buffer is gone, so drop it from the state. The state is + * destroyed once the wb job completes. + */ + state->ct = NULL; + wl_list_remove(&state->buffer_destroy_listener.link); +} + static void drm_output_pick_writeback_capture_task(struct drm_output *output) { @@ -814,6 +833,11 @@ drm_output_pick_writeback_capture_task(struct drm_output *output) output->wb_state->state = DRM_OUTPUT_WB_SCREENSHOT_PREPARE_COMMIT; output->wb_state->ct = ct; + output->wb_state->buffer_destroy_listener.notify = + drm_writeback_state_buffer_destroy_handler; + wl_resource_add_destroy_listener(buffer->resource, + &output->wb_state->buffer_destroy_listener); + return; err_fb: @@ -3448,12 +3472,21 @@ static void drm_writeback_success_screenshot(struct drm_writeback_state *state) { struct drm_output *output = state->output; - struct weston_buffer *buffer = - weston_capture_task_get_buffer(state->ct); + struct weston_buffer *buffer; int width, height; int dst_stride, src_stride; uint32_t *src, *dst; + /** + * Capture task already retired, see + * drm_writeback_state_buffer_destroy_handler(). Here we destroy the wb + * state. + */ + if (!state->ct) + goto destroy_state; + + buffer = weston_capture_task_get_buffer(state->ct); + if (buffer->type == WESTON_BUFFER_SHM) { src = state->fb->map; src_stride = state->fb->strides[0]; @@ -3472,6 +3505,8 @@ drm_writeback_success_screenshot(struct drm_writeback_state *state) } weston_capture_task_retire_complete(state->ct); + +destroy_state: drm_writeback_state_free(state); output->wb_state = NULL; } @@ -3482,7 +3517,17 @@ drm_writeback_fail_screenshot(struct drm_writeback_state *state, { struct drm_output *output = state->output; + /** + * Capture task already retired, see + * drm_writeback_state_buffer_destroy_handler(). Here we destroy the wb + * state. + */ + if (!state->ct) + goto destroy_state; + weston_capture_task_retire_failed(state->ct, err_msg); + +destroy_state: drm_writeback_state_free(state); output->wb_state = NULL; }