From a5fbffb40ddb56fda1022a24b22a950b6d72107b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 6 Mar 2020 18:09:35 +0100 Subject: [PATCH] Add timespec helpers Borrow timespec helpers from Weston [1]. We'll re-use these helpers for adaptive sync in a future commit. Rename refresh_nsec to next_refresh_nsec, because this value only applies to the next output presentation. [1]: https://gitlab.freedesktop.org/wayland/weston/-/blob/467e6b98831f76c066c10dfb9d8a8688307feea6/shared/timespec-util.h --- common/util.c | 38 ++++++++++++++++++++++++++++++++++++++ include/sway/output.h | 2 +- include/util.h | 9 +++++++++ sway/desktop/output.c | 28 ++++++++-------------------- 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/common/util.c b/common/util.c index c7ef2ac49..60e146a1d 100644 --- a/common/util.c +++ b/common/util.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "log.h" #include "util.h" @@ -102,3 +103,40 @@ bool sway_set_cloexec(int fd, bool cloexec) { } return true; } + +static const int64_t NSEC_PER_SEC = 1000000000; + +void timespec_sub(struct timespec *r, const struct timespec *a, + const struct timespec *b) { + r->tv_sec = a->tv_sec - b->tv_sec; + r->tv_nsec = a->tv_nsec - b->tv_nsec; + if (r->tv_nsec < 0) { + r->tv_sec--; + r->tv_nsec += NSEC_PER_SEC; + } +} + +int64_t timespec_sub_to_nsec(const struct timespec *a, + const struct timespec *b) { + struct timespec r; + timespec_sub(&r, a, b); + return timespec_to_nsec(&r); +} + +void timespec_add_nsec(struct timespec *r, const struct timespec *a, + int64_t nsec) { + r->tv_sec = a->tv_sec + (nsec / NSEC_PER_SEC); + r->tv_nsec = a->tv_nsec + (nsec % NSEC_PER_SEC); + + if (r->tv_nsec >= NSEC_PER_SEC) { + r->tv_sec++; + r->tv_nsec -= NSEC_PER_SEC; + } else if (r->tv_nsec < 0) { + r->tv_sec--; + r->tv_nsec += NSEC_PER_SEC; + } +} + +int64_t timespec_to_nsec(const struct timespec *t) { + return (int64_t)t->tv_sec * NSEC_PER_SEC + t->tv_nsec; +} diff --git a/include/sway/output.h b/include/sway/output.h index ccc1097f3..10715c64d 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -54,7 +54,7 @@ struct sway_output { } events; struct timespec last_presentation; - uint32_t refresh_nsec; + uint32_t next_refresh_nsec; int max_render_time; // In milliseconds struct wl_event_source *repaint_timer; bool surface_needs_frame; diff --git a/include/util.h b/include/util.h index 867eb0a43..ebd27bf75 100644 --- a/include/util.h +++ b/include/util.h @@ -37,4 +37,13 @@ const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel); bool sway_set_cloexec(int fd, bool cloexec); +struct timespec; + +void timespec_sub(struct timespec *r, const struct timespec *a, + const struct timespec *b); +int64_t timespec_sub_to_nsec(const struct timespec *a, const struct timespec *b); +void timespec_add_nsec(struct timespec *r, const struct timespec *a, + int64_t nsec); +int64_t timespec_to_nsec(const struct timespec *t); + #endif diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 16c6fc542..22ff19e68 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -29,6 +29,7 @@ #include "sway/tree/root.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" +#include "util.h" struct sway_output *output_by_name_or_id(const char *name_or_id) { for (int i = 0; i < root->outputs->length; ++i) { @@ -590,28 +591,15 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { = wlr_backend_get_presentation_clock(server.backend); clock_gettime(presentation_clock, &now); - const long NSEC_IN_SECONDS = 1000000000; - struct timespec predicted_refresh = output->last_presentation; - predicted_refresh.tv_nsec += output->refresh_nsec % NSEC_IN_SECONDS; - predicted_refresh.tv_sec += output->refresh_nsec / NSEC_IN_SECONDS; - if (predicted_refresh.tv_nsec >= NSEC_IN_SECONDS) { - predicted_refresh.tv_sec += 1; - predicted_refresh.tv_nsec -= NSEC_IN_SECONDS; - } + struct timespec predicted_refresh; + timespec_add_nsec(&predicted_refresh, &output->last_presentation, + output->next_refresh_nsec); // If the predicted refresh time is before the current time then // there's no point in delaying. - // - // We only check tv_sec because if the predicted refresh time is less - // than a second before the current time, then msec_until_refresh will - // end up slightly below zero, which will effectively disable the delay - // without potential disastrous negative overflows that could occur if - // tv_sec was not checked. - if (predicted_refresh.tv_sec >= now.tv_sec) { - long nsec_until_refresh - = (predicted_refresh.tv_sec - now.tv_sec) * NSEC_IN_SECONDS - + (predicted_refresh.tv_nsec - now.tv_nsec); - + int64_t nsec_until_refresh = + timespec_sub_to_nsec(&predicted_refresh, &now); + if (nsec_until_refresh >= 0) { // We want msec_until_refresh to be conservative, that is, floored. // If we have 7.9 msec until refresh, we better compute the delay // as if we had only 7 msec, so that we don't accidentally delay @@ -862,7 +850,7 @@ static void handle_present(struct wl_listener *listener, void *data) { } output->last_presentation = *output_event->when; - output->refresh_nsec = output_event->refresh; + output->next_refresh_nsec = output_event->refresh; } void handle_new_output(struct wl_listener *listener, void *data) {