diff --git a/include/buffer-pool.h b/include/buffer-pool.h new file mode 100644 index 0000000..159d5f3 --- /dev/null +++ b/include/buffer-pool.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 Andri Yngvason + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once + +#include "buffer.h" + +#include +#include + +struct buffer_pool; +struct buffer; + +struct buffer_pool* buffer_pool_create(enum buffer_type type, uint16_t width, + uint16_t height, uint32_t format, uint16_t stride, int scale); +void buffer_pool_destroy(struct buffer_pool* self); + +bool buffer_pool_resize(struct buffer_pool* self, uint16_t width, + uint16_t height, uint32_t format, uint16_t stride, int scale); + +struct buffer* buffer_pool_acquire(struct buffer_pool* self); + +void buffer_pool_damage_all(struct buffer_pool* self, + struct pixman_region16* damage); diff --git a/include/buffer.h b/include/buffer.h index 6b12b00..93c6095 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Andri Yngvason + * Copyright (c) 2022 - 2023 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,6 +16,8 @@ #pragma once +#include "sys/queue.h" + #include #include #include @@ -31,6 +33,15 @@ enum buffer_type { }; struct buffer { + int ref; + int hold; + + LIST_ENTRY(buffer) registry_link; + TAILQ_ENTRY(buffer) pool_link; + + void (*release_fn)(struct buffer*, void* ud); + void* release_ud; + enum buffer_type type; int width, height; @@ -38,8 +49,6 @@ struct buffer { size_t size; uint32_t format; struct wl_buffer* wl_buffer; - bool is_attached; - bool please_clean_up; struct pixman_region16 damage; // wl_shm: @@ -53,3 +62,12 @@ struct buffer { struct buffer* buffer_create_shm(int width, int height, int stride, uint32_t format); struct buffer* buffer_create_dmabuf(int width, int height, uint32_t format); void buffer_destroy(struct buffer* self); + +void buffer_ref(struct buffer*); +void buffer_unref(struct buffer*); + +void buffer_set_release_fn(struct buffer*, void (*)(struct buffer*, void* ud), + void* userdata); + +void buffer_hold(struct buffer*); +void buffer_release(struct buffer*); diff --git a/meson.build b/meson.build index 95f177d..b775155 100644 --- a/meson.build +++ b/meson.build @@ -77,6 +77,7 @@ sources = [ 'src/renderer.c', 'src/renderer-egl.c', 'src/buffer.c', + 'src/buffer-pool.c', 'src/open-h264.c', 'src/cursor.c', 'src/rfbproto.c', diff --git a/src/buffer-pool.c b/src/buffer-pool.c new file mode 100644 index 0000000..951d823 --- /dev/null +++ b/src/buffer-pool.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2023 Andri Yngvason + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "buffer-pool.h" +#include "buffer.h" + +#include "sys/queue.h" + +#include +#include + +LIST_HEAD(buffer_list, buffer); +TAILQ_HEAD(bufferq, buffer); + +struct buffer_pool { + int ref; + + struct buffer_list registry; + struct bufferq buffers; + + enum buffer_type type; + uint16_t width; + uint16_t height; + int32_t stride; + uint32_t format; + int scale; +}; + +struct buffer_pool* buffer_pool_create(enum buffer_type type, uint16_t width, + uint16_t height, uint32_t format, uint16_t stride, int scale) +{ + struct buffer_pool* self = calloc(1, sizeof(*self)); + if (!self) + return NULL; + + self->ref = 1; + + LIST_INIT(&self->registry); + TAILQ_INIT(&self->buffers); + self->type = type; + self->width = width; + self->height = height; + self->stride = stride; + self->format = format; + self->scale = scale; + + return self; +} + +static void buffer_pool__unref_buffers(struct buffer_pool* self) +{ + while (!LIST_EMPTY(&self->registry)) { + struct buffer* buffer = LIST_FIRST(&self->registry); + LIST_REMOVE(buffer, registry_link); + buffer_set_release_fn(buffer, NULL, NULL); + buffer_unref(buffer); + } + TAILQ_INIT(&self->buffers); +} + +void buffer_pool_destroy(struct buffer_pool* self) +{ + buffer_pool__unref_buffers(self); + free(self); +} + +bool buffer_pool_resize(struct buffer_pool* self, uint16_t width, + uint16_t height, uint32_t format, uint16_t stride, int scale) +{ + if (width == self->width && height == self->height && + format == self->format && stride == self->stride && + scale == self->scale) + return false; + + buffer_pool__unref_buffers(self); + + self->width = width; + self->height = height; + self->stride = stride; + self->format = format; + self->scale = scale; + + return true; +} + +static void buffer_pool__on_buffer_release(struct buffer* buffer, void* userdata) +{ + struct buffer_pool* self = userdata; + + if (buffer->width != self->width || buffer->height != self->height || + buffer->format != self->format || + (buffer->type == BUFFER_WL_SHM && buffer->stride != self->stride) || + buffer->scale != self->scale) { + buffer_unref(buffer); + } else { + TAILQ_INSERT_TAIL(&self->buffers, buffer, pool_link); + } +} + +static struct buffer* buffer_pool__acquire_new(struct buffer_pool* self) +{ + struct buffer* buffer; + switch (self->type) { + case BUFFER_WL_SHM: + buffer = buffer_create_shm(self->width, self->height, + self->stride, self->format); + break; + case BUFFER_DMABUF: + buffer = buffer_create_dmabuf(self->width, self->height, + self->format); + break; + case BUFFER_UNSPEC: + abort(); + } + if (!buffer) + return NULL; + + buffer->scale = self->scale; + + LIST_INSERT_HEAD(&self->registry, buffer, registry_link); + buffer_set_release_fn(buffer, buffer_pool__on_buffer_release, self); + + return buffer; +} + +static struct buffer* buffer_pool__acquire_from_list(struct buffer_pool* self) +{ + struct buffer* buffer = TAILQ_FIRST(&self->buffers); + assert(buffer); + + TAILQ_REMOVE(&self->buffers, buffer, pool_link); + + return buffer; +} + +struct buffer* buffer_pool_acquire(struct buffer_pool* self) +{ + return TAILQ_EMPTY(&self->buffers) ? + buffer_pool__acquire_new(self) : + buffer_pool__acquire_from_list(self); +} + +void buffer_pool_damage_all(struct buffer_pool* self, + struct pixman_region16* damage) +{ + struct buffer* buffer; + LIST_FOREACH(buffer, &self->registry, registry_link) { + pixman_region_union(&buffer->damage, damage, damage); + } +} diff --git a/src/buffer.c b/src/buffer.c index fe9c202..5b35a6a 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Andri Yngvason + * Copyright (c) 2022 - 2023 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -25,24 +25,22 @@ #include #include +// TODO: Some buffers needn't be wayland buffers. + /* Origin: main.c */ extern struct wl_shm* wl_shm; extern struct gbm_device* gbm_device; extern struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1; -static void buffer_release(void* data, struct wl_buffer* wl_buffer) +static void buffer_wl_release(void* data, struct wl_buffer* wl_buffer) { (void)wl_buffer; struct buffer* self = data; - self->is_attached = false; - - if (self->please_clean_up) { - buffer_destroy(self); - } + buffer_release(self); } static const struct wl_buffer_listener buffer_listener = { - .release = buffer_release, + .release = buffer_wl_release, }; struct buffer* buffer_create_shm(int width, int height, int stride, @@ -54,6 +52,7 @@ struct buffer* buffer_create_shm(int width, int height, int stride, if (!self) return NULL; + self->ref = 1; self->type = BUFFER_WL_SHM; self->width = width; self->height = height; @@ -106,6 +105,7 @@ struct buffer* buffer_create_dmabuf(int width, int height, uint32_t format) if (!self) return NULL; + self->ref = 1; self->type = BUFFER_DMABUF; self->width = width; self->height = height; @@ -159,9 +159,6 @@ void buffer_destroy(struct buffer* self) if (!self) return; - if (self->is_attached) - self->please_clean_up = true; - pixman_region_fini(&self->damage); wl_buffer_destroy(self->wl_buffer); @@ -179,3 +176,41 @@ void buffer_destroy(struct buffer* self) free(self); } + +void buffer_ref(struct buffer* self) +{ + ++self->ref; +} + +void buffer_unref(struct buffer* self) +{ + if (self && --self->ref == 0) + buffer_destroy(self); +} + +void buffer_set_release_fn(struct buffer* self, + void (*fn)(struct buffer*, void* ud), void* userdata) +{ + self->release_fn = fn; + self->release_ud = userdata; +} + +void buffer_hold(struct buffer* self) +{ + assert(self); + ++self->hold; +} + +void buffer_release(struct buffer* self) +{ + assert(self); + + int hold = --self->hold; + if (hold != 0) + return; + + if (self->release_fn) + self->release_fn(self, self->release_ud); + else + buffer_unref(self); +} diff --git a/src/main.c b/src/main.c index 16a5160..f57828b 100644 --- a/src/main.c +++ b/src/main.c @@ -42,6 +42,7 @@ #include "pixels.h" #include "region.h" #include "buffer.h" +#include "buffer-pool.h" #include "renderer.h" #include "renderer-egl.h" #include "linux-dmabuf-unstable-v1.h" @@ -63,9 +64,8 @@ struct window { struct xdg_surface* xdg_surface; struct xdg_toplevel* xdg_toplevel; - struct buffer* buffers[3]; + struct buffer_pool* buffers; struct buffer* back_buffer; - int buffer_index; struct pixman_region16 current_damage; @@ -243,8 +243,10 @@ void on_wayland_event(void* obj) } } - if (wl_display_dispatch_pending(wl_display) < 0) + if (wl_display_dispatch_pending(wl_display) < 0) { fprintf(stderr, "Failed to dispatch pending\n"); + abort(); + } } static int init_wayland_event_handler(void) @@ -279,7 +281,7 @@ static int init_signal_handler(void) static void window_attach(struct window* w, int x, int y) { - w->back_buffer->is_attached = true; + buffer_hold(w->back_buffer); wl_surface_attach(w->wl_surface, w->back_buffer->wl_buffer, x, y); wl_surface_set_buffer_scale(window->wl_surface, window->back_buffer->scale); @@ -370,8 +372,9 @@ static void window_commit(struct window* w) static void window_swap(struct window* w) { - w->buffer_index = (w->buffer_index + 1) % 3; - w->back_buffer = w->buffers[w->buffer_index]; + buffer_unref(w->back_buffer); + w->back_buffer = buffer_pool_acquire(w->buffers); + buffer_ref(w->back_buffer); } static void window_damage(struct window* w, int x, int y, int width, int height) @@ -401,24 +404,19 @@ static void window_resize(struct window* w, int width, int height, int scale) if (width == 0 || height == 0 || scale == 0) return; - if (w->back_buffer && w->back_buffer->width == width && - w->back_buffer->height == height && - w->back_buffer->scale == scale) - return; - - for (int i = 0; i < 3; ++i) - buffer_destroy(w->buffers[i]); - - for (int i = 0; i < 3; ++i) { - w->buffers[i] = have_egl - ? buffer_create_dmabuf(scale * width, scale * height, - dmabuf_format) - : buffer_create_shm(scale * width, scale * height, - scale * 4 * width, shm_format); - w->buffers[i]->scale = scale; + uint32_t format = have_egl ? dmabuf_format : shm_format; + if (!w->buffers) { + enum buffer_type type = have_egl ? BUFFER_DMABUF : BUFFER_WL_SHM; + w->buffers = buffer_pool_create(type, scale * width, + scale * height, format, scale * 4 * width, + scale); + } else { + buffer_pool_resize(w->buffers, scale * width, scale * height, + format, scale * 4 * width, scale); } - w->back_buffer = w->buffers[0]; + w->back_buffer = buffer_pool_acquire(w->buffers); + buffer_ref(w->back_buffer); } static void xdg_toplevel_configure(void* data, struct xdg_toplevel* toplevel, @@ -479,9 +477,8 @@ wl_surface_failure: static void window_destroy(struct window* w) { - for (int i = 0; i < 3; ++i) - buffer_destroy(w->buffers[i]); - + buffer_unref(w->back_buffer); + buffer_pool_destroy(w->buffers); free(w->vnc_fb); xdg_toplevel_destroy(w->xdg_toplevel); xdg_surface_destroy(w->xdg_surface); @@ -592,9 +589,7 @@ static void get_frame_damage(struct vnc_client* client, static void apply_buffer_damage(struct pixman_region16* damage) { - for (int i = 0; i < 3; ++i) - pixman_region_union(&window->buffers[i]->damage, - &window->buffers[i]->damage, damage); + buffer_pool_damage_all(window->buffers, damage); } static void window_damage_region(struct window* w, @@ -636,9 +631,6 @@ static void render_from_vnc(void) if (window->is_frame_committed) return; - if (window->back_buffer->is_attached) - fprintf(stderr, "Oops, back-buffer is still attached.\n"); - window_attach(window, 0, 0); double scale;