blob: 2fc8da306640251056d2b02a1ae06ddff428148f [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"
leng.fang32af9fc2024-06-13 11:22:15 +080044#include "aml-weston/aml-backend.h"
Daniel Stonee404b722019-06-22 18:40:31 +010045
46enum drm_output_propose_state_mode {
47 DRM_OUTPUT_PROPOSE_STATE_MIXED, /**< mix renderer & planes */
48 DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY, /**< only assign to renderer & cursor */
49 DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY, /**< no renderer use, only planes */
50};
51
52static const char *const drm_output_propose_state_mode_as_string[] = {
53 [DRM_OUTPUT_PROPOSE_STATE_MIXED] = "mixed state",
54 [DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY] = "render-only state",
55 [DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY] = "plane-only state"
56};
57
58static const char *
59drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode)
60{
61 if (mode < 0 || mode >= ARRAY_LENGTH(drm_output_propose_state_mode_as_string))
62 return " unknown compositing mode";
63
64 return drm_output_propose_state_mode_as_string[mode];
65}
66
Marius Vlad2538aac2019-10-14 11:05:30 +030067static void
68drm_output_add_zpos_plane(struct drm_plane *plane, struct wl_list *planes)
69{
70 struct drm_backend *b = plane->backend;
71 struct drm_plane_zpos *h_plane;
72 struct drm_plane_zpos *plane_zpos;
73
74 plane_zpos = zalloc(sizeof(*plane_zpos));
75 if (!plane_zpos)
76 return;
77
78 plane_zpos->plane = plane;
79
80 drm_debug(b, "\t\t\t\t[plane] plane %d added to candidate list\n",
81 plane->plane_id);
82
83 if (wl_list_empty(planes)) {
84 wl_list_insert(planes, &plane_zpos->link);
85 return;
86 }
87
88 h_plane = wl_container_of(planes->next, h_plane, link);
89 if (h_plane->plane->zpos_max >= plane->zpos_max) {
90 wl_list_insert(planes->prev, &plane_zpos->link);
91 } else {
92 struct drm_plane_zpos *p_zpos = NULL;
93
94 if (wl_list_length(planes) == 1) {
95 wl_list_insert(planes->prev, &plane_zpos->link);
96 return;
97 }
98
99 wl_list_for_each(p_zpos, planes, link) {
100 if (p_zpos->plane->zpos_max >
101 plane_zpos->plane->zpos_max)
102 break;
103 }
104
105 wl_list_insert(p_zpos->link.prev, &plane_zpos->link);
106 }
107}
108
109static void
110drm_output_destroy_zpos_plane(struct drm_plane_zpos *plane_zpos)
111{
112 wl_list_remove(&plane_zpos->link);
113 free(plane_zpos);
114}
115
116static bool
117drm_output_check_plane_has_view_assigned(struct drm_plane *plane,
Emmanuel Gil Peyroteff793a2021-07-31 17:25:41 +0200118 struct drm_output_state *output_state)
Marius Vlad2538aac2019-10-14 11:05:30 +0300119{
120 struct drm_plane_state *ps;
121 wl_list_for_each(ps, &output_state->plane_list, link) {
122 if (ps->plane == plane && ps->fb)
123 return true;
124 }
125 return false;
126}
127
Daniel Stonee404b722019-06-22 18:40:31 +0100128static struct drm_plane_state *
Marius Vlad677e4592019-10-25 00:07:37 +0300129drm_output_prepare_overlay_view(struct drm_plane *plane,
130 struct drm_output_state *output_state,
Daniel Stonee404b722019-06-22 18:40:31 +0100131 struct weston_view *ev,
Marius Vlad2538aac2019-10-14 11:05:30 +0300132 enum drm_output_propose_state_mode mode,
Marius Vladeef69452019-10-31 12:48:35 +0200133 struct drm_fb *fb, uint64_t zpos)
Daniel Stonee404b722019-06-22 18:40:31 +0100134{
135 struct drm_output *output = output_state->output;
136 struct weston_compositor *ec = output->base.compositor;
137 struct drm_backend *b = to_drm_backend(ec);
Daniel Stonee404b722019-06-22 18:40:31 +0100138 struct drm_plane_state *state = NULL;
Daniel Stonee404b722019-06-22 18:40:31 +0100139 int ret;
Daniel Stonee404b722019-06-22 18:40:31 +0100140
141 assert(!b->sprites_are_broken);
142 assert(b->atomic_modeset);
143
Daniel Stonee404b722019-06-22 18:40:31 +0100144 if (!fb) {
145 drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
146 " couldn't get fb\n", ev);
147 return NULL;
148 }
149
Marius Vlad677e4592019-10-25 00:07:37 +0300150 state = drm_output_state_get_plane(output_state, plane);
Marius Vladeef69452019-10-31 12:48:35 +0200151 /* we can't have a 'pending' framebuffer as never set one before reaching here */
152 assert(!state->fb);
Daniel Stonee404b722019-06-22 18:40:31 +0100153
Marius Vlad677e4592019-10-25 00:07:37 +0300154 state->ev = ev;
155 state->output = output;
Marius Vladeef69452019-10-31 12:48:35 +0200156
Marius Vlad677e4592019-10-25 00:07:37 +0300157 if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
158 drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
159 "unsuitable transform\n", ev);
Daniel Stonee404b722019-06-22 18:40:31 +0100160 drm_plane_state_put_back(state);
161 state = NULL;
Marius Vlad677e4592019-10-25 00:07:37 +0300162 goto out;
Daniel Stonee404b722019-06-22 18:40:31 +0100163 }
164
Marius Vlad677e4592019-10-25 00:07:37 +0300165 /* If the surface buffer has an in-fence fd, but the plane
166 * doesn't support fences, we can't place the buffer on this
167 * plane. */
168 if (ev->surface->acquire_fence_fd >= 0 &&
169 plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
170 drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
171 "no in-fence support\n", ev);
172 drm_plane_state_put_back(state);
173 state = NULL;
174 goto out;
175 }
176
Marius Vladeef69452019-10-31 12:48:35 +0200177 /* We hold one reference for the lifetime of this function; from
178 * calling drm_fb_get_from_view() in drm_output_prepare_plane_view(),
179 * so, we take another reference here to live within the state. */
Marius Vlad677e4592019-10-25 00:07:37 +0300180 state->fb = drm_fb_ref(fb);
181
182 state->in_fence_fd = ev->surface->acquire_fence_fd;
183
184 /* In planes-only mode, we don't have an incremental state to
185 * test against, so we just hope it'll work. */
186 if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
187 drm_debug(b, "\t\t\t[overlay] provisionally placing "
188 "view %p on overlay %lu in planes-only mode\n",
189 ev, (unsigned long) plane->plane_id);
Marius Vlad677e4592019-10-25 00:07:37 +0300190 goto out;
191 }
192
193 ret = drm_pending_state_test(output_state->pending_state);
194 if (ret == 0) {
195 drm_debug(b, "\t\t\t[overlay] provisionally placing "
196 "view %p on overlay %d in mixed mode\n",
197 ev, plane->plane_id);
Marius Vlad677e4592019-10-25 00:07:37 +0300198 goto out;
199 }
200
201 drm_debug(b, "\t\t\t[overlay] not placing view %p on overlay %lu "
202 "in mixed mode: kernel test failed\n",
203 ev, (unsigned long) plane->plane_id);
204
205 drm_plane_state_put_back(state);
206 state = NULL;
207
Daniel Stonee404b722019-06-22 18:40:31 +0100208out:
Daniel Stonee404b722019-06-22 18:40:31 +0100209 return state;
210}
211
Stefan Agnerccf24072019-07-09 22:02:00 +0200212#ifdef BUILD_DRM_GBM
Daniel Stonee404b722019-06-22 18:40:31 +0100213/**
214 * Update the image for the current cursor surface
215 *
216 * @param plane_state DRM cursor plane state
217 * @param ev Source view for cursor
218 */
219static void
220cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev)
221{
222 struct drm_backend *b = plane_state->plane->backend;
223 struct gbm_bo *bo = plane_state->fb->bo;
224 struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
225 uint32_t buf[b->cursor_width * b->cursor_height];
226 int32_t stride;
227 uint8_t *s;
228 int i;
229
230 assert(buffer && buffer->shm_buffer);
231 assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource));
232 assert(buffer->width <= b->cursor_width);
233 assert(buffer->height <= b->cursor_height);
234
235 memset(buf, 0, sizeof buf);
236 stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
237 s = wl_shm_buffer_get_data(buffer->shm_buffer);
238
239 wl_shm_buffer_begin_access(buffer->shm_buffer);
240 for (i = 0; i < buffer->height; i++)
241 memcpy(buf + i * b->cursor_width,
242 s + i * stride,
243 buffer->width * 4);
244 wl_shm_buffer_end_access(buffer->shm_buffer);
245
246 if (gbm_bo_write(bo, buf, sizeof buf) < 0)
247 weston_log("failed update cursor: %s\n", strerror(errno));
248}
249
250static struct drm_plane_state *
251drm_output_prepare_cursor_view(struct drm_output_state *output_state,
Marius Vlad2538aac2019-10-14 11:05:30 +0300252 struct weston_view *ev, uint64_t zpos)
Daniel Stonee404b722019-06-22 18:40:31 +0100253{
254 struct drm_output *output = output_state->output;
255 struct drm_backend *b = to_drm_backend(output->base.compositor);
256 struct drm_plane *plane = output->cursor_plane;
257 struct drm_plane_state *plane_state;
Daniel Stonee404b722019-06-22 18:40:31 +0100258 bool needs_update = false;
Marius Vlad7a465d82021-04-03 19:54:05 +0300259 struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
Marius Vlad36f11a52019-11-01 23:49:21 +0200260 const char *p_name = drm_output_get_plane_type_name(plane);
Daniel Stonee404b722019-06-22 18:40:31 +0100261
262 assert(!b->cursors_are_broken);
263
264 if (!plane)
265 return NULL;
266
267 if (!plane->state_cur->complete)
268 return NULL;
269
270 if (plane->state_cur->output && plane->state_cur->output != output)
271 return NULL;
272
273 /* We use GBM to import SHM buffers. */
274 if (b->gbm == NULL)
275 return NULL;
276
Leandro Ribeiro05cecc82020-08-13 17:34:58 -0300277 plane_state = drm_output_state_get_plane(output_state, plane);
Daniel Stonee404b722019-06-22 18:40:31 +0100278
279 if (plane_state && plane_state->fb)
280 return NULL;
281
282 /* We can't scale with the legacy API, and we don't try to account for
283 * simple cropping/translation in cursor_bo_update. */
284 plane_state->output = output;
Marius Vlad36f11a52019-11-01 23:49:21 +0200285 if (!drm_plane_state_coords_for_view(plane_state, ev, zpos)) {
286 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
287 "unsuitable transform\n", p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100288 goto err;
Marius Vlad36f11a52019-11-01 23:49:21 +0200289 }
Daniel Stonee404b722019-06-22 18:40:31 +0100290
Marius Vlad7a465d82021-04-03 19:54:05 +0300291 if (buffer->width > b->cursor_width ||
292 buffer->height > b->cursor_height) {
293 drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
294 "(surface buffer (%dx%d) larger than permitted"
295 " (%dx%d))\n", p_name, ev, p_name,
296 buffer->width, buffer->height,
297 b->cursor_width, b->cursor_height);
298 goto err;
299 }
300
Daniel Stonee404b722019-06-22 18:40:31 +0100301 if (plane_state->src_x != 0 || plane_state->src_y != 0 ||
302 plane_state->src_w > (unsigned) b->cursor_width << 16 ||
303 plane_state->src_h > (unsigned) b->cursor_height << 16 ||
304 plane_state->src_w != plane_state->dest_w << 16 ||
305 plane_state->src_h != plane_state->dest_h << 16) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200306 drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
307 "(positioning requires cropping or scaling)\n",
308 p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100309 goto err;
310 }
311
312 /* Since we're setting plane state up front, we need to work out
313 * whether or not we need to upload a new cursor. We can't use the
314 * plane damage, since the planes haven't actually been calculated
315 * yet: instead try to figure it out directly. KMS cursor planes are
316 * pretty unique here, in that they lie partway between a Weston plane
317 * (direct scanout) and a renderer. */
318 if (ev != output->cursor_view ||
319 pixman_region32_not_empty(&ev->surface->damage)) {
320 output->current_cursor++;
321 output->current_cursor =
322 output->current_cursor %
323 ARRAY_LENGTH(output->gbm_cursor_fb);
324 needs_update = true;
325 }
326
Alexandros Frantzis10937fe2021-06-14 13:09:44 +0300327 drm_output_set_cursor_view(output, ev);
Daniel Stonee404b722019-06-22 18:40:31 +0100328 plane_state->ev = ev;
329
330 plane_state->fb =
331 drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]);
332
333 if (needs_update) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200334 drm_debug(b, "\t\t\t\t[%s] copying new content to cursor BO\n", p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100335 cursor_bo_update(plane_state, ev);
336 }
337
338 /* The cursor API is somewhat special: in cursor_bo_update(), we upload
339 * a buffer which is always cursor_width x cursor_height, even if the
340 * surface we want to promote is actually smaller than this. Manually
341 * mangle the plane state to deal with this. */
342 plane_state->src_w = b->cursor_width << 16;
343 plane_state->src_h = b->cursor_height << 16;
344 plane_state->dest_w = b->cursor_width;
345 plane_state->dest_h = b->cursor_height;
346
Marius Vlad36f11a52019-11-01 23:49:21 +0200347 drm_debug(b, "\t\t\t\t[%s] provisionally assigned view %p to cursor\n",
348 p_name, ev);
Daniel Stonee404b722019-06-22 18:40:31 +0100349
350 return plane_state;
351
352err:
353 drm_plane_state_put_back(plane_state);
354 return NULL;
355}
Stefan Agnerccf24072019-07-09 22:02:00 +0200356#else
357static struct drm_plane_state *
358drm_output_prepare_cursor_view(struct drm_output_state *output_state,
Marius Vlad2538aac2019-10-14 11:05:30 +0300359 struct weston_view *ev, uint64_t zpos)
Stefan Agnerccf24072019-07-09 22:02:00 +0200360{
361 return NULL;
362}
363#endif
Daniel Stonee404b722019-06-22 18:40:31 +0100364
365static struct drm_plane_state *
366drm_output_prepare_scanout_view(struct drm_output_state *output_state,
367 struct weston_view *ev,
Marius Vlad2538aac2019-10-14 11:05:30 +0300368 enum drm_output_propose_state_mode mode,
Marius Vladeef69452019-10-31 12:48:35 +0200369 struct drm_fb *fb, uint64_t zpos)
Daniel Stonee404b722019-06-22 18:40:31 +0100370{
371 struct drm_output *output = output_state->output;
372 struct drm_backend *b = to_drm_backend(output->base.compositor);
373 struct drm_plane *scanout_plane = output->scanout_plane;
374 struct drm_plane_state *state;
Marius Vlad36f11a52019-11-01 23:49:21 +0200375 const char *p_name = drm_output_get_plane_type_name(scanout_plane);
Daniel Stonee404b722019-06-22 18:40:31 +0100376
377 assert(!b->sprites_are_broken);
378 assert(b->atomic_modeset);
379 assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
380
381 /* Check the view spans exactly the output size, calculated in the
382 * logical co-ordinate space. */
Marius Vlad28bb2da2019-10-19 18:18:38 +0300383 if (!weston_view_matches_output_entirely(ev, &output->base)) {
384 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
385 " view does not match output entirely\n",
386 p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100387 return NULL;
Marius Vlad28bb2da2019-10-19 18:18:38 +0300388 }
Daniel Stonee404b722019-06-22 18:40:31 +0100389
390 /* If the surface buffer has an in-fence fd, but the plane doesn't
391 * support fences, we can't place the buffer on this plane. */
392 if (ev->surface->acquire_fence_fd >= 0 &&
Marius Vlad36f11a52019-11-01 23:49:21 +0200393 scanout_plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
394 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
395 "no in-fence support\n", p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100396 return NULL;
Marius Vlad36f11a52019-11-01 23:49:21 +0200397 }
Daniel Stonee404b722019-06-22 18:40:31 +0100398
Daniel Stonee404b722019-06-22 18:40:31 +0100399 if (!fb) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200400 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
401 " couldn't get fb\n", p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100402 return NULL;
403 }
404
405 state = drm_output_state_get_plane(output_state, scanout_plane);
406
407 /* The only way we can already have a buffer in the scanout plane is
408 * if we are in mixed mode, or if a client buffer has already been
409 * placed into scanout. The former case will never call into here,
410 * and in the latter case, the view must have been marked as occluded,
411 * meaning we should never have ended up here. */
412 assert(!state->fb);
Marius Vladeef69452019-10-31 12:48:35 +0200413
414 /* take another reference here to live within the state */
415 state->fb = drm_fb_ref(fb);
Daniel Stonee404b722019-06-22 18:40:31 +0100416 state->ev = ev;
417 state->output = output;
Marius Vlad36f11a52019-11-01 23:49:21 +0200418 if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
419 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
420 "unsuitable transform\n", p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100421 goto err;
Marius Vlad36f11a52019-11-01 23:49:21 +0200422 }
Daniel Stonee404b722019-06-22 18:40:31 +0100423
424 if (state->dest_x != 0 || state->dest_y != 0 ||
leng.fang32af9fc2024-06-13 11:22:15 +0800425#ifdef MESON_DRM_FIX_UI_SIZE
426 state->dest_w != (unsigned) output->display_size.width ||
427 state->dest_h != (unsigned) output->display_size.height
428#else
Daniel Stonee404b722019-06-22 18:40:31 +0100429 state->dest_w != (unsigned) output->base.current_mode->width ||
leng.fang32af9fc2024-06-13 11:22:15 +0800430 state->dest_h != (unsigned) output->base.current_mode->height
431#endif
432 ) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200433 drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
434 " invalid plane state\n", p_name, ev, p_name);
Daniel Stonee404b722019-06-22 18:40:31 +0100435 goto err;
Marius Vlad36f11a52019-11-01 23:49:21 +0200436 }
Daniel Stonee404b722019-06-22 18:40:31 +0100437
438 state->in_fence_fd = ev->surface->acquire_fence_fd;
439
440 /* In plane-only mode, we don't need to test the state now, as we
441 * will only test it once at the end. */
442 return state;
443
444err:
445 drm_plane_state_put_back(state);
446 return NULL;
447}
448
Marius Vlad2538aac2019-10-14 11:05:30 +0300449static struct drm_plane_state *
450drm_output_try_view_on_plane(struct drm_plane *plane,
451 struct drm_output_state *state,
452 struct weston_view *ev,
453 enum drm_output_propose_state_mode mode,
Marius Vladeef69452019-10-31 12:48:35 +0200454 struct drm_fb *fb, uint64_t zpos)
Marius Vlad2538aac2019-10-14 11:05:30 +0300455{
456 struct drm_backend *b = state->pending_state->backend;
457 struct weston_output *wet_output = &state->output->base;
458 bool view_matches_entire_output, scanout_has_view_assigned;
459 struct drm_plane *scanout_plane = state->output->scanout_plane;
Marius Vlad36f11a52019-11-01 23:49:21 +0200460 struct drm_plane_state *ps = NULL;
461 const char *p_name = drm_output_get_plane_type_name(plane);
Daniel Stone267f6cc2021-11-18 15:17:53 +0000462 struct weston_surface *surface = ev->surface;
Marius Vlad36f11a52019-11-01 23:49:21 +0200463 enum {
464 NO_PLANES, /* generic err-handle */
465 NO_PLANES_ACCEPTED,
466 PLACED_ON_PLANE,
467 } availability = NO_PLANES;
Marius Vlad2538aac2019-10-14 11:05:30 +0300468
469 /* sanity checks in case we over/underflow zpos or pass incorrect
470 * values */
471 assert(zpos <= plane->zpos_max ||
472 zpos != DRM_PLANE_ZPOS_INVALID_PLANE);
473
474 switch (plane->type) {
475 case WDRM_PLANE_TYPE_CURSOR:
476 if (b->cursors_are_broken) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200477 availability = NO_PLANES_ACCEPTED;
478 goto out;
Marius Vlad2538aac2019-10-14 11:05:30 +0300479 }
Marius Vlad36f11a52019-11-01 23:49:21 +0200480
481 ps = drm_output_prepare_cursor_view(state, ev, zpos);
482 if (ps)
483 availability = PLACED_ON_PLANE;
484 break;
Marius Vlad2538aac2019-10-14 11:05:30 +0300485 case WDRM_PLANE_TYPE_OVERLAY:
486 /* do not attempt to place it in the overlay if we don't have
487 * anything in the scanout/primary and the view doesn't cover
488 * the entire output */
489 view_matches_entire_output =
490 weston_view_matches_output_entirely(ev, wet_output);
491 scanout_has_view_assigned =
492 drm_output_check_plane_has_view_assigned(scanout_plane,
493 state);
leng.fang32af9fc2024-06-13 11:22:15 +0800494#if 0
Marius Vlad2538aac2019-10-14 11:05:30 +0300495 if (view_matches_entire_output && !scanout_has_view_assigned) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200496 availability = NO_PLANES_ACCEPTED;
497 goto out;
Marius Vlad2538aac2019-10-14 11:05:30 +0300498 }
leng.fang32af9fc2024-06-13 11:22:15 +0800499#endif
500 drm_debug(b, "\t\t\t\t[overlay] view %p WDRM_PLANE_TYPE_OVERLAY\n", ev);
Marius Vlad36f11a52019-11-01 23:49:21 +0200501 ps = drm_output_prepare_overlay_view(plane, state, ev, mode,
502 fb, zpos);
503 if (ps)
504 availability = PLACED_ON_PLANE;
505 break;
Marius Vlad2538aac2019-10-14 11:05:30 +0300506 case WDRM_PLANE_TYPE_PRIMARY:
507 if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
Marius Vlad36f11a52019-11-01 23:49:21 +0200508 availability = NO_PLANES_ACCEPTED;
509 goto out;
Marius Vlad2538aac2019-10-14 11:05:30 +0300510 }
Marius Vlad36f11a52019-11-01 23:49:21 +0200511
512 ps = drm_output_prepare_scanout_view(state, ev, mode,
513 fb, zpos);
514 if (ps)
515 availability = PLACED_ON_PLANE;
516 break;
Marius Vlad2538aac2019-10-14 11:05:30 +0300517 default:
518 assert(0);
519 break;
520 }
Marius Vlad36f11a52019-11-01 23:49:21 +0200521
522out:
523 switch (availability) {
524 case NO_PLANES:
525 /* set initial to this catch-all case, such that
526 * prepare_cursor/overlay/scanout() should have/contain the
527 * reason for failling */
528 break;
529 case NO_PLANES_ACCEPTED:
530 drm_debug(b, "\t\t\t\t[plane] plane %d refusing to "
531 "place view %p in %s\n",
532 plane->plane_id, ev, p_name);
533 break;
534 case PLACED_ON_PLANE:
Daniel Stone267f6cc2021-11-18 15:17:53 +0000535 /* Take a reference on the buffer so that we don't release it
536 * back to the client until we're done with it; cursor buffers
537 * don't require a reference since we copy them. */
Daniel Stone2ecc38b2021-11-18 15:33:17 +0000538 assert(ps->fb_ref.buffer.buffer == NULL);
539 assert(ps->fb_ref.release.buffer_release == NULL);
Daniel Stone267f6cc2021-11-18 15:17:53 +0000540 if (ps->plane->type == WDRM_PLANE_TYPE_CURSOR) {
541 assert(ps->fb->type == BUFFER_CURSOR);
542 } else if (fb->type == BUFFER_CLIENT || fb->type == BUFFER_DMABUF) {
543 assert(ps->fb == fb);
Daniel Stone2ecc38b2021-11-18 15:33:17 +0000544 weston_buffer_reference(&ps->fb_ref.buffer,
545 surface->buffer_ref.buffer);
546 weston_buffer_release_reference(&ps->fb_ref.release,
Daniel Stone267f6cc2021-11-18 15:17:53 +0000547 surface->buffer_release_ref.buffer_release);
548 }
Marius Vlad36f11a52019-11-01 23:49:21 +0200549 break;
550 }
551
552
553 return ps;
Marius Vlad2538aac2019-10-14 11:05:30 +0300554}
555
Marius Vlad48bc5ef2019-11-15 14:32:08 +0200556static void
Marius Vlad2538aac2019-10-14 11:05:30 +0300557drm_output_check_zpos_plane_states(struct drm_output_state *state)
558{
Marius Vlad2538aac2019-10-14 11:05:30 +0300559 struct drm_plane_state *ps;
Marius Vlad2538aac2019-10-14 11:05:30 +0300560
561 wl_list_for_each(ps, &state->plane_list, link) {
562 struct wl_list *next_node = ps->link.next;
563 bool found_dup = false;
564
Marius Vlad788e80d2019-11-15 00:25:11 +0200565 /* skip any plane that is not enabled */
566 if (!ps->fb)
567 continue;
568
569 assert(ps->zpos != DRM_PLANE_ZPOS_INVALID_PLANE);
570
Marius Vlad2538aac2019-10-14 11:05:30 +0300571 /* find another plane with the same zpos value */
572 if (next_node == &state->plane_list)
573 break;
574
575 while (next_node && next_node != &state->plane_list) {
576 struct drm_plane_state *ps_next;
577
578 ps_next = container_of(next_node,
579 struct drm_plane_state,
580 link);
581
582 if (ps->zpos == ps_next->zpos) {
583 found_dup = true;
584 break;
585 }
586 next_node = next_node->next;
587 }
588
Marius Vlad48bc5ef2019-11-15 14:32:08 +0200589 /* this should never happen so exit hard in case
590 * we screwed up that bad */
591 assert(!found_dup);
Marius Vlad2538aac2019-10-14 11:05:30 +0300592 }
Marius Vlad2538aac2019-10-14 11:05:30 +0300593}
594
Leandro Ribeiro54293022021-10-12 14:48:36 -0300595static bool
596dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev,
597 uint32_t try_view_on_plane_failure_reasons)
598{
599 struct weston_dmabuf_feedback *dmabuf_feedback = ev->surface->dmabuf_feedback;
600 struct weston_dmabuf_feedback_tranche *scanout_tranche;
601 dev_t scanout_dev = b->drm.devnum;
602 uint32_t scanout_flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT;
603 uint32_t action_needed = ACTION_NEEDED_NONE;
604 struct timespec current_time, delta_time;
605 const time_t MAX_TIME_SECONDS = 2;
606
607 /* Find out what we need to do with the dma-buf feedback */
608 if (try_view_on_plane_failure_reasons & FAILURE_REASONS_FORCE_RENDERER)
609 action_needed |= ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE;
610 if (try_view_on_plane_failure_reasons &
611 (FAILURE_REASONS_ADD_FB_FAILED |
612 FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE |
613 FAILURE_REASONS_DMABUF_MODIFIER_INVALID))
614 action_needed |= ACTION_NEEDED_ADD_SCANOUT_TRANCHE;
615
616 assert(action_needed != (ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE |
617 ACTION_NEEDED_ADD_SCANOUT_TRANCHE));
618
619 /* Look for scanout tranche. If not found, add it but in disabled mode
620 * (we still don't know if we'll have to send it to clients). This
621 * simplifies the code. */
622 scanout_tranche =
623 weston_dmabuf_feedback_find_tranche(dmabuf_feedback, scanout_dev,
624 scanout_flags, SCANOUT_PREF);
625 if (!scanout_tranche) {
626 scanout_tranche =
627 weston_dmabuf_feedback_tranche_create(dmabuf_feedback,
628 b->compositor->dmabuf_feedback_format_table,
629 scanout_dev, scanout_flags,
630 SCANOUT_PREF);
631 scanout_tranche->active = false;
632 }
633
634 /* No actions needed, so disarm timer and return */
635 if (action_needed == ACTION_NEEDED_NONE ||
636 (action_needed == ACTION_NEEDED_ADD_SCANOUT_TRANCHE &&
637 scanout_tranche->active) ||
638 (action_needed == ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE &&
639 !scanout_tranche->active)) {
640 dmabuf_feedback->action_needed = ACTION_NEEDED_NONE;
641 return false;
642 }
643
644 /* We hit this if:
645 *
646 * 1. timer is still off, or
647 * 2. the action needed when it was set to on does not match the most
648 * recent needed action we've detected.
649 *
650 * So we reset the timestamp, set the timer to on it with the most
651 * recent needed action, return and leave the timer running. */
652 if (dmabuf_feedback->action_needed == ACTION_NEEDED_NONE ||
653 dmabuf_feedback->action_needed != action_needed) {
654 clock_gettime(CLOCK_MONOTONIC, &dmabuf_feedback->timer);
655 dmabuf_feedback->action_needed = action_needed;
656 return false;
657 /* Timer is already on and the action needed when it was set to on does
658 * not conflict with the most recent needed action we've detected. If
659 * more than MAX_TIME_SECONDS has passed, we need to resend the dma-buf
660 * feedback. Otherwise, return and leave the timer running. */
661 } else {
662 clock_gettime(CLOCK_MONOTONIC, &current_time);
663 delta_time.tv_sec = current_time.tv_sec -
664 dmabuf_feedback->timer.tv_sec;
665 if (delta_time.tv_sec < MAX_TIME_SECONDS)
666 return false;
667 }
668
669 /* If we got here it means that the timer has triggered, so we have
670 * pending actions with the dma-buf feedback. So we update and resend
671 * them. */
672 if (action_needed == ACTION_NEEDED_ADD_SCANOUT_TRANCHE)
673 scanout_tranche->active = true;
674 else if (action_needed == ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE)
675 scanout_tranche->active = false;
676 else
677 assert(0);
678
679 drm_debug(b, "\t[repaint] Need to update and resend the "
680 "dma-buf feedback for surface of view %p\n", ev);
681 weston_dmabuf_feedback_send_all(dmabuf_feedback,
682 b->compositor->dmabuf_feedback_format_table);
683
684 /* Set the timer to off */
685 dmabuf_feedback->action_needed = ACTION_NEEDED_NONE;
686
687 return true;
688}
689
Marius Vlad2538aac2019-10-14 11:05:30 +0300690static struct drm_plane_state *
691drm_output_prepare_plane_view(struct drm_output_state *state,
692 struct weston_view *ev,
693 enum drm_output_propose_state_mode mode,
Marius Vladbd002b92019-11-15 13:33:38 +0200694 struct drm_plane_state *scanout_state,
Leandro Ribeiro0a7034c2021-09-13 14:52:53 -0300695 uint64_t current_lowest_zpos,
696 uint32_t *try_view_on_plane_failure_reasons)
Marius Vlad2538aac2019-10-14 11:05:30 +0300697{
698 struct drm_output *output = state->output;
699 struct drm_backend *b = to_drm_backend(output->base.compositor);
700
701 struct drm_plane_state *ps = NULL;
702 struct drm_plane *plane;
703 struct drm_plane_zpos *p_zpos, *p_zpos_next;
704 struct wl_list zpos_candidate_list;
705
Leandro Ribeiro4b5df8b2021-03-23 16:30:09 -0300706 struct weston_buffer *buffer;
707 struct wl_shm_buffer *shmbuf;
Marius Vlad26dcce02019-10-24 14:49:33 +0300708 struct drm_fb *fb;
709
Marius Vlad2538aac2019-10-14 11:05:30 +0300710 wl_list_init(&zpos_candidate_list);
711
712 /* check view for valid buffer, doesn't make sense to even try */
713 if (!weston_view_has_valid_buffer(ev))
714 return ps;
715
Leandro Ribeiro4b5df8b2021-03-23 16:30:09 -0300716 buffer = ev->surface->buffer_ref.buffer;
717 shmbuf = wl_shm_buffer_get(buffer->resource);
Leandro Ribeiro0a7034c2021-09-13 14:52:53 -0300718 fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons);
limin.tianc616dad2024-07-15 11:35:38 +0000719 drm_debug(b, "\t\t\t\t buffer:%p shmbuf:%p fb:%p",
720 buffer, shmbuf, fb);
Daniel Stone9e379db2021-11-18 15:21:08 +0000721 if (!shmbuf && !fb)
722 return NULL;
Marius Vlad26dcce02019-10-24 14:49:33 +0300723
Marius Vlad2538aac2019-10-14 11:05:30 +0300724 /* assemble a list with possible candidates */
725 wl_list_for_each(plane, &b->plane_list, link) {
726 if (!drm_plane_is_available(plane, output))
727 continue;
728
729 if (drm_output_check_plane_has_view_assigned(plane, state)) {
730 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to"
731 " candidate list: view already assigned "
732 "to a plane\n", plane->plane_id);
733 continue;
734 }
735
736 if (plane->zpos_min >= current_lowest_zpos) {
737 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
Maxime Roussin-Bélanger35e34502020-12-17 17:08:56 -0500738 "candidate list: minimum zpos (%"PRIu64") "
Marius Vlad2538aac2019-10-14 11:05:30 +0300739 "plane's above current lowest zpos "
740 "(%"PRIu64")\n", plane->plane_id,
741 plane->zpos_min, current_lowest_zpos);
742 continue;
743 }
744
Marius Vladbd002b92019-11-15 13:33:38 +0200745 if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
746 assert(scanout_state != NULL);
leng.fang32af9fc2024-06-13 11:22:15 +0800747#if MESON_VIDEO_PLAN_SUPPORT
748 /* we need transparent the region coverd by video,
749 * so we can ignore zpos of scanout_state for video surface.
750 */
751 if (!ev->surface->is_video_surface && scanout_state->zpos >= plane->zpos_max)
752#else
753 if (scanout_state->zpos >= plane->zpos_max)
754#endif
755 {
Marius Vladbd002b92019-11-15 13:33:38 +0200756 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
757 "candidate list: primary's zpos "
758 "value (%"PRIu64") higher than "
759 "plane's maximum value (%"PRIu64")\n",
760 plane->plane_id, scanout_state->zpos,
761 plane->zpos_max);
762 continue;
763 }
764 }
765
Marius Vlad18462e82019-11-15 14:11:49 +0200766 if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY &&
767 (plane->type == WDRM_PLANE_TYPE_OVERLAY ||
768 plane->type == WDRM_PLANE_TYPE_PRIMARY)) {
769 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
770 "candidate list: renderer-only mode\n",
771 plane->plane_id);
772 continue;
773 }
774
Daniel Stone57d609a2021-11-16 18:56:09 +0000775 if (plane->type == WDRM_PLANE_TYPE_CURSOR &&
776 (!shmbuf || wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888)) {
Leandro Ribeiro4b5df8b2021-03-23 16:30:09 -0300777 drm_debug(b, "\t\t\t\t[plane] not adding plane %d, type cursor to "
Daniel Stone57d609a2021-11-16 18:56:09 +0000778 "candidate list: cursor planes only support ARGB8888"
779 "wl_shm buffers and the view buffer is of another type\n",
Leandro Ribeiro4b5df8b2021-03-23 16:30:09 -0300780 plane->plane_id);
781 continue;
782 }
783
Daniel Stone57d609a2021-11-16 18:56:09 +0000784 if (plane->type != WDRM_PLANE_TYPE_CURSOR &&
785 (!fb || !(fb->plane_mask & (1 << plane->plane_idx)))) {
Leandro Ribeiro0a7034c2021-09-13 14:52:53 -0300786 *try_view_on_plane_failure_reasons |=
787 FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
Marius Vlad26dcce02019-10-24 14:49:33 +0300788 drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
789 "candidate list: invalid pixel format\n",
790 plane->plane_id);
791 continue;
792 }
793
Marius Vlad2538aac2019-10-14 11:05:30 +0300794 drm_output_add_zpos_plane(plane, &zpos_candidate_list);
795 }
796
797 /* go over the potential candidate list and try to find a possible
798 * plane suitable for \c ev; start with the highest zpos value of a
799 * plane to maximize our chances, but do note we pass the zpos value
800 * based on current tracked value by \c current_lowest_zpos_in_use */
801 while (!wl_list_empty(&zpos_candidate_list)) {
802 struct drm_plane_zpos *head_p_zpos =
803 wl_container_of(zpos_candidate_list.next,
804 head_p_zpos, link);
805 struct drm_plane *plane = head_p_zpos->plane;
806 const char *p_name = drm_output_get_plane_type_name(plane);
807 uint64_t zpos;
808
809 if (current_lowest_zpos == DRM_PLANE_ZPOS_INVALID_PLANE)
810 zpos = plane->zpos_max;
811 else
812 zpos = MIN(current_lowest_zpos - 1, plane->zpos_max);
813
814 drm_debug(b, "\t\t\t\t[plane] plane %d picked "
815 "from candidate list, type: %s\n",
816 plane->plane_id, p_name);
817
leng.fang32af9fc2024-06-13 11:22:15 +0800818 if (drm_check_video_plane_destroy_zpos(b, ev, plane, p_name)) {
819 drm_output_destroy_zpos_plane(head_p_zpos);
820 continue;
821 }
822 drm_debug(b, "\t\t\t\t[plane] try view on plane %d \n",
823 plane->plane_id );
824
Marius Vladeef69452019-10-31 12:48:35 +0200825 ps = drm_output_try_view_on_plane(plane, state, ev,
826 mode, fb, zpos);
Marius Vlad2538aac2019-10-14 11:05:30 +0300827 drm_output_destroy_zpos_plane(head_p_zpos);
Marius Vlad2538aac2019-10-14 11:05:30 +0300828 if (ps) {
829 drm_debug(b, "\t\t\t\t[view] view %p has been placed to "
830 "%s plane with computed zpos %"PRIu64"\n",
831 ev, p_name, zpos);
832 break;
833 }
834 }
835
836 wl_list_for_each_safe(p_zpos, p_zpos_next, &zpos_candidate_list, link)
837 drm_output_destroy_zpos_plane(p_zpos);
838
Marius Vlad26dcce02019-10-24 14:49:33 +0300839 drm_fb_unref(fb);
leng.fang32af9fc2024-06-13 11:22:15 +0800840 drm_update_keep_frame_info(ev, fb, ps);
Marius Vlad2538aac2019-10-14 11:05:30 +0300841 return ps;
842}
843
Daniel Stonee404b722019-06-22 18:40:31 +0100844static struct drm_output_state *
845drm_output_propose_state(struct weston_output *output_base,
846 struct drm_pending_state *pending_state,
847 enum drm_output_propose_state_mode mode)
848{
849 struct drm_output *output = to_drm_output(output_base);
850 struct drm_backend *b = to_drm_backend(output->base.compositor);
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +0300851 struct weston_paint_node *pnode;
Daniel Stonee404b722019-06-22 18:40:31 +0100852 struct drm_output_state *state;
853 struct drm_plane_state *scanout_state = NULL;
Marius Vlad80a62e52019-09-11 18:21:59 +0300854
leng.fang32af9fc2024-06-13 11:22:15 +0800855 pixman_region32_t renderer_region, video_cover_region;
Marius Vlad80a62e52019-09-11 18:21:59 +0300856 pixman_region32_t occluded_region;
857
Daniel Stonee404b722019-06-22 18:40:31 +0100858 bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
859 int ret;
Marius Vlad2538aac2019-10-14 11:05:30 +0300860 uint64_t current_lowest_zpos = DRM_PLANE_ZPOS_INVALID_PLANE;
Daniel Stonee404b722019-06-22 18:40:31 +0100861
862 assert(!output->state_last);
863 state = drm_output_state_duplicate(output->state_cur,
864 pending_state,
865 DRM_OUTPUT_STATE_CLEAR_PLANES);
866
867 /* We implement mixed mode by progressively creating and testing
868 * incremental states, of scanout + overlay + cursor. Since we
869 * walk our views top to bottom, the scanout plane is last, however
870 * we always need it in our scene for the test modeset to be
871 * meaningful. To do this, we steal a reference to the last
872 * renderer framebuffer we have, if we think it's basically
873 * compatible. If we don't have that, then we conservatively fall
874 * back to only using the renderer for this repaint. */
875 if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
876 struct drm_plane *plane = output->scanout_plane;
877 struct drm_fb *scanout_fb = plane->state_cur->fb;
878
879 if (!scanout_fb ||
880 (scanout_fb->type != BUFFER_GBM_SURFACE &&
881 scanout_fb->type != BUFFER_PIXMAN_DUMB)) {
882 drm_debug(b, "\t\t[state] cannot propose mixed mode: "
883 "for output %s (%lu): no previous renderer "
884 "fb\n",
885 output->base.name,
886 (unsigned long) output->base.id);
887 drm_output_state_free(state);
888 return NULL;
889 }
890
leng.fang32af9fc2024-06-13 11:22:15 +0800891 /*if (scanout_fb->width != output_base->current_mode->width ||
Daniel Stonee404b722019-06-22 18:40:31 +0100892 scanout_fb->height != output_base->current_mode->height) {
893 drm_debug(b, "\t\t[state] cannot propose mixed mode "
894 "for output %s (%lu): previous fb has "
895 "different size\n",
896 output->base.name,
897 (unsigned long) output->base.id);
898 drm_output_state_free(state);
899 return NULL;
leng.fang32af9fc2024-06-13 11:22:15 +0800900 }*/
Daniel Stonee404b722019-06-22 18:40:31 +0100901
902 scanout_state = drm_plane_state_duplicate(state,
903 plane->state_cur);
Daniel Stone71e6ba52020-08-25 17:46:27 +0100904 /* assign the primary the lowest zpos value */
Marius Vladbd002b92019-11-15 13:33:38 +0200905 scanout_state->zpos = plane->zpos_min;
Daniel Stonee404b722019-06-22 18:40:31 +0100906 drm_debug(b, "\t\t[state] using renderer FB ID %lu for mixed "
907 "mode for output %s (%lu)\n",
908 (unsigned long) scanout_fb->fb_id, output->base.name,
909 (unsigned long) output->base.id);
Marius Vladbd002b92019-11-15 13:33:38 +0200910 drm_debug(b, "\t\t[state] scanout will use for zpos %"PRIu64"\n",
911 scanout_state->zpos);
Daniel Stonee404b722019-06-22 18:40:31 +0100912 }
913
Marius Vlad80a62e52019-09-11 18:21:59 +0300914 /* - renderer_region contains the total region which which will be
915 * covered by the renderer
Marius Vlad80a62e52019-09-11 18:21:59 +0300916 * - occluded_region contains the total region which which will be
917 * covered by the renderer and hardware planes, where the view's
918 * visible-and-opaque region is added in both cases (the view's
919 * opaque region accumulates there for each view); it is being used
920 * to skip the view, if it is completely occluded; includes the
921 * situation where occluded_region covers entire output's region.
Daniel Stonee404b722019-06-22 18:40:31 +0100922 */
923 pixman_region32_init(&renderer_region);
924 pixman_region32_init(&occluded_region);
leng.fang32af9fc2024-06-13 11:22:15 +0800925 pixman_region32_init(&video_cover_region);
Daniel Stonee404b722019-06-22 18:40:31 +0100926
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +0300927 wl_list_for_each(pnode, &output->base.paint_node_z_order_list,
928 z_order_link) {
929 struct weston_view *ev = pnode->view;
Daniel Stonee404b722019-06-22 18:40:31 +0100930 struct drm_plane_state *ps = NULL;
931 bool force_renderer = false;
932 pixman_region32_t clipped_view;
Daniel Stone8ca5d732020-08-25 17:44:35 +0100933 pixman_region32_t surface_overlap;
Daniel Stonee404b722019-06-22 18:40:31 +0100934 bool totally_occluded = false;
Daniel Stonee404b722019-06-22 18:40:31 +0100935
leng.fang32af9fc2024-06-13 11:22:15 +0800936#if MESON_VIDEO_PLAN_SUPPORT
937 pixman_region32_init(&ev->clip);
938#endif
Daniel Stonee404b722019-06-22 18:40:31 +0100939 drm_debug(b, "\t\t\t[view] evaluating view %p for "
940 "output %s (%lu)\n",
941 ev, output->base.name,
942 (unsigned long) output->base.id);
943
944 /* If this view doesn't touch our output at all, there's no
945 * reason to do anything with it. */
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +0300946 /* TODO: turn this into assert once z_order_list is pruned. */
Daniel Stonee404b722019-06-22 18:40:31 +0100947 if (!(ev->output_mask & (1u << output->base.id))) {
948 drm_debug(b, "\t\t\t\t[view] ignoring view %p "
949 "(not on our output)\n", ev);
950 continue;
951 }
952
Pekka Paalanen90a5ffa2021-02-25 12:03:28 +0200953 /* Cannot show anything without a color transform. */
954 if (!pnode->surf_xform_valid) {
955 drm_debug(b, "\t\t\t\t[view] ignoring view %p "
956 "(color transform failed)\n", ev);
957 continue;
958 }
959
Daniel Stonee404b722019-06-22 18:40:31 +0100960 /* Ignore views we know to be totally occluded. */
961 pixman_region32_init(&clipped_view);
962 pixman_region32_intersect(&clipped_view,
963 &ev->transform.boundingbox,
964 &output->base.region);
965
966 pixman_region32_init(&surface_overlap);
967 pixman_region32_subtract(&surface_overlap, &clipped_view,
968 &occluded_region);
Marius Vlad80a62e52019-09-11 18:21:59 +0300969 /* if the view is completely occluded then ignore that
970 * view; includes the case where occluded_region covers
971 * the entire output */
Daniel Stonee404b722019-06-22 18:40:31 +0100972 totally_occluded = !pixman_region32_not_empty(&surface_overlap);
leng.fang32af9fc2024-06-13 11:22:15 +0800973 if (!ev->surface->is_video_surface) {
Daniel Stonee404b722019-06-22 18:40:31 +0100974 if (totally_occluded) {
leng.fang32af9fc2024-06-13 11:22:15 +0800975#if MESON_VIDEO_PLAN_SUPPORT
976 // if occluded , we need set the clip, to make sure it will not draw when glrenderer.
977 // to avoid the video plane coverd by this view.
978 pixman_region32_union(&ev->clip, &ev->clip, &clipped_view);
979#endif
Daniel Stonee404b722019-06-22 18:40:31 +0100980 drm_debug(b, "\t\t\t\t[view] ignoring view %p "
981 "(occluded on our output)\n", ev);
982 pixman_region32_fini(&surface_overlap);
983 pixman_region32_fini(&clipped_view);
984 continue;
985 }
leng.fang32af9fc2024-06-13 11:22:15 +0800986 }
Michael Olbrich3097acc2020-07-14 11:18:54 +0200987 /* We only assign planes to views which are exclusively present
988 * on our output. */
989 if (ev->output_mask != (1u << output->base.id)) {
990 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
991 "(on multiple outputs)\n", ev);
992 force_renderer = true;
993 }
994
995 if (!weston_view_has_valid_buffer(ev)) {
996 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
997 "(no buffer available)\n", ev);
998 force_renderer = true;
999 }
1000
Pekka Paalanen90a5ffa2021-02-25 12:03:28 +02001001 if (pnode->surf_xform.transform != NULL ||
1002 !pnode->surf_xform.identity_pipeline) {
1003 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
1004 "(requires color transform)\n", ev);
1005 force_renderer = true;
1006 }
1007
Daniel Stonee404b722019-06-22 18:40:31 +01001008 /* Since we process views from top to bottom, we know that if
1009 * the view intersects the calculated renderer region, it must
1010 * be part of, or occluded by, it, and cannot go on a plane. */
1011 pixman_region32_intersect(&surface_overlap, &renderer_region,
1012 &clipped_view);
1013 if (pixman_region32_not_empty(&surface_overlap)) {
leng.fang32af9fc2024-06-13 11:22:15 +08001014#if MESON_VIDEO_PLAN_SUPPORT
1015 /* video overly zpos range on meson is huge (0-255) , but
1016 * big then primary plane zpos(65-128) part is not effect, the video
1017 * overly always below primary plane So if video view (NV21/NV21
1018 * format) occluded by renderer views (put into primary plane),
1019 * no need force to gpu composition.
1020 */
1021 if (!ev->surface->is_video_surface) {
limin.tianc616dad2024-07-15 11:35:38 +00001022 if (ev->surface->buffer_ref.buffer && is_direct_display(ev->surface->buffer_ref.buffer)) {
1023 drm_debug(b, "\t\t\t\t[view]direct display buffer %p to plane "
1024 "(client send commit continuous memory buffer)\n", ev);
1025 }
1026 else {
1027 drm_debug(b, "\t\t\t\t[view]none-video surface not assigning view %p to plane "
leng.fang32af9fc2024-06-13 11:22:15 +08001028 "(occluded by renderer views)\n", ev);
limin.tianc616dad2024-07-15 11:35:38 +00001029 force_renderer = true;
1030 }
leng.fang32af9fc2024-06-13 11:22:15 +08001031 }
1032#else
Daniel Stonee404b722019-06-22 18:40:31 +01001033 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
1034 "(occluded by renderer views)\n", ev);
1035 force_renderer = true;
leng.fang32af9fc2024-06-13 11:22:15 +08001036#endif
Daniel Stonee404b722019-06-22 18:40:31 +01001037 }
limin.tianc616dad2024-07-15 11:35:38 +00001038 if ((!ev->surface->is_video_surface) && (ev->surface->buffer_ref.buffer)
1039 && (!is_direct_display(ev->surface->buffer_ref.buffer))) {
1040 drm_debug(b, "\t\t\t\t[view] view %p not video view not direct display "
1041 "(force render)\n", ev);
1042 force_renderer = true;
1043 }
leng.fang32af9fc2024-06-13 11:22:15 +08001044
1045#if MESON_VIDEO_PLAN_SUPPORT
1046 pixman_region32_intersect(&surface_overlap, &video_cover_region,
1047 &clipped_view);
1048 if (pixman_region32_not_empty(&surface_overlap)) {
limin.tianc616dad2024-07-15 11:35:38 +00001049 if (!ev->surface->is_video_surface && (!is_direct_display(ev->surface->buffer_ref.buffer))) {
leng.fang32af9fc2024-06-13 11:22:15 +08001050 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
limin.tianc616dad2024-07-15 11:35:38 +00001051 "(block the video views) \n", ev);
leng.fang32af9fc2024-06-13 11:22:15 +08001052 force_renderer = true;
1053 pixman_region32_copy(&ev->transform.transparent, &surface_overlap);
1054 }
1055 } else {
1056 pixman_region32_clear(&ev->transform.transparent);
1057 }
1058#endif
Marius Vlad2538aac2019-10-14 11:05:30 +03001059 pixman_region32_fini(&surface_overlap);
Daniel Stonee404b722019-06-22 18:40:31 +01001060
Ankit Nautiyala344fe32019-05-14 18:36:08 +05301061 /* In case of enforced mode of content-protection do not
1062 * assign planes for a protected surface on an unsecured output.
1063 */
1064 if (ev->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED &&
1065 ev->surface->desired_protection > output_base->current_protection) {
1066 drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
1067 "(enforced protection mode on unsecured output)\n", ev);
1068 force_renderer = true;
1069 }
1070
Daniel Stoneebcf4f32020-08-25 17:46:55 +01001071 /* Now try to place it on a plane if we can. */
Marius Vlad2538aac2019-10-14 11:05:30 +03001072 if (!force_renderer) {
1073 drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n",
1074 current_lowest_zpos);
1075 ps = drm_output_prepare_plane_view(state, ev, mode,
Marius Vladbd002b92019-11-15 13:33:38 +02001076 scanout_state,
Leandro Ribeiro0a7034c2021-09-13 14:52:53 -03001077 current_lowest_zpos,
1078 &pnode->try_view_on_plane_failure_reasons);
1079 /* If we were able to place the view in a plane, set
1080 * failure reasons to none. */
1081 if (ps)
1082 pnode->try_view_on_plane_failure_reasons =
1083 FAILURE_REASONS_NONE;
1084 } else {
1085 /* We are forced to place the view in the renderer, set
1086 * the failure reason accordingly. */
1087 pnode->try_view_on_plane_failure_reasons =
1088 FAILURE_REASONS_FORCE_RENDERER;
Daniel Stone2cb926c2019-11-11 16:48:54 +00001089 }
Daniel Stone2cb926c2019-11-11 16:48:54 +00001090
leng.fang32af9fc2024-06-13 11:22:15 +08001091#if MESON_VIDEO_PLAN_SUPPORT
1092 if (ps && ev->surface->is_video_surface) {
1093 drm_debug(b, "meson set video cover region");
1094 pixman_region32_copy(&video_cover_region, &clipped_view);
1095 }
1096#endif
1097
Daniel Stone2cb926c2019-11-11 16:48:54 +00001098 if (ps) {
Marius Vlad2538aac2019-10-14 11:05:30 +03001099 current_lowest_zpos = ps->zpos;
1100 drm_debug(b, "\t\t\t[plane] next zpos to use %"PRIu64"\n",
1101 current_lowest_zpos);
Daniel Stoneebcf4f32020-08-25 17:46:55 +01001102 } else if (!ps && !renderer_ok) {
Daniel Stonee404b722019-06-22 18:40:31 +01001103 drm_debug(b, "\t\t[view] failing state generation: "
1104 "placing view %p to renderer not allowed\n",
1105 ev);
1106 pixman_region32_fini(&clipped_view);
1107 goto err_region;
Daniel Stoneebcf4f32020-08-25 17:46:55 +01001108 } else if (!ps) {
1109 /* clipped_view contains the area that's going to be
1110 * visible on screen; add this to the renderer region */
1111 pixman_region32_union(&renderer_region,
1112 &renderer_region,
1113 &clipped_view);
Daniel Stonee404b722019-06-22 18:40:31 +01001114
Daniel Stoneebcf4f32020-08-25 17:46:55 +01001115 drm_debug(b, "\t\t\t\t[view] view %p will be placed "
1116 "on the renderer\n", ev);
1117 }
Marius Vlad80a62e52019-09-11 18:21:59 +03001118
Daniel Stoneb625cdc2020-08-25 17:37:34 +01001119 /* Opaque areas of our clipped view occlude areas behind it;
1120 * however, anything not in the opaque region (which is the
1121 * entire clipped area if the whole view is known to be
1122 * opaque) does not necessarily occlude what's behind it, as
1123 * it could be alpha-blended. */
leng.fang32af9fc2024-06-13 11:22:15 +08001124#if MESON_VIDEO_PLAN_SUPPORT
1125 // video layer need force to opaque
1126 if (!weston_view_is_opaque(ev, &clipped_view) || ev->surface->is_video_surface)
1127#else
Marius Vlad80a62e52019-09-11 18:21:59 +03001128 if (!weston_view_is_opaque(ev, &clipped_view))
leng.fang32af9fc2024-06-13 11:22:15 +08001129#endif
Marius Vlad80a62e52019-09-11 18:21:59 +03001130 pixman_region32_intersect(&clipped_view,
1131 &clipped_view,
1132 &ev->transform.opaque);
Marius Vlad80a62e52019-09-11 18:21:59 +03001133 pixman_region32_union(&occluded_region,
1134 &occluded_region,
1135 &clipped_view);
1136
Daniel Stonee404b722019-06-22 18:40:31 +01001137 pixman_region32_fini(&clipped_view);
1138 }
Marius Vlad80a62e52019-09-11 18:21:59 +03001139
Daniel Stonee404b722019-06-22 18:40:31 +01001140 pixman_region32_fini(&renderer_region);
1141 pixman_region32_fini(&occluded_region);
leng.fang32af9fc2024-06-13 11:22:15 +08001142 pixman_region32_fini(&video_cover_region);
Daniel Stonee404b722019-06-22 18:40:31 +01001143
1144 /* In renderer-only mode, we can't test the state as we don't have a
1145 * renderer buffer yet. */
1146 if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY)
1147 return state;
1148
Marius Vlad2538aac2019-10-14 11:05:30 +03001149 /* check if we have invalid zpos values, like duplicate(s) */
Marius Vlad48bc5ef2019-11-15 14:32:08 +02001150 drm_output_check_zpos_plane_states(state);
Marius Vlad2538aac2019-10-14 11:05:30 +03001151
Daniel Stonee404b722019-06-22 18:40:31 +01001152 /* Check to see if this state will actually work. */
1153 ret = drm_pending_state_test(state->pending_state);
1154 if (ret != 0) {
1155 drm_debug(b, "\t\t[view] failing state generation: "
1156 "atomic test not OK\n");
1157 goto err;
1158 }
1159
1160 /* Counterpart to duplicating scanout state at the top of this
1161 * function: if we have taken a renderer framebuffer and placed it in
1162 * the pending state in order to incrementally test overlay planes,
1163 * remove it now. */
1164 if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
1165 assert(scanout_state->fb->type == BUFFER_GBM_SURFACE ||
1166 scanout_state->fb->type == BUFFER_PIXMAN_DUMB);
1167 drm_plane_state_put_back(scanout_state);
1168 }
1169 return state;
1170
1171err_region:
1172 pixman_region32_fini(&renderer_region);
1173 pixman_region32_fini(&occluded_region);
1174err:
1175 drm_output_state_free(state);
1176 return NULL;
1177}
1178
1179void
1180drm_assign_planes(struct weston_output *output_base, void *repaint_data)
1181{
1182 struct drm_backend *b = to_drm_backend(output_base->compositor);
1183 struct drm_pending_state *pending_state = repaint_data;
1184 struct drm_output *output = to_drm_output(output_base);
1185 struct drm_output_state *state = NULL;
1186 struct drm_plane_state *plane_state;
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +03001187 struct weston_paint_node *pnode;
Daniel Stonee404b722019-06-22 18:40:31 +01001188 struct weston_plane *primary = &output_base->compositor->primary_plane;
1189 enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY;
1190
1191 drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n",
1192 output_base->name, (unsigned long) output_base->id);
1193
1194 if (!b->sprites_are_broken && !output->virtual) {
1195 drm_debug(b, "\t[repaint] trying planes-only build state\n");
1196 state = drm_output_propose_state(output_base, pending_state, mode);
1197 if (!state) {
1198 drm_debug(b, "\t[repaint] could not build planes-only "
1199 "state, trying mixed\n");
1200 mode = DRM_OUTPUT_PROPOSE_STATE_MIXED;
1201 state = drm_output_propose_state(output_base,
1202 pending_state,
1203 mode);
1204 }
1205 if (!state) {
1206 drm_debug(b, "\t[repaint] could not build mixed-mode "
1207 "state, trying renderer-only\n");
1208 }
1209 } else {
1210 drm_debug(b, "\t[state] no overlay plane support\n");
1211 }
1212
1213 if (!state) {
1214 mode = DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY;
1215 state = drm_output_propose_state(output_base, pending_state,
1216 mode);
1217 }
1218
1219 assert(state);
1220 drm_debug(b, "\t[repaint] Using %s composition\n",
1221 drm_propose_state_mode_to_string(mode));
1222
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +03001223 wl_list_for_each(pnode, &output->base.paint_node_z_order_list,
1224 z_order_link) {
1225 struct weston_view *ev = pnode->view;
Daniel Stonee404b722019-06-22 18:40:31 +01001226 struct drm_plane *target_plane = NULL;
1227
1228 /* If this view doesn't touch our output at all, there's no
1229 * reason to do anything with it. */
Pekka Paalanenaa2e0b22021-05-03 14:06:55 +03001230 /* TODO: turn this into assert once z_order_list is pruned. */
Daniel Stonee404b722019-06-22 18:40:31 +01001231 if (!(ev->output_mask & (1u << output->base.id)))
1232 continue;
1233
Leandro Ribeiro54293022021-10-12 14:48:36 -03001234 /* Update dmabuf-feedback if needed */
1235 if (ev->surface->dmabuf_feedback)
1236 dmabuf_feedback_maybe_update(b, ev,
1237 pnode->try_view_on_plane_failure_reasons);
1238 pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE;
1239
Daniel Stonee404b722019-06-22 18:40:31 +01001240 /* Test whether this buffer can ever go into a plane:
1241 * non-shm, or small enough to be a cursor.
1242 *
1243 * Also, keep a reference when using the pixman renderer.
1244 * That makes it possible to do a seamless switch to the GL
1245 * renderer and since the pixman renderer keeps a reference
1246 * to the buffer anyway, there is no side effects.
1247 */
1248 if (b->use_pixman ||
Marius Vlad5f6bee42019-09-11 16:41:04 +03001249 (weston_view_has_valid_buffer(ev) &&
Daniel Stonee404b722019-06-22 18:40:31 +01001250 (!wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) ||
1251 (ev->surface->width <= b->cursor_width &&
1252 ev->surface->height <= b->cursor_height))))
1253 ev->surface->keep_buffer = true;
1254 else
1255 ev->surface->keep_buffer = false;
1256
1257 /* This is a bit unpleasant, but lacking a temporary place to
1258 * hang a plane off the view, we have to do a nested walk.
1259 * Our first-order iteration has to be planes rather than
1260 * views, because otherwise we won't reset views which were
1261 * previously on planes to being on the primary plane. */
1262 wl_list_for_each(plane_state, &state->plane_list, link) {
1263 if (plane_state->ev == ev) {
1264 plane_state->ev = NULL;
1265 target_plane = plane_state->plane;
1266 break;
1267 }
1268 }
1269
1270 if (target_plane) {
1271 drm_debug(b, "\t[repaint] view %p on %s plane %lu\n",
1272 ev, plane_type_enums[target_plane->type].name,
1273 (unsigned long) target_plane->plane_id);
1274 weston_view_move_to_plane(ev, &target_plane->base);
1275 } else {
1276 drm_debug(b, "\t[repaint] view %p using renderer "
1277 "composition\n", ev);
1278 weston_view_move_to_plane(ev, primary);
1279 }
1280
1281 if (!target_plane ||
1282 target_plane->type == WDRM_PLANE_TYPE_CURSOR) {
1283 /* cursor plane & renderer involve a copy */
1284 ev->psf_flags = 0;
1285 } else {
1286 /* All other planes are a direct scanout of a
1287 * single client buffer.
1288 */
1289 ev->psf_flags = WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
1290 }
1291 }
1292
1293 /* We rely on output->cursor_view being both an accurate reflection of
1294 * the cursor plane's state, but also being maintained across repaints
1295 * to avoid unnecessary damage uploads, per the comment in
1296 * drm_output_prepare_cursor_view. In the event that we go from having
1297 * a cursor view to not having a cursor view, we need to clear it. */
1298 if (output->cursor_view) {
1299 plane_state =
1300 drm_output_state_get_existing_plane(state,
1301 output->cursor_plane);
1302 if (!plane_state || !plane_state->fb)
Alexandros Frantzis10937fe2021-06-14 13:09:44 +03001303 drm_output_set_cursor_view(output, NULL);
1304 }
1305}
1306
1307static void
1308drm_output_handle_cursor_view_destroy(struct wl_listener *listener, void *data)
1309{
1310 struct drm_output *output =
1311 container_of(listener, struct drm_output,
1312 cursor_view_destroy_listener);
1313
1314 drm_output_set_cursor_view(output, NULL);
1315}
1316
1317/** Set the current cursor view used for an output.
1318 *
1319 * Ensure the stored value will be properly cleared if the view is destroyed.
1320 * The stored cursor view helps avoid unnecessary uploads of cursor data to
1321 * cursor plane buffer objects (see drm_output_prepare_cursor_view).
1322 */
1323void
1324drm_output_set_cursor_view(struct drm_output *output, struct weston_view *ev)
1325{
1326 if (output->cursor_view == ev)
1327 return;
1328
1329 if (output->cursor_view)
1330 wl_list_remove(&output->cursor_view_destroy_listener.link);
1331
1332 output->cursor_view = ev;
1333
1334 if (ev) {
1335 output->cursor_view_destroy_listener.notify =
1336 drm_output_handle_cursor_view_destroy;
1337 wl_signal_add(&ev->destroy_signal,
1338 &output->cursor_view_destroy_listener);
Daniel Stonee404b722019-06-22 18:40:31 +01001339 }
1340}