From ee01712847c97317b176c19f42aa5497e5f14b84 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 16 Feb 2024 15:25:51 +0100 Subject: [PATCH] backend/drm: unify connector test and commit codepaths There is some duplicated logic between these two functions. The commit codepath was calling the test function before doing the real commit, so this also saves an unnecessary test-only commit when performing a real commit. --- backend/drm/drm.c | 187 ++++++++++++++++++---------------------------- 1 file changed, 71 insertions(+), 116 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 971f11cf..2c1e3b48 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -723,96 +723,6 @@ static bool drm_connector_set_pending_layer_fbs(struct wlr_drm_connector *conn, static bool drm_connector_alloc_crtc(struct wlr_drm_connector *conn); -static bool drm_connector_test(struct wlr_output *output, - const struct wlr_output_state *state) { - struct wlr_drm_connector *conn = get_drm_connector_from_output(output); - - if (!conn->backend->session->active) { - return false; - } - - uint32_t unsupported = state->committed & ~SUPPORTED_OUTPUT_STATE; - if (unsupported != 0) { - wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32, - unsupported); - return false; - } - - if ((state->committed & COMMIT_OUTPUT_STATE) == 0) { - // This commit doesn't change the KMS state - return true; - } - - if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && state->enabled) { - if (output->current_mode == NULL && - !(state->committed & WLR_OUTPUT_STATE_MODE)) { - wlr_drm_conn_log(conn, WLR_DEBUG, - "Can't enable an output without a mode"); - return false; - } - } - - if (output_pending_enabled(output, state) && !drm_connector_alloc_crtc(conn)) { - wlr_drm_conn_log(conn, WLR_DEBUG, - "No CRTC available for this connector"); - return false; - } - - bool ok = false; - struct wlr_drm_connector_state pending = {0}; - drm_connector_state_init(&pending, conn, state); - struct wlr_drm_device_state pending_dev = {0}; - drm_device_state_init_single(&pending_dev, &pending); - - if ((state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) && - state->adaptive_sync_enabled && - !drm_connector_supports_vrr(conn)) { - goto out; - } - - if (conn->backend->parent) { - // If we're running as a secondary GPU, we can't perform an atomic - // commit without blitting a buffer. - ok = true; - goto out; - } - - if (!conn->crtc) { - // If the output is disabled, we don't have a crtc even after - // reallocation - ok = true; - goto out; - } - - if (state->committed & WLR_OUTPUT_STATE_BUFFER) { - if (!drm_connector_state_update_primary_fb(conn, &pending)) { - goto out; - } - - if (pending.base->tearing_page_flip && !conn->backend->supports_tearing_page_flips) { - wlr_log(WLR_ERROR, "Attempted to submit a tearing page flip to an unsupported backend!"); - goto out; - } - } - if (state->committed & WLR_OUTPUT_STATE_LAYERS) { - if (!drm_connector_set_pending_layer_fbs(conn, pending.base)) { - return false; - } - } - - if (pending.active && !pending.primary_fb) { - wlr_drm_conn_log(conn, WLR_DEBUG, - "No primary frame buffer available for this connector"); - goto out; - } - - ok = drm_commit(conn->backend, &pending_dev, 0, true); - -out: - drm_connector_state_finish(&pending); - return ok; -} - bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) { struct wlr_drm_backend *drm = conn->backend; @@ -840,45 +750,89 @@ bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) { } static bool drm_connector_commit_state(struct wlr_drm_connector *conn, - const struct wlr_output_state *base) { + const struct wlr_output_state *state, bool test_only) { struct wlr_drm_backend *drm = conn->backend; + struct wlr_output *output = &conn->output; if (!drm->session->active) { return false; } + uint32_t unsupported = state->committed & ~SUPPORTED_OUTPUT_STATE; + if (unsupported != 0) { + wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32, + unsupported); + return false; + } + + if (test_only && (state->committed & COMMIT_OUTPUT_STATE) == 0) { + // This commit doesn't change the KMS state + return true; + } + + if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && state->enabled) { + if (output->current_mode == NULL && + !(state->committed & WLR_OUTPUT_STATE_MODE)) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "Can't enable an output without a mode"); + return false; + } + } + bool ok = false; struct wlr_drm_connector_state pending = {0}; - drm_connector_state_init(&pending, conn, base); + drm_connector_state_init(&pending, conn, state); struct wlr_drm_device_state pending_dev = {0}; drm_device_state_init_single(&pending_dev, &pending); + if (output_pending_enabled(output, state) && !drm_connector_alloc_crtc(conn)) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "No CRTC available for this connector"); + return false; + } + + if ((state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) && + state->adaptive_sync_enabled && + !drm_connector_supports_vrr(conn)) { + goto out; + } + + if (test_only && conn->backend->parent) { + // If we're running as a secondary GPU, we can't perform an atomic + // commit without blitting a buffer. + ok = true; + goto out; + } + if (!pending.active && conn->crtc == NULL) { // Disabling an already-disabled connector ok = true; goto out; } - if (pending.active) { - if (!drm_connector_alloc_crtc(conn)) { - wlr_drm_conn_log(conn, WLR_ERROR, - "No CRTC available for this connector"); - goto out; - } - } - - if (pending.base->committed & WLR_OUTPUT_STATE_BUFFER) { + if (state->committed & WLR_OUTPUT_STATE_BUFFER) { if (!drm_connector_state_update_primary_fb(conn, &pending)) { goto out; } + + if (pending.base->tearing_page_flip && !conn->backend->supports_tearing_page_flips) { + wlr_log(WLR_ERROR, "Attempted to submit a tearing page flip to an unsupported backend!"); + goto out; + } } - if (pending.base->committed & WLR_OUTPUT_STATE_LAYERS) { + if (state->committed & WLR_OUTPUT_STATE_LAYERS) { if (!drm_connector_set_pending_layer_fbs(conn, pending.base)) { - return false; + goto out; } } - if (pending_dev.modeset) { + if (pending.active && !pending.primary_fb) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "No primary frame buffer available for this connector"); + goto out; + } + + if (!test_only && pending_dev.modeset) { if (pending.active) { wlr_drm_conn_log(conn, WLR_INFO, "Modesetting with %dx%d @ %.3f Hz", pending.mode.hdisplay, pending.mode.vdisplay, @@ -892,26 +846,26 @@ static bool drm_connector_commit_state(struct wlr_drm_connector *conn, // 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_dev.nonblock && conn->pending_page_flip != NULL) { + if (!test_only && pending_dev.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) { + if (!test_only && pending.active) { flags |= DRM_MODE_PAGE_FLIP_EVENT; } if (pending.base->tearing_page_flip) { flags |= DRM_MODE_PAGE_FLIP_ASYNC; } - ok = drm_commit(drm, &pending_dev, flags, false); + ok = drm_commit(drm, &pending_dev, flags, test_only); if (!ok) { goto out; } - if (!pending.active) { + if (!test_only && !pending.active) { drm_plane_finish_surface(conn->crtc->primary); drm_plane_finish_surface(conn->crtc->cursor); drm_fb_clear(&conn->cursor_pending_fb); @@ -925,15 +879,16 @@ out: return ok; } +static bool drm_connector_test(struct wlr_output *output, + const struct wlr_output_state *state) { + struct wlr_drm_connector *conn = get_drm_connector_from_output(output); + return drm_connector_commit_state(conn, state, true); +} + static bool drm_connector_commit(struct wlr_output *output, const struct wlr_output_state *state) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); - - if (!drm_connector_test(output, state)) { - return false; - } - - return drm_connector_commit_state(conn, state); + return drm_connector_commit_state(conn, state, false); } size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, @@ -1280,7 +1235,7 @@ static void dealloc_crtc(struct wlr_drm_connector *conn) { struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, false); - if (!drm_connector_commit_state(conn, &state)) { + if (!drm_connector_commit_state(conn, &state, false)) { // On GPU unplug, disabling the CRTC can fail with EPERM wlr_drm_conn_log(conn, WLR_ERROR, "Failed to disable CRTC %"PRIu32, conn->crtc->id); @@ -1900,7 +1855,7 @@ void restore_drm_device(struct wlr_drm_backend *drm) { wl_list_for_each(conn, &drm->connectors, link) { struct wlr_output_state state; build_current_connector_state(&state, conn); - if (!drm_connector_commit_state(conn, &state)) { + if (!drm_connector_commit_state(conn, &state, false)) { wlr_drm_conn_log(conn, WLR_ERROR, "Failed to restore state after VT switch"); } wlr_output_state_finish(&state);