From ecb2a2b0d3c5766f0147af2a54ec7d213c070f88 Mon Sep 17 00:00:00 2001 From: Versus Void Date: Sun, 22 Oct 2017 10:01:21 +0000 Subject: [PATCH 1/4] Emit output resolution event only when resolution changes --- backend/drm/drm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 51a5f636..4c13d01a 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -437,10 +437,12 @@ static bool wlr_drm_connector_set_mode(struct wlr_output *output, crtc->cursor ? crtc->cursor - drm->cursor_planes : -1); conn->state = WLR_DRM_CONN_CONNECTED; - conn->output.width = mode->width; - conn->output.height = mode->height; conn->output.current_mode = mode; - wl_signal_emit(&conn->output.events.resolution, &conn->output); + if (conn->output.width != mode->width || conn->output.height != mode->height) { + conn->output.width = mode->width; + conn->output.height = mode->height; + wl_signal_emit(&conn->output.events.resolution, &conn->output); + } // Since realloc_crtcs can deallocate planes on OTHER outputs, // we actually need to reinitalise all of them From 3c31209a979faf9e25ec055ae32f17f9b64b70be Mon Sep 17 00:00:00 2001 From: Versus Void Date: Sun, 22 Oct 2017 10:15:02 +0000 Subject: [PATCH 2/4] Reinitialize only changed DRM outputs after setting mode on one --- backend/drm/drm.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 4c13d01a..cd3208d3 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -259,7 +259,8 @@ static void wlr_drm_connector_enable(struct wlr_output *output, bool enable) { } } -static void realloc_planes(struct wlr_drm_backend *drm, const uint32_t *crtc_in) { +static void realloc_planes(struct wlr_drm_backend *drm, const uint32_t *crtc_in, + bool *changed_outputs) { // overlay, primary, cursor for (int type = 0; type < 3; ++type) { if (drm->num_type_planes[type] == 0) { @@ -298,6 +299,7 @@ static void realloc_planes(struct wlr_drm_backend *drm, const uint32_t *crtc_in) struct wlr_drm_plane *new = &drm->type_planes[type][crtc_res[i]]; if (*old != new) { + changed_outputs[crtc_res[i]] = true; if (*old) { wlr_drm_surface_finish(&(*old)->surf); } @@ -308,7 +310,8 @@ static void realloc_planes(struct wlr_drm_backend *drm, const uint32_t *crtc_in) } } -static void realloc_crtcs(struct wlr_drm_backend *drm, struct wlr_drm_connector *conn) { +static void realloc_crtcs(struct wlr_drm_backend *drm, + struct wlr_drm_connector *conn, bool *changed_outputs) { uint32_t crtc[drm->num_crtcs]; uint32_t crtc_res[drm->num_crtcs]; uint32_t possible_crtc[wl_list_length(&drm->outputs)]; @@ -356,12 +359,15 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, struct wlr_drm_connector return; } + changed_outputs[index] = true; + for (size_t i = 0; i < drm->num_crtcs; ++i) { if (crtc_res[i] == UNMATCHED) { continue; } if (crtc_res[i] != crtc[i]) { + changed_outputs[crtc_res[i]] = true; struct wlr_drm_connector *c; size_t pos = 0; wl_list_for_each(c, &drm->outputs, link) { @@ -374,7 +380,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, struct wlr_drm_connector } } - realloc_planes(drm, crtc_res); + realloc_planes(drm, crtc_res, changed_outputs); } static uint32_t get_possible_crtcs(int fd, uint32_t conn_id) { @@ -413,6 +419,7 @@ static bool wlr_drm_connector_set_mode(struct wlr_output *output, struct wlr_output_mode *mode) { struct wlr_drm_connector *conn = (struct wlr_drm_connector *)output; struct wlr_drm_backend *drm = (struct wlr_drm_backend *)output->backend; + bool changed_outputs[wl_list_length(&drm->outputs)]; wlr_log(L_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", conn->output.name, mode->width, mode->height, mode->refresh); @@ -422,7 +429,8 @@ static bool wlr_drm_connector_set_mode(struct wlr_output *output, goto error_conn; } - realloc_crtcs(drm, conn); + memset(changed_outputs, false, sizeof(changed_outputs)); + realloc_crtcs(drm, conn, changed_outputs); if (!conn->crtc) { wlr_log(L_ERROR, "Unable to match %s with a CRTC", conn->output.name); @@ -445,12 +453,15 @@ static bool wlr_drm_connector_set_mode(struct wlr_output *output, } // Since realloc_crtcs can deallocate planes on OTHER outputs, - // we actually need to reinitalise all of them + // we actually need to reinitalise any than has changed + ssize_t output_index = -1; wl_list_for_each(conn, &drm->outputs, link) { + output_index += 1; struct wlr_output_mode *mode = conn->output.current_mode; struct wlr_drm_crtc *crtc = conn->crtc; - if (conn->state != WLR_DRM_CONN_CONNECTED) { + if (conn->state != WLR_DRM_CONN_CONNECTED || + !changed_outputs[output_index]) { continue; } From 549777ca19b2040ff0824ac7764bbe1cb834e953 Mon Sep 17 00:00:00 2001 From: Versus Void Date: Sun, 22 Oct 2017 21:38:30 +0000 Subject: [PATCH 3/4] Set crtc field when scanning for DRM connectors and always use it when matching CRTCs with connectors. Fix deactivated monitors check. --- backend/drm/drm.c | 57 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index cd3208d3..94923d04 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -314,7 +314,8 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, struct wlr_drm_connector *conn, bool *changed_outputs) { uint32_t crtc[drm->num_crtcs]; uint32_t crtc_res[drm->num_crtcs]; - uint32_t possible_crtc[wl_list_length(&drm->outputs)]; + ssize_t num_outputs = wl_list_length(&drm->outputs); + uint32_t possible_crtc[num_outputs]; for (size_t i = 0; i < drm->num_crtcs; ++i) { crtc[i] = UNMATCHED; @@ -330,35 +331,41 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, index = i; } - if (c->state != WLR_DRM_CONN_CONNECTED) { - continue; + if (c->crtc) { + crtc[c->crtc - drm->crtcs] = i; + } + + if (c->state == WLR_DRM_CONN_CONNECTED) { + possible_crtc[i] = c->possible_crtc; } - possible_crtc[i] = c->possible_crtc; - crtc[c->crtc - drm->crtcs] = i; } assert(index != -1); possible_crtc[index] = conn->possible_crtc; - match_obj(wl_list_length(&drm->outputs), possible_crtc, drm->num_crtcs, - crtc, crtc_res); + match_obj(wl_list_length(&drm->outputs), possible_crtc, + drm->num_crtcs, crtc, crtc_res); - bool matched = false; + bool matched[num_outputs]; + memset(matched, false, sizeof(matched)); for (size_t i = 0; i < drm->num_crtcs; ++i) { - // We don't want any of the current monitors to be deactivated. - if (crtc[i] != UNMATCHED && crtc_res[i] == UNMATCHED) { - return; - } - if (crtc_res[i] == index) { - matched = true; + if (crtc_res[i] != UNMATCHED) { + matched[crtc_res[i]] = true; } } // There is no point doing anything if this monitor doesn't get activated - if (!matched) { + if (!matched[index]) { return; } + for (size_t i = 0; i < drm->num_crtcs; ++i) { + // We don't want any of the current monitors to be deactivated. + if (crtc[i] != UNMATCHED && !matched[crtc[i]]) { + return; + } + } + changed_outputs[index] = true; for (size_t i = 0; i < drm->num_crtcs; ++i) { @@ -711,6 +718,9 @@ void wlr_drm_scan_connectors(struct wlr_drm_backend *drm) { wlr_log_errno(L_ERROR, "Failed to get DRM connector"); continue; } + drmModeEncoder *curr_enc = drmModeGetEncoder(drm->fd, + drm_conn->encoder_id); + int index = -1; struct wlr_drm_connector *c, *wlr_conn = NULL; wl_list_for_each(c, &drm->outputs, link) { @@ -720,10 +730,12 @@ void wlr_drm_scan_connectors(struct wlr_drm_backend *drm) { break; } } + if (!wlr_conn) { wlr_conn = calloc(1, sizeof(*wlr_conn)); if (!wlr_conn) { wlr_log_errno(L_ERROR, "Allocation failed"); + drmModeFreeEncoder(curr_enc); drmModeFreeConnector(drm_conn); continue; } @@ -737,11 +749,8 @@ void wlr_drm_scan_connectors(struct wlr_drm_backend *drm) { wlr_conn->state = WLR_DRM_CONN_DISCONNECTED; wlr_conn->id = drm_conn->connector_id; - drmModeEncoder *curr_enc = drmModeGetEncoder(drm->fd, - drm_conn->encoder_id); if (curr_enc) { wlr_conn->old_crtc = drmModeGetCrtc(drm->fd, curr_enc->crtc_id); - drmModeFreeEncoder(curr_enc); } wlr_conn->output.phys_width = drm_conn->mmWidth; @@ -767,6 +776,17 @@ void wlr_drm_scan_connectors(struct wlr_drm_backend *drm) { seen[index] = true; } + if (curr_enc) { + for (size_t i = 0; i < drm->num_crtcs; ++i) { + if (drm->crtcs[i].id == curr_enc->crtc_id) { + wlr_conn->crtc = &drm->crtcs[i]; + break; + } + } + } else { + wlr_conn->crtc = NULL; + } + if (wlr_conn->state == WLR_DRM_CONN_DISCONNECTED && drm_conn->connection == DRM_MODE_CONNECTED) { @@ -802,6 +822,7 @@ void wlr_drm_scan_connectors(struct wlr_drm_backend *drm) { wlr_drm_connector_cleanup(wlr_conn); } + drmModeFreeEncoder(curr_enc); drmModeFreeConnector(drm_conn); } From 3ed3271b9817500b8fb2234381023204c5768ac6 Mon Sep 17 00:00:00 2001 From: Versus Void Date: Sun, 22 Oct 2017 21:44:24 +0000 Subject: [PATCH 4/4] Rescan connectors on DRM resume --- backend/drm/backend.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 9fbfe58c..40b559e2 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -62,10 +62,13 @@ static void session_signal(struct wl_listener *listener, void *data) { if (session->active) { wlr_log(L_INFO, "DRM fd resumed"); + wlr_drm_scan_connectors(drm); struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->outputs, link){ - wlr_drm_connector_start_renderer(conn); + if (conn->output.current_mode) { + wlr_output_set_mode(&conn->output, conn->output.current_mode); + } if (!conn->crtc) { continue;