diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 079a68952..9349c1aaf 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -472,6 +472,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 11a5c3d4c..102d753bc 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -570,9 +570,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) { @@ -626,6 +645,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: @@ -2899,12 +2923,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); + src = state->fb->map; src_stride = state->fb->strides[0]; @@ -2921,6 +2954,8 @@ drm_writeback_success_screenshot(struct drm_writeback_state *state) wl_shm_buffer_end_access(buffer->shm_buffer); weston_capture_task_retire_complete(state->ct); + +destroy_state: drm_writeback_state_free(state); output->wb_state = NULL; } @@ -2931,7 +2966,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; }