blob: 247762f4b43f8f523346fdd9fd33d9dd1b2dd75b [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,
Emmanuel Gil Peyroteff793a2021-07-31 17:25:41 +0200117 struct drm_output_state *output_state)
Marius Vlad2538aac2019-10-14 11:05:30 +0300118{
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
Emmanuel Gil Peyroteff793a2021-07-31 17:25:41 +0200143 * 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
Alexandros Frantzis10937fe2021-06-14 13:09:44 +0300384 drm_output_set_cursor_view(output, ev);
Daniel Stonee404b722019-06-22 18:40:31 +0100385 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
Leandro Ribeiro54293022021-10-12 14:48:36 -0300652static bool
653dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev,
654 uint32_t try_view_on_plane_failure_reasons)
655{
656 struct weston_dmabuf_feedback *dmabuf_feedback = ev->surface->dmabuf_feedback;
657 struct weston_dmabuf_feedback_tranche *scanout_tranche;
658 dev_t scanout_dev = b->drm.devnum;
659 uint32_t scanout_flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT;
660 uint32_t action_needed = ACTION_NEEDED_NONE;
661 struct timespec current_time, delta_time;
662 const time_t MAX_TIME_SECONDS = 2;
663
664 /* Find out what we need to do with the dma-buf feedback */
665 if (try_view_on_plane_failure_reasons & FAILURE_REASONS_FORCE_RENDERER)
666 action_needed |= ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE;
667 if (try_view_on_plane_failure_reasons &
668 (FAILURE_REASONS_ADD_FB_FAILED |
669 FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE |
670 FAILURE_REASONS_DMABUF_MODIFIER_INVALID))
671 action_needed |= ACTION_NEEDED_ADD_SCANOUT_TRANCHE;
672
673 assert(action_needed != (ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE |
674 ACTION_NEEDED_ADD_SCANOUT_TRANCHE));
675
676 /* Look for scanout tranche. If not found, add it but in disabled mode
677 * (we still don't know if we'll have to send it to clients). This
678 * simplifies the code. */
679 scanout_tranche =
680 weston_dmabuf_feedback_find_tranche(dmabuf_feedback, scanout_dev,
681 scanout_flags, SCANOUT_PREF);
682 if (!scanout_tranche) {
683 scanout_tranche =
684 weston_dmabuf_feedback_tranche_create(dmabuf_feedback,
685 b->compositor->dmabuf_feedback_format_table,
686 scanout_dev, scanout_flags,
687 SCANOUT_PREF);
688 scanout_tranche->active = false;
689 }
690
691 /* No actions needed, so disarm timer and return */
692 if (action_needed == ACTION_NEEDED_NONE ||
693 (action_needed == ACTION_NEEDED_ADD_SCANOUT_TRANCHE &&
694 scanout_tranche->active) ||
695 (action_needed == ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE &&
696 !scanout_tranche->active)) {
697 dmabuf_feedback->action_needed = ACTION_NEEDED_NONE;
698 return false;
699 }
700
701 /* We hit this if:
702 *
703 * 1. timer is still off, or
704 * 2. the action needed when it was set to on does not match the most
705 * recent needed action we've detected.
706 *
707 * So we reset the timestamp, set the timer to on it with the most
708 * recent needed action, return and leave the timer running. */
709 if (dmabuf_feedback->action_needed == ACTION_NEEDED_NONE ||
710 dmabuf_feedback->action_needed != action_needed) {
711 clock_gettime(CLOCK_MONOTONIC, &dmabuf_feedback->timer);
712 dmabuf_feedback->action_needed = action_needed;
713 return false;
714 /* Timer is already on and the action needed when it was set to on does
715 * not conflict with the most recent needed action we've detected. If
716 * more than MAX_TIME_SECONDS has passed, we need to resend the dma-buf
717 * feedback. Otherwise, return and leave the timer running. */
718 } else {
719 clock_gettime(CLOCK_MONOTONIC, &current_time);
720 delta_time.tv_sec = current_time.tv_sec -
721 dmabuf_feedback->timer.tv_sec;
722 if (delta_time.tv_sec < MAX_TIME_SECONDS)
723 return false;
724 }
725
726 /* If we got here it means that the timer has triggered, so we have
727 * pending actions with the dma-buf feedback. So we update and resend
728 * them. */
729 if (action_needed == ACTION_NEEDED_ADD_SCANOUT_TRANCHE)
730 scanout_tranche->active = true;
731 else if (action_needed == ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE)
732 scanout_tranche->active = false;
733 else
734 assert(0);
735
736 drm_debug(b, "\t[repaint] Need to update and resend the "
737 "dma-buf feedback for surface of view %p\n", ev);
738 weston_dmabuf_feedback_send_all(dmabuf_feedback,
739 b->compositor->dmabuf_feedback_format_table);
740
741 /* Set the timer to off */
742 dmabuf_feedback->action_needed = ACTION_NEEDED_NONE;
743
744 return true;
745}
746
Marius Vlad2538aac2019-10-14 11:05:30 +0300747static struct drm_plane_state *
748drm_output_prepare_plane_view(struct drm_output_state *state,
749 struct weston_view *ev,
750 enum drm_output_propose_state_mode mode,
Marius Vladbd002b92019-11-15 13:33:38 +0200751 struct drm_plane_state *scanout_state,
Leandro Ribeiro0a7034c2021-09-13 14:52:53 -0300752 uint64_t current_lowest_zpos,
753 uint32_t *try_view_on_plane_failure_reasons)
Marius Vlad2538aac2019-10-14 11:05:30 +0300754{
755 struct drm_output *output = state->output;
756 struct drm_backend *b = to_drm_backend(output->base.compositor);
757
758 struct drm_plane_state *ps = NULL;
759 struct drm_plane *plane;
760 struct drm_plane_zpos *p_zpos, *p_zpos_next;
761 struct wl_list zpos_candidate_list;
762
Leandro Ribeiro4b5df8b2021-03-23 16:30:09 -0300763 struct weston_buffer *buffer;
764 struct wl_shm_buffer *shmbuf;
Marius Vlad26dcce02019-10-24 14:49:33 +0300765 struct drm_fb *fb;
766
Marius Vlad2538aac2019-10-14 11:05:30 +0300767 wl_list_init(&zpos_candidate_list);
768
769 /* check view for valid buffer, doesn't make sense to even try */
770 if (!weston_view_has_valid_buffer(ev))
771 return ps;
772
Leandro Ribeiro4b5df8b2021-03-23 16:30:09 -0300773 buffer = ev->surface->buffer_ref.buffer;
774 shmbuf = wl_shm_buffer_get(buffer->resource);
Leandro Ribeiro0a7034c2021-09-13 14:52:53 -0300775 fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons);
Marius Vlad26dcce02019-10-24 14:49:33 +0300776
Marius Vlad2538aac2019-10-14 11:05:30 +0300777 /* assemble a list with possible candidates */
778 wl_list_for_each(plane, &b->plane_list, link) {
779 if (!drm_plane_is_available(plane, output))
780 continue;
781
782 if (drm_output_check_plane_has_view_assigned(plane, state)) {
783 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to"
784 " candidate list: view already assigned "
785 "to a plane\n", plane->plane_id);
786 continue;
787 }
788
789 if (plane->zpos_min >= current_lowest_zpos) {
790 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
Maxime Roussin-Bélanger35e34502020-12-17 17:08:56 -0500791 "candidate list: minimum zpos (%"PRIu64") "
Marius Vlad2538aac2019-10-14 11:05:30 +0300792 "plane's above current lowest zpos "
793 "(%"PRIu64")\n", plane->plane_id,
794 plane->zpos_min, current_lowest_zpos);
795 continue;
796 }
797
Marius Vladbd002b92019-11-15 13:33:38 +0200798 if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
799 assert(scanout_state != NULL);
800 if (scanout_state->zpos >= plane->zpos_max) {
801 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
802 "candidate list: primary's zpos "
803 "value (%"PRIu64") higher than "
804 "plane's maximum value (%"PRIu64")\n",
805 plane->plane_id, scanout_state->zpos,
806 plane->zpos_max);
807 continue;
808 }
809 }
810
Marius Vlad18462e82019-11-15 14:11:49 +0200811 if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY &&
812 (plane->type == WDRM_PLANE_TYPE_OVERLAY ||
813 plane->type == WDRM_PLANE_TYPE_PRIMARY)) {
814 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
815 "candidate list: renderer-only mode\n",
816 plane->plane_id);
817 continue;
818 }
819
Leandro Ribeiro4b5df8b2021-03-23 16:30:09 -0300820 if (plane->type == WDRM_PLANE_TYPE_CURSOR && !shmbuf) {
821 drm_debug(b, "\t\t\t\t[plane] not adding plane %d, type cursor to "
822 "candidate list: cursor planes only support wl_shm "
823 "buffers and the view buffer is of another type\n",
824 plane->plane_id);
825 continue;
826 }
827
Marius Vlad26dcce02019-10-24 14:49:33 +0300828 if (!drm_output_plane_view_has_valid_format(plane, state, ev, fb)) {
Leandro Ribeiro0a7034c2021-09-13 14:52:53 -0300829 *try_view_on_plane_failure_reasons |=
830 FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
Marius Vlad26dcce02019-10-24 14:49:33 +0300831 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
832 "candidate list: invalid pixel format\n",
833 plane->plane_id);
834 continue;
835 }
836
Marius Vlad2538aac2019-10-14 11:05:30 +0300837 drm_output_add_zpos_plane(plane, &zpos_candidate_list);
838 }
839
840 /* go over the potential candidate list and try to find a possible
841 * plane suitable for \c ev; start with the highest zpos value of a
842 * plane to maximize our chances, but do note we pass the zpos value
843 * based on current tracked value by \c current_lowest_zpos_in_use */
844 while (!wl_list_empty(&zpos_candidate_list)) {
845 struct drm_plane_zpos *head_p_zpos =
846 wl_container_of(zpos_candidate_list.next,
847 head_p_zpos, link);
848 struct drm_plane *plane = head_p_zpos->plane;
849 const char *p_name = drm_output_get_plane_type_name(plane);
850 uint64_t zpos;
851
852 if (current_lowest_zpos == DRM_PLANE_ZPOS_INVALID_PLANE)
853 zpos = plane->zpos_max;
854 else
855 zpos = MIN(current_lowest_zpos - 1, plane->zpos_max);
856
857 drm_debug(b, "\t\t\t\t[plane] plane %d picked "
858 "from candidate list, type: %s\n",
859 plane->plane_id, p_name);
860
Marius Vladeef69452019-10-31 12:48:35 +0200861 ps = drm_output_try_view_on_plane(plane, state, ev,
862 mode, fb, zpos);
Marius Vlad2538aac2019-10-14 11:05:30 +0300863 drm_output_destroy_zpos_plane(head_p_zpos);
Marius Vlad2538aac2019-10-14 11:05:30 +0300864 if (ps) {
865 drm_debug(b, "\t\t\t\t[view] view %p has been placed to "
866 "%s plane with computed zpos %"PRIu64"\n",
867 ev, p_name, zpos);
868 break;
869 }
870 }
871
872 wl_list_for_each_safe(p_zpos, p_zpos_next, &zpos_candidate_list, link)
873 drm_output_destroy_zpos_plane(p_zpos);
874
Marius Vlad26dcce02019-10-24 14:49:33 +0300875 drm_fb_unref(fb);
Marius Vlad2538aac2019-10-14 11:05:30 +0300876 return ps;
877}
878
Daniel Stonee404b722019-06-22 18:40:31 +0100879static struct drm_output_state *
880drm_output_propose_state(struct weston_output *output_base,
881 struct drm_pending_state *pending_state,
882 enum drm_output_propose_state_mode mode)
883{
884 struct drm_output *output = to_drm_output(output_base);
885 struct drm_backend *b = to_drm_backend(output->base.compositor);
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +0300886 struct weston_paint_node *pnode;
Daniel Stonee404b722019-06-22 18:40:31 +0100887 struct drm_output_state *state;
888 struct drm_plane_state *scanout_state = NULL;
Marius Vlad80a62e52019-09-11 18:21:59 +0300889
Daniel Stone8ca5d732020-08-25 17:44:35 +0100890 pixman_region32_t renderer_region;
Marius Vlad80a62e52019-09-11 18:21:59 +0300891 pixman_region32_t occluded_region;
892
Daniel Stonee404b722019-06-22 18:40:31 +0100893 bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
894 int ret;
Marius Vlad2538aac2019-10-14 11:05:30 +0300895 uint64_t current_lowest_zpos = DRM_PLANE_ZPOS_INVALID_PLANE;
Daniel Stonee404b722019-06-22 18:40:31 +0100896
897 assert(!output->state_last);
898 state = drm_output_state_duplicate(output->state_cur,
899 pending_state,
900 DRM_OUTPUT_STATE_CLEAR_PLANES);
901
902 /* We implement mixed mode by progressively creating and testing
903 * incremental states, of scanout + overlay + cursor. Since we
904 * walk our views top to bottom, the scanout plane is last, however
905 * we always need it in our scene for the test modeset to be
906 * meaningful. To do this, we steal a reference to the last
907 * renderer framebuffer we have, if we think it's basically
908 * compatible. If we don't have that, then we conservatively fall
909 * back to only using the renderer for this repaint. */
910 if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
911 struct drm_plane *plane = output->scanout_plane;
912 struct drm_fb *scanout_fb = plane->state_cur->fb;
913
914 if (!scanout_fb ||
915 (scanout_fb->type != BUFFER_GBM_SURFACE &&
916 scanout_fb->type != BUFFER_PIXMAN_DUMB)) {
917 drm_debug(b, "\t\t[state] cannot propose mixed mode: "
918 "for output %s (%lu): no previous renderer "
919 "fb\n",
920 output->base.name,
921 (unsigned long) output->base.id);
922 drm_output_state_free(state);
923 return NULL;
924 }
925
926 if (scanout_fb->width != output_base->current_mode->width ||
927 scanout_fb->height != output_base->current_mode->height) {
928 drm_debug(b, "\t\t[state] cannot propose mixed mode "
929 "for output %s (%lu): previous fb has "
930 "different size\n",
931 output->base.name,
932 (unsigned long) output->base.id);
933 drm_output_state_free(state);
934 return NULL;
935 }
936
937 scanout_state = drm_plane_state_duplicate(state,
938 plane->state_cur);
Daniel Stone71e6ba52020-08-25 17:46:27 +0100939 /* assign the primary the lowest zpos value */
Marius Vladbd002b92019-11-15 13:33:38 +0200940 scanout_state->zpos = plane->zpos_min;
Daniel Stonee404b722019-06-22 18:40:31 +0100941 drm_debug(b, "\t\t[state] using renderer FB ID %lu for mixed "
942 "mode for output %s (%lu)\n",
943 (unsigned long) scanout_fb->fb_id, output->base.name,
944 (unsigned long) output->base.id);
Marius Vladbd002b92019-11-15 13:33:38 +0200945 drm_debug(b, "\t\t[state] scanout will use for zpos %"PRIu64"\n",
946 scanout_state->zpos);
Daniel Stonee404b722019-06-22 18:40:31 +0100947 }
948
Marius Vlad80a62e52019-09-11 18:21:59 +0300949 /* - renderer_region contains the total region which which will be
950 * covered by the renderer
Marius Vlad80a62e52019-09-11 18:21:59 +0300951 * - occluded_region contains the total region which which will be
952 * covered by the renderer and hardware planes, where the view's
953 * visible-and-opaque region is added in both cases (the view's
954 * opaque region accumulates there for each view); it is being used
955 * to skip the view, if it is completely occluded; includes the
956 * situation where occluded_region covers entire output's region.
Daniel Stonee404b722019-06-22 18:40:31 +0100957 */
958 pixman_region32_init(&renderer_region);
959 pixman_region32_init(&occluded_region);
960
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +0300961 wl_list_for_each(pnode, &output->base.paint_node_z_order_list,
962 z_order_link) {
963 struct weston_view *ev = pnode->view;
Daniel Stonee404b722019-06-22 18:40:31 +0100964 struct drm_plane_state *ps = NULL;
965 bool force_renderer = false;
966 pixman_region32_t clipped_view;
Daniel Stone8ca5d732020-08-25 17:44:35 +0100967 pixman_region32_t surface_overlap;
Daniel Stonee404b722019-06-22 18:40:31 +0100968 bool totally_occluded = false;
Daniel Stonee404b722019-06-22 18:40:31 +0100969
970 drm_debug(b, "\t\t\t[view] evaluating view %p for "
971 "output %s (%lu)\n",
972 ev, output->base.name,
973 (unsigned long) output->base.id);
974
975 /* If this view doesn't touch our output at all, there's no
976 * reason to do anything with it. */
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +0300977 /* TODO: turn this into assert once z_order_list is pruned. */
Daniel Stonee404b722019-06-22 18:40:31 +0100978 if (!(ev->output_mask & (1u << output->base.id))) {
979 drm_debug(b, "\t\t\t\t[view] ignoring view %p "
980 "(not on our output)\n", ev);
981 continue;
982 }
983
Pekka Paalanen90a5ffa2021-02-25 12:03:28 +0200984 /* Cannot show anything without a color transform. */
985 if (!pnode->surf_xform_valid) {
986 drm_debug(b, "\t\t\t\t[view] ignoring view %p "
987 "(color transform failed)\n", ev);
988 continue;
989 }
990
Daniel Stonee404b722019-06-22 18:40:31 +0100991 /* Ignore views we know to be totally occluded. */
992 pixman_region32_init(&clipped_view);
993 pixman_region32_intersect(&clipped_view,
994 &ev->transform.boundingbox,
995 &output->base.region);
996
997 pixman_region32_init(&surface_overlap);
998 pixman_region32_subtract(&surface_overlap, &clipped_view,
999 &occluded_region);
Marius Vlad80a62e52019-09-11 18:21:59 +03001000 /* if the view is completely occluded then ignore that
1001 * view; includes the case where occluded_region covers
1002 * the entire output */
Daniel Stonee404b722019-06-22 18:40:31 +01001003 totally_occluded = !pixman_region32_not_empty(&surface_overlap);
1004 if (totally_occluded) {
1005 drm_debug(b, "\t\t\t\t[view] ignoring view %p "
1006 "(occluded on our output)\n", ev);
1007 pixman_region32_fini(&surface_overlap);
1008 pixman_region32_fini(&clipped_view);
1009 continue;
1010 }
1011
Michael Olbrich3097acc2020-07-14 11:18:54 +02001012 /* We only assign planes to views which are exclusively present
1013 * on our output. */
1014 if (ev->output_mask != (1u << output->base.id)) {
1015 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
1016 "(on multiple outputs)\n", ev);
1017 force_renderer = true;
1018 }
1019
1020 if (!weston_view_has_valid_buffer(ev)) {
1021 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
1022 "(no buffer available)\n", ev);
1023 force_renderer = true;
1024 }
1025
Pekka Paalanen90a5ffa2021-02-25 12:03:28 +02001026 if (pnode->surf_xform.transform != NULL ||
1027 !pnode->surf_xform.identity_pipeline) {
1028 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
1029 "(requires color transform)\n", ev);
1030 force_renderer = true;
1031 }
1032
Daniel Stonee404b722019-06-22 18:40:31 +01001033 /* Since we process views from top to bottom, we know that if
1034 * the view intersects the calculated renderer region, it must
1035 * be part of, or occluded by, it, and cannot go on a plane. */
1036 pixman_region32_intersect(&surface_overlap, &renderer_region,
1037 &clipped_view);
1038 if (pixman_region32_not_empty(&surface_overlap)) {
1039 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
1040 "(occluded by renderer views)\n", ev);
1041 force_renderer = true;
1042 }
Marius Vlad2538aac2019-10-14 11:05:30 +03001043 pixman_region32_fini(&surface_overlap);
Daniel Stonee404b722019-06-22 18:40:31 +01001044
Ankit Nautiyala344fe32019-05-14 18:36:08 +05301045 /* In case of enforced mode of content-protection do not
1046 * assign planes for a protected surface on an unsecured output.
1047 */
1048 if (ev->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED &&
1049 ev->surface->desired_protection > output_base->current_protection) {
1050 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
1051 "(enforced protection mode on unsecured output)\n", ev);
1052 force_renderer = true;
1053 }
1054
Daniel Stoneebcf4f32020-08-25 17:46:55 +01001055 /* Now try to place it on a plane if we can. */
Marius Vlad2538aac2019-10-14 11:05:30 +03001056 if (!force_renderer) {
1057 drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n",
1058 current_lowest_zpos);
1059 ps = drm_output_prepare_plane_view(state, ev, mode,
Marius Vladbd002b92019-11-15 13:33:38 +02001060 scanout_state,
Leandro Ribeiro0a7034c2021-09-13 14:52:53 -03001061 current_lowest_zpos,
1062 &pnode->try_view_on_plane_failure_reasons);
1063 /* If we were able to place the view in a plane, set
1064 * failure reasons to none. */
1065 if (ps)
1066 pnode->try_view_on_plane_failure_reasons =
1067 FAILURE_REASONS_NONE;
1068 } else {
1069 /* We are forced to place the view in the renderer, set
1070 * the failure reason accordingly. */
1071 pnode->try_view_on_plane_failure_reasons =
1072 FAILURE_REASONS_FORCE_RENDERER;
Daniel Stone2cb926c2019-11-11 16:48:54 +00001073 }
Daniel Stone2cb926c2019-11-11 16:48:54 +00001074
1075 if (ps) {
Marius Vlad2538aac2019-10-14 11:05:30 +03001076 current_lowest_zpos = ps->zpos;
1077 drm_debug(b, "\t\t\t[plane] next zpos to use %"PRIu64"\n",
1078 current_lowest_zpos);
Daniel Stoneebcf4f32020-08-25 17:46:55 +01001079 } else if (!ps && !renderer_ok) {
Daniel Stonee404b722019-06-22 18:40:31 +01001080 drm_debug(b, "\t\t[view] failing state generation: "
1081 "placing view %p to renderer not allowed\n",
1082 ev);
1083 pixman_region32_fini(&clipped_view);
1084 goto err_region;
Daniel Stoneebcf4f32020-08-25 17:46:55 +01001085 } else if (!ps) {
1086 /* clipped_view contains the area that's going to be
1087 * visible on screen; add this to the renderer region */
1088 pixman_region32_union(&renderer_region,
1089 &renderer_region,
1090 &clipped_view);
Daniel Stonee404b722019-06-22 18:40:31 +01001091
Daniel Stoneebcf4f32020-08-25 17:46:55 +01001092 drm_debug(b, "\t\t\t\t[view] view %p will be placed "
1093 "on the renderer\n", ev);
1094 }
Marius Vlad80a62e52019-09-11 18:21:59 +03001095
Daniel Stoneb625cdc2020-08-25 17:37:34 +01001096 /* Opaque areas of our clipped view occlude areas behind it;
1097 * however, anything not in the opaque region (which is the
1098 * entire clipped area if the whole view is known to be
1099 * opaque) does not necessarily occlude what's behind it, as
1100 * it could be alpha-blended. */
Marius Vlad80a62e52019-09-11 18:21:59 +03001101 if (!weston_view_is_opaque(ev, &clipped_view))
1102 pixman_region32_intersect(&clipped_view,
1103 &clipped_view,
1104 &ev->transform.opaque);
Marius Vlad80a62e52019-09-11 18:21:59 +03001105 pixman_region32_union(&occluded_region,
1106 &occluded_region,
1107 &clipped_view);
1108
Daniel Stonee404b722019-06-22 18:40:31 +01001109 pixman_region32_fini(&clipped_view);
1110 }
Marius Vlad80a62e52019-09-11 18:21:59 +03001111
Daniel Stonee404b722019-06-22 18:40:31 +01001112 pixman_region32_fini(&renderer_region);
1113 pixman_region32_fini(&occluded_region);
1114
1115 /* In renderer-only mode, we can't test the state as we don't have a
1116 * renderer buffer yet. */
1117 if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY)
1118 return state;
1119
Marius Vlad2538aac2019-10-14 11:05:30 +03001120 /* check if we have invalid zpos values, like duplicate(s) */
Marius Vlad48bc5ef2019-11-15 14:32:08 +02001121 drm_output_check_zpos_plane_states(state);
Marius Vlad2538aac2019-10-14 11:05:30 +03001122
Daniel Stonee404b722019-06-22 18:40:31 +01001123 /* Check to see if this state will actually work. */
1124 ret = drm_pending_state_test(state->pending_state);
1125 if (ret != 0) {
1126 drm_debug(b, "\t\t[view] failing state generation: "
1127 "atomic test not OK\n");
1128 goto err;
1129 }
1130
1131 /* Counterpart to duplicating scanout state at the top of this
1132 * function: if we have taken a renderer framebuffer and placed it in
1133 * the pending state in order to incrementally test overlay planes,
1134 * remove it now. */
1135 if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
1136 assert(scanout_state->fb->type == BUFFER_GBM_SURFACE ||
1137 scanout_state->fb->type == BUFFER_PIXMAN_DUMB);
1138 drm_plane_state_put_back(scanout_state);
1139 }
1140 return state;
1141
1142err_region:
1143 pixman_region32_fini(&renderer_region);
1144 pixman_region32_fini(&occluded_region);
1145err:
1146 drm_output_state_free(state);
1147 return NULL;
1148}
1149
1150void
1151drm_assign_planes(struct weston_output *output_base, void *repaint_data)
1152{
1153 struct drm_backend *b = to_drm_backend(output_base->compositor);
1154 struct drm_pending_state *pending_state = repaint_data;
1155 struct drm_output *output = to_drm_output(output_base);
1156 struct drm_output_state *state = NULL;
1157 struct drm_plane_state *plane_state;
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +03001158 struct weston_paint_node *pnode;
Daniel Stonee404b722019-06-22 18:40:31 +01001159 struct weston_plane *primary = &output_base->compositor->primary_plane;
1160 enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY;
1161
1162 drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n",
1163 output_base->name, (unsigned long) output_base->id);
1164
1165 if (!b->sprites_are_broken && !output->virtual) {
1166 drm_debug(b, "\t[repaint] trying planes-only build state\n");
1167 state = drm_output_propose_state(output_base, pending_state, mode);
1168 if (!state) {
1169 drm_debug(b, "\t[repaint] could not build planes-only "
1170 "state, trying mixed\n");
1171 mode = DRM_OUTPUT_PROPOSE_STATE_MIXED;
1172 state = drm_output_propose_state(output_base,
1173 pending_state,
1174 mode);
1175 }
1176 if (!state) {
1177 drm_debug(b, "\t[repaint] could not build mixed-mode "
1178 "state, trying renderer-only\n");
1179 }
1180 } else {
1181 drm_debug(b, "\t[state] no overlay plane support\n");
1182 }
1183
1184 if (!state) {
1185 mode = DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY;
1186 state = drm_output_propose_state(output_base, pending_state,
1187 mode);
1188 }
1189
1190 assert(state);
1191 drm_debug(b, "\t[repaint] Using %s composition\n",
1192 drm_propose_state_mode_to_string(mode));
1193
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +03001194 wl_list_for_each(pnode, &output->base.paint_node_z_order_list,
1195 z_order_link) {
1196 struct weston_view *ev = pnode->view;
Daniel Stonee404b722019-06-22 18:40:31 +01001197 struct drm_plane *target_plane = NULL;
1198
1199 /* If this view doesn't touch our output at all, there's no
1200 * reason to do anything with it. */
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +03001201 /* TODO: turn this into assert once z_order_list is pruned. */
Daniel Stonee404b722019-06-22 18:40:31 +01001202 if (!(ev->output_mask & (1u << output->base.id)))
1203 continue;
1204
Leandro Ribeiro54293022021-10-12 14:48:36 -03001205 /* Update dmabuf-feedback if needed */
1206 if (ev->surface->dmabuf_feedback)
1207 dmabuf_feedback_maybe_update(b, ev,
1208 pnode->try_view_on_plane_failure_reasons);
1209 pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE;
1210
Daniel Stonee404b722019-06-22 18:40:31 +01001211 /* Test whether this buffer can ever go into a plane:
1212 * non-shm, or small enough to be a cursor.
1213 *
1214 * Also, keep a reference when using the pixman renderer.
1215 * That makes it possible to do a seamless switch to the GL
1216 * renderer and since the pixman renderer keeps a reference
1217 * to the buffer anyway, there is no side effects.
1218 */
1219 if (b->use_pixman ||
Marius Vlad5f6bee42019-09-11 16:41:04 +03001220 (weston_view_has_valid_buffer(ev) &&
Daniel Stonee404b722019-06-22 18:40:31 +01001221 (!wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) ||
1222 (ev->surface->width <= b->cursor_width &&
1223 ev->surface->height <= b->cursor_height))))
1224 ev->surface->keep_buffer = true;
1225 else
1226 ev->surface->keep_buffer = false;
1227
1228 /* This is a bit unpleasant, but lacking a temporary place to
1229 * hang a plane off the view, we have to do a nested walk.
1230 * Our first-order iteration has to be planes rather than
1231 * views, because otherwise we won't reset views which were
1232 * previously on planes to being on the primary plane. */
1233 wl_list_for_each(plane_state, &state->plane_list, link) {
1234 if (plane_state->ev == ev) {
1235 plane_state->ev = NULL;
1236 target_plane = plane_state->plane;
1237 break;
1238 }
1239 }
1240
1241 if (target_plane) {
1242 drm_debug(b, "\t[repaint] view %p on %s plane %lu\n",
1243 ev, plane_type_enums[target_plane->type].name,
1244 (unsigned long) target_plane->plane_id);
1245 weston_view_move_to_plane(ev, &target_plane->base);
1246 } else {
1247 drm_debug(b, "\t[repaint] view %p using renderer "
1248 "composition\n", ev);
1249 weston_view_move_to_plane(ev, primary);
1250 }
1251
1252 if (!target_plane ||
1253 target_plane->type == WDRM_PLANE_TYPE_CURSOR) {
1254 /* cursor plane & renderer involve a copy */
1255 ev->psf_flags = 0;
1256 } else {
1257 /* All other planes are a direct scanout of a
1258 * single client buffer.
1259 */
1260 ev->psf_flags = WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
1261 }
1262 }
1263
1264 /* We rely on output->cursor_view being both an accurate reflection of
1265 * the cursor plane's state, but also being maintained across repaints
1266 * to avoid unnecessary damage uploads, per the comment in
1267 * drm_output_prepare_cursor_view. In the event that we go from having
1268 * a cursor view to not having a cursor view, we need to clear it. */
1269 if (output->cursor_view) {
1270 plane_state =
1271 drm_output_state_get_existing_plane(state,
1272 output->cursor_plane);
1273 if (!plane_state || !plane_state->fb)
Alexandros Frantzis10937fe2021-06-14 13:09:44 +03001274 drm_output_set_cursor_view(output, NULL);
1275 }
1276}
1277
1278static void
1279drm_output_handle_cursor_view_destroy(struct wl_listener *listener, void *data)
1280{
1281 struct drm_output *output =
1282 container_of(listener, struct drm_output,
1283 cursor_view_destroy_listener);
1284
1285 drm_output_set_cursor_view(output, NULL);
1286}
1287
1288/** Set the current cursor view used for an output.
1289 *
1290 * Ensure the stored value will be properly cleared if the view is destroyed.
1291 * The stored cursor view helps avoid unnecessary uploads of cursor data to
1292 * cursor plane buffer objects (see drm_output_prepare_cursor_view).
1293 */
1294void
1295drm_output_set_cursor_view(struct drm_output *output, struct weston_view *ev)
1296{
1297 if (output->cursor_view == ev)
1298 return;
1299
1300 if (output->cursor_view)
1301 wl_list_remove(&output->cursor_view_destroy_listener.link);
1302
1303 output->cursor_view = ev;
1304
1305 if (ev) {
1306 output->cursor_view_destroy_listener.notify =
1307 drm_output_handle_cursor_view_destroy;
1308 wl_signal_add(&ev->destroy_signal,
1309 &output->cursor_view_destroy_listener);
Daniel Stonee404b722019-06-22 18:40:31 +01001310 }
1311}