diff options
Diffstat (limited to 'src/common/backend.c')
| -rw-r--r-- | src/common/backend.c | 72 |
1 files changed, 50 insertions, 22 deletions
diff --git a/src/common/backend.c b/src/common/backend.c index 9910e08..1a81a7b 100644 --- a/src/common/backend.c +++ b/src/common/backend.c @@ -22,14 +22,17 @@ static const struct yasl_private_backend_ops *backend_ops(enum yasl_private_back } } +static void resolve_frame_requests(struct yasl_private_display *d); + /* The display drives the loop it runs on; see struct yasl_private_loop_driver. */ static bool driver_prepare(void *ud) { struct yasl_private_display *d = ud; bool ready = d->ops->display_prepare(d); + resolve_frame_requests(d); /* Readiness the backends cannot see: events already queued, an error to * report, or an owed REFRESH. */ - return ready || yasl_private_events_pending(d) || yasl_private_events_error(d) || + return ready || yasl_private_events_pending(d) || yasl_private_events_failed(d) || (d->pending && d->pending(d)); } @@ -584,43 +587,70 @@ void yasl_private_surface_request_frame(struct yasl_surface *s) yasl_private_loop_wake(s->display->loop); } +static uint64_t refresh_interval_ns(struct yasl_surface *s) +{ + double refresh = yasl_private_surface_timing(s).refresh_ns; + return refresh >= 1000000.0 && refresh <= 1000000000.0 ? (uint64_t)refresh : 16666667; +} + void yasl_private_surface_notify_frame_shown(struct yasl_surface *s) { if (!s || !s->frame_requested) return; s->frame_requested = false; + s->frame_answered_ns = yasl_private_loop_now_ns(); s->refresh_owed = true; } +static void resolve_frame_request(struct yasl_surface *s); + static uint64_t frame_interval_done(void *ud, yasl_private_timer_id id, uint64_t interval_ns) { (void)id; (void)interval_ns; - yasl_private_surface_notify_frame_shown(ud); + struct yasl_surface *s = ud; + s->frame_timer = 0; + /* A present since the timer was set moves the next frame later. */ + resolve_frame_request(s); return 0; } -bool yasl_private_surface_request_shown_frame(struct yasl_surface *s) +/* The frame a request waits for is the next one the application presents, + * which is often after the request. So the loop decides at the start of each + * step, after the callbacks that draw have run. */ +static void resolve_frame_request(struct yasl_surface *s) { - s->frame_requested = true; + if (!s->frame_requested || yasl_private_loop_timer_armed(s->display->loop, s->frame_timer)) + return; + /* The system reports when the frame in flight is shown. */ const struct yasl_private_backend_ops *ops = s->display->ops; - if (ops->surface_frame_in_flight) { - if (!ops->surface_frame_in_flight(s)) - yasl_private_surface_notify_frame_shown(s); - return true; - } - /* No frame signal: one frame per refresh interval since the last present. */ - if (yasl_private_loop_timer_armed(s->display->loop, s->frame_timer)) - return true; - double refresh = yasl_private_surface_timing(s).refresh_ns; - uint64_t interval = refresh >= 1000000.0 && refresh <= 1000000000.0 ? (uint64_t)refresh : 16666667; - uint64_t presented = atomic_load(&s->presented_ns); - uint64_t since = yasl_private_loop_now_ns() - presented; - if (!presented || since >= interval) { + if (ops->surface_frame_in_flight && ops->surface_frame_in_flight(s)) + return; + /* Otherwise one frame per refresh interval, counted from the last + * present or answer, so a request with no present cannot spin. */ + uint64_t last = atomic_load(&s->presented_ns); + if (s->frame_answered_ns > last) + last = s->frame_answered_ns; + uint64_t interval = refresh_interval_ns(s); + uint64_t since = yasl_private_loop_now_ns() - last; + if (!last || since >= interval) { yasl_private_surface_notify_frame_shown(s); - return true; + return; } s->frame_timer = yasl_private_surface_add_timer(s, interval - since, frame_interval_done, s); - return s->frame_timer != 0; + /* Without a timer, an early frame is better than none. */ + if (!s->frame_timer) + yasl_private_surface_notify_frame_shown(s); +} + +void yasl_private_surface_request_shown_frame(struct yasl_surface *s) +{ + s->frame_requested = true; +} + +static void resolve_frame_requests(struct yasl_private_display *d) +{ + for (struct yasl_surface *s = d->surfaces; s; s = s->event_next) + resolve_frame_request(s); } static uint64_t pacing_tick(void *ud, yasl_private_timer_id id, uint64_t interval) @@ -628,9 +658,7 @@ static uint64_t pacing_tick(void *ud, yasl_private_timer_id id, uint64_t interva (void)id; (void)interval; struct yasl_surface *s = ud; yasl_private_surface_request_frame(s); - double refresh = yasl_private_surface_timing(s).refresh_ns; - return refresh >= 1000000.0 && refresh <= 1000000000.0 - ? (uint64_t)refresh : 16666667; + return refresh_interval_ns(s); } bool yasl_private_surface_set_pacing(struct yasl_surface *s, yasl_private_pacing_mode mode) |
