compositor-drm: Add test-only mode to state application
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>
This commit is contained in:
@@ -260,6 +260,7 @@ enum drm_output_state_duplicate_mode {
|
|||||||
enum drm_state_apply_mode {
|
enum drm_state_apply_mode {
|
||||||
DRM_STATE_APPLY_SYNC, /**< state fully processed */
|
DRM_STATE_APPLY_SYNC, /**< state fully processed */
|
||||||
DRM_STATE_APPLY_ASYNC, /**< state pending event delivery */
|
DRM_STATE_APPLY_ASYNC, /**< state pending event delivery */
|
||||||
|
DRM_STATE_TEST_ONLY, /**< test if the state can be applied */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct drm_backend {
|
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_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
|
* 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:
|
case DRM_STATE_APPLY_ASYNC:
|
||||||
flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
|
flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
|
||||||
break;
|
break;
|
||||||
|
case DRM_STATE_TEST_ONLY:
|
||||||
|
flags |= DRM_MODE_ATOMIC_TEST_ONLY;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
|
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) {
|
if (ret != 0) {
|
||||||
weston_log("atomic: couldn't commit new state: %m\n");
|
weston_log("atomic: couldn't commit new state: %m\n");
|
||||||
goto out;
|
goto out;
|
||||||
@@ -2633,6 +2646,40 @@ out:
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
* 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
|
* applying KMS state to a device. Updates the state for all outputs in the
|
||||||
@@ -3271,6 +3318,7 @@ drm_output_propose_state(struct weston_output *output_base,
|
|||||||
struct weston_view *ev;
|
struct weston_view *ev;
|
||||||
pixman_region32_t surface_overlap, renderer_region, occluded_region;
|
pixman_region32_t surface_overlap, renderer_region, occluded_region;
|
||||||
bool planes_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY);
|
bool planes_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY);
|
||||||
|
int ret;
|
||||||
|
|
||||||
assert(!output->state_last);
|
assert(!output->state_last);
|
||||||
state = drm_output_state_duplicate(output->state_cur,
|
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(&renderer_region);
|
||||||
pixman_region32_fini(&occluded_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;
|
return state;
|
||||||
|
|
||||||
|
err:
|
||||||
|
drm_output_state_free(state);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|||||||
Reference in New Issue
Block a user