From 1d3e62c67f07a180fca02a6df1cf9648f2b50349 Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Sun, 12 Mar 2023 16:48:58 -0400 Subject: [PATCH] Stop pooling background surface buffers The wl_buffers for the background surface only need to be updated when the output dimensions change. Using the fixed pool of two buffers to cache these buffers does not help, since if a new buffer is needed, it will have a different size than whatever buffers were cached. Furthermore, because the pool has fixed size, it is possible to run out of buffers if configure events arrive faster than pool buffers are marked not busy, which can lead to protocol errors when the background surface is committed after acknowledging a new size, but without attaching a buffer that matches that size. --- include/pool-buffer.h | 4 +++- include/swaylock.h | 7 ++++--- main.c | 2 -- pool-buffer.c | 2 +- render.c | 49 ++++++++++++++++++++++++++----------------- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/include/pool-buffer.h b/include/pool-buffer.h index 0ebf787..38e8218 100644 --- a/include/pool-buffer.h +++ b/include/pool-buffer.h @@ -15,8 +15,10 @@ struct pool_buffer { bool busy; }; +struct pool_buffer *create_buffer(struct wl_shm *shm, struct pool_buffer *buf, + int32_t width, int32_t height, uint32_t format); struct pool_buffer *get_next_buffer(struct wl_shm *shm, - struct pool_buffer pool[static 2], uint32_t width, uint32_t height); + struct pool_buffer pool[static 2], uint32_t width, uint32_t height); void destroy_buffer(struct pool_buffer *buffer); #endif diff --git a/include/swaylock.h b/include/swaylock.h index 9de8ab6..bb71c8c 100644 --- a/include/swaylock.h +++ b/include/swaylock.h @@ -98,11 +98,10 @@ struct swaylock_surface { struct swaylock_state *state; struct wl_output *output; uint32_t output_global_name; - struct wl_surface *surface; - struct wl_surface *child; // surface made into subsurface + struct wl_surface *surface; // surface for background + struct wl_surface *child; // indicator surface made into subsurface struct wl_subsurface *subsurface; struct ext_session_lock_surface_v1 *ext_session_lock_surface_v1; - struct pool_buffer buffers[2]; struct pool_buffer indicator_buffers[2]; bool frame_pending, dirty; uint32_t width, height; @@ -110,6 +109,8 @@ struct swaylock_surface { enum wl_output_subpixel subpixel; char *output_name; struct wl_list link; + // Dimensions of last wl_buffer committed to background surface + int last_buffer_width, last_buffer_height; }; // There is exactly one swaylock_image for each -i argument diff --git a/main.c b/main.c index 26d30a5..ca89be7 100644 --- a/main.c +++ b/main.c @@ -100,8 +100,6 @@ static void destroy_surface(struct swaylock_surface *surface) { if (surface->surface != NULL) { wl_surface_destroy(surface->surface); } - destroy_buffer(&surface->buffers[0]); - destroy_buffer(&surface->buffers[1]); destroy_buffer(&surface->indicator_buffers[0]); destroy_buffer(&surface->indicator_buffers[1]); wl_output_destroy(surface->output); diff --git a/pool-buffer.c b/pool-buffer.c index 02884de..dfb4d0d 100644 --- a/pool-buffer.c +++ b/pool-buffer.c @@ -46,7 +46,7 @@ static const struct wl_buffer_listener buffer_listener = { .release = buffer_release }; -static struct pool_buffer *create_buffer(struct wl_shm *shm, +struct pool_buffer *create_buffer(struct wl_shm *shm, struct pool_buffer *buf, int32_t width, int32_t height, uint32_t format) { uint32_t stride = width * 4; diff --git a/render.c b/render.c index 3ce7a80..b7febc1 100644 --- a/render.c +++ b/render.c @@ -4,6 +4,7 @@ #include "cairo.h" #include "background-image.h" #include "swaylock.h" +#include "log.h" #define M_PI 3.14159265358979323846 const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; @@ -41,30 +42,40 @@ void render_frame_background(struct swaylock_surface *surface) { return; // not yet configured } - struct pool_buffer *buffer = get_next_buffer(state->shm, - surface->buffers, buffer_width, buffer_height); - if (buffer == NULL) { - return; - } + if (buffer_width != surface->last_buffer_width || + buffer_height != surface->last_buffer_height) { + struct pool_buffer buffer; + if (!create_buffer(state->shm, &buffer, buffer_width, buffer_height, + WL_SHM_FORMAT_ARGB8888)) { + swaylock_log(LOG_ERROR, + "Failed to create new buffer for frame background."); + return; + } - cairo_t *cairo = buffer->cairo; - cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); + cairo_t *cairo = buffer.cairo; + cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); - cairo_save(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - cairo_set_source_u32(cairo, state->args.colors.background); - cairo_paint(cairo); - if (surface->image && state->args.mode != BACKGROUND_MODE_SOLID_COLOR) { - cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); - render_background_image(cairo, surface->image, - state->args.mode, buffer_width, buffer_height); + cairo_save(cairo); + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); + cairo_set_source_u32(cairo, state->args.colors.background); + cairo_paint(cairo); + if (surface->image && state->args.mode != BACKGROUND_MODE_SOLID_COLOR) { + cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); + render_background_image(cairo, surface->image, + state->args.mode, buffer_width, buffer_height); + } + cairo_restore(cairo); + cairo_identity_matrix(cairo); + + wl_surface_attach(surface->surface, buffer.buffer, 0, 0); + wl_surface_damage_buffer(surface->surface, 0, 0, INT32_MAX, INT32_MAX); + destroy_buffer(&buffer); + + surface->last_buffer_width = buffer_width; + surface->last_buffer_height = buffer_height; } - cairo_restore(cairo); - cairo_identity_matrix(cairo); wl_surface_set_buffer_scale(surface->surface, surface->scale); - wl_surface_attach(surface->surface, buffer->buffer, 0, 0); - wl_surface_damage_buffer(surface->surface, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(surface->surface); }