From fbe407e34729ab7a1343a19e9c7e32f928ff0ab1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 1 Jun 2020 19:48:39 +0200 Subject: [PATCH 01/14] render: introduce wlr_allocator --- include/render/allocator.h | 42 ++++++++++++++++++++++++++++++++++++++ render/allocator.c | 20 ++++++++++++++++++ render/meson.build | 1 + 3 files changed, 63 insertions(+) create mode 100644 include/render/allocator.h create mode 100644 render/allocator.c diff --git a/include/render/allocator.h b/include/render/allocator.h new file mode 100644 index 0000000000..d47184af17 --- /dev/null +++ b/include/render/allocator.h @@ -0,0 +1,42 @@ +#ifndef RENDER_ALLOCATOR +#define RENDER_ALLOCATOR + +#include +#include +#include +#include + +struct wlr_allocator; + +struct wlr_allocator_interface { + struct wlr_buffer *(*create_buffer)(struct wlr_allocator *alloc, + int width, int height, const struct wlr_drm_format *format); + void (*destroy)(struct wlr_allocator *alloc); +}; + +struct wlr_allocator { + const struct wlr_allocator_interface *impl; + + struct { + struct wl_signal destroy; + } events; +}; + +/** + * Destroy the allocator. + */ +void wlr_allocator_destroy(struct wlr_allocator *alloc); +/** + * Allocate a new buffer. + * + * When the caller is done with it, they must unreference it by calling + * wlr_buffer_drop. + */ +struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc, + int width, int height, const struct wlr_drm_format *format); + +// For wlr_allocator implementors +void wlr_allocator_init(struct wlr_allocator *alloc, + const struct wlr_allocator_interface *impl); + +#endif diff --git a/render/allocator.c b/render/allocator.c new file mode 100644 index 0000000000..3638bd62a6 --- /dev/null +++ b/render/allocator.c @@ -0,0 +1,20 @@ +#include +#include +#include "render/allocator.h" + +void wlr_allocator_init(struct wlr_allocator *alloc, + const struct wlr_allocator_interface *impl) { + assert(impl && impl->destroy && impl->create_buffer); + alloc->impl = impl; + wl_signal_init(&alloc->events.destroy); +} + +void wlr_allocator_destroy(struct wlr_allocator *alloc) { + wl_signal_emit(&alloc->events.destroy, NULL); + alloc->impl->destroy(alloc); +} + +struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc, + int width, int height, const struct wlr_drm_format *format) { + return alloc->impl->create_buffer(alloc, width, height, format); +} diff --git a/render/meson.build b/render/meson.build index 9486c22de2..58cc6761e2 100644 --- a/render/meson.build +++ b/render/meson.build @@ -1,4 +1,5 @@ wlr_files += files( + 'allocator.c', 'dmabuf.c', 'egl.c', 'drm_format_set.c', From 72fc534c91c83dde5105967365d857b6c175a69c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 1 Jun 2020 19:49:10 +0200 Subject: [PATCH 02/14] render: introduce wlr_gbm_allocator --- include/render/gbm_allocator.h | 29 ++++++ render/gbm_allocator.c | 182 +++++++++++++++++++++++++++++++++ render/meson.build | 1 + 3 files changed, 212 insertions(+) create mode 100644 include/render/gbm_allocator.h create mode 100644 render/gbm_allocator.c diff --git a/include/render/gbm_allocator.h b/include/render/gbm_allocator.h new file mode 100644 index 0000000000..f4627b9057 --- /dev/null +++ b/include/render/gbm_allocator.h @@ -0,0 +1,29 @@ +#ifndef RENDER_GBM_ALLOCATOR_H +#define RENDER_GBM_ALLOCATOR_H + +#include +#include +#include "render/allocator.h" + +struct wlr_gbm_buffer { + struct wlr_buffer base; + + struct gbm_bo *gbm_bo; + struct wlr_dmabuf_attributes dmabuf; +}; + +struct wlr_gbm_allocator { + struct wlr_allocator base; + + int fd; + struct gbm_device *gbm_device; +}; + +/** + * Creates a new GBM allocator from a render FD. + * + * Takes ownership over the FD. + */ +struct wlr_gbm_allocator *wlr_gbm_allocator_create(int render_fd); + +#endif diff --git a/render/gbm_allocator.c b/render/gbm_allocator.c new file mode 100644 index 0000000000..da29501ae2 --- /dev/null +++ b/render/gbm_allocator.c @@ -0,0 +1,182 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include "render/gbm_allocator.h" + +static const struct wlr_buffer_impl buffer_impl; + +static struct wlr_gbm_buffer *get_gbm_buffer_from_buffer( + struct wlr_buffer *buffer) { + assert(buffer->impl == &buffer_impl); + return (struct wlr_gbm_buffer *)buffer; +} + +static struct wlr_gbm_buffer *create_buffer(struct gbm_device *gbm_device, + int width, int height, const struct wlr_drm_format *format) { + struct gbm_bo *bo = NULL; + if (format->len > 0) { + bo = gbm_bo_create_with_modifiers(gbm_device, width, height, + format->format, format->modifiers, format->len); + } + if (bo == NULL) { + uint32_t usage = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; + if (format->len == 1 && + format->modifiers[0] == DRM_FORMAT_MOD_LINEAR) { + usage |= GBM_BO_USE_LINEAR; + } + bo = gbm_bo_create(gbm_device, width, height, format->format, usage); + } + if (bo == NULL) { + wlr_log(WLR_ERROR, "gbm_bo_create failed"); + return NULL; + } + + struct wlr_gbm_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + gbm_bo_destroy(bo); + return NULL; + } + wlr_buffer_init(&buffer->base, &buffer_impl, width, height); + buffer->gbm_bo = bo; + + wlr_log(WLR_DEBUG, "Allocated %dx%d GBM buffer (format 0x%"PRIX32", " + "modifier 0x%"PRIX64")", buffer->base.width, buffer->base.height, + gbm_bo_get_format(bo), gbm_bo_get_modifier(bo)); + + return buffer; +} + +static void buffer_destroy(struct wlr_buffer *wlr_buffer) { + struct wlr_gbm_buffer *buffer = + get_gbm_buffer_from_buffer(wlr_buffer); + wlr_dmabuf_attributes_finish(&buffer->dmabuf); + gbm_bo_destroy(buffer->gbm_bo); + free(buffer); +} + +static bool buffer_create_dmabuf(struct wlr_gbm_buffer *buffer) { + assert(buffer->dmabuf.n_planes == 0); + + struct gbm_bo *bo = buffer->gbm_bo; + struct wlr_dmabuf_attributes attribs = {0}; + + attribs.n_planes = gbm_bo_get_plane_count(bo); + if (attribs.n_planes > WLR_DMABUF_MAX_PLANES) { + wlr_log(WLR_ERROR, "GBM BO contains too many planes (%d)", + attribs.n_planes); + return false; + } + + attribs.width = gbm_bo_get_width(bo); + attribs.height = gbm_bo_get_height(bo); + attribs.format = gbm_bo_get_format(bo); + attribs.modifier = gbm_bo_get_modifier(bo); + + int i; + for (i = 0; i < attribs.n_planes; ++i) { + union gbm_bo_handle handle = gbm_bo_get_handle_for_plane(bo, i); + if (handle.s32 < 0) { + wlr_log(WLR_ERROR, "gbm_bo_get_handle_for_plane failed"); + goto error_fd; + } + + int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo)); + int ret = drmPrimeHandleToFD(drm_fd, handle.s32, + DRM_CLOEXEC, &attribs.fd[i]); + if (ret < 0 || attribs.fd[i] < 0) { + wlr_log_errno(WLR_ERROR, "drmPrimeHandleToFD failed"); + goto error_fd; + } + + attribs.offset[i] = gbm_bo_get_offset(bo, i); + attribs.stride[i] = gbm_bo_get_stride_for_plane(bo, i); + } + + memcpy(&buffer->dmabuf, &attribs, sizeof(attribs)); + return true; + +error_fd: + for (int j = 0; j < i; ++j) { + close(attribs.fd[j]); + } + return false; +} + +static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, + struct wlr_dmabuf_attributes *attribs) { + struct wlr_gbm_buffer *buffer = + get_gbm_buffer_from_buffer(wlr_buffer); + + memset(attribs, 0, sizeof(*attribs)); + + // Only export the buffer once + if (buffer->dmabuf.n_planes == 0) { + if (!buffer_create_dmabuf(buffer)) { + return false; + } + } + + memcpy(attribs, &buffer->dmabuf, sizeof(buffer->dmabuf)); + return true; +} + +static const struct wlr_buffer_impl buffer_impl = { + .destroy = buffer_destroy, + .get_dmabuf = buffer_get_dmabuf, +}; + +static const struct wlr_allocator_interface allocator_impl; + +static struct wlr_gbm_allocator *get_gbm_alloc_from_alloc( + struct wlr_allocator *alloc) { + assert(alloc->impl == &allocator_impl); + return (struct wlr_gbm_allocator *)alloc; +} + +struct wlr_gbm_allocator *wlr_gbm_allocator_create(int fd) { + struct wlr_gbm_allocator *alloc = calloc(1, sizeof(*alloc)); + if (alloc == NULL) { + return NULL; + } + wlr_allocator_init(&alloc->base, &allocator_impl); + + alloc->fd = fd; + + alloc->gbm_device = gbm_create_device(fd); + if (alloc->gbm_device == NULL) { + wlr_log(WLR_ERROR, "gbm_create_device failed"); + free(alloc); + return NULL; + } + + return alloc; +} + +static void allocator_destroy(struct wlr_allocator *wlr_alloc) { + struct wlr_gbm_allocator *alloc = get_gbm_alloc_from_alloc(wlr_alloc); + gbm_device_destroy(alloc->gbm_device); + close(alloc->fd); + free(alloc); +} + +static struct wlr_buffer *allocator_create_buffer( + struct wlr_allocator *wlr_alloc, int width, int height, + const struct wlr_drm_format *format) { + struct wlr_gbm_allocator *alloc = get_gbm_alloc_from_alloc(wlr_alloc); + struct wlr_gbm_buffer *buffer = + create_buffer(alloc->gbm_device, width, height, format); + if (buffer == NULL) { + return NULL; + } + return &buffer->base; +} + +static const struct wlr_allocator_interface allocator_impl = { + .destroy = allocator_destroy, + .create_buffer = allocator_create_buffer, +}; diff --git a/render/meson.build b/render/meson.build index 58cc6761e2..f68322fea6 100644 --- a/render/meson.build +++ b/render/meson.build @@ -3,6 +3,7 @@ wlr_files += files( 'dmabuf.c', 'egl.c', 'drm_format_set.c', + 'gbm_allocator.c', 'gles2/pixel_format.c', 'gles2/renderer.c', 'gles2/shaders.c', From 59efa54e69219fbf0a3817bf51908858721626e1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 7 Aug 2020 19:02:49 +0200 Subject: [PATCH 03/14] render/drm_format_set: introduce wlr_drm_format_dup --- include/render/drm_format_set.h | 8 ++++++++ render/drm_format_set.c | 12 ++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 include/render/drm_format_set.h diff --git a/include/render/drm_format_set.h b/include/render/drm_format_set.h new file mode 100644 index 0000000000..ae1fcc4722 --- /dev/null +++ b/include/render/drm_format_set.h @@ -0,0 +1,8 @@ +#ifndef RENDER_DRM_FORMAT_SET_H +#define RENDER_DRM_FORMAT_SET_H + +#include + +struct wlr_drm_format *wlr_drm_format_dup(const struct wlr_drm_format *format); + +#endif diff --git a/render/drm_format_set.c b/render/drm_format_set.c index cbcc0875fc..54fc8ae107 100644 --- a/render/drm_format_set.c +++ b/render/drm_format_set.c @@ -6,6 +6,7 @@ #include #include #include +#include "render/drm_format_set.h" void wlr_drm_format_set_finish(struct wlr_drm_format_set *set) { for (size_t i = 0; i < set->len; ++i) { @@ -125,3 +126,14 @@ bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, set->formats[set->len++] = fmt; return true; } + +struct wlr_drm_format *wlr_drm_format_dup(const struct wlr_drm_format *format) { + size_t format_size = sizeof(struct wlr_drm_format) + + format->len * sizeof(format->modifiers[0]); + struct wlr_drm_format *duped_format = malloc(format_size); + if (duped_format == NULL) { + return NULL; + } + memcpy(duped_format, format, format_size); + return duped_format; +} From ce3ef609f66944c316519cd87aae6415557a33f2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 1 Jun 2020 19:49:32 +0200 Subject: [PATCH 04/14] 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 --- include/render/swapchain.h | 41 ++++++++++++++ render/meson.build | 1 + render/swapchain.c | 108 +++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 include/render/swapchain.h create mode 100644 render/swapchain.c diff --git a/include/render/swapchain.h b/include/render/swapchain.h new file mode 100644 index 0000000000..57a0cd3f00 --- /dev/null +++ b/include/render/swapchain.h @@ -0,0 +1,41 @@ +#ifndef RENDER_SWAPCHAIN_H +#define RENDER_SWAPCHAIN_H + +#include +#include +#include + +#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 f68322fea6..f8609ef839 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 0000000000..4145efd2a7 --- /dev/null +++ b/render/swapchain.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#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); +} From 4c92da4fc31a63c565d297d00a7fcdad95e33c31 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Jul 2020 10:46:46 +0200 Subject: [PATCH 05/14] buffer: add wlr_client_buffer_get --- include/wlr/types/wlr_buffer.h | 5 +++++ types/wlr_buffer.c | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index 14bb2a8da6..8455589999 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -103,6 +103,11 @@ struct wlr_client_buffer { struct wlr_renderer; +/** + * Get a client buffer from a generic buffer. If the buffer isn't a client + * buffer, returns NULL. + */ +struct wlr_client_buffer *wlr_client_buffer_get(struct wlr_buffer *buffer); /** * Check if a resource is a wl_buffer resource. */ diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c index 2f914c127c..29b2641202 100644 --- a/types/wlr_buffer.c +++ b/types/wlr_buffer.c @@ -96,10 +96,18 @@ bool wlr_resource_get_buffer_size(struct wl_resource *resource, static const struct wlr_buffer_impl client_buffer_impl; +struct wlr_client_buffer *wlr_client_buffer_get(struct wlr_buffer *buffer) { + if (buffer->impl != &client_buffer_impl) { + return NULL; + } + return (struct wlr_client_buffer *)buffer; +} + 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; + struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(buffer); + assert(client_buffer != NULL); + return client_buffer; } static void client_buffer_destroy(struct wlr_buffer *_buffer) { From 0f7be07275c50ba4e8404bdca0c1af8167334295 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Jul 2020 16:56:18 +0200 Subject: [PATCH 06/14] render: introduce wlr_renderer_bind_buffer --- include/render/wlr_renderer.h | 8 ++++++++ include/wlr/render/interface.h | 2 ++ include/wlr/render/wlr_renderer.h | 1 + render/wlr_renderer.c | 10 ++++++++++ 4 files changed, 21 insertions(+) create mode 100644 include/render/wlr_renderer.h diff --git a/include/render/wlr_renderer.h b/include/render/wlr_renderer.h new file mode 100644 index 0000000000..0ef1ef82fd --- /dev/null +++ b/include/render/wlr_renderer.h @@ -0,0 +1,8 @@ +#ifndef RENDER_WLR_RENDERER_H +#define RENDER_WLR_RENDERER_H + +#include + +bool wlr_renderer_bind_buffer(struct wlr_renderer *r, struct wlr_buffer *buffer); + +#endif diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index 998b1cfa7f..1b57c66033 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -30,6 +30,8 @@ #include struct wlr_renderer_impl { + bool (*bind_buffer)(struct wlr_renderer *renderer, + struct wlr_buffer *buffer); void (*begin)(struct wlr_renderer *renderer, uint32_t width, uint32_t height); void (*end)(struct wlr_renderer *renderer); diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index 363a12e454..8908d3ce45 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -21,6 +21,7 @@ enum wlr_renderer_read_pixels_flags { struct wlr_renderer_impl; struct wlr_drm_format_set; +struct wlr_buffer; struct wlr_renderer { const struct wlr_renderer_impl *impl; diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index bdcc836f10..311747f10f 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -7,6 +7,7 @@ #include #include #include "util/signal.h" +#include "render/wlr_renderer.h" void wlr_renderer_init(struct wlr_renderer *renderer, const struct wlr_renderer_impl *impl) { @@ -37,6 +38,15 @@ void wlr_renderer_destroy(struct wlr_renderer *r) { } } +bool wlr_renderer_bind_buffer(struct wlr_renderer *r, + struct wlr_buffer *buffer) { + assert(!r->rendering); + if (!r->impl->bind_buffer) { + return false; + } + return r->impl->bind_buffer(r, buffer); +} + void wlr_renderer_begin(struct wlr_renderer *r, uint32_t width, uint32_t height) { assert(!r->rendering); From af45341b56f7fb172a4c88948c5d5f4b313fdca7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Jul 2020 16:56:23 +0200 Subject: [PATCH 07/14] render/gles2: implement wlr_renderer_bind_buffer --- include/render/gles2.h | 15 +++++ render/gles2/renderer.c | 141 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) diff --git a/include/render/gles2.h b/include/render/gles2.h index a64aa77ceb..2eff0aa52c 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -72,9 +72,24 @@ struct wlr_gles2_renderer { struct wlr_gles2_tex_shader tex_ext; } shaders; + struct wl_list buffers; // wlr_gles2_buffer.link + + struct wlr_gles2_buffer *current_buffer; uint32_t viewport_width, viewport_height; }; +struct wlr_gles2_buffer { + struct wlr_buffer *buffer; + struct wlr_gles2_renderer *renderer; + struct wl_list link; // wlr_gles2_renderer.buffers + + EGLImageKHR image; + GLuint rbo; + GLuint fbo; + + struct wl_listener buffer_destroy; +}; + struct wlr_gles2_texture { struct wlr_texture wlr_texture; struct wlr_gles2_renderer *renderer; diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 9ec87293ff..2ff29c114d 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -36,6 +36,139 @@ static struct wlr_gles2_renderer *gles2_get_renderer_in_context( return renderer; } +static void destroy_buffer(struct wlr_gles2_buffer *buffer) { + wl_list_remove(&buffer->link); + wl_list_remove(&buffer->buffer_destroy.link); + + wlr_egl_make_current(buffer->renderer->egl, EGL_NO_SURFACE, NULL); + + push_gles2_debug(buffer->renderer); + + glDeleteFramebuffers(1, &buffer->fbo); + glDeleteRenderbuffers(1, &buffer->rbo); + + pop_gles2_debug(buffer->renderer); + + wlr_egl_destroy_image(buffer->renderer->egl, buffer->image); + wlr_egl_unset_current(buffer->renderer->egl); + free(buffer); +} + +static struct wlr_gles2_buffer *get_buffer(struct wlr_gles2_renderer *renderer, + struct wlr_buffer *wlr_buffer) { + struct wlr_gles2_buffer *buffer; + wl_list_for_each(buffer, &renderer->buffers, link) { + if (buffer->buffer == wlr_buffer) { + return buffer; + } + } + return NULL; +} + +static void handle_buffer_destroy(struct wl_listener *listener, void *data) { + struct wlr_gles2_buffer *buffer = + wl_container_of(listener, buffer, buffer_destroy); + destroy_buffer(buffer); +} + +static struct wlr_gles2_buffer *create_buffer(struct wlr_gles2_renderer *renderer, + struct wlr_buffer *wlr_buffer) { + struct wlr_gles2_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + buffer->buffer = wlr_buffer; + buffer->renderer = renderer; + + struct wlr_dmabuf_attributes dmabuf = {0}; + if (!wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { + goto error_buffer; + } + + bool external_only; + buffer->image = wlr_egl_create_image_from_dmabuf(renderer->egl, + &dmabuf, &external_only); + if (buffer->image == EGL_NO_IMAGE_KHR) { + goto error_buffer; + } + + push_gles2_debug(renderer); + + glGenRenderbuffers(1, &buffer->rbo); + glBindRenderbuffer(GL_RENDERBUFFER, buffer->rbo); + renderer->procs.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, + buffer->image); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + glGenFramebuffers(1, &buffer->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, buffer->fbo); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, buffer->rbo); + GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + pop_gles2_debug(renderer); + + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + wlr_log(WLR_ERROR, "Failed to create FBO"); + goto error_image; + } + + buffer->buffer_destroy.notify = handle_buffer_destroy; + wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy); + + wl_list_insert(&renderer->buffers, &buffer->link); + + wlr_log(WLR_DEBUG, "Created GL FBO for buffer %dx%d", + wlr_buffer->width, wlr_buffer->height); + + return buffer; + +error_image: + wlr_egl_destroy_image(renderer->egl, buffer->image); +error_buffer: + free(buffer); + return NULL; +} + +static bool gles2_bind_buffer(struct wlr_renderer *wlr_renderer, + struct wlr_buffer *wlr_buffer) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer_in_context(wlr_renderer); + + if (renderer->current_buffer != NULL) { + push_gles2_debug(renderer); + glFlush(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + pop_gles2_debug(renderer); + + wlr_buffer_unlock(renderer->current_buffer->buffer); + renderer->current_buffer = NULL; + } + + if (wlr_buffer == NULL) { + return true; + } + + struct wlr_gles2_buffer *buffer = get_buffer(renderer, wlr_buffer); + if (buffer == NULL) { + buffer = create_buffer(renderer, wlr_buffer); + } + if (buffer == NULL) { + return false; + } + + wlr_buffer_lock(wlr_buffer); + renderer->current_buffer = buffer; + + push_gles2_debug(renderer); + glBindFramebuffer(GL_FRAMEBUFFER, renderer->current_buffer->fbo); + pop_gles2_debug(renderer); + + return true; +} + static void gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width, uint32_t height) { struct wlr_gles2_renderer *renderer = @@ -477,6 +610,11 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) { wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL); + struct wlr_gles2_buffer *buffer, *buffer_tmp; + wl_list_for_each_safe(buffer, buffer_tmp, &renderer->buffers, link) { + destroy_buffer(buffer); + } + push_gles2_debug(renderer); glDeleteProgram(renderer->shaders.quad.program); glDeleteProgram(renderer->shaders.ellipse.program); @@ -497,6 +635,7 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) { static const struct wlr_renderer_impl renderer_impl = { .destroy = gles2_destroy, + .bind_buffer = gles2_bind_buffer, .begin = gles2_begin, .end = gles2_end, .clear = gles2_clear, @@ -668,6 +807,8 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { } wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl); + wl_list_init(&renderer->buffers); + renderer->egl = egl; renderer->exts_str = exts_str; From 4268164f265a3146dad893cae488716f7e4332d9 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 11 Jun 2020 18:40:29 +0200 Subject: [PATCH 08/14] render/gles2: fix y-inverted output when rendering to buffer --- render/gles2/renderer.c | 81 ++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 2ff29c114d..faef832f08 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -214,8 +214,12 @@ static void gles2_scissor(struct wlr_renderer *wlr_renderer, push_gles2_debug(renderer); if (box != NULL) { struct wlr_box gl_box; - wlr_box_transform(&gl_box, box, WL_OUTPUT_TRANSFORM_FLIPPED_180, - renderer->viewport_width, renderer->viewport_height); + if (renderer->current_buffer != NULL) { + memcpy(&gl_box, box, sizeof(gl_box)); + } else { + wlr_box_transform(&gl_box, box, WL_OUTPUT_TRANSFORM_FLIPPED_180, + renderer->viewport_width, renderer->viewport_height); + } glScissor(gl_box.x, gl_box.y, gl_box.width, gl_box.height); glEnable(GL_SCISSOR_TEST); @@ -225,6 +229,12 @@ static void gles2_scissor(struct wlr_renderer *wlr_renderer, pop_gles2_debug(renderer); } +static const float flip_180[9] = { + 1.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, 0.0f, 1.0f, +}; + static bool gles2_render_subtexture_with_matrix( struct wlr_renderer *wlr_renderer, struct wlr_texture *wlr_texture, const struct wlr_fbox *box, const float matrix[static 9], @@ -257,10 +267,15 @@ static bool gles2_render_subtexture_with_matrix( abort(); } + float gl_matrix[9]; + if (renderer->current_buffer != NULL) { + wlr_matrix_multiply(gl_matrix, flip_180, matrix); + } else { + memcpy(gl_matrix, matrix, sizeof(gl_matrix)); + } // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set // to GL_FALSE - float transposition[9]; - wlr_matrix_transpose(transposition, matrix); + wlr_matrix_transpose(gl_matrix, gl_matrix); push_gles2_debug(renderer); @@ -271,7 +286,7 @@ static bool gles2_render_subtexture_with_matrix( glUseProgram(shader->program); - glUniformMatrix3fv(shader->proj, 1, GL_FALSE, transposition); + glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); glUniform1i(shader->invert_y, texture->inverted_y); glUniform1i(shader->tex, 0); glUniform1f(shader->alpha, alpha); @@ -309,15 +324,20 @@ static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); + float gl_matrix[9]; + if (renderer->current_buffer != NULL) { + wlr_matrix_multiply(gl_matrix, flip_180, matrix); + } else { + memcpy(gl_matrix, matrix, sizeof(gl_matrix)); + } // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set // to GL_FALSE - float transposition[9]; - wlr_matrix_transpose(transposition, matrix); + wlr_matrix_transpose(gl_matrix, gl_matrix); push_gles2_debug(renderer); glUseProgram(renderer->shaders.quad.program); - glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, transposition); + glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, gl_matrix); glUniform4f(renderer->shaders.quad.color, color[0], color[1], color[2], color[3]); glVertexAttribPointer(renderer->shaders.quad.pos_attrib, 2, GL_FLOAT, GL_FALSE, @@ -337,10 +357,15 @@ static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer, struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); + float gl_matrix[9]; + if (renderer->current_buffer != NULL) { + wlr_matrix_multiply(gl_matrix, flip_180, matrix); + } else { + memcpy(gl_matrix, matrix, sizeof(gl_matrix)); + } // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set // to GL_FALSE - float transposition[9]; - wlr_matrix_transpose(transposition, matrix); + wlr_matrix_transpose(gl_matrix, gl_matrix); static const GLfloat texcoord[] = { 1, 0, // top right @@ -352,7 +377,7 @@ static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer, push_gles2_debug(renderer); glUseProgram(renderer->shaders.ellipse.program); - glUniformMatrix3fv(renderer->shaders.ellipse.proj, 1, GL_FALSE, transposition); + glUniformMatrix3fv(renderer->shaders.ellipse.proj, 1, GL_FALSE, gl_matrix); glUniform4f(renderer->shaders.ellipse.color, color[0], color[1], color[2], color[3]); glVertexAttribPointer(renderer->shaders.ellipse.pos_attrib, 2, GL_FLOAT, @@ -472,16 +497,32 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, if (pack_stride == stride && dst_x == 0 && flags != NULL) { // Under these particular conditions, we can read the pixels with only // one glReadPixels call - glReadPixels(src_x, renderer->viewport_height - height - src_y, - width, height, fmt->gl_format, fmt->gl_type, p); - *flags = WLR_RENDERER_READ_PIXELS_Y_INVERT; + + uint32_t y = src_y; + if (renderer->current_buffer == NULL) { + y = renderer->viewport_height - height - src_y; + } + + glReadPixels(src_x, y, width, height, fmt->gl_format, fmt->gl_type, p); + + if (renderer->current_buffer != NULL) { + *flags = 0; + } else { + *flags = WLR_RENDERER_READ_PIXELS_Y_INVERT; + } } else { // Unfortunately GLES2 doesn't support GL_PACK_*, so we have to read // the lines out row by row for (size_t i = 0; i < height; ++i) { - glReadPixels(src_x, renderer->viewport_height - src_y - i - 1, width, 1, fmt->gl_format, + uint32_t y = src_y + i; + if (renderer->current_buffer == NULL) { + y = renderer->viewport_height - src_y - i - 1; + } + + glReadPixels(src_x, y, width, 1, fmt->gl_format, fmt->gl_type, p + i * stride + dst_x * fmt->bpp / 8); } + if (flags != NULL) { *flags = 0; } @@ -510,6 +551,7 @@ static bool gles2_blit_dmabuf(struct wlr_renderer *wlr_renderer, goto restore_context_out; } + // TODO: get inverted_y right when current_buffer != NULL // This is to take into account y-inversion on both buffers rather than // just the source buffer. bool src_inverted_y = @@ -517,9 +559,12 @@ static bool gles2_blit_dmabuf(struct wlr_renderer *wlr_renderer, bool dst_inverted_y = !!(dst_attr->flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT); struct wlr_gles2_texture *gles2_src_tex = gles2_get_texture(src_tex); - // The result is negated because wlr_matrix_projection y-inverts the - // texture. - gles2_src_tex->inverted_y = !(src_inverted_y ^ dst_inverted_y); + gles2_src_tex->inverted_y = src_inverted_y ^ dst_inverted_y; + if (renderer->current_buffer == NULL) { + // The result is negated because wlr_matrix_projection y-inverts the + // texture. + gles2_src_tex->inverted_y = !gles2_src_tex->inverted_y; + } if (!wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL)) { goto texture_destroy_out; From 5469683d0bcca82df4122d78225cc71b15ed345f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 1 Jun 2020 19:49:51 +0200 Subject: [PATCH 09/14] backend/drm: use wlr_swapchain --- backend/drm/drm.c | 6 +- backend/drm/renderer.c | 140 ++++++++++++++++++++------------- include/backend/drm/renderer.h | 11 ++- 3 files changed, 94 insertions(+), 63 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 40b73925a9..ecd98fdab0 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -593,8 +593,8 @@ static bool drm_connector_commit(struct wlr_output *output) { } static void drm_connector_rollback_render(struct wlr_output *output) { - struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); - wlr_egl_unset_current(&drm->renderer.egl); + struct wlr_drm_connector *conn = get_drm_connector_from_output(output); + return drm_surface_unset_current(&conn->crtc->primary->surf); } size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, @@ -883,7 +883,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output, return false; } - if (!plane->surf.gbm) { + if (!plane->surf.swapchain) { int ret; uint64_t w, h; ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &w); diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 4b6e9bf18c..b78cf7e28f 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -12,9 +12,13 @@ #include #include #include "backend/drm/drm.h" +#include "render/gbm_allocator.h" +#include "render/swapchain.h" +#include "render/wlr_renderer.h" bool init_drm_renderer(struct wlr_drm_backend *drm, struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_renderer_func) { + // TODO: get rid of renderer->gbm renderer->gbm = gbm_create_device(drm->fd); if (!renderer->gbm) { wlr_log(WLR_ERROR, "Failed to create GBM device"); @@ -44,9 +48,17 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, goto error_gbm; } + renderer->allocator = wlr_gbm_allocator_create(drm->fd); + if (renderer->allocator == NULL) { + wlr_log(WLR_ERROR, "Failed to create allocator"); + goto error_wlr_rend; + } + renderer->fd = drm->fd; return true; +error_wlr_rend: + wlr_renderer_destroy(renderer->wlr_rend); error_gbm: gbm_device_destroy(renderer->gbm); return false; @@ -57,6 +69,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) { return; } + wlr_allocator_destroy(&renderer->allocator->base); wlr_renderer_destroy(renderer->wlr_rend); wlr_egl_finish(&renderer->egl); gbm_device_destroy(renderer->gbm); @@ -64,7 +77,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) { static bool init_drm_surface(struct wlr_drm_surface *surf, struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height, - uint32_t format, struct wlr_drm_format_set *set, uint32_t flags) { + uint32_t format, const struct wlr_drm_format_set *set, uint32_t flags) { if (surf->width == width && surf->height == height) { return true; } @@ -73,43 +86,41 @@ static bool init_drm_surface(struct wlr_drm_surface *surf, surf->width = width; surf->height = height; - if (surf->gbm) { - gbm_surface_destroy(surf->gbm); - surf->gbm = NULL; - } - wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl); - - if (!(flags & GBM_BO_USE_LINEAR) && set != NULL) { - const struct wlr_drm_format *drm_format = - wlr_drm_format_set_get(set, format); - if (drm_format != NULL) { - surf->gbm = gbm_surface_create_with_modifiers(renderer->gbm, - width, height, format, drm_format->modifiers, drm_format->len); - } - } + wlr_buffer_unlock(surf->back_buffer); + surf->back_buffer = NULL; + wlr_swapchain_destroy(surf->swapchain); + surf->swapchain = NULL; - if (surf->gbm == NULL) { - surf->gbm = gbm_surface_create(renderer->gbm, width, height, - format, GBM_BO_USE_RENDERING | flags); - } - if (!surf->gbm) { - wlr_log_errno(WLR_ERROR, "Failed to create GBM surface"); - goto error_zero; + const struct wlr_drm_format *drm_format = NULL; + const struct wlr_drm_format format_no_modifiers = { .format = format }; + if (set != NULL) { + drm_format = wlr_drm_format_set_get(set, format); + } else { + drm_format = &format_no_modifiers; } - surf->egl = wlr_egl_create_surface(&renderer->egl, surf->gbm); - if (surf->egl == EGL_NO_SURFACE) { - wlr_log(WLR_ERROR, "Failed to create EGL surface"); - goto error_gbm; + struct wlr_drm_format *format_linear = NULL; + if (flags & GBM_BO_USE_LINEAR) { + format_linear = calloc(1, sizeof(struct wlr_drm_format) + sizeof(uint64_t)); + if (format_linear == NULL) { + return false; + } + format_linear->format = format; + format_linear->len = 1; + format_linear->modifiers[0] = DRM_FORMAT_MOD_LINEAR; + drm_format = format_linear; + } + + surf->swapchain = wlr_swapchain_create(&renderer->allocator->base, + width, height, drm_format); + free(format_linear); + if (surf->swapchain == NULL) { + wlr_log(WLR_ERROR, "Failed to create swapchain"); + memset(surf, 0, sizeof(*surf)); + return false; } return true; - -error_gbm: - gbm_surface_destroy(surf->gbm); -error_zero: - memset(surf, 0, sizeof(*surf)); - return false; } static void finish_drm_surface(struct wlr_drm_surface *surf) { @@ -117,17 +128,44 @@ static void finish_drm_surface(struct wlr_drm_surface *surf) { return; } - wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl); - if (surf->gbm) { - gbm_surface_destroy(surf->gbm); - } + wlr_buffer_unlock(surf->back_buffer); + wlr_swapchain_destroy(surf->swapchain); memset(surf, 0, sizeof(*surf)); } bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age) { - return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_age); + wlr_buffer_unlock(surf->back_buffer); + surf->back_buffer = wlr_swapchain_acquire(surf->swapchain); + if (surf->back_buffer == NULL) { + wlr_log(WLR_ERROR, "Failed to acquire swapchain buffer"); + return false; + } + + if (!wlr_egl_make_current(&surf->renderer->egl, EGL_NO_SURFACE, NULL)) { + return false; + } + if (!wlr_renderer_bind_buffer(surf->renderer->wlr_rend, surf->back_buffer)) { + wlr_log(WLR_ERROR, "Failed to attach buffer to renderer"); + return false; + } + + // TODO: damage tracking + if (buffer_age != NULL) { + *buffer_age = -1; + } + return true; +} + +void drm_surface_unset_current(struct wlr_drm_surface *surf) { + assert(surf->back_buffer != NULL); + + wlr_renderer_bind_buffer(surf->renderer->wlr_rend, NULL); + wlr_egl_unset_current(&surf->renderer->egl); + + wlr_buffer_unlock(surf->back_buffer); + surf->back_buffer = NULL; } bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) { @@ -251,7 +289,7 @@ void drm_fb_clear(struct wlr_drm_fb *fb) { assert(!fb->bo); break; case WLR_DRM_FB_TYPE_SURFACE: - gbm_surface_release_buffer(fb->surf->gbm, fb->bo); + abort(); // TODO: remove this case entirely break; case WLR_DRM_FB_TYPE_WLR_BUFFER: gbm_bo_destroy(fb->bo); @@ -264,29 +302,22 @@ void drm_fb_clear(struct wlr_drm_fb *fb) { fb->bo = NULL; if (fb->mgpu_bo) { - assert(fb->mgpu_surf); + // TODO + /*assert(fb->mgpu_surf); gbm_surface_release_buffer(fb->mgpu_surf->gbm, fb->mgpu_bo); fb->mgpu_bo = NULL; - fb->mgpu_surf = NULL; + fb->mgpu_surf = NULL;*/ } } bool drm_fb_lock_surface(struct wlr_drm_fb *fb, struct wlr_drm_surface *surf) { - drm_fb_clear(fb); + assert(surf->back_buffer != NULL); - if (!wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, NULL)) { - wlr_log(WLR_ERROR, "Failed to swap buffers"); - return false; - } - - fb->bo = gbm_surface_lock_front_buffer(surf->gbm); - if (!fb->bo) { - wlr_log(WLR_ERROR, "Failed to lock front buffer"); + if (!drm_fb_import_wlr(fb, surf->renderer, surf->back_buffer, NULL)) { return false; } - fb->type = WLR_DRM_FB_TYPE_SURFACE; - fb->surf = surf; + drm_surface_unset_current(surf); return true; } @@ -299,7 +330,7 @@ bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer, return false; } - if (!wlr_drm_format_set_has(set, attribs.format, attribs.modifier)) { + if (set && !wlr_drm_format_set_has(set, attribs.format, attribs.modifier)) { // The format isn't supported by the plane. Try stripping the alpha // channel, if any. uint32_t format = strip_alpha_channel(attribs.format); @@ -410,7 +441,8 @@ struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f); wlr_renderer_end(renderer); - if (!wlr_egl_swap_buffers(&mgpu->renderer->egl, mgpu->egl, NULL)) { + // TODO + /*if (!wlr_egl_swap_buffers(&mgpu->renderer->egl, mgpu->egl, NULL)) { wlr_log(WLR_ERROR, "Failed to swap buffers"); return NULL; } @@ -421,6 +453,6 @@ struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm return NULL; } - fb->mgpu_surf = mgpu; + fb->mgpu_surf = mgpu;*/ return fb->mgpu_bo; } diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index bfccf9d5c9..8cc4b0de1f 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -20,6 +20,7 @@ struct wlr_drm_renderer { uint32_t gbm_format; struct wlr_renderer *wlr_rend; + struct wlr_gbm_allocator *allocator; }; struct wlr_drm_surface { @@ -28,8 +29,8 @@ struct wlr_drm_surface { uint32_t width; uint32_t height; - struct gbm_surface *gbm; - EGLSurface egl; + struct wlr_swapchain *swapchain; + struct wlr_buffer *back_buffer; }; enum wlr_drm_fb_type { @@ -45,10 +46,7 @@ struct wlr_drm_fb { struct wlr_drm_surface *mgpu_surf; struct gbm_bo *mgpu_bo; - union { - struct wlr_drm_surface *surf; - struct wlr_buffer *wlr_buf; - }; + struct wlr_buffer *wlr_buf; }; bool init_drm_renderer(struct wlr_drm_backend *drm, @@ -56,6 +54,7 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, void finish_drm_renderer(struct wlr_drm_renderer *renderer); bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age); +void drm_surface_unset_current(struct wlr_drm_surface *surf); bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs); void drm_fb_clear(struct wlr_drm_fb *fb); From 5f8bfdf7cfd6e459103140845f6067e6cc1acb0c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jul 2020 18:36:31 +0200 Subject: [PATCH 10/14] render/swapchain: add support for buffer age --- include/render/swapchain.h | 13 +++++++++-- render/swapchain.c | 46 +++++++++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/include/render/swapchain.h b/include/render/swapchain.h index 57a0cd3f00..243f0404cc 100644 --- a/include/render/swapchain.h +++ b/include/render/swapchain.h @@ -10,6 +10,7 @@ struct wlr_swapchain_slot { struct wlr_buffer *buffer; bool acquired; // waiting for release + int age; struct wl_listener release; }; @@ -35,7 +36,15 @@ void wlr_swapchain_destroy(struct wlr_swapchain *swapchain); * 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); +struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, + int *age); +/** + * Mark the buffer as submitted for presentation. This needs to be called by + * swap chain users on frame boundaries. + * + * If the buffer hasn't been created via the swap chain, the call is ignored. + */ +void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain, + struct wlr_buffer *buffer); #endif diff --git a/render/swapchain.c b/render/swapchain.c index 4145efd2a7..fd9f872538 100644 --- a/render/swapchain.c +++ b/render/swapchain.c @@ -63,7 +63,8 @@ static void slot_handle_release(struct wl_listener *listener, void *data) { slot->acquired = false; } -static struct wlr_buffer *slot_acquire(struct wlr_swapchain_slot *slot) { +static struct wlr_buffer *slot_acquire(struct wlr_swapchain *swapchain, + struct wlr_swapchain_slot *slot, int *age) { assert(!slot->acquired); assert(slot->buffer != NULL); @@ -72,11 +73,15 @@ static struct wlr_buffer *slot_acquire(struct wlr_swapchain_slot *slot) { slot->release.notify = slot_handle_release; wl_signal_add(&slot->buffer->events.release, &slot->release); + if (age != NULL) { + *age = slot->age; + } + return wlr_buffer_lock(slot->buffer); } -struct wlr_buffer *wlr_swapchain_acquire( - struct wlr_swapchain *swapchain) { +struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, + int *age) { 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]; @@ -84,7 +89,7 @@ struct wlr_buffer *wlr_swapchain_acquire( continue; } if (slot->buffer != NULL) { - return slot_acquire(slot); + return slot_acquire(swapchain, slot, age); } free_slot = slot; } @@ -104,5 +109,36 @@ struct wlr_buffer *wlr_swapchain_acquire( wlr_log(WLR_ERROR, "Failed to allocate buffer"); return NULL; } - return slot_acquire(free_slot); + return slot_acquire(swapchain, free_slot, age); +} + +static bool swapchain_has_buffer(struct wlr_swapchain *swapchain, + struct wlr_buffer *buffer) { + for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { + struct wlr_swapchain_slot *slot = &swapchain->slots[i]; + if (slot->buffer == buffer) { + return true; + } + } + return false; +} + +void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain, + struct wlr_buffer *buffer) { + assert(buffer != NULL); + + if (!swapchain_has_buffer(swapchain, buffer)) { + return; + } + + // See the algorithm described in: + // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_buffer_age.txt + for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { + struct wlr_swapchain_slot *slot = &swapchain->slots[i]; + if (slot->buffer == buffer) { + slot->age = 1; + } else if (slot->age > 0) { + slot->age++; + } + } } From 5030f6848316a8ad9f0f82733ec28dfa3ad21402 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jul 2020 18:37:40 +0200 Subject: [PATCH 11/14] backend/drm: add support for wlr_swapchain buffer age --- backend/drm/drm.c | 14 ++++++++++++-- backend/drm/renderer.c | 6 +----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index ecd98fdab0..bd482be3c8 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -27,6 +27,7 @@ #include "backend/drm/drm.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" +#include "render/swapchain.h" #include "util/signal.h" bool check_drm_features(struct wlr_drm_backend *drm) { @@ -331,6 +332,15 @@ static bool drm_connector_attach_render(struct wlr_output *output, return drm_surface_make_current(&conn->crtc->primary->surf, buffer_age); } +static void drm_plane_set_committed(struct wlr_drm_plane *plane) { + drm_fb_move(&plane->queued_fb, &plane->pending_fb); + + struct wlr_buffer *queued = plane->queued_fb.wlr_buf; + if (queued != NULL) { + wlr_swapchain_set_buffer_submitted(plane->surf.swapchain, queued); + } +} + static bool drm_crtc_commit(struct wlr_drm_connector *conn, uint32_t flags) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(conn->output.backend); @@ -338,9 +348,9 @@ static bool drm_crtc_commit(struct wlr_drm_connector *conn, uint32_t flags) { bool ok = drm->iface->crtc_commit(drm, conn, flags); if (ok && !(flags & DRM_MODE_ATOMIC_TEST_ONLY)) { memcpy(&crtc->current, &crtc->pending, sizeof(struct wlr_drm_crtc_state)); - drm_fb_move(&crtc->primary->queued_fb, &crtc->primary->pending_fb); + drm_plane_set_committed(crtc->primary); if (crtc->cursor != NULL) { - drm_fb_move(&crtc->cursor->queued_fb, &crtc->cursor->pending_fb); + drm_plane_set_committed(crtc->cursor); } } else { memcpy(&crtc->pending, &crtc->current, sizeof(struct wlr_drm_crtc_state)); diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index b78cf7e28f..1d6a32776b 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -137,7 +137,7 @@ static void finish_drm_surface(struct wlr_drm_surface *surf) { bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age) { wlr_buffer_unlock(surf->back_buffer); - surf->back_buffer = wlr_swapchain_acquire(surf->swapchain); + surf->back_buffer = wlr_swapchain_acquire(surf->swapchain, buffer_age); if (surf->back_buffer == NULL) { wlr_log(WLR_ERROR, "Failed to acquire swapchain buffer"); return false; @@ -151,10 +151,6 @@ bool drm_surface_make_current(struct wlr_drm_surface *surf, return false; } - // TODO: damage tracking - if (buffer_age != NULL) { - *buffer_age = -1; - } return true; } From 59bc42f12975a43071432aa61395fc2177242e4e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Jul 2020 10:47:20 +0200 Subject: [PATCH 12/14] backend/drm: get rid of wlr_drm_fb_type Since all DRM FBs are backed by a wlr_buffer, there's no need for this anymore. --- backend/drm/drm.c | 20 ++++++++++---------- backend/drm/renderer.c | 25 +++++++++---------------- include/backend/drm/renderer.h | 10 +--------- 3 files changed, 20 insertions(+), 35 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index bd482be3c8..95f8c3d0a7 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -377,7 +377,7 @@ static bool drm_crtc_page_flip(struct wlr_drm_connector *conn) { } assert(crtc->pending.active); - assert(plane_get_next_fb(crtc->primary)->type != WLR_DRM_FB_TYPE_NONE); + assert(plane_get_next_fb(crtc->primary)->bo); if (!drm_crtc_commit(conn, DRM_MODE_PAGE_FLIP_EVENT)) { return false; } @@ -650,10 +650,10 @@ static bool drm_connector_export_dmabuf(struct wlr_output *output, } struct wlr_drm_fb *fb = &crtc->primary->queued_fb; - if (fb->type == WLR_DRM_FB_TYPE_NONE) { + if (fb->bo == NULL) { fb = &crtc->primary->current_fb; } - if (fb->type == WLR_DRM_FB_TYPE_NONE) { + if (fb->bo == NULL) { return false; } @@ -661,10 +661,10 @@ static bool drm_connector_export_dmabuf(struct wlr_output *output, } struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane) { - if (plane->pending_fb.type != WLR_DRM_FB_TYPE_NONE) { + if (plane->pending_fb.bo) { return &plane->pending_fb; } - if (plane->queued_fb.type != WLR_DRM_FB_TYPE_NONE) { + if (plane->queued_fb.bo) { return &plane->queued_fb; } return &plane->current_fb; @@ -680,7 +680,7 @@ static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn) { // drm_crtc_page_flip expects a FB to be available struct wlr_drm_plane *plane = crtc->primary; - if (plane_get_next_fb(plane)->type == WLR_DRM_FB_TYPE_NONE) { + if (!plane_get_next_fb(plane)->bo) { drm_surface_render_black_frame(&plane->surf); if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) { return false; @@ -1507,11 +1507,10 @@ static void page_flip_handler(int fd, unsigned seq, } struct wlr_drm_plane *plane = conn->crtc->primary; - if (plane->queued_fb.type != WLR_DRM_FB_TYPE_NONE) { + if (plane->queued_fb.bo) { drm_fb_move(&plane->current_fb, &plane->queued_fb); } - if (conn->crtc->cursor && - conn->crtc->cursor->queued_fb.type != WLR_DRM_FB_TYPE_NONE) { + if (conn->crtc->cursor && conn->crtc->cursor->queued_fb.bo) { drm_fb_move(&conn->crtc->cursor->current_fb, &conn->crtc->cursor->queued_fb); } @@ -1522,7 +1521,8 @@ static void page_flip_handler(int fd, unsigned seq, * data between the GPUs, even if we were using the direct scanout * interface. */ - if (!drm->parent && plane->current_fb.type == WLR_DRM_FB_TYPE_WLR_BUFFER) { + if (!drm->parent && plane->current_fb.wlr_buf && + wlr_client_buffer_get(plane->current_fb.wlr_buf)) { present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY; } diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 1d6a32776b..a841aecf69 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -280,21 +280,15 @@ bool drm_plane_init_surface(struct wlr_drm_plane *plane, } void drm_fb_clear(struct wlr_drm_fb *fb) { - switch (fb->type) { - case WLR_DRM_FB_TYPE_NONE: - assert(!fb->bo); - break; - case WLR_DRM_FB_TYPE_SURFACE: - abort(); // TODO: remove this case entirely - break; - case WLR_DRM_FB_TYPE_WLR_BUFFER: - gbm_bo_destroy(fb->bo); - wlr_buffer_unlock(fb->wlr_buf); - fb->wlr_buf = NULL; - break; - } - - fb->type = WLR_DRM_FB_TYPE_NONE; + if (!fb->bo) { + assert(!fb->wlr_buf); + return; + } + + gbm_bo_destroy(fb->bo); + wlr_buffer_unlock(fb->wlr_buf); + + fb->wlr_buf = NULL; fb->bo = NULL; if (fb->mgpu_bo) { @@ -376,7 +370,6 @@ bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer, return false; } - fb->type = WLR_DRM_FB_TYPE_WLR_BUFFER; fb->wlr_buf = wlr_buffer_lock(buf); return true; diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index 8cc4b0de1f..e3fb6c9eb0 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -33,20 +33,12 @@ struct wlr_drm_surface { struct wlr_buffer *back_buffer; }; -enum wlr_drm_fb_type { - WLR_DRM_FB_TYPE_NONE, - WLR_DRM_FB_TYPE_SURFACE, - WLR_DRM_FB_TYPE_WLR_BUFFER -}; - struct wlr_drm_fb { - enum wlr_drm_fb_type type; struct gbm_bo *bo; + struct wlr_buffer *wlr_buf; struct wlr_drm_surface *mgpu_surf; struct gbm_bo *mgpu_bo; - - struct wlr_buffer *wlr_buf; }; bool init_drm_renderer(struct wlr_drm_backend *drm, From 213970e99a059359eeedab52d9c2c5c5a4e9fe7f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Jul 2020 16:42:59 +0200 Subject: [PATCH 13/14] backend/drm: add support for wlr_swapchain multi-GPU --- backend/drm/renderer.c | 30 ++++++++++++++---------------- include/backend/drm/renderer.h | 1 + 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index a841aecf69..c9f0e6f024 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -292,11 +292,12 @@ void drm_fb_clear(struct wlr_drm_fb *fb) { fb->bo = NULL; if (fb->mgpu_bo) { - // TODO - /*assert(fb->mgpu_surf); - gbm_surface_release_buffer(fb->mgpu_surf->gbm, fb->mgpu_bo); + assert(fb->mgpu_surf); + gbm_bo_destroy(fb->mgpu_bo); + wlr_buffer_unlock(fb->mgpu_wlr_buf); fb->mgpu_bo = NULL; - fb->mgpu_surf = NULL;*/ + fb->mgpu_wlr_buf = NULL; + fb->mgpu_surf = NULL; } } @@ -430,18 +431,15 @@ struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f); wlr_renderer_end(renderer); - // TODO - /*if (!wlr_egl_swap_buffers(&mgpu->renderer->egl, mgpu->egl, NULL)) { - wlr_log(WLR_ERROR, "Failed to swap buffers"); - return NULL; - } - - fb->mgpu_bo = gbm_surface_lock_front_buffer(mgpu->gbm); - if (!fb->mgpu_bo) { - wlr_log(WLR_ERROR, "Failed to lock front buffer"); - return NULL; + struct wlr_drm_fb mgpu_fb = { + .bo = fb->mgpu_bo, + .wlr_buf = fb->mgpu_wlr_buf, + }; + if (!drm_fb_lock_surface(&mgpu_fb, mgpu)) { + return false; } - - fb->mgpu_surf = mgpu;*/ + fb->mgpu_bo = mgpu_fb.bo; + fb->mgpu_wlr_buf = mgpu_fb.wlr_buf; + fb->mgpu_surf = mgpu; return fb->mgpu_bo; } diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index e3fb6c9eb0..7202b0ed09 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -39,6 +39,7 @@ struct wlr_drm_fb { struct wlr_drm_surface *mgpu_surf; struct gbm_bo *mgpu_bo; + struct wlr_buffer *mgpu_wlr_buf; }; bool init_drm_renderer(struct wlr_drm_backend *drm, From 2aac3c2c13230a541aad81d4b83538d5027f0f87 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 14 Aug 2020 12:04:29 +0200 Subject: [PATCH 14/14] backend/drm: check drm_surface_render_black_frame return value This avoids hitting an assertion in drm_fb_lock_surface when we failed to render a black frame. --- backend/drm/drm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 95f8c3d0a7..dd3e9c7167 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -681,7 +681,9 @@ static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn) { // drm_crtc_page_flip expects a FB to be available struct wlr_drm_plane *plane = crtc->primary; if (!plane_get_next_fb(plane)->bo) { - drm_surface_render_black_frame(&plane->surf); + if (!drm_surface_render_black_frame(&plane->surf)) { + return false; + } if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) { return false; }