Switch to global output repaint timer
In preparation for grouping output repaint together where possible, switch the per-output repaint timer, to a global timer which iterates across all outputs. This is implemented by storing the absolute time for the next repaint for each output locally, and maintaining a global timer which iterates all of them, scheduling the repaint for the first available time. Signed-off-by: Daniel Stone <daniels@collabora.com> Cc: Mario Kleiner <mario.kleiner.de@gmail.com> Cc: Pekka Paalanen <pekka.paalanen@collabora.co.uk> [Pekka: The comment about 1 ms delay.] Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
This commit is contained in:
committed by
Pekka Paalanen
parent
c4d7f66c12
commit
6847b858a3
+88
-30
@@ -2338,14 +2338,21 @@ weston_output_schedule_repaint_reset(struct weston_output *output)
|
||||
TL_POINT("core_repaint_exit_loop", TLP_OUTPUT(output), TLP_END);
|
||||
}
|
||||
|
||||
static int
|
||||
output_repaint_timer_handler(void *data)
|
||||
static void
|
||||
weston_output_maybe_repaint(struct weston_output *output,
|
||||
struct timespec *now)
|
||||
{
|
||||
struct weston_output *output = data;
|
||||
struct weston_compositor *compositor = output->compositor;
|
||||
int ret;
|
||||
int64_t msec_to_repaint;
|
||||
|
||||
assert(output->repaint_status == REPAINT_SCHEDULED);
|
||||
/* We're not ready yet; come back to make a decision later. */
|
||||
if (output->repaint_status != REPAINT_SCHEDULED)
|
||||
return;
|
||||
|
||||
msec_to_repaint = timespec_sub_to_msec(&output->next_repaint, now);
|
||||
if (msec_to_repaint > 1)
|
||||
return;
|
||||
|
||||
/* If we're sleeping, drop the repaint machinery entirely; we will
|
||||
* explicitly repaint all outputs when we come back. */
|
||||
@@ -2360,15 +2367,72 @@ output_repaint_timer_handler(void *data)
|
||||
|
||||
/* If repaint fails, we aren't going to get weston_output_finish_frame
|
||||
* to trigger a new repaint, so drop it from repaint and hope
|
||||
* something schedules a successful repaint later. */
|
||||
* something schedules a successful repaint later. As repainting may
|
||||
* take some time, re-read our clock as a courtesy to the next
|
||||
* output. */
|
||||
ret = weston_output_repaint(output);
|
||||
weston_compositor_read_presentation_clock(compositor, now);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
return;
|
||||
|
||||
err:
|
||||
weston_output_schedule_repaint_reset(output);
|
||||
}
|
||||
|
||||
static void
|
||||
output_repaint_timer_arm(struct weston_compositor *compositor)
|
||||
{
|
||||
struct weston_output *output;
|
||||
bool any_should_repaint = false;
|
||||
struct timespec now;
|
||||
int64_t msec_to_next;
|
||||
|
||||
weston_compositor_read_presentation_clock(compositor, &now);
|
||||
|
||||
wl_list_for_each(output, &compositor->output_list, link) {
|
||||
int64_t msec_to_this;
|
||||
|
||||
if (output->repaint_status != REPAINT_SCHEDULED)
|
||||
continue;
|
||||
|
||||
msec_to_this = timespec_sub_to_msec(&output->next_repaint,
|
||||
&now);
|
||||
if (!any_should_repaint || msec_to_this < msec_to_next)
|
||||
msec_to_next = msec_to_this;
|
||||
|
||||
any_should_repaint = true;
|
||||
}
|
||||
|
||||
if (!any_should_repaint)
|
||||
return;
|
||||
|
||||
/* Even if we should repaint immediately, add the minimum 1 ms delay.
|
||||
* This is a workaround to allow coalescing multiple output repaints
|
||||
* particularly from weston_output_finish_frame()
|
||||
* into the same call, which would not happen if we called
|
||||
* output_repaint_timer_handler() directly.
|
||||
*/
|
||||
if (msec_to_next < 1)
|
||||
msec_to_next = 1;
|
||||
|
||||
wl_event_source_timer_update(compositor->repaint_timer, msec_to_next);
|
||||
}
|
||||
|
||||
static int
|
||||
output_repaint_timer_handler(void *data)
|
||||
{
|
||||
struct weston_compositor *compositor = data;
|
||||
struct weston_output *output;
|
||||
struct timespec now;
|
||||
|
||||
weston_compositor_read_presentation_clock(compositor, &now);
|
||||
wl_list_for_each(output, &compositor->output_list, link)
|
||||
weston_output_maybe_repaint(output, &now);
|
||||
|
||||
output_repaint_timer_arm(compositor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2380,9 +2444,7 @@ weston_output_finish_frame(struct weston_output *output,
|
||||
struct weston_compositor *compositor = output->compositor;
|
||||
int32_t refresh_nsec;
|
||||
struct timespec now;
|
||||
struct timespec next;
|
||||
struct timespec remain;
|
||||
int msec_rel = 0;
|
||||
int64_t msec_rel;
|
||||
|
||||
TL_POINT("core_repaint_finished", TLP_OUTPUT(output),
|
||||
TLP_VBLANK(stamp), TLP_END);
|
||||
@@ -2390,11 +2452,15 @@ weston_output_finish_frame(struct weston_output *output,
|
||||
assert(output->repaint_status == REPAINT_AWAITING_COMPLETION);
|
||||
assert(stamp || (presented_flags & WP_PRESENTATION_FEEDBACK_INVALID));
|
||||
|
||||
weston_compositor_read_presentation_clock(compositor, &now);
|
||||
|
||||
/* If we haven't been supplied any timestamp at all, we don't have a
|
||||
* timebase to work against, so any delay just wastes time. Push a
|
||||
* repaint as soon as possible so we can get on with it. */
|
||||
if (!stamp)
|
||||
if (!stamp) {
|
||||
output->next_repaint = now;
|
||||
goto out;
|
||||
}
|
||||
|
||||
refresh_nsec = millihz_to_nsec(output->current_mode->refresh);
|
||||
weston_presentation_feedback_present_list(&output->feedback_list,
|
||||
@@ -2403,22 +2469,21 @@ weston_output_finish_frame(struct weston_output *output,
|
||||
presented_flags);
|
||||
|
||||
output->frame_time = timespec_to_msec(stamp);
|
||||
weston_compositor_read_presentation_clock(compositor, &now);
|
||||
|
||||
timespec_add_nsec(&next, stamp, refresh_nsec);
|
||||
timespec_add_msec(&next, &next, -compositor->repaint_msec);
|
||||
timespec_sub(&remain, &next, &now);
|
||||
msec_rel = timespec_to_msec(&remain);
|
||||
timespec_add_nsec(&output->next_repaint, stamp, refresh_nsec);
|
||||
timespec_add_msec(&output->next_repaint, &output->next_repaint,
|
||||
-compositor->repaint_msec);
|
||||
msec_rel = timespec_sub_to_msec(&output->next_repaint, &now);
|
||||
|
||||
if (msec_rel < -1000 || msec_rel > 1000) {
|
||||
static bool warned;
|
||||
|
||||
if (!warned)
|
||||
weston_log("Warning: computed repaint delay is "
|
||||
"insane: %d msec\n", msec_rel);
|
||||
"insane: %lld msec\n", (long long) msec_rel);
|
||||
warned = true;
|
||||
|
||||
msec_rel = 0;
|
||||
output->next_repaint = now;
|
||||
}
|
||||
|
||||
/* Called from restart_repaint_loop and restart happens already after
|
||||
@@ -2426,15 +2491,12 @@ weston_output_finish_frame(struct weston_output *output,
|
||||
* the deadline of the next frame, to give clients a more predictable
|
||||
* timing of the repaint cycle to lock on. */
|
||||
if (presented_flags == WP_PRESENTATION_FEEDBACK_INVALID && msec_rel < 0)
|
||||
msec_rel += refresh_nsec / 1000000;
|
||||
timespec_add_nsec(&output->next_repaint, &output->next_repaint,
|
||||
refresh_nsec);
|
||||
|
||||
out:
|
||||
output->repaint_status = REPAINT_SCHEDULED;
|
||||
|
||||
if (msec_rel < 1)
|
||||
output_repaint_timer_handler(output);
|
||||
else
|
||||
wl_event_source_timer_update(output->repaint_timer, msec_rel);
|
||||
output_repaint_timer_arm(compositor);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -4425,8 +4487,6 @@ weston_output_transform_coordinate(struct weston_output *output,
|
||||
static void
|
||||
weston_output_enable_undo(struct weston_output *output)
|
||||
{
|
||||
wl_event_source_remove(output->repaint_timer);
|
||||
|
||||
wl_global_destroy(output->global);
|
||||
|
||||
pixman_region32_fini(&output->region);
|
||||
@@ -4608,7 +4668,6 @@ weston_output_enable(struct weston_output *output)
|
||||
{
|
||||
struct weston_compositor *c = output->compositor;
|
||||
struct weston_output *iterator;
|
||||
struct wl_event_loop *loop;
|
||||
int x = 0, y = 0;
|
||||
|
||||
assert(output->enable);
|
||||
@@ -4649,10 +4708,6 @@ weston_output_enable(struct weston_output *output)
|
||||
wl_list_init(&output->feedback_list);
|
||||
wl_list_init(&output->link);
|
||||
|
||||
loop = wl_display_get_event_loop(c->wl_display);
|
||||
output->repaint_timer = wl_event_loop_add_timer(loop,
|
||||
output_repaint_timer_handler, output);
|
||||
|
||||
/* Invert the output id pool and look for the lowest numbered
|
||||
* switch (the least significant bit). Take that bit's position
|
||||
* as our ID, and mark it used in the compositor's output_id_pool.
|
||||
@@ -5154,6 +5209,9 @@ weston_compositor_create(struct wl_display *display, void *user_data)
|
||||
|
||||
loop = wl_display_get_event_loop(ec->wl_display);
|
||||
ec->idle_source = wl_event_loop_add_timer(loop, idle_handler, ec);
|
||||
ec->repaint_timer =
|
||||
wl_event_loop_add_timer(loop, output_repaint_timer_handler,
|
||||
ec);
|
||||
|
||||
weston_layer_init(&ec->fade_layer, ec);
|
||||
weston_layer_init(&ec->cursor_layer, ec);
|
||||
|
||||
@@ -183,7 +183,10 @@ struct weston_output {
|
||||
REPAINT_AWAITING_COMPLETION, /**< last repaint not yet finished */
|
||||
} repaint_status;
|
||||
|
||||
struct wl_event_source *repaint_timer;
|
||||
/** If repaint_status is REPAINT_SCHEDULED, contains the time the
|
||||
* next repaint should be run */
|
||||
struct timespec next_repaint;
|
||||
|
||||
struct weston_output_zoom zoom;
|
||||
int dirty;
|
||||
struct wl_signal frame_signal;
|
||||
@@ -856,6 +859,7 @@ struct weston_compositor {
|
||||
struct wl_event_source *idle_source;
|
||||
uint32_t idle_inhibit;
|
||||
int idle_time; /* timeout, s */
|
||||
struct wl_event_source *repaint_timer;
|
||||
|
||||
const struct weston_pointer_grab_interface *default_pointer_grab;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user