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:
Kenny Levinsen 2026-04-09 11:07:53 +00:00
commit 651f85cb81
34 changed files with 882 additions and 220 deletions

View file

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

View file

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

View file

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

View file

@ -0,0 +1,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

View file

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

View file

@ -224,6 +224,7 @@ struct wlr_scene_output {
struct wlr_output *output;
struct wl_list link; // wlr_scene.outputs
struct wlr_scene *scene;
struct wlr_frame_scheduler *frame_scheduler;
struct wlr_addon addon;
struct wlr_damage_ring damage_ring;
@ -258,7 +259,6 @@ struct wlr_scene_output {
struct wl_listener output_commit;
struct wl_listener output_damage;
struct wl_listener output_needs_frame;
struct wl_list damage_highlight_regions;
@ -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