wlr_frame_scheduler: Add predictive frame scheduler

The predictive frame scheduler builds upon the presentation scheduler,
adding a delay based on the measured CPU and GPU time required to
prepare a frame to place the frame event as close as safely possible to
the commit deadline.
This commit is contained in:
Kenny Levinsen 2026-04-06 16:42:02 +00:00
parent e70de3ffaa
commit df562a8e70
5 changed files with 246 additions and 48 deletions

View file

@ -10,9 +10,11 @@
#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;
@ -65,4 +67,23 @@ void wlr_frame_scheduler_schedule_frame(struct wlr_frame_scheduler *scheduler);
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

@ -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 */
@ -601,8 +598,6 @@ 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.
@ -629,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