diff --git a/include/wlr/types/wlr_output_manager.h b/include/wlr/types/wlr_output_manager.h index fef00a07e..6a8d26b4f 100644 --- a/include/wlr/types/wlr_output_manager.h +++ b/include/wlr/types/wlr_output_manager.h @@ -27,10 +27,15 @@ struct wlr_output_manager_backend { struct wl_list link; // wlr_output_manager.backends + struct { + struct wl_signal recovery; + } events; + // private state uint32_t locks; struct wl_listener backend_destroy; + struct wl_listener renderer_lost; }; struct wlr_output_manager { diff --git a/types/wlr_output_manager.c b/types/wlr_output_manager.c index 45ced4620..34942291d 100644 --- a/types/wlr_output_manager.c +++ b/types/wlr_output_manager.c @@ -17,6 +17,7 @@ static void output_manager_backend_finish( wlr_allocator_destroy(backend->allocator); wlr_renderer_destroy(backend->renderer); wl_list_remove(&backend->backend_destroy.link); + wl_list_remove(&backend->renderer_lost.link); wl_list_remove(&backend->link); } @@ -34,6 +35,44 @@ static void output_manager_handle_backend_destroy( } } +static void output_manager_handle_renderer_lost( + struct wl_listener *listener, void *data) { + struct wlr_output_manager_backend *backend = + wl_container_of(listener, backend, renderer_lost); + + wlr_log(WLR_INFO, "Attempting renderer recovery after GPU reset!"); + + struct wlr_renderer *renderer = wlr_renderer_autocreate(backend->backend); + if (!renderer) { + wlr_log(WLR_ERROR, "Could not create a new renderer after GPU reset"); + return; + } + + struct wlr_allocator *allocator = + wlr_allocator_autocreate(backend->backend, renderer); + if (!allocator) { + wlr_log(WLR_ERROR, "Could not create a new allocator after GPU reset"); + wlr_renderer_destroy(renderer); + return; + } + + wlr_log(WLR_INFO, "Created new renderer and allocator after reset. Attempting to swap..."); + + struct wlr_renderer *old_renderer = backend->renderer; + struct wlr_allocator *old_allocator = backend->allocator; + backend->renderer = renderer; + backend->allocator = allocator; + + wl_signal_add(&backend->renderer->events.lost, &backend->renderer_lost); + + // Only destroy the old state once we signal a recovery to avoid the old + // state being referenced during its destruction. + wlr_allocator_destroy(old_allocator); + wlr_renderer_destroy(old_renderer); + + wl_signal_emit_mutable(&backend->events.recovery, NULL); +} + static bool output_manager_backend_init(struct wlr_output_manager *manager, struct wlr_output_manager_backend *backend, struct wlr_backend *wlr_backend) { backend->renderer = wlr_renderer_autocreate(wlr_backend); @@ -52,9 +91,14 @@ static bool output_manager_backend_init(struct wlr_output_manager *manager, backend->backend = wlr_backend; backend->locks = 1; + wl_signal_init(&backend->events.recovery); + backend->backend_destroy.notify = output_manager_handle_backend_destroy; wl_signal_add(&wlr_backend->events.destroy, &backend->backend_destroy); + backend->renderer_lost.notify = output_manager_handle_renderer_lost; + wl_signal_add(&backend->renderer->events.lost, &backend->renderer_lost); + wl_list_insert(&manager->backends, &backend->link); return true; } @@ -152,7 +196,11 @@ void wlr_output_manager_unlock_backend(struct wlr_output_manager_backend *backen struct output_manager_output { struct wlr_output_manager_backend *backend; + struct wlr_output *output; struct wlr_addon addon; + + // recover from GPU resets + struct wl_listener backend_recovery; }; static void manager_output_handle_output_destroy(struct wlr_addon *addon) { @@ -160,6 +208,7 @@ static void manager_output_handle_output_destroy(struct wlr_addon *addon) { wl_container_of(addon, manager_output, addon); wlr_addon_finish(&manager_output->addon); wlr_output_manager_unlock_backend(manager_output->backend); + wl_list_remove(&manager_output->backend_recovery.link); free(manager_output); } @@ -168,6 +217,14 @@ static const struct wlr_addon_interface output_addon_impl = { .destroy = manager_output_handle_output_destroy, }; +static void output_handle_recovery(struct wl_listener *listener, void *data) { + struct output_manager_output *manager = wl_container_of(listener, manager, backend_recovery); + + // we lost the context, create a new renderer and switch everything out. + wlr_output_init_render(manager->output, manager->backend->allocator, + manager->backend->renderer); +} + bool wlr_output_manager_init_output(struct wlr_output_manager *manager, struct wlr_output *output) { struct output_manager_output *manager_output = calloc(1, sizeof(*manager_output)); @@ -175,6 +232,8 @@ bool wlr_output_manager_init_output(struct wlr_output_manager *manager, return false; } + manager_output->output = output; + manager_output->backend = wlr_output_manager_lock_backend( manager, output->backend); if (!manager_output->backend) { @@ -184,6 +243,9 @@ bool wlr_output_manager_init_output(struct wlr_output_manager *manager, wlr_addon_init(&manager_output->addon, &output->addons, manager, &output_addon_impl); + manager_output->backend_recovery.notify = output_handle_recovery; + wl_signal_add(&manager_output->backend->events.recovery, &manager_output->backend_recovery); + wlr_output_init_render(output, manager_output->backend->allocator, manager_output->backend->renderer);