From b094f8aeb3ce207cc1e158787f4a5338cced2beb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 30 Dec 2025 20:13:23 +0100 Subject: [PATCH] scene/surface: don't cache frame pacing output Storing the frame pacing output in a per-scene and per-surface struct doesn't play well with multiple scenes. outputs_update is only triggered for outputs the scene knows about, but operates on all outputs the surface has entered regardless of the scene. Thus leaving an output on one scene will not refresh the frame pacing output on other scenes, and these other scenes will operate with a stale frame pacing output. The surface will not receive any more wl_surface.frame done events. This also avoids keeping a dangling pointer around when the frame pacing output is destroyed but the output isn't added in the scene. References: https://github.com/swaywm/sway/issues/8885 --- include/wlr/types/wlr_scene.h | 4 ---- types/scene/surface.c | 8 ++++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 2363c93ce..6fd243477 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -126,10 +126,6 @@ struct wlr_scene_surface { struct { struct wlr_box clip; - // Output used for frame pacing (surface frame callbacks, presentation - // time feedback, etc), may be NULL - struct wlr_output *frame_pacing_output; - struct wlr_addon addon; struct wl_listener outputs_update; diff --git a/types/scene/surface.c b/types/scene/surface.c index dadbf8303..bce8c74a6 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -24,6 +24,8 @@ static double get_surface_preferred_buffer_scale(struct wlr_surface *surface) { return scale; } +// Output used for frame pacing (surface frame callbacks, presentation +// time feedback, etc), may be NULL static struct wlr_output *get_surface_frame_pacing_output(struct wlr_surface *surface) { struct wlr_output *frame_pacing_output = NULL; struct wlr_surface_output *surface_output; @@ -94,8 +96,6 @@ static void handle_scene_buffer_outputs_update( wl_container_of(listener, surface, outputs_update); struct wlr_scene *scene = scene_node_get_root(&surface->buffer->node); - surface->frame_pacing_output = get_surface_frame_pacing_output(surface->surface); - // If the surface is no longer visible on any output, keep the last sent // preferred configuration to avoid unnecessary redraws if (wl_list_empty(&surface->surface->current_outputs)) { @@ -138,7 +138,7 @@ static void handle_scene_buffer_output_sample( wl_container_of(listener, surface, output_sample); const struct wlr_scene_output_sample_event *event = data; struct wlr_output *output = event->output->output; - if (surface->frame_pacing_output != output) { + if (get_surface_frame_pacing_output(surface->surface) != output) { return; } @@ -154,7 +154,7 @@ static void handle_scene_buffer_frame_done( struct wlr_scene_surface *surface = wl_container_of(listener, surface, frame_done); struct wlr_scene_frame_done_event *event = data; - if (surface->frame_pacing_output != event->output->output) { + if (get_surface_frame_pacing_output(surface->surface) != event->output->output) { return; }