Replace liftoff_device_apply with liftoff_output_apply

Compositors need to drive multiple connectors, each with its own vblank timings.
For each device, there's one separate rendering loop per output.

It's not possible to call liftoff_device_apply each time we want to submit a new
frame to one output, because this could touch another's output state, submitting
a new frame there in the process. When the other output will submit a new frame,
it'll get EBUSY (can't submit two frames without waiting for vblank).

Closes: https://github.com/emersion/libliftoff/issues/21
This commit is contained in:
Simon Ser 2019-10-19 14:02:54 +03:00
parent ef75ba26e0
commit ceb4a1ff9e
No known key found for this signature in database
GPG key ID: 0FDE7BE0E88F5E48
11 changed files with 83 additions and 90 deletions

View file

@ -36,8 +36,8 @@ liftoff_layer_set_property(layer, "FB_ID", fb_id);
/* Probably setup more properties and more layers */ /* Probably setup more properties and more layers */
req = drmModeAtomicAlloc(); req = drmModeAtomicAlloc();
if (!liftoff_device_apply(device, req)) { if (!liftoff_output_apply(output, req)) {
perror("liftoff_device_apply"); perror("liftoff_output_apply");
exit(1); exit(1);
} }

132
alloc.c
View file

@ -509,23 +509,23 @@ static bool layer_needs_realloc(struct liftoff_layer *layer)
return false; return false;
} }
static bool reuse_previous_alloc(struct liftoff_device *device, static bool reuse_previous_alloc(struct liftoff_output *output,
drmModeAtomicReq *req) drmModeAtomicReq *req)
{ {
struct liftoff_output *output; struct liftoff_device *device;
struct liftoff_layer *layer; struct liftoff_layer *layer;
int cursor; int cursor;
bool compatible; bool compatible;
liftoff_list_for_each(output, &device->outputs, link) { device = output->device;
if (output->layers_changed) {
return false;
}
liftoff_list_for_each(layer, &output->layers, link) { if (output->layers_changed) {
if (layer_needs_realloc(layer)) { return false;
return false; }
}
liftoff_list_for_each(layer, &output->layers, link) {
if (layer_needs_realloc(layer)) {
return false;
} }
} }
@ -542,17 +542,14 @@ static bool reuse_previous_alloc(struct liftoff_device *device,
return true; return true;
} }
static void mark_layers_clean(struct liftoff_device *device) static void mark_layers_clean(struct liftoff_output *output)
{ {
struct liftoff_output *output;
struct liftoff_layer *layer; struct liftoff_layer *layer;
liftoff_list_for_each(output, &device->outputs, link) { output->layers_changed = false;
output->layers_changed = false;
liftoff_list_for_each(layer, &output->layers, link) { liftoff_list_for_each(layer, &output->layers, link) {
layer_mark_clean(layer); layer_mark_clean(layer);
}
} }
} }
@ -575,9 +572,9 @@ static void update_layers_priority(struct liftoff_device *device)
} }
} }
bool liftoff_device_apply(struct liftoff_device *device, drmModeAtomicReq *req) bool liftoff_output_apply(struct liftoff_output *output, drmModeAtomicReq *req)
{ {
struct liftoff_output *output; struct liftoff_device *device;
struct liftoff_plane *plane; struct liftoff_plane *plane;
struct liftoff_layer *layer; struct liftoff_layer *layer;
struct alloc_result result; struct alloc_result result;
@ -585,16 +582,18 @@ bool liftoff_device_apply(struct liftoff_device *device, drmModeAtomicReq *req)
size_t i; size_t i;
bool compatible; bool compatible;
device = output->device;
update_layers_priority(device); update_layers_priority(device);
if (reuse_previous_alloc(device, req)) { if (reuse_previous_alloc(output, req)) {
liftoff_log(LIFTOFF_DEBUG, "Re-using previous plane allocation"); liftoff_log(LIFTOFF_DEBUG, "Re-using previous plane allocation");
return true; return true;
} }
/* Unset all existing plane and layer mappings. */ /* Unset all existing plane and layer mappings. */
liftoff_list_for_each(plane, &device->planes, link) { liftoff_list_for_each(plane, &device->planes, link) {
if (plane->layer != NULL) { if (plane->layer != NULL && plane->layer->output == output) {
plane->layer->plane = NULL; plane->layer->plane = NULL;
plane->layer = NULL; plane->layer = NULL;
} }
@ -623,66 +622,59 @@ bool liftoff_device_apply(struct liftoff_device *device, drmModeAtomicReq *req)
return false; return false;
} }
/* TODO: maybe start by allocating the primary plane on each output to /* For each plane, try to find a layer. Don't do it the other
* make sure we can display at least something without hitting bandwidth * way around (ie. for each layer, try to find a plane) because
* issues? Also: be fair when mapping planes to outputs, don't give all * some drivers want user-space to enable the primary plane
* planes to a single output. Also: don't treat each output separately, * before any other plane. */
* allocate planes for all outputs at once. */
liftoff_list_for_each(output, &device->outputs, link) {
/* 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
* before any other plane. */
result.best_score = -1; result.best_score = -1;
memset(result.best, 0, result.planes_len * sizeof(*result.best)); memset(result.best, 0, result.planes_len * sizeof(*result.best));
result.has_composition_layer = output->composition_layer != NULL; result.has_composition_layer = output->composition_layer != NULL;
result.non_composition_layers_len = result.non_composition_layers_len =
liftoff_list_length(&output->layers); liftoff_list_length(&output->layers);
if (output->composition_layer != NULL) { if (output->composition_layer != NULL) {
result.non_composition_layers_len--; result.non_composition_layers_len--;
} }
step.plane_link = device->planes.next; step.plane_link = device->planes.next;
step.plane_idx = 0; step.plane_idx = 0;
step.score = 0; step.score = 0;
step.last_layer_zpos = INT_MAX; step.last_layer_zpos = INT_MAX;
step.composited = false; step.composited = false;
if (!output_choose_layers(output, &result, &step)) { if (!output_choose_layers(output, &result, &step)) {
return false; return false;
}
liftoff_log(LIFTOFF_DEBUG,
"Found plane allocation for output %p with "
"score=%d", (void *)output, result.best_score);
/* Apply the best allocation */
i = 0;
liftoff_list_for_each(plane, &device->planes, link) {
layer = result.best[i];
i++;
if (layer == NULL) {
continue;
} }
liftoff_log(LIFTOFF_DEBUG, liftoff_log(LIFTOFF_DEBUG,
"Found plane allocation for output %p with " "Assigning layer %p to plane %"PRIu32,
"score=%d", (void *)output, result.best_score); (void *)layer, plane->id);
/* Apply the best allocation */ assert(plane->layer == NULL);
i = 0; assert(layer->plane == NULL);
liftoff_list_for_each(plane, &device->planes, link) { plane->layer = layer;
layer = result.best[i]; layer->plane = plane;
i++; }
if (layer == NULL) {
continue;
}
liftoff_log(LIFTOFF_DEBUG, if (!apply_current(device, req)) {
"Assigning layer %p to plane %"PRIu32, return false;
(void *)layer, plane->id);
assert(plane->layer == NULL);
assert(layer->plane == NULL);
plane->layer = layer;
layer->plane = plane;
}
if (!apply_current(device, req)) {
return false;
}
} }
free(step.alloc); free(step.alloc);
free(result.best); free(result.best);
mark_layers_clean(device); mark_layers_clean(output);
return true; return true;
} }

View file

@ -193,8 +193,8 @@ int main(int argc, char *argv[])
liftoff_output_set_composition_layer(output, composition_layer); liftoff_output_set_composition_layer(output, composition_layer);
req = drmModeAtomicAlloc(); req = drmModeAtomicAlloc();
if (!liftoff_device_apply(device, req)) { if (!liftoff_output_apply(output, req)) {
perror("liftoff_device_commit"); perror("liftoff_output_apply");
return 1; return 1;
} }

View file

@ -29,6 +29,7 @@ struct example_layer {
static int drm_fd = -1; static int drm_fd = -1;
static struct liftoff_device *device = NULL; static struct liftoff_device *device = NULL;
static struct liftoff_output *output = NULL;
static struct example_layer layers[LAYERS_LEN] = {0}; static struct example_layer layers[LAYERS_LEN] = {0};
static size_t active_layer_idx = 2; static size_t active_layer_idx = 2;
@ -107,8 +108,8 @@ static bool draw(void)
draw_layer(drm_fd, active_layer); draw_layer(drm_fd, active_layer);
req = drmModeAtomicAlloc(); req = drmModeAtomicAlloc();
if (!liftoff_device_apply(device, req)) { if (!liftoff_output_apply(output, req)) {
perror("liftoff_device_commit"); perror("liftoff_output_apply");
return false; return false;
} }
@ -140,7 +141,6 @@ int main(int argc, char *argv[])
drmModeRes *drm_res; drmModeRes *drm_res;
drmModeCrtc *crtc; drmModeCrtc *crtc;
drmModeConnector *connector; drmModeConnector *connector;
struct liftoff_output *output;
size_t i; size_t i;
int ret; int ret;

View file

@ -154,9 +154,11 @@ int main(int argc, char *argv[])
} }
req = drmModeAtomicAlloc(); req = drmModeAtomicAlloc();
if (!liftoff_device_apply(device, req)) { for (i = 0; i < outputs_len; i++) {
perror("liftoff_device_commit"); if (!liftoff_output_apply(outputs[i], req)) {
return 1; perror("liftoff_output_apply");
return 1;
}
} }
ret = drmModeAtomicCommit(drm_fd, req, DRM_MODE_ATOMIC_NONBLOCK, NULL); ret = drmModeAtomicCommit(drm_fd, req, DRM_MODE_ATOMIC_NONBLOCK, NULL);

View file

@ -128,8 +128,8 @@ int main(int argc, char *argv[])
} }
req = drmModeAtomicAlloc(); req = drmModeAtomicAlloc();
if (!liftoff_device_apply(device, req)) { if (!liftoff_output_apply(output, req)) {
perror("liftoff_device_commit"); perror("liftoff_output_apply");
return 1; return 1;
} }

View file

@ -22,8 +22,7 @@ void liftoff_device_destroy(struct liftoff_device *device);
* Callers are expected to commit `req` afterwards and can read the layer to * Callers are expected to commit `req` afterwards and can read the layer to
* plane mapping with `liftoff_layer_get_plane_id`. * plane mapping with `liftoff_layer_get_plane_id`.
*/ */
bool liftoff_device_apply(struct liftoff_device *device, bool liftoff_output_apply(struct liftoff_output *output, drmModeAtomicReq *req);
drmModeAtomicReq *req);
/** /**
* Make the device manage a CRTC's planes. The returned output allows callers * Make the device manage a CRTC's planes. The returned output allows callers

View file

@ -99,7 +99,7 @@ int main(int argc, char *argv[])
clock_gettime(CLOCK_MONOTONIC, &start); clock_gettime(CLOCK_MONOTONIC, &start);
req = drmModeAtomicAlloc(); req = drmModeAtomicAlloc();
ok = liftoff_device_apply(device, req); ok = liftoff_output_apply(output, req);
assert(ok); assert(ok);
drmModeAtomicFree(req); drmModeAtomicFree(req);

View file

@ -596,7 +596,7 @@ static void run_test(struct test_layer *test_layers)
} }
req = drmModeAtomicAlloc(); req = drmModeAtomicAlloc();
ok = liftoff_device_apply(device, req); ok = liftoff_output_apply(output, req);
assert(ok); assert(ok);
drmModeAtomicFree(req); drmModeAtomicFree(req);
@ -657,7 +657,7 @@ static void test_basic(void)
liftoff_mock_plane_add_compatible_layer(mock_plane, layer); liftoff_mock_plane_add_compatible_layer(mock_plane, layer);
req = drmModeAtomicAlloc(); req = drmModeAtomicAlloc();
ok = liftoff_device_apply(device, req); ok = liftoff_output_apply(output, req);
assert(ok); assert(ok);
assert(liftoff_mock_plane_get_layer(mock_plane, req) == layer); assert(liftoff_mock_plane_get_layer(mock_plane, req) == layer);
drmModeAtomicFree(req); drmModeAtomicFree(req);

View file

@ -63,7 +63,7 @@ int main(int argc, char *argv[]) {
liftoff_mock_plane_add_compatible_layer(mock_plane, layer); liftoff_mock_plane_add_compatible_layer(mock_plane, layer);
req = drmModeAtomicAlloc(); req = drmModeAtomicAlloc();
ok = liftoff_device_apply(device, req); ok = liftoff_output_apply(output, req);
assert(ok); assert(ok);
assert(liftoff_mock_plane_get_layer(mock_plane, req) == layer); assert(liftoff_mock_plane_get_layer(mock_plane, req) == layer);
drmModeAtomicFree(req); drmModeAtomicFree(req);
@ -96,7 +96,7 @@ int main(int argc, char *argv[]) {
return 1; return 1;
} }
ok = liftoff_device_apply(device, req); ok = liftoff_output_apply(output, req);
assert(ok); assert(ok);
assert(liftoff_mock_plane_get_layer(mock_plane, req) == layer); assert(liftoff_mock_plane_get_layer(mock_plane, req) == layer);
if (want_reuse_prev_alloc) { if (want_reuse_prev_alloc) {

View file

@ -72,7 +72,7 @@ int main(int argc, char *argv[]) {
liftoff_layer_set_property(layer, "FB_ID", fbs[j % 2]); liftoff_layer_set_property(layer, "FB_ID", fbs[j % 2]);
ok = liftoff_device_apply(device, req); ok = liftoff_output_apply(output, req);
assert(ok); assert(ok);
} }