From 5ee760b853b29464afc0ba3b17fd7cf95c1fc67a Mon Sep 17 00:00:00 2001
From: Leandro Ribeiro <leandro.ribeiro@collabora.com>
Date: Tue, 24 Sep 2024 11:23:50 -0300
Subject: [PATCH] drm: avoid dma-buf feedback endless loop

Currently we have the following situation: we add a scanout tranche
because if the client re-allocates with another format/modifier, the
chances of being placed in a DRM/KMS plane is higher.

But then we run out of overlay planes. So we remove the scanout tranche,
because the format/modifier available in the renderer tranche are
optimal for rendering.

Now Weston detects again that the format/modifier is what may be
avoiding the view being place in a plane, re-adding the scanout tranche.
And we have an endless loop.

To avoid this, let's accumulate the reasons why placing the view in a
place failed. So if we detect that we don't have planes available, no
matter the format/modifier, we won't add the scanout tranche.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>

Upstream-Status: Backport
---
 libweston/backend-drm/state-propose.c | 45 +++++++++++----------------
 1 file changed, 18 insertions(+), 27 deletions(-)

diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c
index 30b287e1..f4008dc3 100644
--- a/libweston/backend-drm/state-propose.c
+++ b/libweston/backend-drm/state-propose.c
@@ -394,6 +394,7 @@ drm_output_find_plane_for_view(struct drm_output_state *state,
 
 	bool view_matches_entire_output, scanout_has_view_assigned;
 	uint32_t possible_plane_mask = 0;
+	bool any_candidate_picked = false;
 
 	pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE;
 
@@ -408,23 +409,19 @@ drm_output_find_plane_for_view(struct drm_output_state *state,
 	if (buffer->type == WESTON_BUFFER_SOLID) {
 		pnode->try_view_on_plane_failure_reasons |=
 			FAILURE_REASONS_BUFFER_TYPE;
-		return NULL;
 	} else if (buffer->type == WESTON_BUFFER_SHM) {
-		if (!output->cursor_plane || device->cursors_are_broken) {
+		if (!output->cursor_plane || device->cursors_are_broken)
 			pnode->try_view_on_plane_failure_reasons |=
 				FAILURE_REASONS_BUFFER_TYPE;
-			return NULL;
-		}
 
-		/* Even though this is a SHM buffer, pixel_format stores the
-		 * format code as DRM FourCC */
+		/* Even though this is a SHM buffer, pixel_format stores
+		 * the format code as DRM FourCC */
 		if (buffer->pixel_format->format != DRM_FORMAT_ARGB8888) {
 			drm_debug(b, "\t\t\t\t[view] not placing view %p on "
-			             "plane; SHM buffers must be ARGB8888 for "
+				     "plane; SHM buffers must be ARGB8888 for "
 				     "cursor view\n", ev);
 			pnode->try_view_on_plane_failure_reasons |=
 				FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
-			return NULL;
 		}
 
 		if (buffer->width > device->cursor_width ||
@@ -434,10 +431,10 @@ drm_output_find_plane_for_view(struct drm_output_state *state,
 				     ev, buffer->width, buffer->height);
 			pnode->try_view_on_plane_failure_reasons |=
 				FAILURE_REASONS_BUFFER_TOO_BIG;
-			return NULL;
 		}
 
-		possible_plane_mask = (1 << output->cursor_plane->plane_idx);
+		if (pnode->try_view_on_plane_failure_reasons == FAILURE_REASONS_NONE)
+			possible_plane_mask = (1 << output->cursor_plane->plane_idx);
 	} else {
 		if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY) {
 			drm_debug(b, "\t\t\t\t[view] not assigning view %p "
@@ -453,20 +450,16 @@ drm_output_find_plane_for_view(struct drm_output_state *state,
 				possible_plane_mask |= 1 << plane->plane_idx;
 		}
 
-		if (!possible_plane_mask) {
+		if (!possible_plane_mask)
 			pnode->try_view_on_plane_failure_reasons |=
 				FAILURE_REASONS_INCOMPATIBLE_TRANSFORM;
-			return NULL;
-		}
 
 		fb = drm_fb_get_from_paint_node(state, pnode);
-		if (!fb) {
+		if (fb)
+			possible_plane_mask &= fb->plane_mask;
+		else
 			drm_debug(b, "\t\t\t[view] couldn't get FB for view: 0x%lx\n",
-				  (unsigned long) pnode->try_view_on_plane_failure_reasons);
-			return NULL;
-		}
-
-		possible_plane_mask &= fb->plane_mask;
+				     (unsigned long) pnode->try_view_on_plane_failure_reasons);
 	}
 
 	view_matches_entire_output =
@@ -494,7 +487,6 @@ drm_output_find_plane_for_view(struct drm_output_state *state,
 			assert(plane == output->cursor_plane);
 			break;
 		case WDRM_PLANE_TYPE_PRIMARY:
-			assert(fb);
 			if (plane != output->scanout_plane)
 				continue;
 			if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)
@@ -503,7 +495,6 @@ drm_output_find_plane_for_view(struct drm_output_state *state,
 				continue;
 			break;
 		case WDRM_PLANE_TYPE_OVERLAY:
-			assert(fb);
 			assert(mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY);
 			/* if the view covers the whole output, put it in the
 			 * scanout plane, not overlay */
@@ -570,6 +561,7 @@ drm_output_find_plane_for_view(struct drm_output_state *state,
 		else
 			zpos = MIN(current_lowest_zpos - 1, plane->zpos_max);
 
+		any_candidate_picked = true;
 		drm_debug(b, "\t\t\t\t[plane] plane %d picked "
 			     "from candidate list, type: %s\n",
 			     plane->plane_id, p_name);
@@ -577,9 +569,10 @@ drm_output_find_plane_for_view(struct drm_output_state *state,
 		if (plane->type == WDRM_PLANE_TYPE_CURSOR) {
 			ps = drm_output_prepare_cursor_paint_node(state, pnode, zpos);
 		} else {
-			ps = drm_output_try_paint_node_on_plane(plane, state,
-								pnode, mode,
-								fb, zpos);
+			if (fb)
+				ps = drm_output_try_paint_node_on_plane(plane, state,
+									pnode, mode,
+									fb, zpos);
 		}
 
 		if (ps) {
@@ -593,11 +586,9 @@ drm_output_find_plane_for_view(struct drm_output_state *state,
 			FAILURE_REASONS_PLANES_REJECTED;
 	}
 
-	if (!ps &&
-	    pnode->try_view_on_plane_failure_reasons == FAILURE_REASONS_NONE) {
+	if (!any_candidate_picked)
 		pnode->try_view_on_plane_failure_reasons |=
 			FAILURE_REASONS_NO_PLANES_AVAILABLE;
-	}
 
 	/* if we have a plane state, it has its own ref to the fb; if not then
 	 * we drop ours here */
