From a84806a88e69b64ad80d3ffdaf302d49c01b529b Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Wed, 10 Jan 2024 18:49:17 -0300 Subject: [PATCH] libweston: add unique id generator This is preparation for the CM&HDR protocol implementation. It requires us to give a unique id to each color-profile, so let's do that. In this commit we introduce a generic id generator to libweston, and its first user: the color-profile. Signed-off-by: Leandro Ribeiro --- include/libweston/libweston.h | 2 + libweston/color.c | 5 + libweston/color.h | 3 + libweston/compositor.c | 9 ++ libweston/id-number-allocator.c | 185 +++++++++++++++++++++++++++++ libweston/id-number-allocator.h | 45 +++++++ libweston/meson.build | 1 + shared/weston-assert.h | 13 ++ tests/color-metadata-errors-test.c | 18 +++ 9 files changed, 281 insertions(+) create mode 100644 libweston/id-number-allocator.c create mode 100644 libweston/id-number-allocator.h diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 4c66ecf8b..9d0ba6def 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1438,6 +1438,8 @@ struct weston_compositor { uint32_t capabilities; /* combination of enum weston_capability */ struct weston_color_manager *color_manager; + struct weston_idalloc *color_profile_id_generator; + struct weston_renderer *renderer; const struct pixel_format_info *read_format; diff --git a/libweston/color.c b/libweston/color.c index 2f322e944..7c9c3e4b6 100644 --- a/libweston/color.c +++ b/libweston/color.c @@ -37,6 +37,7 @@ #include #include "color.h" +#include "id-number-allocator.h" #include "libweston-internal.h" #include #include "shared/xalloc.h" @@ -73,6 +74,9 @@ weston_color_profile_unref(struct weston_color_profile *cprof) if (--cprof->ref_count > 0) return; + weston_idalloc_put_id(cprof->cm->compositor->color_profile_id_generator, + cprof->id); + cprof->cm->destroy_color_profile(cprof); } @@ -109,6 +113,7 @@ weston_color_profile_init(struct weston_color_profile *cprof, { cprof->cm = cm; cprof->ref_count = 1; + cprof->id = weston_idalloc_get_id(cm->compositor->color_profile_id_generator); } /** diff --git a/libweston/color.h b/libweston/color.h index f630f237e..2cc8f11ba 100644 --- a/libweston/color.h +++ b/libweston/color.h @@ -40,6 +40,9 @@ struct weston_color_profile { struct weston_color_manager *cm; int ref_count; char *description; + + /* Unique id to be used by the CM&HDR protocol extension. */ + uint32_t id; }; /** Type or formula for a curve */ diff --git a/libweston/compositor.c b/libweston/compositor.c index 8593d3bef..166d6a12d 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -80,6 +80,7 @@ #include "backend.h" #include "libweston-internal.h" #include "color.h" +#include "id-number-allocator.h" #include "output-capture.h" #include "pixman-renderer.h" #include "renderer-gl/gl-renderer.h" @@ -9171,6 +9172,11 @@ weston_compositor_shutdown(struct weston_compositor *ec) ec->color_manager = NULL; } + /* Already destroyed color manager, now we can safely destroy the color + * profile id generator. */ + weston_idalloc_destroy(ec->color_profile_id_generator); + ec->color_profile_id_generator = NULL; + if (ec->renderer) ec->renderer->destroy(ec); @@ -9284,6 +9290,9 @@ weston_compositor_backends_loaded(struct weston_compositor *compositor) if (!compositor->color_manager) return -1; + /* Create id generator before initing the color manager. */ + compositor->color_profile_id_generator = weston_idalloc_create(compositor); + if (!compositor->color_manager->init(compositor->color_manager)) return -1; diff --git a/libweston/id-number-allocator.c b/libweston/id-number-allocator.c new file mode 100644 index 000000000..e1b1ee791 --- /dev/null +++ b/libweston/id-number-allocator.c @@ -0,0 +1,185 @@ +/* + * Copyright 2024 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include "id-number-allocator.h" +#include "shared/helpers.h" +#include "shared/xalloc.h" +#include "shared/weston-assert.h" + +struct weston_idalloc { + struct weston_compositor *compositor; + + /* Each value on this array is a bucket of size 32. Bit is 0 if the id + * is available, 1 otherwise. */ + uint32_t *buckets; + + uint32_t num_buckets; + uint32_t lowest_free_bucket; +}; + +/** + * Creates a unique id allocator + * + * \param compositor The compositor + * \return The unique id allocator + */ +WESTON_EXPORT_FOR_TESTS struct weston_idalloc * +weston_idalloc_create(struct weston_compositor *compositor) +{ + struct weston_idalloc *idalloc; + + idalloc = xzalloc(sizeof(*idalloc)); + + idalloc->compositor = compositor; + + /* Start with 2 buckets. If necessary we increase that on demand. */ + idalloc->num_buckets = 2; + idalloc->buckets = xzalloc(idalloc->num_buckets * sizeof(*idalloc->buckets)); + + /* Let's reserve id 0 for errors. So start with id 0 already taken. Set + * the first bit of the first bucket to 1. */ + idalloc->buckets[idalloc->lowest_free_bucket] = 1; + + return idalloc; +} + +/** + * Destroys a unique id allocator + * + * \param idalloc The unique id allocator to destroy + */ +WESTON_EXPORT_FOR_TESTS void +weston_idalloc_destroy(struct weston_idalloc *idalloc) +{ + /* Sanity check: id 0 should still be taken. */ + weston_assert_true(idalloc->compositor, idalloc->buckets[0] & 1); + + free(idalloc->buckets); + free(idalloc); +} + +static void +update_lowest_free_bucket(struct weston_idalloc *idalloc) +{ + uint32_t old_lowest_free_bucket = idalloc->lowest_free_bucket; + uint32_t *bucket; + unsigned int i; + + for (i = old_lowest_free_bucket; i < idalloc->num_buckets; i++) { + bucket = &idalloc->buckets[i]; + + /* Skip full bucket */ + if (*bucket == 0xffffffff) + continue; + + idalloc->lowest_free_bucket = i; + return; + } + + /* We didn't find any free bucket, so we need to add more buckets. The + * first one (from the new added) will be the lowest free. */ + idalloc->lowest_free_bucket = idalloc->num_buckets; + idalloc->num_buckets *= 2; + idalloc->buckets = xrealloc(idalloc->buckets, + idalloc->num_buckets * sizeof(*idalloc->buckets)); +} + +/** + * Gets an id from unique id allocator + * + * \param idalloc The unique id allocator + * \return The unique id + */ +WESTON_EXPORT_FOR_TESTS uint32_t +weston_idalloc_get_id(struct weston_idalloc *idalloc) +{ + uint32_t *bucket = &idalloc->buckets[idalloc->lowest_free_bucket]; + unsigned int i; + uint32_t id; + + /* Sanity check: lowest free bucket should not be full. */ + weston_assert_uint32_neq(idalloc->compositor, *bucket, 0xffffffff); + + for (i = 0; i < 32; i++) { + /* Id already used, skip it. */ + if ((*bucket >> i) & 1) + continue; + + /* Found free id, take it and set it to 1 on the bucket. */ + *bucket |= 1 << i; + id = (32 * idalloc->lowest_free_bucket) + i; + + /* Bucket may become full... */ + if (*bucket == 0xffffffff) + update_lowest_free_bucket(idalloc); + + return id; + } + + /* We need to find an available id. */ + weston_assert_not_reached(idalloc->compositor, + "should be able to allocate unique id"); +} + +/** + * Releases a id back to unique id allocator + * + * When an id from the unique id allocator will not be used anymore, users + * should call this function so that this id can be advertised again by the id + * allocator. + * + * \param idalloc The unique id allocator + * \param id The id to release + */ +WESTON_EXPORT_FOR_TESTS void +weston_idalloc_put_id(struct weston_idalloc *idalloc, uint32_t id) +{ + uint32_t bucket_index = id / 32; + uint32_t id_index_on_bucket = id % 32; + uint32_t *bucket; + + /* Shouldn't try to release index 0, we never advertise this id to anyone. */ + weston_assert_uint32_neq(idalloc->compositor, id, 0); + + /* Bucket index should be lower than num_buckets. */ + weston_assert_uint32_lt(idalloc->compositor, + bucket_index, idalloc->num_buckets); + + bucket = &idalloc->buckets[bucket_index]; + + /* Shouldn't try to release a free index. */ + weston_assert_true(idalloc->compositor, + (*bucket >> id_index_on_bucket) & 1); + + /* We now have an available index id on this bucket, so it may become + * the lowest bucket. */ + if (bucket_index < idalloc->lowest_free_bucket) + idalloc->lowest_free_bucket = bucket_index; + + /* Zero the bit on the bucket. */ + *bucket &= ~(1 << id_index_on_bucket); +} diff --git a/libweston/id-number-allocator.h b/libweston/id-number-allocator.h new file mode 100644 index 000000000..0e8c95d35 --- /dev/null +++ b/libweston/id-number-allocator.h @@ -0,0 +1,45 @@ +/* + * Copyright 2023 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WESTON_ID_NUMBER_ALLOCATOR_H +#define WESTON_ID_NUMBER_ALLOCATOR_H + +#include + +struct weston_compositor; + +struct weston_idalloc * +weston_idalloc_create(struct weston_compositor *compositor); + +void +weston_idalloc_destroy(struct weston_idalloc *idalloc); + +uint32_t +weston_idalloc_get_id(struct weston_idalloc *idalloc); + +void +weston_idalloc_put_id(struct weston_idalloc *idalloc, uint32_t id); + +#endif /* WESTON_ID_NUMBER_ALLOCATOR_H */ diff --git a/libweston/meson.build b/libweston/meson.build index a83a52dc3..8951675a6 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -19,6 +19,7 @@ srcs_libweston = [ 'content-protection.c', 'data-device.c', 'drm-formats.c', + 'id-number-allocator.c', 'input.c', 'linux-dmabuf.c', 'linux-explicit-synchronization.c', diff --git a/shared/weston-assert.h b/shared/weston-assert.h index f0fb85612..b0771e1d0 100644 --- a/shared/weston-assert.h +++ b/shared/weston-assert.h @@ -74,6 +74,13 @@ weston_assert_fail_(const struct weston_compositor *compositor, const char *fmt, cond; \ }) +#define weston_assert_not_reached(compositor, reason) \ +do { \ + struct weston_compositor *ec = compositor; \ + custom_assert_fail_(ec, "%s:%u: Assertion failed! This should not be reached: %s\n", \ + __FILE__, __LINE__, reason); \ +} while (0) + #define weston_assert_true(compositor, a) \ weston_assert_(compositor, a, true, bool, "%d", ==) @@ -89,5 +96,11 @@ weston_assert_fail_(const struct weston_compositor *compositor, const char *fmt, #define weston_assert_double_eq(compositor, a, b) \ weston_assert_(compositor, a, b, double, "%.10g", ==) +#define weston_assert_uint32_neq(compositor, a, b) \ + weston_assert_(compositor, a, b, uint32_t, "%u", !=) + +#define weston_assert_uint32_lt(compositor, a, b) \ + weston_assert_(compositor, a, b, uint32_t, "%u", <) + #define weston_assert_str_eq(compositor, a, b) \ weston_assert_fn_(compositor, strcmp, a, b, const char *, "%s", ==) diff --git a/tests/color-metadata-errors-test.c b/tests/color-metadata-errors-test.c index 73cfcc212..0633a4f57 100644 --- a/tests/color-metadata-errors-test.c +++ b/tests/color-metadata-errors-test.c @@ -36,6 +36,7 @@ #include "libweston-internal.h" #include "backend.h" #include "color.h" +#include "id-number-allocator.h" #include "shared/xalloc.h" struct config_testcase { @@ -178,6 +179,7 @@ mock_cm_get_stock_sRGB_color_profile(struct weston_color_manager *mock_cm) mock_cprof->cm = mock_cm; mock_cprof->ref_count = 1; mock_cprof->description = xstrdup("mock cprof"); + mock_cprof->id = weston_idalloc_get_id(mock_cm->compositor->color_profile_id_generator); return mock_cprof; } @@ -209,9 +211,12 @@ TEST_P(color_characteristics_config_error, config_cases) }; struct weston_compositor mock_compositor = { .color_manager = &mock_cm.base, + .color_profile_id_generator = weston_idalloc_create(&mock_compositor), }; struct weston_output mock_output = {}; + mock_cm.base.compositor = &mock_compositor; + wl_list_init(&mock_compositor.plane_list); weston_output_init(&mock_output, &mock_compositor, "mockoutput"); @@ -235,6 +240,7 @@ TEST_P(color_characteristics_config_error, config_cases) weston_config_destroy(wc); free(logbuf); weston_output_release(&mock_output); + weston_idalloc_destroy(mock_compositor.color_profile_id_generator); } /* Setting NULL resets group_mask */ @@ -247,9 +253,12 @@ TEST(weston_output_set_color_characteristics_null) }; struct weston_compositor mock_compositor = { .color_manager = &mock_cm.base, + .color_profile_id_generator = weston_idalloc_create(&mock_compositor), }; struct weston_output mock_output = {}; + mock_cm.base.compositor = &mock_compositor; + wl_list_init(&mock_compositor.plane_list); weston_output_init(&mock_output, &mock_compositor, "mockoutput"); @@ -258,6 +267,7 @@ TEST(weston_output_set_color_characteristics_null) assert(mock_output.color_characteristics.group_mask == 0); weston_output_release(&mock_output); + weston_idalloc_destroy(mock_compositor.color_profile_id_generator); } struct value_testcase { @@ -325,12 +335,15 @@ TEST_P(hdr_metadata_type1_errors, value_cases) }; struct weston_compositor mock_compositor = { .color_manager = &mock_cm.base, + .color_profile_id_generator = weston_idalloc_create(&mock_compositor), }; struct weston_output mock_output = {}; bool ret; weston_log_set_handler(no_logger, no_logger); + mock_cm.base.compositor = &mock_compositor; + wl_list_init(&mock_compositor.plane_list); weston_output_init(&mock_output, &mock_compositor, "mockoutput"); @@ -341,6 +354,7 @@ TEST_P(hdr_metadata_type1_errors, value_cases) weston_output_color_outcome_destroy(&mock_output.color_outcome); weston_output_release(&mock_output); + weston_idalloc_destroy(mock_compositor.color_profile_id_generator); } /* Unflagged members are ignored in validity check */ @@ -366,10 +380,13 @@ TEST(hdr_metadata_type1_ignore_unflagged) }; struct weston_compositor mock_compositor = { .color_manager = &mock_cm.base, + .color_profile_id_generator = weston_idalloc_create(&mock_compositor), }; struct weston_output mock_output = {}; bool ret; + mock_cm.base.compositor = &mock_compositor; + wl_list_init(&mock_compositor.plane_list); weston_log_set_handler(no_logger, no_logger); @@ -380,4 +397,5 @@ TEST(hdr_metadata_type1_ignore_unflagged) weston_output_color_outcome_destroy(&mock_output.color_outcome); weston_output_release(&mock_output); + weston_idalloc_destroy(mock_compositor.color_profile_id_generator); }