diff options
author | Simon Ser <contact@emersion.fr> | 2024-02-14 20:04:22 +0100 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2024-02-15 09:56:33 +0100 |
commit | 17fe87af5e1a5fded8a210a2e6b281804d57bebe (patch) | |
tree | 4f7f0fb8df107f6e061a3298514a12e6fdb9793d /backend | |
parent | 653e28d2a303304a280bd7fa51acbe27299b44d7 (diff) |
backend/drm: skip reset after VT switch if possible
If all connectors and planes already have the right CRTC set, or
are disabled, we can skip the device-wide reset after a VT switch.
I've contemplated using a more fine-grained logic to only reset
the connectors, CRTCs and planes that need to be migrated. However,
writing a correct algorithm for this would be quite involved, and it
doesn't seem worth the trouble anyways.
Closes: https://github.com/swaywm/sway/issues/7956
Diffstat (limited to 'backend')
-rw-r--r-- | backend/drm/drm.c | 64 |
1 files changed, 63 insertions, 1 deletions
diff --git a/backend/drm/drm.c b/backend/drm/drm.c index fe5f4f8f..42080603 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1738,6 +1738,68 @@ static void build_current_connector_state(struct wlr_output_state *state, } } +/** + * Check whether we need to perform a full reset after a VT switch. + * + * If any connector or plane has a different CRTC, we need to perform a full + * reset to restore our mapping. We couldn't avoid a full reset even if we + * used a single KMS atomic commit to apply our state: the kernel rejects + * commits which migrate a plane from one CRTC to another without going through + * an intermediate state where the plane is disabled. + */ +static bool skip_reset_for_restore(struct wlr_drm_backend *drm) { + struct wlr_drm_connector *conn; + wl_list_for_each(conn, &drm->connectors, link) { + drmModeConnector *drm_conn = drmModeGetConnectorCurrent(drm->fd, conn->id); + if (drm_conn == NULL) { + return false; + } + struct wlr_drm_crtc *crtc = connector_get_current_crtc(conn, drm_conn); + drmModeFreeConnector(drm_conn); + + if (crtc != NULL && conn->crtc != crtc) { + return false; + } + } + + for (size_t i = 0; i < drm->num_planes; i++) { + struct wlr_drm_plane *plane = &drm->planes[i]; + + drmModePlane *drm_plane = drmModeGetPlane(drm->fd, plane->id); + if (drm_plane == NULL) { + return false; + } + uint32_t crtc_id = drm_plane->crtc_id; + drmModeFreePlane(drm_plane); + + struct wlr_drm_crtc *crtc = NULL; + for (size_t i = 0; i < drm->num_crtcs; i++) { + if (drm->crtcs[i].id == crtc_id) { + crtc = &drm->crtcs[i]; + break; + } + } + if (crtc == NULL) { + continue; + } + + bool ok = false; + switch (plane->type) { + case DRM_PLANE_TYPE_PRIMARY: + ok = crtc->primary == plane; + break; + case DRM_PLANE_TYPE_CURSOR: + ok = crtc->cursor == plane; + break; + } + if (!ok) { + return false; + } + } + + return true; +} + void restore_drm_device(struct wlr_drm_backend *drm) { // The previous DRM master leaves KMS in an undefined state. We need // to restore our own state, but be careful to avoid invalid @@ -1745,7 +1807,7 @@ void restore_drm_device(struct wlr_drm_backend *drm) { // first disable all CRTCs, then light up the ones we were using // before the VT switch. // TODO: better use the atomic API to improve restoration after a VT switch - if (!drm->iface->reset(drm)) { + if (!skip_reset_for_restore(drm) && !drm->iface->reset(drm)) { wlr_log(WLR_ERROR, "Failed to reset state after VT switch"); } |