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]: 467e6b9883/shared/timespec-util.h
This commit is contained in:
Simon Ser 2020-03-06 18:09:35 +01:00
parent c0811fcf87
commit a5fbffb40d
No known key found for this signature in database
GPG key ID: 0FDE7BE0E88F5E48
4 changed files with 56 additions and 21 deletions

View file

@ -7,6 +7,7 @@
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <wayland-server-protocol.h> #include <wayland-server-protocol.h>
#include <time.h>
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
@ -102,3 +103,40 @@ bool sway_set_cloexec(int fd, bool cloexec) {
} }
return true; 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;
}

View file

@ -54,7 +54,7 @@ struct sway_output {
} events; } events;
struct timespec last_presentation; struct timespec last_presentation;
uint32_t refresh_nsec; uint32_t next_refresh_nsec;
int max_render_time; // In milliseconds int max_render_time; // In milliseconds
struct wl_event_source *repaint_timer; struct wl_event_source *repaint_timer;
bool surface_needs_frame; bool surface_needs_frame;

View file

@ -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); 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 #endif

View file

@ -29,6 +29,7 @@
#include "sway/tree/root.h" #include "sway/tree/root.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.h" #include "sway/tree/workspace.h"
#include "util.h"
struct sway_output *output_by_name_or_id(const char *name_or_id) { struct sway_output *output_by_name_or_id(const char *name_or_id) {
for (int i = 0; i < root->outputs->length; ++i) { 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); = wlr_backend_get_presentation_clock(server.backend);
clock_gettime(presentation_clock, &now); clock_gettime(presentation_clock, &now);
const long NSEC_IN_SECONDS = 1000000000; struct timespec predicted_refresh;
struct timespec predicted_refresh = output->last_presentation; timespec_add_nsec(&predicted_refresh, &output->last_presentation,
predicted_refresh.tv_nsec += output->refresh_nsec % NSEC_IN_SECONDS; output->next_refresh_nsec);
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;
}
// If the predicted refresh time is before the current time then // If the predicted refresh time is before the current time then
// there's no point in delaying. // there's no point in delaying.
// int64_t nsec_until_refresh =
// We only check tv_sec because if the predicted refresh time is less timespec_sub_to_nsec(&predicted_refresh, &now);
// than a second before the current time, then msec_until_refresh will if (nsec_until_refresh >= 0) {
// 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);
// We want msec_until_refresh to be conservative, that is, floored. // We want msec_until_refresh to be conservative, that is, floored.
// If we have 7.9 msec until refresh, we better compute the delay // 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 // 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->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) { void handle_new_output(struct wl_listener *listener, void *data) {