Use wlroots surface locking for transactions

References: https://github.com/swaywm/wlroots/issues/2957
This commit is contained in:
Simon Ser 2021-07-02 12:02:12 +02:00
parent 22226560e3
commit 6f9940c039
7 changed files with 92 additions and 140 deletions

View file

@ -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.

View file

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

View file

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

View file

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

View file

@ -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 =

View file

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

View file

@ -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,