[PATCH v20 08/10] compositor-drm: Incrementally test plane states in mixed mode

Daniel Stone daniels at collabora.com
Wed Jul 11 10:41:32 UTC 2018


In the plane-only mode, we try to place every view on a hardware plane,
and fail if we can't do this. This requires a full walk of the scene
graph to come up with a complete configuration in order to be able to
test.

In mixed mode, we know at least some visible views will fail to be
promoted to planes and must be composited via the renderer. In order to
still use some planes where possible, we use atomic modesetting's
test-only mode to incrementally test configurations.

We know that the renderer output will always be visible, and because it
is the renderer, that it will be occupying the scanout plane underneath
everything else. The actual renderer buffer doesn't materialise until
after assign_planes, because it cannot know what to render until then.

However, in order to test whether a configuration is valid, we need the
renderer buffer in the scanout plane. For testing, we fake this by
temporarily stealing the old buffer - if it seems sufficiently
compatible - and placing it in the state we construct. This is used to
test whether or not a renderer buffer will work with the addition of
overlay planes.

Doing this incremental testing will allow us to enable plane usage for
atomic by default, since we know ahead of time that our chosen plane
configuration will work.

Signed-off-by: Daniel Stone <daniels at collabora.com>
---
 libweston/compositor-drm.c | 133 ++++++++++++++++++++++++++++---------
 1 file changed, 102 insertions(+), 31 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d4555da78..d664a8df5 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1949,14 +1949,17 @@ enum drm_output_propose_state_mode {
 
 static struct drm_plane_state *
 drm_output_prepare_scanout_view(struct drm_output_state *output_state,
-				struct weston_view *ev)
+				struct weston_view *ev,
+				enum drm_output_propose_state_mode mode)
 {
 	struct drm_output *output = output_state->output;
 	struct drm_backend *b = to_drm_backend(output->base.compositor);
 	struct drm_plane *scanout_plane = output->scanout_plane;
 	struct drm_plane_state *state;
+	struct drm_plane_state *state_old = NULL;
 	struct drm_fb *fb;
 	pixman_box32_t *extents;
+	int ret;
 
 	assert(!b->sprites_are_broken);
 
@@ -1983,11 +1986,20 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
 	}
 
 	state = drm_output_state_get_plane(output_state, scanout_plane);
-	if (state->fb) {
-		/* If there is already a framebuffer on the scanout plane,
-		 * a client view has already been placed on the scanout
-		 * view. In that case, do not free or put back the state,
-		 * but just leave it in place and quietly exit. */
+
+	/* Check if we've already placed a buffer on this plane; if there's a
+	 * buffer there but it comes from GBM, then it's the result of
+	 * drm_output_propose_state placing it here for testing purposes.
+	 * If we already have another client buffer, then we can't use this
+	 * plane. */
+	if (state->fb &&
+	    (state->fb->type == BUFFER_GBM_SURFACE ||
+	     state->fb->type == BUFFER_PIXMAN_DUMB)) {
+		state_old = calloc(1, sizeof(*state_old));
+		memcpy(state_old, state, sizeof(*state_old));
+		state_old->output_state = NULL;
+		wl_list_init(&state_old->link);
+	} else if (state->fb) {
 		drm_fb_unref(fb);
 		return NULL;
 	}
@@ -2007,10 +2019,26 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
 	    state->dest_h != (unsigned) output->base.current_mode->height)
 		goto err;
 
+	/* In plane-only mode, we don't need to test the state now, as we
+	 * will only test it once at the end. */
+	if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
+		drm_plane_state_free(state_old, false);
+		return state;
+	}
+
+	ret = drm_pending_state_test(output_state->pending_state);
+	if (ret != 0)
+		goto err;
+
+	drm_plane_state_free(state_old, false);
 	return state;
 
 err:
-	drm_plane_state_put_back(state);
+	drm_plane_state_free(state, false);
+	if (state_old) {
+		wl_list_insert(&output_state->plane_list, &state_old->link);
+		state_old->output_state = output_state;
+	}
 	return NULL;
 }
 
@@ -2078,7 +2106,9 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
 	 * want to render. */
 	scanout_state = drm_output_state_get_plane(state,
 						   output->scanout_plane);
-	if (scanout_state->fb)
+	if (scanout_state->fb &&
+	    scanout_state->fb->type != BUFFER_GBM_SURFACE &&
+	    scanout_state->fb->type != BUFFER_PIXMAN_DUMB)
 		return;
 
 	if (!pixman_region32_not_empty(damage) &&
@@ -2101,6 +2131,8 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
 		return;
 	}
 
+	/* Unref an old renderer FB put there by drm_output_propose_state. */
+	drm_fb_unref(scanout_state->fb);
 	scanout_state->fb = fb;
 	scanout_state->output = output;
 
@@ -3040,7 +3072,8 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
 
 static struct drm_plane_state *
 drm_output_prepare_overlay_view(struct drm_output_state *output_state,
-				struct weston_view *ev)
+				struct weston_view *ev,
+				enum drm_output_propose_state_mode mode)
 {
 	struct drm_output *output = output_state->output;
 	struct weston_compositor *ec = output->base.compositor;
@@ -3049,6 +3082,7 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
 	struct drm_plane_state *state = NULL;
 	struct drm_fb *fb;
 	unsigned int i;
+	int ret;
 
 	assert(!b->sprites_are_broken);
 
@@ -3089,31 +3123,37 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
 			continue;
 		}
 
-		break;
-	}
+		state->ev = ev;
+		state->output = output;
+		drm_plane_state_coords_for_view(state, ev);
+		if (state->src_w != state->dest_w << 16 ||
+		    state->src_h != state->dest_h << 16) {
+			drm_plane_state_put_back(state);
+			continue;
+		}
 
-	/* No sprites available */
-	if (!state) {
-		drm_fb_unref(fb);
-		return NULL;
-	}
+		/* We hold one reference for the lifetime of this function;
+		 * from calling drm_fb_get_from_view, to the out label where
+		 * we unconditionally drop the reference. So, we take another
+		 * reference here to live within the state. */
+		state->fb = drm_fb_ref(fb);
 
-	state->fb = fb;
-	state->ev = ev;
-	state->output = output;
+		/* In planes-only mode, we don't have an incremental state to
+		 * test against, so we just hope it'll work. */
+		if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)
+			goto out;
 
-	if (!drm_plane_state_coords_for_view(state, ev))
-		goto err;
+		ret = drm_pending_state_test(output_state->pending_state);
+		if (ret == 0)
+			goto out;
 
-	if (state->src_w != state->dest_w << 16 ||
-	    state->src_h != state->dest_h << 16)
-		goto err;
+		drm_plane_state_put_back(state);
+		state = NULL;
+	}
 
+out:
+	drm_fb_unref(fb);
 	return state;
-
-err:
-	drm_plane_state_put_back(state);
-	return NULL;
 }
 
 /**
@@ -3309,15 +3349,46 @@ drm_output_propose_state(struct weston_output *output_base,
 	struct drm_output_state *state;
 	struct weston_view *ev;
 	pixman_region32_t surface_overlap, renderer_region, occluded_region;
-	bool planes_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY);
 	bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
+	bool planes_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY);
+	bool duplicate_renderer = false;
 	int ret;
 
+	/* We implement mixed mode by progressively creating and testing
+	 * incremental states, of scanout + overlay + cursor. Since we
+	 * walk our views top to bottom, the scanout plane is last, however
+	 * we always need it in our scene for the test modeset to be
+	 * meaningful. To do this, we steal a reference to the last
+	 * renderer framebuffer we have, if we think it's basically
+	 * compatible. If we don't have that, then we conservatively fall
+	 * back to only using the renderer for this repaint. */
+	if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
+		struct drm_fb *scanout_fb =
+			output->scanout_plane->state_cur->fb;
+
+		if (!scanout_fb)
+			return NULL;
+
+		if (scanout_fb->type != BUFFER_GBM_SURFACE &&
+		    scanout_fb->type != BUFFER_PIXMAN_DUMB)
+			return NULL;
+
+		if (scanout_fb->width != output_base->current_mode->width ||
+		    scanout_fb->height != output_base->current_mode->height)
+			return NULL;
+
+		duplicate_renderer = true;
+	}
+
 	assert(!output->state_last);
 	state = drm_output_state_duplicate(output->state_cur,
 					   pending_state,
 					   DRM_OUTPUT_STATE_CLEAR_PLANES);
 
+	if (duplicate_renderer)
+		drm_plane_state_duplicate(state,
+					  output->scanout_plane->state_cur);
+
 	/*
 	 * Find a surface for each sprite in the output using some heuristics:
 	 * 1) size
@@ -3408,9 +3479,9 @@ drm_output_propose_state(struct weston_output *output_base,
 		}
 
 		if (!ps && !force_renderer)
-			ps = drm_output_prepare_scanout_view(state, ev);
+			ps = drm_output_prepare_scanout_view(state, ev, mode);
 		if (!ps && !force_renderer)
-			ps = drm_output_prepare_overlay_view(state, ev);
+			ps = drm_output_prepare_overlay_view(state, ev, mode);
 
 		if (ps) {
 			/* If we have been assigned to an overlay or scanout
-- 
2.17.1



More information about the wayland-devel mailing list