Add interval frame scheduler

Headless present events aren't suitable for the present scheduler. This
new scheduler is a good place for headless' existing frame logic to go,
so move it into there.

With this decoupling, there's nothing tying the scheduler to the
headless backend, hence the generic name.
This commit is contained in:
Rose Hudson 2023-08-09 17:42:56 +01:00 committed by Kenny Levinsen
parent ad145dce86
commit ee100e2586
2 changed files with 77 additions and 0 deletions

View file

@ -1,4 +1,5 @@
#include <assert.h>
#include <backend/headless.h>
#include <stdlib.h>
#include <types/wlr_output.h>
#include <wayland-client-protocol.h>
@ -215,11 +216,82 @@ static struct wlr_frame_scheduler *wl_scheduler_create(struct wlr_output *output
return &scheduler->base.base;
}
#define DEFAULT_REFRESH (60 * 1000) // 60 Hz in mHz
struct interval_scheduler {
struct idle_frame_scheduler base;
struct wl_event_source *frame_timer;
struct wl_listener commit;
int32_t frame_delay;
};
static void interval_scheduler_destroy(struct wlr_frame_scheduler *wlr_scheduler) {
struct interval_scheduler *scheduler = wl_container_of(wlr_scheduler, scheduler, base.base);
idle_frame_scheduler_finish(&scheduler->base);
wl_event_source_remove(scheduler->frame_timer);
wl_list_remove(&scheduler->commit.link);
free(scheduler);
}
static void interval_scheduler_handle_commit(struct wl_listener *listener, void *data) {
struct interval_scheduler *scheduler = wl_container_of(listener, scheduler, commit);
struct wlr_output_event_commit *commit = data;
struct wlr_output *output = commit->output;
if (commit->state->committed & WLR_OUTPUT_STATE_MODE) {
int32_t refresh = output->refresh ? output->refresh : DEFAULT_REFRESH;
scheduler->frame_delay = 1000 * 1000 / refresh;
}
if (output->enabled) {
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 interval_scheduler_handle_timer(void *data) {
struct interval_scheduler *scheduler = data;
idle_frame_scheduler_emit_frame(&scheduler->base);
return 0;
}
static const struct wlr_frame_scheduler_impl interval_scheduler_impl = {
.schedule_frame = idle_frame_scheduler_schedule_frame,
.destroy = interval_scheduler_destroy,
};
struct wlr_frame_scheduler *wlr_interval_scheduler_create(struct wlr_output *output) {
if (!wlr_output_is_headless(output)) {
return NULL;
}
struct interval_scheduler *scheduler = calloc(1, sizeof(*scheduler));
if (!scheduler) {
return NULL;
}
wlr_frame_scheduler_init(&scheduler->base.base, &interval_scheduler_impl, output);
scheduler->frame_delay = 1000 * 1000 / DEFAULT_REFRESH;
scheduler->frame_timer = wl_event_loop_add_timer(output->event_loop,
interval_scheduler_handle_timer, scheduler);
scheduler->commit.notify = interval_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_scheduler_create(output);
}
if (wlr_output_is_headless(output)) {
return wlr_interval_scheduler_create(output);
}
return wlr_present_scheduler_create(output);
}