GCC Code Coverage Report


Directory: ./
File: alloc.c
Date: 2025-02-19 14:16:04
Exec Total Coverage
Lines: 453 488 92.8%
Functions: 27 27 100.0%
Branches: 286 340 84.1%

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 810 timespec_to_nsec(struct timespec ts)
108 {
109 810 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 405 check_deadline(struct timespec start, int64_t timeout_ns)
116 {
117 struct timespec now;
118
119
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→6) taken 405 times.
405 if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
120 liftoff_log_errno(LIFTOFF_ERROR, "clock_gettime");
121 return false;
122 }
123
124 405 return timespec_to_nsec(now) - timeout_ns < timespec_to_nsec(start);
125 }
126
127 static void
128 322 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 322 plane = liftoff_container_of(prev->plane_link, plane, link);
136
137 322 step->plane_link = prev->plane_link->next;
138 322 step->plane_idx = prev->plane_idx + 1;
139 322 step->alloc = prev->alloc;
140 322 step->alloc[prev->plane_idx] = layer;
141
142
4/4
✓ Branch 0 (2→3) taken 100 times.
✓ Branch 1 (2→8) taken 222 times.
✓ Branch 2 (3→4) taken 6 times.
✓ Branch 3 (3→8) taken 94 times.
322 if (layer != NULL && layer == layer->output->composition_layer) {
143
1/2
✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 6 times.
6 assert(!prev->composited);
144 6 step->composited = true;
145 } else {
146 316 step->composited = prev->composited;
147 }
148
149
4/4
✓ Branch 0 (9→10) taken 100 times.
✓ Branch 1 (9→12) taken 222 times.
✓ Branch 2 (10→11) taken 94 times.
✓ Branch 3 (10→12) taken 6 times.
322 if (layer != NULL && layer != layer->output->composition_layer) {
150 94 step->score = prev->score + 1;
151 } else {
152 228 step->score = prev->score;
153 }
154
155 322 zpos_prop = NULL;
156
2/2
✓ Branch 0 (13→14) taken 100 times.
✓ Branch 1 (13→15) taken 222 times.
322 if (layer != NULL) {
157 100 zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS);
158 }
159
4/4
✓ Branch 0 (15→16) taken 54 times.
✓ Branch 1 (15→18) taken 268 times.
✓ Branch 2 (16→17) taken 31 times.
✓ Branch 3 (16→18) taken 23 times.
322 if (zpos_prop != NULL && plane->type != DRM_PLANE_TYPE_PRIMARY) {
160 31 step->last_layer_zpos = zpos_prop->value;
161 } else {
162 291 step->last_layer_zpos = prev->last_layer_zpos;
163 }
164
4/4
✓ Branch 0 (19→20) taken 54 times.
✓ Branch 1 (19→22) taken 268 times.
✓ Branch 2 (20→21) taken 23 times.
✓ Branch 3 (20→22) taken 31 times.
322 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 299 step->primary_layer_zpos = prev->primary_layer_zpos;
169 299 step->primary_plane_zpos = prev->primary_plane_zpos;
170 }
171
172
2/2
✓ Branch 0 (23→24) taken 100 times.
✓ Branch 1 (23→27) taken 222 times.
322 if (layer != NULL) {
173 100 len = strlen(prev->log_prefix) + 2;
174
1/2
✗ Branch 0 (24→25) not taken.
✓ Branch 1 (24→26) 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 222 memcpy(step->log_prefix, prev->log_prefix,
181 sizeof(step->log_prefix));
182 }
183 322 }
184
185 static bool
186 2157 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 (6→3) taken 2828 times.
✓ Branch 1 (6→7) taken 1686 times.
4514 for (i = 0; i < step->plane_idx; i++) {
193
2/2
✓ Branch 0 (3→4) taken 471 times.
✓ Branch 1 (3→5) taken 2357 times.
2828 if (step->alloc[i] == layer) {
194 471 return true;
195 }
196 }
197 1686 return false;
198 }
199
200 static bool
201 354 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 354 zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS);
208
2/2
✓ Branch 0 (3→4) taken 110 times.
✓ Branch 1 (3→5) taken 244 times.
354 if (zpos_prop == NULL) {
209 110 return false;
210 }
211
212
2/2
✓ Branch 0 (17→6) taken 665 times.
✓ Branch 1 (17→18) taken 132 times.
797 liftoff_list_for_each(other_layer, &output->layers, link) {
213
2/2
✓ Branch 0 (7→8) taken 160 times.
✓ Branch 1 (7→9) taken 505 times.
665 if (is_layer_allocated(step, other_layer)) {
214 160 continue;
215 }
216
217 505 other_zpos_prop = layer_get_core_property(other_layer,
218 LIFTOFF_PROP_ZPOS);
219
1/2
✗ Branch 0 (10→11) not taken.
✓ Branch 1 (10→12) taken 505 times.
505 if (other_zpos_prop == NULL) {
220 continue;
221 }
222
223
2/2
✓ Branch 0 (13→14) taken 453 times.
✓ Branch 1 (13→16) taken 52 times.
505 if (layer_intersects(layer, other_layer) &&
224
2/2
✓ Branch 0 (14→15) taken 112 times.
✓ Branch 1 (14→16) taken 341 times.
453 other_zpos_prop->value > zpos_prop->value) {
225 112 return true;
226 }
227 }
228
229 132 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 (3→4) not taken.
✓ Branch 1 (3→5) taken 8 times.
8 if (zpos_prop == NULL) {
243 return false;
244 }
245
246 8 i = -1;
247
1/2
✓ Branch 0 (20→6) taken 29 times.
✗ Branch 1 (20→21) not taken.
29 liftoff_list_for_each(other_plane, &output->device->planes, link) {
248 29 i++;
249
2/2
✓ Branch 0 (6→7) taken 8 times.
✓ Branch 1 (6→8) taken 21 times.
29 if (i >= (ssize_t)step->plane_idx) {
250 8 break;
251 }
252
2/2
✓ Branch 0 (8→9) taken 8 times.
✓ Branch 1 (8→10) 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 (10→11) taken 4 times.
✓ Branch 1 (10→12) 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 (13→14) not taken.
✓ Branch 1 (13→15) 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 (15→16) taken 8 times.
✓ Branch 1 (15→19) taken 1 times.
✗ Branch 2 (17→18) not taken.
✓ Branch 3 (17→19) 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 303 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 303 plane = liftoff_container_of(step->plane_link, plane, link);
287
288 303 i = -1;
289
1/2
✓ Branch 0 (14→3) taken 742 times.
✗ Branch 1 (14→15) not taken.
742 liftoff_list_for_each(other_plane, &output->device->planes, link) {
290 742 i++;
291
2/2
✓ Branch 0 (3→4) taken 301 times.
✓ Branch 1 (3→5) taken 441 times.
742 if (i >= (ssize_t)step->plane_idx) {
292 301 break;
293 }
294
2/2
✓ Branch 0 (5→6) taken 236 times.
✓ Branch 1 (5→7) taken 205 times.
441 if (other_plane->type == DRM_PLANE_TYPE_PRIMARY) {
295 236 continue;
296 }
297
2/2
✓ Branch 0 (7→8) taken 162 times.
✓ Branch 1 (7→9) taken 43 times.
205 if (step->alloc[i] == NULL) {
298 162 continue;
299 }
300
301
4/4
✓ Branch 0 (9→10) taken 3 times.
✓ Branch 1 (9→13) taken 40 times.
✓ Branch 2 (11→12) taken 2 times.
✓ Branch 3 (11→13) taken 1 times.
46 if (plane->zpos >= other_plane->zpos &&
302 3 layer_intersects(layer, step->alloc[i])) {
303 2 return true;
304 }
305 }
306
307 301 return false;
308 }
309
310 static bool
311 676 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 676 output = layer->output;
319
320 /* Skip this layer if already allocated */
321
2/2
✓ Branch 0 (3→4) taken 150 times.
✓ Branch 1 (3→5) taken 526 times.
676 if (is_layer_allocated(step, layer)) {
322 150 return false;
323 }
324
325 526 zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS);
326
2/2
✓ Branch 0 (6→7) taken 311 times.
✓ Branch 1 (6→22) taken 215 times.
526 if (zpos_prop != NULL) {
327
3/4
✓ Branch 0 (7→8) taken 8 times.
✓ Branch 1 (7→12) taken 303 times.
✗ Branch 2 (9→10) not taken.
✓ Branch 3 (9→12) taken 8 times.
319 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 (12→13) taken 303 times.
✓ Branch 1 (12→17) taken 8 times.
✓ Branch 2 (14→15) taken 2 times.
✓ Branch 3 (14→17) taken 301 times.
614 if ((int)zpos_prop->value < step->last_layer_zpos &&
338 303 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 (17→18) taken 250 times.
✓ Branch 1 (17→22) taken 59 times.
309 if (plane->type != DRM_PLANE_TYPE_PRIMARY &&
351
2/2
✓ Branch 0 (18→19) taken 6 times.
✓ Branch 1 (18→22) taken 244 times.
250 (int)zpos_prop->value < step->primary_layer_zpos &&
352
1/2
✓ Branch 0 (19→20) taken 6 times.
✗ Branch 1 (19→22) 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 (22→23) taken 354 times.
✓ Branch 1 (22→27) taken 164 times.
✓ Branch 2 (24→25) taken 112 times.
✓ Branch 3 (24→27) taken 242 times.
872 if (plane->type != DRM_PLANE_TYPE_PRIMARY &&
368 354 has_composited_layer_over(output, step, layer)) {
369 112 liftoff_log(LIFTOFF_DEBUG,
370 "%s Layer %p -> plane %"PRIu32": "
371 "has composited layer on top",
372 112 step->log_prefix, (void *)layer, plane->id);
373 112 return false;
374 }
375
376
2/2
✓ Branch 0 (27→28) taken 242 times.
✓ Branch 1 (27→31) taken 164 times.
406 if (plane->type != DRM_PLANE_TYPE_PRIMARY &&
377
2/2
✓ Branch 0 (28→29) taken 1 times.
✓ Branch 1 (28→31) taken 241 times.
242 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 405 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 (2→3) taken 11 times.
✓ Branch 1 (2→7) taken 74 times.
✓ Branch 2 (3→4) taken 5 times.
✓ Branch 3 (3→7) taken 6 times.
85 if (result->has_composition_layer && !step->composited &&
398
2/2
✓ Branch 0 (4→5) taken 3 times.
✓ Branch 1 (4→7) 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 (7→8) taken 6 times.
✓ Branch 1 (7→11) taken 76 times.
82 if (step->composited &&
408
1/2
✗ Branch 0 (8→9) not taken.
✓ Branch 1 (8→11) 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 814 check_plane_output_compatible(struct liftoff_plane *plane, struct liftoff_output *output)
422 {
423
2/2
✓ Branch 0 (2→3) taken 45 times.
✓ Branch 1 (2→5) taken 769 times.
814 if (plane->free_after_pageflip != 0 &&
424
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 45 times.
45 plane->free_after_pageflip != output->crtc_id) {
425 /* This plane is still attached to another CRTC until vblank */
426 return false;
427 }
428
429 814 return (plane->possible_crtcs & (1UL << output->crtc_index)) != 0;
430 }
431
432 static int
433 285 count_remaining_compatible_planes(struct liftoff_output *output,
434 struct alloc_step *step)
435 {
436 struct liftoff_list *link;
437 struct liftoff_plane *plane;
438 285 int remaining = 0;
439
440
2/2
✓ Branch 0 (8→3) taken 529 times.
✓ Branch 1 (8→9) taken 285 times.
814 for (link = step->plane_link; link != &output->device->planes; link = link->next) {
441 529 plane = liftoff_container_of(link, plane, link);
442
2/4
✓ Branch 0 (3→4) taken 529 times.
✗ Branch 1 (3→7) not taken.
✓ Branch 2 (5→6) taken 529 times.
✗ Branch 3 (5→7) not taken.
1058 if (plane->layer == NULL &&
443 529 check_plane_output_compatible(plane, output)) {
444 529 remaining++;
445 }
446 }
447
448 285 return remaining;
449 }
450
451 static int
452 285 calculate_best_possible_score(struct liftoff_output *output, struct alloc_result *result,
453 struct alloc_step *step)
454 {
455 struct liftoff_layer *layer;
456 int remaining_planes, remaining_layers, remaining_allocs;
457
458 285 remaining_planes = count_remaining_compatible_planes(output, step);
459
460 /* Count number of remaining scoring layers */
461 285 remaining_layers = 0;
462
2/2
✓ Branch 0 (14→4) taken 816 times.
✓ Branch 1 (14→15) taken 285 times.
1101 liftoff_list_for_each(layer, &output->layers, link) {
463 /* Allocated layers are already included in current score */
464
2/2
✓ Branch 0 (5→6) taken 161 times.
✓ Branch 1 (5→7) taken 655 times.
816 if (is_layer_allocated(step, layer)) {
465 161 continue;
466 }
467 /* Invisible layers won't be allocated */
468
2/2
✓ Branch 0 (8→9) taken 25 times.
✓ Branch 1 (8→10) taken 630 times.
655 if (!layer_is_visible(layer)) {
469 25 continue;
470 }
471 /* Composition layer isn't included in the score */
472
2/2
✓ Branch 0 (10→11) taken 34 times.
✓ Branch 1 (10→12) taken 596 times.
630 if (layer == output->composition_layer) {
473 34 continue;
474 }
475 596 remaining_layers++;
476 }
477
478 285 remaining_allocs = remaining_layers < remaining_planes
479 ? remaining_layers : remaining_planes;
480 285 return step->score + remaining_allocs;
481 }
482
483 static int
484 401 output_choose_layers(struct liftoff_output *output, struct alloc_result *result,
485 struct alloc_step *step)
486 {
487 struct liftoff_device *device;
488 struct liftoff_plane *plane;
489 struct liftoff_layer *layer;
490 int cursor, ret;
491 int best_possible_score;
492 401 struct alloc_step next_step = {0};
493
494 401 device = output->device;
495
496
2/2
✓ Branch 0 (2→3) taken 116 times.
✓ Branch 1 (2→9) taken 285 times.
401 if (step->plane_link == &device->planes) { /* Allocation finished */
497
4/4
✓ Branch 0 (3→4) taken 85 times.
✓ Branch 1 (3→8) taken 31 times.
✓ Branch 2 (5→6) taken 82 times.
✓ Branch 3 (5→8) taken 3 times.
201 if (step->score > result->best_score &&
498 85 check_alloc_valid(output, result, step)) {
499 /* We found a better allocation */
500 82 liftoff_log(LIFTOFF_DEBUG,
501 "%sFound a better allocation with score=%d",
502 82 step->log_prefix, step->score);
503 82 result->best_score = step->score;
504 82 memcpy(result->best, step->alloc,
505 82 result->planes_len * sizeof(struct liftoff_layer *));
506 }
507 116 return 0;
508 }
509
510 285 plane = liftoff_container_of(step->plane_link, plane, link);
511
512 285 best_possible_score = calculate_best_possible_score(output, result, step);
513
514 285 cursor = drmModeAtomicGetCursor(result->req);
515
516
2/4
✓ Branch 0 (11→12) taken 285 times.
✗ Branch 1 (11→14) not taken.
✗ Branch 2 (13→14) not taken.
✓ Branch 3 (13→15) taken 285 times.
285 if (plane->layer != NULL || !check_plane_output_compatible(plane, output)) {
517 goto skip;
518 }
519
520 285 liftoff_log(LIFTOFF_DEBUG,
521 "%sPerforming allocation for plane %"PRIu32" (%zu/%zu)",
522 285 step->log_prefix, plane->id, step->plane_idx + 1, result->planes_len);
523
524
2/2
✓ Branch 0 (59→17) taken 699 times.
✓ Branch 1 (59→60) taken 222 times.
921 liftoff_list_for_each(layer, &output->layers, link) {
525
1/2
✗ Branch 0 (17→18) not taken.
✓ Branch 1 (17→19) taken 699 times.
699 if (layer->plane != NULL) {
526 continue;
527 }
528
2/2
✓ Branch 0 (20→21) taken 23 times.
✓ Branch 1 (20→22) taken 676 times.
699 if (!layer_is_visible(layer)) {
529 23 continue;
530 }
531
2/2
✓ Branch 0 (23→24) taken 271 times.
✓ Branch 1 (23→25) taken 405 times.
676 if (!check_layer_plane_compatible(step, layer, plane)) {
532 271 continue;
533 }
534
535
1/2
✗ Branch 0 (26→27) not taken.
✓ Branch 1 (26→29) taken 405 times.
405 if (!check_deadline(result->started_at, result->timeout_ns)) {
536 liftoff_log(LIFTOFF_DEBUG, "%s Deadline exceeded",
537 step->log_prefix);
538 break;
539 }
540
541
2/2
✓ Branch 0 (29→30) taken 63 times.
✓ Branch 1 (29→32) taken 342 times.
405 if (result->best_score >= best_possible_score) {
542 /* Even if we find a layer for all remaining planes, we won't
543 * find a better allocation. Give up. */
544 63 liftoff_log(LIFTOFF_DEBUG,
545 "%sEarly-exit because %d is the best possible score",
546 63 step->log_prefix, result->best_score);
547 63 return 0;
548 }
549
550 /* Try to use this layer for the current plane */
551 342 ret = plane_apply(plane, layer, result->req);
552
2/2
✓ Branch 0 (33→34) taken 9 times.
✓ Branch 1 (33→36) taken 333 times.
342 if (ret == -EINVAL) {
553 9 liftoff_log(LIFTOFF_DEBUG,
554 "%s Layer %p -> plane %"PRIu32": "
555 "incompatible properties",
556 9 step->log_prefix, (void *)layer, plane->id);
557 9 continue;
558
1/2
✗ Branch 0 (36→37) not taken.
✓ Branch 1 (36→38) taken 333 times.
333 } else if (ret != 0) {
559 return ret;
560 }
561
562 333 layer_add_candidate_plane(layer, plane);
563
564 /* If composition is forced, wait until after the
565 * layer_add_candidate_plane() call to reject the plane: we want
566 * to return a meaningful list of candidate planes so that the
567 * API user has the opportunity to re-allocate its buffers with
568 * scanout-capable ones. Same deal for the FB check. */
569
4/4
✓ Branch 0 (39→40) taken 328 times.
✓ Branch 1 (39→42) taken 5 times.
✓ Branch 2 (41→42) taken 1 times.
✓ Branch 3 (41→44) taken 327 times.
333 if (layer->force_composition || !plane_check_layer_fb(plane, layer)) {
570 6 drmModeAtomicSetCursor(result->req, cursor);
571 6 continue;
572 }
573
574 327 ret = device_test_commit(device, result->req, result->flags);
575
2/2
✓ Branch 0 (45→46) taken 100 times.
✓ Branch 1 (45→51) taken 227 times.
327 if (ret == 0) {
576 100 liftoff_log(LIFTOFF_DEBUG,
577 "%s Layer %p -> plane %"PRIu32": success",
578 100 step->log_prefix, (void *)layer, plane->id);
579 /* Continue with the next plane */
580 100 plane_step_init_next(&next_step, step, layer);
581 100 ret = output_choose_layers(output, result, &next_step);
582
1/2
✗ Branch 0 (49→50) not taken.
✓ Branch 1 (49→57) taken 100 times.
100 if (ret != 0) {
583 return ret;
584 }
585
1/6
✗ Branch 0 (51→52) not taken.
✓ Branch 1 (51→55) taken 227 times.
✗ Branch 2 (52→53) not taken.
✗ Branch 3 (52→55) not taken.
✗ Branch 4 (53→54) not taken.
✗ Branch 5 (53→55) not taken.
227 } else if (ret != -EINVAL && ret != -ERANGE && ret != -ENOSPC) {
586 return ret;
587 } else {
588 227 liftoff_log(LIFTOFF_DEBUG,
589 "%s Layer %p -> plane %"PRIu32": "
590 "test-only commit failed (%s)",
591 227 step->log_prefix, (void *)layer, plane->id,
592 strerror(-ret));
593 }
594
595 327 drmModeAtomicSetCursor(result->req, cursor);
596 }
597
598 222 skip:
599 /* Try not to use the current plane */
600 222 plane_step_init_next(&next_step, step, NULL);
601 222 ret = output_choose_layers(output, result, &next_step);
602
1/2
✗ Branch 0 (63→64) not taken.
✓ Branch 1 (63→65) taken 222 times.
222 if (ret != 0) {
603 return ret;
604 }
605 222 drmModeAtomicSetCursor(result->req, cursor);
606
607 222 return 0;
608 }
609
610 static int
611 1286 apply_current(struct liftoff_device *device, struct liftoff_output *output,
612 drmModeAtomicReq *req)
613 {
614 struct liftoff_plane *plane;
615 int cursor, ret;
616
617 1286 cursor = drmModeAtomicGetCursor(req);
618
619
2/2
✓ Branch 0 (15→4) taken 2598 times.
✓ Branch 1 (15→16) taken 1284 times.
3882 liftoff_list_for_each(plane, &device->planes, link) {
620 /* Don't touch any planes still pending on another CRTC. We won't
621 * have actually used these planes but if we try to set their FB or CRTC
622 * to 0 then we'll get EBUSY if they're still pending elsewhere */
623
2/2
✓ Branch 0 (4→5) taken 558 times.
✓ Branch 1 (4→7) taken 2040 times.
2598 if (plane->free_after_pageflip != 0 &&
624
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 558 times.
558 plane->free_after_pageflip != output->crtc_id) {
625 continue;
626 }
627
628 /* If we go through and set FB=0 CRTC=0 on every currently-inactive
629 * plane then all of those planes will get pulled into this commit and
630 * then can't be touched in other commits while this one is pending.
631 * This is even true if both this commit and the next one are just
632 * disabling the plane (FB=0 CRTC=0) so making no difference.
633 *
634 * Only set FB=0 CRTC=0 on planes which were lit up on this
635 * CRTC and which we have *just* disabled. For any disabled planes
636 * which are not newly-disabled in this commit, just don't touch them. */
637
4/4
✓ Branch 0 (7→8) taken 1305 times.
✓ Branch 1 (7→10) taken 1293 times.
✓ Branch 2 (8→9) taken 1297 times.
✓ Branch 3 (8→10) taken 8 times.
2598 if (!plane->layer && plane->free_after_pageflip != output->crtc_id) {
638 1297 continue;
639 }
640
641 1301 ret = plane_apply(plane, plane->layer, req);
642
2/2
✓ Branch 0 (11→12) taken 2 times.
✓ Branch 1 (11→14) taken 1299 times.
1301 if (ret != 0) {
643 2 drmModeAtomicSetCursor(req, cursor);
644 2 return ret;
645 }
646 }
647
648 1284 return 0;
649 }
650
651 static bool
652 2441 fb_info_needs_realloc(const drmModeFB2 *a, const drmModeFB2 *b)
653 {
654
2/4
✓ Branch 0 (2→3) taken 2441 times.
✗ Branch 1 (2→6) not taken.
✓ Branch 2 (3→4) taken 2441 times.
✗ Branch 3 (3→6) not taken.
2441 if (a->width != b->width || a->height != b->height ||
655
3/4
✓ Branch 0 (4→5) taken 2441 times.
✗ Branch 1 (4→6) not taken.
✓ Branch 2 (5→6) taken 2 times.
✓ Branch 3 (5→7) taken 2439 times.
2441 a->pixel_format != b->pixel_format || a->modifier != b->modifier) {
656 2 return true;
657 }
658
659 /* TODO: consider checking pitch and offset? */
660
661 2439 return false;
662 }
663
664 static bool
665 2 layer_intersection_changed(struct liftoff_layer *this,
666 struct liftoff_output *output)
667 {
668 struct liftoff_layer *other;
669 struct liftoff_rect this_cur, this_prev, other_cur, other_prev;
670
671 2 layer_get_rect(this, &this_cur);
672 2 layer_get_prev_rect(this, &this_prev);
673
2/2
✓ Branch 0 (14→5) taken 4 times.
✓ Branch 1 (14→15) taken 1 times.
5 liftoff_list_for_each(other, &output->layers, link) {
674
2/2
✓ Branch 0 (5→6) taken 1 times.
✓ Branch 1 (5→7) taken 3 times.
4 if (this == other) {
675 1 continue;
676 }
677
678 3 layer_get_rect(other, &other_cur);
679 3 layer_get_prev_rect(other, &other_prev);
680
681
2/2
✓ Branch 0 (11→12) taken 1 times.
✓ Branch 1 (11→13) taken 2 times.
6 if (rect_intersects(&this_cur, &other_cur) !=
682 3 rect_intersects(&this_prev, &other_prev)) {
683 1 return true;
684 }
685 }
686
687 1 return false;
688 }
689
690 static bool
691 2444 layer_needs_realloc(struct liftoff_layer *layer, struct liftoff_output *output)
692 {
693 struct liftoff_layer_property *prop;
694 2444 bool check_crtc_intersect = false;
695 size_t i;
696
697
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→5) taken 2443 times.
2444 if (layer->changed) {
698 1 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
699 "layer property added or force composition changed");
700 1 return true;
701 }
702
703
2/2
✓ Branch 0 (40→6) taken 21971 times.
✓ Branch 1 (40→41) taken 2428 times.
24399 for (i = 0; i < layer->props_len; i++) {
704 21971 prop = &layer->props[i];
705
706 /* If FB_ID changes from non-zero to zero, we don't need to
707 * display this layer anymore, so we may be able to re-use its
708 * plane for another layer. If FB_ID changes from zero to
709 * non-zero, we might be able to find a plane for this layer.
710 * If FB_ID changes from non-zero to non-zero and the FB
711 * attributes didn't change, we can try to re-use the previous
712 * allocation. */
713
2/2
✓ Branch 0 (6→7) taken 2443 times.
✓ Branch 1 (6→19) taken 19528 times.
21971 if (prop->core_index == LIFTOFF_PROP_FB_ID) {
714
3/4
✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→10) taken 2442 times.
✗ Branch 2 (8→9) not taken.
✓ Branch 3 (8→10) taken 1 times.
2443 if (prop->value == 0 && prop->prev_value == 0) {
715 continue;
716 }
717
718
4/4
✓ Branch 0 (10→11) taken 2442 times.
✓ Branch 1 (10→12) taken 1 times.
✓ Branch 2 (11→12) taken 1 times.
✓ Branch 3 (11→14) taken 2441 times.
2443 if (prop->value == 0 || prop->prev_value == 0) {
719 2 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
720 "layer enabled or disabled");
721 2 return true;
722 }
723
724
2/2
✓ Branch 0 (15→16) taken 2 times.
✓ Branch 1 (15→18) taken 2439 times.
2441 if (fb_info_needs_realloc(&layer->fb_info,
725 2441 &layer->prev_fb_info)) {
726 2 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
727 "FB info changed");
728 2 return true;
729 }
730
731 2439 continue;
732 }
733
734 /* For all properties except FB_ID, we can skip realloc if the
735 * value didn't change. */
736
2/2
✓ Branch 0 (19→20) taken 19509 times.
✓ Branch 1 (19→21) taken 19 times.
19528 if (prop->value == prop->prev_value) {
737 19509 continue;
738 }
739
740 /* If the layer was or becomes completely transparent or
741 * completely opaque, we might be able to find a better
742 * allocation. Otherwise, we can keep the current one. */
743
2/2
✓ Branch 0 (21→22) taken 8 times.
✓ Branch 1 (21→29) taken 11 times.
19 if (prop->core_index == LIFTOFF_PROP_ALPHA) {
744
4/4
✓ Branch 0 (22→23) taken 7 times.
✓ Branch 1 (22→26) taken 1 times.
✓ Branch 2 (23→24) taken 6 times.
✓ Branch 3 (23→26) taken 1 times.
8 if (prop->value == 0 || prop->prev_value == 0 ||
745
4/4
✓ Branch 0 (24→25) taken 4 times.
✓ Branch 1 (24→26) taken 2 times.
✓ Branch 2 (25→26) taken 2 times.
✓ Branch 3 (25→28) taken 2 times.
6 prop->value == 0xFFFF || prop->prev_value == 0xFFFF) {
746 6 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
747 "alpha changed");
748 6 return true;
749 }
750 2 continue;
751 }
752
753 /* We should never need a re-alloc when IN_FENCE_FD or
754 * FB_DAMAGE_CLIPS changes. */
755
2/2
✓ Branch 0 (29→30) taken 9 times.
✓ Branch 1 (29→31) taken 2 times.
11 if (strcmp(prop->name, "IN_FENCE_FD") == 0 ||
756
2/2
✓ Branch 0 (30→31) taken 1 times.
✓ Branch 1 (30→32) taken 8 times.
9 strcmp(prop->name, "FB_DAMAGE_CLIPS") == 0) {
757 3 continue;
758 }
759
760 /* If CRTC_* changed, check for intersection later */
761
2/2
✓ Branch 0 (32→33) taken 6 times.
✓ Branch 1 (32→36) taken 2 times.
8 if (strcmp(prop->name, "CRTC_X") == 0 ||
762
2/2
✓ Branch 0 (33→34) taken 5 times.
✓ Branch 1 (33→36) taken 1 times.
6 strcmp(prop->name, "CRTC_Y") == 0 ||
763
1/2
✓ Branch 0 (34→35) taken 5 times.
✗ Branch 1 (34→36) not taken.
5 strcmp(prop->name, "CRTC_W") == 0 ||
764
1/2
✗ Branch 0 (35→36) not taken.
✓ Branch 1 (35→37) taken 5 times.
5 strcmp(prop->name, "CRTC_H") == 0) {
765 3 check_crtc_intersect = true;
766 3 continue;
767 }
768
769 5 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
770 5 "property \"%s\" changed", prop->name);
771 5 return true;
772 }
773
774
4/4
✓ Branch 0 (41→42) taken 2 times.
✓ Branch 1 (41→46) taken 2426 times.
✓ Branch 2 (43→44) taken 1 times.
✓ Branch 3 (43→46) taken 1 times.
2430 if (check_crtc_intersect &&
775 2 layer_intersection_changed(layer, output)) {
776 1 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
777 "intersection with other layer(s) changed");
778 1 return true;
779 }
780
781 2427 return false;
782 }
783
784 static bool
785 1388 layer_is_higher_priority(struct liftoff_layer *this, struct liftoff_layer *other)
786 {
787 struct liftoff_layer_property *this_zpos, *other_zpos;
788 bool this_visible, other_visible, intersects;
789
790 // The composition layer should be highest priority.
791
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 1388 times.
1388 if (this->output->composition_layer == this) {
792 return true;
793
2/2
✓ Branch 0 (4→5) taken 16 times.
✓ Branch 1 (4→6) taken 1372 times.
1388 } else if (this->output->composition_layer == other) {
794 16 return false;
795 }
796
797 // Invisible layers are given lowest priority. Pass-thru if both have
798 // same visibility
799 1372 this_visible = layer_is_visible(this);
800 1372 other_visible = layer_is_visible(other);
801
2/2
✓ Branch 0 (8→9) taken 13 times.
✓ Branch 1 (8→10) taken 1359 times.
1372 if (this_visible != other_visible) {
802 13 return this_visible;
803 }
804
805 // A layer's overall priority is determined by a combination of it's
806 // current_priority, it's zpos, and whether it intersects with others.
807 //
808 // Consider two layers. If they do not intersect, the layer with higher
809 // priority is given overall priority. However if both layers have
810 // identical priority, then the layer with higher zpos is given overall
811 // priority.
812 //
813 // If the layers intersect, their zpos determines the overall priority.
814 // If their zpos are identical, then simply fallback to looking at
815 // current_priority. Otherwise, the layer with higher zpos is given
816 // overall priority, since the top layer needs to be offloaded in order
817 // to offload the bottom layer.
818
819 1359 this_zpos = layer_get_core_property(this, LIFTOFF_PROP_ZPOS);
820 1359 other_zpos = layer_get_core_property(other, LIFTOFF_PROP_ZPOS);
821 1359 intersects = layer_intersects(this, other);
822
823
3/4
✓ Branch 0 (13→14) taken 63 times.
✓ Branch 1 (13→24) taken 1296 times.
✓ Branch 2 (14→15) taken 63 times.
✗ Branch 3 (14→24) not taken.
1359 if (this_zpos != NULL && other_zpos != NULL) {
824
2/2
✓ Branch 0 (15→16) taken 55 times.
✓ Branch 1 (15→20) taken 8 times.
63 if (intersects) {
825 55 return this_zpos->value == other_zpos->value ?
826 55 this->current_priority > other->current_priority :
827
1/2
✗ Branch 0 (16→17) not taken.
✓ Branch 1 (16→18) taken 55 times.
55 this_zpos->value > other_zpos->value;
828 } else {
829 8 return this->current_priority == other->current_priority ?
830 8 this_zpos->value > other_zpos->value :
831
1/2
✓ Branch 0 (20→21) taken 8 times.
✗ Branch 1 (20→22) not taken.
8 this->current_priority > other->current_priority;
832 }
833
2/4
✓ Branch 0 (24→25) taken 1296 times.
✗ Branch 1 (24→27) not taken.
✓ Branch 2 (25→26) taken 1296 times.
✗ Branch 3 (25→27) not taken.
1296 } else if (this_zpos == NULL && other_zpos == NULL) {
834 1296 return this->current_priority > other->current_priority;
835 } else {
836 // Either this or other zpos is null
837 return this_zpos != NULL;
838 }
839 }
840
841 static bool
842 1284 update_layers_order(struct liftoff_output *output)
843 {
844 struct liftoff_list *search, *max, *cur, *head;
845 struct liftoff_layer *this_layer, *other_layer;
846 1284 bool order_changed = false;
847
848 1284 head = &output->layers;
849 1284 cur = head;
850
851 // Run a insertion sort to order layers by priority.
852
2/2
✓ Branch 0 (14→3) taken 2600 times.
✓ Branch 1 (14→15) taken 1284 times.
5168 while (cur->next != head) {
853 2600 cur = cur->next;
854
855 2600 max = cur;
856 2600 search = cur;
857
2/2
✓ Branch 0 (8→4) taken 1388 times.
✓ Branch 1 (8→9) taken 2600 times.
3988 while (search->next != head) {
858 1388 search = search->next;
859 1388 this_layer = liftoff_container_of(search, this_layer, link);
860 1388 other_layer = liftoff_container_of(max, other_layer, link);
861
2/2
✓ Branch 0 (5→6) taken 47 times.
✓ Branch 1 (5→7) taken 1341 times.
1388 if (layer_is_higher_priority(this_layer, other_layer)) {
862 47 max = search;
863 }
864 }
865
866
2/2
✓ Branch 0 (9→10) taken 2564 times.
✓ Branch 1 (9→11) taken 36 times.
2600 if (cur != max) {
867 36 liftoff_list_swap(cur, max);
868 // max is now where iterator cur was, relocate to continue
869 36 cur = max;
870 36 order_changed = true;
871 }
872 }
873
874 1284 return order_changed;
875 }
876
877 static int
878 1284 reuse_previous_alloc(struct liftoff_output *output, drmModeAtomicReq *req,
879 uint32_t flags)
880 {
881 struct liftoff_device *device;
882 struct liftoff_layer *layer;
883 int cursor, ret;
884 bool layer_order_changed;
885
886 1284 device = output->device;
887
888 1284 layer_order_changed = update_layers_order(output);
889
890
2/2
✓ Branch 0 (3→4) taken 59 times.
✓ Branch 1 (3→6) taken 1225 times.
1284 if (output->layers_changed) {
891 59 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
892 "a layer has been added or removed");
893 59 return -EINVAL;
894 }
895
896
2/2
✓ Branch 0 (11→7) taken 2444 times.
✓ Branch 1 (11→12) taken 1208 times.
3652 liftoff_list_for_each(layer, &output->layers, link) {
897
2/2
✓ Branch 0 (8→9) taken 17 times.
✓ Branch 1 (8→10) taken 2427 times.
2444 if (layer_needs_realloc(layer, output)) {
898 17 return -EINVAL;
899 }
900 }
901
902
2/2
✓ Branch 0 (12→13) taken 1 times.
✓ Branch 1 (12→15) taken 1207 times.
1208 if (layer_order_changed) {
903 1 liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
904 "layer priority order changed.");
905 1 return -EINVAL;
906 }
907
908 1207 cursor = drmModeAtomicGetCursor(req);
909
910 1207 ret = apply_current(device, output, req);
911
2/2
✓ Branch 0 (17→18) taken 2 times.
✓ Branch 1 (17→19) taken 1205 times.
1207 if (ret != 0) {
912 2 return ret;
913 }
914
915 1205 ret = device_test_commit(device, req, flags);
916
1/2
✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→22) taken 1205 times.
1205 if (ret != 0) {
917 drmModeAtomicSetCursor(req, cursor);
918 }
919 1205 return ret;
920 }
921
922 static void
923 1284 mark_layers_clean(struct liftoff_output *output)
924 {
925 struct liftoff_layer *layer;
926
927 1284 output->layers_changed = false;
928
929
2/2
✓ Branch 0 (5→3) taken 2600 times.
✓ Branch 1 (5→6) taken 1284 times.
3884 liftoff_list_for_each(layer, &output->layers, link) {
930 2600 layer_mark_clean(layer);
931 }
932 1284 }
933
934 static void
935 1284 update_layers_priority(struct liftoff_device *device)
936 {
937 struct liftoff_output *output;
938 struct liftoff_layer *layer;
939 bool period_elapsed;
940
941 1284 device->page_flip_counter++;
942 1284 period_elapsed = device->page_flip_counter >= LIFTOFF_PRIORITY_PERIOD;
943
2/2
✓ Branch 0 (2→3) taken 20 times.
✓ Branch 1 (2→4) taken 1264 times.
1284 if (period_elapsed) {
944 20 device->page_flip_counter = 0;
945 }
946
947
2/2
✓ Branch 0 (10→5) taken 1284 times.
✓ Branch 1 (10→11) taken 1284 times.
2568 liftoff_list_for_each(output, &device->outputs, link) {
948
2/2
✓ Branch 0 (8→6) taken 2600 times.
✓ Branch 1 (8→9) taken 1284 times.
3884 liftoff_list_for_each(layer, &output->layers, link) {
949 2600 layer_update_priority(layer, period_elapsed);
950 }
951 }
952 1284 }
953
954 static void
955 1284 update_layers_fb_info(struct liftoff_output *output)
956 {
957 struct liftoff_layer *layer;
958
959 /* We don't know what the library user did in-between
960 * liftoff_output_apply() calls. They might've removed the FB and
961 * re-created a completely different one which happens to have the same
962 * FB ID. */
963
2/2
✓ Branch 0 (5→3) taken 2600 times.
✓ Branch 1 (5→6) taken 1284 times.
3884 liftoff_list_for_each(layer, &output->layers, link) {
964 2600 layer->fb_info = (drmModeFB2){0};
965
966 2600 layer_cache_fb_info(layer);
967 /* TODO: propagate error? */
968 }
969 1284 }
970
971 static void
972 1205 log_reuse(struct liftoff_output *output)
973 {
974
2/2
✓ Branch 0 (2→3) taken 9 times.
✓ Branch 1 (2→4) taken 1196 times.
1205 if (output->alloc_reused_counter == 0) {
975 9 liftoff_log(LIFTOFF_DEBUG,
976 "Reusing previous plane allocation on output %"PRIu32,
977 output->crtc_id);
978 }
979 1205 output->alloc_reused_counter++;
980 1205 }
981
982 static void
983 79 log_no_reuse(struct liftoff_output *output)
984 {
985 79 liftoff_log(LIFTOFF_DEBUG, "Computing plane allocation on output %"PRIu32,
986 output->crtc_id);
987
988
2/2
✓ Branch 0 (3→4) taken 1 times.
✓ Branch 1 (3→6) taken 78 times.
79 if (output->alloc_reused_counter != 0) {
989 1 liftoff_log(LIFTOFF_DEBUG,
990 "Stopped reusing previous plane allocation on "
991 "output %"PRIu32" (had reused it %d times)",
992 output->crtc_id, output->alloc_reused_counter);
993 1 output->alloc_reused_counter = 0;
994 }
995 79 }
996
997 static size_t
998 79 non_composition_layers_length(struct liftoff_output *output)
999 {
1000 struct liftoff_layer *layer;
1001 size_t n;
1002
1003 79 n = 0;
1004
2/2
✓ Branch 0 (8→3) taken 186 times.
✓ Branch 1 (8→9) taken 79 times.
265 liftoff_list_for_each(layer, &output->layers, link) {
1005
2/2
✓ Branch 0 (4→5) taken 176 times.
✓ Branch 1 (4→7) taken 10 times.
186 if (layer_is_visible(layer) &&
1006
2/2
✓ Branch 0 (5→6) taken 170 times.
✓ Branch 1 (5→7) taken 6 times.
176 output->composition_layer != layer) {
1007 170 n++;
1008 }
1009 }
1010
1011 79 return n;
1012 }
1013
1014 int
1015 1284 liftoff_output_apply(struct liftoff_output *output, drmModeAtomicReq *req,
1016 uint32_t flags,
1017 const struct liftoff_output_apply_options *options)
1018 {
1019 struct liftoff_device *device;
1020 struct liftoff_plane *plane;
1021 struct liftoff_layer *layer;
1022 1284 struct alloc_result result = {0};
1023 1284 struct alloc_step step = {0};
1024 1284 const struct liftoff_output_apply_options default_options = {0};
1025 size_t i, candidate_planes;
1026 int ret;
1027 bool found_layer;
1028
1029
1/2
✓ Branch 0 (2→3) taken 1284 times.
✗ Branch 1 (2→4) not taken.
1284 if (options == NULL) {
1030 1284 options = &default_options;
1031 }
1032
1033 1284 device = output->device;
1034
1035 1284 update_layers_priority(device);
1036 1284 update_layers_fb_info(output);
1037
1038 1284 ret = reuse_previous_alloc(output, req, flags);
1039
2/2
✓ Branch 0 (7→8) taken 1205 times.
✓ Branch 1 (7→11) taken 79 times.
1284 if (ret == 0) {
1040 1205 log_reuse(output);
1041 1205 mark_layers_clean(output);
1042 1205 return 0;
1043 }
1044 79 log_no_reuse(output);
1045
1046 /* Reset layers' candidate planes */
1047
2/2
✓ Branch 0 (15→13) taken 186 times.
✓ Branch 1 (15→16) taken 79 times.
265 liftoff_list_for_each(layer, &output->layers, link) {
1048 186 layer_reset_candidate_planes(layer);
1049 }
1050
1051 79 device->test_commit_counter = 0;
1052 79 output_log_layers(output);
1053
1054 /* Unset all existing plane and layer mappings. */
1055
2/2
✓ Branch 0 (22→18) taken 184 times.
✓ Branch 1 (22→23) taken 79 times.
263 liftoff_list_for_each(plane, &device->planes, link) {
1056
3/4
✓ Branch 0 (18→19) taken 18 times.
✓ Branch 1 (18→21) taken 166 times.
✓ Branch 2 (19→20) taken 18 times.
✗ Branch 3 (19→21) not taken.
184 if (plane->layer != NULL && plane->layer->output == output) {
1057 18 plane->layer->plane = NULL;
1058 18 plane->layer = NULL;
1059 18 plane->free_after_pageflip = output->crtc_id;
1060 }
1061 }
1062
1063 /* Disable all planes we might use. Do it before building mappings to
1064 * make sure not to hit bandwidth limits because too many planes are
1065 * enabled. */
1066 79 candidate_planes = 0;
1067
2/2
✓ Branch 0 (34→24) taken 184 times.
✓ Branch 1 (34→35) taken 79 times.
263 liftoff_list_for_each(plane, &device->planes, link) {
1068 /* Only disable the planes we've just unmapped. Any planes we touch
1069 * can't be used on other outputs (until vblank) so we only want to
1070 * touch the bare minimum. */
1071
1/2
✓ Branch 0 (24→25) taken 184 times.
✗ Branch 1 (24→33) not taken.
184 if (plane->layer == NULL &&
1072
2/2
✓ Branch 0 (25→26) taken 18 times.
✓ Branch 1 (25→33) taken 166 times.
184 plane->free_after_pageflip == output->crtc_id) {
1073 18 candidate_planes++;
1074 18 liftoff_log(LIFTOFF_DEBUG,
1075 "Disabling plane %"PRIu32, plane->id);
1076 18 ret = plane_apply(plane, NULL, req);
1077
1/2
✗ Branch 0 (28→29) not taken.
✓ Branch 1 (28→30) taken 18 times.
18 assert(ret != -EINVAL);
1078
1/2
✗ Branch 0 (31→32) not taken.
✓ Branch 1 (31→33) taken 18 times.
18 if (ret != 0) {
1079 return ret;
1080 }
1081 }
1082 }
1083
1084 79 result.req = req;
1085 79 result.flags = flags;
1086 79 result.planes_len = liftoff_list_length(&device->planes);
1087
1088 79 step.alloc = malloc(result.planes_len * sizeof(step.alloc[0]));
1089 79 result.best = malloc(result.planes_len * sizeof(result.best[0]));
1090
2/4
✓ Branch 0 (36→37) taken 79 times.
✗ Branch 1 (36→38) not taken.
✗ Branch 2 (37→38) not taken.
✓ Branch 3 (37→40) taken 79 times.
79 if (step.alloc == NULL || result.best == NULL) {
1091 liftoff_log_errno(LIFTOFF_ERROR, "malloc");
1092 return -ENOMEM;
1093 }
1094
1095
1/2
✗ Branch 0 (41→42) not taken.
✓ Branch 1 (41→44) taken 79 times.
79 if (clock_gettime(CLOCK_MONOTONIC, &result.started_at) != 0) {
1096 liftoff_log_errno(LIFTOFF_ERROR, "clock_gettime");
1097 return -errno;
1098 }
1099
1100 79 result.timeout_ns = options->timeout_ns;
1101
1/2
✓ Branch 0 (44→45) taken 79 times.
✗ Branch 1 (44→46) not taken.
79 if (result.timeout_ns == 0) {
1102 79 result.timeout_ns = DEFAULT_ALLOC_TIMEOUT_NSEC;
1103 }
1104
1105 /* For each plane, try to find a layer. Don't do it the other
1106 * way around (ie. for each layer, try to find a plane) because
1107 * some drivers want user-space to enable the primary plane
1108 * before any other plane. */
1109
1110 79 result.best_score = -1;
1111 79 memset(result.best, 0, result.planes_len * sizeof(result.best[0]));
1112 79 result.has_composition_layer = output->composition_layer != NULL;
1113 79 result.non_composition_layers_len =
1114 79 non_composition_layers_length(output);
1115 79 step.plane_link = device->planes.next;
1116 79 step.plane_idx = 0;
1117 79 step.score = 0;
1118 79 step.last_layer_zpos = INT_MAX;
1119 79 step.primary_layer_zpos = INT_MIN;
1120 79 step.primary_plane_zpos = INT_MAX;
1121 79 step.composited = false;
1122 79 ret = output_choose_layers(output, &result, &step);
1123
1/2
✗ Branch 0 (48→49) not taken.
✓ Branch 1 (48→50) taken 79 times.
79 if (ret != 0) {
1124 return ret;
1125 }
1126
1127 79 liftoff_log(LIFTOFF_DEBUG,
1128 "Found plane allocation for output %"PRIu32" "
1129 "(score: %d, candidate planes: %zu, tests: %d):",
1130 output->crtc_id, result.best_score, candidate_planes,
1131 device->test_commit_counter);
1132
1133 /* Apply the best allocation */
1134 79 i = 0;
1135 79 found_layer = false;
1136
2/2
✓ Branch 0 (63→52) taken 184 times.
✓ Branch 1 (63→64) taken 79 times.
263 liftoff_list_for_each(plane, &device->planes, link) {
1137 184 layer = result.best[i];
1138 184 i++;
1139
2/2
✓ Branch 0 (52→53) taken 97 times.
✓ Branch 1 (52→54) taken 87 times.
184 if (layer == NULL) {
1140 97 continue;
1141 }
1142
1143 87 liftoff_log(LIFTOFF_DEBUG, " Layer %p -> plane %"PRIu32,
1144 (void *)layer, plane->id);
1145
1146
1/2
✗ Branch 0 (55→56) not taken.
✓ Branch 1 (55→57) taken 87 times.
87 assert(plane->layer == NULL);
1147
1/2
✗ Branch 0 (58→59) not taken.
✓ Branch 1 (58→60) taken 87 times.
87 assert(layer->plane == NULL);
1148 87 plane->layer = layer;
1149 87 layer->plane = plane;
1150
1151 87 found_layer = true;
1152 }
1153
2/2
✓ Branch 0 (64→65) taken 18 times.
✓ Branch 1 (64→66) taken 61 times.
79 if (!found_layer) {
1154 18 liftoff_log(LIFTOFF_DEBUG, " (No layer has a plane)");
1155 }
1156
1157 79 ret = apply_current(device, output, req);
1158
1/2
✗ Branch 0 (67→68) not taken.
✓ Branch 1 (67→69) taken 79 times.
79 if (ret != 0) {
1159 return ret;
1160 }
1161
1162 79 free(step.alloc);
1163 79 free(result.best);
1164
1165 79 mark_layers_clean(output);
1166
1167 79 return 0;
1168 }
1169