compositor: introduce surface state squashing

State squashing refers to the action of combining the state with the one
right before it; the older state becomes a "sum" of two states, and the
newer state becomes empty. This allows to free up memory and release
client resources as soon as possible.
This commit is contained in:
Kirill Primak 2022-02-04 13:41:23 +03:00
parent 81f35f6d98
commit c6b067779b
2 changed files with 407 additions and 433 deletions

View file

@ -33,6 +33,386 @@ static int max(int fst, int snd) {
}
}
static void surface_state_transformed_buffer_size(struct wlr_surface_state *state,
int *out_width, int *out_height) {
int width = state->buffer_width;
int height = state->buffer_height;
if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) {
int tmp = width;
width = height;
height = tmp;
}
*out_width = width;
*out_height = height;
}
/**
* Computes the surface viewport source size, ie. the size after applying the
* surface's scale, transform and cropping (via the viewport's source
* rectangle) but before applying the viewport scaling (via the viewport's
* destination rectangle).
*/
static void surface_state_viewport_src_size(struct wlr_surface_state *state,
int *out_width, int *out_height) {
if (state->buffer_width == 0 && state->buffer_height == 0) {
*out_width = *out_height = 0;
return;
}
if (state->viewport.has_src) {
*out_width = state->viewport.src.width;
*out_height = state->viewport.src.height;
} else {
surface_state_transformed_buffer_size(state,
out_width, out_height);
*out_width /= state->scale;
*out_height /= state->scale;
}
}
static void surface_finalize_pending(struct wlr_surface *surface) {
struct wlr_surface_state *pending = &surface->pending;
if ((pending->committed & WLR_SURFACE_STATE_BUFFER)) {
if (pending->buffer != NULL) {
pending->buffer_width = pending->buffer->width;
pending->buffer_height = pending->buffer->height;
} else {
pending->buffer_width = pending->buffer_height = 0;
}
}
if (!pending->viewport.has_src &&
(pending->buffer_width % pending->scale != 0 ||
pending->buffer_height % pending->scale != 0)) {
// TODO: send WL_SURFACE_ERROR_INVALID_SIZE error once this issue is
// resolved:
// https://gitlab.freedesktop.org/wayland/wayland/-/issues/194
wlr_log(WLR_DEBUG, "Client bug: submitted a buffer whose size (%dx%d) "
"is not divisible by scale (%d)", pending->buffer_width,
pending->buffer_height, pending->scale);
}
if (pending->viewport.has_dst) {
if (pending->buffer_width == 0 && pending->buffer_height == 0) {
pending->width = pending->height = 0;
} else {
pending->width = pending->viewport.dst_width;
pending->height = pending->viewport.dst_height;
}
} else {
surface_state_viewport_src_size(pending, &pending->width, &pending->height);
}
pixman_region32_intersect_rect(&pending->surface_damage,
&pending->surface_damage, 0, 0, pending->width, pending->height);
pixman_region32_intersect_rect(&pending->buffer_damage,
&pending->buffer_damage, 0, 0, pending->buffer_width,
pending->buffer_height);
}
static void surface_update_damage(struct wlr_surface *surface) {
struct wlr_surface_state *current = &surface->current;
pixman_region32_clear(&surface->buffer_damage);
if (current->width != surface->previous.width ||
current->height != surface->previous.height ||
current->viewport.src.x != surface->previous.viewport_src.x ||
current->viewport.src.y != surface->previous.viewport_src.y ||
current->viewport.src.width != surface->previous.viewport_src.width ||
current->viewport.src.height != surface->previous.viewport_src.height) {
// Damage the whole buffer on resize or viewport source box change
pixman_region32_union_rect(&surface->buffer_damage,
&surface->buffer_damage, 0, 0,
current->buffer_width, current->buffer_height);
} else {
// Copy over surface damage + buffer damage
pixman_region32_t surface_damage;
pixman_region32_init(&surface_damage);
pixman_region32_copy(&surface_damage, &current->surface_damage);
if (current->viewport.has_dst) {
int src_width, src_height;
surface_state_viewport_src_size(current, &src_width, &src_height);
float scale_x = (float)current->viewport.dst_width / src_width;
float scale_y = (float)current->viewport.dst_height / src_height;
wlr_region_scale_xy(&surface_damage, &surface_damage,
1.0 / scale_x, 1.0 / scale_y);
}
if (current->viewport.has_src) {
// This is lossy: do a best-effort conversion
pixman_region32_translate(&surface_damage,
floor(current->viewport.src.x),
floor(current->viewport.src.y));
}
wlr_region_scale(&surface_damage, &surface_damage, current->scale);
int width, height;
surface_state_transformed_buffer_size(current, &width, &height);
wlr_region_transform(&surface_damage, &surface_damage,
wlr_output_transform_invert(current->transform),
width, height);
pixman_region32_union(&surface->buffer_damage,
&current->buffer_damage, &surface_damage);
pixman_region32_fini(&surface_damage);
}
}
static void surface_apply_damage(struct wlr_surface *surface) {
if (surface->current.buffer == NULL) {
// NULL commit
if (surface->buffer != NULL) {
wlr_buffer_unlock(&surface->buffer->base);
}
surface->buffer = NULL;
return;
}
if (surface->buffer != NULL) {
if (wlr_client_buffer_apply_damage(surface->buffer,
surface->current.buffer, &surface->buffer_damage)) {
wlr_buffer_unlock(surface->current.buffer);
surface->current.buffer = NULL;
return;
}
}
struct wlr_client_buffer *buffer = wlr_client_buffer_create(
surface->current.buffer, surface->renderer);
wlr_buffer_unlock(surface->current.buffer);
surface->current.buffer = NULL;
if (buffer == NULL) {
wlr_log(WLR_ERROR, "Failed to upload buffer");
return;
}
if (surface->buffer != NULL) {
wlr_buffer_unlock(&surface->buffer->base);
}
surface->buffer = buffer;
}
static void surface_update_opaque_region(struct wlr_surface *surface) {
struct wlr_texture *texture = wlr_surface_get_texture(surface);
if (texture == NULL) {
pixman_region32_clear(&surface->opaque_region);
return;
}
if (wlr_texture_is_opaque(texture)) {
pixman_region32_init_rect(&surface->opaque_region,
0, 0, surface->current.width, surface->current.height);
return;
}
pixman_region32_intersect_rect(&surface->opaque_region,
&surface->current.opaque,
0, 0, surface->current.width, surface->current.height);
}
static void surface_update_input_region(struct wlr_surface *surface) {
pixman_region32_intersect_rect(&surface->input_region,
&surface->current.input,
0, 0, surface->current.width, surface->current.height);
}
static void surface_state_init(struct wlr_surface_state *state);
static void subsurface_parent_commit(struct wlr_subsurface *subsurface);
static void surface_precommit(struct wlr_surface *surface,
struct wlr_surface_state *next) {
surface->previous.scale = surface->current.scale;
surface->previous.transform = surface->current.transform;
surface->previous.width = surface->current.width;
surface->previous.height = surface->current.height;
surface->previous.buffer_width = surface->current.buffer_width;
surface->previous.buffer_height = surface->current.buffer_height;
surface->current.dx = 0;
surface->current.dy = 0;
pixman_region32_clear(&surface->current.surface_damage);
pixman_region32_clear(&surface->current.buffer_damage);
surface->current.committed = 0;
if (surface->role && surface->role->precommit) {
surface->role->precommit(surface, next);
}
}
static void surface_commit(struct wlr_surface *surface) {
surface->sx += surface->current.dx;
surface->sy += surface->current.dy;
surface_update_damage(surface);
pixman_region32_clear(&surface->external_damage);
if (surface->previous.width > surface->current.width ||
surface->previous.height > surface->current.height ||
surface->current.dx != 0 || surface->current.dy != 0) {
pixman_region32_union_rect(&surface->external_damage,
&surface->external_damage,
-surface->current.dx, -surface->current.dy,
surface->previous.width, surface->previous.height);
}
if (surface->current.committed & WLR_SURFACE_STATE_BUFFER) {
surface_apply_damage(surface);
}
surface_update_opaque_region(surface);
surface_update_input_region(surface);
// commit subsurface order
struct wlr_subsurface *subsurface;
wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_above,
pending.link) {
wl_list_remove(&subsurface->current.link);
wl_list_insert(&surface->current.subsurfaces_above,
&subsurface->current.link);
subsurface_parent_commit(subsurface);
}
wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_below,
pending.link) {
wl_list_remove(&subsurface->current.link);
wl_list_insert(&surface->current.subsurfaces_below,
&subsurface->current.link);
subsurface_parent_commit(subsurface);
}
if (surface->role && surface->role->commit) {
surface->role->commit(surface);
}
wlr_signal_emit_safe(&surface->events.commit, surface);
}
static void collect_subsurface_damage_iter(struct wlr_surface *surface,
int sx, int sy, void *data) {
struct wlr_subsurface *subsurface = data;
pixman_region32_t *damage = &subsurface->parent->external_damage;
pixman_region32_union_rect(damage, damage,
subsurface->current.x + sx,
subsurface->current.y + sy,
surface->current.width, surface->current.height);
}
// TODO: untangle from wlr_surface
static void subsurface_parent_commit(struct wlr_subsurface *subsurface) {
struct wlr_surface *surface = subsurface->surface;
bool moved = subsurface->current.x != subsurface->pending.x ||
subsurface->current.y != subsurface->pending.y;
if (subsurface->mapped && moved) {
wlr_surface_for_each_surface(surface,
collect_subsurface_damage_iter, subsurface);
}
if (subsurface->synchronized && subsurface->has_cache) {
wlr_surface_unlock_cached(surface, subsurface->cached_seq);
subsurface->has_cache = false;
}
subsurface->current.x = subsurface->pending.x;
subsurface->current.y = subsurface->pending.y;
if (subsurface->mapped && (moved || subsurface->reordered)) {
subsurface->reordered = false;
wlr_surface_for_each_surface(surface,
collect_subsurface_damage_iter, subsurface);
}
if (!subsurface->added) {
subsurface->added = true;
wlr_signal_emit_safe(&subsurface->parent->events.new_subsurface,
subsurface);
}
}
/**
* "Squash" a state by merging it into the state right before it in the queue
* (wlr_surface_state.link.prev) and clearing the given state. The previous
* state becomes a "sum" of those two states.
*/
static void surface_squash_state(struct wlr_surface *surface,
struct wlr_surface_state *src) {
assert(src != &surface->current);
struct wlr_surface_state *dst =
wl_container_of(src->link.prev, dst, link);
if (dst == &surface->current) {
surface_precommit(surface, src);
}
dst->width = src->width;
dst->height = src->height;
dst->buffer_width = src->buffer_width;
dst->buffer_height = src->buffer_height;
if (src->committed & WLR_SURFACE_STATE_SCALE) {
dst->scale = src->scale;
}
if (src->committed & WLR_SURFACE_STATE_TRANSFORM) {
dst->transform = src->transform;
}
if (src->committed & WLR_SURFACE_STATE_BUFFER) {
// Surface damage is in surface-local coordinates, so we need
// to take surface movement into account.
pixman_region32_translate(&dst->surface_damage,
-src->dx, -src->dy);
dst->dx += src->dx;
dst->dy += src->dy;
src->dx = src->dy = 0;
wlr_buffer_unlock(dst->buffer);
dst->buffer = src->buffer;
src->buffer = NULL;
} else {
dst->dx = dst->dy = 0;
}
if (src->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) {
pixman_region32_copy(&dst->surface_damage, &src->surface_damage);
pixman_region32_clear(&src->surface_damage);
} else {
pixman_region32_clear(&dst->surface_damage);
}
if (src->committed & WLR_SURFACE_STATE_BUFFER_DAMAGE) {
pixman_region32_copy(&dst->buffer_damage, &src->buffer_damage);
pixman_region32_clear(&src->buffer_damage);
} else {
pixman_region32_clear(&dst->buffer_damage);
}
if (src->committed & WLR_SURFACE_STATE_OPAQUE_REGION) {
pixman_region32_copy(&dst->opaque, &src->opaque);
}
if (src->committed & WLR_SURFACE_STATE_INPUT_REGION) {
pixman_region32_copy(&dst->input, &src->input);
}
if (src->committed & WLR_SURFACE_STATE_VIEWPORT) {
memcpy(&dst->viewport, &src->viewport, sizeof(dst->viewport));
}
if (src->committed & WLR_SURFACE_STATE_FRAME_CALLBACK_LIST) {
wl_list_insert_list(&dst->frame_callback_list,
&src->frame_callback_list);
wl_list_init(&src->frame_callback_list);
}
dst->committed |= src->committed;
src->committed = 0;
if (dst == &surface->current) {
surface_commit(surface);
}
}
static void surface_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
@ -124,393 +504,6 @@ static void surface_handle_set_input_region(struct wl_client *client,
}
}
static void surface_state_transformed_buffer_size(struct wlr_surface_state *state,
int *out_width, int *out_height) {
int width = state->buffer_width;
int height = state->buffer_height;
if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) {
int tmp = width;
width = height;
height = tmp;
}
*out_width = width;
*out_height = height;
}
/**
* Computes the surface viewport source size, ie. the size after applying the
* surface's scale, transform and cropping (via the viewport's source
* rectangle) but before applying the viewport scaling (via the viewport's
* destination rectangle).
*/
static void surface_state_viewport_src_size(struct wlr_surface_state *state,
int *out_width, int *out_height) {
if (state->buffer_width == 0 && state->buffer_height == 0) {
*out_width = *out_height = 0;
return;
}
if (state->viewport.has_src) {
*out_width = state->viewport.src.width;
*out_height = state->viewport.src.height;
} else {
surface_state_transformed_buffer_size(state,
out_width, out_height);
*out_width /= state->scale;
*out_height /= state->scale;
}
}
static void surface_finalize_pending(struct wlr_surface *surface) {
struct wlr_surface_state *pending = &surface->pending;
if ((pending->committed & WLR_SURFACE_STATE_BUFFER)) {
if (pending->buffer != NULL) {
pending->buffer_width = pending->buffer->width;
pending->buffer_height = pending->buffer->height;
} else {
pending->buffer_width = pending->buffer_height = 0;
}
}
if (!pending->viewport.has_src &&
(pending->buffer_width % pending->scale != 0 ||
pending->buffer_height % pending->scale != 0)) {
// TODO: send WL_SURFACE_ERROR_INVALID_SIZE error once this issue is
// resolved:
// https://gitlab.freedesktop.org/wayland/wayland/-/issues/194
wlr_log(WLR_DEBUG, "Client bug: submitted a buffer whose size (%dx%d) "
"is not divisible by scale (%d)", pending->buffer_width,
pending->buffer_height, pending->scale);
}
if (pending->viewport.has_dst) {
if (pending->buffer_width == 0 && pending->buffer_height == 0) {
pending->width = pending->height = 0;
} else {
pending->width = pending->viewport.dst_width;
pending->height = pending->viewport.dst_height;
}
} else {
surface_state_viewport_src_size(pending, &pending->width, &pending->height);
}
pixman_region32_intersect_rect(&pending->surface_damage,
&pending->surface_damage, 0, 0, pending->width, pending->height);
pixman_region32_intersect_rect(&pending->buffer_damage,
&pending->buffer_damage, 0, 0, pending->buffer_width,
pending->buffer_height);
}
static void surface_update_damage(pixman_region32_t *buffer_damage,
struct wlr_surface_state *current, struct wlr_surface_state *pending) {
pixman_region32_clear(buffer_damage);
if (pending->width != current->width ||
pending->height != current->height ||
pending->viewport.src.x != current->viewport.src.x ||
pending->viewport.src.y != current->viewport.src.y ||
pending->viewport.src.width != current->viewport.src.width ||
pending->viewport.src.height != current->viewport.src.height) {
// Damage the whole buffer on resize or viewport source box change
pixman_region32_union_rect(buffer_damage, buffer_damage, 0, 0,
pending->buffer_width, pending->buffer_height);
} else {
// Copy over surface damage + buffer damage
pixman_region32_t surface_damage;
pixman_region32_init(&surface_damage);
pixman_region32_copy(&surface_damage, &pending->surface_damage);
if (pending->viewport.has_dst) {
int src_width, src_height;
surface_state_viewport_src_size(pending, &src_width, &src_height);
float scale_x = (float)pending->viewport.dst_width / src_width;
float scale_y = (float)pending->viewport.dst_height / src_height;
wlr_region_scale_xy(&surface_damage, &surface_damage,
1.0 / scale_x, 1.0 / scale_y);
}
if (pending->viewport.has_src) {
// This is lossy: do a best-effort conversion
pixman_region32_translate(&surface_damage,
floor(pending->viewport.src.x),
floor(pending->viewport.src.y));
}
wlr_region_scale(&surface_damage, &surface_damage, pending->scale);
int width, height;
surface_state_transformed_buffer_size(pending, &width, &height);
wlr_region_transform(&surface_damage, &surface_damage,
wlr_output_transform_invert(pending->transform),
width, height);
pixman_region32_union(buffer_damage,
&pending->buffer_damage, &surface_damage);
pixman_region32_fini(&surface_damage);
}
}
/**
* Append pending state to current state and clear pending state.
*/
static void surface_state_move(struct wlr_surface_state *state,
struct wlr_surface_state *next) {
state->width = next->width;
state->height = next->height;
state->buffer_width = next->buffer_width;
state->buffer_height = next->buffer_height;
if (next->committed & WLR_SURFACE_STATE_SCALE) {
state->scale = next->scale;
}
if (next->committed & WLR_SURFACE_STATE_TRANSFORM) {
state->transform = next->transform;
}
if (next->committed & WLR_SURFACE_STATE_BUFFER) {
state->dx = next->dx;
state->dy = next->dy;
next->dx = next->dy = 0;
wlr_buffer_unlock(state->buffer);
state->buffer = NULL;
if (next->buffer) {
state->buffer = wlr_buffer_lock(next->buffer);
}
wlr_buffer_unlock(next->buffer);
next->buffer = NULL;
} else {
state->dx = state->dy = 0;
}
if (next->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) {
pixman_region32_copy(&state->surface_damage, &next->surface_damage);
pixman_region32_clear(&next->surface_damage);
} else {
pixman_region32_clear(&state->surface_damage);
}
if (next->committed & WLR_SURFACE_STATE_BUFFER_DAMAGE) {
pixman_region32_copy(&state->buffer_damage, &next->buffer_damage);
pixman_region32_clear(&next->buffer_damage);
} else {
pixman_region32_clear(&state->buffer_damage);
}
if (next->committed & WLR_SURFACE_STATE_OPAQUE_REGION) {
pixman_region32_copy(&state->opaque, &next->opaque);
}
if (next->committed & WLR_SURFACE_STATE_INPUT_REGION) {
pixman_region32_copy(&state->input, &next->input);
}
if (next->committed & WLR_SURFACE_STATE_VIEWPORT) {
memcpy(&state->viewport, &next->viewport, sizeof(state->viewport));
}
if (next->committed & WLR_SURFACE_STATE_FRAME_CALLBACK_LIST) {
wl_list_insert_list(&state->frame_callback_list,
&next->frame_callback_list);
wl_list_init(&next->frame_callback_list);
}
state->committed |= next->committed;
next->committed = 0;
state->seq = next->seq;
state->n_locks = next->n_locks;
next->n_locks = 0;
}
static void surface_apply_damage(struct wlr_surface *surface) {
if (surface->current.buffer == NULL) {
// NULL commit
if (surface->buffer != NULL) {
wlr_buffer_unlock(&surface->buffer->base);
}
surface->buffer = NULL;
return;
}
if (surface->buffer != NULL) {
if (wlr_client_buffer_apply_damage(surface->buffer,
surface->current.buffer, &surface->buffer_damage)) {
wlr_buffer_unlock(surface->current.buffer);
surface->current.buffer = NULL;
return;
}
}
struct wlr_client_buffer *buffer = wlr_client_buffer_create(
surface->current.buffer, surface->renderer);
wlr_buffer_unlock(surface->current.buffer);
surface->current.buffer = NULL;
if (buffer == NULL) {
wlr_log(WLR_ERROR, "Failed to upload buffer");
return;
}
if (surface->buffer != NULL) {
wlr_buffer_unlock(&surface->buffer->base);
}
surface->buffer = buffer;
}
static void surface_update_opaque_region(struct wlr_surface *surface) {
struct wlr_texture *texture = wlr_surface_get_texture(surface);
if (texture == NULL) {
pixman_region32_clear(&surface->opaque_region);
return;
}
if (wlr_texture_is_opaque(texture)) {
pixman_region32_init_rect(&surface->opaque_region,
0, 0, surface->current.width, surface->current.height);
return;
}
pixman_region32_intersect_rect(&surface->opaque_region,
&surface->current.opaque,
0, 0, surface->current.width, surface->current.height);
}
static void surface_update_input_region(struct wlr_surface *surface) {
pixman_region32_intersect_rect(&surface->input_region,
&surface->current.input,
0, 0, surface->current.width, surface->current.height);
}
static void surface_state_init(struct wlr_surface_state *state);
static void subsurface_parent_commit(struct wlr_subsurface *subsurface);
static void surface_cache_pending(struct wlr_surface *surface) {
struct wlr_surface_state *cached = calloc(1, sizeof(*cached));
if (!cached) {
wl_resource_post_no_memory(surface->resource);
return;
}
surface_state_init(cached);
surface_state_move(cached, &surface->pending);
// Insert before the pending state
wl_list_insert(surface->pending.link.prev, &cached->link);
surface->pending.seq++;
}
static void surface_commit_state(struct wlr_surface *surface,
struct wlr_surface_state *next) {
assert(next->n_locks == 0);
if (surface->role && surface->role->precommit) {
surface->role->precommit(surface, next);
}
bool invalid_buffer = next->committed & WLR_SURFACE_STATE_BUFFER;
surface->sx += next->dx;
surface->sy += next->dy;
surface_update_damage(&surface->buffer_damage, &surface->current, next);
pixman_region32_clear(&surface->external_damage);
if (surface->current.width > next->width ||
surface->current.height > next->height ||
next->dx != 0 || next->dy != 0) {
pixman_region32_union_rect(&surface->external_damage,
&surface->external_damage, -next->dx, -next->dy,
surface->current.width, surface->current.height);
}
surface->previous.scale = surface->current.scale;
surface->previous.transform = surface->current.transform;
surface->previous.width = surface->current.width;
surface->previous.height = surface->current.height;
surface->previous.buffer_width = surface->current.buffer_width;
surface->previous.buffer_height = surface->current.buffer_height;
surface_state_move(&surface->current, next);
if (invalid_buffer) {
surface_apply_damage(surface);
}
surface_update_opaque_region(surface);
surface_update_input_region(surface);
// commit subsurface order
struct wlr_subsurface *subsurface;
wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_above,
pending.link) {
wl_list_remove(&subsurface->current.link);
wl_list_insert(&surface->current.subsurfaces_above,
&subsurface->current.link);
subsurface_parent_commit(subsurface);
}
wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_below,
pending.link) {
wl_list_remove(&subsurface->current.link);
wl_list_insert(&surface->current.subsurfaces_below,
&subsurface->current.link);
subsurface_parent_commit(subsurface);
}
// If we're committing the pending state, bump the pending sequence number
// here, to allow commit listeners to lock the new pending state.
if (next == &surface->pending) {
surface->pending.seq++;
}
if (surface->role && surface->role->commit) {
surface->role->commit(surface);
}
wlr_signal_emit_safe(&surface->events.commit, surface);
}
static void collect_subsurface_damage_iter(struct wlr_surface *surface,
int sx, int sy, void *data) {
struct wlr_subsurface *subsurface = data;
pixman_region32_t *damage = &subsurface->parent->external_damage;
pixman_region32_union_rect(damage, damage,
subsurface->current.x + sx,
subsurface->current.y + sy,
surface->current.width, surface->current.height);
}
// TODO: untangle from wlr_surface
static void subsurface_parent_commit(struct wlr_subsurface *subsurface) {
struct wlr_surface *surface = subsurface->surface;
bool moved = subsurface->current.x != subsurface->pending.x ||
subsurface->current.y != subsurface->pending.y;
if (subsurface->mapped && moved) {
wlr_surface_for_each_surface(surface,
collect_subsurface_damage_iter, subsurface);
}
if (subsurface->synchronized && subsurface->has_cache) {
wlr_surface_unlock_cached(surface, subsurface->cached_seq);
subsurface->has_cache = false;
}
subsurface->current.x = subsurface->pending.x;
subsurface->current.y = subsurface->pending.y;
if (subsurface->mapped && (moved || subsurface->reordered)) {
subsurface->reordered = false;
wlr_surface_for_each_surface(surface,
collect_subsurface_damage_iter, subsurface);
}
if (!subsurface->added) {
subsurface->added = true;
wlr_signal_emit_safe(&subsurface->parent->events.new_subsurface,
subsurface);
}
}
static void surface_handle_commit(struct wl_client *client,
struct wl_resource *resource) {
struct wlr_surface *surface = wlr_surface_from_resource(resource);
@ -518,12 +511,21 @@ static void surface_handle_commit(struct wl_client *client,
wlr_signal_emit_safe(&surface->events.client_commit, NULL);
if (surface->pending.n_locks > 0 ||
surface->pending.link.prev != &surface->current.link) {
surface_cache_pending(surface);
} else {
surface_commit_state(surface, &surface->pending);
if (surface->pending.n_locks > 0) {
struct wlr_surface_state *cached = calloc(1, sizeof(*cached));
if (!cached) {
wl_resource_post_no_memory(surface->resource);
return;
}
surface_state_init(cached);
wl_list_insert(surface->pending.link.prev, &cached->link);
cached->seq = surface->pending.seq;
cached->n_locks = surface->pending.n_locks;
surface->pending.n_locks = 0;
}
++surface->pending.seq;
surface_squash_state(surface, &surface->pending);
}
static void surface_handle_set_buffer_transform(struct wl_client *client,
@ -753,52 +755,23 @@ uint32_t wlr_surface_lock_pending(struct wlr_surface *surface) {
}
void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq) {
if (surface->pending.seq == seq) {
assert(surface->pending.n_locks > 0);
surface->pending.n_locks--;
return;
}
bool found = false;
struct wlr_surface_state *cached;
wl_list_for_each(cached, &surface->states, link) {
if (cached == &surface->current || cached == &surface->pending) {
struct wlr_surface_state *state;
wl_list_for_each(state, &surface->states, link) {
if (state == &surface->current) {
continue;
}
if (cached->seq == seq) {
found = true;
break;
if (state->seq == seq) {
assert(state->n_locks > 0);
--state->n_locks;
if (state->n_locks == 0 && state != &surface->pending) {
surface_squash_state(surface, state);
surface_state_destroy_cached(state);
}
return;
}
}
assert(found);
assert(cached->n_locks > 0);
cached->n_locks--;
if (cached->n_locks != 0) {
return;
}
if (cached->link.prev != &surface->current.link) {
// This isn't the first cached state. This means we're blocked on a
// previous cached state.
return;
}
// TODO: consider merging all committed states together
struct wlr_surface_state *tmp;
wl_list_for_each_safe(cached, tmp, &surface->states, link) {
if (cached == &surface->current) {
continue;
}
if (cached == &surface->pending || cached->n_locks > 0) {
break;
}
surface_commit_state(surface, cached);
surface_state_destroy_cached(cached);
}
assert(0 && "Can't find a state to unlock");
}
struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface) {