From 696eebccddc7591086560319d2d3b670a1eceeb8 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Wed, 22 Feb 2023 09:52:38 +0900 Subject: [PATCH] subsurface: Add cached states This makes subsurface work with cached states depending on the sync mode. Change-Id: I186de52c6c4bc20329e61e7ec2a85185dedfd6c8 --- src/compositor/compositor_private.h | 19 ++- src/compositor/subsurface.c | 81 +++++++++- src/compositor/surface.c | 287 +++++++++++++++++++----------------- 3 files changed, 252 insertions(+), 135 deletions(-) diff --git a/src/compositor/compositor_private.h b/src/compositor/compositor_private.h index 1860be3..d8cf53b 100644 --- a/src/compositor/compositor_private.h +++ b/src/compositor/compositor_private.h @@ -7,7 +7,6 @@ #include #include "libds/log.h" -#include "libds/util/defs.h" #include "libds/util/box.h" #include "libds/util/addon.h" #include "libds/types/ds_surface.h" @@ -42,6 +41,9 @@ struct ds_subsurface struct wl_listener parent_destroy; } listener; + struct ds_surface_state cached; + bool has_cache; + bool synchronized; bool reordered; bool mapped; @@ -55,6 +57,16 @@ void subcompositor_finish(struct ds_subcompositor *subcomp); struct ds_surface * create_surface(struct wl_client *client, uint32_t version, uint32_t id); +void surface_state_move(struct ds_surface_state *state, + struct ds_surface_state *next); + +void surface_commit_state(struct ds_surface *surface, + struct ds_surface_state *next); + +void surface_state_init(struct ds_surface_state *state); + +void surface_state_finish(struct ds_surface_state *state); + struct ds_surface * ds_surface_get_root_surface(struct ds_surface *surface); @@ -69,4 +81,9 @@ subsurface_from_ds_surface(struct ds_surface *surface); struct ds_surface * subsurface_get_parent(struct ds_subsurface *subsurface); +void subsurface_commit(struct ds_subsurface *subsurface); + +void subsurface_parent_commit(struct ds_subsurface *subsurface, + bool synchronized); + #endif diff --git a/src/compositor/subsurface.c b/src/compositor/subsurface.c index 719bb4c..d21a2a4 100644 --- a/src/compositor/subsurface.c +++ b/src/compositor/subsurface.c @@ -14,6 +14,8 @@ static void subsurface_link_parent(struct ds_subsurface *subsurface, static void subsurface_unlink_parent(struct ds_subsurface *subsurface); static struct ds_subsurface *subsurface_find_sibling(struct ds_subsurface *subsurface, struct ds_surface *surface); +static bool subsurface_is_synchronized(struct ds_subsurface *subsurface); +static void subsurface_synchronized_commit(struct ds_subsurface *subsurface); struct ds_subsurface * create_subsurface(struct wl_resource *subcomp_resource, @@ -70,6 +72,47 @@ ds_surface_is_subsurface(struct ds_surface *surface) return ds_surface_get_role(surface) == &subsurface_role; } +void +subsurface_commit(struct ds_subsurface *subsurface) +{ + struct ds_surface *surface = subsurface->surface; + struct ds_subsurface *child; + + if (subsurface_is_synchronized(subsurface)) { + surface_state_move(&subsurface->cached, &surface->pending); + subsurface->has_cache = true; + } + else { + if (subsurface->has_cache) { + surface_state_move(&subsurface->cached, &surface->pending); + surface_commit_state(surface, &subsurface->cached); + subsurface->has_cache = false; + } + else { + surface_commit_state(surface, &surface->pending); + } + + wl_list_for_each(child, &surface->current.subsurfaces_below, current.link) + subsurface_parent_commit(child, false); + wl_list_for_each(child, &surface->current.subsurfaces_above, current.link) + subsurface_parent_commit(child, false); + } +} + +void +subsurface_parent_commit(struct ds_subsurface *subsurface, bool synchronized) +{ + if (subsurface->current.x != subsurface->pending.x || + subsurface->current.y != subsurface->pending.y) { + subsurface->current.x = subsurface->pending.x; + subsurface->current.y = subsurface->pending.y; + } + + if (synchronized || subsurface->synchronized) { + subsurface_synchronized_commit(subsurface); + } +} + WL_EXPORT struct ds_subsurface * ds_subsurface_from_resource(struct wl_resource *resource) { @@ -212,7 +255,8 @@ subsurface_handle_set_desync(struct wl_client *client, if (subsurface->synchronized) { subsurface->synchronized = false; - // TODO: flush + if (!subsurface_is_synchronized(subsurface)) + subsurface_synchronized_commit(subsurface); } } @@ -237,6 +281,7 @@ subsurface_destroy(struct ds_subsurface *subsurface) if (subsurface->surface) subsurface_unlink_surface(subsurface); + surface_state_finish(&subsurface->cached); wl_resource_set_user_data(subsurface->resource, NULL); free(subsurface); @@ -336,3 +381,37 @@ subsurface_find_sibling(struct ds_subsurface *subsurface, struct ds_surface *sur return NULL; } + +static bool +subsurface_is_synchronized(struct ds_subsurface *subsurface) +{ + struct ds_subsurface *iter = subsurface; + + do { + if (iter->synchronized) + return true; + + if (!iter->parent) + break; + } while ((iter = subsurface_from_ds_surface(iter->parent))); + + return false; +} + +static void +subsurface_synchronized_commit(struct ds_subsurface *subsurface) +{ + struct ds_surface *surface = subsurface->surface; + struct ds_subsurface *child; + + if (subsurface->has_cache) { + surface_state_move(&subsurface->cached, &surface->pending); + surface_commit_state(surface, &subsurface->cached); + subsurface->has_cache = false; + } + + wl_list_for_each(child, &surface->current.subsurfaces_below, current.link) + subsurface_parent_commit(child, true); + wl_list_for_each(child, &surface->current.subsurfaces_above, current.link) + subsurface_parent_commit(child, true); +} diff --git a/src/compositor/surface.c b/src/compositor/surface.c index 9e58548..7ab9b29 100644 --- a/src/compositor/surface.c +++ b/src/compositor/surface.c @@ -8,15 +8,12 @@ static const struct wl_surface_interface surface_impl; static void surface_handle_resource_destroy(struct wl_resource *resource); -static void surface_state_init(struct ds_surface_state *state); -static void surface_state_finish(struct ds_surface_state *state); -static void surface_state_move(struct ds_surface_state *state, - struct ds_surface_state *next); static void surface_finalize_pending(struct ds_surface *surface); -static void surface_commit_state(struct ds_surface *surface, - struct ds_surface_state *next); static bool surface_for_each(struct ds_surface *surface, int x, int y, ds_surface_for_each_func_t iterator, void *data); +static void surface_update_damage(pixman_region32_t *buffer_damage, + struct ds_surface_state *current, struct ds_surface_state *pending); +static void surface_update_buffer(struct ds_surface *surface); WL_EXPORT void ds_surface_add_destroy_listener(struct ds_surface *surface, @@ -303,6 +300,147 @@ ds_surface_get_wl_resource(struct ds_surface *surface) return surface->resource; } +void +surface_state_move(struct ds_surface_state *state, struct ds_surface_state *next) +{ + state->width = next->width; + state->height = next->height; + state->buffer_width = next->buffer_width; + state->buffer_height = next->buffer_height; + + if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_SCALE)) + state->scale = next->scale; + + if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_TRANSFORM)) + state->transform = next->transform; + + if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_BUFFER)) { + state->dx = next->dx; + state->dy = next->dy; + next->dx = next->dy = 0; + + if (state->buffer) { + ds_buffer_unlock(state->buffer); + state->buffer = NULL; + } + + if (next->buffer) { + state->buffer = ds_buffer_lock(next->buffer); + ds_buffer_unlock(next->buffer); + next->buffer = NULL; + } + } + else { + state->dx = state->dy = 0; + } + + if (DS_FLAG_IS_SET(next->committed, DS_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 (DS_FLAG_IS_SET(next->committed, DS_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 (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_OPAQUE_REGION)) + pixman_region32_copy(&state->opaque, &next->opaque); + + if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_INPUT_REGION)) + pixman_region32_copy(&state->input, &next->input); + + if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_VIEWPORT)) + memcpy(&state->viewport, &next->viewport, sizeof(state->viewport)); + + if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_FRAME_CALLBACK_LIST)) { + wl_list_insert_list(&state->frame_callback_list, + &next->frame_callback_list); + wl_list_init(&next->frame_callback_list); + } + + // FIXME + // state->committed |= next->committed; ?? + state->committed = next->committed; + next->committed = 0; +} + +void +surface_commit_state(struct ds_surface *surface, struct ds_surface_state *next) +{ + struct ds_subsurface *subsurface; + + surface_finalize_pending(surface); + + surface->sx += next->dx; + surface->sy += next->dy; + surface_update_damage(&surface->buffer_damage, &surface->current, next); + + surface_state_move(&surface->current, next); + + // FIXME no need? + if (DS_FLAG_IS_SET(surface->current.committed, DS_SURFACE_STATE_BUFFER)) + surface_update_buffer(surface); + + + 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); + } + 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); + } + + if (surface->role && surface->role->commit) + surface->role->commit(surface); + + wl_signal_emit(&surface->events.commit, surface); +} + +void +surface_state_init(struct ds_surface_state *state) +{ + state->scale = 1; + state->transform = WL_OUTPUT_TRANSFORM_NORMAL; + + wl_list_init(&state->subsurfaces_above); + wl_list_init(&state->subsurfaces_below); + + wl_list_init(&state->frame_callback_list); + + pixman_region32_init(&state->surface_damage); + pixman_region32_init(&state->buffer_damage); + pixman_region32_init(&state->opaque); + pixman_region32_init_rect(&state->input, + INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); +} + +void +surface_state_finish(struct ds_surface_state *state) +{ + struct wl_resource *resource, *tmp; + + if (state->buffer) + ds_buffer_unlock(state->buffer); + + wl_resource_for_each_safe(resource, tmp, &state->frame_callback_list) + wl_resource_destroy(resource); + + pixman_region32_fini(&state->surface_damage); + pixman_region32_fini(&state->buffer_damage); + pixman_region32_fini(&state->opaque); + pixman_region32_fini(&state->input); +} + static void surface_handle_destroy(struct wl_client *client, struct wl_resource *resource) { @@ -442,18 +580,24 @@ static void surface_handle_commit(struct wl_client *client, struct wl_resource *resource) { struct ds_surface *surface; + struct ds_subsurface *subsurface, *child; surface = wl_resource_get_user_data(resource); ds_dbg("ds_surface(%p) commit", surface); - // TODO handle subsurface - - surface_finalize_pending(surface); + if (ds_surface_is_subsurface(surface)) { + subsurface = subsurface_from_ds_surface(surface); + subsurface_commit(subsurface); + return; + } surface_commit_state(surface, &surface->pending); - // TODO handle subsurfaces of a given surface + wl_list_for_each(child, &surface->current.subsurfaces_below, current.link) + subsurface_parent_commit(child, false); + wl_list_for_each(child, &surface->current.subsurfaces_above, current.link) + subsurface_parent_commit(child, false); } static void @@ -564,110 +708,6 @@ surface_handle_resource_destroy(struct wl_resource *resource) } static void -surface_state_init(struct ds_surface_state *state) -{ - state->scale = 1; - state->transform = WL_OUTPUT_TRANSFORM_NORMAL; - - wl_list_init(&state->subsurfaces_above); - wl_list_init(&state->subsurfaces_below); - - wl_list_init(&state->frame_callback_list); - - pixman_region32_init(&state->surface_damage); - pixman_region32_init(&state->buffer_damage); - pixman_region32_init(&state->opaque); - pixman_region32_init_rect(&state->input, - INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); -} - -static void -surface_state_finish(struct ds_surface_state *state) -{ - struct wl_resource *resource, *tmp; - - if (state->buffer) - ds_buffer_unlock(state->buffer); - - wl_resource_for_each_safe(resource, tmp, &state->frame_callback_list) - wl_resource_destroy(resource); - - pixman_region32_fini(&state->surface_damage); - pixman_region32_fini(&state->buffer_damage); - pixman_region32_fini(&state->opaque); - pixman_region32_fini(&state->input); -} - -static void -surface_state_move(struct ds_surface_state *state, struct ds_surface_state *next) -{ - state->width = next->width; - state->height = next->height; - state->buffer_width = next->buffer_width; - state->buffer_height = next->buffer_height; - - if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_SCALE)) - state->scale = next->scale; - - if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_TRANSFORM)) - state->transform = next->transform; - - if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_BUFFER)) { - state->dx = next->dx; - state->dy = next->dy; - next->dx = next->dy = 0; - - if (state->buffer) { - ds_buffer_unlock(state->buffer); - state->buffer = NULL; - } - - if (next->buffer) { - state->buffer = ds_buffer_lock(next->buffer); - ds_buffer_unlock(next->buffer); - next->buffer = NULL; - } - } - else { - state->dx = state->dy = 0; - } - - if (DS_FLAG_IS_SET(next->committed, DS_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 (DS_FLAG_IS_SET(next->committed, DS_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 (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_OPAQUE_REGION)) - pixman_region32_copy(&state->opaque, &next->opaque); - - if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_INPUT_REGION)) - pixman_region32_copy(&state->input, &next->input); - - if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_VIEWPORT)) - memcpy(&state->viewport, &next->viewport, sizeof(state->viewport)); - - if (DS_FLAG_IS_SET(next->committed, DS_SURFACE_STATE_FRAME_CALLBACK_LIST)) { - wl_list_insert_list(&state->frame_callback_list, - &next->frame_callback_list); - wl_list_init(&next->frame_callback_list); - } - - // FIXME - // state->committed |= next->committed; ?? - state->committed = next->committed; - next->committed = 0; -} - -static void surface_state_viewport_src_size(struct ds_surface_state *state, int *out_width, int *out_height) { @@ -826,25 +866,6 @@ surface_update_buffer(struct ds_surface *surface) } } -static void -surface_commit_state(struct ds_surface *surface, struct ds_surface_state *next) -{ - surface->sx += next->dx; - surface->sy += next->dy; - surface_update_damage(&surface->buffer_damage, &surface->current, next); - - surface_state_move(&surface->current, next); - - // FIXME no need? - if (DS_FLAG_IS_SET(surface->current.committed, DS_SURFACE_STATE_BUFFER)) - surface_update_buffer(surface); - - if (surface->role && surface->role->commit) - surface->role->commit(surface); - - wl_signal_emit(&surface->events.commit, surface); -} - static bool surface_for_each(struct ds_surface *surface, int x, int y, ds_surface_for_each_func_t iterator, void *data) -- 2.7.4