backend-drm: Reinit GL renderer after GPU reset

For each frame, if the WESTON_RENDERER_ERROR_LOST occurs, we destroy
the context (which has already become invalid due to a GPU reset) and
all associated resources, then recreate them to ensure validity within
the new context.

Co-authored-by: Yuling Li <Yuling.Li@amd.com>
Signed-off-by: Trigger Huang <Trigger.Huang@amd.com>
This commit is contained in:
Trigger Huang 2026-04-24 12:15:11 +08:00
parent 8737e8e4d1
commit 3b18e3a69c
2 changed files with 95 additions and 14 deletions

View file

@ -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) {

View file

@ -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;
};