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