From e5e618fb50cae00294882ac1b7da320c34ad3416 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 28 Apr 2024 13:14:20 +0100 Subject: [PATCH] renderer: fix handling of root pixmap changes I thought to myself, "I will add this later", then proceeded to forget about this... The `root_damaged` flag in `struct session` I added in anticipation for this is actually not enough. Imagine this: frame 0 -> frame 1 -> frame 2 (root changed) -> frame 3 frame 2 will be rendered with the `root_damaged` flag set, all good. But `frame 3` wouldn't, and if it has buffer age 2, i.e. it's rendered on top of the base of `frame 1`, then the result will be wrong because frame 1 and 3 has different root background. So instead I added a `root_image_generation` that is incremented every time the root image changes. Fixes #1247 Signed-off-by: Yuxuan Shui --- src/common.h | 5 +++-- src/picom.c | 4 ++-- src/renderer/damage.c | 5 ++--- src/renderer/layout.c | 4 +++- src/renderer/layout.h | 4 +++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/common.h b/src/common.h index 52dc092d2a..aede1873be 100644 --- a/src/common.h +++ b/src/common.h @@ -199,6 +199,9 @@ typedef struct session { paint_t root_tile_paint; /// The backend data the root pixmap bound to image_handle root_image; + /// The root pixmap generation, incremented everytime + /// the root pixmap changes + uint64_t root_image_generation; /// A region of the size of the screen. region_t screen_reg; /// Picture of root window. Destination of painting in no-DBE painting @@ -271,8 +274,6 @@ typedef struct session { /// Render command builder struct command_builder *command_builder; struct renderer *renderer; - /// Whether the root image has been changed since last render - bool root_damaged; /// Whether all windows are currently redirected. bool redirected; /// Pre-generated alpha pictures. diff --git a/src/picom.c b/src/picom.c index ff1a84561a..15812819c0 100644 --- a/src/picom.c +++ b/src/picom.c @@ -1189,12 +1189,12 @@ void root_damaged(session_t *ps) { ps->root_image = ps->backend_data->ops->v2.bind_pixmap( ps->backend_data, pixmap, x_get_visual_info(&ps->c, visual)); + ps->root_image_generation += 1; if (!ps->root_image) { err: log_error("Failed to bind root back pixmap"); } } - ps->root_damaged = true; } // Mark screen damaged @@ -1846,7 +1846,7 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) { auto render_start_us = (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000; layout_manager_append_layout( - ps->layout_manager, ps->wm, + ps->layout_manager, ps->wm, ps->root_image_generation, (struct geometry){.width = ps->root_width, .height = ps->root_height}); bool succeeded = renderer_render( diff --git a/src/renderer/damage.c b/src/renderer/damage.c index fcf06ada84..ea010e0202 100644 --- a/src/renderer/damage.c +++ b/src/renderer/damage.c @@ -147,9 +147,8 @@ void layout_manager_damage(struct layout_manager *lm, unsigned buffer_age, pixman_region32_init(&scratch_region); pixman_region32_clear(damage); if (past_layout->size.width != curr_layout->size.width || - past_layout->size.height != curr_layout->size.height) { - // We might be able to do better for blur size change, but currently we - // don't even support changing that. + past_layout->size.height != curr_layout->size.height || + past_layout->root_image_generation != curr_layout->root_image_generation) { pixman_region32_union_rect(damage, damage, 0, 0, (unsigned)curr_layout->size.width, (unsigned)curr_layout->size.height); diff --git a/src/renderer/layout.c b/src/renderer/layout.c index 6d1d75a403..4be1481d4b 100644 --- a/src/renderer/layout.c +++ b/src/renderer/layout.c @@ -143,11 +143,13 @@ void layout_manager_free(struct layout_manager *lm) { // above. void layout_manager_append_layout(struct layout_manager *lm, struct wm *wm, - struct geometry size) { + uint64_t root_pixmap_generation, struct geometry size) { auto prev_layout = &lm->layouts[lm->current]; lm->current = (lm->current + 1) % lm->max_buffer_age; auto layout = &lm->layouts[lm->current]; command_builder_command_list_free(layout->commands); + layout->root_image_generation = root_pixmap_generation; + unsigned nlayers = wm_stack_num_managed_windows(wm); if (nlayers > layout->capacity) { struct layer *new_layers = diff --git a/src/renderer/layout.h b/src/renderer/layout.h index 968be8ddb8..cacd7b284b 100644 --- a/src/renderer/layout.h +++ b/src/renderer/layout.h @@ -71,6 +71,8 @@ struct layer { /// Layout of windows at a specific frame struct layout { struct geometry size; + /// The root image generation, see `struct session::root_image_generation` + uint64_t root_image_generation; /// Number of layers in `layers` unsigned len; /// Capacity of `layers` @@ -96,7 +98,7 @@ struct layout_manager; /// layouts, with its size chosen at creation time. Calling this will push at new layout /// at the end of the ring buffer, and remove the oldest layout if the buffer is full. void layout_manager_append_layout(struct layout_manager *lm, struct wm *wm, - struct geometry size); + uint64_t root_image_generation, struct geometry size); /// Get the layout `age` frames into the past. Age `0` is the most recently appended /// layout. struct layout *layout_manager_layout(struct layout_manager *lm, unsigned age);