mirror of
				https://gitlab.freedesktop.org/wlroots/wlroots.git
				synced 2025-11-03 09:01:40 -05:00 
			
		
		
		
	wlr_scene: Debounce dmabuf feedback on scanout
Direct scanout can be enabled and disabled on a frame-by-frame basis, and so we could end up sending different feedback to a surface on every other frame. Reacting to new feedback is expensive, as the client may need to reallocate their swapchain. Debounce the state change a number of frames, for now set to 30, to avoid immediate reaction to scanout (or composition) that only lasts a few frames. A timer could be used instead, but it did not seem worth the complexity. What just want to know that the state has been stable across a reasonable number of samples, and a counter seems sufficient for that.
This commit is contained in:
		
							parent
							
								
									792bee9657
								
							
						
					
					
						commit
						c450991c4b
					
				
					 2 changed files with 63 additions and 21 deletions
				
			
		| 
						 | 
					@ -227,6 +227,14 @@ struct wlr_scene_output {
 | 
				
			||||||
		pixman_region32_t pending_commit_damage;
 | 
							pixman_region32_t pending_commit_damage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		uint8_t index;
 | 
							uint8_t index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/**
 | 
				
			||||||
 | 
							 * When scanout is applicable, we increment this every time a frame is rendered until
 | 
				
			||||||
 | 
							 * DMABUF_FEEDBACK_DEBOUNCE_FRAMES is hit to debounce the scanout dmabuf feedback. Likewise,
 | 
				
			||||||
 | 
							 * when scanout is no longer applicable, we decrement this until zero is hit to debounce
 | 
				
			||||||
 | 
							 * composition dmabuf feedback.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							uint8_t dmabuf_feedback_debounce;
 | 
				
			||||||
		bool prev_scanout;
 | 
							bool prev_scanout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		bool gamma_lut_changed;
 | 
							bool gamma_lut_changed;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,7 @@
 | 
				
			||||||
#include <wlr/xwayland/xwayland.h>
 | 
					#include <wlr/xwayland/xwayland.h>
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DMABUF_FEEDBACK_DEBOUNCE_FRAMES  30
 | 
				
			||||||
#define HIGHLIGHT_DAMAGE_FADEOUT_TIME   250
 | 
					#define HIGHLIGHT_DAMAGE_FADEOUT_TIME   250
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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) {
 | 
				
			||||||
| 
						 | 
					@ -1330,7 +1331,6 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct render_list_entry {
 | 
					struct render_list_entry {
 | 
				
			||||||
	struct wlr_scene_node *node;
 | 
						struct wlr_scene_node *node;
 | 
				
			||||||
	bool sent_dmabuf_feedback;
 | 
					 | 
				
			||||||
	bool highlight_transparent_region;
 | 
						bool highlight_transparent_region;
 | 
				
			||||||
	int x, y;
 | 
						int x, y;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -1856,33 +1856,47 @@ static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene,
 | 
				
			||||||
	wlr_linux_dmabuf_feedback_v1_finish(&feedback);
 | 
						wlr_linux_dmabuf_feedback_v1_finish(&feedback);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool scene_entry_try_direct_scanout(struct render_list_entry *entry,
 | 
					enum scene_direct_scanout_result {
 | 
				
			||||||
		struct wlr_output_state *state, const struct render_data *data) {
 | 
						// This scene node is not a candidate for scanout
 | 
				
			||||||
 | 
						SCANOUT_INELIGIBLE,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This scene node is a candidate for scanout, but is currently
 | 
				
			||||||
 | 
						// incompatible due to e.g. buffer mismatch, and if possible we'd like to
 | 
				
			||||||
 | 
						// resolve this incompatibility.
 | 
				
			||||||
 | 
						SCANOUT_CANDIDATE,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Scanout is successful.
 | 
				
			||||||
 | 
						SCANOUT_SUCCESS,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static enum scene_direct_scanout_result scene_entry_try_direct_scanout(
 | 
				
			||||||
 | 
							struct render_list_entry *entry, struct wlr_output_state *state,
 | 
				
			||||||
 | 
							const struct render_data *data) {
 | 
				
			||||||
	struct wlr_scene_output *scene_output = data->output;
 | 
						struct wlr_scene_output *scene_output = data->output;
 | 
				
			||||||
	struct wlr_scene_node *node = entry->node;
 | 
						struct wlr_scene_node *node = entry->node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!scene_output->scene->direct_scanout) {
 | 
						if (!scene_output->scene->direct_scanout) {
 | 
				
			||||||
		return false;
 | 
							return SCANOUT_INELIGIBLE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (node->type != WLR_SCENE_NODE_BUFFER) {
 | 
						if (node->type != WLR_SCENE_NODE_BUFFER) {
 | 
				
			||||||
		return false;
 | 
							return SCANOUT_INELIGIBLE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (state->committed & (WLR_OUTPUT_STATE_MODE |
 | 
						if (state->committed & (WLR_OUTPUT_STATE_MODE |
 | 
				
			||||||
			WLR_OUTPUT_STATE_ENABLED |
 | 
								WLR_OUTPUT_STATE_ENABLED |
 | 
				
			||||||
			WLR_OUTPUT_STATE_RENDER_FORMAT)) {
 | 
								WLR_OUTPUT_STATE_RENDER_FORMAT)) {
 | 
				
			||||||
		// Legacy DRM will explode if we try to modeset with a direct scanout buffer
 | 
							// Legacy DRM will explode if we try to modeset with a direct scanout buffer
 | 
				
			||||||
		return false;
 | 
							return SCANOUT_INELIGIBLE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!wlr_output_is_direct_scanout_allowed(scene_output->output)) {
 | 
						if (!wlr_output_is_direct_scanout_allowed(scene_output->output)) {
 | 
				
			||||||
		return false;
 | 
							return SCANOUT_INELIGIBLE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
 | 
						struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
 | 
				
			||||||
	if (buffer->buffer == NULL) {
 | 
						if (buffer->buffer == NULL) {
 | 
				
			||||||
		return false;
 | 
							return SCANOUT_INELIGIBLE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// The native size of the buffer after any transform is applied
 | 
						// The native size of the buffer after any transform is applied
 | 
				
			||||||
| 
						 | 
					@ -1895,23 +1909,26 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (buffer->transform != data->transform) {
 | 
						if (buffer->transform != data->transform) {
 | 
				
			||||||
		return false;
 | 
							return SCANOUT_INELIGIBLE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (buffer->primary_output == scene_output) {
 | 
						// We want to ensure optimal buffer selection, but as direct-scanout can be enabled and disabled
 | 
				
			||||||
 | 
						// on a frame-by-frame basis, we wait for a few frames to send the new format recommendations.
 | 
				
			||||||
 | 
						// Maybe we should only send feedback in this case if tests fail.
 | 
				
			||||||
 | 
						if (scene_output->dmabuf_feedback_debounce >= DMABUF_FEEDBACK_DEBOUNCE_FRAMES
 | 
				
			||||||
 | 
								&& buffer->primary_output == scene_output) {
 | 
				
			||||||
		struct wlr_linux_dmabuf_feedback_v1_init_options options = {
 | 
							struct wlr_linux_dmabuf_feedback_v1_init_options options = {
 | 
				
			||||||
			.main_renderer = scene_output->output->renderer,
 | 
								.main_renderer = scene_output->output->renderer,
 | 
				
			||||||
			.scanout_primary_output = scene_output->output,
 | 
								.scanout_primary_output = scene_output->output,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options);
 | 
							scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options);
 | 
				
			||||||
		entry->sent_dmabuf_feedback = true;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct wlr_output_state pending;
 | 
						struct wlr_output_state pending;
 | 
				
			||||||
	wlr_output_state_init(&pending);
 | 
						wlr_output_state_init(&pending);
 | 
				
			||||||
	if (!wlr_output_state_copy(&pending, state)) {
 | 
						if (!wlr_output_state_copy(&pending, state)) {
 | 
				
			||||||
		return false;
 | 
							return SCANOUT_CANDIDATE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!wlr_fbox_empty(&buffer->src_box) &&
 | 
						if (!wlr_fbox_empty(&buffer->src_box) &&
 | 
				
			||||||
| 
						 | 
					@ -1936,10 +1953,9 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry,
 | 
				
			||||||
	if (buffer->wait_timeline != NULL) {
 | 
						if (buffer->wait_timeline != NULL) {
 | 
				
			||||||
		wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point);
 | 
							wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!wlr_output_test_state(scene_output->output, &pending)) {
 | 
						if (!wlr_output_test_state(scene_output->output, &pending)) {
 | 
				
			||||||
		wlr_output_state_finish(&pending);
 | 
							wlr_output_state_finish(&pending);
 | 
				
			||||||
		return false;
 | 
							return SCANOUT_CANDIDATE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_output_state_copy(state, &pending);
 | 
						wlr_output_state_copy(state, &pending);
 | 
				
			||||||
| 
						 | 
					@ -1950,7 +1966,7 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry,
 | 
				
			||||||
		.direct_scanout = true,
 | 
							.direct_scanout = true,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event);
 | 
						wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event);
 | 
				
			||||||
	return true;
 | 
						return SCANOUT_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool wlr_scene_output_needs_frame(struct wlr_scene_output *scene_output) {
 | 
					bool wlr_scene_output_needs_frame(struct wlr_scene_output *scene_output) {
 | 
				
			||||||
| 
						 | 
					@ -2133,17 +2149,31 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
 | 
				
			||||||
	// - 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
 | 
				
			||||||
	bool scanout = options->color_transform == NULL &&
 | 
						enum scene_direct_scanout_result scanout = SCANOUT_INELIGIBLE;
 | 
				
			||||||
		list_len == 1 && debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT &&
 | 
						if (options->color_transform == NULL && list_len == 1
 | 
				
			||||||
		scene_entry_try_direct_scanout(&list_data[0], state, &render_data);
 | 
								&& debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
 | 
				
			||||||
 | 
							scanout = scene_entry_try_direct_scanout(&list_data[0], state, &render_data);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (scanout == SCANOUT_INELIGIBLE) {
 | 
				
			||||||
 | 
							if (scene_output->dmabuf_feedback_debounce > 0) {
 | 
				
			||||||
 | 
								// We cannot scan out, so count down towards sending composition dmabuf feedback
 | 
				
			||||||
 | 
								scene_output->dmabuf_feedback_debounce--;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (scene_output->dmabuf_feedback_debounce < DMABUF_FEEDBACK_DEBOUNCE_FRAMES) {
 | 
				
			||||||
 | 
							// We either want to scan out or successfully scanned out, so count up towards sending
 | 
				
			||||||
 | 
							// scanout dmabuf feedback
 | 
				
			||||||
 | 
							scene_output->dmabuf_feedback_debounce++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool scanout_success = scanout == SCANOUT_SUCCESS;
 | 
				
			||||||
	if (scene_output->prev_scanout != scanout) {
 | 
						if (scene_output->prev_scanout != scanout) {
 | 
				
			||||||
		scene_output->prev_scanout = scanout;
 | 
							scene_output->prev_scanout = scanout;
 | 
				
			||||||
		wlr_log(WLR_DEBUG, "Direct scan-out %s",
 | 
							wlr_log(WLR_DEBUG, "Direct scan-out %s",
 | 
				
			||||||
			scanout ? "enabled" : "disabled");
 | 
								scanout ? "enabled" : "disabled");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (scanout) {
 | 
						if (scanout_success) {
 | 
				
			||||||
		scene_output_state_attempt_gamma(scene_output, state);
 | 
							scene_output_state_attempt_gamma(scene_output, state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (timer) {
 | 
							if (timer) {
 | 
				
			||||||
| 
						 | 
					@ -2249,7 +2279,11 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
 | 
				
			||||||
		if (entry->node->type == WLR_SCENE_NODE_BUFFER) {
 | 
							if (entry->node->type == WLR_SCENE_NODE_BUFFER) {
 | 
				
			||||||
			struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node);
 | 
								struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (buffer->primary_output == scene_output && !entry->sent_dmabuf_feedback) {
 | 
								// Direct scanout counts up to DMABUF_FEEDBACK_DEBOUNCE_FRAMES before sending new dmabuf
 | 
				
			||||||
 | 
								// feedback, and on composition we wait until it hits zero again. If we knew that an
 | 
				
			||||||
 | 
								// entry could never be a scanout candidate, we could send feedback to it
 | 
				
			||||||
 | 
								// unconditionally without debounce, but for now it is all or nothing
 | 
				
			||||||
 | 
								if (scene_output->dmabuf_feedback_debounce == 0 && buffer->primary_output == scene_output) {
 | 
				
			||||||
				struct wlr_linux_dmabuf_feedback_v1_init_options options = {
 | 
									struct wlr_linux_dmabuf_feedback_v1_init_options options = {
 | 
				
			||||||
					.main_renderer = output->renderer,
 | 
										.main_renderer = output->renderer,
 | 
				
			||||||
					.scanout_primary_output = NULL,
 | 
										.scanout_primary_output = NULL,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue