From 4b7586ea15a25c0bf260f700cff1411b13e8dbf9 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Fri, 12 Dec 2025 15:45:34 -0600 Subject: [PATCH] drm: Don't test reused states We shouldn't need to test state, because the state that worked previously should work again. However, to be completely safe against unpredictable edge cases, we've kept a state check. Remove that check and instead force a state rebuild in the case of an application failure. Keep track of how often this happens so we can fall back to checking instead of consistently failing state application. fixes #1081 Signed-off-by: Derek Foreman --- libweston/backend-drm/drm-internal.h | 7 +++++++ libweston/backend-drm/drm.c | 23 ++++++++++++++++++++++- libweston/backend-drm/state-propose.c | 21 +++++++++++++++++---- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 0da8a2f2d..698ba8158 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -82,6 +82,8 @@ #define MAX_DMABUF_PLANES 4 #endif +#define DRM_MAX_REUSE_FAILURES 10 + /** * A small wrapper to print information into the 'drm-backend' debug scope. * @@ -240,6 +242,8 @@ struct drm_device { /* struct drm_colorop_3x1d_lut::link */ struct wl_list drm_colorop_3x1d_lut_list; + + int reused_state_failures; }; struct drm_backend { @@ -664,6 +668,9 @@ struct drm_output { submit_frame_cb virtual_submit_frame; enum wdrm_content_type content_type; + + bool reused_state; + bool force_rebuild_state; }; void diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 0d40881d8..324a920d9 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -1201,6 +1201,7 @@ drm_repaint_flush_device(struct drm_device *device) struct drm_pending_state *pending_state; struct weston_output *base; int ret; + bool failed_reuse = false; pending_state = device->repaint_data; if (!pending_state) @@ -1222,7 +1223,27 @@ drm_repaint_flush_device(struct drm_device *device) if (!base->will_repaint || !tmp || tmp->device != device) continue; - if (ret == -EBUSY) + /* We shouldn't be failing at all when using a previous state, + * and when we do it can lead to choppy frame scheduling. + * Keep track of any failures and if we have a few, just give + * up on ever reusing state. + */ + if (tmp->reused_state) { + failed_reuse = true; + tmp->force_rebuild_state = true; + device->reused_state_failures++; + } + } + + if (failed_reuse) + drm_debug(b, "[repaint] failed with reused state, will rebuild and try again.\n"); + + wl_list_for_each(base, &b->compositor->output_list, link) { + struct drm_output *tmp = to_drm_output(base); + if (!base->will_repaint || !tmp || tmp->device != device) + continue; + + if (ret == -EBUSY || failed_reuse) weston_output_schedule_repaint_restart(base); else weston_output_schedule_repaint_reset(base); diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 350d5af37..41fb7b2dd 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -1033,6 +1033,11 @@ drm_output_propose_state_try_reuse(struct weston_output *output_base, struct weston_paint_node *pnode; struct drm_output_state *state; + if (output->force_rebuild_state) { + debug_propose_fail(output, mode, "previous state failed"); + return NULL; + } + /* These states are where we end up when we can't use planes * at all, sometimes simply because we don't have a renderer * fb yet. In that case we'd immediately get into a better @@ -1139,10 +1144,13 @@ drm_output_propose_state_try_reuse(struct weston_output *output_base, pnode->surface->buffer_release_ref.buffer_release); } - if (drm_pending_state_test(pending_state) != 0) { - debug_propose_fail(output, mode, "atomic test not OK"); - drm_output_state_free(state); - return NULL; + if (device->reused_state_failures > DRM_MAX_REUSE_FAILURES) { + drm_debug(b, "\t\t[reuse] must test due to high number of failures\n"); + if (drm_pending_state_test(pending_state) != 0) { + debug_propose_fail(output, mode, "atomic test not OK"); + drm_output_state_free(state); + return NULL; + } } /* In any state with the renderer, we need to unreference and remove @@ -1163,6 +1171,7 @@ drm_output_propose_state_try_reuse(struct weston_output *output_base, pstate->fb = NULL; } + output->reused_state = true; return state; } @@ -1192,6 +1201,8 @@ drm_output_propose_state(struct weston_output *output_base, /* Record the current lowest zpos of the underlay plane */ uint64_t current_lowest_zpos_underlay = DRM_PLANE_ZPOS_INVALID_PLANE; + output->reused_state = false; + if (mode & DRM_OUTPUT_PROPOSE_STATE_REUSE) { return drm_output_propose_state_try_reuse(output_base, pending_state, @@ -1603,6 +1614,8 @@ drm_assign_planes(struct weston_output *output_base) assert(state); assert(state->planes_enabled == !output_base->disable_planes); + output->force_rebuild_state = false; + drm_debug(b, "\t[repaint] Using %s composition\n", drm_propose_state_mode_to_string(mode));