From e97c2c3639119831ced4f6b9f704b096c2075973 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 22 Oct 2019 18:41:47 +0200 Subject: [PATCH] backend/drm: retry without modifiers for the primary plane On some Intel cards using modifiers can fill the FIFO and prevent hotplugged outputs from being properly enabled. Add a fallback without modifiers. Fixes: 2bdd1d0896cc ("backend/drm: use modifiers for our GBM buffers") References: https://github.com/swaywm/wlroots/issues/1840 Closes: https://github.com/swaywm/wlroots/issues/1852 --- backend/drm/drm.c | 95 ++++++++++++++++++++++++++-------- backend/drm/renderer.c | 9 ++-- include/backend/drm/renderer.h | 2 +- 3 files changed, 79 insertions(+), 27 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index b9a9d9ba..de9ddc09 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -500,27 +500,33 @@ static bool drm_connector_export_dmabuf(struct wlr_output *output, return export_drm_bo(surf->back, attribs); } -static void drm_connector_start_renderer(struct wlr_drm_connector *conn) { - if (conn->state != WLR_DRM_CONN_CONNECTED) { - return; - } - - wlr_log(WLR_DEBUG, "Starting renderer on output '%s'", conn->output.name); - +static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn, + struct wlr_drm_mode *mode) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(conn->output.backend); struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { - return; + wlr_log(WLR_ERROR, "Page-flip failed on connector '%s': no CRTC", + conn->output.name); + return false; } struct wlr_drm_plane *plane = crtc->primary; struct gbm_bo *bo = get_drm_surface_front( drm->parent ? &plane->mgpu_surf : &plane->surf); uint32_t fb_id = get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers); + return drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, &mode->drm_mode); +} + +static void drm_connector_start_renderer(struct wlr_drm_connector *conn) { + if (conn->state != WLR_DRM_CONN_CONNECTED) { + return; + } + + wlr_log(WLR_DEBUG, "Starting renderer on output '%s'", conn->output.name); struct wlr_drm_mode *mode = (struct wlr_drm_mode *)conn->output.current_mode; - if (drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, &mode->drm_mode)) { + if (drm_connector_pageflip_renderer(conn, mode)) { conn->pageflip_pending = true; wlr_output_update_enabled(&conn->output, true); } else { @@ -529,6 +535,53 @@ static void drm_connector_start_renderer(struct wlr_drm_connector *conn) { } } +static bool drm_connector_init_renderer(struct wlr_drm_connector *conn, + struct wlr_drm_mode *mode) { + struct wlr_drm_backend *drm = + get_drm_backend_from_backend(conn->output.backend); + + if (conn->state != WLR_DRM_CONN_CONNECTED && + conn->state != WLR_DRM_CONN_NEEDS_MODESET) { + return false; + } + + wlr_log(WLR_DEBUG, "Initializing renderer on connector '%s'", + conn->output.name); + + struct wlr_drm_crtc *crtc = conn->crtc; + if (!crtc) { + wlr_log(WLR_ERROR, "Failed to initialize renderer on connector '%s': " + "no CRTC", conn->output.name); + return false; + } + struct wlr_drm_plane *plane = crtc->primary; + + int width = mode->wlr_mode.width; + int height = mode->wlr_mode.height; + uint32_t format = drm->renderer.gbm_format; + + if (!init_drm_plane_surfaces(plane, drm, width, height, format, true) || + !drm_connector_pageflip_renderer(conn, mode)) { + // If page-flipping with modifiers enabled doesn't work, retry without + // modifiers + wlr_log(WLR_INFO, "Page-flip failed with primary FB modifiers enabled, " + "retrying without modifiers"); + finish_drm_surface(&plane->surf); + finish_drm_surface(&plane->mgpu_surf); + if (!init_drm_plane_surfaces(plane, drm, width, height, format, false)) { + return false; + } + if (!drm_connector_pageflip_renderer(conn, mode)) { + wlr_log(WLR_ERROR, "Failed to initialize renderer " + "on connector '%s': initial page-flip failed", + conn->output.name); + return false; + } + } + + return true; +} + static void realloc_crtcs(struct wlr_drm_backend *drm); static void attempt_enable_needs_modeset(struct wlr_drm_backend *drm) { @@ -581,7 +634,7 @@ bool enable_drm_connector(struct wlr_output *output, bool enable) { static void drm_connector_cleanup(struct wlr_drm_connector *conn); bool drm_connector_set_mode(struct wlr_output *output, - struct wlr_output_mode *mode) { + struct wlr_output_mode *wlr_mode) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); if (conn->crtc == NULL) { @@ -592,27 +645,26 @@ bool drm_connector_set_mode(struct wlr_output *output, wlr_log(WLR_ERROR, "Cannot modeset '%s': no CRTC for this connector", conn->output.name); // Save the desired mode for later, when we'll get a proper CRTC - conn->desired_mode = mode; + conn->desired_mode = wlr_mode; return false; } wlr_log(WLR_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", - conn->output.name, mode->width, mode->height, mode->refresh); + conn->output.name, wlr_mode->width, wlr_mode->height, + wlr_mode->refresh); - if (!init_drm_plane_surfaces(conn->crtc->primary, drm, - mode->width, mode->height, drm->renderer.gbm_format)) { + struct wlr_drm_mode *mode = (struct wlr_drm_mode *)wlr_mode; + if (!drm_connector_init_renderer(conn, mode)) { wlr_log(WLR_ERROR, "Failed to initialize renderer for plane"); return false; } conn->state = WLR_DRM_CONN_CONNECTED; conn->desired_mode = NULL; - wlr_output_update_mode(&conn->output, mode); + wlr_output_update_mode(&conn->output, wlr_mode); wlr_output_update_enabled(&conn->output, true); conn->desired_enabled = true; - drm_connector_start_renderer(conn); - // When switching VTs, the mode is not updated but the buffers become // invalid, so we need to manually damage the output here wlr_output_damage_whole(&conn->output); @@ -1102,17 +1154,14 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) { continue; } - struct wlr_output_mode *mode = conn->output.current_mode; - - if (!init_drm_plane_surfaces(conn->crtc->primary, drm, - mode->width, mode->height, drm->renderer.gbm_format)) { + struct wlr_drm_mode *mode = + (struct wlr_drm_mode *)conn->output.current_mode; + if (!drm_connector_init_renderer(conn, mode)) { wlr_log(WLR_ERROR, "Failed to initialize renderer for plane"); drm_connector_cleanup(conn); break; } - drm_connector_start_renderer(conn); - wlr_output_damage_whole(&conn->output); } } diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index f25ce247..a3bcc943 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -287,10 +287,13 @@ struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest, bool init_drm_plane_surfaces(struct wlr_drm_plane *plane, struct wlr_drm_backend *drm, int32_t width, uint32_t height, - uint32_t format) { + uint32_t format, bool with_modifiers) { + struct wlr_drm_format_set *format_set = + with_modifiers ? &plane->formats : NULL; + if (!drm->parent) { return init_drm_surface(&plane->surf, &drm->renderer, width, height, - format, &plane->formats, GBM_BO_USE_SCANOUT); + format, format_set, GBM_BO_USE_SCANOUT); } if (!init_drm_surface(&plane->surf, &drm->parent->renderer, @@ -299,7 +302,7 @@ bool init_drm_plane_surfaces(struct wlr_drm_plane *plane, } if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer, - width, height, format, &plane->formats, GBM_BO_USE_SCANOUT)) { + width, height, format, format_set, GBM_BO_USE_SCANOUT)) { finish_drm_surface(&plane->surf); return false; } diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index 1b1f1243..567cf95e 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -44,7 +44,7 @@ bool init_drm_surface(struct wlr_drm_surface *surf, bool init_drm_plane_surfaces(struct wlr_drm_plane *plane, struct wlr_drm_backend *drm, int32_t width, uint32_t height, - uint32_t format); + uint32_t format, bool with_modifiers); void finish_drm_surface(struct wlr_drm_surface *surf); bool make_drm_surface_current(struct wlr_drm_surface *surf, int *buffer_age);