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
This commit is contained in:
Simon Ser 2025-12-30 20:13:23 +01:00
parent 2699b68b34
commit b094f8aeb3
2 changed files with 4 additions and 8 deletions

View file

@ -126,10 +126,6 @@ struct wlr_scene_surface {
struct { struct {
struct wlr_box clip; 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 wlr_addon addon;
struct wl_listener outputs_update; struct wl_listener outputs_update;

View file

@ -24,6 +24,8 @@ static double get_surface_preferred_buffer_scale(struct wlr_surface *surface) {
return scale; 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) { static struct wlr_output *get_surface_frame_pacing_output(struct wlr_surface *surface) {
struct wlr_output *frame_pacing_output = NULL; struct wlr_output *frame_pacing_output = NULL;
struct wlr_surface_output *surface_output; struct wlr_surface_output *surface_output;
@ -94,8 +96,6 @@ static void handle_scene_buffer_outputs_update(
wl_container_of(listener, surface, outputs_update); wl_container_of(listener, surface, outputs_update);
struct wlr_scene *scene = scene_node_get_root(&surface->buffer->node); 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 // If the surface is no longer visible on any output, keep the last sent
// preferred configuration to avoid unnecessary redraws // preferred configuration to avoid unnecessary redraws
if (wl_list_empty(&surface->surface->current_outputs)) { 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); wl_container_of(listener, surface, output_sample);
const struct wlr_scene_output_sample_event *event = data; const struct wlr_scene_output_sample_event *event = data;
struct wlr_output *output = event->output->output; struct wlr_output *output = event->output->output;
if (surface->frame_pacing_output != output) { if (get_surface_frame_pacing_output(surface->surface) != output) {
return; return;
} }
@ -154,7 +154,7 @@ static void handle_scene_buffer_frame_done(
struct wlr_scene_surface *surface = struct wlr_scene_surface *surface =
wl_container_of(listener, surface, frame_done); wl_container_of(listener, surface, frame_done);
struct wlr_scene_frame_done_event *event = data; 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; return;
} }