blob: 3ba5fc4a60f14edbf991776b0347111d115d29df [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
Daniel Stonec228e232013-05-22 18:03:19 +030023#include "config.h"
24
Pekka Paalanend7265bc2013-05-22 18:03:06 +030025#include <stdlib.h>
26#include <assert.h>
27#include <string.h>
28
Pekka Paalanend7265bc2013-05-22 18:03:06 +030029#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
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500229shm_buffer_get_vc_format(struct wl_shm_buffer *buffer)
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300230{
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;
Tomeu Vizoso03681892013-08-06 20:05:57 +0200236 case WL_SHM_FORMAT_RGB565:
237 return VC_IMAGE_RGB565;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300238 default:
239 /* invalid format */
240 return VC_IMAGE_MIN;
241 }
242}
243
244static int
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500245rpi_resource_update(struct rpi_resource *resource, struct weston_buffer *buffer,
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300246 pixman_region32_t *region)
247{
248 pixman_region32_t write_region;
249 pixman_box32_t *r;
250 VC_RECT_T rect;
251 VC_IMAGE_TYPE_T ifmt;
252 uint32_t *pixels;
253 int width;
254 int height;
255 int stride;
256 int ret;
257#ifdef HAVE_RESOURCE_WRITE_DATA_RECT
258 int n;
259#endif
260
261 if (!buffer)
262 return -1;
263
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500264 ifmt = shm_buffer_get_vc_format(buffer->shm_buffer);
265 width = wl_shm_buffer_get_width(buffer->shm_buffer);
266 height = wl_shm_buffer_get_height(buffer->shm_buffer);
267 stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
268 pixels = wl_shm_buffer_get_data(buffer->shm_buffer);
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300269
270 ret = rpi_resource_realloc(resource, ifmt & ~PREMULT_ALPHA_FLAG,
271 width, height, stride, height);
272 if (ret < 0)
273 return -1;
274
275 pixman_region32_init_rect(&write_region, 0, 0, width, height);
276 if (ret == 0)
277 pixman_region32_intersect(&write_region,
278 &write_region, region);
279
280#ifdef HAVE_RESOURCE_WRITE_DATA_RECT
281 /* XXX: Can this do a format conversion, so that scanout does not have to? */
282 r = pixman_region32_rectangles(&write_region, &n);
283 while (n--) {
284 vc_dispmanx_rect_set(&rect, r[n].x1, r[n].y1,
285 r[n].x2 - r[n].x1, r[n].y2 - r[n].y1);
286
287 ret = vc_dispmanx_resource_write_data_rect(resource->handle,
288 ifmt, stride,
289 pixels, &rect,
290 rect.x, rect.y);
291 DBG("%s: %p %ux%u@%u,%u, ret %d\n", __func__, resource,
292 rect.width, rect.height, rect.x, rect.y, ret);
293 if (ret)
294 break;
295 }
296#else
297 /* vc_dispmanx_resource_write_data() ignores ifmt,
298 * rect.x, rect.width, and uses stride only for computing
299 * the size of the transfer as rect.height * stride.
300 * Therefore we can only write rows starting at x=0.
301 * To be able to write more than one scanline at a time,
302 * the resource must have been created with the same stride
303 * as used here, and we must write full scanlines.
304 */
305
306 r = pixman_region32_extents(&write_region);
307 vc_dispmanx_rect_set(&rect, 0, r->y1, width, r->y2 - r->y1);
308 ret = vc_dispmanx_resource_write_data(resource->handle,
309 ifmt, stride, pixels, &rect);
310 DBG("%s: %p %ux%u@%u,%u, ret %d\n", __func__, resource,
311 width, r->y2 - r->y1, 0, r->y1, ret);
312#endif
313
314 pixman_region32_fini(&write_region);
315
316 return ret ? -1 : 0;
317}
318
319static struct rpir_surface *
320rpir_surface_create(struct rpi_renderer *renderer)
321{
322 struct rpir_surface *surface;
323
324 surface = calloc(1, sizeof *surface);
325 if (!surface)
326 return NULL;
327
328 wl_list_init(&surface->link);
329 surface->single_buffer = renderer->single_buffer;
330 surface->handle = DISPMANX_NO_HANDLE;
331 rpi_resource_init(&surface->resources[0]);
332 rpi_resource_init(&surface->resources[1]);
333 surface->front = &surface->resources[0];
334 if (surface->single_buffer)
335 surface->back = &surface->resources[0];
336 else
337 surface->back = &surface->resources[1];
338
339 pixman_region32_init(&surface->prev_damage);
340
341 return surface;
342}
343
344static void
345rpir_surface_destroy(struct rpir_surface *surface)
346{
347 wl_list_remove(&surface->link);
348
349 if (surface->handle != DISPMANX_NO_HANDLE)
350 weston_log("ERROR rpi: destroying on-screen element\n");
351
352 pixman_region32_fini(&surface->prev_damage);
353 rpi_resource_release(&surface->resources[0]);
354 rpi_resource_release(&surface->resources[1]);
355 DBG("rpir_surface %p destroyed (%u)\n", surface, surface->handle);
356
357 free(surface);
358}
359
360static int
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500361rpir_surface_damage(struct rpir_surface *surface, struct weston_buffer *buffer,
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300362 pixman_region32_t *damage)
363{
364 pixman_region32_t upload;
365 int ret;
366
367 if (!pixman_region32_not_empty(damage))
368 return 0;
369
370 DBG("rpir_surface %p update resource %p\n", surface, surface->back);
371
372 /* XXX: todo: if no surface->handle, update front buffer directly
373 * to avoid creating a new back buffer */
374 if (surface->single_buffer) {
375 ret = rpi_resource_update(surface->front, buffer, damage);
376 } else {
377 pixman_region32_init(&upload);
378 pixman_region32_union(&upload, &surface->prev_damage, damage);
379 ret = rpi_resource_update(surface->back, buffer, &upload);
380 pixman_region32_fini(&upload);
381 }
382
383 pixman_region32_copy(&surface->prev_damage, damage);
384 surface->need_swap = 1;
385
386 return ret;
387}
388
389static void
390matrix_type_str(struct weston_matrix *matrix, char *buf, int len)
391{
392 static const char types[33] = "TSRO";
393 unsigned mask = matrix->type;
394 int i = 0;
395
396 while (mask && i < len - 1) {
397 if (mask & (1u << i))
398 *buf++ = types[i];
399 mask &= ~(1u << i);
400 i++;
401 }
402 *buf = '\0';
403}
404
405static void
406log_print_matrix(struct weston_matrix *matrix)
407{
408 char typestr[6];
409 float *d = matrix->d;
410
411 matrix_type_str(matrix, typestr, sizeof typestr);
412 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
413 d[0], d[4], d[8], d[12]);
414 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
415 d[1], d[5], d[9], d[13]);
416 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
417 d[2], d[6], d[10], d[14]);
418 weston_log_continue("%14.6e %14.6e %14.6e %14.6e type: %s\n",
419 d[3], d[7], d[11], d[15], typestr);
420}
421
422static void
423warn_bad_matrix(struct weston_matrix *total, struct weston_matrix *output,
424 struct weston_matrix *surface)
425{
426 static int n_warn;
427 char typestr[6];
428
429 if (n_warn++ == 10)
430 weston_log("%s: not showing more warnings\n", __func__);
431
432 if (n_warn > 10)
433 return;
434
435 weston_log("%s: warning: total transformation is not renderable:\n",
436 __func__);
437 log_print_matrix(total);
438
439 matrix_type_str(surface, typestr, sizeof typestr);
440 weston_log_continue("surface matrix type: %s\n", typestr);
441 matrix_type_str(output, typestr, sizeof typestr);
442 weston_log_continue("output matrix type: %s\n", typestr);
443}
444
445/*#define SURFACE_TRANSFORM */
446
447static int
448rpir_surface_compute_rects(struct rpir_surface *surface,
449 VC_RECT_T *src_rect, VC_RECT_T *dst_rect,
450 VC_IMAGE_TRANSFORM_T *flipmask)
451{
452 struct weston_output *output_base = surface->surface->output;
453 struct rpir_output *output = to_rpir_output(output_base);
454 struct weston_matrix matrix = surface->surface->transform.matrix;
455 VC_IMAGE_TRANSFORM_T flipt = 0;
456 int src_x, src_y;
457 int dst_x, dst_y;
458 int src_width, src_height;
459 int dst_width, dst_height;
460 struct weston_vector p1 = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
461 struct weston_vector p2 = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
462 int t;
463 int over;
464
465 /* XXX: take buffer transform into account */
466
467 /* src is in 16.16, dst is in 32.0 fixed point.
468 * Negative values are not allowed in VC_RECT_T.
469 * Clip size to output boundaries, firmware ignores
470 * huge elements like 8192x8192.
471 */
472
473 src_x = 0 << 16;
474 src_y = 0 << 16;
475 src_width = surface->front->width << 16;
476 src_height = surface->front->height << 16;
477
478 weston_matrix_multiply(&matrix, &output->matrix);
479
480#ifdef SURFACE_TRANSFORM
481 if (matrix.type >= WESTON_MATRIX_TRANSFORM_OTHER) {
482#else
483 if (matrix.type >= WESTON_MATRIX_TRANSFORM_ROTATE) {
484#endif
485 warn_bad_matrix(&matrix, &output->matrix,
486 &surface->surface->transform.matrix);
487 } else {
488 if (matrix.type & WESTON_MATRIX_TRANSFORM_ROTATE) {
489 if (fabsf(matrix.d[0]) < 1e-4f &&
490 fabsf(matrix.d[5]) < 1e-4f) {
491 flipt |= TRANSFORM_TRANSPOSE;
492 } else if (fabsf(matrix.d[1]) < 1e-4 &&
493 fabsf(matrix.d[4]) < 1e-4) {
494 /* no transpose */
495 } else {
496 warn_bad_matrix(&matrix, &output->matrix,
497 &surface->surface->transform.matrix);
498 }
499 }
500 }
501
502 p2.f[0] = surface->surface->geometry.width;
503 p2.f[1] = surface->surface->geometry.height;
504
505 /* transform top-left and bot-right corner into screen coordinates */
506 weston_matrix_transform(&matrix, &p1);
507 weston_matrix_transform(&matrix, &p2);
508
509 /* Compute the destination rectangle on screen, converting
510 * negative dimensions to flips.
511 */
512
513 dst_width = round(p2.f[0] - p1.f[0]);
514 if (dst_width < 0) {
515 dst_x = round(p2.f[0]);
516 dst_width = -dst_width;
517
518 if (!(flipt & TRANSFORM_TRANSPOSE))
519 flipt |= TRANSFORM_HFLIP;
520 else
521 flipt |= TRANSFORM_VFLIP;
522 } else {
523 dst_x = round(p1.f[0]);
524 }
525
526 dst_height = round(p2.f[1] - p1.f[1]);
527 if (dst_height < 0) {
528 dst_y = round(p2.f[1]);
529 dst_height = -dst_height;
530
531 if (!(flipt & TRANSFORM_TRANSPOSE))
532 flipt |= TRANSFORM_VFLIP;
533 else
534 flipt |= TRANSFORM_HFLIP;
535 } else {
536 dst_y = round(p1.f[1]);
537 }
538
539 if (dst_width == 0 || dst_height == 0) {
540 DBG("ignored, zero surface area before clipping\n");
541 return -1;
542 }
543
544#ifdef SURFACE_TRANSFORM
545 /* Dispmanx works as if you flipped the whole screen, when
546 * you flip an element. But, we want to flip an element in place.
547 * XXX: fixme
548 */
549 if (flipt & TRANSFORM_HFLIP)
550 dst_x = output_base->width - dst_x;
551 if (flipt & TRANSFORM_VFLIP)
552 dst_y = output_base->height - dst_y;
553 if (flipt & TRANSFORM_TRANSPOSE) {
554 int_swap(&dst_x, &dst_y);
555 int_swap(&dst_width, &dst_height);
556 }
557#else
558 switch (output_base->transform) {
559 case WL_OUTPUT_TRANSFORM_FLIPPED:
560 flipt = TRANSFORM_HFLIP;
561 break;
562 case WL_OUTPUT_TRANSFORM_NORMAL:
563 flipt = 0;
564 break;
565
566 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
567 flipt = TRANSFORM_HFLIP | TRANSFORM_VFLIP | TRANSFORM_TRANSPOSE;
568 break;
569 case WL_OUTPUT_TRANSFORM_90:
570 flipt = TRANSFORM_VFLIP | TRANSFORM_TRANSPOSE;
571 break;
572 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
573 flipt = TRANSFORM_VFLIP;
574 break;
575 case WL_OUTPUT_TRANSFORM_180:
576 flipt = TRANSFORM_HFLIP | TRANSFORM_VFLIP;
577 break;
578
579 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
580 flipt = TRANSFORM_TRANSPOSE;
581 break;
582 case WL_OUTPUT_TRANSFORM_270:
583 flipt = TRANSFORM_HFLIP | TRANSFORM_TRANSPOSE;
584 break;
585 default:
586 break;
587 }
588#endif
589
590 /* clip destination rectangle to screen dimensions */
591
592 if (dst_x < 0) {
593 t = (int64_t)dst_x * src_width / dst_width;
594 src_width += t;
595 dst_width += dst_x;
596 src_x -= t;
597 dst_x = 0;
598 }
599
600 if (dst_y < 0) {
601 t = (int64_t)dst_y * src_height / dst_height;
602 src_height += t;
603 dst_height += dst_y;
604 src_y -= t;
605 dst_y = 0;
606 }
607
608 over = dst_x + dst_width - output_base->width;
609 if (over > 0) {
610 t = (int64_t)over * src_width / dst_width;
611 src_width -= t;
612 dst_width -= over;
613 }
614
615 over = dst_y + dst_height - output_base->height;
616 if (over > 0) {
617 t = (int64_t)over * src_height / dst_height;
618 src_height -= t;
619 dst_height -= over;
620 }
621
622 src_width = int_max(src_width, 0);
623 src_height = int_max(src_height, 0);
624
625 DBG("rpir_surface %p %dx%d: p1 %f, %f; p2 %f, %f\n", surface,
626 surface->surface->geometry.width,
627 surface->surface->geometry.height,
628 p1.f[0], p1.f[1], p2.f[0], p2.f[1]);
629 DBG("src rect %d;%d, %d;%d, %d;%dx%d;%d\n",
630 src_x >> 16, src_x & 0xffff,
631 src_y >> 16, src_y & 0xffff,
632 src_width >> 16, src_width & 0xffff,
633 src_height >> 16, src_height & 0xffff);
634 DBG("dest rect %d, %d, %dx%d%s%s%s\n",
635 dst_x, dst_y, dst_width, dst_height,
636 (flipt & TRANSFORM_HFLIP) ? " hflip" : "",
637 (flipt & TRANSFORM_VFLIP) ? " vflip" : "",
638 (flipt & TRANSFORM_TRANSPOSE) ? " transp" : "");
639
640 assert(src_x >= 0);
641 assert(src_y >= 0);
642 assert(dst_x >= 0);
643 assert(dst_y >= 0);
644
645 if (dst_width < 1 || dst_height < 1) {
646 DBG("ignored, zero surface area after clipping\n");
647 return -1;
648 }
649
650 vc_dispmanx_rect_set(src_rect, src_x, src_y, src_width, src_height);
651 vc_dispmanx_rect_set(dst_rect, dst_x, dst_y, dst_width, dst_height);
652 *flipmask = flipt;
653
654 return 0;
655}
656
657static DISPMANX_TRANSFORM_T
658vc_image2dispmanx_transform(VC_IMAGE_TRANSFORM_T t)
659{
660 /* XXX: uhh, are these right? */
661 switch (t) {
662 case VC_IMAGE_ROT0:
663 return DISPMANX_NO_ROTATE;
664 case VC_IMAGE_MIRROR_ROT0:
665 return DISPMANX_FLIP_HRIZ;
666 case VC_IMAGE_MIRROR_ROT180:
667 return DISPMANX_FLIP_VERT;
668 case VC_IMAGE_ROT180:
669 return DISPMANX_ROTATE_180;
670 case VC_IMAGE_MIRROR_ROT90:
671 return DISPMANX_ROTATE_90 | DISPMANX_FLIP_HRIZ;
672 case VC_IMAGE_ROT270:
673 return DISPMANX_ROTATE_270;
674 case VC_IMAGE_ROT90:
675 return DISPMANX_ROTATE_90;
676 case VC_IMAGE_MIRROR_ROT270:
677 return DISPMANX_ROTATE_270 | DISPMANX_FLIP_VERT;
678 default:
679 assert(0 && "bad VC_IMAGE_TRANSFORM_T");
680 return DISPMANX_NO_ROTATE;
681 }
682}
683
684static int
685rpir_surface_dmx_add(struct rpir_surface *surface, struct rpir_output *output,
686 DISPMANX_UPDATE_HANDLE_T update, int layer)
687{
688 /* Do not use DISPMANX_FLAGS_ALPHA_PREMULT here.
689 * If you define PREMULT and ALPHA_MIX, the hardware will not
690 * multiply the source color with the element alpha, leading to
691 * bad colors. Instead, we define PREMULT during pixel data upload.
692 */
693 VC_DISPMANX_ALPHA_T alphasetup = {
694 DISPMANX_FLAGS_ALPHA_FROM_SOURCE |
695 DISPMANX_FLAGS_ALPHA_MIX,
696 float2uint8(surface->surface->alpha), /* opacity 0-255 */
697 0 /* mask resource handle */
698 };
699 VC_RECT_T dst_rect;
700 VC_RECT_T src_rect;
701 VC_IMAGE_TRANSFORM_T flipmask;
702 int ret;
703
704 ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect,
705 &flipmask);
706 if (ret < 0)
707 return 0;
708
709 surface->handle = vc_dispmanx_element_add(
710 update,
711 output->display,
712 layer,
713 &dst_rect,
714 surface->front->handle,
715 &src_rect,
716 DISPMANX_PROTECTION_NONE,
717 &alphasetup,
718 NULL /* clamp */,
719 vc_image2dispmanx_transform(flipmask));
720 DBG("rpir_surface %p add %u, alpha %f\n", surface, surface->handle,
721 surface->surface->alpha);
722
723 if (surface->handle == DISPMANX_NO_HANDLE)
724 return -1;
725
726 return 1;
727}
728
729static void
730rpir_surface_dmx_swap(struct rpir_surface *surface,
731 DISPMANX_UPDATE_HANDLE_T update)
732{
733 VC_RECT_T rect;
734 pixman_box32_t *r;
735
736 /* XXX: skip, iff resource was not reallocated, and single-buffering */
737 vc_dispmanx_element_change_source(update, surface->handle,
738 surface->front->handle);
739
740 /* This is current damage now, after rpir_surface_damage() */
741 r = pixman_region32_extents(&surface->prev_damage);
742
743 vc_dispmanx_rect_set(&rect, r->x1, r->y1,
744 r->x2 - r->x1, r->y2 - r->y1);
745 vc_dispmanx_element_modified(update, surface->handle, &rect);
746 DBG("rpir_surface %p swap\n", surface);
747}
748
749static int
750rpir_surface_dmx_move(struct rpir_surface *surface,
751 DISPMANX_UPDATE_HANDLE_T update, int layer)
752{
753 uint8_t alpha = float2uint8(surface->surface->alpha);
754 VC_RECT_T dst_rect;
755 VC_RECT_T src_rect;
756 VC_IMAGE_TRANSFORM_T flipmask;
757 int ret;
758
759 /* XXX: return early, if all attributes stay the same */
760
761 ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect,
762 &flipmask);
763 if (ret < 0)
764 return 0;
765
766 ret = vc_dispmanx_element_change_attributes(
767 update,
768 surface->handle,
769 ELEMENT_CHANGE_LAYER |
770 ELEMENT_CHANGE_OPACITY |
771 ELEMENT_CHANGE_TRANSFORM |
772 ELEMENT_CHANGE_DEST_RECT |
773 ELEMENT_CHANGE_SRC_RECT,
774 layer,
775 alpha,
776 &dst_rect,
777 &src_rect,
778 DISPMANX_NO_HANDLE,
779 /* This really is DISPMANX_TRANSFORM_T, no matter
780 * what the header says. */
781 vc_image2dispmanx_transform(flipmask));
782 DBG("rpir_surface %p move\n", surface);
783
784 if (ret)
785 return -1;
786
787 return 1;
788}
789
790static void
791rpir_surface_dmx_remove(struct rpir_surface *surface,
792 DISPMANX_UPDATE_HANDLE_T update)
793{
794 if (surface->handle == DISPMANX_NO_HANDLE)
795 return;
796
797 vc_dispmanx_element_remove(update, surface->handle);
798 DBG("rpir_surface %p remove %u\n", surface, surface->handle);
799 surface->handle = DISPMANX_NO_HANDLE;
800}
801
802static void
803rpir_surface_swap_pointers(struct rpir_surface *surface)
804{
805 struct rpi_resource *tmp;
806
807 tmp = surface->front;
808 surface->front = surface->back;
809 surface->back = tmp;
810 surface->need_swap = 0;
811 DBG("new back %p, new front %p\n", surface->back, surface->front);
812}
813
814static int
815is_surface_not_visible(struct weston_surface *surface)
816{
817 /* Return true, if surface is guaranteed to be totally obscured. */
818 int ret;
819 pixman_region32_t unocc;
820
821 pixman_region32_init(&unocc);
822 pixman_region32_subtract(&unocc, &surface->transform.boundingbox,
823 &surface->clip);
824 ret = !pixman_region32_not_empty(&unocc);
825 pixman_region32_fini(&unocc);
826
827 return ret;
828}
829
830static void
831rpir_surface_update(struct rpir_surface *surface, struct rpir_output *output,
832 DISPMANX_UPDATE_HANDLE_T update, int layer)
833{
834 int need_swap = surface->need_swap;
835 int ret;
836 int obscured;
837
838 if (need_swap)
839 rpir_surface_swap_pointers(surface);
840
841 obscured = is_surface_not_visible(surface->surface);
842 if (obscured) {
843 DBG("rpir_surface %p totally obscured.\n", surface);
844
845 wl_list_remove(&surface->link);
846 if (surface->handle == DISPMANX_NO_HANDLE) {
847 wl_list_init(&surface->link);
848 } else {
849 rpir_surface_dmx_remove(surface, update);
850 wl_list_insert(&output->surface_cleanup_list,
851 &surface->link);
852 }
853
854 goto out;
855 }
856
857 if (surface->handle == DISPMANX_NO_HANDLE) {
858 ret = rpir_surface_dmx_add(surface, output, update, layer);
859 if (ret == 0) {
860 wl_list_remove(&surface->link);
861 wl_list_init(&surface->link);
862 } else if (ret < 0) {
863 weston_log("ERROR rpir_surface_dmx_add() failed.\n");
864 }
865 } else {
866 if (need_swap)
867 rpir_surface_dmx_swap(surface, update);
868
869 ret = rpir_surface_dmx_move(surface, update, layer);
870 if (ret == 0) {
871 rpir_surface_dmx_remove(surface, update);
872
873 wl_list_remove(&surface->link);
874 wl_list_insert(&output->surface_cleanup_list,
875 &surface->link);
876 } else if (ret < 0) {
877 weston_log("ERROR rpir_surface_dmx_move() failed.\n");
878 }
879 }
880
881out:
882 surface->layer = layer;
883}
884
885static int
886rpi_renderer_read_pixels(struct weston_output *base,
887 pixman_format_code_t format, void *pixels,
888 uint32_t x, uint32_t y,
889 uint32_t width, uint32_t height)
890{
891 struct rpir_output *output = to_rpir_output(base);
892 struct rpi_resource *buffer = &output->capture_buffer;
893 VC_RECT_T rect;
894 uint32_t fb_width, fb_height;
895 uint32_t dst_pitch;
896 uint32_t i;
897 int ret;
898
Hardeningff39efa2013-09-18 23:56:35 +0200899 fb_width = base->current_mode->width;
900 fb_height = base->current_mode->height;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300901
902 DBG("%s(%u, %u, %u, %u), resource %p\n", __func__,
903 x, y, width, height, buffer);
904
905 if (format != PIXMAN_a8r8g8b8) {
906 weston_log("rpi-renderer error: bad read_format\n");
907 return -1;
908 }
909
910 dst_pitch = fb_width * 4;
911
912 if (buffer->handle == DISPMANX_NO_HANDLE) {
913 free(output->capture_data);
914 output->capture_data = NULL;
915
916 ret = rpi_resource_realloc(buffer, VC_IMAGE_ARGB8888,
917 fb_width, fb_height,
918 dst_pitch, fb_height);
919 if (ret < 0) {
920 weston_log("rpi-renderer error: "
921 "allocating read buffer failed\n");
922 return -1;
923 }
924
925 ret = vc_dispmanx_snapshot(output->display, buffer->handle,
926 VC_IMAGE_ROT0);
927 if (ret) {
928 weston_log("rpi-renderer error: "
929 "vc_dispmanx_snapshot returned %d\n", ret);
930 return -1;
931 }
932 DBG("%s: snapshot done.\n", __func__);
933 }
934
935 /*
936 * If vc_dispmanx_resource_read_data was able to read sub-rectangles,
937 * we could read directly into 'pixels'. But it cannot, it does not
938 * use rect.x or rect.width, and does this:
939 * host_start = (uint8_t *)dst_address + (dst_pitch * p_rect->y);
940 * In other words, it is only good for reading the full buffer in
941 * one go.
942 */
943 vc_dispmanx_rect_set(&rect, 0, 0, fb_width, fb_height);
944
945 if (x == 0 && y == 0 && width == fb_width && height == fb_height) {
946 ret = vc_dispmanx_resource_read_data(buffer->handle, &rect,
947 pixels, dst_pitch);
948 if (ret) {
949 weston_log("rpi-renderer error: "
950 "resource_read_data returned %d\n", ret);
951 return -1;
952 }
953 DBG("%s: full frame done.\n", __func__);
954 return 0;
955 }
956
957 if (!output->capture_data) {
958 output->capture_data = malloc(fb_height * dst_pitch);
959 if (!output->capture_data) {
960 weston_log("rpi-renderer error: "
961 "out of memory\n");
962 return -1;
963 }
964
965 ret = vc_dispmanx_resource_read_data(buffer->handle, &rect,
966 output->capture_data,
967 dst_pitch);
968 if (ret) {
969 weston_log("rpi-renderer error: "
970 "resource_read_data returned %d\n", ret);
971 return -1;
972 }
973 }
974
975 for (i = 0; i < height; i++) {
976 uint8_t *src = output->capture_data +
977 (y + i) * dst_pitch + x * 4;
978 uint8_t *dst = (uint8_t *)pixels + i * width * 4;
979 memcpy(dst, src, width * 4);
980 }
981
982 return 0;
983}
984
985static void
986rpir_output_dmx_remove_all(struct rpir_output *output,
987 DISPMANX_UPDATE_HANDLE_T update)
988{
989 struct rpir_surface *surface;
990
991 while (!wl_list_empty(&output->surface_list)) {
992 surface = container_of(output->surface_list.next,
993 struct rpir_surface, link);
994 rpir_surface_dmx_remove(surface, update);
995
996 wl_list_remove(&surface->link);
997 wl_list_insert(&output->surface_cleanup_list, &surface->link);
998 }
999}
1000
1001static void
1002output_compute_matrix(struct weston_output *base)
1003{
1004 struct rpir_output *output = to_rpir_output(base);
1005 struct weston_matrix *matrix = &output->matrix;
1006 const float half_w = 0.5f * base->width;
1007 const float half_h = 0.5f * base->height;
1008 float mag;
1009 float dx, dy;
1010
1011 weston_matrix_init(matrix);
1012 weston_matrix_translate(matrix, -base->x, -base->y, 0.0f);
1013
1014#ifdef SURFACE_TRANSFORM
1015 weston_matrix_translate(matrix, -half_w, -half_h, 0.0f);
1016 switch (base->transform) {
1017 case WL_OUTPUT_TRANSFORM_FLIPPED:
1018 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1019 case WL_OUTPUT_TRANSFORM_NORMAL:
1020 /* weston_matrix_rotate_xy(matrix, 1.0f, 0.0f); no-op */
1021 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1022 break;
1023
1024 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
1025 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1026 case WL_OUTPUT_TRANSFORM_90:
1027 weston_matrix_rotate_xy(matrix, 0.0f, 1.0f);
1028 weston_matrix_translate(matrix, half_h, half_w, 0.0f);
1029 break;
1030
1031 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
1032 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1033 case WL_OUTPUT_TRANSFORM_180:
1034 weston_matrix_rotate_xy(matrix, -1.0f, 0.0f);
1035 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1036 break;
1037
1038 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
1039 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1040 case WL_OUTPUT_TRANSFORM_270:
1041 weston_matrix_rotate_xy(matrix, 0.0f, -1.0f);
1042 weston_matrix_translate(matrix, half_h, half_w, 0.0f);
1043 break;
1044
1045 default:
1046 break;
1047 }
1048#endif
1049
1050 if (base->zoom.active) {
1051 /* The base->zoom stuff is in GL coordinate system */
1052 mag = 1.0f / (1.0f - base->zoom.spring_z.current);
1053 dx = -(base->zoom.trans_x + 1.0f) * half_w;
1054 dy = -(base->zoom.trans_y + 1.0f) * half_h;
1055 weston_matrix_translate(matrix, dx, dy, 0.0f);
1056 weston_matrix_scale(matrix, mag, mag, 1.0f);
1057 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1058 }
1059}
1060
1061/* Note: this won't work right for multiple outputs. A DispmanX Element
1062 * is tied to one DispmanX Display, i.e. output.
1063 */
1064static void
1065rpi_renderer_repaint_output(struct weston_output *base,
1066 pixman_region32_t *output_damage)
1067{
1068 struct weston_compositor *compositor = base->compositor;
1069 struct rpir_output *output = to_rpir_output(base);
1070 struct weston_surface *ws;
1071 struct rpir_surface *surface;
1072 struct wl_list done_list;
1073 int layer = 1;
1074
1075 assert(output->update != DISPMANX_NO_HANDLE);
1076
1077 output_compute_matrix(base);
1078
1079 rpi_resource_release(&output->capture_buffer);
1080 free(output->capture_data);
1081 output->capture_data = NULL;
1082
1083 /* update all renderable surfaces */
1084 wl_list_init(&done_list);
1085 wl_list_for_each_reverse(ws, &compositor->surface_list, link) {
1086 if (ws->plane != &compositor->primary_plane)
1087 continue;
1088
1089 surface = to_rpir_surface(ws);
1090 assert(!wl_list_empty(&surface->link) ||
1091 surface->handle == DISPMANX_NO_HANDLE);
1092
1093 wl_list_remove(&surface->link);
1094 wl_list_insert(&done_list, &surface->link);
1095 rpir_surface_update(surface, output, output->update, layer++);
1096 }
1097
1098 /* Remove all surfaces that are still on screen, but were
1099 * not rendered this time.
1100 */
1101 rpir_output_dmx_remove_all(output, output->update);
1102
1103 wl_list_insert_list(&output->surface_list, &done_list);
1104 output->update = DISPMANX_NO_HANDLE;
1105
1106 /* The frame_signal is emitted in rpi_renderer_finish_frame(),
1107 * so that the firmware can capture the up-to-date contents.
1108 */
1109}
1110
1111static void
1112rpi_renderer_flush_damage(struct weston_surface *base)
1113{
1114 /* Called for every surface just before repainting it, if
1115 * having an shm buffer.
1116 */
1117 struct rpir_surface *surface = to_rpir_surface(base);
Jason Ekstrand6bd62942013-06-20 20:38:23 -05001118 struct weston_buffer *buffer = surface->buffer_ref.buffer;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001119 int ret;
1120
1121 assert(buffer);
Jason Ekstrand6bd62942013-06-20 20:38:23 -05001122 assert(wl_shm_buffer_get(buffer->resource));
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001123
1124 ret = rpir_surface_damage(surface, buffer, &base->damage);
1125 if (ret)
1126 weston_log("%s error: updating Dispmanx resource failed.\n",
1127 __func__);
1128
1129 weston_buffer_reference(&surface->buffer_ref, NULL);
1130}
1131
1132static void
Jason Ekstrand6bd62942013-06-20 20:38:23 -05001133rpi_renderer_attach(struct weston_surface *base, struct weston_buffer *buffer)
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001134{
1135 /* Called every time a client commits an attach. */
1136 static int warned;
1137 struct rpir_surface *surface = to_rpir_surface(base);
1138
1139 assert(surface);
1140 if (!surface)
1141 return;
1142
Jason Ekstrand6bd62942013-06-20 20:38:23 -05001143 if (buffer && !wl_shm_buffer_get(buffer->resource) && !warned) {
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001144 weston_log("Error: non-wl_shm buffers not supported.\n");
1145 warned = 1;
1146 return;
1147 }
1148
Jason Ekstrand6bd62942013-06-20 20:38:23 -05001149 if (wl_shm_buffer_get(buffer->resource)) {
1150 buffer->shm_buffer = wl_shm_buffer_get(buffer->resource);
1151 buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer);
1152 buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer);
1153 }
1154
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001155 weston_buffer_reference(&surface->buffer_ref, buffer);
1156
1157 /* XXX: need to check if in middle of update
1158 if (!buffer && !surface->single_buffer)
1159 rpi_resource_release(surface->back); */
1160
1161 /* XXX: cannot do this, if middle of an update
1162 if (surface->handle == DISPMANX_NO_HANDLE)
1163 rpi_resource_release(surface->front); */
1164
1165 /* If buffer is NULL, Weston core unmaps the surface, the surface
1166 * will not appear in repaint list, and so rpi_renderer_repaint_output
1167 * will remove the DispmanX element. Later, also the front buffer
1168 * will be released in the cleanup_list processing.
1169 */
1170}
1171
1172static int
1173rpi_renderer_create_surface(struct weston_surface *base)
1174{
1175 struct rpi_renderer *renderer = to_rpi_renderer(base->compositor);
1176 struct rpir_surface *surface;
1177
1178 assert(base->renderer_state == NULL);
1179
1180 surface = rpir_surface_create(renderer);
1181 if (!surface)
1182 return -1;
1183
1184 surface->surface = base;
1185 base->renderer_state = surface;
1186 return 0;
1187}
1188
1189static void
1190rpi_renderer_surface_set_color(struct weston_surface *base,
1191 float red, float green, float blue, float alpha)
1192{
1193 struct rpir_surface *surface = to_rpir_surface(base);
1194 uint8_t color[4];
1195 VC_RECT_T rect;
1196 int ret;
1197
1198 assert(surface);
1199
1200 ret = rpi_resource_realloc(surface->back, VC_IMAGE_ARGB8888,
1201 1, 1, 4, 1);
1202 if (ret < 0) {
1203 weston_log("Error: %s: rpi_resource_realloc failed.\n",
1204 __func__);
1205 return;
1206 }
1207
1208 color[0] = float2uint8(blue);
1209 color[1] = float2uint8(green);
1210 color[2] = float2uint8(red);
1211 color[3] = float2uint8(alpha);
1212
1213 vc_dispmanx_rect_set(&rect, 0, 0, 1, 1);
1214 ret = vc_dispmanx_resource_write_data(surface->back->handle,
1215 VC_IMAGE_ARGB8888,
1216 4, color, &rect);
1217 if (ret) {
1218 weston_log("Error: %s: resource_write_data failed.\n",
1219 __func__);
1220 return;
1221 }
1222
1223 DBG("%s: resource %p solid color BGRA %u,%u,%u,%u\n", __func__,
1224 surface->back, color[0], color[1], color[2], color[3]);
1225
1226 /*pixman_region32_copy(&surface->prev_damage, damage);*/
1227 surface->need_swap = 1;
1228}
1229
1230static void
1231rpi_renderer_destroy_surface(struct weston_surface *base)
1232{
1233 struct rpir_surface *surface = to_rpir_surface(base);
1234
1235 assert(surface);
1236 assert(surface->surface == base);
1237 if (!surface)
1238 return;
1239
1240 surface->surface = NULL;
1241 base->renderer_state = NULL;
1242
1243 /* If guaranteed to not be on screen, just detroy it. */
1244 if (wl_list_empty(&surface->link))
1245 rpir_surface_destroy(surface);
1246
1247 /* Otherwise, the surface is either on screen and needs
1248 * to be removed by a repaint update, or it is in the
1249 * surface_cleanup_list, and will be destroyed by
1250 * rpi_renderer_finish_frame().
1251 */
1252}
1253
1254static void
1255rpi_renderer_destroy(struct weston_compositor *compositor)
1256{
1257 struct rpi_renderer *renderer = to_rpi_renderer(compositor);
1258
1259 free(renderer);
1260 compositor->renderer = NULL;
1261}
1262
1263WL_EXPORT int
1264rpi_renderer_create(struct weston_compositor *compositor,
1265 const struct rpi_renderer_parameters *params)
1266{
1267 struct rpi_renderer *renderer;
1268
1269 weston_log("Initializing the DispmanX compositing renderer\n");
1270
1271 renderer = calloc(1, sizeof *renderer);
1272 if (renderer == NULL)
1273 return -1;
1274
1275 renderer->single_buffer = params->single_buffer;
1276
1277 renderer->base.read_pixels = rpi_renderer_read_pixels;
1278 renderer->base.repaint_output = rpi_renderer_repaint_output;
1279 renderer->base.flush_damage = rpi_renderer_flush_damage;
1280 renderer->base.attach = rpi_renderer_attach;
1281 renderer->base.create_surface = rpi_renderer_create_surface;
1282 renderer->base.surface_set_color = rpi_renderer_surface_set_color;
1283 renderer->base.destroy_surface = rpi_renderer_destroy_surface;
1284 renderer->base.destroy = rpi_renderer_destroy;
1285
1286 compositor->renderer = &renderer->base;
1287 compositor->read_format = PIXMAN_a8r8g8b8;
1288 /* WESTON_CAP_ROTATION_ANY not supported */
1289
Tomeu Vizoso03681892013-08-06 20:05:57 +02001290 wl_display_add_shm_format(compositor->wl_display, WL_SHM_FORMAT_RGB565);
1291
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001292 return 0;
1293}
1294
1295WL_EXPORT int
1296rpi_renderer_output_create(struct weston_output *base,
1297 DISPMANX_DISPLAY_HANDLE_T display)
1298{
1299 struct rpir_output *output;
1300
1301 assert(base->renderer_state == NULL);
1302
1303 output = calloc(1, sizeof *output);
1304 if (!output)
1305 return -1;
1306
1307 output->display = display;
1308 output->update = DISPMANX_NO_HANDLE;
1309 wl_list_init(&output->surface_list);
1310 wl_list_init(&output->surface_cleanup_list);
1311 rpi_resource_init(&output->capture_buffer);
1312 base->renderer_state = output;
1313
1314 return 0;
1315}
1316
1317WL_EXPORT void
1318rpi_renderer_output_destroy(struct weston_output *base)
1319{
1320 struct rpir_output *output = to_rpir_output(base);
1321 struct rpir_surface *surface;
1322 DISPMANX_UPDATE_HANDLE_T update;
1323
1324 rpi_resource_release(&output->capture_buffer);
1325 free(output->capture_data);
1326 output->capture_data = NULL;
1327
1328 update = vc_dispmanx_update_start(0);
1329 rpir_output_dmx_remove_all(output, update);
1330 vc_dispmanx_update_submit_sync(update);
1331
1332 while (!wl_list_empty(&output->surface_cleanup_list)) {
1333 surface = container_of(output->surface_cleanup_list.next,
1334 struct rpir_surface, link);
1335 if (surface->surface)
1336 surface->surface->renderer_state = NULL;
1337 rpir_surface_destroy(surface);
1338 }
1339
1340 free(output);
1341 base->renderer_state = NULL;
1342}
1343
1344WL_EXPORT void
1345rpi_renderer_set_update_handle(struct weston_output *base,
1346 DISPMANX_UPDATE_HANDLE_T handle)
1347{
1348 struct rpir_output *output = to_rpir_output(base);
1349
1350 output->update = handle;
1351}
1352
1353WL_EXPORT void
1354rpi_renderer_finish_frame(struct weston_output *base)
1355{
1356 struct rpir_output *output = to_rpir_output(base);
1357 struct rpir_surface *surface;
1358
1359 while (!wl_list_empty(&output->surface_cleanup_list)) {
1360 surface = container_of(output->surface_cleanup_list.next,
1361 struct rpir_surface, link);
1362
1363 if (surface->surface) {
1364 /* The weston_surface still exists, but is
1365 * temporarily not visible, and hence its Element
1366 * was removed. The current front buffer contents
1367 * must be preserved.
1368 */
1369 if (!surface->single_buffer)
1370 rpi_resource_release(surface->back);
1371
1372 wl_list_remove(&surface->link);
1373 wl_list_init(&surface->link);
1374 } else {
1375 rpir_surface_destroy(surface);
1376 }
1377 }
1378
1379 wl_signal_emit(&base->frame_signal, base);
1380}