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 |