Merge branch 'gpu-reset-recover' into 'master'

Automatic GPU reset recovery

See merge request wlroots/wlroots!3910
This commit is contained in:
Alexander Orzechowski 2024-10-09 09:31:41 +00:00
commit 863ed02d54
29 changed files with 1822 additions and 541 deletions

View file

@ -6,6 +6,7 @@
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_linux_drm_syncobj_v1.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_raster.h>
#include <wlr/util/transform.h>
#include "types/wlr_scene.h"
@ -76,28 +77,6 @@ static void scene_surface_handle_surface_destroy(
wlr_scene_node_destroy(&surface->buffer->node);
}
// This is used for wlr_scene where it unconditionally locks buffers preventing
// reuse of the existing texture for shm clients. With the usage pattern of
// wlr_scene surface handling, we can mark its locked buffer as safe
// for mutation.
static void client_buffer_mark_next_can_damage(struct wlr_client_buffer *buffer) {
buffer->n_ignore_locks++;
}
static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buffer) {
if (!scene_buffer->buffer) {
return;
}
struct wlr_client_buffer *buffer = wlr_client_buffer_get(scene_buffer->buffer);
if (!buffer) {
return;
}
assert(buffer->n_ignore_locks > 0);
buffer->n_ignore_locks--;
}
static int min(int a, int b) {
return a < b ? a : b;
}
@ -160,29 +139,13 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) {
wlr_scene_buffer_set_transform(scene_buffer, state->transform);
wlr_scene_buffer_set_opacity(scene_buffer, opacity);
scene_buffer_unmark_client_buffer(scene_buffer);
if (surface->buffer) {
client_buffer_mark_next_can_damage(surface->buffer);
struct wlr_raster *raster = wlr_raster_from_surface(surface);
if (raster) {
wlr_scene_buffer_set_raster_with_damage(scene_buffer,
raster, &surface->buffer_damage);
struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state =
wlr_linux_drm_syncobj_v1_get_surface_state(surface);
struct wlr_drm_syncobj_timeline *wait_timeline = NULL;
uint64_t wait_point = 0;
if (syncobj_surface_state != NULL) {
wait_timeline = syncobj_surface_state->acquire_timeline;
wait_point = syncobj_surface_state->acquire_point;
}
struct wlr_scene_buffer_set_buffer_options options = {
.damage = &surface->buffer_damage,
.wait_timeline = wait_timeline,
.wait_point = wait_point,
};
wlr_scene_buffer_set_buffer_with_options(scene_buffer,
&surface->buffer->base, &options);
if (syncobj_surface_state != NULL &&
(surface->current.committed & WLR_SURFACE_STATE_BUFFER)) {
wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state,
@ -192,6 +155,7 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) {
wlr_scene_buffer_set_buffer(scene_buffer, NULL);
}
wlr_raster_unlock(raster);
pixman_region32_fini(&opaque);
}
@ -231,8 +195,6 @@ static bool scene_buffer_point_accepts_input(struct wlr_scene_buffer *scene_buff
static void surface_addon_destroy(struct wlr_addon *addon) {
struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon);
scene_buffer_unmark_client_buffer(surface->buffer);
wlr_addon_finish(&surface->addon);
wl_list_remove(&surface->outputs_update.link);

View file

@ -10,6 +10,7 @@
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_raster.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
@ -91,8 +92,6 @@ struct highlight_region {
static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer,
struct wlr_buffer *buffer);
static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer,
struct wlr_texture *texture);
void wlr_scene_node_destroy(struct wlr_scene_node *node) {
if (node == NULL) {
@ -123,9 +122,8 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) {
}
scene_buffer_set_buffer(scene_buffer, NULL);
scene_buffer_set_texture(scene_buffer, NULL);
wlr_raster_unlock(scene_buffer->raster);
pixman_region32_fini(&scene_buffer->opaque_region);
wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline);
} else if (node->type == WLR_SCENE_NODE_TREE) {
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
@ -259,7 +257,7 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
} else if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
if (!scene_buffer->buffer) {
if (!scene_buffer->raster) {
return;
}
@ -267,7 +265,7 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
return;
}
if (!scene_buffer->buffer_is_opaque) {
if (!scene_buffer->raster->opaque) {
pixman_region32_copy(opaque, &scene_buffer->opaque_region);
pixman_region32_intersect_rect(opaque, opaque, 0, 0, width, height);
pixman_region32_translate(opaque, x, y);
@ -753,57 +751,17 @@ static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer,
if (scene_buffer->own_buffer) {
wlr_buffer_unlock(scene_buffer->buffer);
}
scene_buffer->buffer = NULL;
scene_buffer->buffer = buffer;
scene_buffer->own_buffer = false;
scene_buffer->buffer_width = scene_buffer->buffer_height = 0;
scene_buffer->buffer_is_opaque = false;
if (!buffer) {
return;
}
scene_buffer->own_buffer = true;
scene_buffer->buffer = wlr_buffer_lock(buffer);
scene_buffer->buffer_width = buffer->width;
scene_buffer->buffer_height = buffer->height;
scene_buffer->buffer_is_opaque = buffer_is_opaque(buffer);
scene_buffer->buffer_release.notify = scene_buffer_handle_buffer_release;
wl_signal_add(&buffer->events.release, &scene_buffer->buffer_release);
}
static void scene_buffer_handle_renderer_destroy(struct wl_listener *listener,
void *data) {
struct wlr_scene_buffer *scene_buffer = wl_container_of(listener, scene_buffer, renderer_destroy);
scene_buffer_set_texture(scene_buffer, NULL);
}
static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer,
struct wlr_texture *texture) {
wl_list_remove(&scene_buffer->renderer_destroy.link);
wlr_texture_destroy(scene_buffer->texture);
scene_buffer->texture = texture;
if (texture != NULL) {
scene_buffer->renderer_destroy.notify = scene_buffer_handle_renderer_destroy;
wl_signal_add(&texture->renderer->events.destroy, &scene_buffer->renderer_destroy);
} else {
wl_list_init(&scene_buffer->renderer_destroy.link);
}
}
static void scene_buffer_set_wait_timeline(struct wlr_scene_buffer *scene_buffer,
struct wlr_drm_syncobj_timeline *timeline, uint64_t point) {
wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline);
if (timeline != NULL) {
scene_buffer->wait_timeline = wlr_drm_syncobj_timeline_ref(timeline);
scene_buffer->wait_point = point;
} else {
scene_buffer->wait_timeline = NULL;
scene_buffer->wait_point = 0;
}
}
struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent,
struct wlr_buffer *buffer) {
struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer));
@ -822,8 +780,14 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent,
wl_list_init(&scene_buffer->buffer_release.link);
wl_list_init(&scene_buffer->renderer_destroy.link);
scene_buffer->opacity = 1;
scene_buffer_set_buffer(scene_buffer, buffer);
if (buffer) {
scene_buffer->raster = wlr_raster_create(buffer, NULL);
wlr_buffer_lock(buffer);
scene_buffer->own_buffer = true;
}
scene_node_update(&scene_buffer->node, NULL);
return scene_buffer;
@ -836,32 +800,47 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf
options = &default_options;
}
// specifying a region for a NULL buffer doesn't make sense. We need to know
// about the buffer to scale the buffer local coordinates down to scene
struct wlr_raster *raster = NULL;
if (buffer) {
raster = wlr_raster_create(buffer, &(struct wlr_raster_create_options) {
.wait_timeline = options->wait_timeline,
.wait_point = options->wait_point,
});
}
wlr_scene_buffer_set_raster_with_damage(scene_buffer, raster, options->damage);
if (raster) {
wlr_buffer_lock(buffer);
scene_buffer->own_buffer = true;
}
wlr_raster_unlock(raster);
}
void wlr_scene_buffer_set_raster_with_damage(struct wlr_scene_buffer *scene_buffer,
struct wlr_raster *raster, const pixman_region32_t *damage) {
// specifying a region for a NULL raster doesn't make sense. We need to know
// about the raster to scale the raster local coordinates down to scene
// coordinates.
assert(buffer || !options->damage);
assert(raster || !damage);
bool mapped = buffer != NULL;
bool prev_mapped = scene_buffer->buffer != NULL || scene_buffer->texture != NULL;
if (!mapped && !prev_mapped) {
// unmapping already unmapped buffer - noop
if (raster == scene_buffer->raster) {
return;
}
// if this node used to not be mapped or its previous displayed
// buffer region will be different from what the new buffer would
// produce we need to update the node.
bool update = mapped != prev_mapped;
if (buffer != NULL && scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0) {
update = update || scene_buffer->buffer_width != buffer->width ||
scene_buffer->buffer_height != buffer->height;
bool update = !raster != !scene_buffer->raster;
if (raster != NULL && scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0) {
update = update || scene_buffer->raster->width != raster->width ||
scene_buffer->raster->height != raster->height;
}
scene_buffer_set_buffer(scene_buffer, buffer);
scene_buffer_set_texture(scene_buffer, NULL);
scene_buffer_set_wait_timeline(scene_buffer,
options->wait_timeline, options->wait_point);
wlr_raster_unlock(scene_buffer->raster);
scene_buffer_set_buffer(scene_buffer, raster ? raster->buffer : NULL);
scene_buffer->raster = raster ? wlr_raster_lock(raster) : NULL;
if (update) {
scene_node_update(&scene_buffer->node, NULL);
@ -876,8 +855,7 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf
}
pixman_region32_t fallback_damage;
pixman_region32_init_rect(&fallback_damage, 0, 0, buffer->width, buffer->height);
const pixman_region32_t *damage = options->damage;
pixman_region32_init_rect(&fallback_damage, 0, 0, raster->width, raster->height);
if (!damage) {
damage = &fallback_damage;
}
@ -886,26 +864,26 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf
if (wlr_fbox_empty(&box)) {
box.x = 0;
box.y = 0;
box.width = buffer->width;
box.height = buffer->height;
box.width = raster->width;
box.height = raster->height;
}
wlr_fbox_transform(&box, &box, scene_buffer->transform,
buffer->width, buffer->height);
raster->width, raster->height);
float scale_x, scale_y;
if (scene_buffer->dst_width || scene_buffer->dst_height) {
scale_x = scene_buffer->dst_width / box.width;
scale_y = scene_buffer->dst_height / box.height;
} else {
scale_x = buffer->width / box.width;
scale_y = buffer->height / box.height;
scale_x = raster->width / box.width;
scale_y = raster->height / box.height;
}
pixman_region32_t trans_damage;
pixman_region32_init(&trans_damage);
wlr_region_transform(&trans_damage, damage,
scene_buffer->transform, buffer->width, buffer->height);
scene_buffer->transform, raster->width, raster->height);
pixman_region32_intersect_rect(&trans_damage, &trans_damage,
box.x, box.y, box.width, box.height);
pixman_region32_translate(&trans_damage, -box.x, -box.y);
@ -1055,28 +1033,6 @@ void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer,
scene_node_update(&scene_buffer->node, NULL);
}
static struct wlr_texture *scene_buffer_get_texture(
struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) {
if (scene_buffer->buffer == NULL || scene_buffer->texture != NULL) {
return scene_buffer->texture;
}
struct wlr_client_buffer *client_buffer =
wlr_client_buffer_get(scene_buffer->buffer);
if (client_buffer != NULL) {
return client_buffer->texture;
}
struct wlr_texture *texture =
wlr_texture_from_buffer(renderer, scene_buffer->buffer);
if (texture != NULL && scene_buffer->own_buffer) {
scene_buffer->own_buffer = false;
wlr_buffer_unlock(scene_buffer->buffer);
}
scene_buffer_set_texture(scene_buffer, texture);
return texture;
}
static void scene_node_get_size(struct wlr_scene_node *node,
int *width, int *height) {
*width = 0;
@ -1095,9 +1051,9 @@ static void scene_node_get_size(struct wlr_scene_node *node,
if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) {
*width = scene_buffer->dst_width;
*height = scene_buffer->dst_height;
} else {
*width = scene_buffer->buffer_width;
*height = scene_buffer->buffer_height;
} else if (scene_buffer->raster) {
*width = scene_buffer->raster->width;
*height = scene_buffer->raster->height;
wlr_output_transform_coords(scene_buffer->transform, width, height);
}
break;
@ -1380,8 +1336,12 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
case WLR_SCENE_NODE_BUFFER:;
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer,
data->output->output->renderer);
struct wlr_texture *texture = NULL;
if (scene_buffer->raster) {
texture = wlr_raster_obtain_texture(scene_buffer->raster,
data->output->output->renderer);
}
if (texture == NULL) {
scene_output_damage(data->output, &render_region);
break;
@ -1402,8 +1362,8 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
.blend_mode = !data->output->scene->calculate_visibility ||
pixman_region32_not_empty(&opaque) ?
WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE,
.wait_timeline = scene_buffer->wait_timeline,
.wait_point = scene_buffer->wait_point,
.wait_timeline = scene_buffer->raster->wait_timeline,
.wait_point = scene_buffer->raster->wait_point,
});
struct wlr_scene_output_sample_event sample_event = {
@ -1702,7 +1662,7 @@ static bool scene_node_invisible(struct wlr_scene_node *node) {
} else if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
return buffer->buffer == NULL && buffer->texture == NULL;
return buffer->raster == NULL;
}
return false;
@ -1825,8 +1785,8 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry,
return false;
}
int default_width = buffer->buffer->width;
int default_height = buffer->buffer->height;
int default_width = buffer->raster->width;
int default_height = buffer->raster->height;
wlr_output_transform_coords(buffer->transform, &default_width, &default_height);
struct wlr_fbox default_box = {
.width = default_width,
@ -1866,8 +1826,9 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry,
}
wlr_output_state_set_buffer(&pending, buffer->buffer);
if (buffer->wait_timeline != NULL) {
wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point);
if (buffer->raster->wait_timeline != NULL) {
wlr_output_state_set_wait_timeline(&pending,
buffer->raster->wait_timeline, buffer->raster->wait_point);
}
if (!wlr_output_test_state(scene_output->output, &pending)) {
@ -2125,6 +2086,20 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
return false;
}
// upload all the textures that will be used within this pass before we start
// rendering. We need to do this because some of those textures might be
// created as part of a multirender blit.
for (int i = list_len - 1; i >= 0; i--) {
struct render_list_entry *entry = &list_data[i];
if (entry->node->type != WLR_SCENE_NODE_BUFFER) {
continue;
}
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node);
wlr_raster_obtain_texture_with_allocator(buffer->raster,
output->renderer, output->allocator);
}
render_data.render_pass = render_pass;
pixman_region32_init(&render_data.damage);