GCC Code Coverage Report


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