From 4ec901bb33bdac70356bf00a23133d81ca0d10df Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 21 Nov 2023 16:39:36 +0100 Subject: [PATCH] scene: release shm buffers once uploaded Once a shm buffer is uploaded there's no reason for wlr_scene_buffer to keep it locked any further. Allow the compositor to re-use it. --- include/wlr/types/wlr_scene.h | 6 +++ types/scene/wlr_scene.c | 84 +++++++++++++++++++++++++---------- 2 files changed, 66 insertions(+), 24 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 76ac32ec..253de169 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -185,6 +185,12 @@ struct wlr_scene_buffer { uint64_t active_outputs; struct wlr_texture *texture; struct wlr_linux_dmabuf_feedback_v1_init_options prev_feedback_options; + + bool own_buffer; + int buffer_width, buffer_height; + bool buffer_is_opaque; + + struct wl_listener buffer_release; }; /** A viewport for an output in the scene-graph */ diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index d5aba68e..7c507179 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -82,6 +82,9 @@ struct highlight_region { struct wl_list link; }; +static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer); + void wlr_scene_node_destroy(struct wlr_scene_node *node) { if (node == NULL) { return; @@ -111,7 +114,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { } wlr_texture_destroy(scene_buffer->texture); - wlr_buffer_unlock(scene_buffer->buffer); + scene_buffer_set_buffer(scene_buffer, NULL); pixman_region32_fini(&scene_buffer->opaque_region); } else if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); @@ -249,7 +252,7 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, return; } - if (!buffer_is_opaque(scene_buffer->buffer)) { + if (!scene_buffer->buffer_is_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); @@ -600,6 +603,39 @@ void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[sta scene_node_update(&rect->node, NULL); } +static void scene_buffer_handle_buffer_release(struct wl_listener *listener, + void *data) { + struct wlr_scene_buffer *scene_buffer = + wl_container_of(listener, scene_buffer, buffer_release); + scene_buffer_set_buffer(scene_buffer, NULL); +} + +static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer) { + wl_list_remove(&scene_buffer->buffer_release.link); + wl_list_init(&scene_buffer->buffer_release.link); + if (scene_buffer->own_buffer) { + wlr_buffer_unlock(scene_buffer->buffer); + } + scene_buffer->buffer = NULL; + 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); +} + 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)); @@ -609,18 +645,16 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, assert(parent); scene_node_init(&scene_buffer->node, WLR_SCENE_NODE_BUFFER, parent); - if (buffer) { - scene_buffer->buffer = wlr_buffer_lock(buffer); - } - wl_signal_init(&scene_buffer->events.outputs_update); wl_signal_init(&scene_buffer->events.output_enter); wl_signal_init(&scene_buffer->events.output_leave); wl_signal_init(&scene_buffer->events.output_sample); wl_signal_init(&scene_buffer->events.frame_done); pixman_region32_init(&scene_buffer->opaque_region); + wl_list_init(&scene_buffer->buffer_release.link); scene_buffer->opacity = 1; + scene_buffer_set_buffer(scene_buffer, buffer); scene_node_update(&scene_buffer->node, NULL); return scene_buffer; @@ -642,19 +676,15 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff // 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. - update = !scene_buffer->buffer || - (scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0 && - (scene_buffer->buffer->width != buffer->width || - scene_buffer->buffer->height != buffer->height)); - - wlr_buffer_unlock(scene_buffer->buffer); - scene_buffer->buffer = wlr_buffer_lock(buffer); + update = scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0 && + (scene_buffer->buffer_width != buffer->width || + scene_buffer->buffer_height != buffer->height); } else { - wlr_buffer_unlock(scene_buffer->buffer); update = true; - scene_buffer->buffer = NULL; } + scene_buffer_set_buffer(scene_buffer, buffer); + if (update) { scene_node_update(&scene_buffer->node, NULL); // updating the node will already damage the whole node for us. Return @@ -842,18 +872,22 @@ void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, 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; } - if (scene_buffer->texture != NULL) { - return scene_buffer->texture; - } - scene_buffer->texture = wlr_texture_from_buffer(renderer, scene_buffer->buffer); + if (scene_buffer->texture != NULL && scene_buffer->own_buffer) { + scene_buffer->own_buffer = false; + wlr_buffer_unlock(scene_buffer->buffer); + } return scene_buffer->texture; } @@ -875,9 +909,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 if (scene_buffer->buffer) { - *width = scene_buffer->buffer->width; - *height = scene_buffer->buffer->height; + } else { + *width = scene_buffer->buffer_width; + *height = scene_buffer->buffer_height; wlr_output_transform_coords(scene_buffer->transform, width, height); } break; @@ -1155,7 +1189,6 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - assert(scene_buffer->buffer); struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, data->output->output->renderer); @@ -1403,7 +1436,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; + return buffer->buffer == NULL && buffer->texture == NULL; } return false; @@ -1539,6 +1572,9 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, } struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); + if (buffer->buffer == NULL) { + return false; + } int default_width = buffer->buffer->width; int default_height = buffer->buffer->height;