diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 47d4e585..618a9f43 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -315,12 +315,8 @@ static bool atomic_crtc_commit(struct wlr_drm_connector *conn, } if (modeset) { flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; - } else if (!test_only && (state->base->committed & WLR_OUTPUT_STATE_BUFFER)) { - // The wlr_output API requires non-modeset commits with a new buffer to - // wait for the frame event. However compositors often perform - // non-modesets commits without a new buffer without waiting for the - // frame event. In that case we need to make the KMS commit blocking, - // otherwise the kernel will error out with EBUSY. + } + if (!test_only && state->nonblock) { flags |= DRM_MODE_ATOMIC_NONBLOCK; } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 25bfb319..dc442f66 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -489,6 +489,13 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state, .modeset = base->allow_reconfiguration, .active = (base->committed & WLR_OUTPUT_STATE_ENABLED) ? base->enabled : conn->output.enabled, + // The wlr_output API requires non-modeset commits with a new buffer to + // wait for the frame event. However compositors often perform + // non-modesets commits without a new buffer without waiting for the + // frame event. In that case we need to make the KMS commit blocking, + // otherwise the kernel will error out with EBUSY. + .nonblock = !base->allow_reconfiguration && + (base->committed & WLR_OUTPUT_STATE_BUFFER), }; struct wlr_output_mode *mode = conn->output.current_mode; @@ -763,16 +770,6 @@ bool drm_connector_commit_state(struct wlr_drm_connector *conn, if (!drm_connector_state_update_primary_fb(conn, &pending)) { goto out; } - - // wlr_drm_interface.crtc_commit will perform either a non-blocking - // page-flip, either a blocking modeset. When performing a blocking modeset - // we'll wait for all queued page-flips to complete, so we don't need this - // safeguard. - if (conn->pending_page_flip != NULL && !pending.modeset) { - wlr_drm_conn_log(conn, WLR_ERROR, "Failed to page-flip output: " - "a page-flip is already pending"); - goto out; - } } if (pending.base->committed & WLR_OUTPUT_STATE_LAYERS) { if (!drm_connector_set_pending_layer_fbs(conn, pending.base)) { @@ -790,6 +787,16 @@ bool drm_connector_commit_state(struct wlr_drm_connector *conn, } } + // wlr_drm_interface.crtc_commit will perform either a non-blocking + // page-flip, either a blocking modeset. When performing a blocking modeset + // we'll wait for all queued page-flips to complete, so we don't need this + // safeguard. + if (pending.nonblock && conn->pending_page_flip != NULL) { + wlr_drm_conn_log(conn, WLR_ERROR, "Failed to page-flip output: " + "a page-flip is already pending"); + goto out; + } + uint32_t flags = 0; if (pending.active) { flags |= DRM_MODE_PAGE_FLIP_EVENT; diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index d7f01d2a..cafae807 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -368,12 +368,8 @@ static bool crtc_commit(struct wlr_drm_connector *conn, } if (modeset) { flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; - } else if (!test_only && (state->base->committed & WLR_OUTPUT_STATE_BUFFER)) { - // The wlr_output API requires non-modeset commits with a new buffer to - // wait for the frame event. However compositors often perform - // non-modesets commits without a new buffer without waiting for the - // frame event. In that case we need to make the KMS commit blocking, - // otherwise the kernel will error out with EBUSY. + } + if (!test_only && state->nonblock) { flags |= DRM_MODE_ATOMIC_NONBLOCK; } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 4718eee0..88eacc6e 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -126,6 +126,7 @@ struct wlr_drm_mode { struct wlr_drm_connector_state { const struct wlr_output_state *base; bool modeset; + bool nonblock; bool active; drmModeModeInfo mode; struct wlr_drm_fb *primary_fb;