From 5a66e567ece0628da525eb44d63855d944f05f87 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 13 Oct 2019 18:28:45 +0300 Subject: [PATCH] Track layer property changes This commit adds basic per-property change tracking. When only FB_ID has changed, we try to re-use the previous allocation. If that fails, go on with regular plane allocation. Closes: https://github.com/emersion/libliftoff/issues/6 --- display.c | 102 ++++++++++++++++++++++++++++++++++++++++++++-- include/private.h | 2 + layer.c | 13 ++++++ 3 files changed, 113 insertions(+), 4 deletions(-) diff --git a/display.c b/display.c index 31ff0e3..6e03724 100644 --- a/display.c +++ b/display.c @@ -607,6 +607,93 @@ skip: return true; } +static bool apply_current(struct liftoff_display *display, + drmModeAtomicReq *req) +{ + struct liftoff_plane *plane; + int cursor; + bool compatible; + + cursor = drmModeAtomicGetCursor(req); + + liftoff_list_for_each(plane, &display->planes, link) { + if (!plane_apply(plane, plane->layer, req, &compatible)) { + drmModeAtomicSetCursor(req, cursor); + return false; + } + assert(compatible); + } + + return true; +} + +static bool layer_needs_realloc(struct liftoff_layer *layer) +{ + size_t i; + struct liftoff_layer_property *prop; + + for (i = 0; i < layer->props_len; i++) { + prop = &layer->props[i]; + if (!prop->changed) { + continue; + } + if (strcmp(prop->name, "FB_ID") == 0) { + /* TODO: check format/modifier is the same. Check + * previous/next value isn't zero. */ + continue; + } + + /* TODO: if CRTC_{X,Y,W,H} changed but intersection with other + * layers hasn't changed, don't realloc */ + return true; + } + + return false; +} + +static bool reuse_previous_alloc(struct liftoff_display *display, + drmModeAtomicReq *req) +{ + struct liftoff_output *output; + struct liftoff_layer *layer; + int cursor, ret; + + liftoff_list_for_each(output, &display->outputs, link) { + liftoff_list_for_each(layer, &output->layers, link) { + if (layer_needs_realloc(layer)) { + return false; + } + } + } + + cursor = drmModeAtomicGetCursor(req); + + if (!apply_current(display, req)) { + return false; + } + + ret = drmModeAtomicCommit(display->drm_fd, req, + DRM_MODE_ATOMIC_TEST_ONLY, NULL); + if (ret != 0) { + drmModeAtomicSetCursor(req, cursor); + return false; + } + + return true; +} + +static void mark_layers_clean(struct liftoff_display *display) +{ + struct liftoff_output *output; + struct liftoff_layer *layer; + + liftoff_list_for_each(output, &display->outputs, link) { + liftoff_list_for_each(layer, &output->layers, link) { + layer_mark_clean(layer); + } + } +} + bool liftoff_display_apply(struct liftoff_display *display, drmModeAtomicReq *req) { struct liftoff_output *output; @@ -617,6 +704,11 @@ bool liftoff_display_apply(struct liftoff_display *display, drmModeAtomicReq *re size_t i; bool compatible; + if (reuse_previous_alloc(display, req)) { + liftoff_log(LIFTOFF_DEBUG, "Re-using previous plane allocation"); + return true; + } + /* Unset all existing plane and layer mappings. TODO: incremental updates keeping old configuration if possible */ liftoff_list_for_each(plane, &display->planes, link) { @@ -693,20 +785,22 @@ bool liftoff_display_apply(struct liftoff_display *display, drmModeAtomicReq *re liftoff_log(LIFTOFF_DEBUG, "Assigning layer %p to plane %"PRIu32, (void *)layer, plane->id); - if (!plane_apply(plane, layer, req, &compatible)) { - return false; - } - assert(compatible); assert(plane->layer == NULL); assert(layer->plane == NULL); plane->layer = layer; layer->plane = plane; } + + if (!apply_current(display, req)) { + return false; + } } free(step.alloc); free(result.best); + mark_layers_clean(display); + return true; } diff --git a/include/private.h b/include/private.h index 5b293db..d83116c 100644 --- a/include/private.h +++ b/include/private.h @@ -39,6 +39,7 @@ struct liftoff_layer { struct liftoff_layer_property { char name[DRM_PROP_NAME_LEN]; uint64_t value; + bool changed; }; struct liftoff_plane { @@ -69,6 +70,7 @@ struct liftoff_layer_property *layer_get_property(struct liftoff_layer *layer, const char *name); void layer_get_rect(struct liftoff_layer *layer, struct liftoff_rect *rect); bool layer_intersects(struct liftoff_layer *a, struct liftoff_layer *b); +void layer_mark_clean(struct liftoff_layer *layer); struct liftoff_plane *plane_create(struct liftoff_display *display, uint32_t id); void plane_destroy(struct liftoff_plane *plane); diff --git a/layer.c b/layer.c index 0a4da9d..49400ac 100644 --- a/layer.c +++ b/layer.c @@ -66,6 +66,10 @@ void liftoff_layer_set_property(struct liftoff_layer *layer, const char *name, prop = &layer->props[layer->props_len - 1]; memset(prop, 0, sizeof(*prop)); strncpy(prop->name, name, sizeof(prop->name) - 1); + + prop->changed = true; + } else { + prop->changed = prop->value != value; } prop->value = value; @@ -104,3 +108,12 @@ bool layer_intersects(struct liftoff_layer *a, struct liftoff_layer *b) return ra.x < rb.x + rb.width && ra.y < rb.y + rb.height && ra.x + ra.width > rb.x && ra.y + ra.height > rb.y; } + +void layer_mark_clean(struct liftoff_layer *layer) +{ + size_t i; + + for (i = 0; i < layer->props_len; i++) { + layer->props[i].changed = false; + } +}