diff options
author | Simon Ser <contact@emersion.fr> | 2020-06-01 19:49:32 +0200 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2020-11-15 22:48:42 +0100 |
commit | b0a663d39d2de1dcfd9a0381cc822c4713da508e (patch) | |
tree | 770628233efc28b72a6c37b6f8b01b5bf6de5550 | |
parent | 7c6212a0f71eeab23a4ff4241cb266ec219fad2d (diff) |
render: introduce wlr_swapchain
The swapchain maximum capacity is set to 4, so that we have enough room
for:
- A buffer currently displayed on screen
- A buffer queued for display (e.g. to KMS)
- A pending buffer that'll be queued next commit
- An additional pending buffer in case we want to invalidate the
currently pending one
-rw-r--r-- | include/render/swapchain.h | 41 | ||||
-rw-r--r-- | render/meson.build | 1 | ||||
-rw-r--r-- | render/swapchain.c | 108 |
3 files changed, 150 insertions, 0 deletions
diff --git a/include/render/swapchain.h b/include/render/swapchain.h new file mode 100644 index 00000000..57a0cd3f --- /dev/null +++ b/include/render/swapchain.h @@ -0,0 +1,41 @@ +#ifndef RENDER_SWAPCHAIN_H +#define RENDER_SWAPCHAIN_H + +#include <stdbool.h> +#include <wayland-server-core.h> +#include <wlr/render/drm_format_set.h> + +#define WLR_SWAPCHAIN_CAP 4 + +struct wlr_swapchain_slot { + struct wlr_buffer *buffer; + bool acquired; // waiting for release + + struct wl_listener release; +}; + +struct wlr_swapchain { + struct wlr_allocator *allocator; // NULL if destroyed + + int width, height; + struct wlr_drm_format *format; + + struct wlr_swapchain_slot slots[WLR_SWAPCHAIN_CAP]; + + struct wl_listener allocator_destroy; +}; + +struct wlr_swapchain *wlr_swapchain_create( + struct wlr_allocator *alloc, int width, int height, + const struct wlr_drm_format *format); +void wlr_swapchain_destroy(struct wlr_swapchain *swapchain); +/** + * Acquire a buffer from the swap chain. + * + * The returned buffer is locked. When the caller is done with it, they must + * unlock it by calling wlr_buffer_unlock. + */ +struct wlr_buffer *wlr_swapchain_acquire( + struct wlr_swapchain *swapchain); + +#endif diff --git a/render/meson.build b/render/meson.build index f68322fe..f8609ef8 100644 --- a/render/meson.build +++ b/render/meson.build @@ -8,6 +8,7 @@ wlr_files += files( 'gles2/renderer.c', 'gles2/shaders.c', 'gles2/texture.c', + 'swapchain.c', 'wlr_renderer.c', 'wlr_texture.c', ) diff --git a/render/swapchain.c b/render/swapchain.c new file mode 100644 index 00000000..4145efd2 --- /dev/null +++ b/render/swapchain.c @@ -0,0 +1,108 @@ +#include <assert.h> +#include <stdlib.h> +#include <wlr/util/log.h> +#include <wlr/types/wlr_buffer.h> +#include "render/allocator.h" +#include "render/drm_format_set.h" +#include "render/swapchain.h" + +static void swapchain_handle_allocator_destroy(struct wl_listener *listener, + void *data) { + struct wlr_swapchain *swapchain = + wl_container_of(listener, swapchain, allocator_destroy); + swapchain->allocator = NULL; +} + +struct wlr_swapchain *wlr_swapchain_create( + struct wlr_allocator *alloc, int width, int height, + const struct wlr_drm_format *format) { + struct wlr_swapchain *swapchain = calloc(1, sizeof(*swapchain)); + if (swapchain == NULL) { + return NULL; + } + swapchain->allocator = alloc; + swapchain->width = width; + swapchain->height = height; + + swapchain->format = wlr_drm_format_dup(format); + if (swapchain->format == NULL) { + free(swapchain); + return NULL; + } + + swapchain->allocator_destroy.notify = swapchain_handle_allocator_destroy; + wl_signal_add(&alloc->events.destroy, &swapchain->allocator_destroy); + + return swapchain; +} + +static void slot_reset(struct wlr_swapchain_slot *slot) { + if (slot->acquired) { + wl_list_remove(&slot->release.link); + } + wlr_buffer_drop(slot->buffer); + memset(slot, 0, sizeof(*slot)); +} + +void wlr_swapchain_destroy(struct wlr_swapchain *swapchain) { + if (swapchain == NULL) { + return; + } + for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { + slot_reset(&swapchain->slots[i]); + } + wl_list_remove(&swapchain->allocator_destroy.link); + free(swapchain->format); + free(swapchain); +} + +static void slot_handle_release(struct wl_listener *listener, void *data) { + struct wlr_swapchain_slot *slot = + wl_container_of(listener, slot, release); + wl_list_remove(&slot->release.link); + slot->acquired = false; +} + +static struct wlr_buffer *slot_acquire(struct wlr_swapchain_slot *slot) { + assert(!slot->acquired); + assert(slot->buffer != NULL); + + slot->acquired = true; + + slot->release.notify = slot_handle_release; + wl_signal_add(&slot->buffer->events.release, &slot->release); + + return wlr_buffer_lock(slot->buffer); +} + +struct wlr_buffer *wlr_swapchain_acquire( + struct wlr_swapchain *swapchain) { + struct wlr_swapchain_slot *free_slot = NULL; + for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { + struct wlr_swapchain_slot *slot = &swapchain->slots[i]; + if (slot->acquired) { + continue; + } + if (slot->buffer != NULL) { + return slot_acquire(slot); + } + free_slot = slot; + } + if (free_slot == NULL) { + wlr_log(WLR_ERROR, "No free output buffer slot"); + return NULL; + } + + if (swapchain->allocator == NULL) { + return NULL; + } + + wlr_log(WLR_DEBUG, "Allocating new swapchain buffer"); + free_slot->buffer = wlr_allocator_create_buffer(swapchain->allocator, + swapchain->width, swapchain->height, swapchain->format); + if (free_slot->buffer == NULL) { + wlr_log(WLR_ERROR, "Failed to allocate buffer"); + return NULL; + } + return slot_acquire(free_slot); +} |