mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2025-11-14 06:59:43 -05:00
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:
parent
81f35f6d98
commit
c6b067779b
2 changed files with 407 additions and 433 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue