blob: 6b39e8c283f460421dcf42ed365d54ffa60040f9 [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
Tomeu Vizosob4659eb2013-10-07 11:02:20 +020038#ifdef ENABLE_EGL
39#include <EGL/egl.h>
40#include <EGL/eglext.h>
41#include "weston-egl-ext.h"
42#endif
43
Pekka Paalanend7265bc2013-05-22 18:03:06 +030044/*
45 * Dispmanx API offers alpha-blended overlays for hardware compositing.
46 * The final composite consists of dispmanx elements, and their contents:
47 * the dispmanx resource assigned to the element. The elements may be
48 * scanned out directly, or composited to a temporary surface, depending on
49 * how the firmware decides to handle the scene. Updates to multiple elements
50 * may be queued in a single dispmanx update object, resulting in atomic and
51 * vblank synchronized display updates.
52 *
53 * To avoid tearing and display artifacts, the current dispmanx resource in a
54 * dispmanx element must not be touched. Therefore each element must be
55 * double-buffered, using two resources, the front and the back. While a
56 * dispmanx update is running, the both resources must be considered in use.
57 *
58 * A resource may be destroyed only, when the update removing the element has
59 * completed. Otherwise you risk showing an incomplete composition.
60 */
61
62#ifndef ELEMENT_CHANGE_LAYER
63/* copied from interface/vmcs_host/vc_vchi_dispmanx.h of userland.git */
64#define ELEMENT_CHANGE_LAYER (1<<0)
65#define ELEMENT_CHANGE_OPACITY (1<<1)
66#define ELEMENT_CHANGE_DEST_RECT (1<<2)
67#define ELEMENT_CHANGE_SRC_RECT (1<<3)
68#define ELEMENT_CHANGE_MASK_RESOURCE (1<<4)
69#define ELEMENT_CHANGE_TRANSFORM (1<<5)
70#endif
71
72#if 0
73#define DBG(...) \
74 weston_log(__VA_ARGS__)
75#else
76#define DBG(...) do {} while (0)
77#endif
78
79/* If we had a fully featured vc_dispmanx_resource_write_data()... */
80/*#define HAVE_RESOURCE_WRITE_DATA_RECT 1*/
81
82struct rpi_resource {
83 DISPMANX_RESOURCE_HANDLE_T handle;
84 int width;
85 int height; /* height of the image (valid pixel data) */
86 int stride; /* bytes */
87 int buffer_height; /* height of the buffer */
88 VC_IMAGE_TYPE_T ifmt;
89};
90
91struct rpir_output;
92
Tomeu Vizosob4659eb2013-10-07 11:02:20 +020093struct rpir_egl_buffer {
94 struct weston_buffer_reference buffer_ref;
95 DISPMANX_RESOURCE_HANDLE_T resource_handle;
96};
97
98enum buffer_type {
99 BUFFER_TYPE_NULL,
100 BUFFER_TYPE_SHM,
101 BUFFER_TYPE_EGL
102};
103
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300104struct rpir_surface {
105 struct weston_surface *surface;
106
107 /* If link is empty, the surface is guaranteed to not be on screen,
108 * i.e. updates removing Elements have completed.
109 */
110 struct wl_list link;
111
112 DISPMANX_ELEMENT_HANDLE_T handle;
113 int layer;
114 int need_swap;
115 int single_buffer;
116
117 struct rpi_resource resources[2];
118 struct rpi_resource *front;
119 struct rpi_resource *back;
120 pixman_region32_t prev_damage;
121
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200122 struct rpir_egl_buffer *egl_front;
123 struct rpir_egl_buffer *egl_back;
124 struct rpir_egl_buffer *egl_old_front;
125
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300126 struct weston_buffer_reference buffer_ref;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200127 enum buffer_type buffer_type;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300128};
129
130struct rpir_output {
131 DISPMANX_DISPLAY_HANDLE_T display;
132
133 DISPMANX_UPDATE_HANDLE_T update;
134 struct weston_matrix matrix;
135
136 /* all Elements currently on screen */
137 struct wl_list surface_list; /* struct rpir_surface::link */
138
139 /* Elements just removed, waiting for update completion */
140 struct wl_list surface_cleanup_list; /* struct rpir_surface::link */
141
142 struct rpi_resource capture_buffer;
143 uint8_t *capture_data;
144};
145
146struct rpi_renderer {
147 struct weston_renderer base;
148
149 int single_buffer;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200150
151#ifdef ENABLE_EGL
152 EGLDisplay egl_display;
153
154 PFNEGLBINDWAYLANDDISPLAYWL bind_display;
155 PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
156 PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
157#endif
158 int has_bind_display;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300159};
160
161static inline struct rpir_surface *
162to_rpir_surface(struct weston_surface *surface)
163{
164 return surface->renderer_state;
165}
166
167static inline struct rpir_output *
168to_rpir_output(struct weston_output *output)
169{
170 return output->renderer_state;
171}
172
173static inline struct rpi_renderer *
174to_rpi_renderer(struct weston_compositor *compositor)
175{
176 return container_of(compositor->renderer, struct rpi_renderer, base);
177}
178
179static inline int
180int_max(int a, int b)
181{
182 return a > b ? a : b;
183}
184
185static inline void
186int_swap(int *a, int *b)
187{
188 int tmp = *a;
189 *a = *b;
190 *b = tmp;
191}
192
193static uint8_t
194float2uint8(float f)
195{
196 int v = roundf(f * 255.0f);
197
198 return v < 0 ? 0 : (v > 255 ? 255 : v);
199}
200
201static void
202rpi_resource_init(struct rpi_resource *resource)
203{
204 resource->handle = DISPMANX_NO_HANDLE;
205}
206
207static void
208rpi_resource_release(struct rpi_resource *resource)
209{
210 if (resource->handle == DISPMANX_NO_HANDLE)
211 return;
212
213 vc_dispmanx_resource_delete(resource->handle);
214 DBG("resource %p release\n", resource);
215 resource->handle = DISPMANX_NO_HANDLE;
216}
217
218static int
219rpi_resource_realloc(struct rpi_resource *resource, VC_IMAGE_TYPE_T ifmt,
220 int width, int height, int stride, int buffer_height)
221{
222 uint32_t dummy;
223
224 if (resource->handle != DISPMANX_NO_HANDLE &&
225 resource->width == width &&
226 resource->height == height &&
227 resource->stride == stride &&
228 resource->buffer_height == buffer_height &&
229 resource->ifmt == ifmt)
230 return 0;
231
232 rpi_resource_release(resource);
233
234 /* NOTE: if stride is not a multiple of 16 pixels in bytes,
235 * the vc_image_* functions may break. Dispmanx elements
236 * should be fine, though. Buffer_height probably has similar
237 * constraints, too.
238 */
239 resource->handle =
240 vc_dispmanx_resource_create(ifmt,
241 width | (stride << 16),
242 height | (buffer_height << 16),
243 &dummy);
244 if (resource->handle == DISPMANX_NO_HANDLE)
245 return -1;
246
247 resource->width = width;
248 resource->height = height;
249 resource->stride = stride;
250 resource->buffer_height = buffer_height;
251 resource->ifmt = ifmt;
252 DBG("resource %p alloc\n", resource);
253 return 1;
254}
255
256/* A firmware workaround for broken ALPHA_PREMULT + ALPHA_MIX hardware. */
257#define PREMULT_ALPHA_FLAG (1 << 31)
258
259static VC_IMAGE_TYPE_T
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500260shm_buffer_get_vc_format(struct wl_shm_buffer *buffer)
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300261{
262 switch (wl_shm_buffer_get_format(buffer)) {
263 case WL_SHM_FORMAT_XRGB8888:
264 return VC_IMAGE_XRGB8888;
265 case WL_SHM_FORMAT_ARGB8888:
266 return VC_IMAGE_ARGB8888 | PREMULT_ALPHA_FLAG;
Tomeu Vizoso03681892013-08-06 20:05:57 +0200267 case WL_SHM_FORMAT_RGB565:
268 return VC_IMAGE_RGB565;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300269 default:
270 /* invalid format */
271 return VC_IMAGE_MIN;
272 }
273}
274
275static int
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500276rpi_resource_update(struct rpi_resource *resource, struct weston_buffer *buffer,
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300277 pixman_region32_t *region)
278{
279 pixman_region32_t write_region;
280 pixman_box32_t *r;
281 VC_RECT_T rect;
282 VC_IMAGE_TYPE_T ifmt;
283 uint32_t *pixels;
284 int width;
285 int height;
286 int stride;
287 int ret;
288#ifdef HAVE_RESOURCE_WRITE_DATA_RECT
289 int n;
290#endif
291
292 if (!buffer)
293 return -1;
294
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500295 ifmt = shm_buffer_get_vc_format(buffer->shm_buffer);
296 width = wl_shm_buffer_get_width(buffer->shm_buffer);
297 height = wl_shm_buffer_get_height(buffer->shm_buffer);
298 stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
299 pixels = wl_shm_buffer_get_data(buffer->shm_buffer);
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300300
301 ret = rpi_resource_realloc(resource, ifmt & ~PREMULT_ALPHA_FLAG,
302 width, height, stride, height);
303 if (ret < 0)
304 return -1;
305
306 pixman_region32_init_rect(&write_region, 0, 0, width, height);
307 if (ret == 0)
308 pixman_region32_intersect(&write_region,
309 &write_region, region);
310
311#ifdef HAVE_RESOURCE_WRITE_DATA_RECT
312 /* XXX: Can this do a format conversion, so that scanout does not have to? */
313 r = pixman_region32_rectangles(&write_region, &n);
314 while (n--) {
315 vc_dispmanx_rect_set(&rect, r[n].x1, r[n].y1,
316 r[n].x2 - r[n].x1, r[n].y2 - r[n].y1);
317
318 ret = vc_dispmanx_resource_write_data_rect(resource->handle,
319 ifmt, stride,
320 pixels, &rect,
321 rect.x, rect.y);
322 DBG("%s: %p %ux%u@%u,%u, ret %d\n", __func__, resource,
323 rect.width, rect.height, rect.x, rect.y, ret);
324 if (ret)
325 break;
326 }
327#else
328 /* vc_dispmanx_resource_write_data() ignores ifmt,
329 * rect.x, rect.width, and uses stride only for computing
330 * the size of the transfer as rect.height * stride.
331 * Therefore we can only write rows starting at x=0.
332 * To be able to write more than one scanline at a time,
333 * the resource must have been created with the same stride
334 * as used here, and we must write full scanlines.
335 */
336
337 r = pixman_region32_extents(&write_region);
338 vc_dispmanx_rect_set(&rect, 0, r->y1, width, r->y2 - r->y1);
339 ret = vc_dispmanx_resource_write_data(resource->handle,
340 ifmt, stride, pixels, &rect);
341 DBG("%s: %p %ux%u@%u,%u, ret %d\n", __func__, resource,
342 width, r->y2 - r->y1, 0, r->y1, ret);
343#endif
344
345 pixman_region32_fini(&write_region);
346
347 return ret ? -1 : 0;
348}
349
350static struct rpir_surface *
351rpir_surface_create(struct rpi_renderer *renderer)
352{
353 struct rpir_surface *surface;
354
355 surface = calloc(1, sizeof *surface);
356 if (!surface)
357 return NULL;
358
359 wl_list_init(&surface->link);
360 surface->single_buffer = renderer->single_buffer;
361 surface->handle = DISPMANX_NO_HANDLE;
362 rpi_resource_init(&surface->resources[0]);
363 rpi_resource_init(&surface->resources[1]);
364 surface->front = &surface->resources[0];
365 if (surface->single_buffer)
366 surface->back = &surface->resources[0];
367 else
368 surface->back = &surface->resources[1];
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200369 surface->buffer_type = BUFFER_TYPE_NULL;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300370
371 pixman_region32_init(&surface->prev_damage);
372
373 return surface;
374}
375
376static void
377rpir_surface_destroy(struct rpir_surface *surface)
378{
379 wl_list_remove(&surface->link);
380
381 if (surface->handle != DISPMANX_NO_HANDLE)
382 weston_log("ERROR rpi: destroying on-screen element\n");
383
384 pixman_region32_fini(&surface->prev_damage);
385 rpi_resource_release(&surface->resources[0]);
386 rpi_resource_release(&surface->resources[1]);
387 DBG("rpir_surface %p destroyed (%u)\n", surface, surface->handle);
388
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200389 if (surface->egl_back != NULL) {
390 weston_buffer_reference(&surface->egl_back->buffer_ref, NULL);
391 free(surface->egl_back);
392 surface->egl_back = NULL;
393 }
394
395 if (surface->egl_front != NULL) {
396 weston_buffer_reference(&surface->egl_front->buffer_ref, NULL);
397 free(surface->egl_front);
398 surface->egl_front = NULL;
399 }
400
401 if (surface->egl_old_front != NULL) {
402 weston_buffer_reference(&surface->egl_old_front->buffer_ref, NULL);
403 free(surface->egl_old_front);
404 surface->egl_old_front = NULL;
405 }
406
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300407 free(surface);
408}
409
410static int
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500411rpir_surface_damage(struct rpir_surface *surface, struct weston_buffer *buffer,
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300412 pixman_region32_t *damage)
413{
414 pixman_region32_t upload;
415 int ret;
416
417 if (!pixman_region32_not_empty(damage))
418 return 0;
419
420 DBG("rpir_surface %p update resource %p\n", surface, surface->back);
421
422 /* XXX: todo: if no surface->handle, update front buffer directly
423 * to avoid creating a new back buffer */
424 if (surface->single_buffer) {
425 ret = rpi_resource_update(surface->front, buffer, damage);
426 } else {
427 pixman_region32_init(&upload);
428 pixman_region32_union(&upload, &surface->prev_damage, damage);
429 ret = rpi_resource_update(surface->back, buffer, &upload);
430 pixman_region32_fini(&upload);
431 }
432
433 pixman_region32_copy(&surface->prev_damage, damage);
434 surface->need_swap = 1;
435
436 return ret;
437}
438
439static void
440matrix_type_str(struct weston_matrix *matrix, char *buf, int len)
441{
442 static const char types[33] = "TSRO";
443 unsigned mask = matrix->type;
444 int i = 0;
445
446 while (mask && i < len - 1) {
447 if (mask & (1u << i))
448 *buf++ = types[i];
449 mask &= ~(1u << i);
450 i++;
451 }
452 *buf = '\0';
453}
454
455static void
456log_print_matrix(struct weston_matrix *matrix)
457{
458 char typestr[6];
459 float *d = matrix->d;
460
461 matrix_type_str(matrix, typestr, sizeof typestr);
462 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
463 d[0], d[4], d[8], d[12]);
464 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
465 d[1], d[5], d[9], d[13]);
466 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
467 d[2], d[6], d[10], d[14]);
468 weston_log_continue("%14.6e %14.6e %14.6e %14.6e type: %s\n",
469 d[3], d[7], d[11], d[15], typestr);
470}
471
472static void
473warn_bad_matrix(struct weston_matrix *total, struct weston_matrix *output,
474 struct weston_matrix *surface)
475{
476 static int n_warn;
477 char typestr[6];
478
479 if (n_warn++ == 10)
480 weston_log("%s: not showing more warnings\n", __func__);
481
482 if (n_warn > 10)
483 return;
484
485 weston_log("%s: warning: total transformation is not renderable:\n",
486 __func__);
487 log_print_matrix(total);
488
489 matrix_type_str(surface, typestr, sizeof typestr);
490 weston_log_continue("surface matrix type: %s\n", typestr);
491 matrix_type_str(output, typestr, sizeof typestr);
492 weston_log_continue("output matrix type: %s\n", typestr);
493}
494
495/*#define SURFACE_TRANSFORM */
496
497static int
498rpir_surface_compute_rects(struct rpir_surface *surface,
499 VC_RECT_T *src_rect, VC_RECT_T *dst_rect,
500 VC_IMAGE_TRANSFORM_T *flipmask)
501{
502 struct weston_output *output_base = surface->surface->output;
503 struct rpir_output *output = to_rpir_output(output_base);
504 struct weston_matrix matrix = surface->surface->transform.matrix;
505 VC_IMAGE_TRANSFORM_T flipt = 0;
506 int src_x, src_y;
507 int dst_x, dst_y;
508 int src_width, src_height;
509 int dst_width, dst_height;
510 struct weston_vector p1 = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
511 struct weston_vector p2 = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
512 int t;
513 int over;
514
515 /* XXX: take buffer transform into account */
516
517 /* src is in 16.16, dst is in 32.0 fixed point.
518 * Negative values are not allowed in VC_RECT_T.
519 * Clip size to output boundaries, firmware ignores
520 * huge elements like 8192x8192.
521 */
522
523 src_x = 0 << 16;
524 src_y = 0 << 16;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200525
526 if (surface->buffer_type == BUFFER_TYPE_EGL) {
527 struct weston_buffer *buffer = surface->egl_front->buffer_ref.buffer;
528
529 src_width = buffer->width << 16;
530 src_height = buffer->height << 16;
531 } else {
532 src_width = surface->front->width << 16;
533 src_height = surface->front->height << 16;
534 }
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300535
536 weston_matrix_multiply(&matrix, &output->matrix);
537
538#ifdef SURFACE_TRANSFORM
539 if (matrix.type >= WESTON_MATRIX_TRANSFORM_OTHER) {
540#else
541 if (matrix.type >= WESTON_MATRIX_TRANSFORM_ROTATE) {
542#endif
543 warn_bad_matrix(&matrix, &output->matrix,
544 &surface->surface->transform.matrix);
545 } else {
546 if (matrix.type & WESTON_MATRIX_TRANSFORM_ROTATE) {
547 if (fabsf(matrix.d[0]) < 1e-4f &&
548 fabsf(matrix.d[5]) < 1e-4f) {
549 flipt |= TRANSFORM_TRANSPOSE;
550 } else if (fabsf(matrix.d[1]) < 1e-4 &&
551 fabsf(matrix.d[4]) < 1e-4) {
552 /* no transpose */
553 } else {
554 warn_bad_matrix(&matrix, &output->matrix,
555 &surface->surface->transform.matrix);
556 }
557 }
558 }
559
560 p2.f[0] = surface->surface->geometry.width;
561 p2.f[1] = surface->surface->geometry.height;
562
563 /* transform top-left and bot-right corner into screen coordinates */
564 weston_matrix_transform(&matrix, &p1);
565 weston_matrix_transform(&matrix, &p2);
566
567 /* Compute the destination rectangle on screen, converting
568 * negative dimensions to flips.
569 */
570
571 dst_width = round(p2.f[0] - p1.f[0]);
572 if (dst_width < 0) {
573 dst_x = round(p2.f[0]);
574 dst_width = -dst_width;
575
576 if (!(flipt & TRANSFORM_TRANSPOSE))
577 flipt |= TRANSFORM_HFLIP;
578 else
579 flipt |= TRANSFORM_VFLIP;
580 } else {
581 dst_x = round(p1.f[0]);
582 }
583
584 dst_height = round(p2.f[1] - p1.f[1]);
585 if (dst_height < 0) {
586 dst_y = round(p2.f[1]);
587 dst_height = -dst_height;
588
589 if (!(flipt & TRANSFORM_TRANSPOSE))
590 flipt |= TRANSFORM_VFLIP;
591 else
592 flipt |= TRANSFORM_HFLIP;
593 } else {
594 dst_y = round(p1.f[1]);
595 }
596
597 if (dst_width == 0 || dst_height == 0) {
598 DBG("ignored, zero surface area before clipping\n");
599 return -1;
600 }
601
602#ifdef SURFACE_TRANSFORM
603 /* Dispmanx works as if you flipped the whole screen, when
604 * you flip an element. But, we want to flip an element in place.
605 * XXX: fixme
606 */
607 if (flipt & TRANSFORM_HFLIP)
608 dst_x = output_base->width - dst_x;
609 if (flipt & TRANSFORM_VFLIP)
610 dst_y = output_base->height - dst_y;
611 if (flipt & TRANSFORM_TRANSPOSE) {
612 int_swap(&dst_x, &dst_y);
613 int_swap(&dst_width, &dst_height);
614 }
615#else
616 switch (output_base->transform) {
617 case WL_OUTPUT_TRANSFORM_FLIPPED:
618 flipt = TRANSFORM_HFLIP;
619 break;
620 case WL_OUTPUT_TRANSFORM_NORMAL:
621 flipt = 0;
622 break;
623
624 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
625 flipt = TRANSFORM_HFLIP | TRANSFORM_VFLIP | TRANSFORM_TRANSPOSE;
626 break;
627 case WL_OUTPUT_TRANSFORM_90:
628 flipt = TRANSFORM_VFLIP | TRANSFORM_TRANSPOSE;
629 break;
630 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
631 flipt = TRANSFORM_VFLIP;
632 break;
633 case WL_OUTPUT_TRANSFORM_180:
634 flipt = TRANSFORM_HFLIP | TRANSFORM_VFLIP;
635 break;
636
637 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
638 flipt = TRANSFORM_TRANSPOSE;
639 break;
640 case WL_OUTPUT_TRANSFORM_270:
641 flipt = TRANSFORM_HFLIP | TRANSFORM_TRANSPOSE;
642 break;
643 default:
644 break;
645 }
646#endif
647
648 /* clip destination rectangle to screen dimensions */
649
650 if (dst_x < 0) {
651 t = (int64_t)dst_x * src_width / dst_width;
652 src_width += t;
653 dst_width += dst_x;
654 src_x -= t;
655 dst_x = 0;
656 }
657
658 if (dst_y < 0) {
659 t = (int64_t)dst_y * src_height / dst_height;
660 src_height += t;
661 dst_height += dst_y;
662 src_y -= t;
663 dst_y = 0;
664 }
665
666 over = dst_x + dst_width - output_base->width;
667 if (over > 0) {
668 t = (int64_t)over * src_width / dst_width;
669 src_width -= t;
670 dst_width -= over;
671 }
672
673 over = dst_y + dst_height - output_base->height;
674 if (over > 0) {
675 t = (int64_t)over * src_height / dst_height;
676 src_height -= t;
677 dst_height -= over;
678 }
679
680 src_width = int_max(src_width, 0);
681 src_height = int_max(src_height, 0);
682
683 DBG("rpir_surface %p %dx%d: p1 %f, %f; p2 %f, %f\n", surface,
684 surface->surface->geometry.width,
685 surface->surface->geometry.height,
686 p1.f[0], p1.f[1], p2.f[0], p2.f[1]);
687 DBG("src rect %d;%d, %d;%d, %d;%dx%d;%d\n",
688 src_x >> 16, src_x & 0xffff,
689 src_y >> 16, src_y & 0xffff,
690 src_width >> 16, src_width & 0xffff,
691 src_height >> 16, src_height & 0xffff);
692 DBG("dest rect %d, %d, %dx%d%s%s%s\n",
693 dst_x, dst_y, dst_width, dst_height,
694 (flipt & TRANSFORM_HFLIP) ? " hflip" : "",
695 (flipt & TRANSFORM_VFLIP) ? " vflip" : "",
696 (flipt & TRANSFORM_TRANSPOSE) ? " transp" : "");
697
698 assert(src_x >= 0);
699 assert(src_y >= 0);
700 assert(dst_x >= 0);
701 assert(dst_y >= 0);
702
703 if (dst_width < 1 || dst_height < 1) {
704 DBG("ignored, zero surface area after clipping\n");
705 return -1;
706 }
707
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200708 /* EGL buffers will be upside-down related to what DispmanX expects */
709 if (surface->buffer_type == BUFFER_TYPE_EGL)
710 flipt ^= TRANSFORM_VFLIP;
711
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300712 vc_dispmanx_rect_set(src_rect, src_x, src_y, src_width, src_height);
713 vc_dispmanx_rect_set(dst_rect, dst_x, dst_y, dst_width, dst_height);
714 *flipmask = flipt;
715
716 return 0;
717}
718
719static DISPMANX_TRANSFORM_T
720vc_image2dispmanx_transform(VC_IMAGE_TRANSFORM_T t)
721{
722 /* XXX: uhh, are these right? */
723 switch (t) {
724 case VC_IMAGE_ROT0:
725 return DISPMANX_NO_ROTATE;
726 case VC_IMAGE_MIRROR_ROT0:
727 return DISPMANX_FLIP_HRIZ;
728 case VC_IMAGE_MIRROR_ROT180:
729 return DISPMANX_FLIP_VERT;
730 case VC_IMAGE_ROT180:
731 return DISPMANX_ROTATE_180;
732 case VC_IMAGE_MIRROR_ROT90:
733 return DISPMANX_ROTATE_90 | DISPMANX_FLIP_HRIZ;
734 case VC_IMAGE_ROT270:
735 return DISPMANX_ROTATE_270;
736 case VC_IMAGE_ROT90:
737 return DISPMANX_ROTATE_90;
738 case VC_IMAGE_MIRROR_ROT270:
739 return DISPMANX_ROTATE_270 | DISPMANX_FLIP_VERT;
740 default:
741 assert(0 && "bad VC_IMAGE_TRANSFORM_T");
742 return DISPMANX_NO_ROTATE;
743 }
744}
745
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200746
747static DISPMANX_RESOURCE_HANDLE_T
748rpir_surface_get_resource(struct rpir_surface *surface)
749{
750 switch (surface->buffer_type) {
751 case BUFFER_TYPE_SHM:
752 case BUFFER_TYPE_NULL:
753 return surface->front->handle;
754 case BUFFER_TYPE_EGL:
755 if (surface->egl_front != NULL)
756 return surface->egl_front->resource_handle;
757 default:
758 return DISPMANX_NO_HANDLE;
759 }
760}
761
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300762static int
763rpir_surface_dmx_add(struct rpir_surface *surface, struct rpir_output *output,
764 DISPMANX_UPDATE_HANDLE_T update, int layer)
765{
766 /* Do not use DISPMANX_FLAGS_ALPHA_PREMULT here.
767 * If you define PREMULT and ALPHA_MIX, the hardware will not
768 * multiply the source color with the element alpha, leading to
769 * bad colors. Instead, we define PREMULT during pixel data upload.
770 */
771 VC_DISPMANX_ALPHA_T alphasetup = {
772 DISPMANX_FLAGS_ALPHA_FROM_SOURCE |
773 DISPMANX_FLAGS_ALPHA_MIX,
774 float2uint8(surface->surface->alpha), /* opacity 0-255 */
775 0 /* mask resource handle */
776 };
777 VC_RECT_T dst_rect;
778 VC_RECT_T src_rect;
779 VC_IMAGE_TRANSFORM_T flipmask;
780 int ret;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200781 DISPMANX_RESOURCE_HANDLE_T resource_handle;
782
783 resource_handle = rpir_surface_get_resource(surface);
784 if (resource_handle == DISPMANX_NO_HANDLE) {
785 weston_log("%s: no buffer yet, aborting\n", __func__);
786 return 0;
787 }
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300788
789 ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect,
790 &flipmask);
791 if (ret < 0)
792 return 0;
793
794 surface->handle = vc_dispmanx_element_add(
795 update,
796 output->display,
797 layer,
798 &dst_rect,
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200799 resource_handle,
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300800 &src_rect,
801 DISPMANX_PROTECTION_NONE,
802 &alphasetup,
803 NULL /* clamp */,
804 vc_image2dispmanx_transform(flipmask));
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200805 DBG("rpir_surface %p add %u, alpha %f resource %d\n", surface,
806 surface->handle, surface->surface->alpha, resource_handle);
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300807
808 return 1;
809}
810
811static void
812rpir_surface_dmx_swap(struct rpir_surface *surface,
813 DISPMANX_UPDATE_HANDLE_T update)
814{
815 VC_RECT_T rect;
816 pixman_box32_t *r;
817
818 /* XXX: skip, iff resource was not reallocated, and single-buffering */
819 vc_dispmanx_element_change_source(update, surface->handle,
820 surface->front->handle);
821
822 /* This is current damage now, after rpir_surface_damage() */
823 r = pixman_region32_extents(&surface->prev_damage);
824
825 vc_dispmanx_rect_set(&rect, r->x1, r->y1,
826 r->x2 - r->x1, r->y2 - r->y1);
827 vc_dispmanx_element_modified(update, surface->handle, &rect);
828 DBG("rpir_surface %p swap\n", surface);
829}
830
831static int
832rpir_surface_dmx_move(struct rpir_surface *surface,
833 DISPMANX_UPDATE_HANDLE_T update, int layer)
834{
835 uint8_t alpha = float2uint8(surface->surface->alpha);
836 VC_RECT_T dst_rect;
837 VC_RECT_T src_rect;
838 VC_IMAGE_TRANSFORM_T flipmask;
839 int ret;
840
841 /* XXX: return early, if all attributes stay the same */
842
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200843 if (surface->buffer_type == BUFFER_TYPE_EGL) {
844 DISPMANX_RESOURCE_HANDLE_T resource_handle;
845
846 resource_handle = rpir_surface_get_resource(surface);
847 if (resource_handle == DISPMANX_NO_HANDLE) {
848 weston_log("%s: no buffer yet, aborting\n", __func__);
849 return 0;
850 }
851
852 vc_dispmanx_element_change_source(update,
853 surface->handle,
854 resource_handle);
855 }
856
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300857 ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect,
858 &flipmask);
859 if (ret < 0)
860 return 0;
861
862 ret = vc_dispmanx_element_change_attributes(
863 update,
864 surface->handle,
865 ELEMENT_CHANGE_LAYER |
866 ELEMENT_CHANGE_OPACITY |
867 ELEMENT_CHANGE_TRANSFORM |
868 ELEMENT_CHANGE_DEST_RECT |
869 ELEMENT_CHANGE_SRC_RECT,
870 layer,
871 alpha,
872 &dst_rect,
873 &src_rect,
874 DISPMANX_NO_HANDLE,
875 /* This really is DISPMANX_TRANSFORM_T, no matter
876 * what the header says. */
877 vc_image2dispmanx_transform(flipmask));
878 DBG("rpir_surface %p move\n", surface);
879
880 if (ret)
881 return -1;
882
883 return 1;
884}
885
886static void
887rpir_surface_dmx_remove(struct rpir_surface *surface,
888 DISPMANX_UPDATE_HANDLE_T update)
889{
890 if (surface->handle == DISPMANX_NO_HANDLE)
891 return;
892
893 vc_dispmanx_element_remove(update, surface->handle);
894 DBG("rpir_surface %p remove %u\n", surface, surface->handle);
895 surface->handle = DISPMANX_NO_HANDLE;
896}
897
898static void
899rpir_surface_swap_pointers(struct rpir_surface *surface)
900{
901 struct rpi_resource *tmp;
902
903 tmp = surface->front;
904 surface->front = surface->back;
905 surface->back = tmp;
906 surface->need_swap = 0;
907 DBG("new back %p, new front %p\n", surface->back, surface->front);
908}
909
910static int
911is_surface_not_visible(struct weston_surface *surface)
912{
913 /* Return true, if surface is guaranteed to be totally obscured. */
914 int ret;
915 pixman_region32_t unocc;
916
917 pixman_region32_init(&unocc);
918 pixman_region32_subtract(&unocc, &surface->transform.boundingbox,
919 &surface->clip);
920 ret = !pixman_region32_not_empty(&unocc);
921 pixman_region32_fini(&unocc);
922
923 return ret;
924}
925
926static void
927rpir_surface_update(struct rpir_surface *surface, struct rpir_output *output,
928 DISPMANX_UPDATE_HANDLE_T update, int layer)
929{
930 int need_swap = surface->need_swap;
931 int ret;
932 int obscured;
933
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200934 if (surface->buffer_type == BUFFER_TYPE_EGL) {
935 if (surface->egl_back != NULL) {
936 assert(surface->egl_old_front == NULL);
937 surface->egl_old_front = surface->egl_front;
938 surface->egl_front = surface->egl_back;
939 surface->egl_back = NULL;
940 }
941 if (surface->egl_front->buffer_ref.buffer == NULL) {
942 weston_log("warning: client unreffed current front buffer\n");
943
944 wl_list_remove(&surface->link);
945 if (surface->handle == DISPMANX_NO_HANDLE) {
946 wl_list_init(&surface->link);
947 } else {
948 rpir_surface_dmx_remove(surface, update);
949 wl_list_insert(&output->surface_cleanup_list,
950 &surface->link);
951 }
952
953 goto out;
954 }
955 } else {
956 if (need_swap)
957 rpir_surface_swap_pointers(surface);
958 }
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300959
960 obscured = is_surface_not_visible(surface->surface);
961 if (obscured) {
962 DBG("rpir_surface %p totally obscured.\n", surface);
963
964 wl_list_remove(&surface->link);
965 if (surface->handle == DISPMANX_NO_HANDLE) {
966 wl_list_init(&surface->link);
967 } else {
968 rpir_surface_dmx_remove(surface, update);
969 wl_list_insert(&output->surface_cleanup_list,
970 &surface->link);
971 }
972
973 goto out;
974 }
975
976 if (surface->handle == DISPMANX_NO_HANDLE) {
977 ret = rpir_surface_dmx_add(surface, output, update, layer);
978 if (ret == 0) {
979 wl_list_remove(&surface->link);
980 wl_list_init(&surface->link);
981 } else if (ret < 0) {
982 weston_log("ERROR rpir_surface_dmx_add() failed.\n");
983 }
984 } else {
985 if (need_swap)
986 rpir_surface_dmx_swap(surface, update);
987
988 ret = rpir_surface_dmx_move(surface, update, layer);
989 if (ret == 0) {
990 rpir_surface_dmx_remove(surface, update);
991
992 wl_list_remove(&surface->link);
993 wl_list_insert(&output->surface_cleanup_list,
994 &surface->link);
995 } else if (ret < 0) {
996 weston_log("ERROR rpir_surface_dmx_move() failed.\n");
997 }
998 }
999
1000out:
1001 surface->layer = layer;
1002}
1003
1004static int
1005rpi_renderer_read_pixels(struct weston_output *base,
1006 pixman_format_code_t format, void *pixels,
1007 uint32_t x, uint32_t y,
1008 uint32_t width, uint32_t height)
1009{
1010 struct rpir_output *output = to_rpir_output(base);
1011 struct rpi_resource *buffer = &output->capture_buffer;
1012 VC_RECT_T rect;
1013 uint32_t fb_width, fb_height;
1014 uint32_t dst_pitch;
1015 uint32_t i;
1016 int ret;
1017
Hardeningff39efa2013-09-18 23:56:35 +02001018 fb_width = base->current_mode->width;
1019 fb_height = base->current_mode->height;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001020
1021 DBG("%s(%u, %u, %u, %u), resource %p\n", __func__,
1022 x, y, width, height, buffer);
1023
1024 if (format != PIXMAN_a8r8g8b8) {
1025 weston_log("rpi-renderer error: bad read_format\n");
1026 return -1;
1027 }
1028
1029 dst_pitch = fb_width * 4;
1030
1031 if (buffer->handle == DISPMANX_NO_HANDLE) {
1032 free(output->capture_data);
1033 output->capture_data = NULL;
1034
1035 ret = rpi_resource_realloc(buffer, VC_IMAGE_ARGB8888,
1036 fb_width, fb_height,
1037 dst_pitch, fb_height);
1038 if (ret < 0) {
1039 weston_log("rpi-renderer error: "
1040 "allocating read buffer failed\n");
1041 return -1;
1042 }
1043
1044 ret = vc_dispmanx_snapshot(output->display, buffer->handle,
1045 VC_IMAGE_ROT0);
1046 if (ret) {
1047 weston_log("rpi-renderer error: "
1048 "vc_dispmanx_snapshot returned %d\n", ret);
1049 return -1;
1050 }
1051 DBG("%s: snapshot done.\n", __func__);
1052 }
1053
1054 /*
1055 * If vc_dispmanx_resource_read_data was able to read sub-rectangles,
1056 * we could read directly into 'pixels'. But it cannot, it does not
1057 * use rect.x or rect.width, and does this:
1058 * host_start = (uint8_t *)dst_address + (dst_pitch * p_rect->y);
1059 * In other words, it is only good for reading the full buffer in
1060 * one go.
1061 */
1062 vc_dispmanx_rect_set(&rect, 0, 0, fb_width, fb_height);
1063
1064 if (x == 0 && y == 0 && width == fb_width && height == fb_height) {
1065 ret = vc_dispmanx_resource_read_data(buffer->handle, &rect,
1066 pixels, dst_pitch);
1067 if (ret) {
1068 weston_log("rpi-renderer error: "
1069 "resource_read_data returned %d\n", ret);
1070 return -1;
1071 }
1072 DBG("%s: full frame done.\n", __func__);
1073 return 0;
1074 }
1075
1076 if (!output->capture_data) {
1077 output->capture_data = malloc(fb_height * dst_pitch);
1078 if (!output->capture_data) {
1079 weston_log("rpi-renderer error: "
1080 "out of memory\n");
1081 return -1;
1082 }
1083
1084 ret = vc_dispmanx_resource_read_data(buffer->handle, &rect,
1085 output->capture_data,
1086 dst_pitch);
1087 if (ret) {
1088 weston_log("rpi-renderer error: "
1089 "resource_read_data returned %d\n", ret);
1090 return -1;
1091 }
1092 }
1093
1094 for (i = 0; i < height; i++) {
1095 uint8_t *src = output->capture_data +
1096 (y + i) * dst_pitch + x * 4;
1097 uint8_t *dst = (uint8_t *)pixels + i * width * 4;
1098 memcpy(dst, src, width * 4);
1099 }
1100
1101 return 0;
1102}
1103
1104static void
1105rpir_output_dmx_remove_all(struct rpir_output *output,
1106 DISPMANX_UPDATE_HANDLE_T update)
1107{
1108 struct rpir_surface *surface;
1109
1110 while (!wl_list_empty(&output->surface_list)) {
1111 surface = container_of(output->surface_list.next,
1112 struct rpir_surface, link);
1113 rpir_surface_dmx_remove(surface, update);
1114
1115 wl_list_remove(&surface->link);
1116 wl_list_insert(&output->surface_cleanup_list, &surface->link);
1117 }
1118}
1119
1120static void
1121output_compute_matrix(struct weston_output *base)
1122{
1123 struct rpir_output *output = to_rpir_output(base);
1124 struct weston_matrix *matrix = &output->matrix;
1125 const float half_w = 0.5f * base->width;
1126 const float half_h = 0.5f * base->height;
1127 float mag;
1128 float dx, dy;
1129
1130 weston_matrix_init(matrix);
1131 weston_matrix_translate(matrix, -base->x, -base->y, 0.0f);
1132
1133#ifdef SURFACE_TRANSFORM
1134 weston_matrix_translate(matrix, -half_w, -half_h, 0.0f);
1135 switch (base->transform) {
1136 case WL_OUTPUT_TRANSFORM_FLIPPED:
1137 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1138 case WL_OUTPUT_TRANSFORM_NORMAL:
1139 /* weston_matrix_rotate_xy(matrix, 1.0f, 0.0f); no-op */
1140 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1141 break;
1142
1143 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
1144 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1145 case WL_OUTPUT_TRANSFORM_90:
1146 weston_matrix_rotate_xy(matrix, 0.0f, 1.0f);
1147 weston_matrix_translate(matrix, half_h, half_w, 0.0f);
1148 break;
1149
1150 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
1151 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1152 case WL_OUTPUT_TRANSFORM_180:
1153 weston_matrix_rotate_xy(matrix, -1.0f, 0.0f);
1154 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1155 break;
1156
1157 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
1158 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1159 case WL_OUTPUT_TRANSFORM_270:
1160 weston_matrix_rotate_xy(matrix, 0.0f, -1.0f);
1161 weston_matrix_translate(matrix, half_h, half_w, 0.0f);
1162 break;
1163
1164 default:
1165 break;
1166 }
1167#endif
1168
1169 if (base->zoom.active) {
1170 /* The base->zoom stuff is in GL coordinate system */
1171 mag = 1.0f / (1.0f - base->zoom.spring_z.current);
1172 dx = -(base->zoom.trans_x + 1.0f) * half_w;
1173 dy = -(base->zoom.trans_y + 1.0f) * half_h;
1174 weston_matrix_translate(matrix, dx, dy, 0.0f);
1175 weston_matrix_scale(matrix, mag, mag, 1.0f);
1176 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1177 }
1178}
1179
1180/* Note: this won't work right for multiple outputs. A DispmanX Element
1181 * is tied to one DispmanX Display, i.e. output.
1182 */
1183static void
1184rpi_renderer_repaint_output(struct weston_output *base,
1185 pixman_region32_t *output_damage)
1186{
1187 struct weston_compositor *compositor = base->compositor;
1188 struct rpir_output *output = to_rpir_output(base);
1189 struct weston_surface *ws;
1190 struct rpir_surface *surface;
1191 struct wl_list done_list;
1192 int layer = 1;
1193
1194 assert(output->update != DISPMANX_NO_HANDLE);
1195
1196 output_compute_matrix(base);
1197
1198 rpi_resource_release(&output->capture_buffer);
1199 free(output->capture_data);
1200 output->capture_data = NULL;
1201
1202 /* update all renderable surfaces */
1203 wl_list_init(&done_list);
1204 wl_list_for_each_reverse(ws, &compositor->surface_list, link) {
1205 if (ws->plane != &compositor->primary_plane)
1206 continue;
1207
1208 surface = to_rpir_surface(ws);
1209 assert(!wl_list_empty(&surface->link) ||
1210 surface->handle == DISPMANX_NO_HANDLE);
1211
1212 wl_list_remove(&surface->link);
1213 wl_list_insert(&done_list, &surface->link);
1214 rpir_surface_update(surface, output, output->update, layer++);
1215 }
1216
1217 /* Remove all surfaces that are still on screen, but were
1218 * not rendered this time.
1219 */
1220 rpir_output_dmx_remove_all(output, output->update);
1221
1222 wl_list_insert_list(&output->surface_list, &done_list);
1223 output->update = DISPMANX_NO_HANDLE;
1224
1225 /* The frame_signal is emitted in rpi_renderer_finish_frame(),
1226 * so that the firmware can capture the up-to-date contents.
1227 */
1228}
1229
1230static void
1231rpi_renderer_flush_damage(struct weston_surface *base)
1232{
1233 /* Called for every surface just before repainting it, if
1234 * having an shm buffer.
1235 */
1236 struct rpir_surface *surface = to_rpir_surface(base);
Jason Ekstrand6bd62942013-06-20 20:38:23 -05001237 struct weston_buffer *buffer = surface->buffer_ref.buffer;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001238 int ret;
1239
1240 assert(buffer);
Jason Ekstrand6bd62942013-06-20 20:38:23 -05001241 assert(wl_shm_buffer_get(buffer->resource));
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001242
1243 ret = rpir_surface_damage(surface, buffer, &base->damage);
1244 if (ret)
1245 weston_log("%s error: updating Dispmanx resource failed.\n",
1246 __func__);
1247
1248 weston_buffer_reference(&surface->buffer_ref, NULL);
1249}
1250
1251static void
Jason Ekstrand6bd62942013-06-20 20:38:23 -05001252rpi_renderer_attach(struct weston_surface *base, struct weston_buffer *buffer)
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001253{
1254 /* Called every time a client commits an attach. */
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001255 struct rpir_surface *surface = to_rpir_surface(base);
1256
1257 assert(surface);
1258 if (!surface)
1259 return;
1260
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001261 if (surface->buffer_type == BUFFER_TYPE_SHM) {
1262 if (!surface->single_buffer)
1263 /* XXX: need to check if in middle of update */
1264 rpi_resource_release(surface->back);
1265
1266 if (surface->handle == DISPMANX_NO_HANDLE)
1267 /* XXX: cannot do this, if middle of an update */
1268 rpi_resource_release(surface->front);
1269
1270 weston_buffer_reference(&surface->buffer_ref, NULL);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001271 }
1272
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001273 /* If buffer is NULL, Weston core unmaps the surface, the surface
1274 * will not appear in repaint list, and so rpi_renderer_repaint_output
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001275 * will remove the DispmanX element. Later, for SHM, also the front
1276 * buffer will be released in the cleanup_list processing.
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001277 */
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001278 if (!buffer)
1279 return;
1280
1281 if (wl_shm_buffer_get(buffer->resource)) {
1282 surface->buffer_type = BUFFER_TYPE_SHM;
1283 buffer->shm_buffer = wl_shm_buffer_get(buffer->resource);
1284 buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer);
1285 buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer);
1286
1287 weston_buffer_reference(&surface->buffer_ref, buffer);
1288 } else {
1289#if ENABLE_EGL
1290 struct rpi_renderer *renderer = to_rpi_renderer(base->compositor);
1291 struct wl_resource *wl_resource =
1292 (struct wl_resource *)buffer->resource;
1293
1294 if (!renderer->has_bind_display ||
1295 !renderer->query_buffer(renderer->egl_display,
1296 wl_resource,
1297 EGL_WIDTH, &buffer->width)) {
1298 weston_log("unhandled buffer type!\n");
1299 weston_buffer_reference(&surface->buffer_ref, NULL);
1300 surface->buffer_type = BUFFER_TYPE_NULL;
1301 }
1302
1303 renderer->query_buffer(renderer->egl_display,
1304 wl_resource,
1305 EGL_HEIGHT, &buffer->height);
1306
1307 surface->buffer_type = BUFFER_TYPE_EGL;
1308
1309 if(surface->egl_back == NULL)
1310 surface->egl_back = calloc(1, sizeof *surface->egl_back);
1311
1312 weston_buffer_reference(&surface->egl_back->buffer_ref, buffer);
1313 surface->egl_back->resource_handle =
1314 vc_dispmanx_get_handle_from_wl_buffer(wl_resource);
1315#else
1316 weston_log("unhandled buffer type!\n");
1317 weston_buffer_reference(&surface->buffer_ref, NULL);
1318 surface->buffer_type = BUFFER_TYPE_NULL;
1319#endif
1320 }
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001321}
1322
1323static int
1324rpi_renderer_create_surface(struct weston_surface *base)
1325{
1326 struct rpi_renderer *renderer = to_rpi_renderer(base->compositor);
1327 struct rpir_surface *surface;
1328
1329 assert(base->renderer_state == NULL);
1330
1331 surface = rpir_surface_create(renderer);
1332 if (!surface)
1333 return -1;
1334
1335 surface->surface = base;
1336 base->renderer_state = surface;
1337 return 0;
1338}
1339
1340static void
1341rpi_renderer_surface_set_color(struct weston_surface *base,
1342 float red, float green, float blue, float alpha)
1343{
1344 struct rpir_surface *surface = to_rpir_surface(base);
1345 uint8_t color[4];
1346 VC_RECT_T rect;
1347 int ret;
1348
1349 assert(surface);
1350
1351 ret = rpi_resource_realloc(surface->back, VC_IMAGE_ARGB8888,
1352 1, 1, 4, 1);
1353 if (ret < 0) {
1354 weston_log("Error: %s: rpi_resource_realloc failed.\n",
1355 __func__);
1356 return;
1357 }
1358
1359 color[0] = float2uint8(blue);
1360 color[1] = float2uint8(green);
1361 color[2] = float2uint8(red);
1362 color[3] = float2uint8(alpha);
1363
1364 vc_dispmanx_rect_set(&rect, 0, 0, 1, 1);
1365 ret = vc_dispmanx_resource_write_data(surface->back->handle,
1366 VC_IMAGE_ARGB8888,
1367 4, color, &rect);
1368 if (ret) {
1369 weston_log("Error: %s: resource_write_data failed.\n",
1370 __func__);
1371 return;
1372 }
1373
1374 DBG("%s: resource %p solid color BGRA %u,%u,%u,%u\n", __func__,
1375 surface->back, color[0], color[1], color[2], color[3]);
1376
1377 /*pixman_region32_copy(&surface->prev_damage, damage);*/
1378 surface->need_swap = 1;
1379}
1380
1381static void
1382rpi_renderer_destroy_surface(struct weston_surface *base)
1383{
1384 struct rpir_surface *surface = to_rpir_surface(base);
1385
1386 assert(surface);
1387 assert(surface->surface == base);
1388 if (!surface)
1389 return;
1390
1391 surface->surface = NULL;
1392 base->renderer_state = NULL;
1393
1394 /* If guaranteed to not be on screen, just detroy it. */
1395 if (wl_list_empty(&surface->link))
1396 rpir_surface_destroy(surface);
1397
1398 /* Otherwise, the surface is either on screen and needs
1399 * to be removed by a repaint update, or it is in the
1400 * surface_cleanup_list, and will be destroyed by
1401 * rpi_renderer_finish_frame().
1402 */
1403}
1404
1405static void
1406rpi_renderer_destroy(struct weston_compositor *compositor)
1407{
1408 struct rpi_renderer *renderer = to_rpi_renderer(compositor);
1409
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001410#if ENABLE_EGL
1411 if (renderer->has_bind_display)
1412 renderer->unbind_display(renderer->egl_display,
1413 compositor->wl_display);
1414#endif
1415
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001416 free(renderer);
1417 compositor->renderer = NULL;
1418}
1419
1420WL_EXPORT int
1421rpi_renderer_create(struct weston_compositor *compositor,
1422 const struct rpi_renderer_parameters *params)
1423{
1424 struct rpi_renderer *renderer;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001425#if ENABLE_EGL
1426 const char *extensions;
1427 EGLBoolean ret;
1428 EGLint major, minor;
1429#endif
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001430
1431 weston_log("Initializing the DispmanX compositing renderer\n");
1432
1433 renderer = calloc(1, sizeof *renderer);
1434 if (renderer == NULL)
1435 return -1;
1436
1437 renderer->single_buffer = params->single_buffer;
1438
1439 renderer->base.read_pixels = rpi_renderer_read_pixels;
1440 renderer->base.repaint_output = rpi_renderer_repaint_output;
1441 renderer->base.flush_damage = rpi_renderer_flush_damage;
1442 renderer->base.attach = rpi_renderer_attach;
1443 renderer->base.create_surface = rpi_renderer_create_surface;
1444 renderer->base.surface_set_color = rpi_renderer_surface_set_color;
1445 renderer->base.destroy_surface = rpi_renderer_destroy_surface;
1446 renderer->base.destroy = rpi_renderer_destroy;
1447
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001448#ifdef ENABLE_EGL
1449 renderer->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
1450 if (renderer->egl_display == EGL_NO_DISPLAY) {
1451 weston_log("failed to create EGL display\n");
1452 return -1;
1453 }
1454
1455 if (!eglInitialize(renderer->egl_display, &major, &minor)) {
1456 weston_log("failed to initialize EGL display\n");
1457 return -1;
1458 }
1459
1460 renderer->bind_display =
1461 (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
1462 renderer->unbind_display =
1463 (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
1464 renderer->query_buffer =
1465 (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
1466
1467 extensions = (const char *) eglQueryString(renderer->egl_display,
1468 EGL_EXTENSIONS);
1469 if (!extensions) {
1470 weston_log("Retrieving EGL extension string failed.\n");
1471 return -1;
1472 }
1473
1474 if (strstr(extensions, "EGL_WL_bind_wayland_display"))
1475 renderer->has_bind_display = 1;
1476
1477 if (renderer->has_bind_display) {
1478 ret = renderer->bind_display(renderer->egl_display,
1479 compositor->wl_display);
1480 if (!ret)
1481 renderer->has_bind_display = 0;
1482 }
1483#endif
1484
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001485 compositor->renderer = &renderer->base;
1486 compositor->read_format = PIXMAN_a8r8g8b8;
1487 /* WESTON_CAP_ROTATION_ANY not supported */
1488
Tomeu Vizoso03681892013-08-06 20:05:57 +02001489 wl_display_add_shm_format(compositor->wl_display, WL_SHM_FORMAT_RGB565);
1490
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001491 return 0;
1492}
1493
1494WL_EXPORT int
1495rpi_renderer_output_create(struct weston_output *base,
1496 DISPMANX_DISPLAY_HANDLE_T display)
1497{
1498 struct rpir_output *output;
1499
1500 assert(base->renderer_state == NULL);
1501
1502 output = calloc(1, sizeof *output);
1503 if (!output)
1504 return -1;
1505
1506 output->display = display;
1507 output->update = DISPMANX_NO_HANDLE;
1508 wl_list_init(&output->surface_list);
1509 wl_list_init(&output->surface_cleanup_list);
1510 rpi_resource_init(&output->capture_buffer);
1511 base->renderer_state = output;
1512
1513 return 0;
1514}
1515
1516WL_EXPORT void
1517rpi_renderer_output_destroy(struct weston_output *base)
1518{
1519 struct rpir_output *output = to_rpir_output(base);
1520 struct rpir_surface *surface;
1521 DISPMANX_UPDATE_HANDLE_T update;
1522
1523 rpi_resource_release(&output->capture_buffer);
1524 free(output->capture_data);
1525 output->capture_data = NULL;
1526
1527 update = vc_dispmanx_update_start(0);
1528 rpir_output_dmx_remove_all(output, update);
1529 vc_dispmanx_update_submit_sync(update);
1530
1531 while (!wl_list_empty(&output->surface_cleanup_list)) {
1532 surface = container_of(output->surface_cleanup_list.next,
1533 struct rpir_surface, link);
1534 if (surface->surface)
1535 surface->surface->renderer_state = NULL;
1536 rpir_surface_destroy(surface);
1537 }
1538
1539 free(output);
1540 base->renderer_state = NULL;
1541}
1542
1543WL_EXPORT void
1544rpi_renderer_set_update_handle(struct weston_output *base,
1545 DISPMANX_UPDATE_HANDLE_T handle)
1546{
1547 struct rpir_output *output = to_rpir_output(base);
1548
1549 output->update = handle;
1550}
1551
1552WL_EXPORT void
1553rpi_renderer_finish_frame(struct weston_output *base)
1554{
1555 struct rpir_output *output = to_rpir_output(base);
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001556 struct weston_compositor *compositor = base->compositor;
1557 struct weston_surface *ws;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001558 struct rpir_surface *surface;
1559
1560 while (!wl_list_empty(&output->surface_cleanup_list)) {
1561 surface = container_of(output->surface_cleanup_list.next,
1562 struct rpir_surface, link);
1563
1564 if (surface->surface) {
1565 /* The weston_surface still exists, but is
1566 * temporarily not visible, and hence its Element
1567 * was removed. The current front buffer contents
1568 * must be preserved.
1569 */
1570 if (!surface->single_buffer)
1571 rpi_resource_release(surface->back);
1572
1573 wl_list_remove(&surface->link);
1574 wl_list_init(&surface->link);
1575 } else {
1576 rpir_surface_destroy(surface);
1577 }
1578 }
1579
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001580 wl_list_for_each(ws, &compositor->surface_list, link) {
1581 surface = to_rpir_surface(ws);
1582
1583 if (surface->buffer_type != BUFFER_TYPE_EGL)
1584 continue;
1585
1586 if(surface->egl_old_front == NULL)
1587 continue;
1588
1589 weston_buffer_reference(&surface->egl_old_front->buffer_ref, NULL);
1590 free(surface->egl_old_front);
1591 surface->egl_old_front = NULL;
1592 }
1593
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001594 wl_signal_emit(&base->frame_signal, base);
1595}