From 7ecafde6fdd587f8c2e6bb33491a4904c4f83279 Mon Sep 17 00:00:00 2001 From: Rose Hudson Date: Wed, 9 Aug 2023 17:42:56 +0100 Subject: [PATCH] add headless idle frame scheduler Headless present events aren't suitable for the present idle scheduler. This new scheduler is a good place for headless' existing frame logic to go, so move it into there. --- include/wlr/types/wlr_frame_scheduler.h | 6 ++ types/wlr_frame_scheduler.c | 73 +++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/include/wlr/types/wlr_frame_scheduler.h b/include/wlr/types/wlr_frame_scheduler.h index 4305209ac..e1216a712 100644 --- a/include/wlr/types/wlr_frame_scheduler.h +++ b/include/wlr/types/wlr_frame_scheduler.h @@ -31,6 +31,12 @@ struct wlr_frame_scheduler *wlr_present_idle_scheduler_create(struct wlr_output * the render loop, it emits the frame signal when the compositor's event loop is idle. */ struct wlr_frame_scheduler *wlr_wl_idle_scheduler_create(struct wlr_output *output); +/* + * The headless-idle scheduler maintains a render loop based on the headless backend's refresh + * timer. To wake the render loop, it emits the frame signal when the compositor's event loop is + * idle. + */ +struct wlr_frame_scheduler *wlr_headless_idle_scheduler_create(struct wlr_output *output); /* * Creates an appropriate frame scheduler for the given output's backend capabilities. diff --git a/types/wlr_frame_scheduler.c b/types/wlr_frame_scheduler.c index e0fc9ef62..0c5947be5 100644 --- a/types/wlr_frame_scheduler.c +++ b/types/wlr_frame_scheduler.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -208,11 +209,83 @@ static struct wlr_frame_scheduler *wl_idle_scheduler_create(struct wlr_output *o return &scheduler->base.base; } +#define HEADLESS_DEFAULT_REFRESH (60 * 1000) // 60 Hz + +struct headless_idle_scheduler { + struct idle_frame_scheduler base; + struct wl_event_source *frame_timer; + struct wl_listener commit; + + int32_t frame_delay; +}; + +static void headless_idle_scheduler_destroy(struct wlr_frame_scheduler *wlr_scheduler) { + struct headless_idle_scheduler *scheduler = wl_container_of(wlr_scheduler, scheduler, base.base); + idle_frame_scheduler_finish(&scheduler->base); + wl_event_source_remove(scheduler->frame_timer); + free(scheduler); +} + +static void headless_idle_scheduler_handle_commit(struct wl_listener *listener, void *data) { + struct headless_idle_scheduler *scheduler = wl_container_of(listener, scheduler, commit); + struct wlr_output_event_commit *commit = data; + struct wlr_output *output = commit->output; + if (commit->committed & WLR_OUTPUT_STATE_MODE) { + int32_t refresh = output->refresh ? output->refresh : HEADLESS_DEFAULT_REFRESH; + scheduler->frame_delay = 1000 * 1000 / refresh; + } + + if (commit->committed & WLR_OUTPUT_STATE_BUFFER) { + assert(scheduler->frame_delay != 0); + wl_event_source_timer_update(scheduler->frame_timer, scheduler->frame_delay); + idle_frame_scheduler_set_frame_pending(&scheduler->base); + } +} + +static int headless_idle_scheduler_handle_timer(void *data) { + struct headless_idle_scheduler *scheduler = data; + scheduler->base.frame_pending = false; + idle_frame_scheduler_emit_frame(&scheduler->base); + return 0; +} + +struct wlr_frame_scheduler_impl headless_idle_scheduler_impl = { + .schedule_frame = idle_frame_scheduler_schedule_frame, + .destroy = headless_idle_scheduler_destroy, +}; + +struct wlr_frame_scheduler *wlr_headless_idle_scheduler_create(struct wlr_output *output) { + if (!wlr_output_is_headless(output)) { + return NULL; + } + + struct headless_idle_scheduler *scheduler = calloc(1, sizeof(struct headless_idle_scheduler)); + if (!scheduler) { + return NULL; + } + frame_scheduler_init(&scheduler->base.base, &headless_idle_scheduler_impl, output); + + scheduler->frame_delay = 1000 * 1000 / HEADLESS_DEFAULT_REFRESH; + + struct wl_event_loop *ev = wl_display_get_event_loop(output->display); + scheduler->frame_timer = wl_event_loop_add_timer(ev, headless_idle_scheduler_handle_timer, + scheduler); + + scheduler->commit.notify = headless_idle_scheduler_handle_commit; + wl_signal_add(&output->events.commit, &scheduler->commit); + + return &scheduler->base.base; +} + struct wlr_frame_scheduler *wlr_frame_scheduler_autocreate(struct wlr_output *output) { if (wlr_output_is_wl(output) && !wlr_wl_backend_has_presentation_time(output->backend)) { wlr_log(WLR_INFO, "wp_presentation not available, falling back to frame callbacks"); return wl_idle_scheduler_create(output); } + if (wlr_output_is_headless(output)) { + return wlr_headless_idle_scheduler_create(output); + } + return wlr_present_idle_scheduler_create(output); }