aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
authorSimon Ser <contact@emersion.fr>2023-11-15 16:38:51 +0100
committerSimon Ser <contact@emersion.fr>2023-11-17 16:59:04 +0000
commit3b53d1cbf199aa5db0b1c81df92e43fc05670543 (patch)
treeca7b0a2a87911eaf7738e1a6fb5b7425958a31b6 /backend
parentc9c9dd6a5b866055a6a39fd78e55f6d5797fec28 (diff)
backend/drm: introduce page-flip tracking struct
Introduce a per-page-flip tracking struct passed to the kernel when we request a page-flip event for an atomic commit. The kernel will pass us back this pointer when delivering the event. This eliminates any risk of mixing up events together. In particular, if two events are pending, or if the CRTC of a connector is swapped, we no longer blow up in the page-flip event handler. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3753
Diffstat (limited to 'backend')
-rw-r--r--backend/drm/atomic.c11
-rw-r--r--backend/drm/drm.c71
-rw-r--r--backend/drm/legacy.c4
-rw-r--r--backend/drm/libliftoff.c6
4 files changed, 55 insertions, 37 deletions
diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c
index 4715c6f0..47d4e585 100644
--- a/backend/drm/atomic.c
+++ b/backend/drm/atomic.c
@@ -61,13 +61,14 @@ static void atomic_begin(struct atomic *atom) {
}
static bool atomic_commit(struct atomic *atom,
- struct wlr_drm_connector *conn, uint32_t flags) {
+ struct wlr_drm_connector *conn, struct wlr_drm_page_flip *page_flip,
+ uint32_t flags) {
struct wlr_drm_backend *drm = conn->backend;
if (atom->failed) {
return false;
}
- int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, drm);
+ int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, page_flip);
if (ret != 0) {
wlr_drm_conn_log_errno(conn,
(flags & DRM_MODE_ATOMIC_TEST_ONLY) ? WLR_DEBUG : WLR_ERROR,
@@ -257,8 +258,8 @@ static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm,
}
static bool atomic_crtc_commit(struct wlr_drm_connector *conn,
- const struct wlr_drm_connector_state *state, uint32_t flags,
- bool test_only) {
+ const struct wlr_drm_connector_state *state,
+ struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only) {
struct wlr_drm_backend *drm = conn->backend;
struct wlr_output *output = &conn->output;
struct wlr_drm_crtc *crtc = conn->crtc;
@@ -367,7 +368,7 @@ static bool atomic_crtc_commit(struct wlr_drm_connector *conn,
}
}
- bool ok = atomic_commit(&atom, conn, flags);
+ bool ok = atomic_commit(&atom, conn, page_flip, flags);
atomic_finish(&atom);
if (ok && !test_only) {
diff --git a/backend/drm/drm.c b/backend/drm/drm.c
index e8a6f5c6..92feed6c 100644
--- a/backend/drm/drm.c
+++ b/backend/drm/drm.c
@@ -412,15 +412,32 @@ static struct wlr_drm_layer *get_or_create_layer(struct wlr_drm_backend *drm,
return layer;
}
+static void drm_connector_set_pending_page_flip(struct wlr_drm_connector *conn,
+ struct wlr_drm_page_flip *page_flip) {
+ if (conn->pending_page_flip != NULL) {
+ conn->pending_page_flip->conn = NULL;
+ }
+ conn->pending_page_flip = page_flip;
+}
+
static bool drm_crtc_commit(struct wlr_drm_connector *conn,
const struct wlr_drm_connector_state *state,
uint32_t flags, bool test_only) {
// Disallow atomic-only flags
assert((flags & ~DRM_MODE_PAGE_FLIP_FLAGS) == 0);
+ struct wlr_drm_page_flip *page_flip = NULL;
+ if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
+ page_flip = calloc(1, sizeof(*page_flip));
+ if (page_flip == NULL) {
+ return false;
+ }
+ page_flip->conn = conn;
+ }
+
struct wlr_drm_backend *drm = conn->backend;
struct wlr_drm_crtc *crtc = conn->crtc;
- bool ok = drm->iface->crtc_commit(conn, state, flags, test_only);
+ bool ok = drm->iface->crtc_commit(conn, state, page_flip, flags, test_only);
if (ok && !test_only) {
drm_fb_clear(&crtc->primary->queued_fb);
if (state->primary_fb != NULL) {
@@ -434,6 +451,8 @@ static bool drm_crtc_commit(struct wlr_drm_connector *conn,
wl_list_for_each(layer, &crtc->layers, link) {
drm_fb_move(&layer->queued_fb, &layer->pending_fb);
}
+
+ drm_connector_set_pending_page_flip(conn, page_flip);
} else {
// The set_cursor() hook is a bit special: it's not really synchronized
// to commit() or test(). Once set_cursor() returns true, the new
@@ -446,6 +465,8 @@ static bool drm_crtc_commit(struct wlr_drm_connector *conn,
wl_list_for_each(layer, &crtc->layers, link) {
drm_fb_clear(&layer->pending_fb);
}
+
+ free(page_flip);
}
return ok;
}
@@ -732,16 +753,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_crtc && !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)) {
@@ -759,7 +770,19 @@ bool drm_connector_commit_state(struct wlr_drm_connector *conn,
}
}
- uint32_t flags = pending.active ? DRM_MODE_PAGE_FLIP_EVENT : 0;
+ uint32_t flags = 0;
+ if (pending.active) {
+ flags |= DRM_MODE_PAGE_FLIP_EVENT;
+ // 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->tearing_page_flip) {
flags |= DRM_MODE_PAGE_FLIP_ASYNC;
}
@@ -777,9 +800,6 @@ bool drm_connector_commit_state(struct wlr_drm_connector *conn,
conn->cursor_enabled = false;
conn->crtc = NULL;
}
- if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
- conn->pending_page_flip_crtc = conn->crtc->id;
- }
out:
drm_connector_state_finish(&pending);
@@ -1029,7 +1049,7 @@ static void drm_connector_destroy_output(struct wlr_output *output) {
dealloc_crtc(conn);
conn->status = DRM_MODE_DISCONNECTED;
- conn->pending_page_flip_crtc = 0;
+ drm_connector_set_pending_page_flip(conn, NULL);
struct wlr_drm_mode *mode, *mode_tmp;
wl_list_for_each_safe(mode, mode_tmp, &conn->output.modes, wlr_mode.link) {
@@ -1660,22 +1680,19 @@ static int mhz_to_nsec(int mhz) {
static void handle_page_flip(int fd, unsigned seq,
unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void *data) {
- struct wlr_drm_backend *drm = data;
+ struct wlr_drm_page_flip *page_flip = data;
- bool found = false;
- struct wlr_drm_connector *conn;
- wl_list_for_each(conn, &drm->connectors, link) {
- if (conn->pending_page_flip_crtc == crtc_id) {
- found = true;
- break;
- }
+ struct wlr_drm_connector *conn = page_flip->conn;
+ if (conn != NULL) {
+ conn->pending_page_flip = NULL;
}
- if (!found) {
- wlr_log(WLR_DEBUG, "Unexpected page-flip event for CRTC %u", crtc_id);
+ free(page_flip);
+
+ if (conn == NULL) {
return;
}
- conn->pending_page_flip_crtc = 0;
+ struct wlr_drm_backend *drm = conn->backend;
if (conn->status != DRM_MODE_CONNECTED || conn->crtc == NULL) {
wlr_drm_conn_log(conn, WLR_DEBUG,
diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c
index ee64f78c..7663c0be 100644
--- a/backend/drm/legacy.c
+++ b/backend/drm/legacy.c
@@ -59,7 +59,7 @@ static bool legacy_crtc_test(struct wlr_drm_connector *conn,
static bool legacy_crtc_commit(struct wlr_drm_connector *conn,
const struct wlr_drm_connector_state *state,
- uint32_t flags, bool test_only) {
+ struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only) {
if (!legacy_crtc_test(conn, state)) {
return false;
}
@@ -181,7 +181,7 @@ static bool legacy_crtc_commit(struct wlr_drm_connector *conn,
}
if (drmModePageFlip(drm->fd, crtc->id, fb_id,
- page_flags, drm)) {
+ page_flags, page_flip)) {
wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModePageFlip failed");
return false;
}
diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c
index 5b828e17..d7f01d2a 100644
--- a/backend/drm/libliftoff.c
+++ b/backend/drm/libliftoff.c
@@ -301,8 +301,8 @@ static void update_layer_feedback(struct wlr_drm_backend *drm,
}
static bool crtc_commit(struct wlr_drm_connector *conn,
- const struct wlr_drm_connector_state *state, uint32_t flags,
- bool test_only) {
+ const struct wlr_drm_connector_state *state,
+ struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only) {
struct wlr_drm_backend *drm = conn->backend;
struct wlr_output *output = &conn->output;
struct wlr_drm_crtc *crtc = conn->crtc;
@@ -455,7 +455,7 @@ static bool crtc_commit(struct wlr_drm_connector *conn,
goto out;
}
- ret = drmModeAtomicCommit(drm->fd, req, flags, drm);
+ ret = drmModeAtomicCommit(drm->fd, req, flags, page_flip);
if (ret != 0) {
wlr_drm_conn_log_errno(conn, test_only ? WLR_DEBUG : WLR_ERROR,
"Atomic commit failed");