blob: 6bc08b5da06d9caa7c247c7893651b9876fc4d93 [file] [log] [blame]
Leandro Ribeiro78f01922020-12-01 16:39:47 -03001/*
2 * Copyright © 2021 Collabora, Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26#include "config.h"
27
28#include <assert.h>
29
30#include <libweston/libweston.h>
31#include "libweston-internal.h"
32#include "shared/weston-drm-fourcc.h"
33
34/**
Leandro Ribeiro78f01922020-12-01 16:39:47 -030035 * Initialize a weston_drm_format_array
36 *
37 * @param formats The weston_drm_format_array to initialize
38 */
39WL_EXPORT void
40weston_drm_format_array_init(struct weston_drm_format_array *formats)
41{
42 wl_array_init(&formats->arr);
43}
44
45/**
Leandro Ribeiro78f01922020-12-01 16:39:47 -030046 * Finish a weston_drm_format_array
47 *
48 * It releases the modifiers set for each format and then the
49 * formats array itself.
50 *
51 * @param formats The weston_drm_format_array to finish
52 */
53WL_EXPORT void
54weston_drm_format_array_fini(struct weston_drm_format_array *formats)
55{
56 struct weston_drm_format *fmt;
57
58 wl_array_for_each(fmt, &formats->arr)
59 wl_array_release(&fmt->modifiers);
60
61 wl_array_release(&formats->arr);
62}
63
64static int
65add_format_and_modifiers(struct weston_drm_format_array *formats,
66 uint32_t format, struct wl_array *modifiers)
67{
68 struct weston_drm_format *fmt;
69 int ret;
70
71 fmt = weston_drm_format_array_add_format(formats, format);
72 if (!fmt)
73 return -1;
74
75 ret = wl_array_copy(&fmt->modifiers, modifiers);
76 if (ret < 0) {
77 weston_log("%s: out of memory\n", __func__);
78 return -1;
79 }
80
81 return 0;
82}
83
84/**
85 * Replace the content of a weston_drm_format_array
86 *
87 * Frees the content of the array and then perform a deep copy using
88 * source_formats. It duplicates the array of formats and for each format it
89 * duplicates the modifiers set as well.
90 *
91 * @param formats The weston_drm_format_array that gets its content replaced
92 * @param source_formats The weston_drm_format_array to copy
93 * @return 0 on success, -1 on failure
94 */
95WL_EXPORT int
96weston_drm_format_array_replace(struct weston_drm_format_array *formats,
97 const struct weston_drm_format_array *source_formats)
98{
99 struct weston_drm_format *source_fmt;
100 int ret;
101
102 weston_drm_format_array_fini(formats);
103 weston_drm_format_array_init(formats);
104
105 wl_array_for_each(source_fmt, &source_formats->arr) {
106 ret = add_format_and_modifiers(formats, source_fmt->format,
107 &source_fmt->modifiers);
108 if (ret < 0)
109 return -1;
110 }
111
112 return 0;
113}
114
115/**
116 * Add format to weston_drm_format_array
117 *
118 * Adding repeated formats is considered an error.
119 *
120 * @param formats The weston_drm_format_array that receives the format
121 * @param format The format to add to the array
122 * @return The weston_drm_format, or NULL on failure
123 */
124WL_EXPORT struct weston_drm_format *
125weston_drm_format_array_add_format(struct weston_drm_format_array *formats,
126 uint32_t format)
127{
128 struct weston_drm_format *fmt;
129
130 /* We should not try to add repeated formats to an array. */
131 assert(!weston_drm_format_array_find_format(formats, format));
132
133 fmt = wl_array_add(&formats->arr, sizeof(*fmt));
134 if (!fmt) {
135 weston_log("%s: out of memory\n", __func__);
136 return NULL;
137 }
138
139 fmt->format = format;
140 wl_array_init(&fmt->modifiers);
141
142 return fmt;
143}
144
145/**
146 * Remove latest format added to a weston_drm_format_array
147 *
148 * Calling this function for an empty array is an error, at least one element
149 * must be in the array.
150 *
151 * @param formats The weston_drm_format_array from which the format is removed
152 */
153WL_EXPORT void
154weston_drm_format_array_remove_latest_format(struct weston_drm_format_array *formats)
155{
156 struct wl_array *array = &formats->arr;
157 struct weston_drm_format *fmt;
158
159 assert(array->size >= sizeof(*fmt));
160
161 array->size -= sizeof(*fmt);
162
163 fmt = array->data + array->size;
164 wl_array_release(&fmt->modifiers);
165}
166
167/**
168 * Find format in a weston_drm_format_array
169 *
170 * @param formats The weston_drm_format_array where to look for the format
171 * @param format The format to look for
172 * @return The weston_drm_format if format was found, or NULL otherwise
173 */
174WL_EXPORT struct weston_drm_format *
175weston_drm_format_array_find_format(const struct weston_drm_format_array *formats,
176 uint32_t format)
177{
178 struct weston_drm_format *fmt;
179
180 wl_array_for_each(fmt, &formats->arr)
181 if (fmt->format == format)
182 return fmt;
183
184 return NULL;
185}
186
187/**
Leandro Ribeiro60c7fee2021-10-06 12:05:40 -0300188 * Counts the number of format/modifier pairs in a weston_drm_format_array
189 *
190 * @param formats The weston_drm_format_array
191 * @return The number of format/modifier pairs in the array
192 */
193WL_EXPORT unsigned int
194weston_drm_format_array_count_pairs(const struct weston_drm_format_array *formats)
195{
196 struct weston_drm_format *fmt;
197 unsigned int num_pairs = 0;
198
199 wl_array_for_each(fmt, &formats->arr)
200 num_pairs += fmt->modifiers.size / sizeof(uint64_t);
201
202 return num_pairs;
203}
204
205/**
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300206 * Compare the content of two weston_drm_format_array
207 *
208 * @param formats_A One of the weston_drm_format_array to compare
209 * @param formats_B The other weston_drm_format_array to compare
210 * @return True if both sets are equivalent, false otherwise
211 */
212WL_EXPORT bool
213weston_drm_format_array_equal(const struct weston_drm_format_array *formats_A,
214 const struct weston_drm_format_array *formats_B)
215{
216 struct weston_drm_format *fmt_A, *fmt_B;
217 const uint64_t *modifiers_A;
218 unsigned num_modifiers_A, num_modifiers_B;
219 unsigned int i;
220
221 if (formats_A->arr.size != formats_B->arr.size)
222 return false;
223
224 wl_array_for_each(fmt_A, &formats_A->arr) {
225 fmt_B = weston_drm_format_array_find_format(formats_B,
226 fmt_A->format);
227 if (!fmt_B)
228 return false;
229
230 modifiers_A = weston_drm_format_get_modifiers(fmt_A, &num_modifiers_A);
231 weston_drm_format_get_modifiers(fmt_B, &num_modifiers_B);
232 if (num_modifiers_A != num_modifiers_B)
233 return false;
234 for (i = 0; i < num_modifiers_A; i++)
235 if (!weston_drm_format_has_modifier(fmt_B, modifiers_A[i]))
236 return false;
237 }
238
239 return true;
240}
241
242/**
243 * Joins two weston_drm_format_array, keeping the result in A
244 *
245 * @param formats_A The weston_drm_format_array that receives the formats from B
246 * @param formats_B The weston_drm_format_array whose formats are added to A
247 * @return 0 on success, -1 on failure
248 */
249WL_EXPORT int
250weston_drm_format_array_join(struct weston_drm_format_array *formats_A,
251 const struct weston_drm_format_array *formats_B)
252{
253 struct weston_drm_format *fmt_A, *fmt_B;
254 const uint64_t *modifiers;
255 unsigned int num_modifiers;
256 unsigned int i;
257 int ret;
258
259 wl_array_for_each(fmt_B, &formats_B->arr) {
260 fmt_A = weston_drm_format_array_find_format(formats_A,
261 fmt_B->format);
262 if (!fmt_A) {
263 fmt_A = weston_drm_format_array_add_format(formats_A,
264 fmt_B->format);
265 if (!fmt_A)
266 return -1;
267 }
268
269 modifiers = weston_drm_format_get_modifiers(fmt_B, &num_modifiers);
270 for (i = 0; i < num_modifiers; i++) {
271 if (weston_drm_format_has_modifier(fmt_A, modifiers[i]))
272 continue;
273 ret = weston_drm_format_add_modifier(fmt_A, modifiers[i]);
274 if (ret < 0)
275 return -1;
276 }
277 }
278
279 return 0;
280}
281
282static int
283modifiers_intersect(const struct weston_drm_format *fmt_A,
284 const struct weston_drm_format *fmt_B,
285 struct wl_array *modifiers_result)
286{
287 const uint64_t *modifiers;
288 unsigned int num_modifiers;
289 uint64_t *mod;
290 unsigned int i;
291
292 modifiers = weston_drm_format_get_modifiers(fmt_A, &num_modifiers);
293 for (i = 0; i < num_modifiers; i++) {
294 if (!weston_drm_format_has_modifier(fmt_B, modifiers[i]))
295 continue;
296 mod = wl_array_add(modifiers_result, sizeof(modifiers[i]));
297 if (!mod) {
298 weston_log("%s: out of memory\n", __func__);
299 return -1;
300 }
301 *mod = modifiers[i];
302 }
303
304 return 0;
305}
306
307/**
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300308 * Compute the intersection between two DRM-format arrays, keeping the result in A
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300309 *
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300310 * @param formats_A The weston_drm_format_array that keeps the result
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300311 * @param formats_B The other weston_drm_format_array
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300312 * @return 0 on success, -1 on failure
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300313 */
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300314WL_EXPORT int
315weston_drm_format_array_intersect(struct weston_drm_format_array *formats_A,
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300316 const struct weston_drm_format_array *formats_B)
317{
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300318 struct weston_drm_format_array formats_result;
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300319 struct weston_drm_format *fmt_result, *fmt_A, *fmt_B;
320 int ret;
321
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300322 weston_drm_format_array_init(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300323
324 wl_array_for_each(fmt_A, &formats_A->arr) {
325 fmt_B = weston_drm_format_array_find_format(formats_B,
326 fmt_A->format);
327 if (!fmt_B)
328 continue;
329
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300330 fmt_result = weston_drm_format_array_add_format(&formats_result,
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300331 fmt_A->format);
332 if (!fmt_result)
333 goto err;
334
335 ret = modifiers_intersect(fmt_A, fmt_B, &fmt_result->modifiers);
336 if (ret < 0)
337 goto err;
338
339 if (fmt_result->modifiers.size == 0)
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300340 weston_drm_format_array_remove_latest_format(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300341 }
342
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300343 ret = weston_drm_format_array_replace(formats_A, &formats_result);
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300344 if (ret < 0)
345 goto err;
346
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300347 weston_drm_format_array_fini(&formats_result);
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300348 return 0;
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300349
350err:
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300351 weston_drm_format_array_fini(&formats_result);
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300352 return -1;
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300353}
354
355static int
356modifiers_subtract(const struct weston_drm_format *fmt_A,
357 const struct weston_drm_format *fmt_B,
358 struct wl_array *modifiers_result)
359{
360 const uint64_t *modifiers;
361 unsigned int num_modifiers;
362 uint64_t *mod;
363 unsigned int i;
364
365 modifiers = weston_drm_format_get_modifiers(fmt_A, &num_modifiers);
366 for (i = 0; i < num_modifiers; i++) {
367 if (weston_drm_format_has_modifier(fmt_B, modifiers[i]))
368 continue;
369 mod = wl_array_add(modifiers_result, sizeof(modifiers[i]));
370 if (!mod) {
371 weston_log("%s: out of memory\n", __func__);
372 return -1;
373 }
374 *mod = modifiers[i];
375 }
376
377 return 0;
378}
379
380/**
381 * Compute the subtraction between two DRM-format arrays, keeping the result in A
382 *
383 * @param formats_A The minuend weston_drm_format_array
384 * @param formats_B The subtrahend weston_drm_format_array
385 * @return 0 on success, -1 on failure
386 */
387WL_EXPORT int
388weston_drm_format_array_subtract(struct weston_drm_format_array *formats_A,
389 const struct weston_drm_format_array *formats_B)
390{
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300391 struct weston_drm_format_array formats_result;
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300392 struct weston_drm_format *fmt_result, *fmt_A, *fmt_B;
393 int ret;
394
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300395 weston_drm_format_array_init(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300396
397 wl_array_for_each(fmt_A, &formats_A->arr) {
398 fmt_B = weston_drm_format_array_find_format(formats_B,
399 fmt_A->format);
400 if (!fmt_B) {
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300401 ret = add_format_and_modifiers(&formats_result, fmt_A->format,
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300402 &fmt_A->modifiers);
403 if (ret < 0)
404 goto err;
405
406 continue;
407 }
408
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300409 fmt_result = weston_drm_format_array_add_format(&formats_result,
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300410 fmt_A->format);
411 if (!fmt_result)
412 goto err;
413
414 ret = modifiers_subtract(fmt_A, fmt_B, &fmt_result->modifiers);
415 if (ret < 0)
416 goto err;
417
418 if (fmt_result->modifiers.size == 0)
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300419 weston_drm_format_array_remove_latest_format(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300420 }
421
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300422 ret = weston_drm_format_array_replace(formats_A, &formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300423 if (ret < 0)
424 goto err;
425
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300426 weston_drm_format_array_fini(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300427 return 0;
428
429err:
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300430 weston_drm_format_array_fini(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300431 return -1;
432}
433
434/**
435 * Add modifier to modifier set of a weston_drm_format.
436 *
437 * Adding repeated modifiers is considered an error.
438 *
439 * @param format The weston_drm_format that owns the modifier set to which
440 * the modifier should be added
441 * @param modifier The modifier to add
442 * @return 0 on success, -1 on failure
443 */
444WL_EXPORT int
445weston_drm_format_add_modifier(struct weston_drm_format *format,
446 uint64_t modifier)
447{
448 uint64_t *mod;
449
450 /* We should not try to add repeated modifiers to a set. */
451 assert(!weston_drm_format_has_modifier(format, modifier));
452
453 mod = wl_array_add(&format->modifiers, sizeof(*mod));
454 if (!mod) {
455 weston_log("%s: out of memory\n", __func__);
456 return -1;
457 }
458 *mod = modifier;
459
460 return 0;
461}
462
463/**
464 * Check if modifier set of a weston_drm_format contains a certain modifier
465 *
466 * @param format The weston_drm_format that owns the modifier set where to
467 * look for the modifier
468 * @param modifier The modifier to look for
469 * @return True if modifier was found, false otherwise
470 */
471WL_EXPORT bool
472weston_drm_format_has_modifier(const struct weston_drm_format *format,
473 uint64_t modifier)
474{
475 const uint64_t *modifiers;
476 unsigned int num_modifiers;
477 unsigned int i;
478
479 modifiers = weston_drm_format_get_modifiers(format, &num_modifiers);
480 for (i = 0; i < num_modifiers; i++)
481 if (modifiers[i] == modifier)
482 return true;
483
484 return false;
485}
486
487/**
488 * Get array of modifiers and modifiers count from a weston_drm_format
489 *
490 * @param format The weston_drm_format that contains the modifiers
491 * @param count_out Parameter that receives the modifiers count
492 * @return The array of modifiers
493 */
494WL_EXPORT const uint64_t *
495weston_drm_format_get_modifiers(const struct weston_drm_format *format,
496 unsigned int *count_out)
497{
498 *count_out = format->modifiers.size / sizeof(uint64_t);
499 return format->modifiers.data;
500}