mirror of
https://gitlab.freedesktop.org/emersion/libliftoff.git
synced 2024-12-26 21:59:18 +01:00
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:
parent
ef75ba26e0
commit
ceb4a1ff9e
11 changed files with 83 additions and 90 deletions
|
@ -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
132
alloc.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue