blob: b0f375c03ef9c8241ddce7833e497afd9d7544cd [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/**
35 * Create and initialize a weston_drm_format_array
36 *
37 * @return The weston_drm_format_array, or NULL on failure
38 */
39WL_EXPORT struct weston_drm_format_array *
40weston_drm_format_array_create(void)
41{
42 struct weston_drm_format_array *formats;
43
44 formats = zalloc(sizeof(*formats));
45 if (!formats) {
46 weston_log("%s: out of memory\n", __func__);
47 return NULL;
48 }
49
50 weston_drm_format_array_init(formats);
51
52 return formats;
53}
54
55/**
56 * Initialize a weston_drm_format_array
57 *
58 * @param formats The weston_drm_format_array to initialize
59 */
60WL_EXPORT void
61weston_drm_format_array_init(struct weston_drm_format_array *formats)
62{
63 wl_array_init(&formats->arr);
64}
65
66/**
67 * Fini and destroy a weston_drm_format_array
68 *
69 * @param formats The weston_drm_format_array to destroy
70 */
71WL_EXPORT void
72weston_drm_format_array_destroy(struct weston_drm_format_array *formats)
73{
74 weston_drm_format_array_fini(formats);
75 free(formats);
76}
77
78/**
79 * Finish a weston_drm_format_array
80 *
81 * It releases the modifiers set for each format and then the
82 * formats array itself.
83 *
84 * @param formats The weston_drm_format_array to finish
85 */
86WL_EXPORT void
87weston_drm_format_array_fini(struct weston_drm_format_array *formats)
88{
89 struct weston_drm_format *fmt;
90
91 wl_array_for_each(fmt, &formats->arr)
92 wl_array_release(&fmt->modifiers);
93
94 wl_array_release(&formats->arr);
95}
96
97static int
98add_format_and_modifiers(struct weston_drm_format_array *formats,
99 uint32_t format, struct wl_array *modifiers)
100{
101 struct weston_drm_format *fmt;
102 int ret;
103
104 fmt = weston_drm_format_array_add_format(formats, format);
105 if (!fmt)
106 return -1;
107
108 ret = wl_array_copy(&fmt->modifiers, modifiers);
109 if (ret < 0) {
110 weston_log("%s: out of memory\n", __func__);
111 return -1;
112 }
113
114 return 0;
115}
116
117/**
118 * Replace the content of a weston_drm_format_array
119 *
120 * Frees the content of the array and then perform a deep copy using
121 * source_formats. It duplicates the array of formats and for each format it
122 * duplicates the modifiers set as well.
123 *
124 * @param formats The weston_drm_format_array that gets its content replaced
125 * @param source_formats The weston_drm_format_array to copy
126 * @return 0 on success, -1 on failure
127 */
128WL_EXPORT int
129weston_drm_format_array_replace(struct weston_drm_format_array *formats,
130 const struct weston_drm_format_array *source_formats)
131{
132 struct weston_drm_format *source_fmt;
133 int ret;
134
135 weston_drm_format_array_fini(formats);
136 weston_drm_format_array_init(formats);
137
138 wl_array_for_each(source_fmt, &source_formats->arr) {
139 ret = add_format_and_modifiers(formats, source_fmt->format,
140 &source_fmt->modifiers);
141 if (ret < 0)
142 return -1;
143 }
144
145 return 0;
146}
147
148/**
149 * Add format to weston_drm_format_array
150 *
151 * Adding repeated formats is considered an error.
152 *
153 * @param formats The weston_drm_format_array that receives the format
154 * @param format The format to add to the array
155 * @return The weston_drm_format, or NULL on failure
156 */
157WL_EXPORT struct weston_drm_format *
158weston_drm_format_array_add_format(struct weston_drm_format_array *formats,
159 uint32_t format)
160{
161 struct weston_drm_format *fmt;
162
163 /* We should not try to add repeated formats to an array. */
164 assert(!weston_drm_format_array_find_format(formats, format));
165
166 fmt = wl_array_add(&formats->arr, sizeof(*fmt));
167 if (!fmt) {
168 weston_log("%s: out of memory\n", __func__);
169 return NULL;
170 }
171
172 fmt->format = format;
173 wl_array_init(&fmt->modifiers);
174
175 return fmt;
176}
177
178/**
179 * Remove latest format added to a weston_drm_format_array
180 *
181 * Calling this function for an empty array is an error, at least one element
182 * must be in the array.
183 *
184 * @param formats The weston_drm_format_array from which the format is removed
185 */
186WL_EXPORT void
187weston_drm_format_array_remove_latest_format(struct weston_drm_format_array *formats)
188{
189 struct wl_array *array = &formats->arr;
190 struct weston_drm_format *fmt;
191
192 assert(array->size >= sizeof(*fmt));
193
194 array->size -= sizeof(*fmt);
195
196 fmt = array->data + array->size;
197 wl_array_release(&fmt->modifiers);
198}
199
200/**
201 * Find format in a weston_drm_format_array
202 *
203 * @param formats The weston_drm_format_array where to look for the format
204 * @param format The format to look for
205 * @return The weston_drm_format if format was found, or NULL otherwise
206 */
207WL_EXPORT struct weston_drm_format *
208weston_drm_format_array_find_format(const struct weston_drm_format_array *formats,
209 uint32_t format)
210{
211 struct weston_drm_format *fmt;
212
213 wl_array_for_each(fmt, &formats->arr)
214 if (fmt->format == format)
215 return fmt;
216
217 return NULL;
218}
219
220/**
221 * Compare the content of two weston_drm_format_array
222 *
223 * @param formats_A One of the weston_drm_format_array to compare
224 * @param formats_B The other weston_drm_format_array to compare
225 * @return True if both sets are equivalent, false otherwise
226 */
227WL_EXPORT bool
228weston_drm_format_array_equal(const struct weston_drm_format_array *formats_A,
229 const struct weston_drm_format_array *formats_B)
230{
231 struct weston_drm_format *fmt_A, *fmt_B;
232 const uint64_t *modifiers_A;
233 unsigned num_modifiers_A, num_modifiers_B;
234 unsigned int i;
235
236 if (formats_A->arr.size != formats_B->arr.size)
237 return false;
238
239 wl_array_for_each(fmt_A, &formats_A->arr) {
240 fmt_B = weston_drm_format_array_find_format(formats_B,
241 fmt_A->format);
242 if (!fmt_B)
243 return false;
244
245 modifiers_A = weston_drm_format_get_modifiers(fmt_A, &num_modifiers_A);
246 weston_drm_format_get_modifiers(fmt_B, &num_modifiers_B);
247 if (num_modifiers_A != num_modifiers_B)
248 return false;
249 for (i = 0; i < num_modifiers_A; i++)
250 if (!weston_drm_format_has_modifier(fmt_B, modifiers_A[i]))
251 return false;
252 }
253
254 return true;
255}
256
257/**
258 * Joins two weston_drm_format_array, keeping the result in A
259 *
260 * @param formats_A The weston_drm_format_array that receives the formats from B
261 * @param formats_B The weston_drm_format_array whose formats are added to A
262 * @return 0 on success, -1 on failure
263 */
264WL_EXPORT int
265weston_drm_format_array_join(struct weston_drm_format_array *formats_A,
266 const struct weston_drm_format_array *formats_B)
267{
268 struct weston_drm_format *fmt_A, *fmt_B;
269 const uint64_t *modifiers;
270 unsigned int num_modifiers;
271 unsigned int i;
272 int ret;
273
274 wl_array_for_each(fmt_B, &formats_B->arr) {
275 fmt_A = weston_drm_format_array_find_format(formats_A,
276 fmt_B->format);
277 if (!fmt_A) {
278 fmt_A = weston_drm_format_array_add_format(formats_A,
279 fmt_B->format);
280 if (!fmt_A)
281 return -1;
282 }
283
284 modifiers = weston_drm_format_get_modifiers(fmt_B, &num_modifiers);
285 for (i = 0; i < num_modifiers; i++) {
286 if (weston_drm_format_has_modifier(fmt_A, modifiers[i]))
287 continue;
288 ret = weston_drm_format_add_modifier(fmt_A, modifiers[i]);
289 if (ret < 0)
290 return -1;
291 }
292 }
293
294 return 0;
295}
296
297static int
298modifiers_intersect(const struct weston_drm_format *fmt_A,
299 const struct weston_drm_format *fmt_B,
300 struct wl_array *modifiers_result)
301{
302 const uint64_t *modifiers;
303 unsigned int num_modifiers;
304 uint64_t *mod;
305 unsigned int i;
306
307 modifiers = weston_drm_format_get_modifiers(fmt_A, &num_modifiers);
308 for (i = 0; i < num_modifiers; i++) {
309 if (!weston_drm_format_has_modifier(fmt_B, modifiers[i]))
310 continue;
311 mod = wl_array_add(modifiers_result, sizeof(modifiers[i]));
312 if (!mod) {
313 weston_log("%s: out of memory\n", __func__);
314 return -1;
315 }
316 *mod = modifiers[i];
317 }
318
319 return 0;
320}
321
322/**
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300323 * Compute the intersection between two DRM-format arrays, keeping the result in A
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300324 *
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300325 * @param formats_A The weston_drm_format_array that keeps the result
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300326 * @param formats_B The other weston_drm_format_array
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300327 * @return 0 on success, -1 on failure
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300328 */
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300329WL_EXPORT int
330weston_drm_format_array_intersect(struct weston_drm_format_array *formats_A,
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300331 const struct weston_drm_format_array *formats_B)
332{
333 struct weston_drm_format_array *formats_result;
334 struct weston_drm_format *fmt_result, *fmt_A, *fmt_B;
335 int ret;
336
337 formats_result = weston_drm_format_array_create();
338 if (!formats_result)
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300339 return -1;
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300340
341 wl_array_for_each(fmt_A, &formats_A->arr) {
342 fmt_B = weston_drm_format_array_find_format(formats_B,
343 fmt_A->format);
344 if (!fmt_B)
345 continue;
346
347 fmt_result = weston_drm_format_array_add_format(formats_result,
348 fmt_A->format);
349 if (!fmt_result)
350 goto err;
351
352 ret = modifiers_intersect(fmt_A, fmt_B, &fmt_result->modifiers);
353 if (ret < 0)
354 goto err;
355
356 if (fmt_result->modifiers.size == 0)
357 weston_drm_format_array_remove_latest_format(formats_result);
358 }
359
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300360 ret = weston_drm_format_array_replace(formats_A, formats_result);
361 if (ret < 0)
362 goto err;
363
364 weston_drm_format_array_destroy(formats_result);
365 return 0;
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300366
367err:
368 weston_drm_format_array_destroy(formats_result);
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300369 return -1;
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300370}
371
372static int
373modifiers_subtract(const struct weston_drm_format *fmt_A,
374 const struct weston_drm_format *fmt_B,
375 struct wl_array *modifiers_result)
376{
377 const uint64_t *modifiers;
378 unsigned int num_modifiers;
379 uint64_t *mod;
380 unsigned int i;
381
382 modifiers = weston_drm_format_get_modifiers(fmt_A, &num_modifiers);
383 for (i = 0; i < num_modifiers; i++) {
384 if (weston_drm_format_has_modifier(fmt_B, modifiers[i]))
385 continue;
386 mod = wl_array_add(modifiers_result, sizeof(modifiers[i]));
387 if (!mod) {
388 weston_log("%s: out of memory\n", __func__);
389 return -1;
390 }
391 *mod = modifiers[i];
392 }
393
394 return 0;
395}
396
397/**
398 * Compute the subtraction between two DRM-format arrays, keeping the result in A
399 *
400 * @param formats_A The minuend weston_drm_format_array
401 * @param formats_B The subtrahend weston_drm_format_array
402 * @return 0 on success, -1 on failure
403 */
404WL_EXPORT int
405weston_drm_format_array_subtract(struct weston_drm_format_array *formats_A,
406 const struct weston_drm_format_array *formats_B)
407{
408 struct weston_drm_format_array *formats_result;
409 struct weston_drm_format *fmt_result, *fmt_A, *fmt_B;
410 int ret;
411
412 formats_result = weston_drm_format_array_create();
413 if (!formats_result)
414 return -1;
415
416 wl_array_for_each(fmt_A, &formats_A->arr) {
417 fmt_B = weston_drm_format_array_find_format(formats_B,
418 fmt_A->format);
419 if (!fmt_B) {
420 ret = add_format_and_modifiers(formats_result, fmt_A->format,
421 &fmt_A->modifiers);
422 if (ret < 0)
423 goto err;
424
425 continue;
426 }
427
428 fmt_result = weston_drm_format_array_add_format(formats_result,
429 fmt_A->format);
430 if (!fmt_result)
431 goto err;
432
433 ret = modifiers_subtract(fmt_A, fmt_B, &fmt_result->modifiers);
434 if (ret < 0)
435 goto err;
436
437 if (fmt_result->modifiers.size == 0)
438 weston_drm_format_array_remove_latest_format(formats_result);
439 }
440
441 ret = weston_drm_format_array_replace(formats_A, formats_result);
442 if (ret < 0)
443 goto err;
444
Leandro Ribeiro01575912021-06-15 17:29:29 -0300445 weston_drm_format_array_destroy(formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300446 return 0;
447
448err:
449 weston_drm_format_array_destroy(formats_result);
450 return -1;
451}
452
453/**
454 * Add modifier to modifier set of a weston_drm_format.
455 *
456 * Adding repeated modifiers is considered an error.
457 *
458 * @param format The weston_drm_format that owns the modifier set to which
459 * the modifier should be added
460 * @param modifier The modifier to add
461 * @return 0 on success, -1 on failure
462 */
463WL_EXPORT int
464weston_drm_format_add_modifier(struct weston_drm_format *format,
465 uint64_t modifier)
466{
467 uint64_t *mod;
468
469 /* We should not try to add repeated modifiers to a set. */
470 assert(!weston_drm_format_has_modifier(format, modifier));
471
472 mod = wl_array_add(&format->modifiers, sizeof(*mod));
473 if (!mod) {
474 weston_log("%s: out of memory\n", __func__);
475 return -1;
476 }
477 *mod = modifier;
478
479 return 0;
480}
481
482/**
483 * Check if modifier set of a weston_drm_format contains a certain modifier
484 *
485 * @param format The weston_drm_format that owns the modifier set where to
486 * look for the modifier
487 * @param modifier The modifier to look for
488 * @return True if modifier was found, false otherwise
489 */
490WL_EXPORT bool
491weston_drm_format_has_modifier(const struct weston_drm_format *format,
492 uint64_t modifier)
493{
494 const uint64_t *modifiers;
495 unsigned int num_modifiers;
496 unsigned int i;
497
498 modifiers = weston_drm_format_get_modifiers(format, &num_modifiers);
499 for (i = 0; i < num_modifiers; i++)
500 if (modifiers[i] == modifier)
501 return true;
502
503 return false;
504}
505
506/**
507 * Get array of modifiers and modifiers count from a weston_drm_format
508 *
509 * @param format The weston_drm_format that contains the modifiers
510 * @param count_out Parameter that receives the modifiers count
511 * @return The array of modifiers
512 */
513WL_EXPORT const uint64_t *
514weston_drm_format_get_modifiers(const struct weston_drm_format *format,
515 unsigned int *count_out)
516{
517 *count_out = format->modifiers.size / sizeof(uint64_t);
518 return format->modifiers.data;
519}