From 057fd1a0e3b5b9a714d567679939beb862340ce1 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 14 Sep 2024 17:42:40 -0400 Subject: [PATCH 01/10] wlr_raster: Introduce new abstraction --- include/wlr/types/wlr_raster.h | 86 +++++++++++++++++++++++ types/meson.build | 1 + types/wlr_raster.c | 122 +++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 include/wlr/types/wlr_raster.h create mode 100644 types/wlr_raster.c diff --git a/include/wlr/types/wlr_raster.h b/include/wlr/types/wlr_raster.h new file mode 100644 index 000000000..c63cb8ee0 --- /dev/null +++ b/include/wlr/types/wlr_raster.h @@ -0,0 +1,86 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_RASTER_H +#define WLR_TYPES_WLR_RASTER_H + +#include +#include +#include + +struct wlr_buffer; +struct wlr_texture; +struct wlr_renderer; +struct wlr_drm_syncobj_timeline; + +struct wlr_raster { + // May be NULL + struct wlr_buffer *buffer; + + uint32_t width, height; + bool opaque; + + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; + + struct { + struct wl_signal destroy; + } events; + + // private state + + size_t n_locks; + + struct wl_listener buffer_release; + + struct wlr_texture *texture; + struct wl_listener renderer_destroy; +}; + +struct wlr_raster_create_options { + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; +}; + +/** + * Creates a new wlr_raster being backed by the given buffer. The raster will + * not lock the given buffer meaning that once it's released, the raster will + * NULL its buffer reference and potentially become invalid. + * The creation function is referenced: once the creator is done with the raster, + * wlr_raster_unlock must be called as the reference count will start at 1 + * from creation. + * + * Options can be NULL. + */ +struct wlr_raster *wlr_raster_create(struct wlr_buffer *buffer, + const struct wlr_raster_create_options *options); + +/** + * Lock the raster for use. As long as the raster has at least one lock, it + * will not be destroyed. + */ +struct wlr_raster *wlr_raster_lock(struct wlr_raster *raster); + +/** + * Unlock the raster. This must be called after wlr_raster_lock once the raster + * has been finished being used or after creation from wlr_raster_create. + */ +void wlr_raster_unlock(struct wlr_raster *raster); + +/** + * Returns the texture allocated for this renderer. If there is none, + * a new texture will be created and attached to this wlr_raster. Users do not + * own the texture returned by this function and can only be used for read-only + * purposes. + * + * Will return NULL if the creation was unsuccessful. + */ +struct wlr_texture *wlr_raster_obtain_texture(struct wlr_raster *raster, + struct wlr_renderer *renderer); + +#endif diff --git a/types/meson.build b/types/meson.build index ec70d4b7c..032143db6 100644 --- a/types/meson.build +++ b/types/meson.build @@ -68,6 +68,7 @@ wlr_files += files( 'wlr_presentation_time.c', 'wlr_primary_selection_v1.c', 'wlr_primary_selection.c', + 'wlr_raster.c', 'wlr_region.c', 'wlr_relative_pointer_v1.c', 'wlr_screencopy_v1.c', diff --git a/types/wlr_raster.c b/types/wlr_raster.c new file mode 100644 index 000000000..c1ce68438 --- /dev/null +++ b/types/wlr_raster.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include +#include +#include +#include "types/wlr_buffer.h" + +static void raster_handle_buffer_release(struct wl_listener *listener, void *data) { + struct wlr_raster *raster = wl_container_of(listener, raster, buffer_release); + raster->buffer = NULL; + wl_list_remove(&raster->buffer_release.link); + wl_list_init(&raster->buffer_release.link); +} + +struct wlr_raster *wlr_raster_create(struct wlr_buffer *buffer, + const struct wlr_raster_create_options *options) { + struct wlr_raster *raster = calloc(1, sizeof(*raster)); + if (!raster) { + return NULL; + } + + wl_signal_init(&raster->events.destroy); + + assert(buffer); + raster->opaque = buffer_is_opaque(buffer); + raster->width = buffer->width; + raster->height = buffer->height; + raster->buffer = buffer; + + raster->n_locks = 1; + + raster->buffer_release.notify = raster_handle_buffer_release; + wl_signal_add(&raster->buffer->events.release, &raster->buffer_release); + + if (options && options->wait_timeline) { + raster->wait_timeline = wlr_drm_syncobj_timeline_ref(options->wait_timeline); + raster->wait_point = options->wait_point; + } + + return raster; +} + +static void raster_consider_destroy(struct wlr_raster *raster) { + if (raster->n_locks > 0) { + return; + } + + wl_signal_emit_mutable(&raster->events.destroy, NULL); + + if (raster->texture) { + wl_list_remove(&raster->renderer_destroy.link); + wlr_texture_destroy(raster->texture); + } + + wl_list_remove(&raster->buffer_release.link); + wlr_drm_syncobj_timeline_unref(raster->wait_timeline); + free(raster); +} + +struct wlr_raster *wlr_raster_lock(struct wlr_raster *raster) { + raster->n_locks++; + return raster; +} + +void wlr_raster_unlock(struct wlr_raster *raster) { + if (!raster) { + return; + } + + assert(raster->n_locks > 0); + + raster->n_locks--; + raster_consider_destroy(raster); +} + +static void raster_detach(struct wlr_raster *raster, struct wlr_texture *texture) { + assert(texture); + assert(raster->texture == texture); + + wl_list_remove(&raster->renderer_destroy.link); + raster->texture = NULL; +} + +static void handle_renderer_destroy(struct wl_listener *listener, void *data) { + struct wlr_raster *raster = wl_container_of(listener, raster, renderer_destroy); + raster_detach(raster, raster->texture); +} + +static void raster_attach(struct wlr_raster *raster, struct wlr_texture *texture) { + assert(texture->width == raster->width && texture->height == raster->height); + assert(!raster->texture); + + raster->renderer_destroy.notify = handle_renderer_destroy; + wl_signal_add(&texture->renderer->events.destroy, &raster->renderer_destroy); + + raster->texture = texture; +} + +struct wlr_texture *wlr_raster_obtain_texture(struct wlr_raster *raster, + struct wlr_renderer *renderer) { + if (raster->texture) { + assert(raster->texture->renderer == renderer); + return raster->texture; + } + + assert(raster->buffer); + + struct wlr_client_buffer *client_buffer = + wlr_client_buffer_get(raster->buffer); + if (client_buffer != NULL) { + return client_buffer->texture; + } + + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, raster->buffer); + if (texture) { + raster_attach(raster, texture); + } + + return texture; +} From e83147bb71d30f970fbdaf74c339451e39bde9a5 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 21 Apr 2023 20:56:23 +0200 Subject: [PATCH 02/10] wlr_buffer: Introduce prerelease The prerelease signal lets users do things things at the last moment that would be inappropriate to do on the release signal. Inside the prerelease signal, it is allowed to lock the buffer and also upload/import the contents of the buffer to a texture. --- include/wlr/types/wlr_buffer.h | 1 + types/buffer/buffer.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index 9a655dd2b..91543b5ea 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -56,6 +56,7 @@ struct wlr_buffer { struct { struct wl_signal destroy; struct wl_signal release; + struct wl_signal prerelease; } events; struct wlr_addon_set addons; diff --git a/types/buffer/buffer.c b/types/buffer/buffer.c index 953207a2c..ef744f1b5 100644 --- a/types/buffer/buffer.c +++ b/types/buffer/buffer.c @@ -19,6 +19,7 @@ void wlr_buffer_init(struct wlr_buffer *buffer, }; wl_signal_init(&buffer->events.destroy); wl_signal_init(&buffer->events.release); + wl_signal_init(&buffer->events.prerelease); wlr_addon_set_init(&buffer->addons); } @@ -58,6 +59,10 @@ void wlr_buffer_unlock(struct wlr_buffer *buffer) { assert(buffer->n_locks > 0); buffer->n_locks--; + if (buffer->n_locks == 0) { + wl_signal_emit_mutable(&buffer->events.prerelease, NULL); + } + if (buffer->n_locks == 0) { wl_signal_emit_mutable(&buffer->events.release, NULL); } From 7d87ae563e1648b84494c4cf7bfb34381817d66c Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 14 Sep 2024 17:16:28 -0400 Subject: [PATCH 03/10] 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; +} From 851440f59dd1f99327da0d75eed94224a4226748 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 14 Sep 2024 16:42:24 -0400 Subject: [PATCH 04/10] wlr_raster: Add partial texture uploads to surface helper --- types/wlr_raster.c | 92 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/types/wlr_raster.c b/types/wlr_raster.c index bb1efa6d5..a4a95a8d2 100644 --- a/types/wlr_raster.c +++ b/types/wlr_raster.c @@ -124,6 +124,91 @@ struct wlr_texture *wlr_raster_obtain_texture(struct wlr_raster *raster, return texture; } +struct raster_update_state { + struct wlr_buffer *buffer; + pixman_region32_t damage; + + struct wlr_raster *new_raster; + struct wlr_raster *old_raster; + + struct wl_listener old_raster_destroy; + struct wl_listener new_raster_destroy; + struct wl_listener buffer_release; +}; + +static void destroy_raster_update_state(struct raster_update_state *state) { + wl_list_remove(&state->old_raster_destroy.link); + wl_list_remove(&state->new_raster_destroy.link); + wl_list_remove(&state->buffer_release.link); + pixman_region32_fini(&state->damage); + free(state); +} + +static void raster_update_handle_new_raster_destroy(struct wl_listener *listener, void *data) { + struct raster_update_state *state = wl_container_of(listener, state, new_raster_destroy); + destroy_raster_update_state(state); +} + +static void raster_update_handle_old_raster_destroy(struct wl_listener *listener, void *data) { + struct raster_update_state *state = wl_container_of(listener, state, old_raster_destroy); + + // if the new raster already has a texture, there's nothing we can do to help. + if (state->new_raster->texture) { + assert(state->new_raster->texture->renderer == state->old_raster->texture->renderer); + destroy_raster_update_state(state); + return; + } + + struct wlr_texture *texture = state->old_raster->texture; + if (!texture) { + destroy_raster_update_state(state); + return; + } + + if (wlr_texture_update_from_buffer(texture, state->buffer, &state->damage)) { + raster_detach(state->old_raster, texture); + raster_attach(state->new_raster, texture); + } + + destroy_raster_update_state(state); +} + +static void raster_update_handle_buffer_release(struct wl_listener *listener, void *data) { + struct raster_update_state *state = wl_container_of(listener, state, buffer_release); + destroy_raster_update_state(state); +} + +static struct wlr_raster *raster_update(struct wlr_raster *raster, + struct wlr_buffer *buffer, const pixman_region32_t *damage, + const struct wlr_raster_create_options *options) { + struct raster_update_state *state = calloc(1, sizeof(*state)); + if (!state) { + return NULL; + } + + struct wlr_raster *new_raster = wlr_raster_create(buffer, options); + if (!new_raster) { + free(state); + return NULL; + } + + state->old_raster_destroy.notify = raster_update_handle_old_raster_destroy; + wl_signal_add(&raster->events.destroy, &state->old_raster_destroy); + state->new_raster_destroy.notify = raster_update_handle_new_raster_destroy; + wl_signal_add(&new_raster->events.destroy, &state->new_raster_destroy); + state->buffer_release.notify = raster_update_handle_buffer_release; + wl_signal_add(&buffer->events.release, &state->buffer_release); + + state->new_raster = new_raster; + state->old_raster = raster; + state->buffer = buffer; + + pixman_region32_init(&state->damage); + pixman_region32_copy(&state->damage, damage); + + return new_raster; +} + struct surface_raster { struct wlr_raster *raster; struct wlr_surface *surface; @@ -245,9 +330,12 @@ struct wlr_raster *wlr_raster_from_surface(struct wlr_surface *surface) { if (surface_raster->raster->buffer == surface->current.buffer) { return wlr_raster_lock(surface_raster->raster); } - } - raster = wlr_raster_create(surface->current.buffer, &options); + raster = raster_update(surface_raster->raster, + surface->current.buffer, &surface->buffer_damage, &options); + } else { + raster = wlr_raster_create(surface->current.buffer, &options); + } if (!raster) { return NULL; From ce09ecedfddd5251a3dad6fc3335232f9f27da9e Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 10 Apr 2024 11:54:56 -0400 Subject: [PATCH 05/10] wlr_raster: Add backwards compatibility with wlr_client_buffer --- types/wlr_raster.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/types/wlr_raster.c b/types/wlr_raster.c index a4a95a8d2..9248ca471 100644 --- a/types/wlr_raster.c +++ b/types/wlr_raster.c @@ -279,6 +279,21 @@ static struct surface_raster *get_surface_raster(struct wlr_surface *surface) { return surface_raster; } +// Because wlr_raster doesn't lock the buffer itself, we need something extra +// to keep client buffer locked when operating in legacy mode. +struct client_buffer_compat { + struct wlr_client_buffer *buffer; + struct wl_listener destroy; +}; + +static void client_buffer_compat_raster_destroy(struct wl_listener *listener, void *data) { + struct client_buffer_compat *compat = wl_container_of(listener, compat, destroy); + + wlr_buffer_unlock(&compat->buffer->base); + wl_list_remove(&compat->destroy.link); + free(compat); +} + 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); @@ -289,6 +304,32 @@ struct wlr_raster *wlr_raster_from_surface(struct wlr_surface *surface) { options.wait_point = syncobj_surface_state->acquire_point; } + if (surface->compositor->renderer) { + // use legacy wlr_client_buffer + if (!surface->buffer) { + return NULL; + } + + struct client_buffer_compat *compat = calloc(1, sizeof(*compat)); + if (!compat) { + return NULL; + } + + struct wlr_raster *raster = wlr_raster_create(&surface->buffer->base, &options); + if (!raster) { + free(compat); + return NULL; + } + + compat->destroy.notify = client_buffer_compat_raster_destroy; + wl_signal_add(&raster->events.destroy, &compat->destroy); + + compat->buffer = surface->buffer; + wlr_buffer_lock(&surface->buffer->base); + + return raster; + } + struct surface_raster *surface_raster = get_surface_raster(surface); if (!surface_raster) { surface_raster = calloc(1, sizeof(*surface_raster)); From 17cdf9b4ebac4167d510bcb44b3c22044e254380 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 14 Sep 2024 18:42:40 -0400 Subject: [PATCH 06/10] wlr_cursor_set_surface: Use wlr_raster to generate texture from surface Since wlr_raster supports wlr_compositor usage with and without a renderer, use it for wlr_curosr so cursors support running on a surface without a renderer. --- types/wlr_cursor.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index a2489fbd1..9b00e386e 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -539,7 +540,12 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ } else if (cur->state->surface != NULL) { struct wlr_surface *surface = cur->state->surface; - struct wlr_texture *texture = wlr_surface_get_texture(surface); + struct wlr_texture *texture = NULL; + struct wlr_raster *raster = wlr_raster_from_surface(surface); + if (raster) { + texture = wlr_raster_obtain_texture(raster, output_cursor->output_cursor->output->renderer); + } + int32_t hotspot_x = cur->state->surface_hotspot.x; int32_t hotspot_y = cur->state->surface_hotspot.y; @@ -561,12 +567,14 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ &src_box, dst_width, dst_height, surface->current.transform, hotspot_x, hotspot_y, wait_timeline, wait_point); - if (syncobj_surface_state != NULL && surface->buffer != NULL && + if (syncobj_surface_state != NULL && raster && raster->buffer != NULL && (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, - &surface->buffer->base); + raster->buffer); } + wlr_raster_unlock(raster); + if (output_cursor->output_cursor->visible) { wlr_surface_send_enter(surface, output); } else { From 56c38586fcaaf1e7f271ee067f37dbafc9531fc4 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 14 Sep 2024 16:44:57 -0400 Subject: [PATCH 07/10] wlr_scene: Manage textures through a raster --- include/wlr/types/wlr_scene.h | 12 ++-- types/scene/wlr_scene.c | 118 ++++++++++------------------------ 2 files changed, 38 insertions(+), 92 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index bcfbc72f9..2fa1d6b46 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -35,6 +35,7 @@ struct wlr_xdg_surface; struct wlr_layer_surface_v1; struct wlr_drag_icon; struct wlr_surface; +struct wlr_raster; struct wlr_scene_node; struct wlr_scene_buffer; @@ -158,6 +159,7 @@ struct wlr_scene_buffer { // May be NULL struct wlr_buffer *buffer; + struct wlr_raster *raster; struct { struct wl_signal outputs_update; // struct wlr_scene_outputs_update_event @@ -188,15 +190,9 @@ struct wlr_scene_buffer { // private state uint64_t active_outputs; - struct wlr_texture *texture; struct wlr_linux_dmabuf_feedback_v1_init_options prev_feedback_options; bool own_buffer; - int buffer_width, buffer_height; - bool buffer_is_opaque; - - struct wlr_drm_syncobj_timeline *wait_timeline; - uint64_t wait_point; struct wl_listener buffer_release; struct wl_listener renderer_destroy; @@ -427,7 +423,7 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, struct wlr_buffer *buffer); /** - * Sets the buffer's backing buffer. + * Sets the buffer's backing raster. * * If the buffer is NULL, the buffer node will not be displayed. */ @@ -435,7 +431,7 @@ void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, struct wlr_buffer *buffer); /** - * Sets the buffer's backing buffer with a custom damage region. + * Sets the buffer's backing raster with a custom damage region. * * The damage region is in buffer-local coordinates. If the region is NULL, * the whole buffer node will be damaged. diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index b5863f476..7e0bca307 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -91,8 +92,6 @@ struct highlight_region { static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, struct wlr_buffer *buffer); -static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer, - struct wlr_texture *texture); void wlr_scene_node_destroy(struct wlr_scene_node *node) { if (node == NULL) { @@ -123,9 +122,8 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { } scene_buffer_set_buffer(scene_buffer, NULL); - scene_buffer_set_texture(scene_buffer, NULL); + wlr_raster_unlock(scene_buffer->raster); pixman_region32_fini(&scene_buffer->opaque_region); - wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline); } else if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); @@ -259,7 +257,7 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, } else if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - if (!scene_buffer->buffer) { + if (!scene_buffer->raster) { return; } @@ -267,7 +265,7 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, return; } - if (!scene_buffer->buffer_is_opaque) { + if (!scene_buffer->raster->opaque) { pixman_region32_copy(opaque, &scene_buffer->opaque_region); pixman_region32_intersect_rect(opaque, opaque, 0, 0, width, height); pixman_region32_translate(opaque, x, y); @@ -755,9 +753,6 @@ static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, wlr_buffer_unlock(scene_buffer->buffer); } scene_buffer->buffer = NULL; - scene_buffer->own_buffer = false; - scene_buffer->buffer_width = scene_buffer->buffer_height = 0; - scene_buffer->buffer_is_opaque = false; if (!buffer) { return; @@ -765,46 +760,11 @@ static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, scene_buffer->own_buffer = true; scene_buffer->buffer = wlr_buffer_lock(buffer); - scene_buffer->buffer_width = buffer->width; - scene_buffer->buffer_height = buffer->height; - scene_buffer->buffer_is_opaque = buffer_is_opaque(buffer); scene_buffer->buffer_release.notify = scene_buffer_handle_buffer_release; wl_signal_add(&buffer->events.release, &scene_buffer->buffer_release); } -static void scene_buffer_handle_renderer_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_buffer *scene_buffer = wl_container_of(listener, scene_buffer, renderer_destroy); - scene_buffer_set_texture(scene_buffer, NULL); -} - -static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer, - struct wlr_texture *texture) { - wl_list_remove(&scene_buffer->renderer_destroy.link); - wlr_texture_destroy(scene_buffer->texture); - scene_buffer->texture = texture; - - if (texture != NULL) { - scene_buffer->renderer_destroy.notify = scene_buffer_handle_renderer_destroy; - wl_signal_add(&texture->renderer->events.destroy, &scene_buffer->renderer_destroy); - } else { - wl_list_init(&scene_buffer->renderer_destroy.link); - } -} - -static void scene_buffer_set_wait_timeline(struct wlr_scene_buffer *scene_buffer, - struct wlr_drm_syncobj_timeline *timeline, uint64_t point) { - wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline); - if (timeline != NULL) { - scene_buffer->wait_timeline = wlr_drm_syncobj_timeline_ref(timeline); - scene_buffer->wait_point = point; - } else { - scene_buffer->wait_timeline = NULL; - scene_buffer->wait_point = 0; - } -} - struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, struct wlr_buffer *buffer) { struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer)); @@ -823,6 +783,7 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, wl_list_init(&scene_buffer->buffer_release.link); wl_list_init(&scene_buffer->renderer_destroy.link); scene_buffer->opacity = 1; + scene_buffer->raster = buffer ? wlr_raster_create(buffer, NULL) : NULL; scene_buffer_set_buffer(scene_buffer, buffer); scene_node_update(&scene_buffer->node, NULL); @@ -843,7 +804,7 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf assert(buffer || !options->damage); bool mapped = buffer != NULL; - bool prev_mapped = scene_buffer->buffer != NULL || scene_buffer->texture != NULL; + bool prev_mapped = scene_buffer->raster != NULL; if (!mapped && !prev_mapped) { // unmapping already unmapped buffer - noop @@ -855,14 +816,20 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf // produce we need to update the node. bool update = mapped != prev_mapped; if (buffer != NULL && scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0) { - update = update || scene_buffer->buffer_width != buffer->width || - scene_buffer->buffer_height != buffer->height; + update = update || (int)scene_buffer->raster->width != buffer->width || + (int)scene_buffer->raster->height != buffer->height; } + wlr_raster_unlock(scene_buffer->raster); + scene_buffer->raster = NULL; + scene_buffer_set_buffer(scene_buffer, buffer); - scene_buffer_set_texture(scene_buffer, NULL); - scene_buffer_set_wait_timeline(scene_buffer, - options->wait_timeline, options->wait_point); + if (buffer) { + scene_buffer->raster = wlr_raster_create(buffer, &(struct wlr_raster_create_options) { + .wait_timeline = options->wait_timeline, + .wait_point = options->wait_point, + }); + } if (update) { scene_node_update(&scene_buffer->node, NULL); @@ -1057,28 +1024,6 @@ void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, scene_node_update(&scene_buffer->node, NULL); } -static struct wlr_texture *scene_buffer_get_texture( - struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { - if (scene_buffer->buffer == NULL || scene_buffer->texture != NULL) { - return scene_buffer->texture; - } - - struct wlr_client_buffer *client_buffer = - wlr_client_buffer_get(scene_buffer->buffer); - if (client_buffer != NULL) { - return client_buffer->texture; - } - - struct wlr_texture *texture = - wlr_texture_from_buffer(renderer, scene_buffer->buffer); - if (texture != NULL && scene_buffer->own_buffer) { - scene_buffer->own_buffer = false; - wlr_buffer_unlock(scene_buffer->buffer); - } - scene_buffer_set_texture(scene_buffer, texture); - return texture; -} - static void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height) { *width = 0; @@ -1097,9 +1042,9 @@ static void scene_node_get_size(struct wlr_scene_node *node, if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) { *width = scene_buffer->dst_width; *height = scene_buffer->dst_height; - } else { - *width = scene_buffer->buffer_width; - *height = scene_buffer->buffer_height; + } else if (scene_buffer->raster) { + *width = scene_buffer->raster->width; + *height = scene_buffer->raster->height; wlr_output_transform_coords(scene_buffer->transform, width, height); } break; @@ -1368,8 +1313,12 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, - data->output->output->renderer); + struct wlr_texture *texture = NULL; + if (scene_buffer->raster) { + texture = wlr_raster_obtain_texture(scene_buffer->raster, + data->output->output->renderer); + } + if (texture == NULL) { scene_output_damage(data->output, &render_region); break; @@ -1390,8 +1339,8 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .blend_mode = !data->output->scene->calculate_visibility || pixman_region32_not_empty(&opaque) ? WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, - .wait_timeline = scene_buffer->wait_timeline, - .wait_point = scene_buffer->wait_point, + .wait_timeline = scene_buffer->raster->wait_timeline, + .wait_point = scene_buffer->raster->wait_point, }); struct wlr_scene_output_sample_event sample_event = { @@ -1702,7 +1651,7 @@ static bool scene_node_invisible(struct wlr_scene_node *node) { } else if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - return buffer->buffer == NULL && buffer->texture == NULL; + return buffer->raster == NULL; } return false; @@ -1825,8 +1774,8 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, return false; } - int default_width = buffer->buffer->width; - int default_height = buffer->buffer->height; + int default_width = buffer->raster->width; + int default_height = buffer->raster->height; wlr_output_transform_coords(buffer->transform, &default_width, &default_height); struct wlr_fbox default_box = { .width = default_width, @@ -1866,8 +1815,9 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, } wlr_output_state_set_buffer(&pending, buffer->buffer); - if (buffer->wait_timeline != NULL) { - wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point); + if (buffer->raster->wait_timeline != NULL) { + wlr_output_state_set_wait_timeline(&pending, + buffer->raster->wait_timeline, buffer->raster->wait_point); } if (!wlr_output_test_state(scene_output->output, &pending)) { From 99eaf7006fc811609bf780f47e1a1709550e58e5 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 14 Sep 2024 17:05:32 -0400 Subject: [PATCH 08/10] wlr_scene: Introduce wlr_scene_buffer_set_raster_with_damage --- include/wlr/types/wlr_scene.h | 9 ++++ types/scene/wlr_scene.c | 81 ++++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 2fa1d6b46..c3571e6f0 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -461,6 +461,15 @@ struct wlr_scene_buffer_set_buffer_options { void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buffer, struct wlr_buffer *buffer, const struct wlr_scene_buffer_set_buffer_options *options); +/* + * Sets the buffer's backing raster with a custom damage region. + * + * The damage region is in buffer-local coordinates. If the region is NULL, + * the whole buffer node will be damaged. + */ +void wlr_scene_buffer_set_raster_with_damage(struct wlr_scene_buffer *scene_buffer, + struct wlr_raster *raster, const pixman_region32_t *damage); + /** * Sets the buffer's opaque region. This is an optimization hint used to * determine if buffers which reside under this one need to be rendered or not. diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 7e0bca307..3e5e77ace 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -752,15 +752,13 @@ static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, if (scene_buffer->own_buffer) { wlr_buffer_unlock(scene_buffer->buffer); } - scene_buffer->buffer = NULL; + scene_buffer->buffer = buffer; + scene_buffer->own_buffer = false; if (!buffer) { return; } - scene_buffer->own_buffer = true; - scene_buffer->buffer = wlr_buffer_lock(buffer); - scene_buffer->buffer_release.notify = scene_buffer_handle_buffer_release; wl_signal_add(&buffer->events.release, &scene_buffer->buffer_release); } @@ -783,9 +781,14 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, wl_list_init(&scene_buffer->buffer_release.link); wl_list_init(&scene_buffer->renderer_destroy.link); scene_buffer->opacity = 1; - scene_buffer->raster = buffer ? wlr_raster_create(buffer, NULL) : NULL; - scene_buffer_set_buffer(scene_buffer, buffer); + + if (buffer) { + scene_buffer->raster = wlr_raster_create(buffer, NULL); + wlr_buffer_lock(buffer); + scene_buffer->own_buffer = true; + } + scene_node_update(&scene_buffer->node, NULL); return scene_buffer; @@ -798,38 +801,47 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf options = &default_options; } - // specifying a region for a NULL buffer doesn't make sense. We need to know - // about the buffer to scale the buffer local coordinates down to scene + struct wlr_raster *raster = NULL; + if (buffer) { + raster = wlr_raster_create(buffer, &(struct wlr_raster_create_options) { + .wait_timeline = options->wait_timeline, + .wait_point = options->wait_point, + }); + } + + wlr_scene_buffer_set_raster_with_damage(scene_buffer, raster, options->damage); + + if (raster) { + wlr_buffer_lock(buffer); + scene_buffer->own_buffer = true; + } + + wlr_raster_unlock(raster); +} + +void wlr_scene_buffer_set_raster_with_damage(struct wlr_scene_buffer *scene_buffer, + struct wlr_raster *raster, const pixman_region32_t *damage) { + // specifying a region for a NULL raster doesn't make sense. We need to know + // about the raster to scale the raster local coordinates down to scene // coordinates. - assert(buffer || !options->damage); + assert(raster || !damage); - bool mapped = buffer != NULL; - bool prev_mapped = scene_buffer->raster != NULL; - - if (!mapped && !prev_mapped) { - // unmapping already unmapped buffer - noop + if (raster == scene_buffer->raster) { return; } // if this node used to not be mapped or its previous displayed // buffer region will be different from what the new buffer would // produce we need to update the node. - bool update = mapped != prev_mapped; - if (buffer != NULL && scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0) { - update = update || (int)scene_buffer->raster->width != buffer->width || - (int)scene_buffer->raster->height != buffer->height; + bool update = !raster != !scene_buffer->raster; + if (raster != NULL && scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0) { + update = update || scene_buffer->raster->width != raster->width || + scene_buffer->raster->height != raster->height; } wlr_raster_unlock(scene_buffer->raster); - scene_buffer->raster = NULL; - - scene_buffer_set_buffer(scene_buffer, buffer); - if (buffer) { - scene_buffer->raster = wlr_raster_create(buffer, &(struct wlr_raster_create_options) { - .wait_timeline = options->wait_timeline, - .wait_point = options->wait_point, - }); - } + scene_buffer_set_buffer(scene_buffer, raster ? raster->buffer : NULL); + scene_buffer->raster = raster ? wlr_raster_lock(raster) : NULL; if (update) { scene_node_update(&scene_buffer->node, NULL); @@ -844,8 +856,7 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf } pixman_region32_t fallback_damage; - pixman_region32_init_rect(&fallback_damage, 0, 0, buffer->width, buffer->height); - const pixman_region32_t *damage = options->damage; + pixman_region32_init_rect(&fallback_damage, 0, 0, raster->width, raster->height); if (!damage) { damage = &fallback_damage; } @@ -854,26 +865,26 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf if (wlr_fbox_empty(&box)) { box.x = 0; box.y = 0; - box.width = buffer->width; - box.height = buffer->height; + box.width = raster->width; + box.height = raster->height; } wlr_fbox_transform(&box, &box, scene_buffer->transform, - buffer->width, buffer->height); + raster->width, raster->height); float scale_x, scale_y; if (scene_buffer->dst_width || scene_buffer->dst_height) { scale_x = scene_buffer->dst_width / box.width; scale_y = scene_buffer->dst_height / box.height; } else { - scale_x = buffer->width / box.width; - scale_y = buffer->height / box.height; + scale_x = raster->width / box.width; + scale_y = raster->height / box.height; } pixman_region32_t trans_damage; pixman_region32_init(&trans_damage); wlr_region_transform(&trans_damage, damage, - scene_buffer->transform, buffer->width, buffer->height); + scene_buffer->transform, raster->width, raster->height); pixman_region32_intersect_rect(&trans_damage, &trans_damage, box.x, box.y, box.width, box.height); pixman_region32_translate(&trans_damage, -box.x, -box.y); From 19b1b442301752efc07da491dcb74b6aaed2708e Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 14 Sep 2024 18:41:47 -0400 Subject: [PATCH 09/10] wlr_scene: Remove usage of wlr_client_buffer --- types/scene/surface.c | 54 +++++++------------------------------------ 1 file changed, 8 insertions(+), 46 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index 2aff5af37..297c9f632 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "types/wlr_scene.h" @@ -76,28 +77,6 @@ static void scene_surface_handle_surface_destroy( wlr_scene_node_destroy(&surface->buffer->node); } -// This is used for wlr_scene where it unconditionally locks buffers preventing -// reuse of the existing texture for shm clients. With the usage pattern of -// wlr_scene surface handling, we can mark its locked buffer as safe -// for mutation. -static void client_buffer_mark_next_can_damage(struct wlr_client_buffer *buffer) { - buffer->n_ignore_locks++; -} - -static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buffer) { - if (!scene_buffer->buffer) { - return; - } - - struct wlr_client_buffer *buffer = wlr_client_buffer_get(scene_buffer->buffer); - if (!buffer) { - return; - } - - assert(buffer->n_ignore_locks > 0); - buffer->n_ignore_locks--; -} - static int min(int a, int b) { return a < b ? a : b; } @@ -160,38 +139,23 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { wlr_scene_buffer_set_transform(scene_buffer, state->transform); wlr_scene_buffer_set_opacity(scene_buffer, opacity); - scene_buffer_unmark_client_buffer(scene_buffer); - - if (surface->buffer) { - client_buffer_mark_next_can_damage(surface->buffer); + struct wlr_raster *raster = wlr_raster_from_surface(surface); + if (raster) { + wlr_scene_buffer_set_raster_with_damage(scene_buffer, + raster, &surface->buffer_damage); struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = wlr_linux_drm_syncobj_v1_get_surface_state(surface); - - struct wlr_drm_syncobj_timeline *wait_timeline = NULL; - uint64_t wait_point = 0; - if (syncobj_surface_state != NULL) { - wait_timeline = syncobj_surface_state->acquire_timeline; - wait_point = syncobj_surface_state->acquire_point; - } - - struct wlr_scene_buffer_set_buffer_options options = { - .damage = &surface->buffer_damage, - .wait_timeline = wait_timeline, - .wait_point = wait_point, - }; - wlr_scene_buffer_set_buffer_with_options(scene_buffer, - &surface->buffer->base, &options); - - if (syncobj_surface_state != NULL && + if (syncobj_surface_state != NULL && raster->buffer && (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, - &surface->buffer->base); + raster->buffer); } } else { wlr_scene_buffer_set_buffer(scene_buffer, NULL); } + wlr_raster_unlock(raster); pixman_region32_fini(&opaque); } @@ -231,8 +195,6 @@ static bool scene_buffer_point_accepts_input(struct wlr_scene_buffer *scene_buff static void surface_addon_destroy(struct wlr_addon *addon) { struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon); - scene_buffer_unmark_client_buffer(surface->buffer); - wlr_addon_finish(&surface->addon); wl_list_remove(&surface->outputs_update.link); From dc67a853fdb3457c865e56f05f6a958611ff7212 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 14 Sep 2024 17:32:39 -0400 Subject: [PATCH 10/10] tinywl: Use wlr_raster --- tinywl/tinywl.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 6cf08b119..dbae39bc0 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ struct tinywl_server { struct wl_listener cursor_frame; struct wlr_seat *seat; + struct wl_listener new_surface; struct wl_listener new_input; struct wl_listener request_cursor; struct wl_listener request_set_selection; @@ -109,6 +111,12 @@ struct tinywl_keyboard { struct wl_listener destroy; }; +struct tinywl_surface { + struct wlr_surface *surface; + struct wl_listener commit; + struct wl_listener destroy; +}; + static void focus_toplevel(struct tinywl_toplevel *toplevel, struct wlr_surface *surface) { /* Note: this function only deals with keyboard focus. */ if (toplevel == NULL) { @@ -870,6 +878,48 @@ static void server_new_xdg_popup(struct wl_listener *listener, void *data) { wl_signal_add(&xdg_popup->events.destroy, &popup->destroy); } +static void surface_handle_commit(struct wl_listener *listener, void *data) { + struct tinywl_surface *surface = wl_container_of(listener, surface, commit); + + /* + * wlr_raster_from_surface will not automatically latch onto a surface and + * update itself as the surface commits new buffers. We have to handle that + * ourselves. Every time a surface is committed, we have to make sure to + * read from the surface buffer before it is unlocked at the end of the + * commit. Since wlr_raster_from_surface will de-duplicate rasters created + * from the same surface, wlr_scene will consume rasters that are created + * here. + */ + struct wlr_raster *raster = wlr_raster_from_surface(surface->surface); + + // unlock the raster immediately as we're only prepping the surface + wlr_raster_unlock(raster); +} + +static void surface_handle_destroy(struct wl_listener *listener, void *data) { + struct tinywl_surface *surface = wl_container_of(listener, surface, destroy); + wl_list_remove(&surface->commit.link); + wl_list_remove(&surface->destroy.link); + free(surface); +} + +static void handle_new_surface(struct wl_listener *listener, void *data) { + struct wlr_surface *wlr_surface = data; + + struct tinywl_surface *surface = calloc(1, sizeof(*surface)); + if (!surface) { + return; + } + + surface->surface = wlr_surface; + + surface->commit.notify = surface_handle_commit; + wl_signal_add(&wlr_surface->events.commit, &surface->commit); + + surface->destroy.notify = surface_handle_destroy; + wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); +} + int main(int argc, char *argv[]) { wlr_log_init(WLR_DEBUG, NULL); char *startup_cmd = NULL; @@ -934,7 +984,18 @@ int main(int argc, char *argv[]) { * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the handling of the request_set_selection event below.*/ - wlr_compositor_create(server.wl_display, 5, server.renderer); + struct wlr_compositor *compositor = wlr_compositor_create(server.wl_display, 5, NULL); + + /* + * Surfaces act as a container for state that come from a wayland surface + * of a client. Surfaces provide buffers that act as the pixel data that the + * client wants to show. However, buffers aren't immetiately useful for us. + * We need to upload them to the GPU and for this, we'll use wlr_raster to + * help us do that. + */ + server.new_surface.notify = handle_new_surface; + wl_signal_add(&compositor->events.new_surface, &server.new_surface); + wlr_subcompositor_create(server.wl_display); wlr_data_device_manager_create(server.wl_display);