Merge branch 'frame-scheduler-rebase' into 'master'

Add new frame scheduler interface (Rebase of !4307)

See merge request wlroots/wlroots!5330
This commit is contained in:
Kenny Levinsen 2026-04-12 16:16:22 +00:00
commit af452e85b5
29 changed files with 528 additions and 172 deletions

View file

@ -2081,10 +2081,6 @@ static void handle_page_flip(int fd, unsigned seq,
.flags = present_flags,
};
wlr_output_send_present(&conn->output, &present_event);
if (drm->session->active) {
wlr_output_send_frame(&conn->output);
}
}
int handle_drm_event(int fd, uint32_t mask, void *data) {

View file

@ -22,15 +22,6 @@ static struct wlr_headless_output *headless_output_from_output(
return output;
}
static void output_update_refresh(struct wlr_headless_output *output,
int32_t refresh) {
if (refresh <= 0) {
refresh = HEADLESS_DEFAULT_REFRESH;
}
output->frame_delay = 1000000 / refresh;
}
static bool output_test(struct wlr_output *wlr_output,
const struct wlr_output_state *state) {
uint32_t unsupported = state->committed & ~SUPPORTED_OUTPUT_STATE;
@ -55,25 +46,16 @@ static bool output_test(struct wlr_output *wlr_output,
static bool output_commit(struct wlr_output *wlr_output,
const struct wlr_output_state *state) {
struct wlr_headless_output *output =
headless_output_from_output(wlr_output);
if (!output_test(wlr_output, state)) {
return false;
}
if (state->committed & WLR_OUTPUT_STATE_MODE) {
output_update_refresh(output, state->custom_mode.refresh);
}
if (output_pending_enabled(wlr_output, state)) {
struct wlr_output_event_present present_event = {
.commit_seq = wlr_output->commit_seq + 1,
.presented = true,
};
output_defer_present(wlr_output, present_event);
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
}
return true;
@ -94,7 +76,6 @@ static void output_destroy(struct wlr_output *wlr_output) {
wlr_output_finish(wlr_output);
wl_list_remove(&output->link);
wl_event_source_remove(output->frame_timer);
free(output);
}
@ -110,12 +91,6 @@ bool wlr_output_is_headless(const struct wlr_output *wlr_output) {
return wlr_output->impl == &output_impl;
}
static int signal_frame(void *data) {
struct wlr_headless_output *output = data;
wlr_output_send_frame(&output->wlr_output);
return 0;
}
struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
unsigned int width, unsigned int height) {
struct wlr_headless_backend *backend =
@ -136,8 +111,6 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
wlr_output_init(wlr_output, &backend->backend, &output_impl, backend->event_loop, &state);
wlr_output_state_finish(&state);
output_update_refresh(output, 0);
size_t output_num = ++last_output_num;
char name[64];
@ -148,8 +121,6 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
snprintf(description, sizeof(description), "Headless output %zu", output_num);
wlr_output_set_description(wlr_output, description);
output->frame_timer = wl_event_loop_add_timer(backend->event_loop, signal_frame, output);
wl_list_insert(&backend->outputs, &output->link);
if (backend->started) {

View file

@ -581,6 +581,11 @@ bool wlr_backend_is_wl(const struct wlr_backend *b) {
return b->impl == &backend_impl;
}
bool wlr_wl_backend_has_presentation_time(struct wlr_backend *wlr_backend) {
struct wlr_wl_backend *backend = get_wl_backend_from_backend(wlr_backend);
return backend->presentation != NULL;
}
static void handle_event_loop_destroy(struct wl_listener *listener, void *data) {
struct wlr_wl_backend *wl = wl_container_of(listener, wl, event_loop_destroy);
backend_destroy(&wl->backend);

View file

@ -72,8 +72,6 @@ static void surface_frame_callback(void *data, struct wl_callback *cb,
assert(output->frame_callback == cb);
wl_callback_destroy(cb);
output->frame_callback = NULL;
wlr_output_send_frame(&output->wlr_output);
}
static const struct wl_callback_listener frame_listener = {

View file

@ -795,8 +795,6 @@ void handle_x11_present_event(struct wlr_x11_backend *x11,
};
timespec_from_nsec(&present_event.when, complete_notify->ust * 1000);
wlr_output_send_present(&output->wlr_output, &present_event);
wlr_output_send_frame(&output->wlr_output);
break;
default:
wlr_log(WLR_DEBUG, "Unhandled Present event %"PRIu16, event->event_type);

View file

@ -8,6 +8,7 @@
#include <wlr/backend.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
@ -104,10 +105,10 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
struct output *output = calloc(1, sizeof(*output));
output->wlr = wlr_output;
output->server = server;
output->frame.notify = output_handle_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
output->scene_output = wlr_scene_output_create(server->scene, wlr_output);
output->frame.notify = output_handle_frame;
wl_signal_add(&output->scene_output->frame_scheduler->events.frame, &output->frame);
struct wlr_output_state state;
wlr_output_state_init(&state);

View file

@ -10,6 +10,7 @@
#include <wlr/backend/wayland.h>
#include <wlr/render/allocator.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
@ -220,10 +221,10 @@ int main(int argc, char *argv[]) {
wl_subsurface_set_position(subsurface, 20, 20);
struct wlr_output *output = wlr_wl_output_create_from_surface(backend, child_surface);
wlr_output_init_render(output, allocator, renderer);
scene_output = wlr_scene_output_create(scene, output);
scene_output = wlr_scene_output_create(scene, output);
output_frame.notify = output_handle_frame;
wl_signal_add(&output->events.frame, &output_frame);
wl_signal_add(&scene_output->frame_scheduler->events.frame, &output_frame);
struct wlr_output_state state;
wlr_output_state_init(&state);

View file

@ -10,6 +10,7 @@
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layer.h>
@ -56,6 +57,7 @@ struct output {
struct wl_list link;
struct server *server;
struct wlr_output *wlr_output;
struct wlr_frame_scheduler *frame_scheduler;
struct wl_list surfaces;
struct wl_listener frame;
@ -155,6 +157,8 @@ static void output_handle_frame(struct wl_listener *listener, void *data) {
}
wl_array_release(&layers_arr);
wlr_frame_scheduler_schedule_frame(output->frame_scheduler);
}
static void server_handle_new_output(struct wl_listener *listener, void *data) {
@ -167,10 +171,12 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
output->wlr_output = wlr_output;
output->server = server;
wl_list_init(&output->surfaces);
output->frame.notify = output_handle_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
wl_list_insert(&server->outputs, &output->link);
output->frame_scheduler = wlr_frame_scheduler_autocreate(wlr_output);
output->frame.notify = output_handle_frame;
wl_signal_add(&output->frame_scheduler->events.frame, &output->frame);
struct wlr_output_state state;
wlr_output_state_init(&state);
wlr_output_state_set_enabled(&state, true);
@ -181,6 +187,8 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
wlr_output_commit_state(wlr_output, &state);
wlr_output_state_finish(&state);
wlr_frame_scheduler_schedule_frame(output->frame_scheduler);
wlr_output_create_global(wlr_output, server->wl_display);
}

View file

@ -12,6 +12,7 @@
#include <wlr/backend/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_output_layout.h>
@ -37,6 +38,7 @@ struct sample_state {
struct sample_output {
struct sample_state *sample;
struct wlr_output *output;
struct wlr_frame_scheduler *frame_scheduler;
struct wl_listener frame;
struct wl_listener destroy;
};
@ -143,6 +145,8 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
wlr_render_pass_submit(pass);
wlr_output_commit_state(wlr_output, &output_state);
wlr_output_state_finish(&output_state);
wlr_frame_scheduler_schedule_frame(output->frame_scheduler);
}
static void update_velocities(struct sample_state *sample,
@ -157,6 +161,7 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
wlr_output_layout_remove(sample->layout, sample_output->output);
wl_list_remove(&sample_output->frame.link);
wl_list_remove(&sample_output->destroy.link);
wlr_frame_scheduler_destroy(sample_output->frame_scheduler);
free(sample_output);
}
@ -170,7 +175,8 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
wlr_output_layout_add_auto(sample->layout, output);
sample_output->output = output;
sample_output->sample = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
sample_output->frame_scheduler = wlr_frame_scheduler_autocreate(output);
wl_signal_add(&sample_output->frame_scheduler->events.frame, &sample_output->frame);
sample_output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
@ -184,6 +190,8 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
}
wlr_output_commit_state(output, &state);
wlr_output_state_finish(&state);
wlr_frame_scheduler_schedule_frame(sample_output->frame_scheduler);
}
static void keyboard_key_notify(struct wl_listener *listener, void *data) {

View file

@ -11,6 +11,7 @@
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_pointer.h>
@ -62,6 +63,7 @@ struct touch_point {
struct sample_output {
struct sample_state *state;
struct wlr_output *output;
struct wlr_frame_scheduler *frame_scheduler;
struct wl_listener frame;
struct wl_listener destroy;
};
@ -115,6 +117,8 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
wlr_render_pass_submit(pass);
wlr_output_commit_state(wlr_output, &output_state);
wlr_output_state_finish(&output_state);
wlr_frame_scheduler_schedule_frame(sample_output->frame_scheduler);
}
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
@ -255,6 +259,7 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
wlr_output_layout_remove(sample->layout, sample_output->output);
wl_list_remove(&sample_output->frame.link);
wl_list_remove(&sample_output->destroy.link);
wlr_frame_scheduler_destroy(sample_output->frame_scheduler);
free(sample_output);
}
@ -267,7 +272,8 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = calloc(1, sizeof(*sample_output));
sample_output->output = output;
sample_output->state = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
sample_output->frame_scheduler = wlr_frame_scheduler_autocreate(output);
wl_signal_add(&sample_output->frame_scheduler->events.frame, &sample_output->frame);
sample_output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
@ -282,6 +288,8 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
}
wlr_output_commit_state(output, &state);
wlr_output_state_finish(&state);
wlr_frame_scheduler_schedule_frame(sample_output->frame_scheduler);
}

View file

@ -11,6 +11,7 @@
#include <wlr/backend.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h>
@ -33,6 +34,7 @@ struct sample_state {
struct sample_output {
struct sample_state *sample;
struct wlr_output *output;
struct wlr_frame_scheduler *frame_scheduler;
struct wl_listener frame;
struct wl_listener destroy;
float x_offs, y_offs;
@ -93,6 +95,8 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
sample_output->y_offs = 0;
}
sample->last_frame = now;
wlr_frame_scheduler_schedule_frame(sample_output->frame_scheduler);
}
static void update_velocities(struct sample_state *sample,
@ -108,6 +112,7 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
wl_list_remove(&sample_output->frame.link);
wl_list_remove(&sample_output->destroy.link);
wlr_frame_scheduler_destroy(sample_output->frame_scheduler);
free(sample_output);
}
@ -123,7 +128,8 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
sample_output->output = output;
sample_output->sample = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
sample_output->frame_scheduler = wlr_frame_scheduler_autocreate(output);
wl_signal_add(&sample_output->frame_scheduler->events.frame, &sample_output->frame);
sample_output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
@ -139,6 +145,8 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
}
wlr_output_commit_state(output, &state);
wlr_output_state_finish(&state);
wlr_frame_scheduler_schedule_frame(sample_output->frame_scheduler);
}
static void keyboard_key_notify(struct wl_listener *listener, void *data) {

View file

@ -10,6 +10,7 @@
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_xdg_shell.h>
@ -74,10 +75,10 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
struct output *output = calloc(1, sizeof(*output));
output->wlr = wlr_output;
output->server = server;
output->frame.notify = output_handle_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
output->scene_output = wlr_scene_output_create(server->scene, wlr_output);
output->frame.notify = output_handle_frame;
wl_signal_add(&output->scene_output->frame_scheduler->events.frame, &output->frame);
struct wlr_output_state state;
wlr_output_state_init(&state);

View file

@ -8,6 +8,7 @@
#include <wlr/backend/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
@ -28,6 +29,7 @@ struct sample_state {
struct sample_output {
struct sample_state *sample;
struct wlr_output *output;
struct wlr_frame_scheduler *frame_scheduler;
struct wl_listener frame;
struct wl_listener destroy;
};
@ -78,6 +80,8 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
wlr_output_commit_state(wlr_output, &state);
wlr_output_state_finish(&state);
sample->last_frame = now;
wlr_frame_scheduler_schedule_frame(sample_output->frame_scheduler);
}
static void output_remove_notify(struct wl_listener *listener, void *data) {
@ -86,6 +90,7 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
wlr_log(WLR_DEBUG, "Output removed");
wl_list_remove(&sample_output->frame.link);
wl_list_remove(&sample_output->destroy.link);
wlr_frame_scheduler_destroy(sample_output->frame_scheduler);
free(sample_output);
}
@ -99,7 +104,9 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = calloc(1, sizeof(*sample_output));
sample_output->output = output;
sample_output->sample = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
sample_output->frame_scheduler = wlr_frame_scheduler_autocreate(output);
wl_signal_add(&sample_output->frame_scheduler->events.frame, &sample_output->frame);
sample_output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
@ -113,6 +120,8 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
}
wlr_output_commit_state(output, &state);
wlr_output_state_finish(&state);
wlr_frame_scheduler_schedule_frame(sample_output->frame_scheduler);
}
static void keyboard_key_notify(struct wl_listener *listener, void *data) {

View file

@ -11,6 +11,7 @@
#include <wlr/backend/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
@ -66,6 +67,7 @@ struct tablet_pad_state {
struct sample_output {
struct sample_state *sample;
struct wlr_output *output;
struct wlr_frame_scheduler *frame_scheduler;
struct wl_listener frame;
struct wl_listener destroy;
};
@ -160,6 +162,8 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
wlr_output_commit_state(wlr_output, &output_state);
wlr_output_state_finish(&output_state);
sample->last_frame = now;
wlr_frame_scheduler_schedule_frame(sample_output->frame_scheduler);
}
static void tablet_tool_axis_notify(struct wl_listener *listener, void *data) {
@ -261,6 +265,7 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
wl_list_remove(&sample_output->frame.link);
wl_list_remove(&sample_output->destroy.link);
wlr_frame_scheduler_destroy(sample_output->frame_scheduler);
free(sample_output);
}
@ -273,7 +278,9 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = calloc(1, sizeof(*sample_output));
sample_output->output = output;
sample_output->sample = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
sample_output->frame_scheduler = wlr_frame_scheduler_autocreate(output);
wl_signal_add(&sample_output->frame_scheduler->events.frame, &sample_output->frame);
sample_output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
@ -287,6 +294,8 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
}
wlr_output_commit_state(output, &state);
wlr_output_state_finish(&state);
wlr_frame_scheduler_schedule_frame(sample_output->frame_scheduler);
}
static void keyboard_key_notify(struct wl_listener *listener, void *data) {

View file

@ -11,6 +11,7 @@
#include <wlr/backend/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
@ -52,6 +53,7 @@ struct touch_state {
struct sample_output {
struct sample_state *sample;
struct wlr_output *output;
struct wlr_frame_scheduler *frame_scheduler;
struct wl_listener frame;
struct wl_listener destroy;
};
@ -96,6 +98,8 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
wlr_output_commit_state(wlr_output, &output_state);
wlr_output_state_finish(&output_state);
sample->last_frame = now;
wlr_frame_scheduler_schedule_frame(sample_output->frame_scheduler);
}
static void touch_down_notify(struct wl_listener *listener, void *data) {
@ -164,6 +168,7 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
wl_list_remove(&sample_output->frame.link);
wl_list_remove(&sample_output->destroy.link);
wlr_frame_scheduler_destroy(sample_output->frame_scheduler);
free(sample_output);
}
@ -176,7 +181,9 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = calloc(1, sizeof(*sample_output));
sample_output->output = output;
sample_output->sample = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
sample_output->frame_scheduler = wlr_frame_scheduler_autocreate(output);
wl_signal_add(&sample_output->frame_scheduler->events.frame, &sample_output->frame);
sample_output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
@ -190,6 +197,8 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
}
wlr_output_commit_state(output, &state);
wlr_output_state_finish(&state);
wlr_frame_scheduler_schedule_frame(sample_output->frame_scheduler);
}
static void keyboard_key_notify(struct wl_listener *listener, void *data) {

View file

@ -4,8 +4,6 @@
#include <wlr/backend/headless.h>
#include <wlr/backend/interface.h>
#define HEADLESS_DEFAULT_REFRESH (60 * 1000) // 60 Hz
struct wlr_headless_backend {
struct wlr_backend backend;
struct wl_event_loop *event_loop;
@ -19,9 +17,6 @@ struct wlr_headless_output {
struct wlr_headless_backend *backend;
struct wl_list link;
struct wl_event_source *frame_timer;
int frame_delay; // ms
};
struct wlr_headless_backend *headless_backend_from_backend(

View file

@ -48,6 +48,11 @@ struct wlr_output *wlr_wl_output_create_from_surface(struct wlr_backend *backend
*/
bool wlr_backend_is_wl(const struct wlr_backend *backend);
/**
* Check whether the Wayland server advertises the wp_presentation protocol.
*/
bool wlr_wl_backend_has_presentation_time(struct wlr_backend *backend);
/**
* Check whether the provided input device is a Wayland input device.
*/

View file

@ -0,0 +1,26 @@
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_INTERFACES_WLR_FRAME_SCHEDULER_H
#define WLR_INTERFACES_WLR_FRAME_SCHEDULER_H
struct wlr_frame_scheduler;
struct wlr_output;
struct wlr_frame_scheduler_impl {
/**
* Ensure that the scheduler->frame signal will be fired in the future.
*/
void (*schedule_frame)(struct wlr_frame_scheduler *scheduler);
void (*destroy)(struct wlr_frame_scheduler *scheduler);
};
void wlr_frame_scheduler_init(struct wlr_frame_scheduler *scheduler,
const struct wlr_frame_scheduler_impl *impl, struct wlr_output *output);
#endif

View file

@ -117,12 +117,6 @@ void wlr_output_finish(struct wlr_output *output);
* output changes.
*/
void wlr_output_update_needs_frame(struct wlr_output *output);
/**
* Send a frame event.
*
* See wlr_output.events.frame.
*/
void wlr_output_send_frame(struct wlr_output *output);
/**
* Send a present event.
*

View file

@ -0,0 +1,68 @@
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_TYPES_WLR_FRAME_SCHEDULER_H
#define WLR_TYPES_WLR_FRAME_SCHEDULER_H
#include <stdbool.h>
#include <wayland-server-core.h>
struct wlr_frame_scheduler_impl;
struct wlr_frame_scheduler {
struct wlr_output *output;
struct {
struct wl_signal frame;
} events;
// Whether the render loop should be kept awake. True if wlr_frame_scheduler_schedule_frame()
// was called since the last frame event.
bool needs_frame;
// private state
const struct wlr_frame_scheduler_impl *impl;
struct wl_listener backend_needs_frame;
};
/**
* The present scheduler maintains a render loop based on `wlr_output.events.present`. To wake the
* render loop, it emits the frame signal when the compositor's event loop is idle.
*/
struct wlr_frame_scheduler *wlr_present_scheduler_create(struct wlr_output *output);
/**
* The Wayland scheduler maintains a render loop based on wl_surface.frame callbacks. To wake the
* render loop, it emits the frame signal when the compositor's event loop is idle.
*/
struct wlr_frame_scheduler *wlr_wl_scheduler_create(struct wlr_output *output);
/**
* The interval scheduler maintains a render loop based on a timer. To wake the render loop, it
* emits the frame signal when the compositor's event loop is idle.
*/
struct wlr_frame_scheduler *wlr_interval_scheduler_create(struct wlr_output *output);
/**
* Creates an appropriate frame scheduler for the given output's backend capabilities.
*/
struct wlr_frame_scheduler *wlr_frame_scheduler_autocreate(struct wlr_output *output);
/**
* Inform the scheduler that a frame signal is needed. The scheduler implementation will choose a
* good time to emit the signal. The signal is emitted only if this function has been called at
* least once since the last signal.
*/
void wlr_frame_scheduler_schedule_frame(struct wlr_frame_scheduler *scheduler);
/**
* Emits a frame signal if `wlr_frame_scheduler_schedule_frame()` has been called since the last
* frame signal.
*/
void wlr_frame_scheduler_emit_frame(struct wlr_frame_scheduler *scheduler);
void wlr_frame_scheduler_destroy(struct wlr_frame_scheduler *scheduler);
#endif

View file

@ -219,10 +219,6 @@ struct wlr_output {
// true, changes may either succeed or fail.
bool adaptive_sync_supported;
bool needs_frame;
// damage for cursors and fullscreen surface, in output-local coordinates
bool frame_pending;
// true for example with VR headsets
bool non_desktop;
@ -230,8 +226,6 @@ struct wlr_output {
uint32_t commit_seq;
struct {
// Request to render a frame
struct wl_signal frame;
// Emitted when software cursors or backend-specific logic damage the
// output
struct wl_signal damage; // struct wlr_output_event_damage
@ -252,7 +246,6 @@ struct wlr_output {
struct wl_signal destroy;
} events;
struct wl_event_source *idle_frame;
struct wl_event_source *idle_done;
int attach_render_locks; // number of locks forcing rendering
@ -406,11 +399,6 @@ bool wlr_output_test_state(struct wlr_output *output,
*/
bool wlr_output_commit_state(struct wlr_output *output,
const struct wlr_output_state *state);
/**
* Manually schedules a `frame` event. If a `frame` event is already pending,
* it is a no-op.
*/
void wlr_output_schedule_frame(struct wlr_output *output);
/**
* Returns the maximum length of each gamma ramp, or 0 if unsupported.
*/

View file

@ -224,6 +224,7 @@ struct wlr_scene_output {
struct wlr_output *output;
struct wl_list link; // wlr_scene.outputs
struct wlr_scene *scene;
struct wlr_frame_scheduler *frame_scheduler;
struct wlr_addon addon;
struct wlr_damage_ring damage_ring;
@ -258,7 +259,6 @@ struct wlr_scene_output {
struct wl_listener output_commit;
struct wl_listener output_damage;
struct wl_listener output_needs_frame;
struct wl_list damage_highlight_regions;
@ -592,6 +592,13 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output);
*/
void wlr_scene_output_set_position(struct wlr_scene_output *scene_output,
int lx, int ly);
/**
* Replace the frame scheduler for this scene output, destroying the previous
* scheduler. If scene output currently needs a new frame, a frame will be
* scheduled on the new frame scheduler.
*/
void wlr_scene_output_set_frame_scheduler(struct wlr_scene_output *scene_output,
struct wlr_frame_scheduler *scheduler);
struct wlr_scene_output_state_options {
struct wlr_scene_timer *timer;
@ -610,12 +617,6 @@ struct wlr_scene_output_state_options {
struct wlr_swapchain *swapchain;
};
/**
* Returns true if scene wants to render a new frame. False, if no new frame
* is needed and an output commit can be skipped for the current frame.
*/
bool wlr_scene_output_needs_frame(struct wlr_scene_output *scene_output);
/**
* Render and commit an output.
*/

View file

@ -12,6 +12,7 @@
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_output.h>
@ -74,6 +75,7 @@ struct tinywl_output {
struct wl_list link;
struct tinywl_server *server;
struct wlr_output *wlr_output;
struct wlr_frame_scheduler *frame_scheduler;
struct wl_listener frame;
struct wl_listener request_state;
struct wl_listener destroy;
@ -603,6 +605,7 @@ static void output_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&output->request_state.link);
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->link);
wlr_frame_scheduler_destroy(output->frame_scheduler);
free(output);
}
@ -641,10 +644,6 @@ static void server_new_output(struct wl_listener *listener, void *data) {
output->wlr_output = wlr_output;
output->server = server;
/* Sets up a listener for the frame event. */
output->frame.notify = output_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
/* Sets up a listener for the state request event. */
output->request_state.notify = output_request_state;
wl_signal_add(&wlr_output->events.request_state, &output->request_state);
@ -668,6 +667,10 @@ static void server_new_output(struct wl_listener *listener, void *data) {
wlr_output);
struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output);
wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output);
/* Sets up a listener for the frame event. */
output->frame.notify = output_frame;
wl_signal_add(&scene_output->frame_scheduler->events.frame, &output->frame);
}
static void xdg_toplevel_map(struct wl_listener *listener, void *data) {

View file

@ -5,6 +5,7 @@
#include <wlr/interfaces/wlr_ext_image_capture_source_v1.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/types/wlr_ext_image_copy_capture_v1.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/util/log.h>
#include "types/wlr_output.h"
@ -134,11 +135,8 @@ static void source_stop(struct wlr_ext_image_capture_source_v1 *base) {
static void source_request_frame(struct wlr_ext_image_capture_source_v1 *base,
bool schedule_frame) {
struct scene_node_source *source = wl_container_of(base, source, base);
if (source->output.frame_pending) {
wlr_output_send_frame(&source->output);
}
if (schedule_frame) {
wlr_output_update_needs_frame(&source->output);
if (schedule_frame && source->scene_output != NULL) {
wlr_frame_scheduler_schedule_frame(source->scene_output->frame_scheduler);
}
}
@ -246,6 +244,12 @@ static bool output_commit(struct wlr_output *output, const struct wlr_output_sta
pixman_region32_fini(&full_damage);
struct wlr_output_event_present present_event = {
.commit_seq = output->commit_seq + 1,
.presented = true,
};
output_defer_present(output, present_event);
return true;
}
@ -283,11 +287,7 @@ static void source_handle_output_frame(struct wl_listener *listener, void *data)
return;
}
if (!wlr_scene_output_needs_frame(source->scene_output)) {
return;
}
// We can only emit frames with damage
// Only render when there's actual damage to commit
if (!pixman_region32_empty(&source->scene_output->pending_commit_damage)) {
source_render(source);
}
@ -331,7 +331,7 @@ struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_create_w
wl_signal_add(&source->scene_output->events.destroy, &source->scene_output_destroy);
source->output_frame.notify = source_handle_output_frame;
wl_signal_add(&source->output.events.frame, &source->output_frame);
wl_signal_add(&source->scene_output->frame_scheduler->events.frame, &source->output_frame);
return &source->base;
}

View file

@ -50,6 +50,7 @@ wlr_files += files(
'wlr_export_dmabuf_v1.c',
'wlr_ext_background_effect_v1.c',
'wlr_ext_data_control_v1.c',
'wlr_frame_scheduler.c',
'wlr_ext_foreign_toplevel_list_v1.c',
'wlr_ext_image_copy_capture_v1.c',
'wlr_ext_workspace_v1.c',

View file

@ -367,7 +367,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
wl_list_init(&output->layers);
wl_list_init(&output->resources);
wl_signal_init(&output->events.frame);
wl_signal_init(&output->events.damage);
wl_signal_init(&output->events.needs_frame);
wl_signal_init(&output->events.precommit);
@ -397,7 +397,6 @@ void wlr_output_finish(struct wlr_output *output) {
wl_signal_emit_mutable(&output->events.destroy, output);
wlr_addon_set_finish(&output->addons);
assert(wl_list_empty(&output->events.frame.listener_list));
assert(wl_list_empty(&output->events.damage.listener_list));
assert(wl_list_empty(&output->events.needs_frame.listener_list));
assert(wl_list_empty(&output->events.precommit.listener_list));
@ -430,10 +429,6 @@ void wlr_output_finish(struct wlr_output *output) {
wlr_swapchain_destroy(output->swapchain);
if (output->idle_frame != NULL) {
wl_event_source_remove(output->idle_frame);
}
if (output->idle_done != NULL) {
wl_event_source_remove(output->idle_done);
}
@ -763,12 +758,6 @@ bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_st
return false;
}
if ((state->committed & WLR_OUTPUT_STATE_BUFFER) &&
output->idle_frame != NULL) {
wl_event_source_remove(output->idle_frame);
output->idle_frame = NULL;
}
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
@ -785,11 +774,6 @@ bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_st
void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state) {
output->commit_seq++;
if (output_pending_enabled(output, state)) {
output->frame_pending = true;
output->needs_frame = false;
}
output_apply_state(output, state);
}
@ -844,37 +828,6 @@ bool wlr_output_commit_state(struct wlr_output *output,
return true;
}
void wlr_output_send_frame(struct wlr_output *output) {
output->frame_pending = false;
if (output->enabled) {
wl_signal_emit_mutable(&output->events.frame, output);
}
}
static void schedule_frame_handle_idle_timer(void *data) {
struct wlr_output *output = data;
output->idle_frame = NULL;
if (!output->frame_pending) {
wlr_output_send_frame(output);
}
}
void wlr_output_schedule_frame(struct wlr_output *output) {
// Make sure the compositor commits a new frame. This is necessary to make
// clients which ask for frame callbacks without submitting a new buffer
// work.
wlr_output_update_needs_frame(output);
if (output->frame_pending || output->idle_frame != NULL) {
return;
}
// We're using an idle timer here in case a buffer swap happens right after
// this function is called
output->idle_frame = wl_event_loop_add_idle(output->event_loop,
schedule_frame_handle_idle_timer, output);
}
void wlr_output_send_present(struct wlr_output *output,
struct wlr_output_event_present *event) {
assert(event);
@ -988,10 +941,6 @@ size_t wlr_output_get_gamma_size(struct wlr_output *output) {
}
void wlr_output_update_needs_frame(struct wlr_output *output) {
if (output->needs_frame) {
return;
}
output->needs_frame = true;
wl_signal_emit_mutable(&output->events.needs_frame, output);
}

View file

@ -6,6 +6,7 @@
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_linux_drm_syncobj_v1.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_presentation_time.h>
@ -364,7 +365,7 @@ static void handle_scene_surface_surface_commit(
if (!wl_list_empty(&surface->surface->current.frame_callback_list) &&
surface->buffer->primary_output != NULL && enabled) {
wlr_output_schedule_frame(surface->buffer->primary_output->output);
wlr_frame_scheduler_schedule_frame(surface->buffer->primary_output->frame_scheduler);
}
}

View file

@ -8,6 +8,7 @@
#include <wlr/types/wlr_color_management_v1.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_presentation_time.h>
@ -362,7 +363,7 @@ static void scene_output_damage(struct wlr_scene_output *scene_output,
pixman_region32_intersect_rect(&clipped, damage, 0, 0, output->width, output->height);
if (!pixman_region32_empty(&clipped)) {
wlr_output_schedule_frame(scene_output->output);
wlr_frame_scheduler_schedule_frame(scene_output->frame_scheduler);
wlr_damage_ring_add(&scene_output->damage_ring, &clipped);
pixman_region32_union(&scene_output->pending_commit_damage,
@ -1576,7 +1577,7 @@ static void scene_handle_gamma_control_manager_v1_set_gamma(struct wl_listener *
output->gamma_lut = event->control;
wlr_color_transform_unref(output->gamma_lut_color_transform);
output->gamma_lut_color_transform = wlr_gamma_control_v1_get_color_transform(event->control);
wlr_output_schedule_frame(output->output);
wlr_frame_scheduler_schedule_frame(output->frame_scheduler);
}
static void scene_handle_gamma_control_manager_v1_destroy(struct wl_listener *listener,
@ -1691,7 +1692,7 @@ static void scene_output_handle_commit(struct wl_listener *listener, void *data)
if (scene_output->scene->debug_damage_option == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT &&
!wl_list_empty(&scene_output->damage_highlight_regions)) {
wlr_output_schedule_frame(scene_output->output);
wlr_frame_scheduler_schedule_frame(scene_output->frame_scheduler);
}
// Next time the output is enabled, try to re-apply the gamma LUT
@ -1720,12 +1721,6 @@ static void scene_output_handle_damage(struct wl_listener *listener, void *data)
pixman_region32_fini(&damage);
}
static void scene_output_handle_needs_frame(struct wl_listener *listener, void *data) {
struct wlr_scene_output *scene_output = wl_container_of(listener,
scene_output, output_needs_frame);
wlr_output_schedule_frame(scene_output->output);
}
struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
struct wlr_output *output) {
struct wlr_scene_output *scene_output = calloc(1, sizeof(*scene_output));
@ -1733,6 +1728,12 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
return NULL;
}
scene_output->frame_scheduler = wlr_frame_scheduler_autocreate(output);
if (scene_output->frame_scheduler == NULL) {
free(scene_output);
return NULL;
}
scene_output->output = output;
scene_output->scene = scene;
wlr_addon_init(&scene_output->addon, &output->addons, scene, &output_addon_impl);
@ -1778,9 +1779,6 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
scene_output->output_damage.notify = scene_output_handle_damage;
wl_signal_add(&output->events.damage, &scene_output->output_damage);
scene_output->output_needs_frame.notify = scene_output_handle_needs_frame;
wl_signal_add(&output->events.needs_frame, &scene_output->output_needs_frame);
scene_output_update_geometry(scene_output, false);
return scene_output;
@ -1792,6 +1790,16 @@ static void highlight_region_destroy(struct highlight_region *damage) {
free(damage);
}
void wlr_scene_output_set_frame_scheduler(struct wlr_scene_output *scene_output,
struct wlr_frame_scheduler *scheduler) {
bool needs_frame = scene_output->frame_scheduler->needs_frame;
wlr_frame_scheduler_destroy(scene_output->frame_scheduler);
scene_output->frame_scheduler = scheduler;
if (needs_frame) {
wlr_frame_scheduler_schedule_frame(scheduler);
}
}
void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
if (scene_output == NULL) {
return;
@ -1810,12 +1818,12 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
}
wlr_addon_finish(&scene_output->addon);
wlr_frame_scheduler_destroy(scene_output->frame_scheduler);
wlr_damage_ring_finish(&scene_output->damage_ring);
pixman_region32_fini(&scene_output->pending_commit_damage);
wl_list_remove(&scene_output->link);
wl_list_remove(&scene_output->output_commit.link);
wl_list_remove(&scene_output->output_damage.link);
wl_list_remove(&scene_output->output_needs_frame.link);
if (scene_output->in_timeline != NULL) {
wlr_drm_syncobj_timeline_signal(scene_output->in_timeline, UINT64_MAX);
wlr_drm_syncobj_timeline_unref(scene_output->in_timeline);
@ -2145,18 +2153,8 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout(
return SCANOUT_SUCCESS;
}
bool wlr_scene_output_needs_frame(struct wlr_scene_output *scene_output) {
return scene_output->output->needs_frame ||
!pixman_region32_empty(&scene_output->pending_commit_damage) ||
scene_output->gamma_lut_changed;
}
bool wlr_scene_output_commit(struct wlr_scene_output *scene_output,
const struct wlr_scene_output_state_options *options) {
if (!wlr_scene_output_needs_frame(scene_output)) {
return true;
}
bool ok = false;
struct wlr_output_state state;
wlr_output_state_init(&state);

297
types/wlr_frame_scheduler.c Normal file
View file

@ -0,0 +1,297 @@
#include <assert.h>
#include <backend/headless.h>
#include <stdlib.h>
#include <types/wlr_output.h>
#include <wayland-client-protocol.h>
#include <wayland-server-core.h>
#include <wayland-util.h>
#include <wlr/backend/wayland.h>
#include <wlr/interfaces/wlr_frame_scheduler.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/log.h>
void wlr_frame_scheduler_schedule_frame(struct wlr_frame_scheduler *scheduler) {
scheduler->needs_frame = true;
scheduler->impl->schedule_frame(scheduler);
}
void wlr_frame_scheduler_destroy(struct wlr_frame_scheduler *scheduler) {
if (scheduler == NULL) {
return;
}
wl_list_remove(&scheduler->backend_needs_frame.link);
scheduler->impl->destroy(scheduler);
}
void wlr_frame_scheduler_emit_frame(struct wlr_frame_scheduler *scheduler) {
if (!scheduler->needs_frame) {
return;
}
scheduler->needs_frame = false;
wl_signal_emit_mutable(&scheduler->events.frame, NULL);
}
static void frame_scheduler_handle_needs_frame(struct wl_listener *listener, void *data) {
struct wlr_frame_scheduler *scheduler = wl_container_of(listener, scheduler, backend_needs_frame);
wlr_frame_scheduler_schedule_frame(scheduler);
}
void wlr_frame_scheduler_init(struct wlr_frame_scheduler *scheduler,
const struct wlr_frame_scheduler_impl *impl, struct wlr_output *output) {
assert(impl->schedule_frame);
assert(impl->destroy);
*scheduler = (struct wlr_frame_scheduler){
.impl = impl,
.output = output,
};
wl_signal_init(&scheduler->events.frame);
scheduler->backend_needs_frame.notify = frame_scheduler_handle_needs_frame;
wl_signal_add(&output->events.needs_frame, &scheduler->backend_needs_frame);
}
// This struct and its methods are a common base for frame schedulers that restart their render loop
// via an idle event source, which fires "soon", instead of using a more complex schedule. Deferring
// the frame to an idle event is a crude way of ensuring that work done after scheduling the frame
// gets picked up by the renderer, rather than rendering happening inside the schedule call and
// missing out on any immediately following updates.
struct idle_frame_scheduler {
struct wlr_frame_scheduler base;
struct wl_event_source *idle;
// Whether the render loop is already awake, i.e. whether frames from idle events should be
// inhibited.
bool frame_pending;
};
static void idle_frame_scheduler_emit_frame(struct idle_frame_scheduler *scheduler) {
scheduler->frame_pending = false;
wlr_frame_scheduler_emit_frame(&scheduler->base);
}
static void idle_frame_scheduler_handle_idle(void *data) {
struct idle_frame_scheduler *scheduler = data;
if (!scheduler->frame_pending) {
idle_frame_scheduler_emit_frame(scheduler);
}
scheduler->idle = NULL;
}
static void idle_frame_scheduler_schedule_frame(struct wlr_frame_scheduler *wlr_scheduler) {
struct idle_frame_scheduler *scheduler = wl_container_of(wlr_scheduler, scheduler, base);
if (scheduler->idle != NULL || scheduler->frame_pending) {
// Either we are already set up to restart the render loop or it is already running.
return;
}
struct wl_event_loop *loop = scheduler->base.output->event_loop;
scheduler->idle = wl_event_loop_add_idle(loop, idle_frame_scheduler_handle_idle, scheduler);
}
static void idle_frame_scheduler_set_frame_pending(struct idle_frame_scheduler *scheduler) {
scheduler->frame_pending = true;
if (scheduler->idle) {
wl_event_source_remove(scheduler->idle);
scheduler->idle = NULL;
}
}
static void idle_frame_scheduler_finish(struct idle_frame_scheduler *scheduler) {
if (scheduler->idle) {
wl_event_source_remove(scheduler->idle);
}
}
// The present scheduler builds on the idle_frame_scheduler's logic for restarting the render loop,
// and drives the render loop using `wlr_output.events.present`.
struct present_scheduler {
struct idle_frame_scheduler base;
struct wl_listener commit;
struct wl_listener present;
};
static void present_scheduler_destroy(struct wlr_frame_scheduler *wlr_scheduler) {
struct present_scheduler *scheduler = wl_container_of(wlr_scheduler, scheduler, base.base);
idle_frame_scheduler_finish(&scheduler->base);
wl_list_remove(&scheduler->commit.link);
wl_list_remove(&scheduler->present.link);
free(scheduler);
}
static void present_scheduler_handle_commit(struct wl_listener *listener, void *data) {
struct present_scheduler *scheduler = wl_container_of(listener, scheduler, commit);
if (scheduler->base.base.output->enabled) {
idle_frame_scheduler_set_frame_pending(&scheduler->base);
}
}
static void present_scheduler_handle_present(struct wl_listener *listener, void *data) {
struct present_scheduler *scheduler = wl_container_of(listener, scheduler, present);
struct wlr_output_event_present *present = data;
if (present->presented) {
idle_frame_scheduler_emit_frame(&scheduler->base);
} else {
scheduler->base.frame_pending = false;
}
}
static const struct wlr_frame_scheduler_impl present_scheduler_impl = {
.schedule_frame = idle_frame_scheduler_schedule_frame,
.destroy = present_scheduler_destroy,
};
struct wlr_frame_scheduler *wlr_present_scheduler_create(struct wlr_output *output) {
struct present_scheduler *scheduler = calloc(1, sizeof(*scheduler));
if (!scheduler) {
return NULL;
}
wlr_frame_scheduler_init(&scheduler->base.base, &present_scheduler_impl, output);
scheduler->commit.notify = present_scheduler_handle_commit;
wl_signal_add(&output->events.commit, &scheduler->commit);
scheduler->present.notify = present_scheduler_handle_present;
wl_signal_add(&output->events.present, &scheduler->present);
return &scheduler->base.base;
}
// This scheduler builds on idle_frame_scheduler and uses Wayland's frame callbacks for driving the
// render loop.
struct wl_scheduler {
struct idle_frame_scheduler base;
struct wl_callback *frame;
struct wl_listener precommit;
};
static void wl_scheduler_destroy(struct wlr_frame_scheduler *wlr_scheduler) {
struct wl_scheduler *scheduler = wl_container_of(wlr_scheduler, scheduler, base.base);
idle_frame_scheduler_finish(&scheduler->base);
wl_list_remove(&scheduler->precommit.link);
wl_callback_destroy(scheduler->frame);
free(scheduler);
}
static void wl_scheduler_handle_frame(void *data, struct wl_callback *cb, uint32_t time) {
struct wl_scheduler *scheduler = data;
assert(scheduler->frame == cb);
idle_frame_scheduler_emit_frame(&scheduler->base);
}
static const struct wl_callback_listener wl_scheduler_frame_listener = {
.done = wl_scheduler_handle_frame,
};
static void wl_scheduler_handle_precommit(struct wl_listener *listener, void *data) {
struct wl_scheduler *scheduler = wl_container_of(listener, scheduler, precommit);
struct wlr_output_event_precommit *precommit = data;
if (!output_pending_enabled(precommit->output, precommit->state)) {
return;
}
idle_frame_scheduler_set_frame_pending(&scheduler->base);
if (scheduler->frame != NULL) {
wl_callback_destroy(scheduler->frame);
}
struct wl_surface *surface = wlr_wl_output_get_surface(scheduler->base.base.output);
scheduler->frame = wl_surface_frame(surface);
wl_callback_add_listener(scheduler->frame, &wl_scheduler_frame_listener, scheduler);
}
static const struct wlr_frame_scheduler_impl wl_scheduler_impl = {
.schedule_frame = idle_frame_scheduler_schedule_frame,
.destroy = wl_scheduler_destroy,
};
static struct wlr_frame_scheduler *wl_scheduler_create(struct wlr_output *output) {
if (!wlr_output_is_wl(output)) {
return NULL;
}
struct wl_scheduler *scheduler = calloc(1, sizeof(*scheduler));
if (!scheduler) {
return NULL;
}
wlr_frame_scheduler_init(&scheduler->base.base, &wl_scheduler_impl, output);
scheduler->precommit.notify = wl_scheduler_handle_precommit;
wl_signal_add(&output->events.precommit, &scheduler->precommit);
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);
}