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.
This commit is contained in:
Simon Ser 2024-02-16 15:25:51 +01:00 committed by Kenny Levinsen
parent 2ff3479558
commit ee01712847
1 changed files with 71 additions and 116 deletions

View File

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