Merge branch 'fix_direct_scanout_stutter' into 'master'

Draft: wlr_scene: fix fullscreen app stuttering when screensharing

See merge request wlroots/wlroots!5173
This commit is contained in:
Gabriel Ford 2025-10-17 23:58:29 +00:00
commit 28d9d76d8e
3 changed files with 20 additions and 1 deletions

View file

@ -248,6 +248,8 @@ struct wlr_output {
int attach_render_locks; // number of locks forcing rendering int attach_render_locks; // number of locks forcing rendering
int frames_since_locked;
struct wl_list cursors; // wlr_output_cursor.link struct wl_list cursors; // wlr_output_cursor.link
struct wlr_output_cursor *hardware_cursor; struct wlr_output_cursor *hardware_cursor;
struct wlr_swapchain *cursor_swapchain; struct wlr_swapchain *cursor_swapchain;

View file

@ -341,6 +341,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
.transform = WL_OUTPUT_TRANSFORM_NORMAL, .transform = WL_OUTPUT_TRANSFORM_NORMAL,
.scale = 1, .scale = 1,
.commit_seq = 0, .commit_seq = 0,
.frames_since_locked = 0,
}; };
wl_list_init(&output->modes); wl_list_init(&output->modes);

View file

@ -30,6 +30,15 @@
#define DMABUF_FEEDBACK_DEBOUNCE_FRAMES 30 #define DMABUF_FEEDBACK_DEBOUNCE_FRAMES 30
#define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250 #define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250
// wlroot's implementation of wlr_screencopy applys a lock for a brief duration
// every time a frame is captured. If an output is eligible for direct scanout
// then this will result in direct scanout being rapidly toggled on and off
// since the lock count constantly flips between 0 and 1. On some hardware this
// causes stuttering every time direct scanout is enabled then disabled.
//
// To mitigate this we wait for there to be 0 locks for 120 frames so that we
// can be relatively confident that screen recording has stopped.
#define SCANOUT_UNLOCK_FRAMES 120
struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) { struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) {
assert(node->type == WLR_SCENE_NODE_TREE); assert(node->type == WLR_SCENE_NODE_TREE);
@ -2240,12 +2249,19 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
wlr_output_state_set_damage(state, &scene_output->pending_commit_damage); wlr_output_state_set_damage(state, &scene_output->pending_commit_damage);
if (output->attach_render_locks > 0) {
output->frames_since_locked = 0;
} else if (output->attach_render_locks <= SCANOUT_UNLOCK_FRAMES) {
output->frames_since_locked++;
}
// We only want to try direct scanout if: // We only want to try direct scanout if:
// - We are confident that the screen isn't being captured
// - There is only one entry in the render list // - There is only one entry in the render list
// - There are no color transforms that need to be applied // - There are no color transforms that need to be applied
// - Damage highlight debugging is not enabled // - Damage highlight debugging is not enabled
enum scene_direct_scanout_result scanout_result = SCANOUT_INELIGIBLE; enum scene_direct_scanout_result scanout_result = SCANOUT_INELIGIBLE;
if (options->color_transform == NULL && list_len == 1 if (output->frames_since_locked > SCANOUT_UNLOCK_FRAMES
&& options->color_transform == NULL && list_len == 1
&& debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { && debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
scanout_result = scene_entry_try_direct_scanout(&list_data[0], state, &render_data); scanout_result = scene_entry_try_direct_scanout(&list_data[0], state, &render_data);
} }