From 46b0ba0da69eb8e47f453973bc67f2fd69fb16cd Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 14 Sep 2024 17:16:28 -0400 Subject: [PATCH] wlr_raster: Add surface helper --- include/wlr/types/wlr_raster.h | 9 +++ types/wlr_raster.c | 141 +++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/include/wlr/types/wlr_raster.h b/include/wlr/types/wlr_raster.h index c63cb8ee0..6e654bb21 100644 --- a/include/wlr/types/wlr_raster.h +++ b/include/wlr/types/wlr_raster.h @@ -17,6 +17,7 @@ struct wlr_buffer; struct wlr_texture; struct wlr_renderer; struct wlr_drm_syncobj_timeline; +struct wlr_surface; struct wlr_raster { // May be NULL @@ -83,4 +84,12 @@ void wlr_raster_unlock(struct wlr_raster *raster); struct wlr_texture *wlr_raster_obtain_texture(struct wlr_raster *raster, struct wlr_renderer *renderer); +/** + * Creates a wlr_raster from a surface. This will automatically deduplicate + * rasters if multiple are consumed from the same surface so that redundant + * uploads are not performed. The raster returned will automatically be locked. + * Users are required to call wlr_raster_unlock() after invoking this function. + */ +struct wlr_raster *wlr_raster_from_surface(struct wlr_surface *surface); + #endif diff --git a/types/wlr_raster.c b/types/wlr_raster.c index c1ce68438..bb1efa6d5 100644 --- a/types/wlr_raster.c +++ b/types/wlr_raster.c @@ -1,10 +1,13 @@ #include +#include #include +#include #include #include #include #include #include +#include #include "types/wlr_buffer.h" static void raster_handle_buffer_release(struct wl_listener *listener, void *data) { @@ -120,3 +123,141 @@ struct wlr_texture *wlr_raster_obtain_texture(struct wlr_raster *raster, return texture; } + +struct surface_raster { + struct wlr_raster *raster; + struct wlr_surface *surface; + + struct wlr_addon addon; + + struct wl_listener buffer_prerelease; + + bool locking_buffer; +}; + +static void surface_raster_drop_raster(struct surface_raster *surface_raster) { + if (surface_raster->locking_buffer) { + wlr_buffer_unlock(surface_raster->raster->buffer); + surface_raster->locking_buffer = false; + } + + wlr_raster_unlock(surface_raster->raster); + surface_raster->raster = NULL; +} + +static void surface_raster_destroy(struct surface_raster *surface_raster) { + surface_raster_drop_raster(surface_raster); + + wl_list_remove(&surface_raster->buffer_prerelease.link); + wlr_addon_finish(&surface_raster->addon); + free(surface_raster); +} + +static void surface_raster_handle_addon_destroy(struct wlr_addon *addon) { + struct surface_raster *surface_raster = wl_container_of(addon, surface_raster, addon); + surface_raster_destroy(surface_raster); +} + +static void surface_raster_handle_buffer_prerelease(struct wl_listener *listener, void *data) { + struct surface_raster *surface_raster = + wl_container_of(listener, surface_raster, buffer_prerelease); + struct wlr_raster *raster = surface_raster->raster; + + struct wlr_surface_output *output; + wl_list_for_each(output, &surface_raster->surface->current_outputs, link) { + wlr_raster_obtain_texture(raster, output->output->renderer); + } + + // if there was a failed texture upload, keep on locking the buffer + if (!raster->texture) { + wlr_buffer_lock(raster->buffer); + surface_raster->locking_buffer = true; + } + + wl_list_remove(&surface_raster->buffer_prerelease.link); + wl_list_init(&surface_raster->buffer_prerelease.link); +} + +const struct wlr_addon_interface surface_raster_addon_impl = { + .name = "wlr_raster_surface", + .destroy = surface_raster_handle_addon_destroy, +}; + +static struct surface_raster *get_surface_raster(struct wlr_surface *surface) { + struct wlr_addon *addon = wlr_addon_find(&surface->addons, NULL, + &surface_raster_addon_impl); + if (!addon) { + return NULL; + } + + struct surface_raster *surface_raster = wl_container_of(addon, surface_raster, addon); + return surface_raster; +} + +struct wlr_raster *wlr_raster_from_surface(struct wlr_surface *surface) { + struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = + wlr_linux_drm_syncobj_v1_get_surface_state(surface); + + struct wlr_raster_create_options options = {0}; + if (syncobj_surface_state) { + options.wait_timeline = syncobj_surface_state->acquire_timeline; + options.wait_point = syncobj_surface_state->acquire_point; + } + + struct surface_raster *surface_raster = get_surface_raster(surface); + if (!surface_raster) { + surface_raster = calloc(1, sizeof(*surface_raster)); + if (!surface_raster) { + return NULL; + } + + surface_raster->surface = surface; + + wlr_addon_init(&surface_raster->addon, &surface->addons, NULL, + &surface_raster_addon_impl); + + surface_raster->buffer_prerelease.notify = surface_raster_handle_buffer_prerelease; + wl_list_init(&surface_raster->buffer_prerelease.link); + } + + if (!surface->current.buffer) { + // surface is mapped but it hasn't committed a new buffer. We need to keep + // using the old one + if (wlr_surface_has_buffer(surface)) { + if (surface_raster->raster) { + return wlr_raster_lock(surface_raster->raster); + } else { + return NULL; + } + } + + wl_list_remove(&surface_raster->buffer_prerelease.link); + wl_list_init(&surface_raster->buffer_prerelease.link); + + surface_raster_drop_raster(surface_raster); + + return NULL; + } + + struct wlr_raster *raster; + if (surface_raster->raster) { + // make sure we haven't already seen this buffer + if (surface_raster->raster->buffer == surface->current.buffer) { + return wlr_raster_lock(surface_raster->raster); + } + } + + raster = wlr_raster_create(surface->current.buffer, &options); + + if (!raster) { + return NULL; + } + + surface_raster_drop_raster(surface_raster); + surface_raster->raster = wlr_raster_lock(raster); + + wl_list_remove(&surface_raster->buffer_prerelease.link); + wl_signal_add(&surface->current.buffer->events.prerelease, &surface_raster->buffer_prerelease); + + return raster; +}