diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 6a0dd0455..8cbce5047 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -248,6 +248,8 @@ struct wlr_output { int attach_render_locks; // number of locks forcing rendering + int frames_since_locked; + struct wl_list cursors; // wlr_output_cursor.link struct wlr_output_cursor *hardware_cursor; struct wlr_swapchain *cursor_swapchain; diff --git a/types/output/output.c b/types/output/output.c index 1fb0347a0..5e22f4d01 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -341,6 +341,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, .transform = WL_OUTPUT_TRANSFORM_NORMAL, .scale = 1, .commit_seq = 0, + .frames_since_locked = 0, }; wl_list_init(&output->modes); diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index a06b641e3..a3807e412 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -30,6 +30,15 @@ #define DMABUF_FEEDBACK_DEBOUNCE_FRAMES 30 #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) { 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); + 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 are confident that the screen isn't being captured // - There is only one entry in the render list // - There are no color transforms that need to be applied // - Damage highlight debugging is not enabled 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) { scanout_result = scene_entry_try_direct_scanout(&list_data[0], state, &render_data); }