summaryrefslogtreecommitdiff
path: root/src/common/backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/backend.c')
-rw-r--r--src/common/backend.c72
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)