blob: e429d2ef7f23df6683e52062a70d50565377ccfa [file] [log] [blame]
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001/*
2 * Copyright © 2012-2013 Raspberry Pi Foundation
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#include <stdlib.h>
24#include <assert.h>
25#include <string.h>
26
27#include "config.h"
28
29#ifdef HAVE_BCM_HOST
30# include <bcm_host.h>
31#else
32# include "rpi-bcm-stubs.h"
33#endif
34
35#include "compositor.h"
36#include "rpi-renderer.h"
37
38/*
39 * Dispmanx API offers alpha-blended overlays for hardware compositing.
40 * The final composite consists of dispmanx elements, and their contents:
41 * the dispmanx resource assigned to the element. The elements may be
42 * scanned out directly, or composited to a temporary surface, depending on
43 * how the firmware decides to handle the scene. Updates to multiple elements
44 * may be queued in a single dispmanx update object, resulting in atomic and
45 * vblank synchronized display updates.
46 *
47 * To avoid tearing and display artifacts, the current dispmanx resource in a
48 * dispmanx element must not be touched. Therefore each element must be
49 * double-buffered, using two resources, the front and the back. While a
50 * dispmanx update is running, the both resources must be considered in use.
51 *
52 * A resource may be destroyed only, when the update removing the element has
53 * completed. Otherwise you risk showing an incomplete composition.
54 */
55
56#ifndef ELEMENT_CHANGE_LAYER
57/* copied from interface/vmcs_host/vc_vchi_dispmanx.h of userland.git */
58#define ELEMENT_CHANGE_LAYER (1<<0)
59#define ELEMENT_CHANGE_OPACITY (1<<1)
60#define ELEMENT_CHANGE_DEST_RECT (1<<2)
61#define ELEMENT_CHANGE_SRC_RECT (1<<3)
62#define ELEMENT_CHANGE_MASK_RESOURCE (1<<4)
63#define ELEMENT_CHANGE_TRANSFORM (1<<5)
64#endif
65
66#if 0
67#define DBG(...) \
68 weston_log(__VA_ARGS__)
69#else
70#define DBG(...) do {} while (0)
71#endif
72
73/* If we had a fully featured vc_dispmanx_resource_write_data()... */
74/*#define HAVE_RESOURCE_WRITE_DATA_RECT 1*/
75
76struct rpi_resource {
77 DISPMANX_RESOURCE_HANDLE_T handle;
78 int width;
79 int height; /* height of the image (valid pixel data) */
80 int stride; /* bytes */
81 int buffer_height; /* height of the buffer */
82 VC_IMAGE_TYPE_T ifmt;
83};
84
85struct rpir_output;
86
87struct rpir_surface {
88 struct weston_surface *surface;
89
90 /* If link is empty, the surface is guaranteed to not be on screen,
91 * i.e. updates removing Elements have completed.
92 */
93 struct wl_list link;
94
95 DISPMANX_ELEMENT_HANDLE_T handle;
96 int layer;
97 int need_swap;
98 int single_buffer;
99
100 struct rpi_resource resources[2];
101 struct rpi_resource *front;
102 struct rpi_resource *back;
103 pixman_region32_t prev_damage;
104
105 struct weston_buffer_reference buffer_ref;
106};
107
108struct rpir_output {
109 DISPMANX_DISPLAY_HANDLE_T display;
110
111 DISPMANX_UPDATE_HANDLE_T update;
112 struct weston_matrix matrix;
113
114 /* all Elements currently on screen */
115 struct wl_list surface_list; /* struct rpir_surface::link */
116
117 /* Elements just removed, waiting for update completion */
118 struct wl_list surface_cleanup_list; /* struct rpir_surface::link */
119
120 struct rpi_resource capture_buffer;
121 uint8_t *capture_data;
122};
123
124struct rpi_renderer {
125 struct weston_renderer base;
126
127 int single_buffer;
128};
129
130static inline struct rpir_surface *
131to_rpir_surface(struct weston_surface *surface)
132{
133 return surface->renderer_state;
134}
135
136static inline struct rpir_output *
137to_rpir_output(struct weston_output *output)
138{
139 return output->renderer_state;
140}
141
142static inline struct rpi_renderer *
143to_rpi_renderer(struct weston_compositor *compositor)
144{
145 return container_of(compositor->renderer, struct rpi_renderer, base);
146}
147
148static inline int
149int_max(int a, int b)
150{
151 return a > b ? a : b;
152}
153
154static inline void
155int_swap(int *a, int *b)
156{
157 int tmp = *a;
158 *a = *b;
159 *b = tmp;
160}
161
162static uint8_t
163float2uint8(float f)
164{
165 int v = roundf(f * 255.0f);
166
167 return v < 0 ? 0 : (v > 255 ? 255 : v);
168}
169
170static void
171rpi_resource_init(struct rpi_resource *resource)
172{
173 resource->handle = DISPMANX_NO_HANDLE;
174}
175
176static void
177rpi_resource_release(struct rpi_resource *resource)
178{
179 if (resource->handle == DISPMANX_NO_HANDLE)
180 return;
181
182 vc_dispmanx_resource_delete(resource->handle);
183 DBG("resource %p release\n", resource);
184 resource->handle = DISPMANX_NO_HANDLE;
185}
186
187static int
188rpi_resource_realloc(struct rpi_resource *resource, VC_IMAGE_TYPE_T ifmt,
189 int width, int height, int stride, int buffer_height)
190{
191 uint32_t dummy;
192
193 if (resource->handle != DISPMANX_NO_HANDLE &&
194 resource->width == width &&
195 resource->height == height &&
196 resource->stride == stride &&
197 resource->buffer_height == buffer_height &&
198 resource->ifmt == ifmt)
199 return 0;
200
201 rpi_resource_release(resource);
202
203 /* NOTE: if stride is not a multiple of 16 pixels in bytes,
204 * the vc_image_* functions may break. Dispmanx elements
205 * should be fine, though. Buffer_height probably has similar
206 * constraints, too.
207 */
208 resource->handle =
209 vc_dispmanx_resource_create(ifmt,
210 width | (stride << 16),
211 height | (buffer_height << 16),
212 &dummy);
213 if (resource->handle == DISPMANX_NO_HANDLE)
214 return -1;
215
216 resource->width = width;
217 resource->height = height;
218 resource->stride = stride;
219 resource->buffer_height = buffer_height;
220 resource->ifmt = ifmt;
221 DBG("resource %p alloc\n", resource);
222 return 1;
223}
224
225/* A firmware workaround for broken ALPHA_PREMULT + ALPHA_MIX hardware. */
226#define PREMULT_ALPHA_FLAG (1 << 31)
227
228static VC_IMAGE_TYPE_T
229shm_buffer_get_vc_format(struct wl_buffer *buffer)
230{
231 switch (wl_shm_buffer_get_format(buffer)) {
232 case WL_SHM_FORMAT_XRGB8888:
233 return VC_IMAGE_XRGB8888;
234 case WL_SHM_FORMAT_ARGB8888:
235 return VC_IMAGE_ARGB8888 | PREMULT_ALPHA_FLAG;
236 default:
237 /* invalid format */
238 return VC_IMAGE_MIN;
239 }
240}
241
242static int
243rpi_resource_update(struct rpi_resource *resource, struct wl_buffer *buffer,
244 pixman_region32_t *region)
245{
246 pixman_region32_t write_region;
247 pixman_box32_t *r;
248 VC_RECT_T rect;
249 VC_IMAGE_TYPE_T ifmt;
250 uint32_t *pixels;
251 int width;
252 int height;
253 int stride;
254 int ret;
255#ifdef HAVE_RESOURCE_WRITE_DATA_RECT
256 int n;
257#endif
258
259 if (!buffer)
260 return -1;
261
262 ifmt = shm_buffer_get_vc_format(buffer);
263 width = wl_shm_buffer_get_width(buffer);
264 height = wl_shm_buffer_get_height(buffer);
265 stride = wl_shm_buffer_get_stride(buffer);
266 pixels = wl_shm_buffer_get_data(buffer);
267
268 ret = rpi_resource_realloc(resource, ifmt & ~PREMULT_ALPHA_FLAG,
269 width, height, stride, height);
270 if (ret < 0)
271 return -1;
272
273 pixman_region32_init_rect(&write_region, 0, 0, width, height);
274 if (ret == 0)
275 pixman_region32_intersect(&write_region,
276 &write_region, region);
277
278#ifdef HAVE_RESOURCE_WRITE_DATA_RECT
279 /* XXX: Can this do a format conversion, so that scanout does not have to? */
280 r = pixman_region32_rectangles(&write_region, &n);
281 while (n--) {
282 vc_dispmanx_rect_set(&rect, r[n].x1, r[n].y1,
283 r[n].x2 - r[n].x1, r[n].y2 - r[n].y1);
284
285 ret = vc_dispmanx_resource_write_data_rect(resource->handle,
286 ifmt, stride,
287 pixels, &rect,
288 rect.x, rect.y);
289 DBG("%s: %p %ux%u@%u,%u, ret %d\n", __func__, resource,
290 rect.width, rect.height, rect.x, rect.y, ret);
291 if (ret)
292 break;
293 }
294#else
295 /* vc_dispmanx_resource_write_data() ignores ifmt,
296 * rect.x, rect.width, and uses stride only for computing
297 * the size of the transfer as rect.height * stride.
298 * Therefore we can only write rows starting at x=0.
299 * To be able to write more than one scanline at a time,
300 * the resource must have been created with the same stride
301 * as used here, and we must write full scanlines.
302 */
303
304 r = pixman_region32_extents(&write_region);
305 vc_dispmanx_rect_set(&rect, 0, r->y1, width, r->y2 - r->y1);
306 ret = vc_dispmanx_resource_write_data(resource->handle,
307 ifmt, stride, pixels, &rect);
308 DBG("%s: %p %ux%u@%u,%u, ret %d\n", __func__, resource,
309 width, r->y2 - r->y1, 0, r->y1, ret);
310#endif
311
312 pixman_region32_fini(&write_region);
313
314 return ret ? -1 : 0;
315}
316
317static struct rpir_surface *
318rpir_surface_create(struct rpi_renderer *renderer)
319{
320 struct rpir_surface *surface;
321
322 surface = calloc(1, sizeof *surface);
323 if (!surface)
324 return NULL;
325
326 wl_list_init(&surface->link);
327 surface->single_buffer = renderer->single_buffer;
328 surface->handle = DISPMANX_NO_HANDLE;
329 rpi_resource_init(&surface->resources[0]);
330 rpi_resource_init(&surface->resources[1]);
331 surface->front = &surface->resources[0];
332 if (surface->single_buffer)
333 surface->back = &surface->resources[0];
334 else
335 surface->back = &surface->resources[1];
336
337 pixman_region32_init(&surface->prev_damage);
338
339 return surface;
340}
341
342static void
343rpir_surface_destroy(struct rpir_surface *surface)
344{
345 wl_list_remove(&surface->link);
346
347 if (surface->handle != DISPMANX_NO_HANDLE)
348 weston_log("ERROR rpi: destroying on-screen element\n");
349
350 pixman_region32_fini(&surface->prev_damage);
351 rpi_resource_release(&surface->resources[0]);
352 rpi_resource_release(&surface->resources[1]);
353 DBG("rpir_surface %p destroyed (%u)\n", surface, surface->handle);
354
355 free(surface);
356}
357
358static int
359rpir_surface_damage(struct rpir_surface *surface, struct wl_buffer *buffer,
360 pixman_region32_t *damage)
361{
362 pixman_region32_t upload;
363 int ret;
364
365 if (!pixman_region32_not_empty(damage))
366 return 0;
367
368 DBG("rpir_surface %p update resource %p\n", surface, surface->back);
369
370 /* XXX: todo: if no surface->handle, update front buffer directly
371 * to avoid creating a new back buffer */
372 if (surface->single_buffer) {
373 ret = rpi_resource_update(surface->front, buffer, damage);
374 } else {
375 pixman_region32_init(&upload);
376 pixman_region32_union(&upload, &surface->prev_damage, damage);
377 ret = rpi_resource_update(surface->back, buffer, &upload);
378 pixman_region32_fini(&upload);
379 }
380
381 pixman_region32_copy(&surface->prev_damage, damage);
382 surface->need_swap = 1;
383
384 return ret;
385}
386
387static void
388matrix_type_str(struct weston_matrix *matrix, char *buf, int len)
389{
390 static const char types[33] = "TSRO";
391 unsigned mask = matrix->type;
392 int i = 0;
393
394 while (mask && i < len - 1) {
395 if (mask & (1u << i))
396 *buf++ = types[i];
397 mask &= ~(1u << i);
398 i++;
399 }
400 *buf = '\0';
401}
402
403static void
404log_print_matrix(struct weston_matrix *matrix)
405{
406 char typestr[6];
407 float *d = matrix->d;
408
409 matrix_type_str(matrix, typestr, sizeof typestr);
410 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
411 d[0], d[4], d[8], d[12]);
412 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
413 d[1], d[5], d[9], d[13]);
414 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
415 d[2], d[6], d[10], d[14]);
416 weston_log_continue("%14.6e %14.6e %14.6e %14.6e type: %s\n",
417 d[3], d[7], d[11], d[15], typestr);
418}
419
420static void
421warn_bad_matrix(struct weston_matrix *total, struct weston_matrix *output,
422 struct weston_matrix *surface)
423{
424 static int n_warn;
425 char typestr[6];
426
427 if (n_warn++ == 10)
428 weston_log("%s: not showing more warnings\n", __func__);
429
430 if (n_warn > 10)
431 return;
432
433 weston_log("%s: warning: total transformation is not renderable:\n",
434 __func__);
435 log_print_matrix(total);
436
437 matrix_type_str(surface, typestr, sizeof typestr);
438 weston_log_continue("surface matrix type: %s\n", typestr);
439 matrix_type_str(output, typestr, sizeof typestr);
440 weston_log_continue("output matrix type: %s\n", typestr);
441}
442
443/*#define SURFACE_TRANSFORM */
444
445static int
446rpir_surface_compute_rects(struct rpir_surface *surface,
447 VC_RECT_T *src_rect, VC_RECT_T *dst_rect,
448 VC_IMAGE_TRANSFORM_T *flipmask)
449{
450 struct weston_output *output_base = surface->surface->output;
451 struct rpir_output *output = to_rpir_output(output_base);
452 struct weston_matrix matrix = surface->surface->transform.matrix;
453 VC_IMAGE_TRANSFORM_T flipt = 0;
454 int src_x, src_y;
455 int dst_x, dst_y;
456 int src_width, src_height;
457 int dst_width, dst_height;
458 struct weston_vector p1 = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
459 struct weston_vector p2 = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
460 int t;
461 int over;
462
463 /* XXX: take buffer transform into account */
464
465 /* src is in 16.16, dst is in 32.0 fixed point.
466 * Negative values are not allowed in VC_RECT_T.
467 * Clip size to output boundaries, firmware ignores
468 * huge elements like 8192x8192.
469 */
470
471 src_x = 0 << 16;
472 src_y = 0 << 16;
473 src_width = surface->front->width << 16;
474 src_height = surface->front->height << 16;
475
476 weston_matrix_multiply(&matrix, &output->matrix);
477
478#ifdef SURFACE_TRANSFORM
479 if (matrix.type >= WESTON_MATRIX_TRANSFORM_OTHER) {
480#else
481 if (matrix.type >= WESTON_MATRIX_TRANSFORM_ROTATE) {
482#endif
483 warn_bad_matrix(&matrix, &output->matrix,
484 &surface->surface->transform.matrix);
485 } else {
486 if (matrix.type & WESTON_MATRIX_TRANSFORM_ROTATE) {
487 if (fabsf(matrix.d[0]) < 1e-4f &&
488 fabsf(matrix.d[5]) < 1e-4f) {
489 flipt |= TRANSFORM_TRANSPOSE;
490 } else if (fabsf(matrix.d[1]) < 1e-4 &&
491 fabsf(matrix.d[4]) < 1e-4) {
492 /* no transpose */
493 } else {
494 warn_bad_matrix(&matrix, &output->matrix,
495 &surface->surface->transform.matrix);
496 }
497 }
498 }
499
500 p2.f[0] = surface->surface->geometry.width;
501 p2.f[1] = surface->surface->geometry.height;
502
503 /* transform top-left and bot-right corner into screen coordinates */
504 weston_matrix_transform(&matrix, &p1);
505 weston_matrix_transform(&matrix, &p2);
506
507 /* Compute the destination rectangle on screen, converting
508 * negative dimensions to flips.
509 */
510
511 dst_width = round(p2.f[0] - p1.f[0]);
512 if (dst_width < 0) {
513 dst_x = round(p2.f[0]);
514 dst_width = -dst_width;
515
516 if (!(flipt & TRANSFORM_TRANSPOSE))
517 flipt |= TRANSFORM_HFLIP;
518 else
519 flipt |= TRANSFORM_VFLIP;
520 } else {
521 dst_x = round(p1.f[0]);
522 }
523
524 dst_height = round(p2.f[1] - p1.f[1]);
525 if (dst_height < 0) {
526 dst_y = round(p2.f[1]);
527 dst_height = -dst_height;
528
529 if (!(flipt & TRANSFORM_TRANSPOSE))
530 flipt |= TRANSFORM_VFLIP;
531 else
532 flipt |= TRANSFORM_HFLIP;
533 } else {
534 dst_y = round(p1.f[1]);
535 }
536
537 if (dst_width == 0 || dst_height == 0) {
538 DBG("ignored, zero surface area before clipping\n");
539 return -1;
540 }
541
542#ifdef SURFACE_TRANSFORM
543 /* Dispmanx works as if you flipped the whole screen, when
544 * you flip an element. But, we want to flip an element in place.
545 * XXX: fixme
546 */
547 if (flipt & TRANSFORM_HFLIP)
548 dst_x = output_base->width - dst_x;
549 if (flipt & TRANSFORM_VFLIP)
550 dst_y = output_base->height - dst_y;
551 if (flipt & TRANSFORM_TRANSPOSE) {
552 int_swap(&dst_x, &dst_y);
553 int_swap(&dst_width, &dst_height);
554 }
555#else
556 switch (output_base->transform) {
557 case WL_OUTPUT_TRANSFORM_FLIPPED:
558 flipt = TRANSFORM_HFLIP;
559 break;
560 case WL_OUTPUT_TRANSFORM_NORMAL:
561 flipt = 0;
562 break;
563
564 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
565 flipt = TRANSFORM_HFLIP | TRANSFORM_VFLIP | TRANSFORM_TRANSPOSE;
566 break;
567 case WL_OUTPUT_TRANSFORM_90:
568 flipt = TRANSFORM_VFLIP | TRANSFORM_TRANSPOSE;
569 break;
570 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
571 flipt = TRANSFORM_VFLIP;
572 break;
573 case WL_OUTPUT_TRANSFORM_180:
574 flipt = TRANSFORM_HFLIP | TRANSFORM_VFLIP;
575 break;
576
577 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
578 flipt = TRANSFORM_TRANSPOSE;
579 break;
580 case WL_OUTPUT_TRANSFORM_270:
581 flipt = TRANSFORM_HFLIP | TRANSFORM_TRANSPOSE;
582 break;
583 default:
584 break;
585 }
586#endif
587
588 /* clip destination rectangle to screen dimensions */
589
590 if (dst_x < 0) {
591 t = (int64_t)dst_x * src_width / dst_width;
592 src_width += t;
593 dst_width += dst_x;
594 src_x -= t;
595 dst_x = 0;
596 }
597
598 if (dst_y < 0) {
599 t = (int64_t)dst_y * src_height / dst_height;
600 src_height += t;
601 dst_height += dst_y;
602 src_y -= t;
603 dst_y = 0;
604 }
605
606 over = dst_x + dst_width - output_base->width;
607 if (over > 0) {
608 t = (int64_t)over * src_width / dst_width;
609 src_width -= t;
610 dst_width -= over;
611 }
612
613 over = dst_y + dst_height - output_base->height;
614 if (over > 0) {
615 t = (int64_t)over * src_height / dst_height;
616 src_height -= t;
617 dst_height -= over;
618 }
619
620 src_width = int_max(src_width, 0);
621 src_height = int_max(src_height, 0);
622
623 DBG("rpir_surface %p %dx%d: p1 %f, %f; p2 %f, %f\n", surface,
624 surface->surface->geometry.width,
625 surface->surface->geometry.height,
626 p1.f[0], p1.f[1], p2.f[0], p2.f[1]);
627 DBG("src rect %d;%d, %d;%d, %d;%dx%d;%d\n",
628 src_x >> 16, src_x & 0xffff,
629 src_y >> 16, src_y & 0xffff,
630 src_width >> 16, src_width & 0xffff,
631 src_height >> 16, src_height & 0xffff);
632 DBG("dest rect %d, %d, %dx%d%s%s%s\n",
633 dst_x, dst_y, dst_width, dst_height,
634 (flipt & TRANSFORM_HFLIP) ? " hflip" : "",
635 (flipt & TRANSFORM_VFLIP) ? " vflip" : "",
636 (flipt & TRANSFORM_TRANSPOSE) ? " transp" : "");
637
638 assert(src_x >= 0);
639 assert(src_y >= 0);
640 assert(dst_x >= 0);
641 assert(dst_y >= 0);
642
643 if (dst_width < 1 || dst_height < 1) {
644 DBG("ignored, zero surface area after clipping\n");
645 return -1;
646 }
647
648 vc_dispmanx_rect_set(src_rect, src_x, src_y, src_width, src_height);
649 vc_dispmanx_rect_set(dst_rect, dst_x, dst_y, dst_width, dst_height);
650 *flipmask = flipt;
651
652 return 0;
653}
654
655static DISPMANX_TRANSFORM_T
656vc_image2dispmanx_transform(VC_IMAGE_TRANSFORM_T t)
657{
658 /* XXX: uhh, are these right? */
659 switch (t) {
660 case VC_IMAGE_ROT0:
661 return DISPMANX_NO_ROTATE;
662 case VC_IMAGE_MIRROR_ROT0:
663 return DISPMANX_FLIP_HRIZ;
664 case VC_IMAGE_MIRROR_ROT180:
665 return DISPMANX_FLIP_VERT;
666 case VC_IMAGE_ROT180:
667 return DISPMANX_ROTATE_180;
668 case VC_IMAGE_MIRROR_ROT90:
669 return DISPMANX_ROTATE_90 | DISPMANX_FLIP_HRIZ;
670 case VC_IMAGE_ROT270:
671 return DISPMANX_ROTATE_270;
672 case VC_IMAGE_ROT90:
673 return DISPMANX_ROTATE_90;
674 case VC_IMAGE_MIRROR_ROT270:
675 return DISPMANX_ROTATE_270 | DISPMANX_FLIP_VERT;
676 default:
677 assert(0 && "bad VC_IMAGE_TRANSFORM_T");
678 return DISPMANX_NO_ROTATE;
679 }
680}
681
682static int
683rpir_surface_dmx_add(struct rpir_surface *surface, struct rpir_output *output,
684 DISPMANX_UPDATE_HANDLE_T update, int layer)
685{
686 /* Do not use DISPMANX_FLAGS_ALPHA_PREMULT here.
687 * If you define PREMULT and ALPHA_MIX, the hardware will not
688 * multiply the source color with the element alpha, leading to
689 * bad colors. Instead, we define PREMULT during pixel data upload.
690 */
691 VC_DISPMANX_ALPHA_T alphasetup = {
692 DISPMANX_FLAGS_ALPHA_FROM_SOURCE |
693 DISPMANX_FLAGS_ALPHA_MIX,
694 float2uint8(surface->surface->alpha), /* opacity 0-255 */
695 0 /* mask resource handle */
696 };
697 VC_RECT_T dst_rect;
698 VC_RECT_T src_rect;
699 VC_IMAGE_TRANSFORM_T flipmask;
700 int ret;
701
702 ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect,
703 &flipmask);
704 if (ret < 0)
705 return 0;
706
707 surface->handle = vc_dispmanx_element_add(
708 update,
709 output->display,
710 layer,
711 &dst_rect,
712 surface->front->handle,
713 &src_rect,
714 DISPMANX_PROTECTION_NONE,
715 &alphasetup,
716 NULL /* clamp */,
717 vc_image2dispmanx_transform(flipmask));
718 DBG("rpir_surface %p add %u, alpha %f\n", surface, surface->handle,
719 surface->surface->alpha);
720
721 if (surface->handle == DISPMANX_NO_HANDLE)
722 return -1;
723
724 return 1;
725}
726
727static void
728rpir_surface_dmx_swap(struct rpir_surface *surface,
729 DISPMANX_UPDATE_HANDLE_T update)
730{
731 VC_RECT_T rect;
732 pixman_box32_t *r;
733
734 /* XXX: skip, iff resource was not reallocated, and single-buffering */
735 vc_dispmanx_element_change_source(update, surface->handle,
736 surface->front->handle);
737
738 /* This is current damage now, after rpir_surface_damage() */
739 r = pixman_region32_extents(&surface->prev_damage);
740
741 vc_dispmanx_rect_set(&rect, r->x1, r->y1,
742 r->x2 - r->x1, r->y2 - r->y1);
743 vc_dispmanx_element_modified(update, surface->handle, &rect);
744 DBG("rpir_surface %p swap\n", surface);
745}
746
747static int
748rpir_surface_dmx_move(struct rpir_surface *surface,
749 DISPMANX_UPDATE_HANDLE_T update, int layer)
750{
751 uint8_t alpha = float2uint8(surface->surface->alpha);
752 VC_RECT_T dst_rect;
753 VC_RECT_T src_rect;
754 VC_IMAGE_TRANSFORM_T flipmask;
755 int ret;
756
757 /* XXX: return early, if all attributes stay the same */
758
759 ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect,
760 &flipmask);
761 if (ret < 0)
762 return 0;
763
764 ret = vc_dispmanx_element_change_attributes(
765 update,
766 surface->handle,
767 ELEMENT_CHANGE_LAYER |
768 ELEMENT_CHANGE_OPACITY |
769 ELEMENT_CHANGE_TRANSFORM |
770 ELEMENT_CHANGE_DEST_RECT |
771 ELEMENT_CHANGE_SRC_RECT,
772 layer,
773 alpha,
774 &dst_rect,
775 &src_rect,
776 DISPMANX_NO_HANDLE,
777 /* This really is DISPMANX_TRANSFORM_T, no matter
778 * what the header says. */
779 vc_image2dispmanx_transform(flipmask));
780 DBG("rpir_surface %p move\n", surface);
781
782 if (ret)
783 return -1;
784
785 return 1;
786}
787
788static void
789rpir_surface_dmx_remove(struct rpir_surface *surface,
790 DISPMANX_UPDATE_HANDLE_T update)
791{
792 if (surface->handle == DISPMANX_NO_HANDLE)
793 return;
794
795 vc_dispmanx_element_remove(update, surface->handle);
796 DBG("rpir_surface %p remove %u\n", surface, surface->handle);
797 surface->handle = DISPMANX_NO_HANDLE;
798}
799
800static void
801rpir_surface_swap_pointers(struct rpir_surface *surface)
802{
803 struct rpi_resource *tmp;
804
805 tmp = surface->front;
806 surface->front = surface->back;
807 surface->back = tmp;
808 surface->need_swap = 0;
809 DBG("new back %p, new front %p\n", surface->back, surface->front);
810}
811
812static int
813is_surface_not_visible(struct weston_surface *surface)
814{
815 /* Return true, if surface is guaranteed to be totally obscured. */
816 int ret;
817 pixman_region32_t unocc;
818
819 pixman_region32_init(&unocc);
820 pixman_region32_subtract(&unocc, &surface->transform.boundingbox,
821 &surface->clip);
822 ret = !pixman_region32_not_empty(&unocc);
823 pixman_region32_fini(&unocc);
824
825 return ret;
826}
827
828static void
829rpir_surface_update(struct rpir_surface *surface, struct rpir_output *output,
830 DISPMANX_UPDATE_HANDLE_T update, int layer)
831{
832 int need_swap = surface->need_swap;
833 int ret;
834 int obscured;
835
836 if (need_swap)
837 rpir_surface_swap_pointers(surface);
838
839 obscured = is_surface_not_visible(surface->surface);
840 if (obscured) {
841 DBG("rpir_surface %p totally obscured.\n", surface);
842
843 wl_list_remove(&surface->link);
844 if (surface->handle == DISPMANX_NO_HANDLE) {
845 wl_list_init(&surface->link);
846 } else {
847 rpir_surface_dmx_remove(surface, update);
848 wl_list_insert(&output->surface_cleanup_list,
849 &surface->link);
850 }
851
852 goto out;
853 }
854
855 if (surface->handle == DISPMANX_NO_HANDLE) {
856 ret = rpir_surface_dmx_add(surface, output, update, layer);
857 if (ret == 0) {
858 wl_list_remove(&surface->link);
859 wl_list_init(&surface->link);
860 } else if (ret < 0) {
861 weston_log("ERROR rpir_surface_dmx_add() failed.\n");
862 }
863 } else {
864 if (need_swap)
865 rpir_surface_dmx_swap(surface, update);
866
867 ret = rpir_surface_dmx_move(surface, update, layer);
868 if (ret == 0) {
869 rpir_surface_dmx_remove(surface, update);
870
871 wl_list_remove(&surface->link);
872 wl_list_insert(&output->surface_cleanup_list,
873 &surface->link);
874 } else if (ret < 0) {
875 weston_log("ERROR rpir_surface_dmx_move() failed.\n");
876 }
877 }
878
879out:
880 surface->layer = layer;
881}
882
883static int
884rpi_renderer_read_pixels(struct weston_output *base,
885 pixman_format_code_t format, void *pixels,
886 uint32_t x, uint32_t y,
887 uint32_t width, uint32_t height)
888{
889 struct rpir_output *output = to_rpir_output(base);
890 struct rpi_resource *buffer = &output->capture_buffer;
891 VC_RECT_T rect;
892 uint32_t fb_width, fb_height;
893 uint32_t dst_pitch;
894 uint32_t i;
895 int ret;
896
897 fb_width = base->current->width;
898 fb_height = base->current->height;
899
900 DBG("%s(%u, %u, %u, %u), resource %p\n", __func__,
901 x, y, width, height, buffer);
902
903 if (format != PIXMAN_a8r8g8b8) {
904 weston_log("rpi-renderer error: bad read_format\n");
905 return -1;
906 }
907
908 dst_pitch = fb_width * 4;
909
910 if (buffer->handle == DISPMANX_NO_HANDLE) {
911 free(output->capture_data);
912 output->capture_data = NULL;
913
914 ret = rpi_resource_realloc(buffer, VC_IMAGE_ARGB8888,
915 fb_width, fb_height,
916 dst_pitch, fb_height);
917 if (ret < 0) {
918 weston_log("rpi-renderer error: "
919 "allocating read buffer failed\n");
920 return -1;
921 }
922
923 ret = vc_dispmanx_snapshot(output->display, buffer->handle,
924 VC_IMAGE_ROT0);
925 if (ret) {
926 weston_log("rpi-renderer error: "
927 "vc_dispmanx_snapshot returned %d\n", ret);
928 return -1;
929 }
930 DBG("%s: snapshot done.\n", __func__);
931 }
932
933 /*
934 * If vc_dispmanx_resource_read_data was able to read sub-rectangles,
935 * we could read directly into 'pixels'. But it cannot, it does not
936 * use rect.x or rect.width, and does this:
937 * host_start = (uint8_t *)dst_address + (dst_pitch * p_rect->y);
938 * In other words, it is only good for reading the full buffer in
939 * one go.
940 */
941 vc_dispmanx_rect_set(&rect, 0, 0, fb_width, fb_height);
942
943 if (x == 0 && y == 0 && width == fb_width && height == fb_height) {
944 ret = vc_dispmanx_resource_read_data(buffer->handle, &rect,
945 pixels, dst_pitch);
946 if (ret) {
947 weston_log("rpi-renderer error: "
948 "resource_read_data returned %d\n", ret);
949 return -1;
950 }
951 DBG("%s: full frame done.\n", __func__);
952 return 0;
953 }
954
955 if (!output->capture_data) {
956 output->capture_data = malloc(fb_height * dst_pitch);
957 if (!output->capture_data) {
958 weston_log("rpi-renderer error: "
959 "out of memory\n");
960 return -1;
961 }
962
963 ret = vc_dispmanx_resource_read_data(buffer->handle, &rect,
964 output->capture_data,
965 dst_pitch);
966 if (ret) {
967 weston_log("rpi-renderer error: "
968 "resource_read_data returned %d\n", ret);
969 return -1;
970 }
971 }
972
973 for (i = 0; i < height; i++) {
974 uint8_t *src = output->capture_data +
975 (y + i) * dst_pitch + x * 4;
976 uint8_t *dst = (uint8_t *)pixels + i * width * 4;
977 memcpy(dst, src, width * 4);
978 }
979
980 return 0;
981}
982
983static void
984rpir_output_dmx_remove_all(struct rpir_output *output,
985 DISPMANX_UPDATE_HANDLE_T update)
986{
987 struct rpir_surface *surface;
988
989 while (!wl_list_empty(&output->surface_list)) {
990 surface = container_of(output->surface_list.next,
991 struct rpir_surface, link);
992 rpir_surface_dmx_remove(surface, update);
993
994 wl_list_remove(&surface->link);
995 wl_list_insert(&output->surface_cleanup_list, &surface->link);
996 }
997}
998
999static void
1000output_compute_matrix(struct weston_output *base)
1001{
1002 struct rpir_output *output = to_rpir_output(base);
1003 struct weston_matrix *matrix = &output->matrix;
1004 const float half_w = 0.5f * base->width;
1005 const float half_h = 0.5f * base->height;
1006 float mag;
1007 float dx, dy;
1008
1009 weston_matrix_init(matrix);
1010 weston_matrix_translate(matrix, -base->x, -base->y, 0.0f);
1011
1012#ifdef SURFACE_TRANSFORM
1013 weston_matrix_translate(matrix, -half_w, -half_h, 0.0f);
1014 switch (base->transform) {
1015 case WL_OUTPUT_TRANSFORM_FLIPPED:
1016 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1017 case WL_OUTPUT_TRANSFORM_NORMAL:
1018 /* weston_matrix_rotate_xy(matrix, 1.0f, 0.0f); no-op */
1019 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1020 break;
1021
1022 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
1023 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1024 case WL_OUTPUT_TRANSFORM_90:
1025 weston_matrix_rotate_xy(matrix, 0.0f, 1.0f);
1026 weston_matrix_translate(matrix, half_h, half_w, 0.0f);
1027 break;
1028
1029 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
1030 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1031 case WL_OUTPUT_TRANSFORM_180:
1032 weston_matrix_rotate_xy(matrix, -1.0f, 0.0f);
1033 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1034 break;
1035
1036 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
1037 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1038 case WL_OUTPUT_TRANSFORM_270:
1039 weston_matrix_rotate_xy(matrix, 0.0f, -1.0f);
1040 weston_matrix_translate(matrix, half_h, half_w, 0.0f);
1041 break;
1042
1043 default:
1044 break;
1045 }
1046#endif
1047
1048 if (base->zoom.active) {
1049 /* The base->zoom stuff is in GL coordinate system */
1050 mag = 1.0f / (1.0f - base->zoom.spring_z.current);
1051 dx = -(base->zoom.trans_x + 1.0f) * half_w;
1052 dy = -(base->zoom.trans_y + 1.0f) * half_h;
1053 weston_matrix_translate(matrix, dx, dy, 0.0f);
1054 weston_matrix_scale(matrix, mag, mag, 1.0f);
1055 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1056 }
1057}
1058
1059/* Note: this won't work right for multiple outputs. A DispmanX Element
1060 * is tied to one DispmanX Display, i.e. output.
1061 */
1062static void
1063rpi_renderer_repaint_output(struct weston_output *base,
1064 pixman_region32_t *output_damage)
1065{
1066 struct weston_compositor *compositor = base->compositor;
1067 struct rpir_output *output = to_rpir_output(base);
1068 struct weston_surface *ws;
1069 struct rpir_surface *surface;
1070 struct wl_list done_list;
1071 int layer = 1;
1072
1073 assert(output->update != DISPMANX_NO_HANDLE);
1074
1075 output_compute_matrix(base);
1076
1077 rpi_resource_release(&output->capture_buffer);
1078 free(output->capture_data);
1079 output->capture_data = NULL;
1080
1081 /* update all renderable surfaces */
1082 wl_list_init(&done_list);
1083 wl_list_for_each_reverse(ws, &compositor->surface_list, link) {
1084 if (ws->plane != &compositor->primary_plane)
1085 continue;
1086
1087 surface = to_rpir_surface(ws);
1088 assert(!wl_list_empty(&surface->link) ||
1089 surface->handle == DISPMANX_NO_HANDLE);
1090
1091 wl_list_remove(&surface->link);
1092 wl_list_insert(&done_list, &surface->link);
1093 rpir_surface_update(surface, output, output->update, layer++);
1094 }
1095
1096 /* Remove all surfaces that are still on screen, but were
1097 * not rendered this time.
1098 */
1099 rpir_output_dmx_remove_all(output, output->update);
1100
1101 wl_list_insert_list(&output->surface_list, &done_list);
1102 output->update = DISPMANX_NO_HANDLE;
1103
1104 /* The frame_signal is emitted in rpi_renderer_finish_frame(),
1105 * so that the firmware can capture the up-to-date contents.
1106 */
1107}
1108
1109static void
1110rpi_renderer_flush_damage(struct weston_surface *base)
1111{
1112 /* Called for every surface just before repainting it, if
1113 * having an shm buffer.
1114 */
1115 struct rpir_surface *surface = to_rpir_surface(base);
1116 struct wl_buffer *buffer = surface->buffer_ref.buffer;
1117 int ret;
1118
1119 assert(buffer);
1120 assert(wl_buffer_is_shm(buffer));
1121
1122 ret = rpir_surface_damage(surface, buffer, &base->damage);
1123 if (ret)
1124 weston_log("%s error: updating Dispmanx resource failed.\n",
1125 __func__);
1126
1127 weston_buffer_reference(&surface->buffer_ref, NULL);
1128}
1129
1130static void
1131rpi_renderer_attach(struct weston_surface *base, struct wl_buffer *buffer)
1132{
1133 /* Called every time a client commits an attach. */
1134 static int warned;
1135 struct rpir_surface *surface = to_rpir_surface(base);
1136
1137 assert(surface);
1138 if (!surface)
1139 return;
1140
1141 if (buffer && !wl_buffer_is_shm(buffer) && !warned) {
1142 weston_log("Error: non-wl_shm buffers not supported.\n");
1143 warned = 1;
1144 return;
1145 }
1146
1147 weston_buffer_reference(&surface->buffer_ref, buffer);
1148
1149 /* XXX: need to check if in middle of update
1150 if (!buffer && !surface->single_buffer)
1151 rpi_resource_release(surface->back); */
1152
1153 /* XXX: cannot do this, if middle of an update
1154 if (surface->handle == DISPMANX_NO_HANDLE)
1155 rpi_resource_release(surface->front); */
1156
1157 /* If buffer is NULL, Weston core unmaps the surface, the surface
1158 * will not appear in repaint list, and so rpi_renderer_repaint_output
1159 * will remove the DispmanX element. Later, also the front buffer
1160 * will be released in the cleanup_list processing.
1161 */
1162}
1163
1164static int
1165rpi_renderer_create_surface(struct weston_surface *base)
1166{
1167 struct rpi_renderer *renderer = to_rpi_renderer(base->compositor);
1168 struct rpir_surface *surface;
1169
1170 assert(base->renderer_state == NULL);
1171
1172 surface = rpir_surface_create(renderer);
1173 if (!surface)
1174 return -1;
1175
1176 surface->surface = base;
1177 base->renderer_state = surface;
1178 return 0;
1179}
1180
1181static void
1182rpi_renderer_surface_set_color(struct weston_surface *base,
1183 float red, float green, float blue, float alpha)
1184{
1185 struct rpir_surface *surface = to_rpir_surface(base);
1186 uint8_t color[4];
1187 VC_RECT_T rect;
1188 int ret;
1189
1190 assert(surface);
1191
1192 ret = rpi_resource_realloc(surface->back, VC_IMAGE_ARGB8888,
1193 1, 1, 4, 1);
1194 if (ret < 0) {
1195 weston_log("Error: %s: rpi_resource_realloc failed.\n",
1196 __func__);
1197 return;
1198 }
1199
1200 color[0] = float2uint8(blue);
1201 color[1] = float2uint8(green);
1202 color[2] = float2uint8(red);
1203 color[3] = float2uint8(alpha);
1204
1205 vc_dispmanx_rect_set(&rect, 0, 0, 1, 1);
1206 ret = vc_dispmanx_resource_write_data(surface->back->handle,
1207 VC_IMAGE_ARGB8888,
1208 4, color, &rect);
1209 if (ret) {
1210 weston_log("Error: %s: resource_write_data failed.\n",
1211 __func__);
1212 return;
1213 }
1214
1215 DBG("%s: resource %p solid color BGRA %u,%u,%u,%u\n", __func__,
1216 surface->back, color[0], color[1], color[2], color[3]);
1217
1218 /*pixman_region32_copy(&surface->prev_damage, damage);*/
1219 surface->need_swap = 1;
1220}
1221
1222static void
1223rpi_renderer_destroy_surface(struct weston_surface *base)
1224{
1225 struct rpir_surface *surface = to_rpir_surface(base);
1226
1227 assert(surface);
1228 assert(surface->surface == base);
1229 if (!surface)
1230 return;
1231
1232 surface->surface = NULL;
1233 base->renderer_state = NULL;
1234
1235 /* If guaranteed to not be on screen, just detroy it. */
1236 if (wl_list_empty(&surface->link))
1237 rpir_surface_destroy(surface);
1238
1239 /* Otherwise, the surface is either on screen and needs
1240 * to be removed by a repaint update, or it is in the
1241 * surface_cleanup_list, and will be destroyed by
1242 * rpi_renderer_finish_frame().
1243 */
1244}
1245
1246static void
1247rpi_renderer_destroy(struct weston_compositor *compositor)
1248{
1249 struct rpi_renderer *renderer = to_rpi_renderer(compositor);
1250
1251 free(renderer);
1252 compositor->renderer = NULL;
1253}
1254
1255WL_EXPORT int
1256rpi_renderer_create(struct weston_compositor *compositor,
1257 const struct rpi_renderer_parameters *params)
1258{
1259 struct rpi_renderer *renderer;
1260
1261 weston_log("Initializing the DispmanX compositing renderer\n");
1262
1263 renderer = calloc(1, sizeof *renderer);
1264 if (renderer == NULL)
1265 return -1;
1266
1267 renderer->single_buffer = params->single_buffer;
1268
1269 renderer->base.read_pixels = rpi_renderer_read_pixels;
1270 renderer->base.repaint_output = rpi_renderer_repaint_output;
1271 renderer->base.flush_damage = rpi_renderer_flush_damage;
1272 renderer->base.attach = rpi_renderer_attach;
1273 renderer->base.create_surface = rpi_renderer_create_surface;
1274 renderer->base.surface_set_color = rpi_renderer_surface_set_color;
1275 renderer->base.destroy_surface = rpi_renderer_destroy_surface;
1276 renderer->base.destroy = rpi_renderer_destroy;
1277
1278 compositor->renderer = &renderer->base;
1279 compositor->read_format = PIXMAN_a8r8g8b8;
1280 /* WESTON_CAP_ROTATION_ANY not supported */
1281
1282 return 0;
1283}
1284
1285WL_EXPORT int
1286rpi_renderer_output_create(struct weston_output *base,
1287 DISPMANX_DISPLAY_HANDLE_T display)
1288{
1289 struct rpir_output *output;
1290
1291 assert(base->renderer_state == NULL);
1292
1293 output = calloc(1, sizeof *output);
1294 if (!output)
1295 return -1;
1296
1297 output->display = display;
1298 output->update = DISPMANX_NO_HANDLE;
1299 wl_list_init(&output->surface_list);
1300 wl_list_init(&output->surface_cleanup_list);
1301 rpi_resource_init(&output->capture_buffer);
1302 base->renderer_state = output;
1303
1304 return 0;
1305}
1306
1307WL_EXPORT void
1308rpi_renderer_output_destroy(struct weston_output *base)
1309{
1310 struct rpir_output *output = to_rpir_output(base);
1311 struct rpir_surface *surface;
1312 DISPMANX_UPDATE_HANDLE_T update;
1313
1314 rpi_resource_release(&output->capture_buffer);
1315 free(output->capture_data);
1316 output->capture_data = NULL;
1317
1318 update = vc_dispmanx_update_start(0);
1319 rpir_output_dmx_remove_all(output, update);
1320 vc_dispmanx_update_submit_sync(update);
1321
1322 while (!wl_list_empty(&output->surface_cleanup_list)) {
1323 surface = container_of(output->surface_cleanup_list.next,
1324 struct rpir_surface, link);
1325 if (surface->surface)
1326 surface->surface->renderer_state = NULL;
1327 rpir_surface_destroy(surface);
1328 }
1329
1330 free(output);
1331 base->renderer_state = NULL;
1332}
1333
1334WL_EXPORT void
1335rpi_renderer_set_update_handle(struct weston_output *base,
1336 DISPMANX_UPDATE_HANDLE_T handle)
1337{
1338 struct rpir_output *output = to_rpir_output(base);
1339
1340 output->update = handle;
1341}
1342
1343WL_EXPORT void
1344rpi_renderer_finish_frame(struct weston_output *base)
1345{
1346 struct rpir_output *output = to_rpir_output(base);
1347 struct rpir_surface *surface;
1348
1349 while (!wl_list_empty(&output->surface_cleanup_list)) {
1350 surface = container_of(output->surface_cleanup_list.next,
1351 struct rpir_surface, link);
1352
1353 if (surface->surface) {
1354 /* The weston_surface still exists, but is
1355 * temporarily not visible, and hence its Element
1356 * was removed. The current front buffer contents
1357 * must be preserved.
1358 */
1359 if (!surface->single_buffer)
1360 rpi_resource_release(surface->back);
1361
1362 wl_list_remove(&surface->link);
1363 wl_list_init(&surface->link);
1364 } else {
1365 rpir_surface_destroy(surface);
1366 }
1367 }
1368
1369 wl_signal_emit(&base->frame_signal, base);
1370}