compositor: Add an explicit latch point

Latch is the moment when the compositor considers updates for an upcoming
redraw. Nothing that takes place after an output latches for repaint can
change what will be repainted.

This needs a more explicit treatment now that upcoming transactional
protocols require things to happen immediately after the latch (ie:
when it's too late to change the upcoming render).

Add an explicit latch point, a signal to tap for testing, and some asserts
to make sure nothing can violate the inevitability of the current render
state.

Note that currently latch is tied to repaint such that we only claim to
have latched when a repaint will happen. In a future commit this will lead
to forcing the repaint loop to fire without damage when the fifo protocol
needs something to happen after a latch. This could be an area for
future improvement.

Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
This commit is contained in:
Derek Foreman 2025-09-26 13:51:45 -05:00
parent b700c7cbee
commit e4be014f93
5 changed files with 53 additions and 0 deletions

View file

@ -438,6 +438,7 @@ struct weston_output {
struct wl_event_source *idle_repaint_source;
struct wl_signal frame_signal;
struct wl_signal post_latch_signal;
struct wl_signal destroy_signal; /**< sent when disabled */
struct weston_coord_global move;
struct timespec frame_time; /* presentation timestamp */
@ -1493,6 +1494,13 @@ struct weston_compositor {
uint32_t placeholder_color;
bool no_xwm_decorations;
/**
* When set the compositor has latched content updates for the
* upcoming repaint, and no more updates may be applied until after
* that repaint occurs.
*/
bool latched;
};
struct weston_solid_buffer_values {

View file

@ -3615,6 +3615,21 @@ output_assign_planes(struct weston_output *output)
}
}
/* The "latch" point is the last possible instant before a repaint. After
* the latch, no more content updates can be applied by the compositor
* until after the scheduled repaint completes.
*/
static void
weston_output_latch(struct weston_output *output)
{
struct weston_compositor *compositor = output->compositor;
assert(!compositor->latched);
compositor->latched = true;
wl_signal_emit(&output->post_latch_signal, output);
}
static int
weston_output_repaint(struct weston_output *output)
{
@ -3628,6 +3643,8 @@ weston_output_repaint(struct weston_output *output)
uint32_t frame_time_msec;
enum weston_hdcp_protection highest_requested = WESTON_HDCP_DISABLE;
weston_output_latch(output);
TL_POINT(ec, TLP_CORE_REPAINT_BEGIN, TLP_OUTPUT(output), TLP_END);
/* Rebuild the surface list and update surface transforms up front. */
@ -3696,6 +3713,7 @@ weston_output_repaint(struct weston_output *output)
output_accumulate_damage(output);
r = output->repaint(output);
ec->latched = false;
output->repaint_needed = false;
if (r == 0) {
@ -8115,6 +8133,7 @@ weston_output_enable(struct weston_output *output)
output->original_scale = output->current_scale;
wl_signal_init(&output->frame_signal);
wl_signal_init(&output->post_latch_signal);
wl_signal_init(&output->destroy_signal);
weston_output_transform_scale_init(output, output->transform,

View file

@ -272,6 +272,8 @@ weston_surface_apply_state(struct weston_surface *surface,
pixman_region32_t opaque;
enum weston_surface_status status = state->status;
assert(!surface->compositor->latched);
surface->flow_id = state->flow_id;
state->flow_id = 0;

View file

@ -99,6 +99,8 @@
<enum name="breakpoint">
<entry name="post_repaint" value="0"
summary="after output repaint (filter type: wl_output)"/>
<entry name="post_latch" value="1"
summary="after output latch (filter type: wl_output)"/>
</enum>
<request name="client_break">

View file

@ -83,6 +83,7 @@ struct weston_test_output {
struct weston_test *test;
struct weston_output *output;
struct wl_listener repaint_listener;
struct wl_listener post_latch_listener;
struct wl_list link;
};
@ -138,6 +139,20 @@ output_repaint_listener(struct wl_listener *listener, void *data)
}
}
static void
output_post_latch_listener(struct wl_listener *listener, void *data)
{
struct weston_test_output *to =
container_of(listener, struct weston_test_output,
post_latch_listener);
struct weston_head *head;
wl_list_for_each(head, &to->output->head_list, output_link) {
maybe_breakpoint(to->test, WESTON_TEST_BREAKPOINT_POST_LATCH,
head);
}
}
static void
output_created_listener(struct wl_listener *listener, void *data)
{
@ -149,8 +164,13 @@ output_created_listener(struct wl_listener *listener, void *data)
to->test = test;
to->output = output;
to->repaint_listener.notify = output_repaint_listener;
wl_signal_add(&output->frame_signal, &to->repaint_listener);
to->post_latch_listener.notify = output_post_latch_listener;
wl_signal_add(&output->post_latch_signal, &to->post_latch_listener);
wl_list_insert(&test->output_list, &to->link);
}
@ -168,6 +188,7 @@ output_destroyed_listener(struct wl_listener *listener, void *data)
continue;
wl_list_remove(&to->repaint_listener.link);
wl_list_remove(&to->post_latch_listener.link);
wl_list_remove(&to->link);
free(to);
}
@ -883,6 +904,7 @@ out_free:
wl_list_for_each_safe(to, tmp, &test->output_list, link) {
wl_list_remove(&to->repaint_listener.link);
wl_list_remove(&to->post_latch_listener.link);
wl_list_remove(&to->link);
free(to);
}