The atomic API can allow us to test state before we apply it, to see if
it will be valid. Add support for this, which we will later use in
assign_planes to ensure our plane configuration is valid.

Signed-off-by: Daniel Stone <dani...@collabora.com>
Tested-by: Emre Ucan <eu...@de.adit-jv.com>
---
 libweston/compositor-drm.c | 176 ++++++++++++++++++++++++++++---------
 1 file changed, 136 insertions(+), 40 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 3e22140a8..9e062a7b3 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -220,6 +220,7 @@ enum drm_output_state_duplicate_mode {
 enum drm_state_apply_mode {
        DRM_STATE_APPLY_SYNC, /**< state fully processed */
        DRM_STATE_APPLY_ASYNC, /**< state pending event delivery */
+       DRM_STATE_TEST_ONLY, /**< test if the state can be applied */
 };
 
 struct drm_backend {
@@ -1724,6 +1725,7 @@ drm_pending_state_get_output(struct drm_pending_state 
*pending_state,
 }
 
 static int drm_pending_state_apply_sync(struct drm_pending_state *state);
+static int drm_pending_state_test(struct drm_pending_state *state);
 
 /**
  * Mark a drm_output_state (the output's last state) as complete. This handles
@@ -1848,13 +1850,16 @@ 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_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;
 
        /* Check the view spans exactly the output size, calculated in the
         * logical co-ordinate space. */
@@ -1879,11 +1884,18 @@ 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 (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;
        }
@@ -1903,10 +1915,24 @@ drm_output_prepare_scanout_view(struct drm_output_state 
*output_state,
            state->dest_h != (unsigned) output->base.current_mode->height)
                goto err;
 
+       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;
 }
 
@@ -1974,7 +2000,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) &&
@@ -1997,6 +2025,7 @@ drm_output_render(struct drm_output_state *state, 
pixman_region32_t *damage)
                return;
        }
 
+       drm_fb_unref(scanout_state->fb);
        scanout_state->fb = fb;
        scanout_state->output = output;
 
@@ -2513,9 +2542,16 @@ drm_pending_state_apply_atomic(struct drm_pending_state 
*pending_state,
        case DRM_STATE_APPLY_ASYNC:
                flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
                break;
+       case DRM_STATE_TEST_ONLY:
+               flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+               break;
        }
 
        ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
+
+       if (mode == DRM_STATE_TEST_ONLY)
+               return ret;
+
        if (ret != 0) {
                weston_log("atomic: couldn't commit new state: %m\n");
                goto out;
@@ -2543,6 +2579,22 @@ out:
  *
  * Unconditionally takes ownership of pending_state, and clears state_invalid.
  */
+static int
+drm_pending_state_test(struct drm_pending_state *pending_state)
+{
+#ifdef HAVE_DRM_ATOMIC
+       struct drm_backend *b = pending_state->backend;
+
+       if (b->atomic_modeset)
+               return drm_pending_state_apply_atomic(pending_state,
+                                                     DRM_STATE_TEST_ONLY);
+#endif
+
+       /* We have no way to test state before application on the legacy
+        * modesetting API, so just claim it succeeded. */
+       return 0;
+}
+
 static int
 drm_pending_state_apply(struct drm_pending_state *pending_state)
 {
@@ -2904,7 +2956,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;
@@ -2913,9 +2966,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;
-
-       if (b->sprites_are_broken)
-               return NULL;
+       int ret;
 
        fb = drm_fb_get_from_view(output_state, ev);
        if (!fb)
@@ -2954,31 +3005,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;
 }
 
 /**
@@ -3174,12 +3231,42 @@ drm_output_propose_state(struct weston_output 
*output_base,
        struct weston_view *ev;
        pixman_region32_t surface_overlap, renderer_region, occluded_region;
        bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
-       bool planes_ok = !b->sprites_are_broken;
+       bool planes_ok = false;
+       bool duplicate_renderer = false;
+       int ret;
+
+       /* We can only propose planes if we're in plane-only mode (i.e. just
+        * get as much as possible into planes and test at the end), or mixed
+        * mode, where we have our previous renderer buffer (and it's broadly
+        * compatible) to test with. If we don't have these things, then we
+        * don't know whether or not the planes will work, so we conservatively
+        * fall back to just using the renderer. */
+       if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
+               if (b->sprites_are_broken)
+                       return NULL;
+               planes_ok = true;
+       } else if (output->scanout_plane->state_cur->fb) {
+               struct drm_fb *scanout_fb =
+                       output->scanout_plane->state_cur->fb;
+               if ((scanout_fb->type == BUFFER_GBM_SURFACE ||
+                    scanout_fb->type == BUFFER_PIXMAN_DUMB) &&
+                   scanout_fb->width == output_base->current_mode->width &&
+                   scanout_fb->height == output_base->current_mode->height) {
+                       planes_ok = true;
+                       duplicate_renderer = true;
+               }
+       }
 
        assert(!output->state_last);
        state = drm_output_state_duplicate(output->state_cur,
                                           pending_state,
                                           DRM_OUTPUT_STATE_CLEAR_PLANES);
+       if (!planes_ok)
+               return state;
+
+       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:
@@ -3213,6 +3300,9 @@ drm_output_propose_state(struct weston_output 
*output_base,
                if (ev->output_mask != (1u << output->base.id))
                        force_renderer = true;
 
+               if (!ev->surface->buffer_ref.buffer)
+                       force_renderer = true;
+
                /* Ignore views we know to be totally occluded. */
                pixman_region32_init(&clipped_view);
                pixman_region32_intersect(&clipped_view,
@@ -3248,17 +3338,13 @@ drm_output_propose_state(struct weston_output 
*output_base,
                 * cursors_are_broken flag. */
                if (!force_renderer && !b->cursors_are_broken)
                        ps = drm_output_prepare_cursor_view(state, ev);
-
                if (!planes_ok || !drm_view_is_opaque(ev))
                        force_renderer = true;
 
-               if (!drm_view_is_opaque(ev))
-                       force_renderer = true;
-
                if (!force_renderer && !ps)
-                       ps = drm_output_prepare_scanout_view(state, ev);
+                       ps = drm_output_prepare_scanout_view(state, ev, mode);
                if (!force_renderer && !ps)
-                       ps = drm_output_prepare_overlay_view(state, ev);
+                       ps = drm_output_prepare_overlay_view(state, ev, mode);
 
                if (ps) {
 
@@ -3268,7 +3354,6 @@ drm_output_propose_state(struct weston_output 
*output_base,
                        pixman_region32_fini(&clipped_view);
                        continue;
                }
-
                if (!renderer_ok) {
                        pixman_region32_fini(&clipped_view);
                        goto err;
@@ -3282,6 +3367,11 @@ drm_output_propose_state(struct weston_output 
*output_base,
        pixman_region32_fini(&renderer_region);
        pixman_region32_fini(&occluded_region);
 
+       /* Check to see if this state will actually work. */
+       ret = drm_pending_state_test(state->pending_state);
+       if (ret != 0)
+               goto err;
+
        return state;
 
 err:
@@ -3302,8 +3392,14 @@ drm_assign_planes(struct weston_output *output_base, 
void *repaint_data)
        struct weston_view *ev;
        struct weston_plane *primary = &output_base->compositor->primary_plane;
 
+
        state = drm_output_propose_state(output_base, pending_state,
-                                        DRM_OUTPUT_PROPOSE_STATE_MIXED);
+                                        DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
+       if (!state)
+               state = drm_output_propose_state(output_base, pending_state,
+                                                
DRM_OUTPUT_PROPOSE_STATE_MIXED);
+
+       assert(state);
 
        wl_list_for_each(ev, &output_base->compositor->view_list, link) {
                struct drm_plane *target_plane = NULL;
-- 
2.17.1

_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to