diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 8d52504e..1cc52d35 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -173,6 +173,56 @@ void wlr_drm_renderer_free(struct wlr_drm_renderer *renderer) { gbm_device_destroy(renderer->gbm); } +static bool wlr_drm_plane_renderer_init(struct wlr_drm_renderer *renderer, + struct wlr_drm_plane *plane, uint32_t width, uint32_t height) { + if (plane->width == width && plane->height == height) { + return true; + } + + plane->width = width; + plane->height = height; + + plane->gbm = gbm_surface_create(renderer->gbm, width, height, + GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + if (!plane->gbm) { + wlr_log_errno(L_ERROR, "Failed to create GBM surface for plane"); + return false; + } + + plane->egl = wlr_egl_create_surface(&renderer->egl, plane->gbm); + if (plane->egl == EGL_NO_SURFACE) { + wlr_log(L_ERROR, "Failed to create EGL surface for plane"); + return false; + } + + return true; +} + +static void wlr_drm_plane_renderer_free(struct wlr_drm_renderer *renderer, + struct wlr_drm_plane *plane) { + if (!renderer || !plane) + return; + + wlr_log(L_DEBUG, "%s called", __func__); + + if (plane->front) + gbm_surface_release_buffer(plane->gbm, plane->front); + if (plane->back) + gbm_surface_release_buffer(plane->gbm, plane->back); + + if (plane->egl) + eglDestroySurface(renderer->egl.display, plane->egl); + if (plane->gbm) + gbm_surface_destroy(plane->gbm); + + plane->width = 0; + plane->height = 0; + plane->egl = EGL_NO_SURFACE; + plane->gbm = NULL; + plane->front = NULL; + plane->back = NULL; +} + static void free_fb(struct gbm_bo *bo, void *data) { uint32_t id = (uintptr_t)data; @@ -258,40 +308,6 @@ void wlr_drm_output_start_renderer(struct wlr_output_state *output) { output->pageflip_pending = true; } -static bool plane_init_renderer(struct wlr_drm_renderer *renderer, - struct wlr_drm_plane *plane, struct wlr_output_mode *mode) { - plane->width = mode->width; - plane->height = mode->height; - - plane->gbm = gbm_surface_create(renderer->gbm, mode->width, - mode->height, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - if (!plane->gbm) { - wlr_log_errno(L_ERROR, "Failed to create GBM surface for plane"); - return false; - } - - plane->egl = wlr_egl_create_surface(&renderer->egl, plane->gbm); - if (plane->egl == EGL_NO_SURFACE) { - wlr_log(L_ERROR, "Failed to create EGL surface for plane"); - return false; - } - - return true; -} - -static int find_id(const void *item, const void *cmp_to) { - const struct wlr_output_state *output = item; - const uint32_t *id = cmp_to; - - if (output->connector < *id) { - return -1; - } else if (output->connector > *id) { - return 1; - } else { - return 0; - } -} - static void wlr_drm_output_enable(struct wlr_output_state *output, bool enable) { struct wlr_backend_state *state = wl_container_of(output->renderer, state, renderer); @@ -303,11 +319,7 @@ static void wlr_drm_output_enable(struct wlr_output_state *output, bool enable) drmModeConnectorSetProperty(state->fd, output->connector, output->props.dpms, DRM_MODE_DPMS_ON); - // Start rendering loop again by drawing a black frame - wlr_drm_output_make_current(output); - glClearColor(0.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - wlr_drm_output_swap_buffers(output); + wlr_drm_output_start_renderer(output); } else { drmModeConnectorSetProperty(state->fd, output->connector, output->props.dpms, DRM_MODE_DPMS_OFF); @@ -343,13 +355,19 @@ static void realloc_planes(struct wlr_backend_state *drm, const uint32_t *crtc_i continue; struct wlr_drm_crtc *c = &drm->crtcs[i]; - c->planes[type] = &drm->type_planes[type][crtc_res[i]]; + struct wlr_drm_plane **old = &c->planes[type]; + struct wlr_drm_plane *new = &drm->type_planes[type][crtc_res[i]]; + + if (*old != new) { + wlr_drm_plane_renderer_free(&drm->renderer, *old); + wlr_drm_plane_renderer_free(&drm->renderer, new); + *old = new; + } } } } static void realloc_crtcs(struct wlr_backend_state *drm, struct wlr_output_state *output) { - bool handled[drm->outputs->length]; uint32_t crtc[drm->num_crtcs]; uint32_t crtc_res[drm->num_crtcs]; uint32_t possible_crtc[drm->outputs->length]; @@ -359,18 +377,15 @@ static void realloc_crtcs(struct wlr_backend_state *drm, struct wlr_output_state } memset(possible_crtc, 0, sizeof(possible_crtc)); - memset(handled, 0, sizeof(handled)); size_t index; for (size_t i = 0; i < drm->outputs->length; ++i) { struct wlr_output_state *o = drm->outputs->items[i]; - if (o == output) { + if (o == output) index = i; - } - if (o->state != WLR_DRM_OUTPUT_CONNECTED) { + if (o->state != WLR_DRM_OUTPUT_CONNECTED) continue; - } possible_crtc[i] = o->possible_crtc; crtc[o->crtc - drm->crtcs] = i; @@ -379,14 +394,22 @@ static void realloc_crtcs(struct wlr_backend_state *drm, struct wlr_output_state possible_crtc[index] = output->possible_crtc; match_obj(drm->outputs->length, possible_crtc, drm->num_crtcs, crtc, crtc_res); - realloc_planes(drm, crtc_res); + bool matched = false; + 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; + } + + // There is no point doing anything if this monitor doesn't get activated + if (!matched) + return; for (size_t i = 0; i < drm->num_crtcs; ++i) { - if (crtc_res[i] == UNMATCHED) { + if (crtc_res[i] == UNMATCHED) continue; - } - - handled[crtc_res[i]] = true; if (crtc_res[i] != crtc[i]) { struct wlr_output_state *o = drm->outputs->items[crtc_res[i]]; @@ -394,11 +417,7 @@ static void realloc_crtcs(struct wlr_backend_state *drm, struct wlr_output_state } } - for (size_t i = 0; i < drm->outputs->length; ++i) { - if (!handled[i]) { - wlr_drm_output_cleanup(drm->outputs->items[i], false); - } - } + realloc_planes(drm, crtc_res); } static bool wlr_drm_output_set_mode(struct wlr_output_state *output, @@ -450,12 +469,24 @@ static bool wlr_drm_output_set_mode(struct wlr_output_state *output, output->base->current_mode = mode; wl_signal_emit(&output->base->events.resolution, output->base); - if (!plane_init_renderer(&drm->renderer, output->crtc->primary, mode)) { - wlr_log(L_ERROR, "Failed to initalise renderer for plane"); - goto error_enc; - } + // Since realloc_crtcs can deallocate planes on OTHER outputs, + // we actually need to reinitalise all of them + for (size_t i = 0; i < drm->outputs->length; ++i) { + struct wlr_output_state *output = drm->outputs->items[i]; + struct wlr_output_mode *mode = output->base->current_mode; + struct wlr_drm_crtc *crtc = output->crtc; + + if (output->state != WLR_DRM_OUTPUT_CONNECTED) + continue; - wlr_drm_output_start_renderer(output); + if (!wlr_drm_plane_renderer_init(&drm->renderer, crtc->primary, + mode->width, mode->height)) { + wlr_log(L_ERROR, "Failed to initalise renderer for plane"); + goto error_enc; + } + + wlr_drm_output_start_renderer(output); + } drmModeFreeEncoder(enc); drmModeFreeConnector(conn); @@ -566,6 +597,19 @@ static struct wlr_output_impl output_impl = { .swap_buffers = wlr_drm_output_swap_buffers, }; +static int find_id(const void *item, const void *cmp_to) { + const struct wlr_output_state *output = item; + const uint32_t *id = cmp_to; + + if (output->connector < *id) { + return -1; + } else if (output->connector > *id) { + return 1; + } else { + return 0; + } +} + static const int32_t subpixel_map[] = { [DRM_MODE_SUBPIXEL_UNKNOWN] = WL_OUTPUT_SUBPIXEL_UNKNOWN, [DRM_MODE_SUBPIXEL_HORIZONTAL_RGB] = WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB, @@ -742,6 +786,10 @@ void wlr_drm_output_cleanup(struct wlr_output_state *output, bool restore) { restore = false; } + wlr_drm_plane_renderer_free(renderer, output->crtc->overlay); + wlr_drm_plane_renderer_free(renderer, output->crtc->primary); + wlr_drm_plane_renderer_free(renderer, output->crtc->cursor); + output->crtc = NULL; output->possible_crtc = 0; /* Fallthrough */ diff --git a/backend/drm/drm.h b/backend/drm/drm.h index ec068cd0..feb932df 100644 --- a/backend/drm/drm.h +++ b/backend/drm/drm.h @@ -148,7 +148,6 @@ struct wlr_output_state { uint32_t cursor_width, cursor_height; bool pageflip_pending; - bool cleanup; }; bool wlr_drm_check_features(struct wlr_backend_state *drm); diff --git a/types/wlr_output.c b/types/wlr_output.c index fecfe5de..e79e12d3 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -206,7 +206,7 @@ bool wlr_output_set_cursor(struct wlr_output *output, output->cursor.texture = wlr_render_surface_init(output->cursor.renderer); } - wlr_surface_attach_pixels(output->cursor.texture, WL_SHM_FORMAT_ABGR8888, + wlr_surface_attach_pixels(output->cursor.texture, WL_SHM_FORMAT_ARGB8888, stride, width, height, buf); return true;