From dc27a52216dc77ee3589b4e5a462f925c501208b Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Wed, 15 Feb 2023 08:27:37 -0300 Subject: [PATCH] drm-backend: address case in which writeback takes longer than atomic commit In commit "drm-backend: add writeback connector screenshooter to DRM-backend" we were failing the writeback screenshot when the DRM/KMS driver would take longer than the atomic commit to finish. In this patch we address such case. Signed-off-by: Leandro Ribeiro --- libweston/backend-drm/drm-internal.h | 9 ++++- libweston/backend-drm/drm.c | 49 +++++++++++++++++++++++++++- libweston/backend-drm/kms.c | 7 ++-- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 8dc72426c..857075828 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -567,6 +567,12 @@ enum writeback_screenshot_state { * commit is handled by DRM it will give us a sync fd that gets * signalled when the writeback is done. */ DRM_OUTPUT_WB_SCREENSHOT_CHECK_FENCE, + /* The atomic commit completed and we received the sync fd from the + * kernel. We've polled to check if the writeback was over, but it + * wasn't. Now we must stop the repaint loop and wait until the + * writeback is complete, because we can't commit with KMS objects + * (CRTC, planes, etc) that are in used by the writeback job. */ + DRM_OUTPUT_WB_SCREENSHOT_WAITING_SIGNAL, }; struct drm_writeback_state { @@ -578,6 +584,7 @@ struct drm_writeback_state { struct drm_fb *fb; int32_t out_fence_fd; + struct wl_event_source *wb_source; /* Reference to fb's being used by the writeback job. These are all the * framebuffers in every drm_plane_state of the output state that we've @@ -699,7 +706,7 @@ void drm_writeback_reference_planes(struct drm_writeback_state *state, struct wl_list *plane_state_list); bool -drm_writeback_has_finished(struct drm_writeback_state *state); +drm_writeback_should_wait_completion(struct drm_writeback_state *state); void drm_writeback_fail_screenshot(struct drm_writeback_state *state, const char *err_msg); diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index b5e536a57..ca5ecdb56 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -2670,7 +2670,20 @@ drm_writeback_fail_screenshot(struct drm_writeback_state *state, output->wb_state = NULL; } -bool +static int +drm_writeback_save_callback(int fd, uint32_t mask, void *data) +{ + struct drm_writeback_state *state = data; + + wl_event_source_remove(state->wb_source); + close(fd); + + drm_writeback_success_screenshot(state); + + return 0; +} + +static bool drm_writeback_has_finished(struct drm_writeback_state *state) { struct pollfd pollfd; @@ -2695,6 +2708,40 @@ drm_writeback_has_finished(struct drm_writeback_state *state) return false; } +bool +drm_writeback_should_wait_completion(struct drm_writeback_state *state) +{ + struct weston_compositor *ec = state->output->base.compositor; + struct wl_event_loop *event_loop; + + if (state->state == DRM_OUTPUT_WB_SCREENSHOT_WAITING_SIGNAL) + return true; + + if (state->state == DRM_OUTPUT_WB_SCREENSHOT_CHECK_FENCE) { + if (drm_writeback_has_finished(state)) + return false; + + /* The writeback has not finished yet. So add callback that gets + * called when the sync fd of the writeback job gets signalled. + * We need to wait for that to resume the repaint loop. */ + event_loop = wl_display_get_event_loop(ec->wl_display); + state->wb_source = + wl_event_loop_add_fd(event_loop, state->out_fence_fd, + WL_EVENT_READABLE, + drm_writeback_save_callback, state); + if (!state->wb_source) { + drm_writeback_fail_screenshot(state, "drm: out of memory"); + return false; + } + + state->state = DRM_OUTPUT_WB_SCREENSHOT_WAITING_SIGNAL; + + return true; + } + + return false; +} + void drm_writeback_reference_planes(struct drm_writeback_state *state, struct wl_list *plane_state_list) diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 5b499ced0..f909d7799 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -1750,6 +1750,7 @@ on_drm_input(int fd, uint32_t mask, void *data) struct drm_device *device = data; struct drm_writeback_state *state; struct drm_crtc *crtc; + bool wait_wb_completion = false; drmEventContext evctx; /* If we have a pending writeback job for this output, we can't continue @@ -1758,9 +1759,11 @@ on_drm_input(int fd, uint32_t mask, void *data) * uses the KMS objects (CRTC, planes, etc) in use by the writeback. */ wl_list_for_each(crtc, &device->crtc_list, link) { state = crtc->output ? crtc->output->wb_state : NULL; - if (state && !drm_writeback_has_finished(state)) - drm_writeback_fail_screenshot(state, "drm: out fence not signalled yet"); + if (state && drm_writeback_should_wait_completion(state)) + wait_wb_completion = true; } + if (wait_wb_completion) + return 1; memset(&evctx, 0, sizeof evctx); evctx.version = 3;