diff options
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/backend.c | 72 | ||||
| -rw-r--r-- | src/common/backend.h | 7 | ||||
| -rw-r--r-- | src/common/events.c | 11 | ||||
| -rw-r--r-- | src/common/surface.c | 17 | ||||
| -rw-r--r-- | src/common/types.h | 6 |
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); |
