backend/drm: implement KMS state snapshot/restore

Following ideas from [1], snapshot the entire KMS state when the VT
is switched away, and restore it when the VT is switched back.

> Well the neat trick is that userspace doesn’t need to be able to
> understand properties to save and restore them - the actual property
> value transport between kernel and userspace is fully generic.

That way, even if another DRM master changes a property we don't
understand like CTM or HDR_OUTPUT_METADATA, we can switch it back
and avoid getting garbage on screen.

[1]: https://blog.ffwll.ch/2016/01/vt-switching-with-atomic-modeset.html
This commit is contained in:
Simon Ser 2022-10-18 18:23:55 +02:00
parent e0b2bf2a6b
commit f0bb53c4ab
7 changed files with 155 additions and 20 deletions

View file

@ -55,6 +55,7 @@ static void backend_destroy(struct wlr_backend *backend) {
finish_drm_renderer(&drm->mgpu_renderer);
}
drmModeAtomicFree(drm->atomic_snapshot);
finish_drm_resources(drm);
free(drm->name);
@ -101,30 +102,18 @@ static void handle_session_active(struct wl_listener *listener, void *data) {
if (session->active) {
wlr_log(WLR_INFO, "DRM fd resumed");
scan_drm_connectors(drm, NULL);
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->connectors, link) {
struct wlr_output_mode *mode = NULL;
uint32_t committed = WLR_OUTPUT_STATE_ENABLED;
if (conn->status != DRM_MODE_DISCONNECTED && conn->output.enabled
&& conn->output.current_mode != NULL) {
committed |= WLR_OUTPUT_STATE_MODE;
mode = conn->output.current_mode;
}
struct wlr_output_state state = {
.committed = committed,
.allow_artifacts = true,
.enabled = mode != NULL,
.mode_type = WLR_OUTPUT_STATE_MODE_FIXED,
.mode = mode,
};
if (!drm_connector_commit_state(conn, &state)) {
wlr_drm_conn_log(conn, WLR_ERROR, "Failed to restore state after VT switch");
}
if (!drm->iface->restore_state(drm)) {
wlr_log(WLR_ERROR, "Failed to restore KMS state after VT switch");
}
scan_drm_connectors(drm, NULL);
} else {
wlr_log(WLR_INFO, "DRM fd paused");
if (drm->iface->snapshot_state && !drm->iface->snapshot_state(drm)) {
wlr_log(WLR_ERROR, "Failed to snapshot KMS state");
}
}
}