summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/backend.c72
-rw-r--r--src/common/backend.h7
-rw-r--r--src/common/events.c11
-rw-r--r--src/common/surface.c17
-rw-r--r--src/common/types.h6
5 files changed, 80 insertions, 33 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)
diff --git a/src/common/backend.h b/src/common/backend.h
index 7633609..a9076e4 100644
--- a/src/common/backend.h
+++ b/src/common/backend.h
@@ -50,10 +50,13 @@ struct yasl_surface {
struct yasl_private_vector2i reported_size;
uint32_t reported_scale_120, reported_state;
bool refresh_owed;
- /* yasl_surface_request_frame waits for the frame in flight. */
+ /* yasl_surface_request_frame waits for the next frame shown. Each loop
+ * step resolves it after the callbacks that present; see backend.c. */
bool frame_requested;
/* Written by GL presents on a rendering thread. */
_Atomic uint64_t presented_ns;
+ /* When the last frame request was answered. */
+ uint64_t frame_answered_ns;
yasl_private_timer_id frame_timer;
void (*refresh)(struct yasl_surface *);
bool text_input_active;
@@ -93,6 +96,8 @@ struct yasl_surface {
bool yasl_private_events_open(struct yasl_private_display *d);
void yasl_private_events_close(struct yasl_private_display *d);
int yasl_private_events_error(struct yasl_private_display *d);
+/* Whether an event was lost, without reporting it. */
+bool yasl_private_events_failed(struct yasl_private_display *d);
bool yasl_private_events_pending(struct yasl_private_display *d);
struct yasl_surface *yasl_private_events_find(struct yasl_private_display *d, uint64_t id);
bool yasl_private_events_register(struct yasl_surface *s);
diff --git a/src/common/events.c b/src/common/events.c
index 83baf11..c310f06 100644
--- a/src/common/events.c
+++ b/src/common/events.c
@@ -71,6 +71,15 @@ int yasl_private_events_error(struct yasl_private_display *d)
return error;
}
+bool yasl_private_events_failed(struct yasl_private_display *d)
+{
+ if (!d->events) return false;
+ yasl_private_mutex_lock(&d->events->mutex);
+ bool failed = d->events->error != 0;
+ yasl_private_mutex_unlock(&d->events->mutex);
+ return failed;
+}
+
bool yasl_private_events_pending(struct yasl_private_display *d)
{
if (!d->events) return false;
@@ -207,6 +216,8 @@ failed:
yasl_private_mutex_lock(&d->events->mutex);
d->events->error = ENOMEM;
yasl_private_mutex_unlock(&d->events->mutex);
+ /* The loss is an event too; the owner reports it in its next step. */
+ yasl_private_loop_wake(d->loop);
errno = ENOMEM;
return false;
}
diff --git a/src/common/surface.c b/src/common/surface.c
index 5c29a10..6b805be 100644
--- a/src/common/surface.c
+++ b/src/common/surface.c
@@ -451,7 +451,8 @@ int yasl_surface_request_frame(yasl_surface *s)
{
if (!yasl_public_connected(s))
return -errno;
- return STATUS(yasl_private_surface_request_shown_frame(s));
+ yasl_private_surface_request_shown_frame(s);
+ return 0;
}
int yasl_surface_set_cursor(yasl_surface *s, enum yasl_cursor cursor)
{
@@ -901,14 +902,10 @@ static bool convert_event(const struct yasl_private_event *p, yasl_event *e)
return false;
}
}
+/* Queued events go first: input reaches the application before it draws, and
+ * a queued REFRESH also pays one owed to the same surface. */
static int take_event(yasl_event *out)
{
- for (yasl_surface *s = display->surfaces; s; s = s->event_next)
- if (s->refresh_owed && refresh_ready(s)) {
- s->refresh_owed = false;
- *out = (yasl_event){.type = YASL_EVENT_REFRESH, .surface = s, .time_ns = yasl_time_ns()};
- return 1;
- }
struct yasl_private_event p;
while (yasl_private_display_poll_event(display, &p)) {
/* Input method events from an older text field are stale. */
@@ -926,6 +923,12 @@ static int take_event(yasl_event *out)
if (errno)
return yasl_public_error(errno);
}
+ for (yasl_surface *s = display->surfaces; s; s = s->event_next)
+ if (s->refresh_owed && refresh_ready(s)) {
+ s->refresh_owed = false;
+ *out = (yasl_event){.type = YASL_EVENT_REFRESH, .surface = s, .time_ns = yasl_time_ns()};
+ return 1;
+ }
return 0;
}
/* Internal wakeups, such as a redraw request, keep waiting; only an event, the deadline or
diff --git a/src/common/types.h b/src/common/types.h
index 8d00d0b..67aaaa3 100644
--- a/src/common/types.h
+++ b/src/common/types.h
@@ -874,9 +874,9 @@ bool yasl_private_frame_present(struct yasl_private_frame *frame,
const struct yasl_private_rect2i *rects, size_t count);
/** Coalesce a redraw request; use on the display thread. NULL is ignored. */
void yasl_private_surface_request_frame(struct yasl_surface *s);
-/** Owe s a REFRESH once the system shows its frame in flight, or at once when
- * none is. False with errno when the fallback timer cannot start. */
-bool yasl_private_surface_request_shown_frame(struct yasl_surface *s);
+/** Owe s a REFRESH after the next frame shown. The next loop step decides
+ * which frame that is, so a present after the request counts. */
+void yasl_private_surface_request_shown_frame(struct yasl_surface *s);
/** Set drawing policy for either loop style. Returns false with errno on failure. */
bool yasl_private_surface_set_pacing(struct yasl_surface *s, yasl_private_pacing_mode mode);