From 06ad734e70227ad0527fe11b88ad37e93005ce0c Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 27 Apr 2023 10:25:40 +0200 Subject: [PATCH] scene_graph: Port view saved buffers --- include/sway/tree/view.h | 18 ++------ sway/desktop/transaction.c | 49 ++++++--------------- sway/desktop/xdg_shell.c | 10 ++++- sway/desktop/xwayland.c | 10 ++++- sway/tree/view.c | 88 ++++++++++++++++++++++++-------------- 5 files changed, 91 insertions(+), 84 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 8493958e..66d6db1c 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -57,21 +57,13 @@ struct sway_view_impl { void (*destroy)(struct sway_view *view); }; -struct sway_saved_buffer { - struct wlr_client_buffer *buffer; - int x, y; - int width, height; - enum wl_output_transform transform; - struct wlr_fbox source_box; - struct wl_list link; // sway_view::saved_buffers -}; - struct sway_view { enum sway_view_type type; const struct sway_view_impl *impl; struct wlr_scene_tree *scene_tree; struct wlr_scene_tree *content_tree; + struct wlr_scene_tree *saved_surface_tree; struct sway_container *container; // NULL if unmapped and transactions finished struct wlr_surface *surface; // NULL for unmapped views @@ -92,16 +84,10 @@ struct sway_view { bool allow_request_urgent; struct wl_event_source *urgent_timer; - struct wl_list saved_buffers; // sway_saved_buffer::link - // The geometry for whatever the client is committing, regardless of // transaction state. Updated on every commit. struct wlr_box geometry; - // The "old" geometry during a transaction. Used to damage the old location - // when a transaction is applied. - struct wlr_box saved_geometry; - struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; struct wl_listener foreign_activate_request; struct wl_listener foreign_fullscreen_request; @@ -347,4 +333,6 @@ bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx); +void view_send_frame_done(struct sway_view *view); + #endif diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index bb725795..1ae6642b 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -232,21 +232,6 @@ static void apply_workspace_state(struct sway_workspace *ws, static void apply_container_state(struct sway_container *container, struct sway_container_state *state) { struct sway_view *view = container->view; - // Damage the old location - desktop_damage_whole_container(container); - if (view && !wl_list_empty(&view->saved_buffers)) { - struct sway_saved_buffer *saved_buf; - wl_list_for_each(saved_buf, &view->saved_buffers, link) { - struct wlr_box box = { - .x = saved_buf->x - view->saved_geometry.x, - .y = saved_buf->y - view->saved_geometry.y, - .width = saved_buf->width, - .height = saved_buf->height, - }; - desktop_damage_box(&box); - } - } - // There are separate children lists for each instruction state, the // container's current state and the container's pending state // (ie. con->children). The list itself needs to be freed here. @@ -256,17 +241,19 @@ static void apply_container_state(struct sway_container *container, memcpy(&container->current, state, sizeof(struct sway_container_state)); - if (view && !wl_list_empty(&view->saved_buffers)) { - if (!container->node.destroying || container->node.ntxnrefs == 1) { - view_remove_saved_buffer(view); + if (view) { + if (view->saved_surface_tree) { + if (!container->node.destroying || container->node.ntxnrefs == 1) { + view_remove_saved_buffer(view); + } } - } - // If the view hasn't responded to the configure, center it within - // the container. This is important for fullscreen views which - // refuse to resize to the size of the output. - if (view && view->surface) { - view_center_surface(view); + // If the view hasn't responded to the configure, center it within + // the container. This is important for fullscreen views which + // refuse to resize to the size of the output. + if (view->surface) { + view_center_surface(view); + } } // Damage the new location @@ -415,21 +402,11 @@ static void transaction_commit(struct sway_transaction *transaction) { ++transaction->num_waiting; } - // From here on we are rendering a saved buffer of the view, which - // means we can send a frame done event to make the client redraw it - // as soon as possible. Additionally, this is required if a view is - // mapping and its default geometry doesn't intersect an output. - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_surface_send_frame_done( - node->sway_container->view->surface, &now); + view_send_frame_done(node->sway_container->view); } if (!hidden && node_is_view(node) && - wl_list_empty(&node->sway_container->view->saved_buffers)) { + !node->sway_container->view->saved_surface_tree) { view_save_buffer(node->sway_container->view); - memcpy(&node->sway_container->view->saved_geometry, - &node->sway_container->view->geometry, - sizeof(struct wlr_box)); } node->instruction = instruction; } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index fed820cf..95b5cb9d 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -332,8 +332,16 @@ static void handle_commit(struct wl_listener *listener, void *data) { } if (view->container->node.instruction) { - transaction_notify_view_ready_by_serial(view, + bool successful = transaction_notify_view_ready_by_serial(view, xdg_surface->current.configure_serial); + + // If we saved the view and this commit isn't what we're looking for + // that means the user will never actually see the buffers submitted to + // us here. Just send frame done events to these surfaces so they can + // commit another time for us. + if (view->saved_surface_tree && !successful) { + view_send_frame_done(view); + } } } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 0967c7fc..e0c80c07 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -442,8 +442,16 @@ static void handle_commit(struct wl_listener *listener, void *data) { } if (view->container->node.instruction) { - transaction_notify_view_ready_by_geometry(view, + bool successful = transaction_notify_view_ready_by_geometry(view, xsurface->x, xsurface->y, state->width, state->height); + + // If we saved the view and this commit isn't what we're looking for + // that means the user will never actually see the buffers submitted to + // us here. Just send frame done events to these surfaces so they can + // commit another time for us. + if (view->saved_surface_tree && !successful) { + view_send_frame_done(view); + } } } diff --git a/sway/tree/view.c b/sway/tree/view.c index ee25faf1..402fa179 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -56,7 +56,6 @@ bool view_init(struct sway_view *view, enum sway_view_type type, view->type = type; view->impl = impl; view->executed_criteria = create_list(); - wl_list_init(&view->saved_buffers); view->allow_request_urgent = true; view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; wl_signal_init(&view->events.unmap); @@ -77,9 +76,6 @@ void view_destroy(struct sway_view *view) { return; } wl_list_remove(&view->events.unmap.listener_list); - if (!wl_list_empty(&view->saved_buffers)) { - view_remove_saved_buffer(view); - } list_free(view->executed_criteria); view_assign_ctx(view, NULL); @@ -931,10 +927,10 @@ void view_center_surface(struct sway_view *view) { struct sway_container *con = view->container; // We always center the current coordinates rather than the next, as the // geometry immediately affects the currently active rendering. - con->surface_x = fmax(con->current.content_x, con->current.content_x + - (con->current.content_width - view->geometry.width) / 2); - con->surface_y = fmax(con->current.content_y, con->current.content_y + - (con->current.content_height - view->geometry.height) / 2); + int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); + int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); + + wlr_scene_node_set_position(&view->content_tree->node, x, y); } struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { @@ -1161,40 +1157,54 @@ bool view_is_urgent(struct sway_view *view) { } void view_remove_saved_buffer(struct sway_view *view) { - if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { + if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) { return; } - struct sway_saved_buffer *saved_buf, *tmp; - wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { - wlr_buffer_unlock(&saved_buf->buffer->base); - wl_list_remove(&saved_buf->link); - free(saved_buf); - } + + wlr_scene_node_destroy(&view->saved_surface_tree->node); + view->saved_surface_tree = NULL; + wlr_scene_node_set_enabled(&view->content_tree->node, true); } -static void view_save_buffer_iterator(struct wlr_surface *surface, +static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, int sx, int sy, void *data) { - struct sway_view *view = data; - - if (surface && surface->buffer) { - wlr_buffer_lock(&surface->buffer->base); - struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); - saved_buffer->buffer = surface->buffer; - saved_buffer->width = surface->current.width; - saved_buffer->height = surface->current.height; - saved_buffer->x = view->container->surface_x + sx; - saved_buffer->y = view->container->surface_y + sy; - saved_buffer->transform = surface->current.transform; - wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); - wl_list_insert(view->saved_buffers.prev, &saved_buffer->link); + struct wlr_scene_tree *tree = data; + + struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); + if (!sbuf) { + sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface"); + return; } + + wlr_scene_buffer_set_dest_size(sbuf, + buffer->dst_width, buffer->dst_height); + wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region); + wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box); + wlr_scene_node_set_position(&sbuf->node, sx, sy); + wlr_scene_buffer_set_transform(sbuf, buffer->transform); + wlr_scene_buffer_set_buffer(sbuf, buffer->buffer); } void view_save_buffer(struct sway_view *view) { - if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { + if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) { view_remove_saved_buffer(view); } - view_for_each_surface(view, view_save_buffer_iterator, view); + + view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree); + if (!view->saved_surface_tree) { + sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface"); + return; + } + + // Enable and disable the saved surface tree like so to atomitaclly update + // the tree. This will prevent over damaging or other weirdness. + wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false); + + wlr_scene_node_for_each_buffer(&view->content_tree->node, + view_save_buffer_iterator, view->saved_surface_tree); + + wlr_scene_node_set_enabled(&view->content_tree->node, false); + wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true); } bool view_is_transient_for(struct sway_view *child, @@ -1202,3 +1212,19 @@ bool view_is_transient_for(struct sway_view *child, return child->impl->is_transient_for && child->impl->is_transient_for(child, ancestor); } + +static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, + int x, int y, void *data) { + struct timespec *when = data; + wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); +} + +void view_send_frame_done(struct sway_view *view) { + struct timespec when; + clock_gettime(CLOCK_MONOTONIC, &when); + + struct wlr_scene_node *node; + wl_list_for_each(node, &view->content_tree->children, link) { + wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when); + } +}