Add option to set timeout for liftoff_output_apply()

The default limit of 1ms might be too short if the compositor
calls liftoff_output_apply() early in the vblank cycle, or too
long if the refresh rate is high. The compositor is in a better
spot to decide how much time should be spent on atomic tests, so
add an option for that.

Closes: https://gitlab.freedesktop.org/emersion/libliftoff/-/issues/78
This commit is contained in:
Simon Ser 2024-05-14 13:59:39 +02:00
parent c63676eda7
commit 35c06e8995
12 changed files with 43 additions and 21 deletions

22
alloc.c
View file

@ -79,6 +79,7 @@ struct alloc_result {
int best_score;
struct timespec started_at;
int64_t timeout_ns;
/* per-output */
bool has_composition_layer;
@ -108,10 +109,10 @@ timespec_to_nsec(struct timespec ts)
return (int64_t)ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
}
static const int64_t ALLOC_TIMEOUT_NSEC = 1000 * 1000; // 1ms
static const int64_t DEFAULT_ALLOC_TIMEOUT_NSEC = 1000 * 1000; // 1ms
static bool
check_deadline(struct timespec start)
check_deadline(struct timespec start, int64_t timeout_ns)
{
struct timespec now;
int64_t deadline;
@ -121,7 +122,7 @@ check_deadline(struct timespec start)
return false;
}
deadline = timespec_to_nsec(start) + ALLOC_TIMEOUT_NSEC;
deadline = timespec_to_nsec(start) + timeout_ns;
return timespec_to_nsec(now) < deadline;
}
@ -478,7 +479,7 @@ output_choose_layers(struct liftoff_output *output, struct alloc_result *result,
continue;
}
if (!check_deadline(result->started_at)) {
if (!check_deadline(result->started_at, result->timeout_ns)) {
liftoff_log(LIFTOFF_DEBUG, "%s Deadline exceeded",
step->log_prefix);
break;
@ -887,17 +888,23 @@ non_composition_layers_length(struct liftoff_output *output)
int
liftoff_output_apply(struct liftoff_output *output, drmModeAtomicReq *req,
uint32_t flags)
uint32_t flags,
const struct liftoff_output_apply_options *options)
{
struct liftoff_device *device;
struct liftoff_plane *plane;
struct liftoff_layer *layer;
struct alloc_result result = {0};
struct alloc_step step = {0};
const struct liftoff_output_apply_options default_options = {0};
size_t i, candidate_planes;
int ret;
bool found_layer;
if (options == NULL) {
options = &default_options;
}
device = output->device;
update_layers_priority(device);
@ -960,6 +967,11 @@ liftoff_output_apply(struct liftoff_output *output, drmModeAtomicReq *req,
return -errno;
}
result.timeout_ns = options->timeout_ns;
if (result.timeout_ns == 0) {
result.timeout_ns = DEFAULT_ALLOC_TIMEOUT_NSEC;
}
/* For each plane, try to find a layer. Don't do it the other
* way around (ie. for each layer, try to find a plane) because
* some drivers want user-space to enable the primary plane

View file

@ -195,7 +195,7 @@ main(int argc, char *argv[])
flags = DRM_MODE_ATOMIC_NONBLOCK;
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(output, req, flags);
ret = liftoff_output_apply(output, req, flags, NULL);
if (ret != 0) {
perror("liftoff_output_apply");
return 1;

View file

@ -114,7 +114,7 @@ draw(void)
flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT;
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(output, req, flags);
ret = liftoff_output_apply(output, req, flags, NULL);
if (ret != 0) {
perror("liftoff_output_apply");
return false;

View file

@ -157,7 +157,7 @@ main(int argc, char *argv[])
flags = DRM_MODE_ATOMIC_NONBLOCK;
req = drmModeAtomicAlloc();
for (i = 0; i < outputs_len; i++) {
ret = liftoff_output_apply(outputs[i], req, flags);
ret = liftoff_output_apply(outputs[i], req, flags, NULL);
if (ret != 0) {
perror("liftoff_output_apply");
return 1;

View file

@ -130,7 +130,7 @@ main(int argc, char *argv[])
flags = DRM_MODE_ATOMIC_NONBLOCK;
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(output, req, flags);
ret = liftoff_output_apply(output, req, flags, NULL);
if (ret != 0) {
perror("liftoff_output_apply");
return 1;

View file

@ -62,19 +62,29 @@ liftoff_plane_destroy(struct liftoff_plane *plane);
uint32_t
liftoff_plane_get_id(struct liftoff_plane *plane);
/**
* Options for liftoff_output_apply().
*/
struct liftoff_output_apply_options {
/* Timeout in nanoseconds. If zero, a default timeout is used. */
int64_t timeout_ns;
};
/**
* Build a layer to plane mapping and append the plane configuration to req.
*
* Callers are expected to commit req afterwards and can figure out which
* layers need composition via liftoff_layer_needs_composition().
*
* flags is the atomic commit flags the caller intends to use.
* flags is the atomic commit flags the caller intends to use. If options is
* NULL, defaults are used.
*
* Zero is returned on success, negative errno on error.
*/
int
liftoff_output_apply(struct liftoff_output *output, drmModeAtomicReq *req,
uint32_t flags);
uint32_t flags,
const struct liftoff_output_apply_options *options);
/**
* Make the device manage a CRTC's planes.

View file

@ -104,7 +104,7 @@ main(int argc, char *argv[])
clock_gettime(CLOCK_MONOTONIC, &start);
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(output, req, 0);
ret = liftoff_output_apply(output, req, 0, NULL);
assert(ret == 0);
drmModeAtomicFree(req);

View file

@ -803,7 +803,7 @@ run_test(const struct test_case *test)
}
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(output, req, 0);
ret = liftoff_output_apply(output, req, 0, NULL);
assert(ret == 0);
ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
assert(ret == 0);
@ -875,7 +875,7 @@ test_basic(void)
liftoff_mock_plane_add_compatible_layer(mock_plane, layer);
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(output, req, 0);
ret = liftoff_output_apply(output, req, 0, NULL);
assert(ret == 0);
ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
assert(ret == 0);
@ -913,7 +913,7 @@ test_no_props_fail(void)
liftoff_mock_plane_add_compatible_layer(mock_plane, layer);
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(output, req, 0);
ret = liftoff_output_apply(output, req, 0, NULL);
assert(ret == 0);
ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
assert(ret == 0);
@ -959,7 +959,7 @@ test_composition_no_props(void)
liftoff_mock_plane_add_compatible_layer(mock_plane, layer_with_fb);
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(output, req, 0);
ret = liftoff_output_apply(output, req, 0, NULL);
assert(ret == 0);
ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
assert(ret == 0);

View file

@ -60,7 +60,7 @@ test_basic(void)
liftoff_layer_set_property(layer, "COLOR_RANGE", 0);
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(output, req, 0);
ret = liftoff_output_apply(output, req, 0, NULL);
assert(ret == 0);
ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
assert(ret == 0);

View file

@ -50,7 +50,7 @@ first_commit(struct context *ctx)
assert(ctx->commit_count == 0);
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(ctx->output, req, 0);
ret = liftoff_output_apply(ctx->output, req, 0, NULL);
assert(ret == 0);
ret = drmModeAtomicCommit(ctx->drm_fd, req, 0, NULL);
assert(ret == 0);
@ -72,7 +72,7 @@ second_commit(struct context *ctx, bool want_reuse_prev_alloc)
int ret;
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(ctx->output, req, 0);
ret = liftoff_output_apply(ctx->output, req, 0, NULL);
assert(ret == 0);
if (want_reuse_prev_alloc) {
/* The library should perform only one TEST_ONLY commit with the

View file

@ -76,7 +76,7 @@ main(int argc, char *argv[])
liftoff_layer_set_property(layer, "FB_ID", fbs[j % 2]);
ret = liftoff_output_apply(output, req, 0);
ret = liftoff_output_apply(output, req, 0, NULL);
assert(ret == 0);
ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
assert(ret == 0);

View file

@ -34,7 +34,7 @@ commit(int drm_fd, struct liftoff_output *output)
int ret;
req = drmModeAtomicAlloc();
ret = liftoff_output_apply(output, req, 0);
ret = liftoff_output_apply(output, req, 0, NULL);
assert(ret == 0);
ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
assert(ret == 0);