diff --git a/libweston/backend-drm/drm-gbm.c b/libweston/backend-drm/drm-gbm.c index f3caa035e..b9344d85c 100644 --- a/libweston/backend-drm/drm-gbm.c +++ b/libweston/backend-drm/drm-gbm.c @@ -80,9 +80,13 @@ drm_backend_create_gl_renderer(struct drm_backend *b) if (format[1]) options.formats_count = 2; - return weston_compositor_init_renderer(b->compositor, - WESTON_RENDERER_GL, - &options.base); + if (!b->gl_recovering) + return weston_compositor_init_renderer(b->compositor, + WESTON_RENDERER_GL, + &options.base); + else + return b->compositor->renderer->gl->display_create(b->compositor, + &options); } static int @@ -525,24 +529,29 @@ drm_output_init_egl(struct drm_output *output, struct drm_backend *b) options.fb_size.width = mode->width; options.fb_size.height = mode->height; - assert(output->gbm_surface == NULL); - create_gbm_surface(b->gbm, output); - if (!output->gbm_surface) { - weston_log("failed to create gbm surface\n"); - return -1; + /* GBM surface and cursor do not need to be recreated when recovering */ + if (!b->gl_recovering) { + assert(output->gbm_surface == NULL); + create_gbm_surface(b->gbm, output); + if (!output->gbm_surface) { + weston_log("failed to create gbm surface\n"); + return -1; + } + + drm_output_init_cursor_egl(output, b); } options.window_for_legacy = (EGLNativeWindowType) output->gbm_surface; options.window_for_platform = output->gbm_surface; if (renderer->gl->output_window_create(&output->base, &options) < 0) { weston_log("failed to create gl renderer output state\n"); - gbm_surface_destroy(output->gbm_surface); - output->gbm_surface = NULL; + if (!b->gl_recovering) { + gbm_surface_destroy(output->gbm_surface); + output->gbm_surface = NULL; + } return -1; } - drm_output_init_cursor_egl(output, b); - return 0; } @@ -780,6 +789,65 @@ drm_output_fini_vulkan(struct drm_output *output) drm_output_fini_cursor_vulkan(output); } +static int +drm_handle_gl_renderer_error(struct weston_compositor *compositor, int err) +{ + const struct weston_renderer *renderer = compositor->renderer; + struct drm_backend *b = to_drm_backend(compositor); + int ret; + + switch (err) { + case WESTON_RENDERER_ERROR_LOST: { + struct weston_output *output; + + weston_log("Initiating GL renderer recovery...\n"); + b->gl_recovering = true; + renderer->gl->set_recovering(compositor, true); + + /* 1, Destroy the renderer outputs */ + wl_list_for_each(output, &compositor->output_list, link) + renderer->gl->output_destroy(output); + + /* 2, Destroy the renderer */ + renderer->destroy(compositor); + + /* 3, Create the renderer again */ + ret = drm_backend_create_gl_renderer(b); + if (ret < 0) { + weston_log("Failed to recreate gl renderer!\n"); + goto fail; + } + + /* 4, Create the renderer outputs again */ + wl_list_for_each(output, &compositor->output_list, link) { + ret = drm_output_init_egl(to_drm_output(output), b); + if (ret < 0) { + weston_log("Failed to recreate gl output!\n"); + goto fail; + } + } + + b->gl_recovering = false; + drm_device_recovery_required(b->drm); + renderer->gl->set_recovering(compositor, false); + ret = -EAGAIN; + + weston_log("GL renderer recovery completed successfully\n"); + break; + } + default: + weston_log("Unsupported gl renderer err: %d\n", err); + ret = -EINVAL; + } + + return ret; + +fail: + b->gl_recovering = false; + renderer->gl->set_recovering(compositor, false); + return -EIO; +} + struct drm_fb * drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) { @@ -787,9 +855,19 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) struct drm_device *device = output->device; struct gbm_bo *bo; struct drm_fb *ret; + int err; - output->base.compositor->renderer->repaint_output(&output->base, - damage, NULL); +repaint: + err = output->base.compositor->renderer->repaint_output(&output->base, + damage, NULL); + + if (err != WESTON_RENDERER_ERROR_NONE) { + err = drm_handle_gl_renderer_error(output->base.compositor, err); + if (err == -EAGAIN) + goto repaint; + + return NULL; + } bo = gbm_surface_lock_front_buffer(output->gbm_surface); if (!bo) { diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index d4ba085d0..3471b71d3 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -285,6 +285,9 @@ struct drm_backend { bool timer_armed; } perf_page_flips_stats; + /* True, if GL renderer is recovering from GPU reset */ + bool gl_recovering; + /* True if we need a workaround for some very old kernels */ bool stale_timestamp_workaround; };