From 8afc1ed68cfaab0c85dd3252f7680d0a6e9e511e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 28 Feb 2020 12:32:43 +0100 Subject: [PATCH] Introduce wlr_client_buffer Split out the client/resource handling out of wlr_buffer by introducing wlr_client_buffer. Make wlr_buffer an interface so that compositors can create their own wlr_buffers (e.g. backed by GBM, like glider [1]). [1]: https://github.com/emersion/glider/blob/c66847dd1cf8ae5e68666ce7cb3be41427b38dc7/include/gbm_allocator.h#L7 --- include/wlr/types/wlr_buffer.h | 67 ++++++++++----- include/wlr/types/wlr_surface.h | 2 +- types/wlr_buffer.c | 148 ++++++++++++++++++++------------ types/wlr_surface.c | 22 +++-- 4 files changed, 154 insertions(+), 85 deletions(-) diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index 86f8bccb..b46b69a0 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -13,14 +13,52 @@ #include #include +struct wlr_buffer; + +struct wlr_buffer_impl { + void (*destroy)(struct wlr_buffer *buffer); + bool (*get_dmabuf)(struct wlr_buffer *buffer, + struct wlr_dmabuf_attributes *attribs); +}; + +struct wlr_buffer { + const struct wlr_buffer_impl *impl; + + size_t n_refs; +}; + +void wlr_buffer_init(struct wlr_buffer *buffer, + const struct wlr_buffer_impl *impl); +/** + * Reference the buffer. + */ +struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer); +/** + * Unreference the buffer. After this call, `buffer` may not be accessed + * anymore. + */ +void wlr_buffer_unref(struct wlr_buffer *buffer); +/** + * Reads the DMA-BUF attributes of the buffer. If this buffer isn't a DMA-BUF, + * returns false. + */ +bool wlr_buffer_get_dmabuf(struct wlr_buffer *buffer, + struct wlr_dmabuf_attributes *attribs); + /** * A client buffer. */ -struct wlr_buffer { +struct wlr_client_buffer { + struct wlr_buffer base; + /** * The buffer resource, if any. Will be NULL if the client destroys it. */ struct wl_resource *resource; + /** + * Whether a release event has been sent to the resource. + */ + bool resource_released; /** * The buffer's texture, if any. A buffer will not have a texture if the * client destroys the buffer before it has been released. @@ -41,23 +79,13 @@ bool wlr_resource_is_buffer(struct wl_resource *resource); /** * Get the size of a wl_buffer resource. */ -bool wlr_buffer_get_resource_size(struct wl_resource *resource, +bool wlr_resource_get_buffer_size(struct wl_resource *resource, struct wlr_renderer *renderer, int *width, int *height); - /** * Upload a buffer to the GPU and reference it. */ -struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, - struct wl_resource *resource); -/** - * Reference the buffer. - */ -struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer); -/** - * Unreference the buffer. After this call, `buffer` may not be accessed - * anymore. - */ -void wlr_buffer_unref(struct wlr_buffer *buffer); +struct wlr_client_buffer *wlr_client_buffer_create( + struct wlr_renderer *renderer, struct wl_resource *resource); /** * Try to update the buffer's content. On success, returns the updated buffer * and destroys the provided `buffer`. On error, `buffer` is intact and NULL is @@ -66,13 +94,8 @@ void wlr_buffer_unref(struct wlr_buffer *buffer); * Fails if there's more than one reference to the buffer or if the texture * isn't mutable. */ -struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, - struct wl_resource *resource, pixman_region32_t *damage); -/** - * Reads the DMA-BUF attributes of the buffer. If this buffer isn't a DMA-BUF, - * returns false. - */ -bool wlr_buffer_get_dmabuf(struct wlr_buffer *buffer, - struct wlr_dmabuf_attributes *attribs); +struct wlr_client_buffer *wlr_client_buffer_apply_damage( + struct wlr_client_buffer *buffer, struct wl_resource *resource, + pixman_region32_t *damage); #endif diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index f62171d1..fa9fa6d5 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -59,7 +59,7 @@ struct wlr_surface { * have a buffer if it has never committed one, has committed a null buffer, * or something went wrong with uploading the buffer. */ - struct wlr_buffer *buffer; + struct wlr_client_buffer *buffer; /** * The buffer position, in surface-local units. */ diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c index 1a95bcc2..776d82d4 100644 --- a/types/wlr_buffer.c +++ b/types/wlr_buffer.c @@ -5,11 +5,46 @@ #include #include +void wlr_buffer_init(struct wlr_buffer *buffer, + const struct wlr_buffer_impl *impl) { + assert(impl->destroy); + buffer->impl = impl; + buffer->n_refs = 1; +} + +struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer) { + buffer->n_refs++; + return buffer; +} + +void wlr_buffer_unref(struct wlr_buffer *buffer) { + if (buffer == NULL) { + return; + } + + assert(buffer->n_refs > 0); + buffer->n_refs--; + if (buffer->n_refs > 0) { + return; + } + + buffer->impl->destroy(buffer); +} + +bool wlr_buffer_get_dmabuf(struct wlr_buffer *buffer, + struct wlr_dmabuf_attributes *attribs) { + if (!buffer->impl->get_dmabuf) { + return false; + } + return buffer->impl->get_dmabuf(buffer, attribs); +} + + bool wlr_resource_is_buffer(struct wl_resource *resource) { return strcmp(wl_resource_get_class(resource), wl_buffer_interface.name) == 0; } -bool wlr_buffer_get_resource_size(struct wl_resource *resource, +bool wlr_resource_get_buffer_size(struct wl_resource *resource, struct wlr_renderer *renderer, int *width, int *height) { assert(wlr_resource_is_buffer(resource)); @@ -34,10 +69,54 @@ bool wlr_buffer_get_resource_size(struct wl_resource *resource, return true; } +static const struct wlr_buffer_impl client_buffer_impl; -static void buffer_resource_handle_destroy(struct wl_listener *listener, +static struct wlr_client_buffer *client_buffer_from_buffer( + struct wlr_buffer *buffer) { + assert(buffer->impl == &client_buffer_impl); + return (struct wlr_client_buffer *) buffer; +} + +static void client_buffer_destroy(struct wlr_buffer *_buffer) { + struct wlr_client_buffer *buffer = client_buffer_from_buffer(_buffer); + + if (!buffer->resource_released && buffer->resource != NULL) { + wl_buffer_send_release(buffer->resource); + } + + wl_list_remove(&buffer->resource_destroy.link); + wlr_texture_destroy(buffer->texture); + free(buffer); +} + +static bool client_buffer_get_dmabuf(struct wlr_buffer *_buffer, + struct wlr_dmabuf_attributes *attribs) { + struct wlr_client_buffer *buffer = client_buffer_from_buffer(_buffer); + + if (buffer->resource == NULL) { + return false; + } + + struct wl_resource *buffer_resource = buffer->resource; + if (!wlr_dmabuf_v1_resource_is_buffer(buffer_resource)) { + return false; + } + + struct wlr_dmabuf_v1_buffer *dmabuf_buffer = + wlr_dmabuf_v1_buffer_from_buffer_resource(buffer_resource); + memcpy(attribs, &dmabuf_buffer->attributes, + sizeof(struct wlr_dmabuf_attributes)); + return true; +} + +static const struct wlr_buffer_impl client_buffer_impl = { + .destroy = client_buffer_destroy, + .get_dmabuf = client_buffer_get_dmabuf, +}; + +static void client_buffer_resource_handle_destroy(struct wl_listener *listener, void *data) { - struct wlr_buffer *buffer = + struct wlr_client_buffer *buffer = wl_container_of(listener, buffer, resource_destroy); wl_list_remove(&buffer->resource_destroy.link); wl_list_init(&buffer->resource_destroy.link); @@ -51,8 +130,8 @@ static void buffer_resource_handle_destroy(struct wl_listener *listener, // which case we'll read garbage. We decide to accept this risk. } -struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, - struct wl_resource *resource) { +struct wlr_client_buffer *wlr_client_buffer_create( + struct wlr_renderer *renderer, struct wl_resource *resource) { assert(wlr_resource_is_buffer(resource)); struct wlr_texture *texture = NULL; @@ -100,50 +179,27 @@ struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, return NULL; } - struct wlr_buffer *buffer = calloc(1, sizeof(struct wlr_buffer)); + struct wlr_client_buffer *buffer = + calloc(1, sizeof(struct wlr_client_buffer)); if (buffer == NULL) { wlr_texture_destroy(texture); wl_resource_post_no_memory(resource); return NULL; } + wlr_buffer_init(&buffer->base, &client_buffer_impl); buffer->resource = resource; buffer->texture = texture; - buffer->released = released; - buffer->n_refs = 1; + buffer->resource_released = released; wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); - buffer->resource_destroy.notify = buffer_resource_handle_destroy; + buffer->resource_destroy.notify = client_buffer_resource_handle_destroy; return buffer; } -struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer) { - buffer->n_refs++; - return buffer; -} - -void wlr_buffer_unref(struct wlr_buffer *buffer) { - if (buffer == NULL) { - return; - } - - assert(buffer->n_refs > 0); - buffer->n_refs--; - if (buffer->n_refs > 0) { - return; - } - - if (!buffer->released && buffer->resource != NULL) { - wl_buffer_send_release(buffer->resource); - } - - wl_list_remove(&buffer->resource_destroy.link); - wlr_texture_destroy(buffer->texture); - free(buffer); -} - -struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, - struct wl_resource *resource, pixman_region32_t *damage) { +struct wlr_client_buffer *wlr_client_buffer_apply_damage( + struct wlr_client_buffer *buffer, struct wl_resource *resource, + pixman_region32_t *damage) { assert(wlr_resource_is_buffer(resource)); if (buffer->n_refs > 1) { @@ -199,27 +255,9 @@ struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, wl_list_remove(&buffer->resource_destroy.link); wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); - buffer->resource_destroy.notify = buffer_resource_handle_destroy; + buffer->resource_destroy.notify = client_buffer_resource_handle_destroy; buffer->resource = resource; buffer->released = true; return buffer; } - -bool wlr_buffer_get_dmabuf(struct wlr_buffer *buffer, - struct wlr_dmabuf_attributes *attribs) { - if (buffer->resource == NULL) { - return false; - } - - struct wl_resource *buffer_resource = buffer->resource; - if (!wlr_dmabuf_v1_resource_is_buffer(buffer_resource)) { - return false; - } - - struct wlr_dmabuf_v1_buffer *dmabuf_buffer = - wlr_dmabuf_v1_buffer_from_buffer_resource(buffer_resource); - memcpy(attribs, &dmabuf_buffer->attributes, - sizeof(struct wlr_dmabuf_attributes)); - return true; -} diff --git a/types/wlr_surface.c b/types/wlr_surface.c index f44ec53e..d58f0e32 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -142,7 +142,7 @@ static void surface_state_finalize(struct wlr_surface *surface, struct wlr_surface_state *state) { if ((state->committed & WLR_SURFACE_STATE_BUFFER)) { if (state->buffer_resource != NULL) { - wlr_buffer_get_resource_size(state->buffer_resource, + wlr_resource_get_buffer_size(state->buffer_resource, surface->renderer, &state->buffer_width, &state->buffer_height); } else { state->buffer_width = state->buffer_height = 0; @@ -282,27 +282,33 @@ static void surface_apply_damage(struct wlr_surface *surface) { struct wl_resource *resource = surface->current.buffer_resource; if (resource == NULL) { // NULL commit - wlr_buffer_unref(surface->buffer); + if (surface->buffer != NULL) { + wlr_buffer_unref(&surface->buffer->base); + } surface->buffer = NULL; return; } if (surface->buffer != NULL && surface->buffer->released) { - struct wlr_buffer *updated_buffer = wlr_buffer_apply_damage( - surface->buffer, resource, &surface->buffer_damage); + struct wlr_client_buffer *updated_buffer = + wlr_client_buffer_apply_damage(surface->buffer, resource, + &surface->buffer_damage); if (updated_buffer != NULL) { surface->buffer = updated_buffer; return; } } - struct wlr_buffer *buffer = wlr_buffer_create(surface->renderer, resource); + struct wlr_client_buffer *buffer = + wlr_client_buffer_create(surface->renderer, resource); if (buffer == NULL) { wlr_log(WLR_ERROR, "Failed to upload buffer"); return; } - wlr_buffer_unref(surface->buffer); + if (surface->buffer != NULL) { + wlr_buffer_unref(&surface->buffer->base); + } surface->buffer = buffer; } @@ -573,7 +579,9 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { pixman_region32_fini(&surface->buffer_damage); pixman_region32_fini(&surface->opaque_region); pixman_region32_fini(&surface->input_region); - wlr_buffer_unref(surface->buffer); + if (surface->buffer != NULL) { + wlr_buffer_unref(&surface->buffer->base); + } free(surface); }