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 <leandro.ribeiro@collabora.com>
This commit is contained in:
Leandro Ribeiro 2023-02-15 08:27:37 -03:00
parent 5b04895835
commit dc27a52216
3 changed files with 61 additions and 4 deletions

View File

@ -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);

View File

@ -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)

View File

@ -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;