mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-04-17 06:46:39 -04:00
Merge branch 'scheduling-2-better-this-time' into 'master'
Add new frame scheduler interface See merge request wlroots/wlroots!4307
This commit is contained in:
commit
a06c728a05
29 changed files with 507 additions and 153 deletions
|
|
@ -2016,10 +2016,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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -83,7 +65,6 @@ static void output_destroy(struct wlr_output *wlr_output) {
|
|||
struct wlr_headless_output *output =
|
||||
headless_output_from_output(wlr_output);
|
||||
wl_list_remove(&output->link);
|
||||
wl_event_source_remove(output->frame_timer);
|
||||
free(output);
|
||||
}
|
||||
|
||||
|
|
@ -96,12 +77,6 @@ bool wlr_output_is_headless(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 =
|
||||
|
|
@ -122,8 +97,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];
|
||||
|
|
@ -134,8 +107,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) {
|
||||
|
|
|
|||
|
|
@ -560,6 +560,11 @@ bool wlr_backend_is_wl(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);
|
||||
|
|
|
|||
|
|
@ -69,8 +69,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 = {
|
||||
|
|
|
|||
|
|
@ -765,8 +765,6 @@ void handle_x11_present_event(struct wlr_x11_backend *x11,
|
|||
.flags = flags,
|
||||
};
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -103,10 +104,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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,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_fullscreen_shell_v1.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
|
|
@ -38,6 +39,7 @@ struct fullscreen_output {
|
|||
struct wl_list link;
|
||||
struct fullscreen_server *server;
|
||||
struct wlr_output *wlr_output;
|
||||
struct wlr_frame_scheduler *frame_scheduler;
|
||||
struct wlr_surface *surface;
|
||||
struct wl_listener surface_destroy;
|
||||
|
||||
|
|
@ -113,6 +115,8 @@ static void output_handle_frame(struct wl_listener *listener, void *data) {
|
|||
wlr_render_pass_submit(pass);
|
||||
wlr_output_commit_state(output->wlr_output, &state);
|
||||
wlr_output_state_finish(&state);
|
||||
|
||||
wlr_frame_scheduler_schedule_frame(output->frame_scheduler);
|
||||
}
|
||||
|
||||
static void output_set_surface(struct fullscreen_output *output,
|
||||
|
|
@ -156,10 +160,12 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
|
|||
struct fullscreen_output *output = calloc(1, sizeof(*output));
|
||||
output->wlr_output = wlr_output;
|
||||
output->server = server;
|
||||
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);
|
||||
|
||||
wlr_output_layout_add_auto(server->output_layout, wlr_output);
|
||||
wlr_output_create_global(wlr_output, server->wl_display);
|
||||
|
||||
|
|
@ -172,6 +178,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);
|
||||
}
|
||||
|
||||
static void server_handle_present_surface(struct wl_listener *listener,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -157,6 +159,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) {
|
||||
|
|
@ -169,10 +173,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);
|
||||
|
|
@ -183,6 +189,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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -48,6 +48,11 @@ struct wlr_output *wlr_wl_output_create_from_surface(struct wlr_backend *backend
|
|||
*/
|
||||
bool wlr_backend_is_wl(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.
|
||||
*/
|
||||
|
|
|
|||
26
include/wlr/interfaces/wlr_frame_scheduler.h
Normal file
26
include/wlr/interfaces/wlr_frame_scheduler.h
Normal 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
|
||||
|
|
@ -108,12 +108,6 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
|
|||
* 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.
|
||||
*
|
||||
|
|
|
|||
68
include/wlr/types/wlr_frame_scheduler.h
Normal file
68
include/wlr/types/wlr_frame_scheduler.h
Normal 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 iff 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 iff `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
|
||||
|
|
@ -150,10 +150,6 @@ struct wlr_output {
|
|||
enum wlr_output_adaptive_sync_status adaptive_sync_status;
|
||||
uint32_t render_format;
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -161,8 +157,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
|
||||
|
|
@ -183,7 +177,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
|
||||
|
|
@ -334,11 +327,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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -199,6 +199,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;
|
||||
|
|
@ -218,7 +219,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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -73,6 +74,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;
|
||||
|
|
@ -590,6 +592,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);
|
||||
}
|
||||
|
||||
|
|
@ -628,10 +631,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);
|
||||
|
|
@ -655,6 +654,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) {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ wlr_files += files(
|
|||
'wlr_data_control_v1.c',
|
||||
'wlr_drm.c',
|
||||
'wlr_export_dmabuf_v1.c',
|
||||
'wlr_frame_scheduler.c',
|
||||
'wlr_foreign_toplevel_management_v1.c',
|
||||
'wlr_ext_foreign_toplevel_list_v1.c',
|
||||
'wlr_fullscreen_shell_v1.c',
|
||||
|
|
|
|||
|
|
@ -354,7 +354,6 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
|
|||
wl_list_init(&output->cursors);
|
||||
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);
|
||||
|
|
@ -410,10 +409,6 @@ void wlr_output_destroy(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);
|
||||
}
|
||||
|
|
@ -669,12 +664,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);
|
||||
|
||||
|
|
@ -691,11 +680,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);
|
||||
|
||||
struct timespec now;
|
||||
|
|
@ -747,37 +731,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);
|
||||
|
|
@ -860,10 +813,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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,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_presentation_time.h>
|
||||
#include <wlr/util/transform.h>
|
||||
#include "types/wlr_scene.h"
|
||||
|
|
@ -180,7 +181,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <wlr/render/wlr_renderer.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_linux_dmabuf_v1.h>
|
||||
#include <wlr/types/wlr_presentation_time.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
|
|
@ -328,7 +329,7 @@ static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *dam
|
|||
-scene_output->x, -scene_output->y);
|
||||
scale_output_damage(&output_damage, scene_output->output->scale);
|
||||
if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) {
|
||||
wlr_output_schedule_frame(scene_output->output);
|
||||
wlr_frame_scheduler_schedule_frame(scene_output->frame_scheduler);
|
||||
}
|
||||
pixman_region32_fini(&output_damage);
|
||||
}
|
||||
|
|
@ -800,7 +801,7 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff
|
|||
(int)round((lx - scene_output->x) * output_scale),
|
||||
(int)round((ly - scene_output->y) * output_scale));
|
||||
if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) {
|
||||
wlr_output_schedule_frame(scene_output->output);
|
||||
wlr_frame_scheduler_schedule_frame(scene_output->frame_scheduler);
|
||||
}
|
||||
pixman_region32_fini(&output_damage);
|
||||
}
|
||||
|
|
@ -1299,7 +1300,7 @@ static void scene_node_output_update(struct wlr_scene_node *node,
|
|||
static void scene_output_update_geometry(struct wlr_scene_output *scene_output,
|
||||
bool force_update) {
|
||||
wlr_damage_ring_add_whole(&scene_output->damage_ring);
|
||||
wlr_output_schedule_frame(scene_output->output);
|
||||
wlr_frame_scheduler_schedule_frame(scene_output->frame_scheduler);
|
||||
|
||||
scene_node_output_update(&scene_output->scene->tree.node,
|
||||
&scene_output->scene->outputs, NULL, force_update ? scene_output : NULL);
|
||||
|
|
@ -1349,16 +1350,10 @@ static void scene_output_handle_damage(struct wl_listener *listener, void *data)
|
|||
scene_output, output_damage);
|
||||
struct wlr_output_event_damage *event = data;
|
||||
if (wlr_damage_ring_add(&scene_output->damage_ring, event->damage)) {
|
||||
wlr_output_schedule_frame(scene_output->output);
|
||||
wlr_frame_scheduler_schedule_frame(scene_output->frame_scheduler);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
|
@ -1366,6 +1361,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);
|
||||
|
|
@ -1399,9 +1400,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;
|
||||
|
|
@ -1429,12 +1427,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);
|
||||
|
||||
wl_array_release(&scene_output->render_list);
|
||||
free(scene_output);
|
||||
|
|
@ -1674,11 +1672,6 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry,
|
|||
|
||||
bool wlr_scene_output_commit(struct wlr_scene_output *scene_output,
|
||||
const struct wlr_scene_output_state_options *options) {
|
||||
if (!scene_output->output->needs_frame && !pixman_region32_not_empty(
|
||||
&scene_output->pending_commit_damage)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
struct wlr_output_state state;
|
||||
wlr_output_state_init(&state);
|
||||
|
|
@ -1970,7 +1963,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
|
|||
|
||||
if (debug_damage == 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);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
293
types/wlr_frame_scheduler.c
Normal file
293
types/wlr_frame_scheduler.c
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
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_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);
|
||||
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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue