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 <derek.foreman@collabora.com>
This commit is contained in:
Derek Foreman 2025-12-12 15:45:34 -06:00
parent fcb195cbd0
commit 4b7586ea15
3 changed files with 46 additions and 5 deletions

View file

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

View file

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

View file

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