From 3879f1f080c470a053f234f67c989f4f268c3b73 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 17 Jul 2024 17:13:51 -0700 Subject: [PATCH] backend/drm: Implement support for renderer loss recovery This implementation is nearly identical to Sway's, except that it also reloads the configuration, to spur on reloading the server-side decorations. v2: Fix style. v3: Add a reset to the magnifier. v4: Oops, restructure reset handler a bit. v5: Commit the magnifier reset immediately, before freeing the lost allocator and renderer. v6: Also check for failed render pass, which may return NULL. v7: Add a second NULL test, just in case. --- include/labwc.h | 2 ++ include/magnifier.h | 1 + src/magnifier.c | 28 ++++++++++++++++++++++---- src/server.c | 49 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 468da5b5..77bb41d4 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -318,6 +318,8 @@ struct server { */ int pending_output_layout_change; + struct wl_listener renderer_lost; + struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; struct wl_listener gamma_control_set_gamma; diff --git a/include/magnifier.h b/include/magnifier.h index 60005458..daa6d7a3 100644 --- a/include/magnifier.h +++ b/include/magnifier.h @@ -20,5 +20,6 @@ bool output_wants_magnification(struct output *output); void magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box *damage); bool is_magnify_on(void); +void magnify_reset(void); #endif /* LABWC_MAGNIFIER_H */ diff --git a/src/magnifier.c b/src/magnifier.c index 243455aa..fded35fd 100644 --- a/src/magnifier.c +++ b/src/magnifier.c @@ -10,6 +10,10 @@ static bool magnify_on; static double mag_scale = 0.0; +/* Reuse a single scratch buffer */ +static struct wlr_buffer *tmp_buffer = NULL; +static struct wlr_texture *tmp_texture = NULL; + #define CLAMP(in, lower, upper) MAX(MIN((in), (upper)), (lower)) void @@ -21,10 +25,6 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box struct wlr_fbox src_box; bool fullscreen = false; - /* Reuse a single scratch buffer */ - static struct wlr_buffer *tmp_buffer = NULL; - static struct wlr_texture *tmp_texture = NULL; - /* TODO: This looks way too complicated to just get the used format */ struct wlr_drm_format wlr_drm_format = {0}; struct wlr_shm_attributes shm_attribs = {0}; @@ -125,6 +125,10 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box /* Extract source region into temporary buffer */ struct wlr_render_pass *tmp_render_pass = wlr_renderer_begin_buffer_pass( server->renderer, tmp_buffer, NULL); + if (!tmp_render_pass) { + wlr_log(WLR_ERROR, "Failed to begin magnifier render pass"); + return; + } wlr_buffer_lock(output_buffer); struct wlr_texture *output_texture = wlr_texture_from_buffer( @@ -152,6 +156,10 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box /* Render to the output buffer itself */ tmp_render_pass = wlr_renderer_begin_buffer_pass( server->renderer, output_buffer, NULL); + if (!tmp_render_pass) { + wlr_log(WLR_ERROR, "Failed to begin second magnifier render pass"); + goto cleanup; + } /* Borders */ if (fullscreen) { @@ -286,6 +294,18 @@ magnify_set_scale(struct server *server, enum magnify_dir dir) } } +/* Reset any buffers held by the magnifier */ +void +magnify_reset(void) +{ + if (tmp_texture && tmp_buffer) { + wlr_texture_destroy(tmp_texture); + wlr_buffer_drop(tmp_buffer); + tmp_buffer = NULL; + tmp_texture = NULL; + } +} + /* Report whether magnification is enabled */ bool is_magnify_on(void) diff --git a/src/server.c b/src/server.c index b8beee2c..7f1caa30 100644 --- a/src/server.c +++ b/src/server.c @@ -28,6 +28,7 @@ #include "idle.h" #include "labwc.h" #include "layers.h" +#include "magnifier.h" #include "menu/menu.h" #include "output-state.h" #include "output-virtual.h" @@ -249,6 +250,51 @@ get_headless_backend(struct wlr_backend *backend, void *data) } } +static void +handle_renderer_lost(struct wl_listener *listener, void *data) +{ + struct server *server = wl_container_of(listener, server, renderer_lost); + + wlr_log(WLR_INFO, "Re-creating renderer after GPU reset"); + + struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); + if (!renderer) { + wlr_log(WLR_ERROR, "Unable to create renderer"); + return; + } + + struct wlr_allocator *allocator = + wlr_allocator_autocreate(server->backend, renderer); + if (!allocator) { + wlr_log(WLR_ERROR, "Unable to create allocator"); + wlr_renderer_destroy(renderer); + return; + } + + struct wlr_renderer *old_renderer = server->renderer; + struct wlr_allocator *old_allocator = server->allocator; + server->renderer = renderer; + server->allocator = allocator; + + wl_list_remove(&server->renderer_lost.link); + wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + + wlr_compositor_set_renderer(compositor, renderer); + + struct output *output; + wl_list_for_each(output, &server->outputs, link) { + wlr_output_init_render(output->wlr_output, + server->allocator, server->renderer); + } + + reload_config_and_theme(server); + + magnify_reset(); + + wlr_allocator_destroy(old_allocator); + wlr_renderer_destroy(old_renderer); +} + void server_init(struct server *server) { @@ -336,6 +382,9 @@ server_init(struct server *server) exit(EXIT_FAILURE); } + server->renderer_lost.notify = handle_renderer_lost; + wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + wlr_renderer_init_wl_display(server->renderer, server->wl_display); /*