compositor-drm: Add test-only mode to state application
authorDaniel Stone <daniels@collabora.com>
Thu, 8 Dec 2016 17:27:17 +0000 (17:27 +0000)
committerDaniel Stone <daniels@collabora.com>
Wed, 11 Jul 2018 11:49:53 +0000 (12:49 +0100)
The atomic API can allow us to test state before we apply it, to see if
it will be valid. Use this when we construct a plane configuration, to
see if it has a chance of ever working. If not, we can fail
assign_planes early.

This will be used in later patches to incrementally build state by
proposing and testing potential configurations one at a time.

Signed-off-by: Daniel Stone <daniels@collabora.com>
Reviewed-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
libweston/compositor-drm.c

index 4bf1858..d865848 100644 (file)
@@ -260,6 +260,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 {
@@ -1822,6 +1823,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
@@ -2610,9 +2612,20 @@ 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);
+
+       /* Test commits do not take ownership of the state; return
+        * without freeing here. */
+       if (mode == DRM_STATE_TEST_ONLY) {
+               drmModeAtomicFree(req);
+               return ret;
+       }
+
        if (ret != 0) {
                weston_log("atomic: couldn't commit new state: %m\n");
                goto out;
@@ -2634,6 +2647,40 @@ out:
 #endif
 
 /**
+ * Tests a pending state, to see if the kernel will accept the update as
+ * constructed.
+ *
+ * Using atomic modesetting, the kernel performs the same checks as it would
+ * on a real commit, returning success or failure without actually modifying
+ * the running state. It does not return -EBUSY if there are pending updates
+ * in flight, so states may be tested at any point, however this means a
+ * state which passed testing may fail on a real commit if the timing is not
+ * respected (e.g. committing before the previous commit has completed).
+ *
+ * Without atomic modesetting, we have no way to check, so we optimistically
+ * claim it will work.
+ *
+ * Unlike drm_pending_state_apply() and drm_pending_state_apply_sync(), this
+ * function does _not_ take ownership of pending_state, nor does it clear
+ * 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;
+}
+
+/**
  * Applies all of a pending_state asynchronously: the primary entry point for
  * applying KMS state to a device. Updates the state for all outputs in the
  * pending_state, as well as disabling any unclaimed outputs.
@@ -3271,6 +3318,7 @@ drm_output_propose_state(struct weston_output *output_base,
        struct weston_view *ev;
        pixman_region32_t surface_overlap, renderer_region, occluded_region;
        bool planes_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY);
+       int ret;
 
        assert(!output->state_last);
        state = drm_output_state_duplicate(output->state_cur,
@@ -3388,7 +3436,16 @@ 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:
+       drm_output_state_free(state);
+       return NULL;
 }
 
 static void