GCC Code Coverage Report


Directory: ./
File: alloc.c
Date: 2024-12-05 11:41:29
Exec Total Coverage
Lines: 434 470 92.3%
Functions: 26 26 100.0%
Branches: 278 332 83.7%

Line Branch Exec Source
1 #define _POSIX_C_SOURCE 200112L
2 #include <assert.h>
3 #include <errno.h>
4 #include <inttypes.h>
5 #include <limits.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <time.h>
9 #include <unistd.h>
10 #include "log.h"
11 #include "private.h"
12
13 /* Plane allocation algorithm
14 *
15 * Goal: KMS exposes a set of hardware planes, user submitted a set of layers.
16 * We want to map as many layers as possible to planes.
17 *
18 * However, all layers can't be mapped to any plane. There are constraints,
19 * sometimes depending on driver-specific limitations or the configuration of
20 * other planes.
21 *
22 * The only way to discover driver-specific limitations is via an atomic test
23 * commit: we submit a plane configuration, and KMS replies whether it's
24 * supported or not. Thus we need to incrementally build a valid configuration.
25 *
26 * Let's take an example with 2 planes and 3 layers. Plane 1 is only compatible
27 * with layer 2 and plane 2 is only compatible with layer 3. Our algorithm will
28 * discover the solution by building the mapping one plane at a time. It first
29 * starts with plane 1: an atomic commit assigning layer 1 to plane 1 is
30 * submitted. It fails, because this isn't supported by the driver. Then layer
31 * 2 is assigned to plane 1 and the atomic test succeeds. We can go on and
32 * repeat the operation with plane 2. After exploring the whole tree, we end up
33 * with a valid allocation.
34 *
35 *
36 * layer 1 layer 1
37 * +---------> failure +---------> failure
38 * | |
39 * | |
40 * | |
41 * +---------+ | +---------+ |
42 * | | | layer 2 | | | layer 3 final allocation:
43 * | plane 1 +------------>+ plane 2 +--+---------> plane 1 → layer 2
44 * | | | | | plane 2 → layer 3
45 * +---------+ | +---------+
46 * |
47 * |
48 * | layer 3
49 * +---------> failure
50 *
51 *
52 * Note how layer 2 isn't considered for plane 2: it's already mapped to plane
53 * 1. Also note that branches are pruned as soon as an atomic test fails.
54 *
55 * In practice, the primary plane is treated separately. This is where layers
56 * that can't be mapped to any plane (e.g. layer 1 in our example) will be
57 * composited. The primary plane is the first that will be allocated, because
58 * some drivers require it to be enabled in order to light up any other plane.
59 * Then all other planes will be allocated, from the topmost one to the
60 * bottommost one.
61 *
62 * The "zpos" property (which defines ordering between layers/planes) is handled
63 * as a special case. If it's set on layers, it adds additional constraints on
64 * their relative ordering. If two layers intersect, their relative zpos needs
65 * to be preserved during plane allocation.
66 *
67 * Implementation-wise, the output_choose_layers function is called at each node
68 * of the tree. It iterates over layers, check constraints, performs an atomic
69 * test commit and calls itself recursively on the next plane.
70 */
71
72 /* Global data for the allocation algorithm */
73 struct alloc_result {
74 drmModeAtomicReq *req;
75 uint32_t flags;
76 size_t planes_len;
77
78 struct liftoff_layer **best;
79 int best_score;
80
81 struct timespec started_at;
82 int64_t timeout_ns;
83
84 /* per-output */
85 bool has_composition_layer;
86 size_t non_composition_layers_len;
87 };
88
89 /* Transient data, arguments for each step */
90 struct alloc_step {
91 struct liftoff_list *plane_link; /* liftoff_plane.link */
92 size_t plane_idx;
93
94 struct liftoff_layer **alloc; /* only items up to plane_idx are valid */
95 int score; /* number of allocated layers */
96 int last_layer_zpos;
97 int primary_layer_zpos, primary_plane_zpos;
98
99 bool composited; /* per-output */
100
101 char log_prefix[64];
102 };
103
104 static const int64_t NSEC_PER_SEC = 1000 * 1000 * 1000;
105
106 static int64_t
107 706 timespec_to_nsec(struct timespec ts)
108 {
109 706 return (int64_t)ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
110 }
111
112 static const int64_t DEFAULT_ALLOC_TIMEOUT_NSEC = 1000 * 1000; // 1ms
113
114 static bool
115 353 check_deadline(struct timespec start, int64_t timeout_ns)
116 {
117 struct timespec now;
118
119
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 353 times.
353 if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
120 liftoff_log_errno(LIFTOFF_ERROR, "clock_gettime");
121 return false;
122 }
123
124 353 return timespec_to_nsec(now) - timeout_ns < timespec_to_nsec(start);
125 }
126
127 static void
128 332 plane_step_init_next(struct alloc_step *step, struct alloc_step *prev,
129 struct liftoff_layer *layer)
130 {
131 struct liftoff_plane *plane;
132 struct liftoff_layer_property *zpos_prop;
133 size_t len;
134
135 332 plane = liftoff_container_of(prev->plane_link, plane, link);
136
137 332 step->plane_link = prev->plane_link->next;
138 332 step->plane_idx = prev->plane_idx + 1;
139 332 step->alloc = prev->alloc;
140 332 step->alloc[prev->plane_idx] = layer;
141
142
4/4
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 232 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 94 times.
332 if (layer != NULL && layer == layer->output->composition_layer) {
143
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 assert(!prev->composited);
144 6 step->composited = true;
145 } else {
146 326 step->composited = prev->composited;
147 }
148
149
4/4
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 232 times.
✓ Branch 2 taken 94 times.
✓ Branch 3 taken 6 times.
332 if (layer != NULL && layer != layer->output->composition_layer) {
150 94 step->score = prev->score + 1;
151 } else {
152 238 step->score = prev->score;
153 }
154
155 332 zpos_prop = NULL;
156
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 232 times.
332 if (layer != NULL) {
157 100 zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS);
158 }
159
4/4
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 278 times.
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 23 times.
332 if (zpos_prop != NULL && plane->type != DRM_PLANE_TYPE_PRIMARY) {
160 31 step->last_layer_zpos = zpos_prop->value;
161 } else {
162 301 step->last_layer_zpos = prev->last_layer_zpos;
163 }
164
4/4
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 278 times.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 31 times.
332 if (zpos_prop != NULL && plane->type == DRM_PLANE_TYPE_PRIMARY) {
165 23 step->primary_layer_zpos = zpos_prop->value;
166 23 step->primary_plane_zpos = plane->zpos;
167 } else {
168 309 step->primary_layer_zpos = prev->primary_layer_zpos;
169 309 step->primary_plane_zpos = prev->primary_plane_zpos;
170 }
171
172
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 232 times.
332 if (layer != NULL) {
173 100 len = strlen(prev->log_prefix) + 2;
174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
100 if (len > sizeof(step->log_prefix) - 1) {
175 len = sizeof(step->log_prefix) - 1;
176 }
177 100 memset(step->log_prefix, ' ', len);
178 100 step->log_prefix[len] = '\0';
179 } else {
180 232 memcpy(step->log_prefix, prev->log_prefix,
181 sizeof(step->log_prefix));
182 }
183 332 }
184
185 static bool
186 1191 is_layer_allocated(struct alloc_step *step, struct liftoff_layer *layer)
187 {
188 size_t i;
189
190 /* TODO: speed this up with an array of bools indicating whether a layer
191 * has been allocated */
192
2/2
✓ Branch 0 taken 1593 times.
✓ Branch 1 taken 896 times.
2489 for (i = 0; i < step->plane_idx; i++) {
193
2/2
✓ Branch 0 taken 295 times.
✓ Branch 1 taken 1298 times.
1593 if (step->alloc[i] == layer) {
194 295 return true;
195 }
196 }
197 896 return false;
198 }
199
200 static bool
201 302 has_composited_layer_over(struct liftoff_output *output,
202 struct alloc_step *step, struct liftoff_layer *layer)
203 {
204 struct liftoff_layer *other_layer;
205 struct liftoff_layer_property *zpos_prop, *other_zpos_prop;
206
207 302 zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS);
208
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 218 times.
302 if (zpos_prop == NULL) {
209 84 return false;
210 }
211
212
2/2
✓ Branch 0 taken 568 times.
✓ Branch 1 taken 105 times.
673 liftoff_list_for_each(other_layer, &output->layers, link) {
213
2/2
✓ Branch 1 taken 147 times.
✓ Branch 2 taken 421 times.
568 if (is_layer_allocated(step, other_layer)) {
214 147 continue;
215 }
216
217 421 other_zpos_prop = layer_get_core_property(other_layer,
218 LIFTOFF_PROP_ZPOS);
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 421 times.
421 if (other_zpos_prop == NULL) {
220 continue;
221 }
222
223
2/2
✓ Branch 1 taken 379 times.
✓ Branch 2 taken 42 times.
421 if (layer_intersects(layer, other_layer) &&
224
2/2
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 266 times.
379 other_zpos_prop->value > zpos_prop->value) {
225 113 return true;
226 }
227 }
228
229 105 return false;
230 }
231
232 static bool
233 8 has_allocated_layer_over(struct liftoff_output *output, struct alloc_step *step,
234 struct liftoff_layer *layer)
235 {
236 ssize_t i;
237 struct liftoff_plane *other_plane;
238 struct liftoff_layer *other_layer;
239 struct liftoff_layer_property *zpos_prop, *other_zpos_prop;
240
241 8 zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS);
242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (zpos_prop == NULL) {
243 return false;
244 }
245
246 8 i = -1;
247
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 liftoff_list_for_each(other_plane, &output->device->planes, link) {
248 29 i++;
249
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 21 times.
29 if (i >= (ssize_t)step->plane_idx) {
250 8 break;
251 }
252
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 13 times.
21 if (other_plane->type == DRM_PLANE_TYPE_PRIMARY) {
253 8 continue;
254 }
255
256 13 other_layer = step->alloc[i];
257
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 9 times.
13 if (other_layer == NULL) {
258 4 continue;
259 }
260
261 9 other_zpos_prop = layer_get_core_property(other_layer,
262 LIFTOFF_PROP_ZPOS);
263
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (other_zpos_prop == NULL) {
264 continue;
265 }
266
267 /* Since plane zpos is descending, this means the other layer is
268 * supposed to be under but is mapped to a plane over the
269 * current one. */
270
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
17 if (zpos_prop->value > other_zpos_prop->value &&
271 8 layer_intersects(layer, other_layer)) {
272 return true;
273 }
274 }
275
276 8 return false;
277 }
278
279 static bool
280 277 has_allocated_plane_under(struct liftoff_output *output,
281 struct alloc_step *step, struct liftoff_layer *layer)
282 {
283 struct liftoff_plane *plane, *other_plane;
284 ssize_t i;
285
286 277 plane = liftoff_container_of(step->plane_link, plane, link);
287
288 277 i = -1;
289
1/2
✓ Branch 0 taken 655 times.
✗ Branch 1 not taken.
655 liftoff_list_for_each(other_plane, &output->device->planes, link) {
290 655 i++;
291
2/2
✓ Branch 0 taken 275 times.
✓ Branch 1 taken 380 times.
655 if (i >= (ssize_t)step->plane_idx) {
292 275 break;
293 }
294
2/2
✓ Branch 0 taken 212 times.
✓ Branch 1 taken 168 times.
380 if (other_plane->type == DRM_PLANE_TYPE_PRIMARY) {
295 212 continue;
296 }
297
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 41 times.
168 if (step->alloc[i] == NULL) {
298 127 continue;
299 }
300
301
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
44 if (plane->zpos >= other_plane->zpos &&
302 3 layer_intersects(layer, step->alloc[i])) {
303 2 return true;
304 }
305 }
306
307 275 return false;
308 }
309
310 static bool
311 623 check_layer_plane_compatible(struct alloc_step *step,
312 struct liftoff_layer *layer,
313 struct liftoff_plane *plane)
314 {
315 struct liftoff_output *output;
316 struct liftoff_layer_property *zpos_prop;
317
318 623 output = layer->output;
319
320 /* Skip this layer if already allocated */
321
2/2
✓ Branch 1 taken 148 times.
✓ Branch 2 taken 475 times.
623 if (is_layer_allocated(step, layer)) {
322 148 return false;
323 }
324
325 475 zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS);
326
2/2
✓ Branch 0 taken 285 times.
✓ Branch 1 taken 190 times.
475 if (zpos_prop != NULL) {
327
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 277 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
293 if ((int)zpos_prop->value > step->last_layer_zpos &&
328 8 has_allocated_layer_over(output, step, layer)) {
329 /* This layer needs to be on top of the last
330 * allocated one */
331 liftoff_log(LIFTOFF_DEBUG,
332 "%s Layer %p -> plane %"PRIu32": "
333 "layer zpos invalid",
334 step->log_prefix, (void *)layer, plane->id);
335 return false;
336 }
337
4/4
✓ Branch 0 taken 277 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 275 times.
562 if ((int)zpos_prop->value < step->last_layer_zpos &&
338 277 has_allocated_plane_under(output, step, layer)) {
339 /* This layer needs to be under the last
340 * allocated one, but this plane isn't under the
341 * last one (in practice, since planes are
342 * sorted by zpos it means it has the same zpos,
343 * ie. undefined ordering). */
344 2 liftoff_log(LIFTOFF_DEBUG,
345 "%s Layer %p -> plane %"PRIu32": "
346 "plane zpos invalid",
347 2 step->log_prefix, (void *)layer, plane->id);
348 2 return false;
349 }
350
2/2
✓ Branch 0 taken 224 times.
✓ Branch 1 taken 59 times.
283 if (plane->type != DRM_PLANE_TYPE_PRIMARY &&
351
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 218 times.
224 (int)zpos_prop->value < step->primary_layer_zpos &&
352
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 plane->zpos > step->primary_plane_zpos) {
353 /* Primary planes are handled up front, because some
354 * drivers fail all atomic commits when it's missing.
355 * However that messes up with our zpos checks. In
356 * particular, we need to make sure we don't put a layer
357 * configured to be over the primary plane under it.
358 * TODO: revisit this once we add underlay support. */
359 6 liftoff_log(LIFTOFF_DEBUG,
360 "%s Layer %p -> plane %"PRIu32": "
361 "layer zpos under primary",
362 6 step->log_prefix, (void *)layer, plane->id);
363 6 return false;
364 }
365 }
366
367
4/4
✓ Branch 0 taken 302 times.
✓ Branch 1 taken 165 times.
✓ Branch 2 taken 113 times.
✓ Branch 3 taken 189 times.
769 if (plane->type != DRM_PLANE_TYPE_PRIMARY &&
368 302 has_composited_layer_over(output, step, layer)) {
369 113 liftoff_log(LIFTOFF_DEBUG,
370 "%s Layer %p -> plane %"PRIu32": "
371 "has composited layer on top",
372 113 step->log_prefix, (void *)layer, plane->id);
373 113 return false;
374 }
375
376
2/2
✓ Branch 0 taken 189 times.
✓ Branch 1 taken 165 times.
354 if (plane->type != DRM_PLANE_TYPE_PRIMARY &&
377
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 188 times.
189 layer == layer->output->composition_layer) {
378 1 liftoff_log(LIFTOFF_DEBUG,
379 "%s Layer %p -> plane %"PRIu32": "
380 "cannot put composition layer on "
381 "non-primary plane",
382 1 step->log_prefix, (void *)layer, plane->id);
383 1 return false;
384 }
385
386 353 return true;
387 }
388
389 static bool
390 85 check_alloc_valid(struct liftoff_output *output, struct alloc_result *result,
391 struct alloc_step *step)
392 {
393 /* If composition isn't used, we need to have allocated all
394 * layers. */
395 /* TODO: find a way to fail earlier, e.g. when the number of
396 * layers exceeds the number of planes. */
397
4/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 74 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 6 times.
85 if (result->has_composition_layer && !step->composited &&
398
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 step->score != (int)result->non_composition_layers_len) {
399 3 liftoff_log(LIFTOFF_DEBUG,
400 "%sCannot skip composition: some layers "
401 3 "are missing a plane", step->log_prefix);
402 3 return false;
403 }
404 /* On the other hand, if we manage to allocate all layers, we
405 * don't want to use composition. We don't want to use the
406 * composition layer at all. */
407
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 76 times.
82 if (step->composited &&
408
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 step->score == (int)result->non_composition_layers_len) {
409 liftoff_log(LIFTOFF_DEBUG,
410 "%sRefusing to use composition: all layers "
411 "have been put in a plane", step->log_prefix);
412 return false;
413 }
414
415 /* TODO: check allocation isn't empty */
416
417 82 return true;
418 }
419
420 static bool
421 780 check_plane_output_compatible(struct liftoff_plane *plane, struct liftoff_output *output)
422 {
423
3/4
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 738 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
780 if (plane->free_after_pageflip != NULL && plane->free_after_pageflip != output) {
424 /* This plane is still attached to another CRTC until vblank */
425 return false;
426 }
427
428 780 return (plane->possible_crtcs & (1UL << output->crtc_index)) != 0;
429 }
430
431 static int
432 295 count_remaining_compatible_planes(struct liftoff_output *output,
433 struct alloc_step *step)
434 {
435 struct liftoff_list *link;
436 struct liftoff_plane *plane;
437 295 int remaining = 0;
438
439
2/2
✓ Branch 0 taken 548 times.
✓ Branch 1 taken 295 times.
843 for (link = step->plane_link; link != &output->device->planes; link = link->next) {
440 548 plane = liftoff_container_of(link, plane, link);
441
2/4
✓ Branch 0 taken 548 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 548 times.
✗ Branch 3 not taken.
1096 if (plane->layer == NULL &&
442 548 check_plane_output_compatible(plane, output)) {
443 548 remaining++;
444 }
445 }
446
447 295 return remaining;
448 }
449
450 static int
451 411 output_choose_layers(struct liftoff_output *output, struct alloc_result *result,
452 struct alloc_step *step)
453 {
454 struct liftoff_device *device;
455 struct liftoff_plane *plane;
456 struct liftoff_layer *layer;
457 int cursor, ret;
458 int remaining_planes;
459 411 struct alloc_step next_step = {0};
460
461 411 device = output->device;
462
463
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 295 times.
411 if (step->plane_link == &device->planes) { /* Allocation finished */
464
4/4
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 82 times.
✓ Branch 3 taken 3 times.
201 if (step->score > result->best_score &&
465 85 check_alloc_valid(output, result, step)) {
466 /* We found a better allocation */
467 82 liftoff_log(LIFTOFF_DEBUG,
468 "%sFound a better allocation with score=%d",
469 82 step->log_prefix, step->score);
470 82 result->best_score = step->score;
471 82 memcpy(result->best, step->alloc,
472 82 result->planes_len * sizeof(struct liftoff_layer *));
473 }
474 116 return 0;
475 }
476
477 295 plane = liftoff_container_of(step->plane_link, plane, link);
478
479 295 remaining_planes = count_remaining_compatible_planes(output, step);
480
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 232 times.
295 if (result->best_score >= step->score + remaining_planes) {
481 /* Even if we find a layer for all remaining planes, we won't
482 * find a better allocation. Give up. */
483 63 return 0;
484 }
485
486 232 cursor = drmModeAtomicGetCursor(result->req);
487
488
2/4
✓ Branch 0 taken 232 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 232 times.
232 if (plane->layer != NULL || !check_plane_output_compatible(plane, output)) {
489 goto skip;
490 }
491
492 232 liftoff_log(LIFTOFF_DEBUG,
493 "%sPerforming allocation for plane %"PRIu32" (%zu/%zu)",
494 232 step->log_prefix, plane->id, step->plane_idx + 1, result->planes_len);
495
496
2/2
✓ Branch 0 taken 648 times.
✓ Branch 1 taken 232 times.
880 liftoff_list_for_each(layer, &output->layers, link) {
497
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 648 times.
648 if (layer->plane != NULL) {
498 continue;
499 }
500
2/2
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 623 times.
648 if (!layer_is_visible(layer)) {
501 25 continue;
502 }
503
2/2
✓ Branch 1 taken 270 times.
✓ Branch 2 taken 353 times.
623 if (!check_layer_plane_compatible(step, layer, plane)) {
504 270 continue;
505 }
506
507
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 353 times.
353 if (!check_deadline(result->started_at, result->timeout_ns)) {
508 liftoff_log(LIFTOFF_DEBUG, "%s Deadline exceeded",
509 step->log_prefix);
510 break;
511 }
512
513 /* Try to use this layer for the current plane */
514 353 ret = plane_apply(plane, layer, result->req);
515
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 344 times.
353 if (ret == -EINVAL) {
516 9 liftoff_log(LIFTOFF_DEBUG,
517 "%s Layer %p -> plane %"PRIu32": "
518 "incompatible properties",
519 9 step->log_prefix, (void *)layer, plane->id);
520 9 continue;
521
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 344 times.
344 } else if (ret != 0) {
522 return ret;
523 }
524
525 344 layer_add_candidate_plane(layer, plane);
526
527 /* If composition is forced, wait until after the
528 * layer_add_candidate_plane() call to reject the plane: we want
529 * to return a meaningful list of candidate planes so that the
530 * API user has the opportunity to re-allocate its buffers with
531 * scanout-capable ones. Same deal for the FB check. */
532
4/4
✓ Branch 0 taken 339 times.
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 338 times.
344 if (layer->force_composition || !plane_check_layer_fb(plane, layer)) {
533 6 drmModeAtomicSetCursor(result->req, cursor);
534 6 continue;
535 }
536
537 338 ret = device_test_commit(device, result->req, result->flags);
538
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 238 times.
338 if (ret == 0) {
539 100 liftoff_log(LIFTOFF_DEBUG,
540 "%s Layer %p -> plane %"PRIu32": success",
541 100 step->log_prefix, (void *)layer, plane->id);
542 /* Continue with the next plane */
543 100 plane_step_init_next(&next_step, step, layer);
544 100 ret = output_choose_layers(output, result, &next_step);
545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
100 if (ret != 0) {
546 return ret;
547 }
548
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 238 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
238 } else if (ret != -EINVAL && ret != -ERANGE && ret != -ENOSPC) {
549 return ret;
550 } else {
551 238 liftoff_log(LIFTOFF_DEBUG,
552 "%s Layer %p -> plane %"PRIu32": "
553 "test-only commit failed (%s)",
554 238 step->log_prefix, (void *)layer, plane->id,
555 strerror(-ret));
556 }
557
558 338 drmModeAtomicSetCursor(result->req, cursor);
559 }
560
561 232 skip:
562 /* Try not to use the current plane */
563 232 plane_step_init_next(&next_step, step, NULL);
564 232 ret = output_choose_layers(output, result, &next_step);
565
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 232 times.
232 if (ret != 0) {
566 return ret;
567 }
568 232 drmModeAtomicSetCursor(result->req, cursor);
569
570 232 return 0;
571 }
572
573 static int
574 1286 apply_current(struct liftoff_device *device, struct liftoff_output *output,
575 drmModeAtomicReq *req)
576 {
577 struct liftoff_plane *plane;
578 int cursor, ret;
579
580 1286 cursor = drmModeAtomicGetCursor(req);
581
582
2/2
✓ Branch 0 taken 2598 times.
✓ Branch 1 taken 1284 times.
3882 liftoff_list_for_each(plane, &device->planes, link) {
583 /* Don't touch any planes still pending on another CRTC. We won't
584 * have actually used these planes but if we try to set their FB or CRTC
585 * to 0 then we'll get EBUSY if they're still pending elsewhere */
586
3/4
✓ Branch 0 taken 558 times.
✓ Branch 1 taken 2040 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 558 times.
2598 if (plane->free_after_pageflip && plane->free_after_pageflip != output) {
587 continue;
588 }
589
590 /* If we go through and set FB=0 CRTC=0 on every currently-inactive
591 * plane then all of those planes will get pulled into this commit and
592 * then can't be touched in other commits while this one is pending.
593 * This is even true if both this commit and the next one are just
594 * disabling the plane (FB=0 CRTC=0) so making no difference.
595 *
596 * Only set FB=0 CRTC=0 on planes which were lit up on this
597 * CRTC and which we have *just* disabled. For any disabled planes
598 * which are not newly-disabled in this commit, just don't touch them. */
599
4/4
✓ Branch 0 taken 1305 times.
✓ Branch 1 taken 1293 times.
✓ Branch 2 taken 1297 times.
✓ Branch 3 taken 8 times.
2598 if (!plane->layer && plane->free_after_pageflip != output) {
600 1297 continue;
601 }
602
603 1301 ret = plane_apply(plane, plane->layer, req);
604
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1299 times.
1301 if (ret != 0) {
605 2 drmModeAtomicSetCursor(req, cursor);
606 2 return ret;
607 }
608 }
609
610 1284 return 0;
611 }
612
613 static bool
614 2441 fb_info_needs_realloc(const drmModeFB2 *a, const drmModeFB2 *b)
615 {
616
2/4
✓ Branch 0 taken 2441 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2441 times.
✗ Branch 3 not taken.
2441 if (a->width != b->width || a->height != b->height ||
617
3/4
✓ Branch 0 taken 2441 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2439 times.
2441 a->pixel_format != b->pixel_format || a->modifier != b->modifier) {
618 2 return true;
619 }
620
621 /* TODO: consider checking pitch and offset? */
622
623 2439 return false;
624 }
625
626 static bool
627 2 layer_intersection_changed(struct liftoff_layer *this,
628 struct liftoff_output *output)
629 {
630 struct liftoff_layer *other;
631 struct liftoff_rect this_cur, this_prev, other_cur, other_prev;
632
633 2 layer_get_rect(this, &this_cur);
634 2 layer_get_prev_rect(this, &this_prev);
635
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 liftoff_list_for_each(other, &output->layers, link) {
636
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (this == other) {
637 1 continue;
638 }
639
640 3 layer_get_rect(other, &other_cur);
641 3 layer_get_prev_rect(other, &other_prev);
642
643
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
6 if (rect_intersects(&this_cur, &other_cur) !=
644 3 rect_intersects(&this_prev, &other_prev)) {
645 1 return true;
646 }
647 }
648
649 1 return false;
650 }
651
652 static bool
653 2444 layer_needs_realloc(struct liftoff_layer *layer, struct liftoff_output *output)
654 {
655 struct liftoff_layer_property *prop;
656 2444 bool check_crtc_intersect = false;
657 size_t i;
658
659
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2443 times.
2444 if (layer->changed) {
660 1 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
661 "layer property added or force composition changed");
662 1 return true;
663 }
664
665
2/2
✓ Branch 0 taken 21971 times.
✓ Branch 1 taken 2428 times.
24399 for (i = 0; i < layer->props_len; i++) {
666 21971 prop = &layer->props[i];
667
668 /* If FB_ID changes from non-zero to zero, we don't need to
669 * display this layer anymore, so we may be able to re-use its
670 * plane for another layer. If FB_ID changes from zero to
671 * non-zero, we might be able to find a plane for this layer.
672 * If FB_ID changes from non-zero to non-zero and the FB
673 * attributes didn't change, we can try to re-use the previous
674 * allocation. */
675
2/2
✓ Branch 0 taken 2443 times.
✓ Branch 1 taken 19528 times.
21971 if (prop->core_index == LIFTOFF_PROP_FB_ID) {
676
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2442 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
2443 if (prop->value == 0 && prop->prev_value == 0) {
677 continue;
678 }
679
680
4/4
✓ Branch 0 taken 2442 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2441 times.
2443 if (prop->value == 0 || prop->prev_value == 0) {
681 2 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
682 "layer enabled or disabled");
683 2 return true;
684 }
685
686
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2439 times.
2441 if (fb_info_needs_realloc(&layer->fb_info,
687 2441 &layer->prev_fb_info)) {
688 2 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
689 "FB info changed");
690 2 return true;
691 }
692
693 2439 continue;
694 }
695
696 /* For all properties except FB_ID, we can skip realloc if the
697 * value didn't change. */
698
2/2
✓ Branch 0 taken 19509 times.
✓ Branch 1 taken 19 times.
19528 if (prop->value == prop->prev_value) {
699 19509 continue;
700 }
701
702 /* If the layer was or becomes completely transparent or
703 * completely opaque, we might be able to find a better
704 * allocation. Otherwise, we can keep the current one. */
705
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 11 times.
19 if (prop->core_index == LIFTOFF_PROP_ALPHA) {
706
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 1 times.
8 if (prop->value == 0 || prop->prev_value == 0 ||
707
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
6 prop->value == 0xFFFF || prop->prev_value == 0xFFFF) {
708 6 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
709 "alpha changed");
710 6 return true;
711 }
712 2 continue;
713 }
714
715 /* We should never need a re-alloc when IN_FENCE_FD or
716 * FB_DAMAGE_CLIPS changes. */
717
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2 times.
11 if (strcmp(prop->name, "IN_FENCE_FD") == 0 ||
718
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 strcmp(prop->name, "FB_DAMAGE_CLIPS") == 0) {
719 3 continue;
720 }
721
722 /* If CRTC_* changed, check for intersection later */
723
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if (strcmp(prop->name, "CRTC_X") == 0 ||
724
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 strcmp(prop->name, "CRTC_Y") == 0 ||
725
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 strcmp(prop->name, "CRTC_W") == 0 ||
726
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 strcmp(prop->name, "CRTC_H") == 0) {
727 3 check_crtc_intersect = true;
728 3 continue;
729 }
730
731 5 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
732 5 "property \"%s\" changed", prop->name);
733 5 return true;
734 }
735
736
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2426 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2430 if (check_crtc_intersect &&
737 2 layer_intersection_changed(layer, output)) {
738 1 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
739 "intersection with other layer(s) changed");
740 1 return true;
741 }
742
743 2427 return false;
744 }
745
746 static bool
747 1388 layer_is_higher_priority(struct liftoff_layer *this, struct liftoff_layer *other)
748 {
749 struct liftoff_layer_property *this_zpos, *other_zpos;
750 bool this_visible, other_visible, intersects;
751
752 // The composition layer should be highest priority.
753
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1388 times.
1388 if (this->output->composition_layer == this) {
754 return true;
755
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1372 times.
1388 } else if (this->output->composition_layer == other) {
756 16 return false;
757 }
758
759 // Invisible layers are given lowest priority. Pass-thru if both have
760 // same visibility
761 1372 this_visible = layer_is_visible(this);
762 1372 other_visible = layer_is_visible(other);
763
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1359 times.
1372 if (this_visible != other_visible) {
764 13 return this_visible;
765 }
766
767 // A layer's overall priority is determined by a combination of it's
768 // current_priority, it's zpos, and whether it intersects with others.
769 //
770 // Consider two layers. If they do not intersect, the layer with higher
771 // priority is given overall priority. However if both layers have
772 // identical priority, then the layer with higher zpos is given overall
773 // priority.
774 //
775 // If the layers intersect, their zpos determines the overall priority.
776 // If their zpos are identical, then simply fallback to looking at
777 // current_priority. Otherwise, the layer with higher zpos is given
778 // overall priority, since the top layer needs to be offloaded in order
779 // to offload the bottom layer.
780
781 1359 this_zpos = layer_get_core_property(this, LIFTOFF_PROP_ZPOS);
782 1359 other_zpos = layer_get_core_property(other, LIFTOFF_PROP_ZPOS);
783 1359 intersects = layer_intersects(this, other);
784
785
3/4
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 1296 times.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
1359 if (this_zpos != NULL && other_zpos != NULL) {
786
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 8 times.
63 if (intersects) {
787 55 return this_zpos->value == other_zpos->value ?
788
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55 times.
110 this->current_priority > other->current_priority :
789 55 this_zpos->value > other_zpos->value;
790 } else {
791 8 return this->current_priority == other->current_priority ?
792
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 this_zpos->value > other_zpos->value :
793 this->current_priority > other->current_priority;
794 }
795
2/4
✓ Branch 0 taken 1296 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1296 times.
✗ Branch 3 not taken.
1296 } else if (this_zpos == NULL && other_zpos == NULL) {
796 1296 return this->current_priority > other->current_priority;
797 } else {
798 // Either this or other zpos is null
799 return this_zpos != NULL;
800 }
801 }
802
803 static bool
804 1284 update_layers_order(struct liftoff_output *output)
805 {
806 struct liftoff_list *search, *max, *cur, *head;
807 struct liftoff_layer *this_layer, *other_layer;
808 1284 bool order_changed = false;
809
810 1284 head = &output->layers;
811 1284 cur = head;
812
813 // Run a insertion sort to order layers by priority.
814
2/2
✓ Branch 0 taken 2600 times.
✓ Branch 1 taken 1284 times.
5168 while (cur->next != head) {
815 2600 cur = cur->next;
816
817 2600 max = cur;
818 2600 search = cur;
819
2/2
✓ Branch 0 taken 1388 times.
✓ Branch 1 taken 2600 times.
3988 while (search->next != head) {
820 1388 search = search->next;
821 1388 this_layer = liftoff_container_of(search, this_layer, link);
822 1388 other_layer = liftoff_container_of(max, other_layer, link);
823
2/2
✓ Branch 1 taken 47 times.
✓ Branch 2 taken 1341 times.
1388 if (layer_is_higher_priority(this_layer, other_layer)) {
824 47 max = search;
825 }
826 }
827
828
2/2
✓ Branch 0 taken 2564 times.
✓ Branch 1 taken 36 times.
2600 if (cur != max) {
829 36 liftoff_list_swap(cur, max);
830 // max is now where iterator cur was, relocate to continue
831 36 cur = max;
832 36 order_changed = true;
833 }
834 }
835
836 1284 return order_changed;
837 }
838
839 static int
840 1284 reuse_previous_alloc(struct liftoff_output *output, drmModeAtomicReq *req,
841 uint32_t flags)
842 {
843 struct liftoff_device *device;
844 struct liftoff_layer *layer;
845 int cursor, ret;
846 bool layer_order_changed;
847
848 1284 device = output->device;
849
850 1284 layer_order_changed = update_layers_order(output);
851
852
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 1225 times.
1284 if (output->layers_changed) {
853 59 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
854 "a layer has been added or removed");
855 59 return -EINVAL;
856 }
857
858
2/2
✓ Branch 0 taken 2444 times.
✓ Branch 1 taken 1208 times.
3652 liftoff_list_for_each(layer, &output->layers, link) {
859
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 2427 times.
2444 if (layer_needs_realloc(layer, output)) {
860 17 return -EINVAL;
861 }
862 }
863
864
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1207 times.
1208 if (layer_order_changed) {
865 1 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
866 "layer priority order changed.");
867 1 return -EINVAL;
868 }
869
870 1207 cursor = drmModeAtomicGetCursor(req);
871
872 1207 ret = apply_current(device, output, req);
873
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1205 times.
1207 if (ret != 0) {
874 2 return ret;
875 }
876
877 1205 ret = device_test_commit(device, req, flags);
878
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1205 times.
1205 if (ret != 0) {
879 drmModeAtomicSetCursor(req, cursor);
880 }
881 1205 return ret;
882 }
883
884 static void
885 1284 mark_layers_clean(struct liftoff_output *output)
886 {
887 struct liftoff_layer *layer;
888
889 1284 output->layers_changed = false;
890
891
2/2
✓ Branch 0 taken 2600 times.
✓ Branch 1 taken 1284 times.
3884 liftoff_list_for_each(layer, &output->layers, link) {
892 2600 layer_mark_clean(layer);
893 }
894 1284 }
895
896 static void
897 1284 update_layers_priority(struct liftoff_device *device)
898 {
899 struct liftoff_output *output;
900 struct liftoff_layer *layer;
901 bool period_elapsed;
902
903 1284 device->page_flip_counter++;
904 1284 period_elapsed = device->page_flip_counter >= LIFTOFF_PRIORITY_PERIOD;
905
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 1264 times.
1284 if (period_elapsed) {
906 20 device->page_flip_counter = 0;
907 }
908
909
2/2
✓ Branch 0 taken 1284 times.
✓ Branch 1 taken 1284 times.
2568 liftoff_list_for_each(output, &device->outputs, link) {
910
2/2
✓ Branch 0 taken 2600 times.
✓ Branch 1 taken 1284 times.
3884 liftoff_list_for_each(layer, &output->layers, link) {
911 2600 layer_update_priority(layer, period_elapsed);
912 }
913 }
914 1284 }
915
916 static void
917 1284 update_layers_fb_info(struct liftoff_output *output)
918 {
919 struct liftoff_layer *layer;
920
921 /* We don't know what the library user did in-between
922 * liftoff_output_apply() calls. They might've removed the FB and
923 * re-created a completely different one which happens to have the same
924 * FB ID. */
925
2/2
✓ Branch 0 taken 2600 times.
✓ Branch 1 taken 1284 times.
3884 liftoff_list_for_each(layer, &output->layers, link) {
926 2600 layer->fb_info = (drmModeFB2){0};
927
928 2600 layer_cache_fb_info(layer);
929 /* TODO: propagate error? */
930 }
931 1284 }
932
933 static void
934 1205 log_reuse(struct liftoff_output *output)
935 {
936
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1196 times.
1205 if (output->alloc_reused_counter == 0) {
937 9 liftoff_log(LIFTOFF_DEBUG,
938 "Reusing previous plane allocation on output %"PRIu32,
939 output->crtc_id);
940 }
941 1205 output->alloc_reused_counter++;
942 1205 }
943
944 static void
945 79 log_no_reuse(struct liftoff_output *output)
946 {
947 79 liftoff_log(LIFTOFF_DEBUG, "Computing plane allocation on output %"PRIu32,
948 output->crtc_id);
949
950
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 78 times.
79 if (output->alloc_reused_counter != 0) {
951 1 liftoff_log(LIFTOFF_DEBUG,
952 "Stopped reusing previous plane allocation on "
953 "output %"PRIu32" (had reused it %d times)",
954 output->crtc_id, output->alloc_reused_counter);
955 1 output->alloc_reused_counter = 0;
956 }
957 79 }
958
959 static size_t
960 79 non_composition_layers_length(struct liftoff_output *output)
961 {
962 struct liftoff_layer *layer;
963 size_t n;
964
965 79 n = 0;
966
2/2
✓ Branch 0 taken 186 times.
✓ Branch 1 taken 79 times.
265 liftoff_list_for_each(layer, &output->layers, link) {
967
2/2
✓ Branch 1 taken 176 times.
✓ Branch 2 taken 10 times.
186 if (layer_is_visible(layer) &&
968
2/2
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 6 times.
176 output->composition_layer != layer) {
969 170 n++;
970 }
971 }
972
973 79 return n;
974 }
975
976 int
977 1284 liftoff_output_apply(struct liftoff_output *output, drmModeAtomicReq *req,
978 uint32_t flags,
979 const struct liftoff_output_apply_options *options)
980 {
981 struct liftoff_device *device;
982 struct liftoff_plane *plane;
983 struct liftoff_layer *layer;
984 1284 struct alloc_result result = {0};
985 1284 struct alloc_step step = {0};
986 1284 const struct liftoff_output_apply_options default_options = {0};
987 size_t i, candidate_planes;
988 int ret;
989 bool found_layer;
990
991
1/2
✓ Branch 0 taken 1284 times.
✗ Branch 1 not taken.
1284 if (options == NULL) {
992 1284 options = &default_options;
993 }
994
995 1284 device = output->device;
996
997 1284 update_layers_priority(device);
998 1284 update_layers_fb_info(output);
999
1000 1284 ret = reuse_previous_alloc(output, req, flags);
1001
2/2
✓ Branch 0 taken 1205 times.
✓ Branch 1 taken 79 times.
1284 if (ret == 0) {
1002 1205 log_reuse(output);
1003 1205 mark_layers_clean(output);
1004 1205 return 0;
1005 }
1006 79 log_no_reuse(output);
1007
1008 /* Reset layers' candidate planes */
1009
2/2
✓ Branch 0 taken 186 times.
✓ Branch 1 taken 79 times.
265 liftoff_list_for_each(layer, &output->layers, link) {
1010 186 layer_reset_candidate_planes(layer);
1011 }
1012
1013 79 device->test_commit_counter = 0;
1014 79 output_log_layers(output);
1015
1016 /* Unset all existing plane and layer mappings. */
1017
2/2
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 79 times.
263 liftoff_list_for_each(plane, &device->planes, link) {
1018
3/4
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 166 times.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
184 if (plane->layer != NULL && plane->layer->output == output) {
1019 18 plane->layer->plane = NULL;
1020 18 plane->layer = NULL;
1021 18 plane->free_after_pageflip = output;
1022 }
1023 }
1024
1025 /* Disable all planes we might use. Do it before building mappings to
1026 * make sure not to hit bandwidth limits because too many planes are
1027 * enabled. */
1028 79 candidate_planes = 0;
1029
2/2
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 79 times.
263 liftoff_list_for_each(plane, &device->planes, link) {
1030 /* Only disable the planes we've just unmapped. Any planes we touch
1031 * can't be used on other outputs (until vblank) so we only want to
1032 * touch the bare minimum. */
1033
3/4
✓ Branch 0 taken 184 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 166 times.
184 if (plane->layer == NULL && plane->free_after_pageflip == output) {
1034 18 candidate_planes++;
1035 18 liftoff_log(LIFTOFF_DEBUG,
1036 "Disabling plane %"PRIu32, plane->id);
1037 18 ret = plane_apply(plane, NULL, req);
1038
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 assert(ret != -EINVAL);
1039
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (ret != 0) {
1040 return ret;
1041 }
1042 }
1043 }
1044
1045 79 result.req = req;
1046 79 result.flags = flags;
1047 79 result.planes_len = liftoff_list_length(&device->planes);
1048
1049 79 step.alloc = malloc(result.planes_len * sizeof(step.alloc[0]));
1050 79 result.best = malloc(result.planes_len * sizeof(result.best[0]));
1051
2/4
✓ Branch 0 taken 79 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 79 times.
79 if (step.alloc == NULL || result.best == NULL) {
1052 liftoff_log_errno(LIFTOFF_ERROR, "malloc");
1053 return -ENOMEM;
1054 }
1055
1056
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 79 times.
79 if (clock_gettime(CLOCK_MONOTONIC, &result.started_at) != 0) {
1057 liftoff_log_errno(LIFTOFF_ERROR, "clock_gettime");
1058 return -errno;
1059 }
1060
1061 79 result.timeout_ns = options->timeout_ns;
1062
1/2
✓ Branch 0 taken 79 times.
✗ Branch 1 not taken.
79 if (result.timeout_ns == 0) {
1063 79 result.timeout_ns = DEFAULT_ALLOC_TIMEOUT_NSEC;
1064 }
1065
1066 /* For each plane, try to find a layer. Don't do it the other
1067 * way around (ie. for each layer, try to find a plane) because
1068 * some drivers want user-space to enable the primary plane
1069 * before any other plane. */
1070
1071 79 result.best_score = -1;
1072 79 memset(result.best, 0, result.planes_len * sizeof(result.best[0]));
1073 79 result.has_composition_layer = output->composition_layer != NULL;
1074 79 result.non_composition_layers_len =
1075 79 non_composition_layers_length(output);
1076 79 step.plane_link = device->planes.next;
1077 79 step.plane_idx = 0;
1078 79 step.score = 0;
1079 79 step.last_layer_zpos = INT_MAX;
1080 79 step.primary_layer_zpos = INT_MIN;
1081 79 step.primary_plane_zpos = INT_MAX;
1082 79 step.composited = false;
1083 79 ret = output_choose_layers(output, &result, &step);
1084
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
79 if (ret != 0) {
1085 return ret;
1086 }
1087
1088 79 liftoff_log(LIFTOFF_DEBUG,
1089 "Found plane allocation for output %"PRIu32" "
1090 "(score: %d, candidate planes: %zu, tests: %d):",
1091 output->crtc_id, result.best_score, candidate_planes,
1092 device->test_commit_counter);
1093
1094 /* Apply the best allocation */
1095 79 i = 0;
1096 79 found_layer = false;
1097
2/2
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 79 times.
263 liftoff_list_for_each(plane, &device->planes, link) {
1098 184 layer = result.best[i];
1099 184 i++;
1100
2/2
✓ Branch 0 taken 97 times.
✓ Branch 1 taken 87 times.
184 if (layer == NULL) {
1101 97 continue;
1102 }
1103
1104 87 liftoff_log(LIFTOFF_DEBUG, " Layer %p -> plane %"PRIu32,
1105 (void *)layer, plane->id);
1106
1107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 87 times.
87 assert(plane->layer == NULL);
1108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 87 times.
87 assert(layer->plane == NULL);
1109 87 plane->layer = layer;
1110 87 layer->plane = plane;
1111
1112 87 found_layer = true;
1113 }
1114
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 61 times.
79 if (!found_layer) {
1115 18 liftoff_log(LIFTOFF_DEBUG, " (No layer has a plane)");
1116 }
1117
1118 79 ret = apply_current(device, output, req);
1119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
79 if (ret != 0) {
1120 return ret;
1121 }
1122
1123 79 free(step.alloc);
1124 79 free(result.best);
1125
1126 79 mark_layers_clean(output);
1127
1128 79 return 0;
1129 }
1130