From c6b067779b359de0a57e89768e8d0cf7585c4601 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 4 Feb 2022 13:41:23 +0300 Subject: [PATCH] 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. --- include/wlr/types/wlr_compositor.h | 1 + types/wlr_compositor.c | 839 ++++++++++++++--------------- 2 files changed, 407 insertions(+), 433 deletions(-) diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index 4c66cfc8d..0bbf257c0 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -165,6 +165,7 @@ struct wlr_surface { enum wl_output_transform transform; int width, height; int buffer_width, buffer_height; + struct wlr_fbox viewport_src; } previous; }; diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 273dbf534..dc5bb74f4 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -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, ¤t->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, + ¤t->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) {