Add wl_surface.frame based frame scheduler

This commit is contained in:
Rose Hudson 2023-08-10 12:29:18 +01:00 committed by Kenny Levinsen
parent 98cbf09f8a
commit ad145dce86
4 changed files with 86 additions and 0 deletions

View file

@ -581,6 +581,11 @@ bool wlr_backend_is_wl(const struct wlr_backend *b) {
return b->impl == &backend_impl; 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) { 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); struct wlr_wl_backend *wl = wl_container_of(listener, wl, event_loop_destroy);
backend_destroy(&wl->backend); backend_destroy(&wl->backend);

View file

@ -48,6 +48,11 @@ struct wlr_output *wlr_wl_output_create_from_surface(struct wlr_backend *backend
*/ */
bool wlr_backend_is_wl(const struct wlr_backend *backend); 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. * Check whether the provided input device is a Wayland input device.
*/ */

View file

@ -37,6 +37,11 @@ struct wlr_frame_scheduler {
* render loop, it emits the frame signal when the compositor's event loop is idle. * 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); 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);
/** /**
* Creates an appropriate frame scheduler for the given output's backend capabilities. * Creates an appropriate frame scheduler for the given output's backend capabilities.

View file

@ -1,11 +1,15 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <types/wlr_output.h>
#include <wayland-client-protocol.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wayland-util.h> #include <wayland-util.h>
#include <wlr/backend/wayland.h>
#include <wlr/interfaces/wlr_frame_scheduler.h> #include <wlr/interfaces/wlr_frame_scheduler.h>
#include <wlr/interfaces/wlr_output.h> #include <wlr/interfaces/wlr_output.h>
#include <wlr/types/wlr_frame_scheduler.h> #include <wlr/types/wlr_frame_scheduler.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include <wlr/util/log.h>
void wlr_frame_scheduler_schedule_frame(struct wlr_frame_scheduler *scheduler) { void wlr_frame_scheduler_schedule_frame(struct wlr_frame_scheduler *scheduler) {
scheduler->needs_frame = true; scheduler->needs_frame = true;
@ -149,6 +153,73 @@ struct wlr_frame_scheduler *wlr_present_scheduler_create(struct wlr_output *outp
return &scheduler->base.base; 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;
}
struct wlr_frame_scheduler *wlr_frame_scheduler_autocreate(struct wlr_output *output) { 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);
}
return wlr_present_scheduler_create(output); return wlr_present_scheduler_create(output);
} }