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