mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-04-13 08:22:16 -04:00
Merge branch 'predictive-frame-scheduling' into 'master'
Draft: wlr_frame_scheduler: Add predictive frame scheduler See merge request wlroots/wlroots!5332
This commit is contained in:
commit
651f85cb81
34 changed files with 882 additions and 220 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@ struct wlr_vk_device {
|
|||
struct wlr_drm_format_set dmabuf_render_formats;
|
||||
struct wlr_drm_format_set dmabuf_texture_formats;
|
||||
struct wlr_drm_format_set shm_texture_formats;
|
||||
|
||||
float timestamp_period;
|
||||
uint32_t timestamp_valid_bits;
|
||||
};
|
||||
|
||||
// Tries to find the VkPhysicalDevice for the given drm fd.
|
||||
|
|
@ -412,6 +415,12 @@ VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer);
|
|||
// finished execution.
|
||||
bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer, int wait_sync_file_fd);
|
||||
|
||||
struct wlr_vk_render_timer {
|
||||
struct wlr_render_timer base;
|
||||
struct wlr_vk_renderer *renderer;
|
||||
VkQueryPool query_pool;
|
||||
};
|
||||
|
||||
struct wlr_vk_render_pass_texture {
|
||||
struct wlr_vk_texture *texture;
|
||||
|
||||
|
|
@ -436,6 +445,8 @@ struct wlr_vk_render_pass {
|
|||
struct wlr_drm_syncobj_timeline *signal_timeline;
|
||||
uint64_t signal_point;
|
||||
|
||||
struct wlr_vk_render_timer *timer;
|
||||
|
||||
struct wl_array textures; // struct wlr_vk_render_pass_texture
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
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
|
||||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
89
include/wlr/types/wlr_frame_scheduler.h
Normal file
89
include/wlr/types/wlr_frame_scheduler.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
struct wlr_frame_scheduler_impl;
|
||||
struct wlr_render_timer;
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* The predictive scheduler maintains a render loop based on `wlr_output.events.present`, and
|
||||
* schedules frame signals to arrive just before the estimated render deadline. It learns from
|
||||
* historic render times provided via wlr_frame_scheduler_inform_render().
|
||||
*/
|
||||
struct wlr_frame_scheduler *wlr_predictive_frame_scheduler_create(struct wlr_output *output);
|
||||
/**
|
||||
* Provide render timing feedback to the scheduler. Must be called after each output commit.
|
||||
*
|
||||
* pre_render_duration_ns is the wall time from the frame signal to the start of the render pass.
|
||||
*
|
||||
* render_timer is the GPU render timer for this frame. May be NULL if no rendering was performed
|
||||
* (e.g. direct scanout), in which case GPU render time is assumed to be zero.
|
||||
*
|
||||
* This is a no-op for non-predictive schedulers.
|
||||
*/
|
||||
void wlr_frame_scheduler_inform_render(struct wlr_frame_scheduler *scheduler,
|
||||
int64_t pre_render_duration_ns, struct wlr_render_timer *render_timer);
|
||||
|
||||
#endif
|
||||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -268,12 +268,9 @@ struct wlr_scene_output {
|
|||
uint64_t in_point;
|
||||
struct wlr_drm_syncobj_timeline *out_timeline;
|
||||
uint64_t out_point;
|
||||
} WLR_PRIVATE;
|
||||
};
|
||||
|
||||
struct wlr_scene_timer {
|
||||
int64_t pre_render_duration;
|
||||
struct wlr_render_timer *render_timer;
|
||||
struct wlr_render_timer *render_timer;
|
||||
} WLR_PRIVATE;
|
||||
};
|
||||
|
||||
/** A layer shell scene helper */
|
||||
|
|
@ -592,10 +589,15 @@ 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;
|
||||
|
||||
/**
|
||||
* Color transform to apply before the output's color transform. Cannot be
|
||||
* used when the output has a non-NULL image description set.
|
||||
|
|
@ -610,12 +612,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.
|
||||
*/
|
||||
|
|
@ -628,15 +624,6 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output,
|
|||
bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
|
||||
struct wlr_output_state *state, const struct wlr_scene_output_state_options *options);
|
||||
|
||||
/**
|
||||
* Retrieve the duration in nanoseconds between the last wlr_scene_output_commit() call and the end
|
||||
* of its operations, including those on the GPU that may have finished after the call returned.
|
||||
*
|
||||
* Returns -1 if the duration is unavailable.
|
||||
*/
|
||||
int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer);
|
||||
void wlr_scene_timer_finish(struct wlr_scene_timer *timer);
|
||||
|
||||
/**
|
||||
* Call wlr_surface_send_frame_done() on all surfaces in the scene rendered by
|
||||
* wlr_scene_output_commit() for which wlr_scene_surface.primary_output
|
||||
|
|
|
|||
|
|
@ -295,6 +295,11 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
|
|||
|
||||
vkCmdEndRenderPass(render_cb->vk);
|
||||
|
||||
if (pass->timer != NULL) {
|
||||
vkCmdWriteTimestamp(render_cb->vk, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
pass->timer->query_pool, 1);
|
||||
}
|
||||
|
||||
size_t pass_textures_len = pass->textures.size / sizeof(struct wlr_vk_render_pass_texture);
|
||||
size_t render_wait_cap = (1 + pass_textures_len) * WLR_DMABUF_MAX_PLANES;
|
||||
render_wait = calloc(render_wait_cap, sizeof(*render_wait));
|
||||
|
|
@ -1280,6 +1285,14 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend
|
|||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
}
|
||||
|
||||
struct wlr_vk_render_timer *timer = NULL;
|
||||
if (options != NULL && options->timer != NULL) {
|
||||
timer = wl_container_of(options->timer, timer, base);
|
||||
vkCmdResetQueryPool(cb->vk, timer->query_pool, 0, 2);
|
||||
vkCmdWriteTimestamp(cb->vk, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
timer->query_pool, 0);
|
||||
}
|
||||
|
||||
int width = buffer->wlr_buffer->width;
|
||||
int height = buffer->wlr_buffer->height;
|
||||
VkRect2D rect = { .extent = { width, height } };
|
||||
|
|
@ -1308,5 +1321,6 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend
|
|||
pass->render_buffer_out = buffer_out;
|
||||
pass->render_setup = render_setup;
|
||||
pass->command_buffer = cb;
|
||||
pass->timer = timer;
|
||||
return pass;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1556,6 +1556,83 @@ static struct wlr_render_pass *vulkan_begin_buffer_pass(struct wlr_renderer *wlr
|
|||
return &render_pass->base;
|
||||
}
|
||||
|
||||
static const struct wlr_render_timer_impl render_timer_impl;
|
||||
|
||||
static struct wlr_render_timer *vulkan_render_timer_create(
|
||||
struct wlr_renderer *wlr_renderer) {
|
||||
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
|
||||
if (renderer->dev->timestamp_valid_bits == 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to create render timer: "
|
||||
"timestamp queries not supported by queue family");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_vk_render_timer *timer = calloc(1, sizeof(*timer));
|
||||
if (!timer) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VkQueryPoolCreateInfo pool_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
|
||||
.queryType = VK_QUERY_TYPE_TIMESTAMP,
|
||||
.queryCount = 2,
|
||||
};
|
||||
VkResult res = vkCreateQueryPool(renderer->dev->dev, &pool_info,
|
||||
NULL, &timer->query_pool);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkCreateQueryPool", res);
|
||||
free(timer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
timer->base.impl = &render_timer_impl;
|
||||
timer->renderer = renderer;
|
||||
return &timer->base;
|
||||
}
|
||||
|
||||
static int vulkan_render_timer_get_duration_ns(
|
||||
struct wlr_render_timer *wlr_timer) {
|
||||
struct wlr_vk_render_timer *timer =
|
||||
wl_container_of(wlr_timer, timer, base);
|
||||
struct wlr_vk_renderer *renderer = timer->renderer;
|
||||
|
||||
// Layout: [ timestamp1, avail1, timestamp2, avail2 ]
|
||||
uint64_t data[4] = {0};
|
||||
VkResult res = vkGetQueryPoolResults(renderer->dev->dev,
|
||||
timer->query_pool, 0, 2, sizeof(data), data,
|
||||
2 * sizeof(uint64_t),
|
||||
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
|
||||
if (res == VK_NOT_READY || data[1] == 0 || data[3] == 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to get render duration: "
|
||||
"timestamp query results not yet ready");
|
||||
return -1;
|
||||
}
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkGetQueryPoolResults", res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t ticks = data[2] - data[0];
|
||||
if (renderer->dev->timestamp_valid_bits < 64) {
|
||||
ticks &= (1ULL << renderer->dev->timestamp_valid_bits) - 1;
|
||||
}
|
||||
return (int)(ticks * renderer->dev->timestamp_period);
|
||||
}
|
||||
|
||||
static void vulkan_render_timer_destroy(
|
||||
struct wlr_render_timer *wlr_timer) {
|
||||
struct wlr_vk_render_timer *timer =
|
||||
wl_container_of(wlr_timer, timer, base);
|
||||
vkDestroyQueryPool(timer->renderer->dev->dev, timer->query_pool, NULL);
|
||||
free(timer);
|
||||
}
|
||||
|
||||
static const struct wlr_render_timer_impl render_timer_impl = {
|
||||
.get_duration_ns = vulkan_render_timer_get_duration_ns,
|
||||
.destroy = vulkan_render_timer_destroy,
|
||||
};
|
||||
|
||||
static const struct wlr_renderer_impl renderer_impl = {
|
||||
.get_texture_formats = vulkan_get_texture_formats,
|
||||
.get_render_formats = vulkan_get_render_formats,
|
||||
|
|
@ -1563,6 +1640,7 @@ static const struct wlr_renderer_impl renderer_impl = {
|
|||
.get_drm_fd = vulkan_get_drm_fd,
|
||||
.texture_from_buffer = vulkan_texture_from_buffer,
|
||||
.begin_buffer_pass = vulkan_begin_buffer_pass,
|
||||
.render_timer_create = vulkan_render_timer_create,
|
||||
};
|
||||
|
||||
// Initializes the VkDescriptorSetLayout and VkPipelineLayout needed
|
||||
|
|
|
|||
|
|
@ -488,10 +488,15 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini,
|
|||
graphics_found = queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT;
|
||||
if (graphics_found) {
|
||||
dev->queue_family = i;
|
||||
dev->timestamp_valid_bits = queue_props[i].timestampValidBits;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(graphics_found);
|
||||
|
||||
VkPhysicalDeviceProperties phdev_props;
|
||||
vkGetPhysicalDeviceProperties(phdev, &phdev_props);
|
||||
dev->timestamp_period = phdev_props.limits.timestampPeriod;
|
||||
}
|
||||
|
||||
bool exportable_semaphore = false, importable_semaphore = false;
|
||||
|
|
|
|||
|
|
@ -329,6 +329,9 @@ int wlr_render_timer_get_duration_ns(struct wlr_render_timer *timer) {
|
|||
}
|
||||
|
||||
void wlr_render_timer_destroy(struct wlr_render_timer *timer) {
|
||||
if (!timer) {
|
||||
return;
|
||||
}
|
||||
if (!timer->impl->destroy) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,11 +1779,10 @@ 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);
|
||||
|
||||
scene_output->render_timer = wlr_render_timer_create(output->renderer);
|
||||
|
||||
return scene_output;
|
||||
}
|
||||
|
||||
|
|
@ -1792,6 +1792,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 +1820,13 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
|
|||
}
|
||||
|
||||
wlr_addon_finish(&scene_output->addon);
|
||||
wlr_render_timer_destroy(scene_output->render_timer);
|
||||
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 +2156,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);
|
||||
|
|
@ -2281,13 +2282,8 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
|
|||
if (!options) {
|
||||
options = &default_options;
|
||||
}
|
||||
struct wlr_scene_timer *timer = options->timer;
|
||||
struct timespec start_time;
|
||||
if (timer) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||
wlr_scene_timer_finish(timer);
|
||||
*timer = (struct wlr_scene_timer){0};
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||
|
||||
if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && !state->enabled) {
|
||||
// if the state is being disabled, do nothing.
|
||||
|
|
@ -2434,12 +2430,11 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
|
|||
if (scanout) {
|
||||
scene_output_state_attempt_gamma(scene_output, state);
|
||||
|
||||
if (timer) {
|
||||
struct timespec end_time, duration;
|
||||
clock_gettime(CLOCK_MONOTONIC, &end_time);
|
||||
timespec_sub(&duration, &end_time, &start_time);
|
||||
timer->pre_render_duration = timespec_to_nsec(&duration);
|
||||
}
|
||||
struct timespec end_time, duration;
|
||||
clock_gettime(CLOCK_MONOTONIC, &end_time);
|
||||
timespec_sub(&duration, &end_time, &start_time);
|
||||
wlr_frame_scheduler_inform_render(scene_output->frame_scheduler,
|
||||
timespec_to_nsec(&duration), NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2459,13 +2454,12 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
|
|||
|
||||
assert(buffer->width == resolution_width && buffer->height == resolution_height);
|
||||
|
||||
if (timer) {
|
||||
timer->render_timer = wlr_render_timer_create(output->renderer);
|
||||
|
||||
int64_t pre_render_duration_ns;
|
||||
{
|
||||
struct timespec end_time, duration;
|
||||
clock_gettime(CLOCK_MONOTONIC, &end_time);
|
||||
timespec_sub(&duration, &end_time, &start_time);
|
||||
timer->pre_render_duration = timespec_to_nsec(&duration);
|
||||
pre_render_duration_ns = timespec_to_nsec(&duration);
|
||||
}
|
||||
|
||||
if ((render_gamma_lut
|
||||
|
|
@ -2484,7 +2478,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
|
|||
scene_output->in_point++;
|
||||
struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer,
|
||||
&(struct wlr_buffer_pass_options){
|
||||
.timer = timer ? timer->render_timer : NULL,
|
||||
.timer = scene_output->render_timer,
|
||||
.color_transform = scene_output->combined_color_transform,
|
||||
.signal_timeline = scene_output->in_timeline,
|
||||
.signal_point = scene_output->in_point,
|
||||
|
|
@ -2608,24 +2602,11 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
|
|||
scene_output_state_attempt_gamma(scene_output, state);
|
||||
}
|
||||
|
||||
wlr_frame_scheduler_inform_render(scene_output->frame_scheduler,
|
||||
pre_render_duration_ns, scene_output->render_timer);
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer) {
|
||||
int64_t pre_render = timer->pre_render_duration;
|
||||
if (!timer->render_timer) {
|
||||
return pre_render;
|
||||
}
|
||||
int64_t render = wlr_render_timer_get_duration_ns(timer->render_timer);
|
||||
return render != -1 ? pre_render + render : -1;
|
||||
}
|
||||
|
||||
void wlr_scene_timer_finish(struct wlr_scene_timer *timer) {
|
||||
if (timer->render_timer) {
|
||||
wlr_render_timer_destroy(timer->render_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void scene_node_send_frame_done(struct wlr_scene_node *node,
|
||||
struct wlr_scene_output *scene_output, struct timespec *now) {
|
||||
if (!node->enabled) {
|
||||
|
|
|
|||
502
types/wlr_frame_scheduler.c
Normal file
502
types/wlr_frame_scheduler.c
Normal file
|
|
@ -0,0 +1,502 @@
|
|||
#include <assert.h>
|
||||
#include <backend/headless.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.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/render/wlr_renderer.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;
|
||||
}
|
||||
|
||||
// The predictive scheduler schedules frame signals based on historic render times, aiming to
|
||||
// start rendering as late as possible while still meeting the next vblank deadline.
|
||||
struct predictive_scheduler {
|
||||
struct wlr_frame_scheduler base;
|
||||
struct wl_event_source *idle;
|
||||
struct wl_event_source *timer;
|
||||
struct wl_listener commit;
|
||||
struct wl_listener present;
|
||||
|
||||
bool frame_pending;
|
||||
|
||||
int64_t estimated_frame_time_ns;
|
||||
bool has_estimate;
|
||||
|
||||
int64_t pending_pre_render_ns;
|
||||
struct wlr_render_timer *pending_render_timer;
|
||||
bool has_pending_feedback;
|
||||
};
|
||||
|
||||
static const struct wlr_frame_scheduler_impl predictive_scheduler_impl;
|
||||
|
||||
static int64_t compute_margin_ns(int refresh_ns) {
|
||||
int64_t margin = refresh_ns / 20;
|
||||
if (margin < 1500000) {
|
||||
margin = 1500000;
|
||||
}
|
||||
return margin;
|
||||
}
|
||||
|
||||
static void update_estimate(struct predictive_scheduler *scheduler, int64_t sample_ns) {
|
||||
if (!scheduler->has_estimate) {
|
||||
scheduler->estimated_frame_time_ns = sample_ns;
|
||||
scheduler->has_estimate = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sample_ns > scheduler->estimated_frame_time_ns) {
|
||||
// React quickly to spikes
|
||||
scheduler->estimated_frame_time_ns =
|
||||
(9 * sample_ns + scheduler->estimated_frame_time_ns) / 10;
|
||||
} else {
|
||||
// Decay slowly
|
||||
scheduler->estimated_frame_time_ns =
|
||||
(sample_ns + 9 * scheduler->estimated_frame_time_ns) / 10;
|
||||
}
|
||||
}
|
||||
|
||||
static void predictive_scheduler_consume_feedback(struct predictive_scheduler *scheduler) {
|
||||
if (!scheduler->has_pending_feedback) {
|
||||
return;
|
||||
}
|
||||
scheduler->has_pending_feedback = false;
|
||||
|
||||
int64_t gpu_ns = 0;
|
||||
if (scheduler->pending_render_timer != NULL) {
|
||||
int duration = wlr_render_timer_get_duration_ns(scheduler->pending_render_timer);
|
||||
if (duration > 0) {
|
||||
gpu_ns = duration;
|
||||
}
|
||||
}
|
||||
|
||||
update_estimate(scheduler, scheduler->pending_pre_render_ns + gpu_ns);
|
||||
}
|
||||
|
||||
static void predictive_scheduler_handle_idle(void *data) {
|
||||
struct predictive_scheduler *scheduler = data;
|
||||
scheduler->idle = NULL;
|
||||
scheduler->frame_pending = false;
|
||||
wlr_frame_scheduler_emit_frame(&scheduler->base);
|
||||
}
|
||||
|
||||
static int predictive_scheduler_handle_timer(void *data) {
|
||||
struct predictive_scheduler *scheduler = data;
|
||||
scheduler->frame_pending = false;
|
||||
wlr_frame_scheduler_emit_frame(&scheduler->base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void predictive_scheduler_set_timer(struct predictive_scheduler *scheduler, int ms) {
|
||||
if (scheduler->idle != NULL) {
|
||||
wl_event_source_remove(scheduler->idle);
|
||||
scheduler->idle = NULL;
|
||||
}
|
||||
wl_event_source_timer_update(scheduler->timer, ms);
|
||||
}
|
||||
|
||||
static void predictive_scheduler_set_idle(struct predictive_scheduler *scheduler) {
|
||||
wl_event_source_timer_update(scheduler->timer, 0);
|
||||
if (!scheduler->idle) {
|
||||
scheduler->idle = wl_event_loop_add_idle(
|
||||
scheduler->base.output->event_loop,
|
||||
predictive_scheduler_handle_idle, scheduler);
|
||||
}
|
||||
}
|
||||
|
||||
static void predictive_scheduler_cancel(struct predictive_scheduler *scheduler) {
|
||||
if (scheduler->idle != NULL) {
|
||||
wl_event_source_remove(scheduler->idle);
|
||||
scheduler->idle = NULL;
|
||||
}
|
||||
wl_event_source_timer_update(scheduler->timer, 0);
|
||||
}
|
||||
|
||||
static void predictive_scheduler_handle_commit(struct wl_listener *listener, void *data) {
|
||||
struct predictive_scheduler *scheduler = wl_container_of(listener, scheduler, commit);
|
||||
if (scheduler->base.output->enabled) {
|
||||
scheduler->frame_pending = true;
|
||||
predictive_scheduler_cancel(scheduler);
|
||||
}
|
||||
}
|
||||
|
||||
static void predictive_scheduler_handle_present(struct wl_listener *listener, void *data) {
|
||||
struct predictive_scheduler *scheduler = wl_container_of(listener, scheduler, present);
|
||||
struct wlr_output_event_present *event = data;
|
||||
|
||||
if (!event->presented) {
|
||||
scheduler->frame_pending = false;
|
||||
return;
|
||||
}
|
||||
|
||||
predictive_scheduler_consume_feedback(scheduler);
|
||||
|
||||
if (!scheduler->has_estimate || event->refresh == 0) {
|
||||
predictive_scheduler_set_idle(scheduler);
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t refresh_ns = event->refresh;
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
int64_t present_ns = (int64_t)event->when.tv_sec * 1000000000 + event->when.tv_nsec;
|
||||
int64_t now_ns = (int64_t)now.tv_sec * 1000000000 + now.tv_nsec;
|
||||
int64_t next_vblank_ns = present_ns + refresh_ns;
|
||||
|
||||
int64_t margin = compute_margin_ns(refresh_ns);
|
||||
int64_t deadline_ns = next_vblank_ns - scheduler->estimated_frame_time_ns - margin;
|
||||
int64_t delay_ns = deadline_ns - now_ns;
|
||||
|
||||
if (delay_ns < 1000000) {
|
||||
predictive_scheduler_set_idle(scheduler);
|
||||
} else {
|
||||
int delay_ms = (int)(delay_ns / 1000000);
|
||||
predictive_scheduler_set_timer(scheduler, delay_ms);
|
||||
}
|
||||
}
|
||||
|
||||
static void predictive_scheduler_schedule_frame(struct wlr_frame_scheduler *wlr_scheduler) {
|
||||
struct predictive_scheduler *scheduler =
|
||||
wl_container_of(wlr_scheduler, scheduler, base);
|
||||
if (scheduler->idle != NULL || scheduler->frame_pending) {
|
||||
return;
|
||||
}
|
||||
predictive_scheduler_set_idle(scheduler);
|
||||
}
|
||||
|
||||
static void predictive_scheduler_destroy(struct wlr_frame_scheduler *wlr_scheduler) {
|
||||
struct predictive_scheduler *scheduler =
|
||||
wl_container_of(wlr_scheduler, scheduler, base);
|
||||
if (scheduler->idle != NULL) {
|
||||
wl_event_source_remove(scheduler->idle);
|
||||
}
|
||||
wl_event_source_remove(scheduler->timer);
|
||||
wl_list_remove(&scheduler->commit.link);
|
||||
wl_list_remove(&scheduler->present.link);
|
||||
free(scheduler);
|
||||
}
|
||||
|
||||
static const struct wlr_frame_scheduler_impl predictive_scheduler_impl = {
|
||||
.schedule_frame = predictive_scheduler_schedule_frame,
|
||||
.destroy = predictive_scheduler_destroy,
|
||||
};
|
||||
|
||||
struct wlr_frame_scheduler *wlr_predictive_frame_scheduler_create(struct wlr_output *output) {
|
||||
struct predictive_scheduler *scheduler = calloc(1, sizeof(*scheduler));
|
||||
if (!scheduler) {
|
||||
return NULL;
|
||||
}
|
||||
wlr_frame_scheduler_init(&scheduler->base, &predictive_scheduler_impl, output);
|
||||
|
||||
scheduler->timer = wl_event_loop_add_timer(output->event_loop,
|
||||
predictive_scheduler_handle_timer, scheduler);
|
||||
|
||||
scheduler->commit.notify = predictive_scheduler_handle_commit;
|
||||
wl_signal_add(&output->events.commit, &scheduler->commit);
|
||||
scheduler->present.notify = predictive_scheduler_handle_present;
|
||||
wl_signal_add(&output->events.present, &scheduler->present);
|
||||
return &scheduler->base;
|
||||
}
|
||||
|
||||
void wlr_frame_scheduler_inform_render(struct wlr_frame_scheduler *scheduler,
|
||||
int64_t pre_render_duration_ns, struct wlr_render_timer *render_timer) {
|
||||
if (scheduler->impl != &predictive_scheduler_impl) {
|
||||
return;
|
||||
}
|
||||
struct predictive_scheduler *predictive =
|
||||
wl_container_of(scheduler, predictive, base);
|
||||
|
||||
predictive->pending_pre_render_ns = pre_render_duration_ns;
|
||||
predictive->pending_render_timer = render_timer;
|
||||
predictive->has_pending_feedback = true;
|
||||
}
|
||||
|
||||
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