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 */
req = drmModeAtomicAlloc();
if (!liftoff_device_apply(device, req)) {
perror("liftoff_device_apply");
if (!liftoff_output_apply(output, req)) {
perror("liftoff_output_apply");
exit(1);
}

32
alloc.c
View file

@ -509,15 +509,16 @@ static bool layer_needs_realloc(struct liftoff_layer *layer)
return false;
}
static bool reuse_previous_alloc(struct liftoff_device *device,
static bool reuse_previous_alloc(struct liftoff_output *output,
drmModeAtomicReq *req)
{
struct liftoff_output *output;
struct liftoff_device *device;
struct liftoff_layer *layer;
int cursor;
bool compatible;
liftoff_list_for_each(output, &device->outputs, link) {
device = output->device;
if (output->layers_changed) {
return false;
}
@ -527,7 +528,6 @@ static bool reuse_previous_alloc(struct liftoff_device *device,
return false;
}
}
}
cursor = drmModeAtomicGetCursor(req);
@ -542,18 +542,15 @@ static bool reuse_previous_alloc(struct liftoff_device *device,
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;
liftoff_list_for_each(output, &device->outputs, link) {
output->layers_changed = false;
liftoff_list_for_each(layer, &output->layers, link) {
layer_mark_clean(layer);
}
}
}
static void update_layers_priority(struct liftoff_device *device)
@ -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_layer *layer;
struct alloc_result result;
@ -585,16 +582,18 @@ bool liftoff_device_apply(struct liftoff_device *device, drmModeAtomicReq *req)
size_t i;
bool compatible;
device = output->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");
return true;
}
/* Unset all existing plane and layer mappings. */
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 = NULL;
}
@ -623,12 +622,6 @@ bool liftoff_device_apply(struct liftoff_device *device, drmModeAtomicReq *req)
return false;
}
/* TODO: maybe start by allocating the primary plane on each output to
* make sure we can display at least something without hitting bandwidth
* issues? Also: be fair when mapping planes to outputs, don't give all
* planes to a single output. Also: don't treat each output separately,
* 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
@ -677,12 +670,11 @@ bool liftoff_device_apply(struct liftoff_device *device, drmModeAtomicReq *req)
if (!apply_current(device, req)) {
return false;
}
}
free(step.alloc);
free(result.best);
mark_layers_clean(device);
mark_layers_clean(output);
return true;
}

View file

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

View file

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

View file

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

View file

@ -128,8 +128,8 @@ int main(int argc, char *argv[])
}
req = drmModeAtomicAlloc();
if (!liftoff_device_apply(device, req)) {
perror("liftoff_device_commit");
if (!liftoff_output_apply(output, req)) {
perror("liftoff_output_apply");
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
* plane mapping with `liftoff_layer_get_plane_id`.
*/
bool liftoff_device_apply(struct liftoff_device *device,
drmModeAtomicReq *req);
bool liftoff_output_apply(struct liftoff_output *output, drmModeAtomicReq *req);
/**
* 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);
req = drmModeAtomicAlloc();
ok = liftoff_device_apply(device, req);
ok = liftoff_output_apply(output, req);
assert(ok);
drmModeAtomicFree(req);

View file

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

View file

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