From 6f9940c03935e7a7a557a18b6cd1e20f98a6d2b2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 2 Jul 2021 12:02:12 +0200 Subject: [PATCH] Use wlroots surface locking for transactions References: https://github.com/swaywm/wlroots/issues/2957 --- include/sway/desktop/transaction.h | 5 +++ include/sway/tree/view.h | 17 +++----- sway/desktop/output.c | 4 -- sway/desktop/render.c | 69 +----------------------------- sway/desktop/transaction.c | 61 +++++++++++++++----------- sway/desktop/xdg_shell.c | 25 +++++++++++ sway/tree/view.c | 51 ++++++++-------------- 7 files changed, 92 insertions(+), 140 deletions(-) diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 7dd58ba8c..f65602f47 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -42,6 +42,11 @@ void transaction_commit_dirty_client(void); void transaction_notify_view_ready_by_serial(struct sway_view *view, uint32_t serial); +/** + * Unlock the cached surface state held by the transaction system. + */ +void transaction_unlock_view_by_serial(struct sway_view *view, uint32_t serial); + /** * Notify the transaction system that a view is ready for the new layout, but * identifying the instruction by geometry rather than by serial. diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 86bd981c3..45a5db6ea 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -56,15 +56,6 @@ 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; @@ -87,7 +78,8 @@ struct sway_view { bool allow_request_urgent; struct wl_event_source *urgent_timer; - struct wl_list saved_buffers; // sway_saved_buffer::link + bool surface_locked; + uint32_t surface_locked_seq; // The geometry for whatever the client is committing, regardless of // transaction state. Updated on every commit. @@ -129,6 +121,7 @@ struct sway_view { struct sway_xdg_shell_view { struct sway_view view; + struct wl_listener cache; struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -357,9 +350,9 @@ void view_set_urgent(struct sway_view *view, bool enable); bool view_is_urgent(struct sway_view *view); -void view_remove_saved_buffer(struct sway_view *view); +void view_lock_pending(struct sway_view *view); -void view_save_buffer(struct sway_view *view); +void view_unlock_pending(struct sway_view *view); bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index aa1482907..edbbaa23f 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -432,10 +432,6 @@ static bool scan_out_fullscreen_view(struct sway_output *output, return false; } - if (!wl_list_empty(&view->saved_buffers)) { - return false; - } - for (int i = 0; i < workspace->current.floating->length; ++i) { struct sway_container *floater = workspace->current.floating->items[i]; diff --git a/sway/desktop/render.c b/sway/desktop/render.c index bf1b8666c..9740c928a 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -289,76 +289,13 @@ static void render_view_popups(struct sway_view *view, render_surface_iterator, &data); } -static void render_saved_view(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - struct wlr_output *wlr_output = output->wlr_output; - - if (wl_list_empty(&view->saved_buffers)) { - return; - } - - bool floating = container_is_current_floating(view->container); - - struct sway_saved_buffer *saved_buf; - wl_list_for_each(saved_buf, &view->saved_buffers, link) { - if (!saved_buf->buffer->texture) { - continue; - } - - struct wlr_box proj_box = { - .x = saved_buf->x - view->saved_geometry.x - output->lx, - .y = saved_buf->y - view->saved_geometry.y - output->ly, - .width = saved_buf->width, - .height = saved_buf->height, - }; - - struct wlr_box output_box = { - .width = output->width, - .height = output->height, - }; - - struct wlr_box intersection; - bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box); - if (!intersects) { - continue; - } - - struct wlr_box dst_box = proj_box; - scale_box(&proj_box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); - wlr_matrix_project_box(matrix, &proj_box, transform, 0, - wlr_output->transform_matrix); - - if (!floating) { - dst_box.width = fmin(dst_box.width, - view->container->current.content_width - - (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x); - dst_box.height = fmin(dst_box.height, - view->container->current.content_height - - (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y); - } - scale_box(&dst_box, wlr_output->scale); - - render_texture(wlr_output, damage, saved_buf->buffer->texture, - &saved_buf->source_box, &dst_box, matrix, alpha); - } - - // FIXME: we should set the surface that this saved buffer originates from - // as sampled here. - // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 -} - /** * Render a view's surface and left/bottom/right borders. */ static void render_view(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, struct border_colors *colors) { struct sway_view *view = con->view; - if (!wl_list_empty(&view->saved_buffers)) { - render_saved_view(view, output, damage, view->container->alpha); - } else if (view->surface) { + if (view->surface) { render_view_toplevels(view, output, damage, view->container->alpha); } @@ -1063,9 +1000,7 @@ void output_render(struct sway_output *output, struct timespec *when, } if (fullscreen_con->view) { - if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { - render_saved_view(fullscreen_con->view, output, damage, 1.0f); - } else if (fullscreen_con->view->surface) { + if (fullscreen_con->view->surface) { render_view_toplevels(fullscreen_con->view, output, damage, 1.0f); } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index b1f3fb326..00ca57949 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -232,20 +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 @@ -256,12 +242,6 @@ 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 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. @@ -303,6 +283,7 @@ static void transaction_apply(struct sway_transaction *transaction) { } // Apply the instruction state to the node's current state + // Applying the last instruction may destroy the transaction for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; @@ -355,8 +336,29 @@ static int handle_timeout(void *data) { struct sway_transaction *transaction = data; sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", transaction, transaction->num_waiting); - transaction->num_waiting = 0; - transaction_progress(); + + struct sway_view **views = + calloc(transaction->instructions->length, sizeof(struct sway_view *)); + size_t views_len = 0; + + for (int i = 0; i < transaction->instructions->length; ++i) { + struct sway_transaction_instruction *instruction = + transaction->instructions->items[i]; + struct sway_node *node = instruction->node; + + if (node->type == N_CONTAINER && node->sway_container->view && + node->sway_container->view->surface_locked) { + views[views_len] = node->sway_container->view; + views_len++; + } + } + + // This will destroy the transaction + for (size_t i = 0; i < views_len; ++i) { + view_unlock_pending(views[i]); + } + free(views); + return 0; } @@ -415,7 +417,7 @@ static void transaction_commit(struct sway_transaction *transaction) { ++transaction->num_waiting; } - // From here on we are rendering a saved buffer of the view, which + // From here on we are freezing surface commits for 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. @@ -425,8 +427,8 @@ static void transaction_commit(struct sway_transaction *transaction) { node->sway_container->view->surface, &now); } if (!hidden && node_is_view(node) && - wl_list_empty(&node->sway_container->view->saved_buffers)) { - view_save_buffer(node->sway_container->view); + !node->sway_container->view->surface_locked) { + view_lock_pending(node->sway_container->view); memcpy(&node->sway_container->view->saved_geometry, &node->sway_container->view->geometry, sizeof(struct wlr_box)); @@ -508,6 +510,15 @@ void transaction_notify_view_ready_by_serial(struct sway_view *view, } } +void transaction_unlock_view_by_serial(struct sway_view *view, + uint32_t serial) { + struct sway_transaction_instruction *instruction = + view->container->node.instruction; + if (instruction != NULL && instruction->serial == serial) { + view_unlock_pending(view); + } +} + void transaction_notify_view_ready_by_geometry(struct sway_view *view, double x, double y, int width, int height) { struct sway_transaction_instruction *instruction = diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index d34654fd4..1ce91ae05 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -279,6 +279,26 @@ static const struct sway_view_impl view_impl = { .destroy = destroy, }; +static void handle_cache(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, cache); + struct wlr_surface_state *cached = data; + struct sway_view *view = &xdg_shell_view->view; + struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; + + if (!view->container->node.instruction) { + return; + } + + struct wlr_xdg_surface_state *xdg_cached; + wl_list_for_each(xdg_cached, &xdg_surface->cached, cached_state_link) { + if (cached->seq == xdg_cached->seq) { + transaction_unlock_view_by_serial(view, xdg_cached->configure_serial); + break; + } + } +} + static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit); @@ -418,6 +438,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { view_unmap(view); + wl_list_remove(&xdg_shell_view->cache.link); wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); wl_list_remove(&xdg_shell_view->request_fullscreen.link); @@ -458,6 +479,10 @@ static void handle_map(struct wl_listener *listener, void *data) { transaction_commit_dirty(); + xdg_shell_view->cache.notify = handle_cache; + wl_signal_add(&xdg_surface->surface->events.cache, + &xdg_shell_view->cache); + xdg_shell_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, &xdg_shell_view->commit); diff --git a/sway/tree/view.c b/sway/tree/view.c index fcdd06f72..05e4866d1 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -37,7 +37,6 @@ void 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); @@ -57,11 +56,12 @@ 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); + if (view->surface_locked) { + view_unlock_pending(view); } list_free(view->executed_criteria); + free(view->title_format); if (view->impl->destroy) { @@ -1376,41 +1376,28 @@ bool view_is_urgent(struct sway_view *view) { return view->urgent.tv_sec || view->urgent.tv_nsec; } -void view_remove_saved_buffer(struct sway_view *view) { - if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { +void view_lock_pending(struct sway_view *view) { + if (!sway_assert(!view->surface_locked, + "Didn't expect surface to be already locked")) { + view_unlock_pending(view); + } + + if (view->surface == NULL) { 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); - } + + // TODO: maybe lock the whole surface tree? + view->surface_locked = true; + view->surface_locked_seq = wlr_surface_lock_pending(view->surface); } -static void view_save_buffer_iterator(struct wlr_surface *surface, - int sx, int sy, void *data) { - struct sway_view *view = data; - - if (surface && wlr_surface_has_buffer(surface)) { - 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, &saved_buffer->link); +void view_unlock_pending(struct sway_view *view) { + if (!sway_assert(view->surface_locked, "Expected surface to be locked")) { + return; } -} -void view_save_buffer(struct sway_view *view) { - if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { - view_remove_saved_buffer(view); - } - view_for_each_surface(view, view_save_buffer_iterator, view); + view->surface_locked = false; + wlr_surface_unlock_cached(view->surface, view->surface_locked_seq); } bool view_is_transient_for(struct sway_view *child,