From 78f01927b62bbe2ebb13d38eb4e561f437a6de97 Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Tue, 1 Dec 2020 16:39:47 -0300 Subject: [PATCH] libweston: add struct weston_drm_format Add struct weston_drm_format, which contains a DRM format and a list of modifiers. The patch also adds struct weston_drm_format_array and some helper functions to handle these two new structs: init/fini, find elements, add elements, etc. This will be useful in the next commits in which we add support to dmabuf-hints. It also allows a cleanup in the DRM-backend, where we currently have a similar struct in drm_plane but with no helper functions, so the code to handle it is scattered throughout the functions and there is a lot of repetition. This patch is based on previous work of Scott Anderson (@ascent). Signed-off-by: Scott Anderson Signed-off-by: Leandro Ribeiro --- libweston/drm-formats.c | 516 +++++++++++++++++++++++++++++++++ libweston/libweston-internal.h | 66 +++++ libweston/meson.build | 1 + 3 files changed, 583 insertions(+) create mode 100644 libweston/drm-formats.c diff --git a/libweston/drm-formats.c b/libweston/drm-formats.c new file mode 100644 index 00000000..2c5e58bb --- /dev/null +++ b/libweston/drm-formats.c @@ -0,0 +1,516 @@ +/* + * Copyright © 2021 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 + +#include +#include "libweston-internal.h" +#include "shared/weston-drm-fourcc.h" + +/** + * Create and initialize a weston_drm_format_array + * + * @return The weston_drm_format_array, or NULL on failure + */ +WL_EXPORT struct weston_drm_format_array * +weston_drm_format_array_create(void) +{ + struct weston_drm_format_array *formats; + + formats = zalloc(sizeof(*formats)); + if (!formats) { + weston_log("%s: out of memory\n", __func__); + return NULL; + } + + weston_drm_format_array_init(formats); + + return formats; +} + +/** + * Initialize a weston_drm_format_array + * + * @param formats The weston_drm_format_array to initialize + */ +WL_EXPORT void +weston_drm_format_array_init(struct weston_drm_format_array *formats) +{ + wl_array_init(&formats->arr); +} + +/** + * Fini and destroy a weston_drm_format_array + * + * @param formats The weston_drm_format_array to destroy + */ +WL_EXPORT void +weston_drm_format_array_destroy(struct weston_drm_format_array *formats) +{ + weston_drm_format_array_fini(formats); + free(formats); +} + +/** + * Finish a weston_drm_format_array + * + * It releases the modifiers set for each format and then the + * formats array itself. + * + * @param formats The weston_drm_format_array to finish + */ +WL_EXPORT void +weston_drm_format_array_fini(struct weston_drm_format_array *formats) +{ + struct weston_drm_format *fmt; + + wl_array_for_each(fmt, &formats->arr) + wl_array_release(&fmt->modifiers); + + wl_array_release(&formats->arr); +} + +static int +add_format_and_modifiers(struct weston_drm_format_array *formats, + uint32_t format, struct wl_array *modifiers) +{ + struct weston_drm_format *fmt; + int ret; + + fmt = weston_drm_format_array_add_format(formats, format); + if (!fmt) + return -1; + + ret = wl_array_copy(&fmt->modifiers, modifiers); + if (ret < 0) { + weston_log("%s: out of memory\n", __func__); + return -1; + } + + return 0; +} + +/** + * Replace the content of a weston_drm_format_array + * + * Frees the content of the array and then perform a deep copy using + * source_formats. It duplicates the array of formats and for each format it + * duplicates the modifiers set as well. + * + * @param formats The weston_drm_format_array that gets its content replaced + * @param source_formats The weston_drm_format_array to copy + * @return 0 on success, -1 on failure + */ +WL_EXPORT int +weston_drm_format_array_replace(struct weston_drm_format_array *formats, + const struct weston_drm_format_array *source_formats) +{ + struct weston_drm_format *source_fmt; + int ret; + + weston_drm_format_array_fini(formats); + weston_drm_format_array_init(formats); + + wl_array_for_each(source_fmt, &source_formats->arr) { + ret = add_format_and_modifiers(formats, source_fmt->format, + &source_fmt->modifiers); + if (ret < 0) + return -1; + } + + return 0; +} + +/** + * Add format to weston_drm_format_array + * + * Adding repeated formats is considered an error. + * + * @param formats The weston_drm_format_array that receives the format + * @param format The format to add to the array + * @return The weston_drm_format, or NULL on failure + */ +WL_EXPORT struct weston_drm_format * +weston_drm_format_array_add_format(struct weston_drm_format_array *formats, + uint32_t format) +{ + struct weston_drm_format *fmt; + + /* We should not try to add repeated formats to an array. */ + assert(!weston_drm_format_array_find_format(formats, format)); + + fmt = wl_array_add(&formats->arr, sizeof(*fmt)); + if (!fmt) { + weston_log("%s: out of memory\n", __func__); + return NULL; + } + + fmt->format = format; + wl_array_init(&fmt->modifiers); + + return fmt; +} + +/** + * Remove latest format added to a weston_drm_format_array + * + * Calling this function for an empty array is an error, at least one element + * must be in the array. + * + * @param formats The weston_drm_format_array from which the format is removed + */ +WL_EXPORT void +weston_drm_format_array_remove_latest_format(struct weston_drm_format_array *formats) +{ + struct wl_array *array = &formats->arr; + struct weston_drm_format *fmt; + + assert(array->size >= sizeof(*fmt)); + + array->size -= sizeof(*fmt); + + fmt = array->data + array->size; + wl_array_release(&fmt->modifiers); +} + +/** + * Find format in a weston_drm_format_array + * + * @param formats The weston_drm_format_array where to look for the format + * @param format The format to look for + * @return The weston_drm_format if format was found, or NULL otherwise + */ +WL_EXPORT struct weston_drm_format * +weston_drm_format_array_find_format(const struct weston_drm_format_array *formats, + uint32_t format) +{ + struct weston_drm_format *fmt; + + wl_array_for_each(fmt, &formats->arr) + if (fmt->format == format) + return fmt; + + return NULL; +} + +/** + * Compare the content of two weston_drm_format_array + * + * @param formats_A One of the weston_drm_format_array to compare + * @param formats_B The other weston_drm_format_array to compare + * @return True if both sets are equivalent, false otherwise + */ +WL_EXPORT bool +weston_drm_format_array_equal(const struct weston_drm_format_array *formats_A, + const struct weston_drm_format_array *formats_B) +{ + struct weston_drm_format *fmt_A, *fmt_B; + const uint64_t *modifiers_A; + unsigned num_modifiers_A, num_modifiers_B; + unsigned int i; + + if (formats_A->arr.size != formats_B->arr.size) + return false; + + wl_array_for_each(fmt_A, &formats_A->arr) { + fmt_B = weston_drm_format_array_find_format(formats_B, + fmt_A->format); + if (!fmt_B) + return false; + + modifiers_A = weston_drm_format_get_modifiers(fmt_A, &num_modifiers_A); + weston_drm_format_get_modifiers(fmt_B, &num_modifiers_B); + if (num_modifiers_A != num_modifiers_B) + return false; + for (i = 0; i < num_modifiers_A; i++) + if (!weston_drm_format_has_modifier(fmt_B, modifiers_A[i])) + return false; + } + + return true; +} + +/** + * Joins two weston_drm_format_array, keeping the result in A + * + * @param formats_A The weston_drm_format_array that receives the formats from B + * @param formats_B The weston_drm_format_array whose formats are added to A + * @return 0 on success, -1 on failure + */ +WL_EXPORT int +weston_drm_format_array_join(struct weston_drm_format_array *formats_A, + const struct weston_drm_format_array *formats_B) +{ + struct weston_drm_format *fmt_A, *fmt_B; + const uint64_t *modifiers; + unsigned int num_modifiers; + unsigned int i; + int ret; + + wl_array_for_each(fmt_B, &formats_B->arr) { + fmt_A = weston_drm_format_array_find_format(formats_A, + fmt_B->format); + if (!fmt_A) { + fmt_A = weston_drm_format_array_add_format(formats_A, + fmt_B->format); + if (!fmt_A) + return -1; + } + + modifiers = weston_drm_format_get_modifiers(fmt_B, &num_modifiers); + for (i = 0; i < num_modifiers; i++) { + if (weston_drm_format_has_modifier(fmt_A, modifiers[i])) + continue; + ret = weston_drm_format_add_modifier(fmt_A, modifiers[i]); + if (ret < 0) + return -1; + } + } + + return 0; +} + +static int +modifiers_intersect(const struct weston_drm_format *fmt_A, + const struct weston_drm_format *fmt_B, + struct wl_array *modifiers_result) +{ + const uint64_t *modifiers; + unsigned int num_modifiers; + uint64_t *mod; + unsigned int i; + + modifiers = weston_drm_format_get_modifiers(fmt_A, &num_modifiers); + for (i = 0; i < num_modifiers; i++) { + if (!weston_drm_format_has_modifier(fmt_B, modifiers[i])) + continue; + mod = wl_array_add(modifiers_result, sizeof(modifiers[i])); + if (!mod) { + weston_log("%s: out of memory\n", __func__); + return -1; + } + *mod = modifiers[i]; + } + + return 0; +} + +/** + * Compute the intersection between two DRM-format arrays + * + * Callers are responsible for destroying the returned array. + * + * @param formats_A One of the weston_drm_format_array + * @param formats_B The other weston_drm_format_array + * @return Array with formats and modifiers that are present + * on both A and B, or NULL on failure + */ +WL_EXPORT struct weston_drm_format_array * +weston_drm_format_array_intersect(const struct weston_drm_format_array *formats_A, + const struct weston_drm_format_array *formats_B) +{ + struct weston_drm_format_array *formats_result; + struct weston_drm_format *fmt_result, *fmt_A, *fmt_B; + int ret; + + formats_result = weston_drm_format_array_create(); + if (!formats_result) + return NULL; + + wl_array_for_each(fmt_A, &formats_A->arr) { + fmt_B = weston_drm_format_array_find_format(formats_B, + fmt_A->format); + if (!fmt_B) + continue; + + fmt_result = weston_drm_format_array_add_format(formats_result, + fmt_A->format); + if (!fmt_result) + goto err; + + ret = modifiers_intersect(fmt_A, fmt_B, &fmt_result->modifiers); + if (ret < 0) + goto err; + + if (fmt_result->modifiers.size == 0) + weston_drm_format_array_remove_latest_format(formats_result); + } + + return formats_result; + +err: + weston_drm_format_array_destroy(formats_result); + return NULL; +} + +static int +modifiers_subtract(const struct weston_drm_format *fmt_A, + const struct weston_drm_format *fmt_B, + struct wl_array *modifiers_result) +{ + const uint64_t *modifiers; + unsigned int num_modifiers; + uint64_t *mod; + unsigned int i; + + modifiers = weston_drm_format_get_modifiers(fmt_A, &num_modifiers); + for (i = 0; i < num_modifiers; i++) { + if (weston_drm_format_has_modifier(fmt_B, modifiers[i])) + continue; + mod = wl_array_add(modifiers_result, sizeof(modifiers[i])); + if (!mod) { + weston_log("%s: out of memory\n", __func__); + return -1; + } + *mod = modifiers[i]; + } + + return 0; +} + +/** + * Compute the subtraction between two DRM-format arrays, keeping the result in A + * + * @param formats_A The minuend weston_drm_format_array + * @param formats_B The subtrahend weston_drm_format_array + * @return 0 on success, -1 on failure + */ +WL_EXPORT int +weston_drm_format_array_subtract(struct weston_drm_format_array *formats_A, + const struct weston_drm_format_array *formats_B) +{ + struct weston_drm_format_array *formats_result; + struct weston_drm_format *fmt_result, *fmt_A, *fmt_B; + int ret; + + formats_result = weston_drm_format_array_create(); + if (!formats_result) + return -1; + + wl_array_for_each(fmt_A, &formats_A->arr) { + fmt_B = weston_drm_format_array_find_format(formats_B, + fmt_A->format); + if (!fmt_B) { + ret = add_format_and_modifiers(formats_result, fmt_A->format, + &fmt_A->modifiers); + if (ret < 0) + goto err; + + continue; + } + + fmt_result = weston_drm_format_array_add_format(formats_result, + fmt_A->format); + if (!fmt_result) + goto err; + + ret = modifiers_subtract(fmt_A, fmt_B, &fmt_result->modifiers); + if (ret < 0) + goto err; + + if (fmt_result->modifiers.size == 0) + weston_drm_format_array_remove_latest_format(formats_result); + } + + ret = weston_drm_format_array_replace(formats_A, formats_result); + if (ret < 0) + goto err; + + return 0; + +err: + weston_drm_format_array_destroy(formats_result); + return -1; +} + +/** + * Add modifier to modifier set of a weston_drm_format. + * + * Adding repeated modifiers is considered an error. + * + * @param format The weston_drm_format that owns the modifier set to which + * the modifier should be added + * @param modifier The modifier to add + * @return 0 on success, -1 on failure + */ +WL_EXPORT int +weston_drm_format_add_modifier(struct weston_drm_format *format, + uint64_t modifier) +{ + uint64_t *mod; + + /* We should not try to add repeated modifiers to a set. */ + assert(!weston_drm_format_has_modifier(format, modifier)); + + mod = wl_array_add(&format->modifiers, sizeof(*mod)); + if (!mod) { + weston_log("%s: out of memory\n", __func__); + return -1; + } + *mod = modifier; + + return 0; +} + +/** + * Check if modifier set of a weston_drm_format contains a certain modifier + * + * @param format The weston_drm_format that owns the modifier set where to + * look for the modifier + * @param modifier The modifier to look for + * @return True if modifier was found, false otherwise + */ +WL_EXPORT bool +weston_drm_format_has_modifier(const struct weston_drm_format *format, + uint64_t modifier) +{ + const uint64_t *modifiers; + unsigned int num_modifiers; + unsigned int i; + + modifiers = weston_drm_format_get_modifiers(format, &num_modifiers); + for (i = 0; i < num_modifiers; i++) + if (modifiers[i] == modifier) + return true; + + return false; +} + +/** + * Get array of modifiers and modifiers count from a weston_drm_format + * + * @param format The weston_drm_format that contains the modifiers + * @param count_out Parameter that receives the modifiers count + * @return The array of modifiers + */ +WL_EXPORT const uint64_t * +weston_drm_format_get_modifiers(const struct weston_drm_format *format, + unsigned int *count_out) +{ + *count_out = format->modifiers.size / sizeof(uint64_t); + return format->modifiers.data; +} diff --git a/libweston/libweston-internal.h b/libweston/libweston-internal.h index 66c38e86..6921d7d6 100644 --- a/libweston/libweston-internal.h +++ b/libweston/libweston-internal.h @@ -325,6 +325,72 @@ void weston_protected_surface_send_event(struct protected_surface *psurface, enum weston_hdcp_protection protection); +/* weston_drm_format */ + +struct weston_drm_format { + uint32_t format; + struct wl_array modifiers; +}; + +struct weston_drm_format_array { + struct wl_array arr; +}; + +struct weston_drm_format_array * +weston_drm_format_array_create(void); + +void +weston_drm_format_array_init(struct weston_drm_format_array *formats); + +void +weston_drm_format_array_destroy(struct weston_drm_format_array *formats); + +void +weston_drm_format_array_fini(struct weston_drm_format_array *formats); + +int +weston_drm_format_array_replace(struct weston_drm_format_array *formats, + const struct weston_drm_format_array *source_formats); + +struct weston_drm_format * +weston_drm_format_array_add_format(struct weston_drm_format_array *formats, + uint32_t format); + +void +weston_drm_format_array_remove_latest_format(struct weston_drm_format_array *formats); + +struct weston_drm_format * +weston_drm_format_array_find_format(const struct weston_drm_format_array *formats, + uint32_t format); + +bool +weston_drm_format_array_equal(const struct weston_drm_format_array *formats_A, + const struct weston_drm_format_array *formats_B); + +int +weston_drm_format_array_join(struct weston_drm_format_array *formats_A, + const struct weston_drm_format_array *formats_B); + +struct weston_drm_format_array * +weston_drm_format_array_intersect(const struct weston_drm_format_array *formats_A, + const struct weston_drm_format_array *formats_B); + +int +weston_drm_format_array_subtract(struct weston_drm_format_array *formats_A, + const struct weston_drm_format_array *formats_B); + +int +weston_drm_format_add_modifier(struct weston_drm_format *format, + uint64_t modifier); + +bool +weston_drm_format_has_modifier(const struct weston_drm_format *format, + uint64_t modifier); + +const uint64_t * +weston_drm_format_get_modifiers(const struct weston_drm_format *format, + unsigned int *count_out); + /* others */ int wl_data_device_manager_init(struct wl_display *display); diff --git a/libweston/meson.build b/libweston/meson.build index ee1d128c..068907a8 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -15,6 +15,7 @@ srcs_libweston = [ 'compositor.c', 'content-protection.c', 'data-device.c', + 'drm-formats.c', 'input.c', 'linux-dmabuf.c', 'linux-explicit-synchronization.c',