Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libliftoff for openSUSE:Factory checked in at 2024-05-28 17:29:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libliftoff (Old) and /work/SRC/openSUSE:Factory/.libliftoff.new.24587 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libliftoff" Tue May 28 17:29:39 2024 rev:4 rq:1177281 version:0.5.0 Changes: -------- --- /work/SRC/openSUSE:Factory/libliftoff/libliftoff.changes 2023-03-07 16:50:31.153642952 +0100 +++ /work/SRC/openSUSE:Factory/.libliftoff.new.24587/libliftoff.changes 2024-05-28 17:30:37.875985557 +0200 @@ -1,0 +2,7 @@ +Tue May 28 07:58:02 UTC 2024 - llyyr <[email protected]> + +- Update to 0.5.0: + * See full changelog at + https://gitlab.freedesktop.org/emersion/libliftoff/-/tags/v0.5.0 + +------------------------------------------------------------------- Old: ---- libliftoff-v0.4.1.tar.gz New: ---- libliftoff-v0.5.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libliftoff.spec ++++++ --- /var/tmp/diff_new_pack.druoWL/_old 2024-05-28 17:30:38.956025051 +0200 +++ /var/tmp/diff_new_pack.druoWL/_new 2024-05-28 17:30:38.960025198 +0200 @@ -1,7 +1,7 @@ # # spec file for package libliftoff # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %define libname libliftoff0 Name: libliftoff -Version: 0.4.1 +Version: 0.5.0 Release: 0 Summary: KMS plane library Group: Development/Libraries/C and C++ ++++++ libliftoff-v0.4.1.tar.gz -> libliftoff-v0.5.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/.builds/alpine.yml new/libliftoff-v0.5.0/.builds/alpine.yml --- old/libliftoff-v0.4.1/.builds/alpine.yml 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/.builds/alpine.yml 2024-05-28 00:22:40.000000000 +0200 @@ -1,4 +1,4 @@ -image: alpine/edge +image: alpine/latest packages: - gcc - clang @@ -10,11 +10,13 @@ - https://gitlab.freedesktop.org/emersion/libliftoff.git artifacts: - coveragereport.tar.gz +environment: + UBSAN_OPTIONS: halt_on_error=1 tasks: - setup: | cd libliftoff - CC=gcc meson build-gcc/ --fatal-meson-warnings -Db_coverage=true - CC=clang meson build-clang/ --fatal-meson-warnings -Db_sanitize=address,undefined -Db_lundef=false + CC=gcc meson setup build-gcc/ --fatal-meson-warnings -Db_coverage=true + CC=clang meson setup build-clang/ --fatal-meson-warnings -Db_sanitize=address,undefined -Db_lundef=false - build-gcc: | cd libliftoff ninja -C build-gcc/ @@ -29,8 +31,6 @@ ninja -C build-clang/ test - coverage: | cd libliftoff - echo "exclude-directories = test/" >>gcovr.cfg - echo "exclude-directories = example/" >>gcovr.cfg ninja -C build-gcc/ coverage-html cd build-gcc/meson-logs/ tar -czf ~/coveragereport.tar.gz coveragereport diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/.builds/freebsd.yml new/libliftoff-v0.5.0/.builds/freebsd.yml --- old/libliftoff-v0.4.1/.builds/freebsd.yml 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/.builds/freebsd.yml 2024-05-28 00:22:40.000000000 +0200 @@ -8,7 +8,7 @@ tasks: - setup: | cd libliftoff - meson build/ --fatal-meson-warnings + meson setup build/ --fatal-meson-warnings - build: | cd libliftoff ninja -C build/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/README.md new/libliftoff-v0.5.0/README.md --- old/libliftoff-v0.4.1/README.md 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/README.md 2024-05-28 00:22:40.000000000 +0200 @@ -20,7 +20,7 @@ Depends on libdrm. Requires universal planes and atomic. - meson build/ + meson setup build/ ninja -C build/ ## Usage diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/alloc.c new/libliftoff-v0.5.0/alloc.c --- old/libliftoff-v0.4.1/alloc.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/alloc.c 2024-05-28 00:22:40.000000000 +0200 @@ -1,9 +1,11 @@ +#define _POSIX_C_SOURCE 200112L #include <assert.h> #include <errno.h> #include <inttypes.h> #include <limits.h> #include <stdlib.h> #include <string.h> +#include <time.h> #include <unistd.h> #include "log.h" #include "private.h" @@ -76,6 +78,9 @@ struct liftoff_layer **best; int best_score; + struct timespec started_at; + int64_t timeout_ns; + /* per-output */ bool has_composition_layer; size_t non_composition_layers_len; @@ -96,6 +101,29 @@ char log_prefix[64]; }; +static const int64_t NSEC_PER_SEC = 1000 * 1000 * 1000; + +static int64_t +timespec_to_nsec(struct timespec ts) +{ + return (int64_t)ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; +} + +static const int64_t DEFAULT_ALLOC_TIMEOUT_NSEC = 1000 * 1000; // 1ms + +static bool +check_deadline(struct timespec start, int64_t timeout_ns) +{ + struct timespec now; + + if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) { + liftoff_log_errno(LIFTOFF_ERROR, "clock_gettime"); + return false; + } + + return timespec_to_nsec(now) - timeout_ns < timespec_to_nsec(start); +} + static void plane_step_init_next(struct alloc_step *step, struct alloc_step *prev, struct liftoff_layer *layer) @@ -126,7 +154,7 @@ zpos_prop = NULL; if (layer != NULL) { - zpos_prop = layer_get_property(layer, "zpos"); + zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS); } if (zpos_prop != NULL && plane->type != DRM_PLANE_TYPE_PRIMARY) { step->last_layer_zpos = zpos_prop->value; @@ -176,7 +204,7 @@ struct liftoff_layer *other_layer; struct liftoff_layer_property *zpos_prop, *other_zpos_prop; - zpos_prop = layer_get_property(layer, "zpos"); + zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS); if (zpos_prop == NULL) { return false; } @@ -186,7 +214,8 @@ continue; } - other_zpos_prop = layer_get_property(other_layer, "zpos"); + other_zpos_prop = layer_get_core_property(other_layer, + LIFTOFF_PROP_ZPOS); if (other_zpos_prop == NULL) { continue; } @@ -209,7 +238,7 @@ struct liftoff_layer *other_layer; struct liftoff_layer_property *zpos_prop, *other_zpos_prop; - zpos_prop = layer_get_property(layer, "zpos"); + zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS); if (zpos_prop == NULL) { return false; } @@ -229,7 +258,8 @@ continue; } - other_zpos_prop = layer_get_property(other_layer, "zpos"); + other_zpos_prop = layer_get_core_property(other_layer, + LIFTOFF_PROP_ZPOS); if (other_zpos_prop == NULL) { continue; } @@ -292,7 +322,7 @@ return false; } - zpos_prop = layer_get_property(layer, "zpos"); + zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS); if (zpos_prop != NULL) { if ((int)zpos_prop->value > step->last_layer_zpos && has_allocated_layer_over(output, step, layer)) { @@ -387,6 +417,31 @@ return true; } +static bool +check_plane_output_compatible(struct liftoff_plane *plane, struct liftoff_output *output) +{ + return (plane->possible_crtcs & (1 << output->crtc_index)) != 0; +} + +static int +count_remaining_compatible_planes(struct liftoff_output *output, + struct alloc_step *step) +{ + struct liftoff_list *link; + struct liftoff_plane *plane; + int remaining = 0; + + for (link = step->plane_link; link != &output->device->planes; link = link->next) { + plane = liftoff_container_of(link, plane, link); + if (plane->layer == NULL && + check_plane_output_compatible(plane, output)) { + remaining++; + } + } + + return remaining; +} + static int output_choose_layers(struct liftoff_output *output, struct alloc_result *result, struct alloc_step *step) @@ -395,7 +450,7 @@ struct liftoff_plane *plane; struct liftoff_layer *layer; int cursor, ret; - size_t remaining_planes; + int remaining_planes; struct alloc_step next_step = {0}; device = output->device; @@ -416,21 +471,16 @@ plane = liftoff_container_of(step->plane_link, plane, link); - remaining_planes = result->planes_len - step->plane_idx; - if (result->best_score >= step->score + (int)remaining_planes) { + remaining_planes = count_remaining_compatible_planes(output, step); + if (result->best_score >= step->score + remaining_planes) { /* Even if we find a layer for all remaining planes, we won't * find a better allocation. Give up. */ - /* TODO: change remaining_planes to only count those whose - * possible CRTC match and which aren't allocated */ return 0; } cursor = drmModeAtomicGetCursor(result->req); - if (plane->layer != NULL) { - goto skip; - } - if ((plane->possible_crtcs & (1 << output->crtc_index)) == 0) { + if (plane->layer != NULL || !check_plane_output_compatible(plane, output)) { goto skip; } @@ -449,6 +499,12 @@ continue; } + if (!check_deadline(result->started_at, result->timeout_ns)) { + liftoff_log(LIFTOFF_DEBUG, "%s Deadline exceeded", + step->log_prefix); + break; + } + /* Try to use this layer for the current plane */ ret = plane_apply(plane, layer, result->req); if (ret == -EINVAL) { @@ -542,10 +598,37 @@ } static bool -layer_needs_realloc(struct liftoff_layer *layer) +layer_intersection_changed(struct liftoff_layer *this, + struct liftoff_output *output) +{ + struct liftoff_layer *other; + struct liftoff_rect this_cur, this_prev, other_cur, other_prev; + + layer_get_rect(this, &this_cur); + layer_get_prev_rect(this, &this_prev); + liftoff_list_for_each(other, &output->layers, link) { + if (this == other) { + continue; + } + + layer_get_rect(other, &other_cur); + layer_get_prev_rect(other, &other_prev); + + if (rect_intersects(&this_cur, &other_cur) != + rect_intersects(&this_prev, &other_prev)) { + return true; + } + } + + return false; +} + +static bool +layer_needs_realloc(struct liftoff_layer *layer, struct liftoff_output *output) { - size_t i; struct liftoff_layer_property *prop; + bool check_crtc_intersect = false; + size_t i; if (layer->changed) { liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: " @@ -563,7 +646,7 @@ * If FB_ID changes from non-zero to non-zero and the FB * attributes didn't change, we can try to re-use the previous * allocation. */ - if (strcmp(prop->name, "FB_ID") == 0) { + if (prop->core_index == LIFTOFF_PROP_FB_ID) { if (prop->value == 0 && prop->prev_value == 0) { continue; } @@ -593,7 +676,7 @@ /* If the layer was or becomes completely transparent or * completely opaque, we might be able to find a better * allocation. Otherwise, we can keep the current one. */ - if (strcmp(prop->name, "alpha") == 0) { + if (prop->core_index == LIFTOFF_PROP_ALPHA) { if (prop->value == 0 || prop->prev_value == 0 || prop->value == 0xFFFF || prop->prev_value == 0xFFFF) { liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: " @@ -610,16 +693,123 @@ continue; } - /* TODO: if CRTC_{X,Y,W,H} changed but intersection with other - * layers hasn't changed, don't realloc */ + /* If CRTC_* changed, check for intersection later */ + if (strcmp(prop->name, "CRTC_X") == 0 || + strcmp(prop->name, "CRTC_Y") == 0 || + strcmp(prop->name, "CRTC_W") == 0 || + strcmp(prop->name, "CRTC_H") == 0) { + check_crtc_intersect = true; + continue; + } + liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: " "property \"%s\" changed", prop->name); return true; } + if (check_crtc_intersect && + layer_intersection_changed(layer, output)) { + liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: " + "intersection with other layer(s) changed"); + return true; + } + return false; } +static bool +layer_is_higher_priority(struct liftoff_layer *this, struct liftoff_layer *other) +{ + struct liftoff_layer_property *this_zpos, *other_zpos; + bool this_visible, other_visible, intersects; + + // The composition layer should be highest priority. + if (this->output->composition_layer == this) { + return true; + } else if (this->output->composition_layer == other) { + return false; + } + + // Invisible layers are given lowest priority. Pass-thru if both have + // same visibility + this_visible = layer_is_visible(this); + other_visible = layer_is_visible(other); + if (this_visible != other_visible) { + return this_visible; + } + + // A layer's overall priority is determined by a combination of it's + // current_priority, it's zpos, and whether it intersects with others. + // + // Consider two layers. If they do not intersect, the layer with higher + // priority is given overall priority. However if both layers have + // identical priority, then the layer with higher zpos is given overall + // priority. + // + // If the layers intersect, their zpos determines the overall priority. + // If their zpos are identical, then simply fallback to looking at + // current_priority. Otherwise, the layer with higher zpos is given + // overall priority, since the top layer needs to be offloaded in order + // to offload the bottom layer. + + this_zpos = layer_get_core_property(this, LIFTOFF_PROP_ZPOS); + other_zpos = layer_get_core_property(other, LIFTOFF_PROP_ZPOS); + intersects = layer_intersects(this, other); + + if (this_zpos != NULL && other_zpos != NULL) { + if (intersects) { + return this_zpos->value == other_zpos->value ? + this->current_priority > other->current_priority : + this_zpos->value > other_zpos->value; + } else { + return this->current_priority == other->current_priority ? + this_zpos->value > other_zpos->value : + this->current_priority > other->current_priority; + } + } else if (this_zpos == NULL && other_zpos == NULL) { + return this->current_priority > other->current_priority; + } else { + // Either this or other zpos is null + return this_zpos != NULL; + } +} + +static bool +update_layers_order(struct liftoff_output *output) +{ + struct liftoff_list *search, *max, *cur, *head; + struct liftoff_layer *this_layer, *other_layer; + bool order_changed = false; + + head = &output->layers; + cur = head; + + // Run a insertion sort to order layers by priority. + while (cur->next != head) { + cur = cur->next; + + max = cur; + search = cur; + while (search->next != head) { + search = search->next; + this_layer = liftoff_container_of(search, this_layer, link); + other_layer = liftoff_container_of(max, other_layer, link); + if (layer_is_higher_priority(this_layer, other_layer)) { + max = search; + } + } + + if (cur != max) { + liftoff_list_swap(cur, max); + // max is now where iterator cur was, relocate to continue + cur = max; + order_changed = true; + } + } + + return order_changed; +} + static int reuse_previous_alloc(struct liftoff_output *output, drmModeAtomicReq *req, uint32_t flags) @@ -627,9 +817,12 @@ struct liftoff_device *device; struct liftoff_layer *layer; int cursor, ret; + bool layer_order_changed; device = output->device; + layer_order_changed = update_layers_order(output); + if (output->layers_changed) { liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: " "a layer has been added or removed"); @@ -637,11 +830,17 @@ } liftoff_list_for_each(layer, &output->layers, link) { - if (layer_needs_realloc(layer)) { + if (layer_needs_realloc(layer, output)) { return -EINVAL; } } + if (layer_order_changed) { + liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: " + "layer priority order changed."); + return -EINVAL; + } + cursor = drmModeAtomicGetCursor(req); ret = apply_current(device, req); @@ -698,7 +897,8 @@ * re-created a completely different one which happens to have the same * FB ID. */ liftoff_list_for_each(layer, &output->layers, link) { - memset(&layer->fb_info, 0, sizeof(layer->fb_info)); + layer->fb_info = (drmModeFB2){0}; + layer_cache_fb_info(layer); /* TODO: propagate error? */ } @@ -709,8 +909,8 @@ { if (output->alloc_reused_counter == 0) { liftoff_log(LIFTOFF_DEBUG, - "Reusing previous plane allocation on output %p", - (void *)output); + "Reusing previous plane allocation on output %"PRIu32, + output->crtc_id); } output->alloc_reused_counter++; } @@ -718,14 +918,14 @@ static void log_no_reuse(struct liftoff_output *output) { - liftoff_log(LIFTOFF_DEBUG, "Computing plane allocation on output %p", - (void *)output); + liftoff_log(LIFTOFF_DEBUG, "Computing plane allocation on output %"PRIu32, + output->crtc_id); if (output->alloc_reused_counter != 0) { liftoff_log(LIFTOFF_DEBUG, "Stopped reusing previous plane allocation on " - "output %p (had reused it %d times)", - (void *)output, output->alloc_reused_counter); + "output %"PRIu32" (had reused it %d times)", + output->crtc_id, output->alloc_reused_counter); output->alloc_reused_counter = 0; } } @@ -749,15 +949,22 @@ 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; @@ -767,6 +974,7 @@ ret = reuse_previous_alloc(output, req, flags); if (ret == 0) { log_reuse(output); + mark_layers_clean(output); return 0; } log_no_reuse(output); @@ -808,20 +1016,30 @@ result.flags = flags; result.planes_len = liftoff_list_length(&device->planes); - step.alloc = malloc(result.planes_len * sizeof(*step.alloc)); - result.best = malloc(result.planes_len * sizeof(*result.best)); + step.alloc = malloc(result.planes_len * sizeof(step.alloc[0])); + result.best = malloc(result.planes_len * sizeof(result.best[0])); if (step.alloc == NULL || result.best == NULL) { liftoff_log_errno(LIFTOFF_ERROR, "malloc"); return -ENOMEM; } + if (clock_gettime(CLOCK_MONOTONIC, &result.started_at) != 0) { + liftoff_log_errno(LIFTOFF_ERROR, "clock_gettime"); + 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 * before any other plane. */ result.best_score = -1; - memset(result.best, 0, result.planes_len * sizeof(*result.best)); + memset(result.best, 0, result.planes_len * sizeof(result.best[0])); result.has_composition_layer = output->composition_layer != NULL; result.non_composition_layers_len = non_composition_layers_length(output); @@ -838,12 +1056,14 @@ } liftoff_log(LIFTOFF_DEBUG, - "Found plane allocation for output %p (score: %d, candidate planes: %zu, tests: %d):", - (void *)output, result.best_score, candidate_planes, + "Found plane allocation for output %"PRIu32" " + "(score: %d, candidate planes: %zu, tests: %d):", + output->crtc_id, result.best_score, candidate_planes, device->test_commit_counter); /* Apply the best allocation */ i = 0; + found_layer = false; liftoff_list_for_each(plane, &device->planes, link) { layer = result.best[i]; i++; @@ -858,8 +1078,10 @@ assert(layer->plane == NULL); plane->layer = layer; layer->plane = plane; + + found_layer = true; } - if (i == 0) { + if (!found_layer) { liftoff_log(LIFTOFF_DEBUG, " (No layer has a plane)"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/device.c new/libliftoff-v0.5.0/device.c --- old/libliftoff-v0.4.1/device.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/device.c 2024-05-28 00:22:40.000000000 +0200 @@ -122,3 +122,36 @@ return ret; } + +ssize_t +core_property_index(const char *name) +{ + if (strcmp(name, "FB_ID") == 0) { + return LIFTOFF_PROP_FB_ID; + } else if (strcmp(name, "CRTC_ID") == 0) { + return LIFTOFF_PROP_CRTC_ID; + } else if (strcmp(name, "CRTC_X") == 0) { + return LIFTOFF_PROP_CRTC_X; + } else if (strcmp(name, "CRTC_Y") == 0) { + return LIFTOFF_PROP_CRTC_Y; + } else if (strcmp(name, "CRTC_W") == 0) { + return LIFTOFF_PROP_CRTC_W; + } else if (strcmp(name, "CRTC_H") == 0) { + return LIFTOFF_PROP_CRTC_H; + } else if (strcmp(name, "SRC_X") == 0) { + return LIFTOFF_PROP_SRC_X; + } else if (strcmp(name, "SRC_Y") == 0) { + return LIFTOFF_PROP_SRC_Y; + } else if (strcmp(name, "SRC_W") == 0) { + return LIFTOFF_PROP_SRC_W; + } else if (strcmp(name, "SRC_H") == 0) { + return LIFTOFF_PROP_SRC_H; + } else if (strcmp(name, "zpos") == 0) { + return LIFTOFF_PROP_ZPOS; + } else if (strcmp(name, "alpha") == 0) { + return LIFTOFF_PROP_ALPHA; + } else if (strcmp(name, "rotation") == 0) { + return LIFTOFF_PROP_ROTATION; + } + return -1; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/example/compositor.c new/libliftoff-v0.5.0/example/compositor.c --- old/libliftoff-v0.4.1/example/compositor.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/example/compositor.c 2024-05-28 00:22:40.000000000 +0200 @@ -195,7 +195,7 @@ 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; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/example/dynamic.c new/libliftoff-v0.5.0/example/dynamic.c --- old/libliftoff-v0.4.1/example/dynamic.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/example/dynamic.c 2024-05-28 00:22:40.000000000 +0200 @@ -114,7 +114,7 @@ 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; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/example/multi-output.c new/libliftoff-v0.5.0/example/multi-output.c --- old/libliftoff-v0.4.1/example/multi-output.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/example/multi-output.c 2024-05-28 00:22:40.000000000 +0200 @@ -157,7 +157,7 @@ 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; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/example/simple.c new/libliftoff-v0.5.0/example/simple.c --- old/libliftoff-v0.4.1/example/simple.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/example/simple.c 2024-05-28 00:22:40.000000000 +0200 @@ -130,7 +130,7 @@ 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; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/include/libliftoff.h new/libliftoff-v0.5.0/include/libliftoff.h --- old/libliftoff-v0.4.1/include/libliftoff.h 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/include/libliftoff.h 2024-05-28 00:22:40.000000000 +0200 @@ -63,18 +63,28 @@ 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. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/include/list.h new/libliftoff-v0.5.0/include/list.h --- old/libliftoff-v0.4.1/include/list.h 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/include/list.h 2024-05-28 00:22:40.000000000 +0200 @@ -18,6 +18,9 @@ void liftoff_list_remove(struct liftoff_list *elm); +void +liftoff_list_swap(struct liftoff_list *this, struct liftoff_list *other); + size_t liftoff_list_length(const struct liftoff_list *list); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/include/private.h new/libliftoff-v0.5.0/include/private.h --- old/libliftoff-v0.4.1/include/private.h 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/include/private.h 2024-05-28 00:22:40.000000000 +0200 @@ -2,6 +2,7 @@ #define PRIVATE_H #include <libliftoff.h> +#include <sys/types.h> #include "list.h" #include "log.h" @@ -9,6 +10,28 @@ * given number of page-flips */ #define LIFTOFF_PRIORITY_PERIOD 60 +/** + * List of well-known KMS properties. + * + * Keep core_property_index() in sync. + */ +enum liftoff_core_property { + LIFTOFF_PROP_FB_ID, + LIFTOFF_PROP_CRTC_ID, + LIFTOFF_PROP_CRTC_X, + LIFTOFF_PROP_CRTC_Y, + LIFTOFF_PROP_CRTC_W, + LIFTOFF_PROP_CRTC_H, + LIFTOFF_PROP_SRC_X, + LIFTOFF_PROP_SRC_Y, + LIFTOFF_PROP_SRC_W, + LIFTOFF_PROP_SRC_H, + LIFTOFF_PROP_ZPOS, + LIFTOFF_PROP_ALPHA, + LIFTOFF_PROP_ROTATION, + LIFTOFF_PROP_LAST, /* keep last */ +}; + struct liftoff_device { int drm_fd; @@ -45,6 +68,7 @@ struct liftoff_layer_property *props; size_t props_len; + ssize_t core_props[LIFTOFF_PROP_LAST]; /* indices into the props array */ bool force_composition; /* FB needs to be composited */ @@ -62,6 +86,7 @@ struct liftoff_layer_property { char name[DRM_PROP_NAME_LEN]; uint64_t value, prev_value; + ssize_t core_index; }; struct liftoff_plane { @@ -69,12 +94,12 @@ uint32_t possible_crtcs; uint32_t type; int zpos; /* greater values mean closer to the eye */ - /* TODO: formats */ struct liftoff_list link; /* liftoff_device.planes */ drmModePropertyRes **props; size_t props_len; drmModePropertyBlobRes *in_formats_blob; + const drmModePropertyRes *core_props[LIFTOFF_PROP_LAST]; struct liftoff_layer *layer; }; @@ -91,9 +116,18 @@ struct liftoff_layer_property * layer_get_property(struct liftoff_layer *layer, const char *name); +struct liftoff_layer_property * +layer_get_core_property(struct liftoff_layer *layer, enum liftoff_core_property prop); + void layer_get_rect(struct liftoff_layer *layer, struct liftoff_rect *rect); +void +layer_get_prev_rect(struct liftoff_layer *layer, struct liftoff_rect *rect); + +bool +rect_intersects(struct liftoff_rect *a, struct liftoff_rect *b); + bool layer_intersects(struct liftoff_layer *a, struct liftoff_layer *b); @@ -129,4 +163,7 @@ void output_log_layers(struct liftoff_output *output); +ssize_t +core_property_index(const char *name); + #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/layer.c new/libliftoff-v0.5.0/layer.c --- old/libliftoff-v0.4.1/layer.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/layer.c 2024-05-28 00:22:40.000000000 +0200 @@ -10,6 +10,7 @@ liftoff_layer_create(struct liftoff_output *output) { struct liftoff_layer *layer; + size_t i; layer = calloc(1, sizeof(*layer)); if (layer == NULL) { @@ -17,13 +18,16 @@ return NULL; } layer->output = output; - layer->candidate_planes = calloc(sizeof(layer->candidate_planes[0]), - output->device->planes_cap); + layer->candidate_planes = calloc(output->device->planes_cap, + sizeof(layer->candidate_planes[0])); if (layer->candidate_planes == NULL) { liftoff_log_errno(LIFTOFF_ERROR, "calloc"); free(layer); return NULL; } + for (i = 0; i < LIFTOFF_PROP_LAST; i++) { + layer->core_props[i] = -1; + } liftoff_list_insert(output->layers.prev, &layer->link); output->layers_changed = true; return layer; @@ -50,10 +54,28 @@ } struct liftoff_layer_property * +layer_get_core_property(struct liftoff_layer *layer, enum liftoff_core_property prop) +{ + ssize_t i; + + i = layer->core_props[prop]; + if (i < 0) { + return NULL; + } + return &layer->props[i]; +} + +struct liftoff_layer_property * layer_get_property(struct liftoff_layer *layer, const char *name) { + ssize_t core_prop_idx; size_t i; + core_prop_idx = core_property_index(name); + if (core_prop_idx >= 0) { + return layer_get_core_property(layer, core_prop_idx); + } + for (i = 0; i < layer->props_len; i++) { if (strcmp(layer->props[i].name, name) == 0) { return &layer->props[i]; @@ -68,6 +90,7 @@ { struct liftoff_layer_property *props; struct liftoff_layer_property *prop; + size_t i; if (strcmp(name, "CRTC_ID") == 0) { liftoff_log(LIFTOFF_ERROR, @@ -77,8 +100,8 @@ prop = layer_get_property(layer, name); if (prop == NULL) { - props = realloc(layer->props, (layer->props_len + 1) - * sizeof(struct liftoff_layer_property)); + props = realloc(layer->props, + (layer->props_len + 1) * sizeof(props[0])); if (props == NULL) { liftoff_log_errno(LIFTOFF_ERROR, "realloc"); return -ENOMEM; @@ -86,16 +109,22 @@ layer->props = props; layer->props_len++; - prop = &layer->props[layer->props_len - 1]; - memset(prop, 0, sizeof(*prop)); + i = layer->props_len - 1; + prop = &layer->props[i]; + *prop = (struct liftoff_layer_property){0}; strncpy(prop->name, name, sizeof(prop->name) - 1); + prop->core_index = core_property_index(name); layer->changed = true; + + if (prop->core_index >= 0) { + layer->core_props[prop->core_index] = (ssize_t)i; + } } prop->value = value; - if (strcmp(name, "FB_ID") == 0 && layer->force_composition) { + if (prop->core_index == LIFTOFF_PROP_FB_ID && layer->force_composition) { layer->force_composition = false; layer->changed = true; } @@ -113,11 +142,18 @@ return; } + if (prop->core_index >= 0) { + layer->core_props[prop->core_index] = -1; + } + last = &layer->props[layer->props_len - 1]; if (prop != last) { *prop = *last; + if (last->core_index >= 0) { + layer->core_props[last->core_index] = prop - layer->props; + } } - memset(last, 0, sizeof(*last)); + *last = (struct liftoff_layer_property){0}; layer->props_len--; layer->changed = true; @@ -156,10 +192,10 @@ { struct liftoff_layer_property *x_prop, *y_prop, *w_prop, *h_prop; - x_prop = layer_get_property(layer, "CRTC_X"); - y_prop = layer_get_property(layer, "CRTC_Y"); - w_prop = layer_get_property(layer, "CRTC_W"); - h_prop = layer_get_property(layer, "CRTC_H"); + x_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_X); + y_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_Y); + w_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_W); + h_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_H); rect->x = x_prop != NULL ? x_prop->value : 0; rect->y = y_prop != NULL ? y_prop->value : 0; @@ -167,6 +203,29 @@ rect->height = h_prop != NULL ? h_prop->value : 0; } +void +layer_get_prev_rect(struct liftoff_layer *layer, struct liftoff_rect *rect) +{ + struct liftoff_layer_property *x_prop, *y_prop, *w_prop, *h_prop; + + x_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_X); + y_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_Y); + w_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_W); + h_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_H); + + rect->x = x_prop != NULL ? x_prop->prev_value : 0; + rect->y = y_prop != NULL ? y_prop->prev_value : 0; + rect->width = w_prop != NULL ? w_prop->prev_value : 0; + rect->height = h_prop != NULL ? h_prop -> prev_value : 0; +} + +bool +rect_intersects(struct liftoff_rect *ra, struct liftoff_rect *rb) +{ + 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; +} + bool layer_intersects(struct liftoff_layer *a, struct liftoff_layer *b) { @@ -179,8 +238,7 @@ layer_get_rect(a, &ra); layer_get_rect(b, &rb); - 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; + return rect_intersects(&ra, &rb); } void @@ -214,7 +272,7 @@ struct liftoff_layer_property *prop; /* TODO: also bump priority when updating other properties */ - prop = layer_get_property(layer, "FB_ID"); + prop = layer_get_core_property(layer, LIFTOFF_PROP_FB_ID); if (prop != NULL && prop->prev_value != prop->value) { layer->pending_priority++; } @@ -231,7 +289,7 @@ { struct liftoff_layer_property *fb_id_prop; - fb_id_prop = layer_get_property(layer, "FB_ID"); + fb_id_prop = layer_get_core_property(layer, LIFTOFF_PROP_FB_ID); return fb_id_prop != NULL && fb_id_prop->value != 0; } @@ -240,7 +298,7 @@ { struct liftoff_layer_property *alpha_prop; - alpha_prop = layer_get_property(layer, "alpha"); + alpha_prop = layer_get_core_property(layer, LIFTOFF_PROP_ALPHA); if (alpha_prop != NULL && alpha_prop->value == 0) { return false; /* fully transparent */ } @@ -260,9 +318,9 @@ size_t i, j, num_planes; int ret; - fb_id_prop = layer_get_property(layer, "FB_ID"); + fb_id_prop = layer_get_core_property(layer, LIFTOFF_PROP_FB_ID); if (fb_id_prop == NULL || fb_id_prop->value == 0) { - memset(&layer->fb_info, 0, sizeof(layer->fb_info)); + layer->fb_info = (drmModeFB2){0}; return 0; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/list.c new/libliftoff-v0.5.0/list.c --- old/libliftoff-v0.4.1/list.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/list.c 2024-05-28 00:22:40.000000000 +0200 @@ -25,6 +25,19 @@ elm->prev = NULL; } +void +liftoff_list_swap(struct liftoff_list *this, struct liftoff_list *other) +{ + struct liftoff_list tmp; + + liftoff_list_insert(other, &tmp); + liftoff_list_remove(other); + liftoff_list_insert(this, other); + liftoff_list_remove(this); + liftoff_list_insert(&tmp, this); + liftoff_list_remove(&tmp); +} + size_t liftoff_list_length(const struct liftoff_list *list) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/meson.build new/libliftoff-v0.5.0/meson.build --- old/libliftoff-v0.4.1/meson.build 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/meson.build 2024-05-28 00:22:40.000000000 +0200 @@ -1,7 +1,7 @@ project( 'libliftoff', 'c', - version: '0.4.1', + version: '0.5.0', license: 'MIT', meson_version: '>=0.52.0', default_options: [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/output.c new/libliftoff-v0.5.0/output.c --- old/libliftoff-v0.4.1/output.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/output.c 2024-05-28 00:22:40.000000000 +0200 @@ -104,6 +104,9 @@ " (composition layer)" : ""); } + liftoff_log(LIFTOFF_DEBUG, " Priority = %"PRIi32, + layer->current_priority); + for (i = 0; i < layer->props_len; i++) { char *name = layer->props[i].name; uint64_t value = layer->props[i].value; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/plane.c new/libliftoff-v0.5.0/plane.c --- old/libliftoff-v0.4.1/plane.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/plane.c 2024-05-28 00:22:40.000000000 +0200 @@ -43,6 +43,7 @@ drmModePropertyRes *prop; uint64_t value; bool has_type = false, has_zpos = false; + ssize_t core_prop_idx; liftoff_list_for_each(plane, &device->planes, link) { if (plane->id == id) { @@ -105,6 +106,11 @@ return NULL; } } + + core_prop_idx = core_property_index(prop->name); + if (core_prop_idx >= 0) { + plane->core_props[core_prop_idx] = prop; + } } drmModeFreeObjectProperties(drm_props); @@ -173,12 +179,16 @@ } static const drmModePropertyRes * -plane_get_property(struct liftoff_plane *plane, const char *name) +plane_get_property(struct liftoff_plane *plane, + const struct liftoff_layer_property *layer_prop) { size_t i; + if (layer_prop->core_index >= 0) + return plane->core_props[layer_prop->core_index]; + for (i = 0; i < plane->props_len; i++) { - if (strcmp(plane->props[i]->name, name) == 0) { + if (strcmp(plane->props[i]->name, layer_prop->name) == 0) { return plane->props[i]; } } @@ -276,16 +286,16 @@ } static int -set_plane_prop_str(struct liftoff_plane *plane, drmModeAtomicReq *req, - const char *name, uint64_t value) +set_plane_core_prop(struct liftoff_plane *plane, drmModeAtomicReq *req, + enum liftoff_core_property core_prop, uint64_t value) { const drmModePropertyRes *prop; - prop = plane_get_property(plane, name); + prop = plane->core_props[core_prop]; if (prop == NULL) { liftoff_log(LIFTOFF_DEBUG, - "plane %"PRIu32" is missing the %s property", - plane->id, name); + "plane %"PRIu32" is missing core property %d", + plane->id, core_prop); return -EINVAL; } @@ -355,33 +365,34 @@ cursor = drmModeAtomicGetCursor(req); if (layer == NULL) { - ret = set_plane_prop_str(plane, req, "FB_ID", 0); + ret = set_plane_core_prop(plane, req, LIFTOFF_PROP_FB_ID, 0); if (ret != 0) { return ret; } - return set_plane_prop_str(plane, req, "CRTC_ID", 0); + return set_plane_core_prop(plane, req, LIFTOFF_PROP_CRTC_ID, 0); } - ret = set_plane_prop_str(plane, req, "CRTC_ID", layer->output->crtc_id); + ret = set_plane_core_prop(plane, req, LIFTOFF_PROP_CRTC_ID, + layer->output->crtc_id); if (ret != 0) { return ret; } for (i = 0; i < layer->props_len; i++) { layer_prop = &layer->props[i]; - if (strcmp(layer_prop->name, "zpos") == 0) { + if (layer_prop->core_index == LIFTOFF_PROP_ZPOS) { /* We don't yet support setting the zpos property. We * only use it (read-only) during plane allocation. */ continue; } - plane_prop = plane_get_property(plane, layer_prop->name); + plane_prop = plane_get_property(plane, layer_prop); if (plane_prop == NULL) { - if (strcmp(layer_prop->name, "alpha") == 0 && + if (layer_prop->core_index == LIFTOFF_PROP_ALPHA && layer_prop->value == 0xFFFF) { continue; /* Layer is completely opaque */ } - if (strcmp(layer_prop->name, "rotation") == 0 && + if (layer_prop->core_index == LIFTOFF_PROP_ROTATION && layer_prop->value == DRM_MODE_ROTATE_0) { continue; /* Layer isn't rotated */ } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/test/bench.c new/libliftoff-v0.5.0/test/bench.c --- old/libliftoff-v0.4.1/test/bench.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/test/bench.c 2024-05-28 00:22:40.000000000 +0200 @@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 200112L #include <assert.h> #include <libliftoff.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <time.h> @@ -67,6 +68,7 @@ } liftoff_log_set_priority(LIFTOFF_SILENT); + liftoff_mock_verbose = false; for (i = 0; i < planes_len; i++) { plane_type = i == 0 ? DRM_PLANE_TYPE_PRIMARY : @@ -103,7 +105,9 @@ clock_gettime(CLOCK_MONOTONIC, &start); req = drmModeAtomicAlloc(); - ret = liftoff_output_apply(output, req, 0); + ret = liftoff_output_apply(output, req, 0, &(struct liftoff_output_apply_options){ + .timeout_ns = INT64_MAX, + }); assert(ret == 0); drmModeAtomicFree(req); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/test/libdrm_mock.c new/libliftoff-v0.5.0/test/libdrm_mock.c --- old/libliftoff-v0.4.1/test/libdrm_mock.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/test/libdrm_mock.c 2024-05-28 00:22:40.000000000 +0200 @@ -20,6 +20,7 @@ uint32_t liftoff_mock_drm_crtc_id = DRM_MODE_OBJECT_CRTC & OBJ_TYPE_MASK; size_t liftoff_mock_commit_count = 0; bool liftoff_mock_require_primary_plane = false; +bool liftoff_mock_verbose = true; struct liftoff_mock_plane { uint32_t id; @@ -347,6 +348,20 @@ } static void +debug(const char *fmt, ...) +{ + va_list args; + + if (!liftoff_mock_verbose) { + return; + } + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +static void apply_atomic_req(drmModeAtomicReq *req) { int i; @@ -359,9 +374,8 @@ plane = liftoff_mock_drm_get_plane(prop->obj_id); prop_index = get_prop_index(prop->prop_id); plane->prop_values[prop_index] = prop->value; - fprintf(stderr, "libdrm_mock: plane %"PRIu32": " - "setting %s = %"PRIu64"\n", plane->id, - plane_props[prop_index].name, prop->value); + debug("libdrm_mock: plane %"PRIu32": setting %s = %"PRIu64"\n", + plane->id, plane_props[prop_index].name, prop->value); } } @@ -401,23 +415,23 @@ has_crtc = crtc_id != 0; if (has_fb != has_crtc) { - fprintf(stderr, "libdrm_mock: plane %u: both FB_ID and " - "CRTC_ID must be set or unset together " - "(FB_ID = %"PRIu64", CRTC_ID = %"PRIu64")\n", - plane->id, fb_id, crtc_id); + debug("libdrm_mock: plane %u: both FB_ID and CRTC_ID " + "must be set or unset together " + "(FB_ID = %"PRIu64", CRTC_ID = %"PRIu64")\n", + plane->id, fb_id, crtc_id); return -EINVAL; } if (has_fb) { if (crtc_id != liftoff_mock_drm_crtc_id) { - fprintf(stderr, "libdrm_mock: plane %u: " - "invalid CRTC_ID\n", plane->id); + debug("libdrm_mock: plane %u: invalid CRTC_ID\n", + plane->id); return -EINVAL; } layer = mock_fb_get_layer(fb_id); if (layer == NULL) { - fprintf(stderr, "libdrm_mock: plane %u: " - "invalid FB_ID\n", plane->id); + debug("libdrm_mock: plane %u: invalid FB_ID\n", + plane->id); return -EINVAL; } found = false; @@ -428,9 +442,9 @@ } } if (!found) { - fprintf(stderr, "libdrm_mock: plane %u: " - "layer %p is not compatible\n", - plane->id, (void *)layer); + debug("libdrm_mock: plane %u: " + "layer %p is not compatible\n", + plane->id, (void *)layer); return -EINVAL; } @@ -443,8 +457,8 @@ if (liftoff_mock_require_primary_plane && any_plane_enabled && !primary_plane_enabled) { - fprintf(stderr, "libdrm_mock: cannot light up CRTC without " - "enabling the primary plane\n"); + debug("libdrm_mock: cannot light up CRTC without enabling the " + "primary plane\n"); return -EINVAL; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/test/libdrm_mock.h new/libliftoff-v0.5.0/test/libdrm_mock.h --- old/libliftoff-v0.4.1/test/libdrm_mock.h 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/test/libdrm_mock.h 2024-05-28 00:22:40.000000000 +0200 @@ -8,6 +8,7 @@ extern uint32_t liftoff_mock_drm_crtc_id; extern size_t liftoff_mock_commit_count; +extern bool liftoff_mock_verbose; /** * Some drivers require the primary plane to be enabled in order to light up a diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/test/meson.build new/libliftoff-v0.5.0/test/meson.build --- old/libliftoff-v0.4.1/test/meson.build 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/test/meson.build 2024-05-28 00:22:40.000000000 +0200 @@ -63,13 +63,15 @@ 'change-alpha', 'set-alpha-from-opaque', 'set-alpha-from-transparent', + 'change-position-same-intersection', + 'change-position-different-intersection', 'unset-alpha-to-opaque', 'unset-alpha-to-transparent', 'change-in-fence-fd', 'change-fb-damage-clips', ], 'priority': [ - #'basic', + 'basic', ], 'prop': [ 'default-alpha', @@ -79,6 +81,7 @@ 'unmatched', 'unset', 'in-formats', + 'fb-damage-clips', 'range', 'signed-range', 'enum', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/test/test_alloc.c new/libliftoff-v0.5.0/test/test_alloc.c --- old/libliftoff-v0.4.1/test/test_alloc.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/test/test_alloc.c 2024-05-28 00:22:40.000000000 +0200 @@ -803,7 +803,7 @@ } 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 @@ 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 @@ 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 @@ 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); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/test/test_candidate.c new/libliftoff-v0.5.0/test/test_candidate.c --- old/libliftoff-v0.4.1/test/test_candidate.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/test/test_candidate.c 2024-05-28 00:22:40.000000000 +0200 @@ -60,7 +60,7 @@ 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); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/test/test_dynamic.c new/libliftoff-v0.5.0/test/test_dynamic.c --- old/libliftoff-v0.4.1/test/test_dynamic.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/test/test_dynamic.c 2024-05-28 00:22:40.000000000 +0200 @@ -50,7 +50,7 @@ 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 @@ 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 @@ -295,6 +295,33 @@ } static void +run_change_position_same_intersection(struct context *ctx) +{ + + first_commit(ctx); + assert(liftoff_mock_plane_get_layer(ctx->mock_plane) == ctx->layer); + + liftoff_layer_set_property(ctx->other_layer, "CRTC_X", 1); + + second_commit(ctx, true); + assert(liftoff_mock_plane_get_layer(ctx->mock_plane) == ctx->layer); +} + +static void +run_change_position_different_intersection(struct context *ctx) +{ + + first_commit(ctx); + assert(liftoff_mock_plane_get_layer(ctx->mock_plane) == ctx->layer); + + liftoff_layer_set_property(ctx->other_layer, "CRTC_X", 2000); + liftoff_layer_set_property(ctx->other_layer, "CRTC_Y", 2000); + + second_commit(ctx, false); + assert(liftoff_mock_plane_get_layer(ctx->mock_plane) == ctx->layer); +} + +static void run_change_in_fence_fd(struct context *ctx) { liftoff_layer_set_property(ctx->layer, "IN_FENCE_FD", 42); @@ -334,6 +361,8 @@ { .name = "change-alpha", .run = run_change_alpha }, { .name = "set-alpha-from-opaque", .run = run_set_alpha_from_opaque }, { .name = "set-alpha-from-transparent", .run = run_set_alpha_from_transparent }, + { .name = "change-position-same-intersection", .run = run_change_position_same_intersection }, + { .name = "change-position-different-intersection", .run = run_change_position_different_intersection }, { .name = "unset-alpha-to-opaque", .run = run_unset_alpha_to_opaque }, { .name = "unset-alpha-to-transparent", .run = run_unset_alpha_to_transparent }, { .name = "change-in-fence-fd", .run = run_change_in_fence_fd }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/test/test_priority.c new/libliftoff-v0.5.0/test/test_priority.c --- old/libliftoff-v0.4.1/test/test_priority.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/test/test_priority.c 2024-05-28 00:22:40.000000000 +0200 @@ -76,7 +76,7 @@ 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); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libliftoff-v0.4.1/test/test_prop.c new/libliftoff-v0.5.0/test/test_prop.c --- old/libliftoff-v0.4.1/test/test_prop.c 2023-03-05 19:36:15.000000000 +0100 +++ new/libliftoff-v0.5.0/test/test_prop.c 2024-05-28 00:22:40.000000000 +0200 @@ -34,7 +34,7 @@ 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); @@ -277,6 +277,38 @@ return 0; } +static int +test_ignored_prop(const char *name) +{ + struct liftoff_mock_plane *mock_plane; + int drm_fd; + struct liftoff_device *device; + struct liftoff_output *output; + struct liftoff_layer *layer; + + mock_plane = liftoff_mock_drm_create_plane(DRM_PLANE_TYPE_PRIMARY); + + drm_fd = liftoff_mock_drm_open(); + device = liftoff_device_create(drm_fd); + assert(device != NULL); + + liftoff_device_register_all_planes(device); + + output = liftoff_output_create(device, liftoff_mock_drm_crtc_id); + layer = add_layer(output, 0, 0, 1920, 1080); + liftoff_layer_set_property(layer, name, 0); + + liftoff_mock_plane_add_compatible_layer(mock_plane, layer); + + commit(drm_fd, output); + assert(liftoff_mock_plane_get_layer(mock_plane) == layer); + + liftoff_device_destroy(device); + close(drm_fd); + + return 0; +} + struct single_format_modifier_blob { struct drm_format_modifier_blob base; uint32_t formats[1]; @@ -497,6 +529,8 @@ return test_unset_prop(); } else if (strcmp(test_name, "in-formats") == 0) { return test_in_formats(); + } else if (strcmp(test_name, "fb-damage-clips") == 0) { + return test_ignored_prop("FB_DAMAGE_CLIPS"); } else if (strcmp(test_name, "range") == 0) { return test_range(); } else if (strcmp(test_name, "signed-range") == 0) {
