blob: 491ecd9fdbfe49dd4c7f8484802fe11b4c9b57de [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/**
188 * Compare the content of two weston_drm_format_array
189 *
190 * @param formats_A One of the weston_drm_format_array to compare
191 * @param formats_B The other weston_drm_format_array to compare
192 * @return True if both sets are equivalent, false otherwise
193 */
194WL_EXPORT bool
195weston_drm_format_array_equal(const struct weston_drm_format_array *formats_A,
196 const struct weston_drm_format_array *formats_B)
197{
198 struct weston_drm_format *fmt_A, *fmt_B;
199 const uint64_t *modifiers_A;
200 unsigned num_modifiers_A, num_modifiers_B;
201 unsigned int i;
202
203 if (formats_A->arr.size != formats_B->arr.size)
204 return false;
205
206 wl_array_for_each(fmt_A, &formats_A->arr) {
207 fmt_B = weston_drm_format_array_find_format(formats_B,
208 fmt_A->format);
209 if (!fmt_B)
210 return false;
211
212 modifiers_A = weston_drm_format_get_modifiers(fmt_A, &num_modifiers_A);
213 weston_drm_format_get_modifiers(fmt_B, &num_modifiers_B);
214 if (num_modifiers_A != num_modifiers_B)
215 return false;
216 for (i = 0; i < num_modifiers_A; i++)
217 if (!weston_drm_format_has_modifier(fmt_B, modifiers_A[i]))
218 return false;
219 }
220
221 return true;
222}
223
224/**
225 * Joins two weston_drm_format_array, keeping the result in A
226 *
227 * @param formats_A The weston_drm_format_array that receives the formats from B
228 * @param formats_B The weston_drm_format_array whose formats are added to A
229 * @return 0 on success, -1 on failure
230 */
231WL_EXPORT int
232weston_drm_format_array_join(struct weston_drm_format_array *formats_A,
233 const struct weston_drm_format_array *formats_B)
234{
235 struct weston_drm_format *fmt_A, *fmt_B;
236 const uint64_t *modifiers;
237 unsigned int num_modifiers;
238 unsigned int i;
239 int ret;
240
241 wl_array_for_each(fmt_B, &formats_B->arr) {
242 fmt_A = weston_drm_format_array_find_format(formats_A,
243 fmt_B->format);
244 if (!fmt_A) {
245 fmt_A = weston_drm_format_array_add_format(formats_A,
246 fmt_B->format);
247 if (!fmt_A)
248 return -1;
249 }
250
251 modifiers = weston_drm_format_get_modifiers(fmt_B, &num_modifiers);
252 for (i = 0; i < num_modifiers; i++) {
253 if (weston_drm_format_has_modifier(fmt_A, modifiers[i]))
254 continue;
255 ret = weston_drm_format_add_modifier(fmt_A, modifiers[i]);
256 if (ret < 0)
257 return -1;
258 }
259 }
260
261 return 0;
262}
263
264static int
265modifiers_intersect(const struct weston_drm_format *fmt_A,
266 const struct weston_drm_format *fmt_B,
267 struct wl_array *modifiers_result)
268{
269 const uint64_t *modifiers;
270 unsigned int num_modifiers;
271 uint64_t *mod;
272 unsigned int i;
273
274 modifiers = weston_drm_format_get_modifiers(fmt_A, &num_modifiers);
275 for (i = 0; i < num_modifiers; i++) {
276 if (!weston_drm_format_has_modifier(fmt_B, modifiers[i]))
277 continue;
278 mod = wl_array_add(modifiers_result, sizeof(modifiers[i]));
279 if (!mod) {
280 weston_log("%s: out of memory\n", __func__);
281 return -1;
282 }
283 *mod = modifiers[i];
284 }
285
286 return 0;
287}
288
289/**
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300290 * Compute the intersection between two DRM-format arrays, keeping the result in A
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300291 *
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300292 * @param formats_A The weston_drm_format_array that keeps the result
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300293 * @param formats_B The other weston_drm_format_array
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300294 * @return 0 on success, -1 on failure
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300295 */
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300296WL_EXPORT int
297weston_drm_format_array_intersect(struct weston_drm_format_array *formats_A,
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300298 const struct weston_drm_format_array *formats_B)
299{
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300300 struct weston_drm_format_array formats_result;
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300301 struct weston_drm_format *fmt_result, *fmt_A, *fmt_B;
302 int ret;
303
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300304 weston_drm_format_array_init(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300305
306 wl_array_for_each(fmt_A, &formats_A->arr) {
307 fmt_B = weston_drm_format_array_find_format(formats_B,
308 fmt_A->format);
309 if (!fmt_B)
310 continue;
311
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300312 fmt_result = weston_drm_format_array_add_format(&formats_result,
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300313 fmt_A->format);
314 if (!fmt_result)
315 goto err;
316
317 ret = modifiers_intersect(fmt_A, fmt_B, &fmt_result->modifiers);
318 if (ret < 0)
319 goto err;
320
321 if (fmt_result->modifiers.size == 0)
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300322 weston_drm_format_array_remove_latest_format(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300323 }
324
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300325 ret = weston_drm_format_array_replace(formats_A, &formats_result);
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300326 if (ret < 0)
327 goto err;
328
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300329 weston_drm_format_array_fini(&formats_result);
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300330 return 0;
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300331
332err:
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300333 weston_drm_format_array_fini(&formats_result);
Leandro Ribeiroc51d4ad2021-08-30 12:52:26 -0300334 return -1;
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300335}
336
337static int
338modifiers_subtract(const struct weston_drm_format *fmt_A,
339 const struct weston_drm_format *fmt_B,
340 struct wl_array *modifiers_result)
341{
342 const uint64_t *modifiers;
343 unsigned int num_modifiers;
344 uint64_t *mod;
345 unsigned int i;
346
347 modifiers = weston_drm_format_get_modifiers(fmt_A, &num_modifiers);
348 for (i = 0; i < num_modifiers; i++) {
349 if (weston_drm_format_has_modifier(fmt_B, modifiers[i]))
350 continue;
351 mod = wl_array_add(modifiers_result, sizeof(modifiers[i]));
352 if (!mod) {
353 weston_log("%s: out of memory\n", __func__);
354 return -1;
355 }
356 *mod = modifiers[i];
357 }
358
359 return 0;
360}
361
362/**
363 * Compute the subtraction between two DRM-format arrays, keeping the result in A
364 *
365 * @param formats_A The minuend weston_drm_format_array
366 * @param formats_B The subtrahend weston_drm_format_array
367 * @return 0 on success, -1 on failure
368 */
369WL_EXPORT int
370weston_drm_format_array_subtract(struct weston_drm_format_array *formats_A,
371 const struct weston_drm_format_array *formats_B)
372{
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300373 struct weston_drm_format_array formats_result;
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300374 struct weston_drm_format *fmt_result, *fmt_A, *fmt_B;
375 int ret;
376
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300377 weston_drm_format_array_init(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300378
379 wl_array_for_each(fmt_A, &formats_A->arr) {
380 fmt_B = weston_drm_format_array_find_format(formats_B,
381 fmt_A->format);
382 if (!fmt_B) {
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300383 ret = add_format_and_modifiers(&formats_result, fmt_A->format,
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300384 &fmt_A->modifiers);
385 if (ret < 0)
386 goto err;
387
388 continue;
389 }
390
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300391 fmt_result = weston_drm_format_array_add_format(&formats_result,
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300392 fmt_A->format);
393 if (!fmt_result)
394 goto err;
395
396 ret = modifiers_subtract(fmt_A, fmt_B, &fmt_result->modifiers);
397 if (ret < 0)
398 goto err;
399
400 if (fmt_result->modifiers.size == 0)
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300401 weston_drm_format_array_remove_latest_format(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300402 }
403
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300404 ret = weston_drm_format_array_replace(formats_A, &formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300405 if (ret < 0)
406 goto err;
407
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300408 weston_drm_format_array_fini(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300409 return 0;
410
411err:
Leandro Ribeiro0750cea2021-08-30 16:32:03 -0300412 weston_drm_format_array_fini(&formats_result);
Leandro Ribeiro78f01922020-12-01 16:39:47 -0300413 return -1;
414}
415
416/**
417 * Add modifier to modifier set of a weston_drm_format.
418 *
419 * Adding repeated modifiers is considered an error.
420 *
421 * @param format The weston_drm_format that owns the modifier set to which
422 * the modifier should be added
423 * @param modifier The modifier to add
424 * @return 0 on success, -1 on failure
425 */
426WL_EXPORT int
427weston_drm_format_add_modifier(struct weston_drm_format *format,
428 uint64_t modifier)
429{
430 uint64_t *mod;
431
432 /* We should not try to add repeated modifiers to a set. */
433 assert(!weston_drm_format_has_modifier(format, modifier));
434
435 mod = wl_array_add(&format->modifiers, sizeof(*mod));
436 if (!mod) {
437 weston_log("%s: out of memory\n", __func__);
438 return -1;
439 }
440 *mod = modifier;
441
442 return 0;
443}
444
445/**
446 * Check if modifier set of a weston_drm_format contains a certain modifier
447 *
448 * @param format The weston_drm_format that owns the modifier set where to
449 * look for the modifier
450 * @param modifier The modifier to look for
451 * @return True if modifier was found, false otherwise
452 */
453WL_EXPORT bool
454weston_drm_format_has_modifier(const struct weston_drm_format *format,
455 uint64_t modifier)
456{
457 const uint64_t *modifiers;
458 unsigned int num_modifiers;
459 unsigned int i;
460
461 modifiers = weston_drm_format_get_modifiers(format, &num_modifiers);
462 for (i = 0; i < num_modifiers; i++)
463 if (modifiers[i] == modifier)
464 return true;
465
466 return false;
467}
468
469/**
470 * Get array of modifiers and modifiers count from a weston_drm_format
471 *
472 * @param format The weston_drm_format that contains the modifiers
473 * @param count_out Parameter that receives the modifiers count
474 * @return The array of modifiers
475 */
476WL_EXPORT const uint64_t *
477weston_drm_format_get_modifiers(const struct weston_drm_format *format,
478 unsigned int *count_out)
479{
480 *count_out = format->modifiers.size / sizeof(uint64_t);
481 return format->modifiers.data;
482}