blob: a6a7a316d4d7fad535992dbb3b21f0e81ed7a535 [file] [log] [blame]
Daniel Stonee404b722019-06-22 18:40:31 +01001/*
2 * Copyright © 2008-2011 Kristian Høgsberg
3 * Copyright © 2011 Intel Corporation
4 * Copyright © 2017, 2018 Collabora, Ltd.
5 * Copyright © 2017, 2018 General Electric Company
6 * Copyright (c) 2018 DisplayLink (UK) Ltd.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the
17 * next paragraph) shall be included in all copies or substantial
18 * portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 */
29
30#include "config.h"
31
32#include <xf86drm.h>
33#include <xf86drmMode.h>
34
35#include <libweston/libweston.h>
36#include <libweston/backend-drm.h>
37#include <libweston/pixel-formats.h>
38
39#include "drm-internal.h"
40
Pekka Paalanen90a5ffa2021-02-25 12:03:28 +020041#include "color.h"
Daniel Stonee404b722019-06-22 18:40:31 +010042#include "linux-dmabuf.h"
43#include "presentation-time-server-protocol.h"
44
45enum drm_output_propose_state_mode {
46 DRM_OUTPUT_PROPOSE_STATE_MIXED, /**< mix renderer & planes */
47 DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY, /**< only assign to renderer & cursor */
48 DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY, /**< no renderer use, only planes */
49};
50
51static const char *const drm_output_propose_state_mode_as_string[] = {
52 [DRM_OUTPUT_PROPOSE_STATE_MIXED] = "mixed state",
53 [DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY] = "render-only state",
54 [DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY] = "plane-only state"
55};
56
57static const char *
58drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode)
59{
60 if (mode < 0 || mode >= ARRAY_LENGTH(drm_output_propose_state_mode_as_string))
61 return " unknown compositing mode";
62
63 return drm_output_propose_state_mode_as_string[mode];
64}
65
Marius Vlad2538aac2019-10-14 11:05:30 +030066static void
67drm_output_add_zpos_plane(struct drm_plane *plane, struct wl_list *planes)
68{
69 struct drm_backend *b = plane->backend;
70 struct drm_plane_zpos *h_plane;
71 struct drm_plane_zpos *plane_zpos;
72
73 plane_zpos = zalloc(sizeof(*plane_zpos));
74 if (!plane_zpos)
75 return;
76
77 plane_zpos->plane = plane;
78
79 drm_debug(b, "\t\t\t\t[plane] plane %d added to candidate list\n",
80 plane->plane_id);
81
82 if (wl_list_empty(planes)) {
83 wl_list_insert(planes, &plane_zpos->link);
84 return;
85 }
86
87 h_plane = wl_container_of(planes->next, h_plane, link);
88 if (h_plane->plane->zpos_max >= plane->zpos_max) {
89 wl_list_insert(planes->prev, &plane_zpos->link);
90 } else {
91 struct drm_plane_zpos *p_zpos = NULL;
92
93 if (wl_list_length(planes) == 1) {
94 wl_list_insert(planes->prev, &plane_zpos->link);
95 return;
96 }
97
98 wl_list_for_each(p_zpos, planes, link) {
99 if (p_zpos->plane->zpos_max >
100 plane_zpos->plane->zpos_max)
101 break;
102 }
103
104 wl_list_insert(p_zpos->link.prev, &plane_zpos->link);
105 }
106}
107
108static void
109drm_output_destroy_zpos_plane(struct drm_plane_zpos *plane_zpos)
110{
111 wl_list_remove(&plane_zpos->link);
112 free(plane_zpos);
113}
114
115static bool
116drm_output_check_plane_has_view_assigned(struct drm_plane *plane,
117 struct drm_output_state *output_state)
118{
119 struct drm_plane_state *ps;
120 wl_list_for_each(ps, &output_state->plane_list, link) {
121 if (ps->plane == plane && ps->fb)
122 return true;
123 }
124 return false;
125}
126
Marius Vlad4eeb4022019-10-14 00:27:05 +0300127static bool
128drm_output_plane_has_valid_format(struct drm_plane *plane,
129 struct drm_output_state *state,
130 struct drm_fb *fb)
131{
Marius Vlad26dcce02019-10-24 14:49:33 +0300132 struct drm_backend *b = plane->backend;
Scott Anderson74663092021-02-01 15:46:33 -0300133 struct weston_drm_format *fmt;
Marius Vlad4eeb4022019-10-14 00:27:05 +0300134
135 if (!fb)
136 return false;
137
138 /* Check whether the format is supported */
Scott Anderson74663092021-02-01 15:46:33 -0300139 fmt = weston_drm_format_array_find_format(&plane->formats,
140 fb->format->format);
141 if (fmt) {
Leandro Ribeiroa5560d62021-04-26 08:47:45 -0300142 /* We never try to promote a dmabuf with DRM_FORMAT_MOD_INVALID
143 * to a KMS plane (see drm_fb_get_from_dmabuf() for more details).
144 * So if fb->modifier == DRM_FORMAT_MOD_INVALID, we are sure
145 * that this is for the legacy GBM import path, in which a
146 * wl_drm is being used for scanout. Mesa is the only user we
147 * care in this case (even though recent versions are also using
148 * dmabufs), and it should know better what works or not. */
Marius Vlad4eeb4022019-10-14 00:27:05 +0300149 if (fb->modifier == DRM_FORMAT_MOD_INVALID)
150 return true;
Leandro Ribeiroa5560d62021-04-26 08:47:45 -0300151
Scott Anderson74663092021-02-01 15:46:33 -0300152 if (weston_drm_format_has_modifier(fmt, fb->modifier))
153 return true;
Marius Vlad4eeb4022019-10-14 00:27:05 +0300154 }
155
Marius Vlad26dcce02019-10-24 14:49:33 +0300156 drm_debug(b, "\t\t\t\t[%s] not placing view on %s: "
157 "no free %s planes matching format %s (0x%lx) "
158 "modifier 0x%llx\n",
159 drm_output_get_plane_type_name(plane),
160 drm_output_get_plane_type_name(plane),
161 drm_output_get_plane_type_name(plane),
162 fb->format->drm_format_name,
Bastian Kraused8b86172021-05-06 14:09:27 +0200163 (unsigned long) fb->format->format,
Marius Vlad26dcce02019-10-24 14:49:33 +0300164 (unsigned long long) fb->modifier);
165
Marius Vlad4eeb4022019-10-14 00:27:05 +0300166 return false;
167}
168
Marius Vlad3b13f562019-10-14 00:29:18 +0300169static bool
170drm_output_plane_cursor_has_valid_format(struct weston_view *ev)
171{
172 struct wl_shm_buffer *shmbuf =
173 wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource);
174
Leandro Ribeiro4b5df8b2021-03-23 16:30:09 -0300175 /* When we have cursor planes we've already checked for wl_shm buffer in
176 * the view before calling this function. */
177 assert(shmbuf);
178
179 if (wl_shm_buffer_get_format(shmbuf) == WL_SHM_FORMAT_ARGB8888)
Marius Vlad3b13f562019-10-14 00:29:18 +0300180 return true;
181
182 return false;
183}
184
Daniel Stonee404b722019-06-22 18:40:31 +0100185static struct drm_plane_state *
Marius Vlad677e4592019-10-25 00:07:37 +0300186drm_output_prepare_overlay_view(struct drm_plane *plane,
187 struct drm_output_state *output_state,
Daniel Stonee404b722019-06-22 18:40:31 +0100188 struct weston_view *ev,
Marius Vlad2538aac2019-10-14 11:05:30 +0300189 enum drm_output_propose_state_mode mode,
Marius Vladeef69452019-10-31 12:48:35 +0200190 struct drm_fb *fb, uint64_t zpos)
Daniel Stonee404b722019-06-22 18:40:31 +0100191{
192 struct drm_output *output = output_state->output;
193 struct weston_compositor *ec = output->base.compositor;
194 struct drm_backend *b = to_drm_backend(ec);
Daniel Stonee404b722019-06-22 18:40:31 +0100195 struct drm_plane_state *state = NULL;
Daniel Stonee404b722019-06-22 18:40:31 +0100196 int ret;
Daniel Stonee404b722019-06-22 18:40:31 +0100197
198 assert(!b->sprites_are_broken);
199 assert(b->atomic_modeset);
200
Daniel Stonee404b722019-06-22 18:40:31 +0100201 if (!fb) {
202 drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
203 " couldn't get fb\n", ev);
204 return NULL;
205 }
206
Marius Vlad677e4592019-10-25 00:07:37 +0300207 state = drm_output_state_get_plane(output_state, plane);
Marius Vladeef69452019-10-31 12:48:35 +0200208 /* we can't have a 'pending' framebuffer as never set one before reaching here */
209 assert(!state->fb);
Daniel Stonee404b722019-06-22 18:40:31 +0100210
Marius Vlad677e4592019-10-25 00:07:37 +0300211 state->ev = ev;
212 state->output = output;
Marius Vladeef69452019-10-31 12:48:35 +0200213
Marius Vlad677e4592019-10-25 00:07:37 +0300214 if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
215 drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
216 "unsuitable transform\n", ev);
Daniel Stonee404b722019-06-22 18:40:31 +0100217 drm_plane_state_put_back(state);
218 state = NULL;
Marius Vlad677e4592019-10-25 00:07:37 +0300219 goto out;
Daniel Stonee404b722019-06-22 18:40:31 +0100220 }
221
Marius Vlad677e4592019-10-25 00:07:37 +0300222 /* If the surface buffer has an in-fence fd, but the plane
223 * doesn't support fences, we can't place the buffer on this
224 * plane. */
225 if (ev->surface->acquire_fence_fd >= 0 &&
226 plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
227 drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
228 "no in-fence support\n", ev);
229 drm_plane_state_put_back(state);
230 state = NULL;
231 goto out;
232 }
233
Marius Vladeef69452019-10-31 12:48:35 +0200234 /* We hold one reference for the lifetime of this function; from
235 * calling drm_fb_get_from_view() in drm_output_prepare_plane_view(),
236 * so, we take another reference here to live within the state. */
Marius Vlad677e4592019-10-25 00:07:37 +0300237 state->fb = drm_fb_ref(fb);
238
239 state->in_fence_fd = ev->surface->acquire_fence_fd;
240
241 /* In planes-only mode, we don't have an incremental state to
242 * test against, so we just hope it'll work. */
243 if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
244 drm_debug(b, "\t\t\t[overlay] provisionally placing "
245 "view %p on overlay %lu in planes-only mode\n",
246 ev, (unsigned long) plane->plane_id);
Marius Vlad677e4592019-10-25 00:07:37 +0300247 goto out;
248 }
249
250 ret = drm_pending_state_test(output_state->pending_state);
251 if (ret == 0) {
252 drm_debug(b, "\t\t\t[overlay] provisionally placing "
253 "view %p on overlay %d in mixed mode\n",
254 ev, plane->plane_id);
Marius Vlad677e4592019-10-25 00:07:37 +0300255 goto out;
256 }
257
258 drm_debug(b, "\t\t\t[overlay] not placing view %p on overlay %lu "
259 "in mixed mode: kernel test failed\n",
260 ev, (unsigned long) plane->plane_id);
261
262 drm_plane_state_put_back(state);
263 state = NULL;
264
Daniel Stonee404b722019-06-22 18:40:31 +0100265out:
Daniel Stonee404b722019-06-22 18:40:31 +0100266 return state;
267}
268
Stefan Agnerccf24072019-07-09 22:02:00 +0200269#ifdef BUILD_DRM_GBM
Daniel Stonee404b722019-06-22 18:40:31 +0100270/**
271 * Update the image for the current cursor surface
272 *
273 * @param plane_state DRM cursor plane state
274 * @param ev Source view for cursor
275 */
276static void
277cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev)
278{
279 struct drm_backend *b = plane_state->plane->backend;
280 struct gbm_bo *bo = plane_state->fb->bo;
281 struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
282 uint32_t buf[b->cursor_width * b->cursor_height];
283 int32_t stride;
284 uint8_t *s;
285 int i;
286
287 assert(buffer && buffer->shm_buffer);
288 assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource));
289 assert(buffer->width <= b->cursor_width);
290 assert(buffer->height <= b->cursor_height);
291
292 memset(buf, 0, sizeof buf);
293 stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
294 s = wl_shm_buffer_get_data(buffer->shm_buffer);
295
296 wl_shm_buffer_begin_access(buffer->shm_buffer);
297 for (i = 0; i < buffer->height; i++)
298 memcpy(buf + i * b->cursor_width,
299 s + i * stride,
300 buffer->width * 4);
301 wl_shm_buffer_end_access(buffer->shm_buffer);
302
303 if (gbm_bo_write(bo, buf, sizeof buf) < 0)
304 weston_log("failed update cursor: %s\n", strerror(errno));
305}
306
307static struct drm_plane_state *
308drm_output_prepare_cursor_view(struct drm_output_state *output_state,
Marius Vlad2538aac2019-10-14 11:05:30 +0300309 struct weston_view *ev, uint64_t zpos)
Daniel Stonee404b722019-06-22 18:40:31 +0100310{
311 struct drm_output *output = output_state->output;
312 struct drm_backend *b = to_drm_backend(output->base.compositor);
313 struct drm_plane *plane = output->cursor_plane;
314 struct drm_plane_state *plane_state;
Daniel Stonee404b722019-06-22 18:40:31 +0100315 bool needs_update = false;
Marius Vlad7a465d82021-04-03 19:54:05 +0300316 struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
Marius Vlad36f11a52019-11-01 23:49:21 +0200317 const char *p_name = drm_output_get_plane_type_name(plane);
Daniel Stonee404b722019-06-22 18:40:31 +0100318
319 assert(!b->cursors_are_broken);
320
321 if (!plane)
322 return NULL;
323
324 if (!plane->state_cur->complete)
325 return NULL;
326
327 if (plane->state_cur->output && plane->state_cur->output != output)
328 return NULL;
329
330 /* We use GBM to import SHM buffers. */
331 if (b->gbm == NULL)
332 return NULL;
333
Leandro Ribeiro05cecc82020-08-13 17:34:58 -0300334 plane_state = drm_output_state_get_plane(output_state, plane);
Daniel Stonee404b722019-06-22 18:40:31 +0100335
336 if (plane_state && plane_state->fb)
337 return NULL;
338
339 /* We can't scale with the legacy API, and we don't try to account for
340 * simple cropping/translation in cursor_bo_update. */
341 plane_state->output = output;
Marius Vlad36f11a52019-11-01 23:49:21 +0200342 if (!drm_plane_state_coords_for_view(plane_state, ev, zpos)) {
343 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
344 "unsuitable transform\n", p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100345 goto err;
Marius Vlad36f11a52019-11-01 23:49:21 +0200346 }
Daniel Stonee404b722019-06-22 18:40:31 +0100347
Marius Vlad7a465d82021-04-03 19:54:05 +0300348 if (buffer->width > b->cursor_width ||
349 buffer->height > b->cursor_height) {
350 drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
351 "(surface buffer (%dx%d) larger than permitted"
352 " (%dx%d))\n", p_name, ev, p_name,
353 buffer->width, buffer->height,
354 b->cursor_width, b->cursor_height);
355 goto err;
356 }
357
Daniel Stonee404b722019-06-22 18:40:31 +0100358 if (plane_state->src_x != 0 || plane_state->src_y != 0 ||
359 plane_state->src_w > (unsigned) b->cursor_width << 16 ||
360 plane_state->src_h > (unsigned) b->cursor_height << 16 ||
361 plane_state->src_w != plane_state->dest_w << 16 ||
362 plane_state->src_h != plane_state->dest_h << 16) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200363 drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
364 "(positioning requires cropping or scaling)\n",
365 p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100366 goto err;
367 }
368
369 /* Since we're setting plane state up front, we need to work out
370 * whether or not we need to upload a new cursor. We can't use the
371 * plane damage, since the planes haven't actually been calculated
372 * yet: instead try to figure it out directly. KMS cursor planes are
373 * pretty unique here, in that they lie partway between a Weston plane
374 * (direct scanout) and a renderer. */
375 if (ev != output->cursor_view ||
376 pixman_region32_not_empty(&ev->surface->damage)) {
377 output->current_cursor++;
378 output->current_cursor =
379 output->current_cursor %
380 ARRAY_LENGTH(output->gbm_cursor_fb);
381 needs_update = true;
382 }
383
384 output->cursor_view = ev;
385 plane_state->ev = ev;
386
387 plane_state->fb =
388 drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]);
389
390 if (needs_update) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200391 drm_debug(b, "\t\t\t\t[%s] copying new content to cursor BO\n", p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100392 cursor_bo_update(plane_state, ev);
393 }
394
395 /* The cursor API is somewhat special: in cursor_bo_update(), we upload
396 * a buffer which is always cursor_width x cursor_height, even if the
397 * surface we want to promote is actually smaller than this. Manually
398 * mangle the plane state to deal with this. */
399 plane_state->src_w = b->cursor_width << 16;
400 plane_state->src_h = b->cursor_height << 16;
401 plane_state->dest_w = b->cursor_width;
402 plane_state->dest_h = b->cursor_height;
403
Marius Vlad36f11a52019-11-01 23:49:21 +0200404 drm_debug(b, "\t\t\t\t[%s] provisionally assigned view %p to cursor\n",
405 p_name, ev);
Daniel Stonee404b722019-06-22 18:40:31 +0100406
407 return plane_state;
408
409err:
410 drm_plane_state_put_back(plane_state);
411 return NULL;
412}
Stefan Agnerccf24072019-07-09 22:02:00 +0200413#else
414static struct drm_plane_state *
415drm_output_prepare_cursor_view(struct drm_output_state *output_state,
Marius Vlad2538aac2019-10-14 11:05:30 +0300416 struct weston_view *ev, uint64_t zpos)
Stefan Agnerccf24072019-07-09 22:02:00 +0200417{
418 return NULL;
419}
420#endif
Daniel Stonee404b722019-06-22 18:40:31 +0100421
422static struct drm_plane_state *
423drm_output_prepare_scanout_view(struct drm_output_state *output_state,
424 struct weston_view *ev,
Marius Vlad2538aac2019-10-14 11:05:30 +0300425 enum drm_output_propose_state_mode mode,
Marius Vladeef69452019-10-31 12:48:35 +0200426 struct drm_fb *fb, uint64_t zpos)
Daniel Stonee404b722019-06-22 18:40:31 +0100427{
428 struct drm_output *output = output_state->output;
429 struct drm_backend *b = to_drm_backend(output->base.compositor);
430 struct drm_plane *scanout_plane = output->scanout_plane;
431 struct drm_plane_state *state;
Marius Vlad36f11a52019-11-01 23:49:21 +0200432 const char *p_name = drm_output_get_plane_type_name(scanout_plane);
Daniel Stonee404b722019-06-22 18:40:31 +0100433
434 assert(!b->sprites_are_broken);
435 assert(b->atomic_modeset);
436 assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
437
438 /* Check the view spans exactly the output size, calculated in the
439 * logical co-ordinate space. */
Marius Vlad28bb2da2019-10-19 18:18:38 +0300440 if (!weston_view_matches_output_entirely(ev, &output->base)) {
441 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
442 " view does not match output entirely\n",
443 p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100444 return NULL;
Marius Vlad28bb2da2019-10-19 18:18:38 +0300445 }
Daniel Stonee404b722019-06-22 18:40:31 +0100446
447 /* If the surface buffer has an in-fence fd, but the plane doesn't
448 * support fences, we can't place the buffer on this plane. */
449 if (ev->surface->acquire_fence_fd >= 0 &&
Marius Vlad36f11a52019-11-01 23:49:21 +0200450 scanout_plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
451 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
452 "no in-fence support\n", p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100453 return NULL;
Marius Vlad36f11a52019-11-01 23:49:21 +0200454 }
Daniel Stonee404b722019-06-22 18:40:31 +0100455
Daniel Stonee404b722019-06-22 18:40:31 +0100456 if (!fb) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200457 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
458 " couldn't get fb\n", p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100459 return NULL;
460 }
461
462 state = drm_output_state_get_plane(output_state, scanout_plane);
463
464 /* The only way we can already have a buffer in the scanout plane is
465 * if we are in mixed mode, or if a client buffer has already been
466 * placed into scanout. The former case will never call into here,
467 * and in the latter case, the view must have been marked as occluded,
468 * meaning we should never have ended up here. */
469 assert(!state->fb);
Marius Vladeef69452019-10-31 12:48:35 +0200470
471 /* take another reference here to live within the state */
472 state->fb = drm_fb_ref(fb);
Daniel Stonee404b722019-06-22 18:40:31 +0100473 state->ev = ev;
474 state->output = output;
Marius Vlad36f11a52019-11-01 23:49:21 +0200475 if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
476 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
477 "unsuitable transform\n", p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100478 goto err;
Marius Vlad36f11a52019-11-01 23:49:21 +0200479 }
Daniel Stonee404b722019-06-22 18:40:31 +0100480
481 if (state->dest_x != 0 || state->dest_y != 0 ||
482 state->dest_w != (unsigned) output->base.current_mode->width ||
Marius Vlad36f11a52019-11-01 23:49:21 +0200483 state->dest_h != (unsigned) output->base.current_mode->height) {
484 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
485 " invalid plane state\n", p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100486 goto err;
Marius Vlad36f11a52019-11-01 23:49:21 +0200487 }
Daniel Stonee404b722019-06-22 18:40:31 +0100488
489 state->in_fence_fd = ev->surface->acquire_fence_fd;
490
491 /* In plane-only mode, we don't need to test the state now, as we
492 * will only test it once at the end. */
493 return state;
494
495err:
496 drm_plane_state_put_back(state);
497 return NULL;
498}
499
Marius Vlad26dcce02019-10-24 14:49:33 +0300500static bool
501drm_output_plane_view_has_valid_format(struct drm_plane *plane,
502 struct drm_output_state *state,
503 struct weston_view *ev,
504 struct drm_fb *fb)
505{
506 /* depending on the type of the plane we have different requirements */
507 switch (plane->type) {
508 case WDRM_PLANE_TYPE_CURSOR:
509 return drm_output_plane_cursor_has_valid_format(ev);
510 case WDRM_PLANE_TYPE_OVERLAY:
511 return drm_output_plane_has_valid_format(plane, state, fb);
512 case WDRM_PLANE_TYPE_PRIMARY:
513 return drm_output_plane_has_valid_format(plane, state, fb);
514 default:
515 assert(0);
516 return false;
517 }
518
519 return false;
520}
521
Marius Vlad2538aac2019-10-14 11:05:30 +0300522static struct drm_plane_state *
523drm_output_try_view_on_plane(struct drm_plane *plane,
524 struct drm_output_state *state,
525 struct weston_view *ev,
526 enum drm_output_propose_state_mode mode,
Marius Vladeef69452019-10-31 12:48:35 +0200527 struct drm_fb *fb, uint64_t zpos)
Marius Vlad2538aac2019-10-14 11:05:30 +0300528{
529 struct drm_backend *b = state->pending_state->backend;
530 struct weston_output *wet_output = &state->output->base;
531 bool view_matches_entire_output, scanout_has_view_assigned;
532 struct drm_plane *scanout_plane = state->output->scanout_plane;
Marius Vlad36f11a52019-11-01 23:49:21 +0200533 struct drm_plane_state *ps = NULL;
534 const char *p_name = drm_output_get_plane_type_name(plane);
535 enum {
536 NO_PLANES, /* generic err-handle */
537 NO_PLANES_ACCEPTED,
538 PLACED_ON_PLANE,
539 } availability = NO_PLANES;
Marius Vlad2538aac2019-10-14 11:05:30 +0300540
541 /* sanity checks in case we over/underflow zpos or pass incorrect
542 * values */
543 assert(zpos <= plane->zpos_max ||
544 zpos != DRM_PLANE_ZPOS_INVALID_PLANE);
545
546 switch (plane->type) {
547 case WDRM_PLANE_TYPE_CURSOR:
548 if (b->cursors_are_broken) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200549 availability = NO_PLANES_ACCEPTED;
550 goto out;
Marius Vlad2538aac2019-10-14 11:05:30 +0300551 }
Marius Vlad36f11a52019-11-01 23:49:21 +0200552
553 ps = drm_output_prepare_cursor_view(state, ev, zpos);
554 if (ps)
555 availability = PLACED_ON_PLANE;
556 break;
Marius Vlad2538aac2019-10-14 11:05:30 +0300557 case WDRM_PLANE_TYPE_OVERLAY:
558 /* do not attempt to place it in the overlay if we don't have
559 * anything in the scanout/primary and the view doesn't cover
560 * the entire output */
561 view_matches_entire_output =
562 weston_view_matches_output_entirely(ev, wet_output);
563 scanout_has_view_assigned =
564 drm_output_check_plane_has_view_assigned(scanout_plane,
565 state);
566
567 if (view_matches_entire_output && !scanout_has_view_assigned) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200568 availability = NO_PLANES_ACCEPTED;
569 goto out;
Marius Vlad2538aac2019-10-14 11:05:30 +0300570 }
Marius Vlad36f11a52019-11-01 23:49:21 +0200571
572 ps = drm_output_prepare_overlay_view(plane, state, ev, mode,
573 fb, zpos);
574 if (ps)
575 availability = PLACED_ON_PLANE;
576 break;
Marius Vlad2538aac2019-10-14 11:05:30 +0300577 case WDRM_PLANE_TYPE_PRIMARY:
578 if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200579 availability = NO_PLANES_ACCEPTED;
580 goto out;
Marius Vlad2538aac2019-10-14 11:05:30 +0300581 }
Marius Vlad36f11a52019-11-01 23:49:21 +0200582
583 ps = drm_output_prepare_scanout_view(state, ev, mode,
584 fb, zpos);
585 if (ps)
586 availability = PLACED_ON_PLANE;
587 break;
Marius Vlad2538aac2019-10-14 11:05:30 +0300588 default:
589 assert(0);
590 break;
591 }
Marius Vlad36f11a52019-11-01 23:49:21 +0200592
593out:
594 switch (availability) {
595 case NO_PLANES:
596 /* set initial to this catch-all case, such that
597 * prepare_cursor/overlay/scanout() should have/contain the
598 * reason for failling */
599 break;
600 case NO_PLANES_ACCEPTED:
601 drm_debug(b, "\t\t\t\t[plane] plane %d refusing to "
602 "place view %p in %s\n",
603 plane->plane_id, ev, p_name);
604 break;
605 case PLACED_ON_PLANE:
606 break;
607 }
608
609
610 return ps;
Marius Vlad2538aac2019-10-14 11:05:30 +0300611}
612
Marius Vlad48bc5ef2019-11-15 14:32:08 +0200613static void
Marius Vlad2538aac2019-10-14 11:05:30 +0300614drm_output_check_zpos_plane_states(struct drm_output_state *state)
615{
Marius Vlad2538aac2019-10-14 11:05:30 +0300616 struct drm_plane_state *ps;
Marius Vlad2538aac2019-10-14 11:05:30 +0300617
618 wl_list_for_each(ps, &state->plane_list, link) {
619 struct wl_list *next_node = ps->link.next;
620 bool found_dup = false;
621
Marius Vlad788e80d2019-11-15 00:25:11 +0200622 /* skip any plane that is not enabled */
623 if (!ps->fb)
624 continue;
625
626 assert(ps->zpos != DRM_PLANE_ZPOS_INVALID_PLANE);
627
Marius Vlad2538aac2019-10-14 11:05:30 +0300628 /* find another plane with the same zpos value */
629 if (next_node == &state->plane_list)
630 break;
631
632 while (next_node && next_node != &state->plane_list) {
633 struct drm_plane_state *ps_next;
634
635 ps_next = container_of(next_node,
636 struct drm_plane_state,
637 link);
638
639 if (ps->zpos == ps_next->zpos) {
640 found_dup = true;
641 break;
642 }
643 next_node = next_node->next;
644 }
645
Marius Vlad48bc5ef2019-11-15 14:32:08 +0200646 /* this should never happen so exit hard in case
647 * we screwed up that bad */
648 assert(!found_dup);
Marius Vlad2538aac2019-10-14 11:05:30 +0300649 }
Marius Vlad2538aac2019-10-14 11:05:30 +0300650}
651
652static struct drm_plane_state *
653drm_output_prepare_plane_view(struct drm_output_state *state,
654 struct weston_view *ev,
655 enum drm_output_propose_state_mode mode,
Marius Vladbd002b92019-11-15 13:33:38 +0200656 struct drm_plane_state *scanout_state,
Marius Vlad2538aac2019-10-14 11:05:30 +0300657 uint64_t current_lowest_zpos)
658{
659 struct drm_output *output = state->output;
660 struct drm_backend *b = to_drm_backend(output->base.compositor);
661
662 struct drm_plane_state *ps = NULL;
663 struct drm_plane *plane;
664 struct drm_plane_zpos *p_zpos, *p_zpos_next;
665 struct wl_list zpos_candidate_list;
666
Leandro Ribeiro4b5df8b2021-03-23 16:30:09 -0300667 struct weston_buffer *buffer;
668 struct wl_shm_buffer *shmbuf;
Marius Vlad26dcce02019-10-24 14:49:33 +0300669 struct drm_fb *fb;
670
Marius Vlad2538aac2019-10-14 11:05:30 +0300671 wl_list_init(&zpos_candidate_list);
672
673 /* check view for valid buffer, doesn't make sense to even try */
674 if (!weston_view_has_valid_buffer(ev))
675 return ps;
676
Leandro Ribeiro4b5df8b2021-03-23 16:30:09 -0300677 buffer = ev->surface->buffer_ref.buffer;
678 shmbuf = wl_shm_buffer_get(buffer->resource);
Marius Vlad26dcce02019-10-24 14:49:33 +0300679 fb = drm_fb_get_from_view(state, ev);
680
Marius Vlad2538aac2019-10-14 11:05:30 +0300681 /* assemble a list with possible candidates */
682 wl_list_for_each(plane, &b->plane_list, link) {
683 if (!drm_plane_is_available(plane, output))
684 continue;
685
686 if (drm_output_check_plane_has_view_assigned(plane, state)) {
687 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to"
688 " candidate list: view already assigned "
689 "to a plane\n", plane->plane_id);
690 continue;
691 }
692
693 if (plane->zpos_min >= current_lowest_zpos) {
694 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
Maxime Roussin-Bélanger35e34502020-12-17 17:08:56 -0500695 "candidate list: minimum zpos (%"PRIu64") "
Marius Vlad2538aac2019-10-14 11:05:30 +0300696 "plane's above current lowest zpos "
697 "(%"PRIu64")\n", plane->plane_id,
698 plane->zpos_min, current_lowest_zpos);
699 continue;
700 }
701
Marius Vladbd002b92019-11-15 13:33:38 +0200702 if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
703 assert(scanout_state != NULL);
704 if (scanout_state->zpos >= plane->zpos_max) {
705 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
706 "candidate list: primary's zpos "
707 "value (%"PRIu64") higher than "
708 "plane's maximum value (%"PRIu64")\n",
709 plane->plane_id, scanout_state->zpos,
710 plane->zpos_max);
711 continue;
712 }
713 }
714
Marius Vlad18462e82019-11-15 14:11:49 +0200715 if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY &&
716 (plane->type == WDRM_PLANE_TYPE_OVERLAY ||
717 plane->type == WDRM_PLANE_TYPE_PRIMARY)) {
718 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
719 "candidate list: renderer-only mode\n",
720 plane->plane_id);
721 continue;
722 }
723
Leandro Ribeiro4b5df8b2021-03-23 16:30:09 -0300724 if (plane->type == WDRM_PLANE_TYPE_CURSOR && !shmbuf) {
725 drm_debug(b, "\t\t\t\t[plane] not adding plane %d, type cursor to "
726 "candidate list: cursor planes only support wl_shm "
727 "buffers and the view buffer is of another type\n",
728 plane->plane_id);
729 continue;
730 }
731
Marius Vlad26dcce02019-10-24 14:49:33 +0300732 if (!drm_output_plane_view_has_valid_format(plane, state, ev, fb)) {
733 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
734 "candidate list: invalid pixel format\n",
735 plane->plane_id);
736 continue;
737 }
738
Marius Vlad2538aac2019-10-14 11:05:30 +0300739 drm_output_add_zpos_plane(plane, &zpos_candidate_list);
740 }
741
742 /* go over the potential candidate list and try to find a possible
743 * plane suitable for \c ev; start with the highest zpos value of a
744 * plane to maximize our chances, but do note we pass the zpos value
745 * based on current tracked value by \c current_lowest_zpos_in_use */
746 while (!wl_list_empty(&zpos_candidate_list)) {
747 struct drm_plane_zpos *head_p_zpos =
748 wl_container_of(zpos_candidate_list.next,
749 head_p_zpos, link);
750 struct drm_plane *plane = head_p_zpos->plane;
751 const char *p_name = drm_output_get_plane_type_name(plane);
752 uint64_t zpos;
753
754 if (current_lowest_zpos == DRM_PLANE_ZPOS_INVALID_PLANE)
755 zpos = plane->zpos_max;
756 else
757 zpos = MIN(current_lowest_zpos - 1, plane->zpos_max);
758
759 drm_debug(b, "\t\t\t\t[plane] plane %d picked "
760 "from candidate list, type: %s\n",
761 plane->plane_id, p_name);
762
Marius Vladeef69452019-10-31 12:48:35 +0200763 ps = drm_output_try_view_on_plane(plane, state, ev,
764 mode, fb, zpos);
Marius Vlad2538aac2019-10-14 11:05:30 +0300765 drm_output_destroy_zpos_plane(head_p_zpos);
Marius Vlad2538aac2019-10-14 11:05:30 +0300766 if (ps) {
767 drm_debug(b, "\t\t\t\t[view] view %p has been placed to "
768 "%s plane with computed zpos %"PRIu64"\n",
769 ev, p_name, zpos);
770 break;
771 }
772 }
773
774 wl_list_for_each_safe(p_zpos, p_zpos_next, &zpos_candidate_list, link)
775 drm_output_destroy_zpos_plane(p_zpos);
776
Marius Vlad26dcce02019-10-24 14:49:33 +0300777 drm_fb_unref(fb);
Marius Vlad2538aac2019-10-14 11:05:30 +0300778 return ps;
779}
780
Daniel Stonee404b722019-06-22 18:40:31 +0100781static struct drm_output_state *
782drm_output_propose_state(struct weston_output *output_base,
783 struct drm_pending_state *pending_state,
784 enum drm_output_propose_state_mode mode)
785{
786 struct drm_output *output = to_drm_output(output_base);
787 struct drm_backend *b = to_drm_backend(output->base.compositor);
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +0300788 struct weston_paint_node *pnode;
Daniel Stonee404b722019-06-22 18:40:31 +0100789 struct drm_output_state *state;
790 struct drm_plane_state *scanout_state = NULL;
Marius Vlad80a62e52019-09-11 18:21:59 +0300791
Daniel Stone8ca5d732020-08-25 17:44:35 +0100792 pixman_region32_t renderer_region;
Marius Vlad80a62e52019-09-11 18:21:59 +0300793 pixman_region32_t occluded_region;
794
Daniel Stonee404b722019-06-22 18:40:31 +0100795 bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
796 int ret;
Marius Vlad2538aac2019-10-14 11:05:30 +0300797 uint64_t current_lowest_zpos = DRM_PLANE_ZPOS_INVALID_PLANE;
Daniel Stonee404b722019-06-22 18:40:31 +0100798
799 assert(!output->state_last);
800 state = drm_output_state_duplicate(output->state_cur,
801 pending_state,
802 DRM_OUTPUT_STATE_CLEAR_PLANES);
803
804 /* We implement mixed mode by progressively creating and testing
805 * incremental states, of scanout + overlay + cursor. Since we
806 * walk our views top to bottom, the scanout plane is last, however
807 * we always need it in our scene for the test modeset to be
808 * meaningful. To do this, we steal a reference to the last
809 * renderer framebuffer we have, if we think it's basically
810 * compatible. If we don't have that, then we conservatively fall
811 * back to only using the renderer for this repaint. */
812 if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
813 struct drm_plane *plane = output->scanout_plane;
814 struct drm_fb *scanout_fb = plane->state_cur->fb;
815
816 if (!scanout_fb ||
817 (scanout_fb->type != BUFFER_GBM_SURFACE &&
818 scanout_fb->type != BUFFER_PIXMAN_DUMB)) {
819 drm_debug(b, "\t\t[state] cannot propose mixed mode: "
820 "for output %s (%lu): no previous renderer "
821 "fb\n",
822 output->base.name,
823 (unsigned long) output->base.id);
824 drm_output_state_free(state);
825 return NULL;
826 }
827
828 if (scanout_fb->width != output_base->current_mode->width ||
829 scanout_fb->height != output_base->current_mode->height) {
830 drm_debug(b, "\t\t[state] cannot propose mixed mode "
831 "for output %s (%lu): previous fb has "
832 "different size\n",
833 output->base.name,
834 (unsigned long) output->base.id);
835 drm_output_state_free(state);
836 return NULL;
837 }
838
839 scanout_state = drm_plane_state_duplicate(state,
840 plane->state_cur);
Daniel Stone71e6ba52020-08-25 17:46:27 +0100841 /* assign the primary the lowest zpos value */
Marius Vladbd002b92019-11-15 13:33:38 +0200842 scanout_state->zpos = plane->zpos_min;
Daniel Stonee404b722019-06-22 18:40:31 +0100843 drm_debug(b, "\t\t[state] using renderer FB ID %lu for mixed "
844 "mode for output %s (%lu)\n",
845 (unsigned long) scanout_fb->fb_id, output->base.name,
846 (unsigned long) output->base.id);
Marius Vladbd002b92019-11-15 13:33:38 +0200847 drm_debug(b, "\t\t[state] scanout will use for zpos %"PRIu64"\n",
848 scanout_state->zpos);
Daniel Stonee404b722019-06-22 18:40:31 +0100849 }
850
Marius Vlad80a62e52019-09-11 18:21:59 +0300851 /* - renderer_region contains the total region which which will be
852 * covered by the renderer
Marius Vlad80a62e52019-09-11 18:21:59 +0300853 * - occluded_region contains the total region which which will be
854 * covered by the renderer and hardware planes, where the view's
855 * visible-and-opaque region is added in both cases (the view's
856 * opaque region accumulates there for each view); it is being used
857 * to skip the view, if it is completely occluded; includes the
858 * situation where occluded_region covers entire output's region.
Daniel Stonee404b722019-06-22 18:40:31 +0100859 */
860 pixman_region32_init(&renderer_region);
861 pixman_region32_init(&occluded_region);
862
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +0300863 wl_list_for_each(pnode, &output->base.paint_node_z_order_list,
864 z_order_link) {
865 struct weston_view *ev = pnode->view;
Daniel Stonee404b722019-06-22 18:40:31 +0100866 struct drm_plane_state *ps = NULL;
867 bool force_renderer = false;
868 pixman_region32_t clipped_view;
Daniel Stone8ca5d732020-08-25 17:44:35 +0100869 pixman_region32_t surface_overlap;
Daniel Stonee404b722019-06-22 18:40:31 +0100870 bool totally_occluded = false;
Daniel Stonee404b722019-06-22 18:40:31 +0100871
872 drm_debug(b, "\t\t\t[view] evaluating view %p for "
873 "output %s (%lu)\n",
874 ev, output->base.name,
875 (unsigned long) output->base.id);
876
877 /* If this view doesn't touch our output at all, there's no
878 * reason to do anything with it. */
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +0300879 /* TODO: turn this into assert once z_order_list is pruned. */
Daniel Stonee404b722019-06-22 18:40:31 +0100880 if (!(ev->output_mask & (1u << output->base.id))) {
881 drm_debug(b, "\t\t\t\t[view] ignoring view %p "
882 "(not on our output)\n", ev);
883 continue;
884 }
885
Pekka Paalanen90a5ffa2021-02-25 12:03:28 +0200886 /* Cannot show anything without a color transform. */
887 if (!pnode->surf_xform_valid) {
888 drm_debug(b, "\t\t\t\t[view] ignoring view %p "
889 "(color transform failed)\n", ev);
890 continue;
891 }
892
Daniel Stonee404b722019-06-22 18:40:31 +0100893 /* Ignore views we know to be totally occluded. */
894 pixman_region32_init(&clipped_view);
895 pixman_region32_intersect(&clipped_view,
896 &ev->transform.boundingbox,
897 &output->base.region);
898
899 pixman_region32_init(&surface_overlap);
900 pixman_region32_subtract(&surface_overlap, &clipped_view,
901 &occluded_region);
Marius Vlad80a62e52019-09-11 18:21:59 +0300902 /* if the view is completely occluded then ignore that
903 * view; includes the case where occluded_region covers
904 * the entire output */
Daniel Stonee404b722019-06-22 18:40:31 +0100905 totally_occluded = !pixman_region32_not_empty(&surface_overlap);
906 if (totally_occluded) {
907 drm_debug(b, "\t\t\t\t[view] ignoring view %p "
908 "(occluded on our output)\n", ev);
909 pixman_region32_fini(&surface_overlap);
910 pixman_region32_fini(&clipped_view);
911 continue;
912 }
913
Michael Olbrich3097acc2020-07-14 11:18:54 +0200914 /* We only assign planes to views which are exclusively present
915 * on our output. */
916 if (ev->output_mask != (1u << output->base.id)) {
917 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
918 "(on multiple outputs)\n", ev);
919 force_renderer = true;
920 }
921
922 if (!weston_view_has_valid_buffer(ev)) {
923 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
924 "(no buffer available)\n", ev);
925 force_renderer = true;
926 }
927
Pekka Paalanen90a5ffa2021-02-25 12:03:28 +0200928 if (pnode->surf_xform.transform != NULL ||
929 !pnode->surf_xform.identity_pipeline) {
930 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
931 "(requires color transform)\n", ev);
932 force_renderer = true;
933 }
934
Daniel Stonee404b722019-06-22 18:40:31 +0100935 /* Since we process views from top to bottom, we know that if
936 * the view intersects the calculated renderer region, it must
937 * be part of, or occluded by, it, and cannot go on a plane. */
938 pixman_region32_intersect(&surface_overlap, &renderer_region,
939 &clipped_view);
940 if (pixman_region32_not_empty(&surface_overlap)) {
941 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
942 "(occluded by renderer views)\n", ev);
943 force_renderer = true;
944 }
Marius Vlad2538aac2019-10-14 11:05:30 +0300945 pixman_region32_fini(&surface_overlap);
Daniel Stonee404b722019-06-22 18:40:31 +0100946
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530947 /* In case of enforced mode of content-protection do not
948 * assign planes for a protected surface on an unsecured output.
949 */
950 if (ev->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED &&
951 ev->surface->desired_protection > output_base->current_protection) {
952 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
953 "(enforced protection mode on unsecured output)\n", ev);
954 force_renderer = true;
955 }
956
Daniel Stoneebcf4f32020-08-25 17:46:55 +0100957 /* Now try to place it on a plane if we can. */
Marius Vlad2538aac2019-10-14 11:05:30 +0300958 if (!force_renderer) {
959 drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n",
960 current_lowest_zpos);
961 ps = drm_output_prepare_plane_view(state, ev, mode,
Marius Vladbd002b92019-11-15 13:33:38 +0200962 scanout_state,
Marius Vlad2538aac2019-10-14 11:05:30 +0300963 current_lowest_zpos);
Daniel Stone2cb926c2019-11-11 16:48:54 +0000964 }
Daniel Stone2cb926c2019-11-11 16:48:54 +0000965
966 if (ps) {
Marius Vlad2538aac2019-10-14 11:05:30 +0300967 current_lowest_zpos = ps->zpos;
968 drm_debug(b, "\t\t\t[plane] next zpos to use %"PRIu64"\n",
969 current_lowest_zpos);
Daniel Stoneebcf4f32020-08-25 17:46:55 +0100970 } else if (!ps && !renderer_ok) {
Daniel Stonee404b722019-06-22 18:40:31 +0100971 drm_debug(b, "\t\t[view] failing state generation: "
972 "placing view %p to renderer not allowed\n",
973 ev);
974 pixman_region32_fini(&clipped_view);
975 goto err_region;
Daniel Stoneebcf4f32020-08-25 17:46:55 +0100976 } else if (!ps) {
977 /* clipped_view contains the area that's going to be
978 * visible on screen; add this to the renderer region */
979 pixman_region32_union(&renderer_region,
980 &renderer_region,
981 &clipped_view);
Daniel Stonee404b722019-06-22 18:40:31 +0100982
Daniel Stoneebcf4f32020-08-25 17:46:55 +0100983 drm_debug(b, "\t\t\t\t[view] view %p will be placed "
984 "on the renderer\n", ev);
985 }
Marius Vlad80a62e52019-09-11 18:21:59 +0300986
Daniel Stoneb625cdc2020-08-25 17:37:34 +0100987 /* Opaque areas of our clipped view occlude areas behind it;
988 * however, anything not in the opaque region (which is the
989 * entire clipped area if the whole view is known to be
990 * opaque) does not necessarily occlude what's behind it, as
991 * it could be alpha-blended. */
Marius Vlad80a62e52019-09-11 18:21:59 +0300992 if (!weston_view_is_opaque(ev, &clipped_view))
993 pixman_region32_intersect(&clipped_view,
994 &clipped_view,
995 &ev->transform.opaque);
Marius Vlad80a62e52019-09-11 18:21:59 +0300996 pixman_region32_union(&occluded_region,
997 &occluded_region,
998 &clipped_view);
999
Daniel Stonee404b722019-06-22 18:40:31 +01001000 pixman_region32_fini(&clipped_view);
1001 }
Marius Vlad80a62e52019-09-11 18:21:59 +03001002
Daniel Stonee404b722019-06-22 18:40:31 +01001003 pixman_region32_fini(&renderer_region);
1004 pixman_region32_fini(&occluded_region);
1005
1006 /* In renderer-only mode, we can't test the state as we don't have a
1007 * renderer buffer yet. */
1008 if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY)
1009 return state;
1010
Marius Vlad2538aac2019-10-14 11:05:30 +03001011 /* check if we have invalid zpos values, like duplicate(s) */
Marius Vlad48bc5ef2019-11-15 14:32:08 +02001012 drm_output_check_zpos_plane_states(state);
Marius Vlad2538aac2019-10-14 11:05:30 +03001013
Daniel Stonee404b722019-06-22 18:40:31 +01001014 /* Check to see if this state will actually work. */
1015 ret = drm_pending_state_test(state->pending_state);
1016 if (ret != 0) {
1017 drm_debug(b, "\t\t[view] failing state generation: "
1018 "atomic test not OK\n");
1019 goto err;
1020 }
1021
1022 /* Counterpart to duplicating scanout state at the top of this
1023 * function: if we have taken a renderer framebuffer and placed it in
1024 * the pending state in order to incrementally test overlay planes,
1025 * remove it now. */
1026 if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
1027 assert(scanout_state->fb->type == BUFFER_GBM_SURFACE ||
1028 scanout_state->fb->type == BUFFER_PIXMAN_DUMB);
1029 drm_plane_state_put_back(scanout_state);
1030 }
1031 return state;
1032
1033err_region:
1034 pixman_region32_fini(&renderer_region);
1035 pixman_region32_fini(&occluded_region);
1036err:
1037 drm_output_state_free(state);
1038 return NULL;
1039}
1040
1041void
1042drm_assign_planes(struct weston_output *output_base, void *repaint_data)
1043{
1044 struct drm_backend *b = to_drm_backend(output_base->compositor);
1045 struct drm_pending_state *pending_state = repaint_data;
1046 struct drm_output *output = to_drm_output(output_base);
1047 struct drm_output_state *state = NULL;
1048 struct drm_plane_state *plane_state;
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +03001049 struct weston_paint_node *pnode;
Daniel Stonee404b722019-06-22 18:40:31 +01001050 struct weston_plane *primary = &output_base->compositor->primary_plane;
1051 enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY;
1052
1053 drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n",
1054 output_base->name, (unsigned long) output_base->id);
1055
1056 if (!b->sprites_are_broken && !output->virtual) {
1057 drm_debug(b, "\t[repaint] trying planes-only build state\n");
1058 state = drm_output_propose_state(output_base, pending_state, mode);
1059 if (!state) {
1060 drm_debug(b, "\t[repaint] could not build planes-only "
1061 "state, trying mixed\n");
1062 mode = DRM_OUTPUT_PROPOSE_STATE_MIXED;
1063 state = drm_output_propose_state(output_base,
1064 pending_state,
1065 mode);
1066 }
1067 if (!state) {
1068 drm_debug(b, "\t[repaint] could not build mixed-mode "
1069 "state, trying renderer-only\n");
1070 }
1071 } else {
1072 drm_debug(b, "\t[state] no overlay plane support\n");
1073 }
1074
1075 if (!state) {
1076 mode = DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY;
1077 state = drm_output_propose_state(output_base, pending_state,
1078 mode);
1079 }
1080
1081 assert(state);
1082 drm_debug(b, "\t[repaint] Using %s composition\n",
1083 drm_propose_state_mode_to_string(mode));
1084
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +03001085 wl_list_for_each(pnode, &output->base.paint_node_z_order_list,
1086 z_order_link) {
1087 struct weston_view *ev = pnode->view;
Daniel Stonee404b722019-06-22 18:40:31 +01001088 struct drm_plane *target_plane = NULL;
1089
1090 /* If this view doesn't touch our output at all, there's no
1091 * reason to do anything with it. */
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +03001092 /* TODO: turn this into assert once z_order_list is pruned. */
Daniel Stonee404b722019-06-22 18:40:31 +01001093 if (!(ev->output_mask & (1u << output->base.id)))
1094 continue;
1095
1096 /* Test whether this buffer can ever go into a plane:
1097 * non-shm, or small enough to be a cursor.
1098 *
1099 * Also, keep a reference when using the pixman renderer.
1100 * That makes it possible to do a seamless switch to the GL
1101 * renderer and since the pixman renderer keeps a reference
1102 * to the buffer anyway, there is no side effects.
1103 */
1104 if (b->use_pixman ||
Marius Vlad5f6bee42019-09-11 16:41:04 +03001105 (weston_view_has_valid_buffer(ev) &&
Daniel Stonee404b722019-06-22 18:40:31 +01001106 (!wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) ||
1107 (ev->surface->width <= b->cursor_width &&
1108 ev->surface->height <= b->cursor_height))))
1109 ev->surface->keep_buffer = true;
1110 else
1111 ev->surface->keep_buffer = false;
1112
1113 /* This is a bit unpleasant, but lacking a temporary place to
1114 * hang a plane off the view, we have to do a nested walk.
1115 * Our first-order iteration has to be planes rather than
1116 * views, because otherwise we won't reset views which were
1117 * previously on planes to being on the primary plane. */
1118 wl_list_for_each(plane_state, &state->plane_list, link) {
1119 if (plane_state->ev == ev) {
1120 plane_state->ev = NULL;
1121 target_plane = plane_state->plane;
1122 break;
1123 }
1124 }
1125
1126 if (target_plane) {
1127 drm_debug(b, "\t[repaint] view %p on %s plane %lu\n",
1128 ev, plane_type_enums[target_plane->type].name,
1129 (unsigned long) target_plane->plane_id);
1130 weston_view_move_to_plane(ev, &target_plane->base);
1131 } else {
1132 drm_debug(b, "\t[repaint] view %p using renderer "
1133 "composition\n", ev);
1134 weston_view_move_to_plane(ev, primary);
1135 }
1136
1137 if (!target_plane ||
1138 target_plane->type == WDRM_PLANE_TYPE_CURSOR) {
1139 /* cursor plane & renderer involve a copy */
1140 ev->psf_flags = 0;
1141 } else {
1142 /* All other planes are a direct scanout of a
1143 * single client buffer.
1144 */
1145 ev->psf_flags = WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
1146 }
1147 }
1148
1149 /* We rely on output->cursor_view being both an accurate reflection of
1150 * the cursor plane's state, but also being maintained across repaints
1151 * to avoid unnecessary damage uploads, per the comment in
1152 * drm_output_prepare_cursor_view. In the event that we go from having
1153 * a cursor view to not having a cursor view, we need to clear it. */
1154 if (output->cursor_view) {
1155 plane_state =
1156 drm_output_state_get_existing_plane(state,
1157 output->cursor_plane);
1158 if (!plane_state || !plane_state->fb)
1159 output->cursor_view = NULL;
1160 }
1161}