From 7ec66a9990cfe1a932fb28d0f65e5d142db04e26 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 29 Jun 2021 20:11:38 +0200 Subject: [PATCH] buffer: introduce wlr_readonly_data_buffer --- include/types/wlr_buffer.h | 28 ++++++++++++++ types/wlr_buffer.c | 77 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/include/types/wlr_buffer.h b/include/types/wlr_buffer.h index 8abb68d6..85a8d8b4 100644 --- a/include/types/wlr_buffer.h +++ b/include/types/wlr_buffer.h @@ -25,6 +25,34 @@ struct wlr_shm_client_buffer { struct wlr_shm_client_buffer *shm_client_buffer_create( struct wl_resource *resource); +/** + * A read-only buffer that holds a data pointer. + * + * This is suitable for passing raw pixel data to a function that accepts a + * wlr_buffer. + */ +struct wlr_readonly_data_buffer { + struct wlr_buffer base; + + const void *data; + uint32_t format; + size_t stride; + + void *saved_data; +}; + +/** + * Wraps a read-only data pointer into a wlr_buffer. The data pointer may be + * accessed until readonly_data_buffer_drop() is called. + */ +struct wlr_readonly_data_buffer *readonly_data_buffer_create(uint32_t format, + size_t stride, uint32_t width, uint32_t height, const void *data); +/** + * Drops ownership of the buffer (see wlr_buffer_drop() for more details) and + * perform a copy of the data pointer if a consumer still has the buffer locked. + */ +bool readonly_data_buffer_drop(struct wlr_readonly_data_buffer *buffer); + /** * Buffer capabilities. * diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c index 147205b9..ca5c743c 100644 --- a/types/wlr_buffer.c +++ b/types/wlr_buffer.c @@ -451,3 +451,80 @@ struct wlr_shm_client_buffer *shm_client_buffer_create( return buffer; } + +static const struct wlr_buffer_impl readonly_data_buffer_impl; + +static struct wlr_readonly_data_buffer *readonly_data_buffer_from_buffer( + struct wlr_buffer *buffer) { + assert(buffer->impl == &readonly_data_buffer_impl); + return (struct wlr_readonly_data_buffer *)buffer; +} + +static void readonly_data_buffer_destroy(struct wlr_buffer *wlr_buffer) { + struct wlr_readonly_data_buffer *buffer = + readonly_data_buffer_from_buffer(wlr_buffer); + free(buffer->saved_data); + free(buffer); +} + +static bool readonly_data_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, + void **data, uint32_t *format, size_t *stride) { + struct wlr_readonly_data_buffer *buffer = + readonly_data_buffer_from_buffer(wlr_buffer); + if (buffer->data == NULL) { + return false; + } + *data = (void*)buffer->data; + *format = buffer->format; + *stride = buffer->stride; + return true; +} + +static void readonly_data_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { + // This space is intentionally left blank +} + +static const struct wlr_buffer_impl readonly_data_buffer_impl = { + .destroy = readonly_data_buffer_destroy, + .begin_data_ptr_access = readonly_data_buffer_begin_data_ptr_access, + .end_data_ptr_access = readonly_data_buffer_end_data_ptr_access, +}; + +struct wlr_readonly_data_buffer *readonly_data_buffer_create(uint32_t format, + size_t stride, uint32_t width, uint32_t height, const void *data) { + struct wlr_readonly_data_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + return NULL; + } + wlr_buffer_init(&buffer->base, &readonly_data_buffer_impl, width, height); + + buffer->data = data; + buffer->format = format; + buffer->stride = stride; + + return buffer; +} + +bool readonly_data_buffer_drop(struct wlr_readonly_data_buffer *buffer) { + bool ok = true; + + if (buffer->base.n_locks > 0) { + size_t size = buffer->stride * buffer->base.height; + buffer->saved_data = malloc(size); + if (buffer->saved_data == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + ok = false; + buffer->data = NULL; + // We can't destroy the buffer, or we risk use-after-free in the + // consumers. We can't allow accesses to buffer->data anymore, so + // set it to NULL and make subsequent begin_data_ptr_access() + // calls fail. + } else { + memcpy(buffer->saved_data, buffer->data, size); + buffer->data = buffer->saved_data; + } + } + + wlr_buffer_drop(&buffer->base); + return ok; +}