diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index 6ba8d8a07..6524aea25 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -239,6 +239,7 @@ struct wlr_surface { // private state struct wl_listener role_resource_destroy; + struct wl_listener current_buffer_release; struct { int32_t scale; @@ -250,6 +251,7 @@ struct wlr_surface { bool unmap_commit; bool opaque; + bool consumed; bool handling_commit; bool pending_rejected; diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 4b3e71554..24bdaa8fc 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -405,8 +405,12 @@ static void surface_state_move(struct wlr_surface_state *state, } static void surface_apply_damage(struct wlr_surface *surface) { + wl_list_remove(&surface->current_buffer_release.link); + if (surface->current.buffer == NULL) { // NULL commit + wl_list_init(&surface->current_buffer_release.link); + if (surface->buffer != NULL) { wlr_buffer_unlock(&surface->buffer->base); } @@ -415,13 +419,14 @@ static void surface_apply_damage(struct wlr_surface *surface) { return; } + wl_signal_add(&surface->current.buffer->events.release, + &surface->current_buffer_release); + surface->opaque = buffer_is_opaque(surface->current.buffer); 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; } } @@ -508,10 +513,26 @@ error: wl_resource_post_no_memory(surface->resource); } +static void surface_clean_state(struct wlr_surface *surface) { + assert(surface->consumed); + + wl_list_remove(&surface->current_buffer_release.link); + wl_list_init(&surface->current_buffer_release.link); + pixman_region32_clear(&surface->buffer_damage); + surface->current.buffer = NULL; + surface->consumed = false; +} + static void surface_commit_state(struct wlr_surface *surface, struct wlr_surface_state *next) { assert(next->cached_state_locks == 0); + // if the surface was consumed that means we don't own the current buffer + // anymore. + if (surface->consumed) { + surface_clean_state(surface); + } + bool invalid_buffer = next->committed & WLR_SURFACE_STATE_BUFFER; if (invalid_buffer && next->buffer == NULL) { @@ -562,10 +583,8 @@ static void surface_commit_state(struct wlr_surface *surface, // Release the buffer after emitting the commit event, so that listeners can // access it. Don't leave the buffer locked so that wl_shm buffers can be // released immediately on commit when they are uploaded to the GPU. + surface->consumed = true; wlr_buffer_unlock(surface->current.buffer); - surface->current.buffer = NULL; - - pixman_region32_clear(&surface->buffer_damage); } static void surface_handle_commit(struct wl_client *client, @@ -720,6 +739,10 @@ static void surface_destroy_role_object(struct wlr_surface *surface); static void surface_handle_resource_destroy(struct wl_resource *resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); + if (surface->consumed) { + surface_clean_state(surface); + } + struct wlr_surface_output *surface_output, *surface_output_tmp; wl_list_for_each_safe(surface_output, surface_output_tmp, &surface->current_outputs, link) { @@ -738,6 +761,7 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { surface_state_destroy_cached(cached, surface); } + wl_list_remove(&surface->current_buffer_release.link); wl_list_remove(&surface->role_resource_destroy.link); wl_list_remove(&surface->pending_buffer_resource_destroy.link); @@ -753,6 +777,12 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { free(surface); } +static void surface_handle_current_buffer_release(struct wl_listener *listener, + void *data) { + struct wlr_surface *surface = wl_container_of(listener, surface, current_buffer_release); + surface_clean_state(surface); +} + static struct wlr_surface *surface_create(struct wl_client *client, uint32_t version, uint32_t id, struct wlr_compositor *compositor) { struct wlr_surface *surface = calloc(1, sizeof(*surface)); @@ -797,6 +827,9 @@ static struct wlr_surface *surface_create(struct wl_client *client, surface->pending_buffer_resource_destroy.notify = pending_buffer_resource_handle_destroy; wl_list_init(&surface->pending_buffer_resource_destroy.link); + surface->current_buffer_release.notify = surface_handle_current_buffer_release; + wl_list_init(&surface->current_buffer_release.link); + return surface; }