blob: c6b924c627a9489dc879add93e934b279c8c82ec [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
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +010082/* If we had a vc_dispmanx_element_set_opaque_rect()... */
83/*#define HAVE_ELEMENT_SET_OPAQUE_RECT 1*/
84
Pekka Paalanend7265bc2013-05-22 18:03:06 +030085struct rpi_resource {
86 DISPMANX_RESOURCE_HANDLE_T handle;
87 int width;
88 int height; /* height of the image (valid pixel data) */
89 int stride; /* bytes */
90 int buffer_height; /* height of the buffer */
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +010091 int enable_opaque_regions;
Pekka Paalanend7265bc2013-05-22 18:03:06 +030092 VC_IMAGE_TYPE_T ifmt;
93};
94
95struct rpir_output;
96
Tomeu Vizosob4659eb2013-10-07 11:02:20 +020097struct rpir_egl_buffer {
98 struct weston_buffer_reference buffer_ref;
99 DISPMANX_RESOURCE_HANDLE_T resource_handle;
100};
101
102enum buffer_type {
103 BUFFER_TYPE_NULL,
104 BUFFER_TYPE_SHM,
105 BUFFER_TYPE_EGL
106};
107
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300108struct rpir_surface {
109 struct weston_surface *surface;
110
Jason Ekstranda7af7042013-10-12 22:38:11 -0500111 struct wl_list views;
112 int visible_views;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300113 int need_swap;
114 int single_buffer;
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +0100115 int enable_opaque_regions;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300116
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;
Ander Conselvan de Oliveiraaa398ae2013-10-25 16:26:33 +0300128
129 struct wl_listener surface_destroy_listener;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300130};
131
Jason Ekstranda7af7042013-10-12 22:38:11 -0500132struct rpir_view {
133 struct rpir_surface *surface;
134 struct wl_list surface_link;
135 struct weston_view *view;
136
137 /* If link is empty, the view is guaranteed to not be on screen,
138 * i.e. updates removing Elements have completed.
139 */
140 struct wl_list link;
141
142 DISPMANX_ELEMENT_HANDLE_T handle;
143 int layer;
Tomeu Vizoso96dc9e42013-10-28 10:17:45 +0100144
145 struct wl_listener view_destroy_listener;
Jason Ekstranda7af7042013-10-12 22:38:11 -0500146};
147
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300148struct rpir_output {
149 DISPMANX_DISPLAY_HANDLE_T display;
150
151 DISPMANX_UPDATE_HANDLE_T update;
152 struct weston_matrix matrix;
153
154 /* all Elements currently on screen */
Jason Ekstranda7af7042013-10-12 22:38:11 -0500155 struct wl_list view_list; /* struct rpir_surface::link */
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300156
157 /* Elements just removed, waiting for update completion */
Jason Ekstranda7af7042013-10-12 22:38:11 -0500158 struct wl_list view_cleanup_list; /* struct rpir_surface::link */
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300159
160 struct rpi_resource capture_buffer;
161 uint8_t *capture_data;
162};
163
164struct rpi_renderer {
165 struct weston_renderer base;
166
167 int single_buffer;
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +0100168 int enable_opaque_regions;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200169
170#ifdef ENABLE_EGL
171 EGLDisplay egl_display;
172
173 PFNEGLBINDWAYLANDDISPLAYWL bind_display;
174 PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
175 PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
176#endif
177 int has_bind_display;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300178};
179
Ander Conselvan de Oliveiraaa398ae2013-10-25 16:26:33 +0300180static int
181rpi_renderer_create_surface(struct weston_surface *base);
182
Tomeu Vizoso96dc9e42013-10-28 10:17:45 +0100183static int
184rpi_renderer_create_view(struct weston_view *base);
185
186static void
187rpir_view_handle_view_destroy(struct wl_listener *listener, void *data);
188
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300189static inline struct rpir_surface *
190to_rpir_surface(struct weston_surface *surface)
191{
Ander Conselvan de Oliveiraaa398ae2013-10-25 16:26:33 +0300192 if (!surface->renderer_state)
193 rpi_renderer_create_surface(surface);
194
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300195 return surface->renderer_state;
196}
197
Jason Ekstranda7af7042013-10-12 22:38:11 -0500198static inline struct rpir_view *
199to_rpir_view(struct weston_view *view)
200{
Tomeu Vizoso96dc9e42013-10-28 10:17:45 +0100201 if (!view->renderer_state)
202 rpi_renderer_create_view(view);
203
Jason Ekstranda7af7042013-10-12 22:38:11 -0500204 return view->renderer_state;
205}
206
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300207static inline struct rpir_output *
208to_rpir_output(struct weston_output *output)
209{
210 return output->renderer_state;
211}
212
213static inline struct rpi_renderer *
214to_rpi_renderer(struct weston_compositor *compositor)
215{
216 return container_of(compositor->renderer, struct rpi_renderer, base);
217}
218
219static inline int
220int_max(int a, int b)
221{
222 return a > b ? a : b;
223}
224
225static inline void
226int_swap(int *a, int *b)
227{
228 int tmp = *a;
229 *a = *b;
230 *b = tmp;
231}
232
233static uint8_t
234float2uint8(float f)
235{
236 int v = roundf(f * 255.0f);
237
238 return v < 0 ? 0 : (v > 255 ? 255 : v);
239}
240
241static void
242rpi_resource_init(struct rpi_resource *resource)
243{
244 resource->handle = DISPMANX_NO_HANDLE;
245}
246
247static void
248rpi_resource_release(struct rpi_resource *resource)
249{
250 if (resource->handle == DISPMANX_NO_HANDLE)
251 return;
252
253 vc_dispmanx_resource_delete(resource->handle);
254 DBG("resource %p release\n", resource);
255 resource->handle = DISPMANX_NO_HANDLE;
256}
257
258static int
259rpi_resource_realloc(struct rpi_resource *resource, VC_IMAGE_TYPE_T ifmt,
260 int width, int height, int stride, int buffer_height)
261{
262 uint32_t dummy;
263
264 if (resource->handle != DISPMANX_NO_HANDLE &&
265 resource->width == width &&
266 resource->height == height &&
267 resource->stride == stride &&
268 resource->buffer_height == buffer_height &&
269 resource->ifmt == ifmt)
270 return 0;
271
272 rpi_resource_release(resource);
273
274 /* NOTE: if stride is not a multiple of 16 pixels in bytes,
275 * the vc_image_* functions may break. Dispmanx elements
276 * should be fine, though. Buffer_height probably has similar
277 * constraints, too.
278 */
279 resource->handle =
280 vc_dispmanx_resource_create(ifmt,
281 width | (stride << 16),
282 height | (buffer_height << 16),
283 &dummy);
284 if (resource->handle == DISPMANX_NO_HANDLE)
285 return -1;
286
287 resource->width = width;
288 resource->height = height;
289 resource->stride = stride;
290 resource->buffer_height = buffer_height;
291 resource->ifmt = ifmt;
292 DBG("resource %p alloc\n", resource);
293 return 1;
294}
295
296/* A firmware workaround for broken ALPHA_PREMULT + ALPHA_MIX hardware. */
297#define PREMULT_ALPHA_FLAG (1 << 31)
298
299static VC_IMAGE_TYPE_T
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500300shm_buffer_get_vc_format(struct wl_shm_buffer *buffer)
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300301{
302 switch (wl_shm_buffer_get_format(buffer)) {
303 case WL_SHM_FORMAT_XRGB8888:
304 return VC_IMAGE_XRGB8888;
305 case WL_SHM_FORMAT_ARGB8888:
306 return VC_IMAGE_ARGB8888 | PREMULT_ALPHA_FLAG;
Tomeu Vizoso03681892013-08-06 20:05:57 +0200307 case WL_SHM_FORMAT_RGB565:
308 return VC_IMAGE_RGB565;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300309 default:
310 /* invalid format */
311 return VC_IMAGE_MIN;
312 }
313}
314
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +0100315#ifndef HAVE_ELEMENT_SET_OPAQUE_RECT
316static uint32_t *
317apply_opaque_region(struct wl_shm_buffer *buffer,
318 pixman_region32_t *opaque_region)
319{
320 uint32_t *src, *dst;
321 int width;
322 int height;
323 int stride;
324 int x, y;
325
326 width = wl_shm_buffer_get_width(buffer);
327 height = wl_shm_buffer_get_height(buffer);
328 stride = wl_shm_buffer_get_stride(buffer);
329 src = wl_shm_buffer_get_data(buffer);
330
331 dst = malloc(height * stride);
332 if (dst == NULL) {
333 weston_log("rpi-renderer error: out of memory\n");
334 return NULL;
335 }
336
337 for (y = 0; y < height; y++) {
338 for (x = 0; x < width; x++) {
339 int i = y * stride / 4 + x;
340 pixman_box32_t box;
341 if (pixman_region32_contains_point (opaque_region, x, y, &box)) {
342 dst[i] = src[i] | 0xff000000;
343 } else {
344 dst[i] = src[i];
345 }
346 }
347 }
348
349 return dst;
350}
351#endif
352
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300353static int
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500354rpi_resource_update(struct rpi_resource *resource, struct weston_buffer *buffer,
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +0100355 pixman_region32_t *region, pixman_region32_t *opaque_region)
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300356{
357 pixman_region32_t write_region;
358 pixman_box32_t *r;
359 VC_RECT_T rect;
360 VC_IMAGE_TYPE_T ifmt;
361 uint32_t *pixels;
362 int width;
363 int height;
364 int stride;
365 int ret;
366#ifdef HAVE_RESOURCE_WRITE_DATA_RECT
367 int n;
368#endif
369
370 if (!buffer)
371 return -1;
372
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500373 ifmt = shm_buffer_get_vc_format(buffer->shm_buffer);
374 width = wl_shm_buffer_get_width(buffer->shm_buffer);
375 height = wl_shm_buffer_get_height(buffer->shm_buffer);
376 stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
377 pixels = wl_shm_buffer_get_data(buffer->shm_buffer);
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300378
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +0100379#ifndef HAVE_ELEMENT_SET_OPAQUE_RECT
380 if (pixman_region32_not_empty(opaque_region) &&
381 wl_shm_buffer_get_format(buffer->shm_buffer) == WL_SHM_FORMAT_ARGB8888 &&
382 resource->enable_opaque_regions) {
383 pixels = apply_opaque_region(buffer->shm_buffer, opaque_region);
384
385 if (!pixels)
386 return -1;
387 }
388#endif
389
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300390 ret = rpi_resource_realloc(resource, ifmt & ~PREMULT_ALPHA_FLAG,
391 width, height, stride, height);
392 if (ret < 0)
393 return -1;
394
395 pixman_region32_init_rect(&write_region, 0, 0, width, height);
396 if (ret == 0)
397 pixman_region32_intersect(&write_region,
398 &write_region, region);
399
Neil Robertse5051712013-11-13 15:44:06 +0000400 wl_shm_buffer_begin_access(buffer->shm_buffer);
401
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300402#ifdef HAVE_RESOURCE_WRITE_DATA_RECT
403 /* XXX: Can this do a format conversion, so that scanout does not have to? */
404 r = pixman_region32_rectangles(&write_region, &n);
405 while (n--) {
406 vc_dispmanx_rect_set(&rect, r[n].x1, r[n].y1,
407 r[n].x2 - r[n].x1, r[n].y2 - r[n].y1);
408
409 ret = vc_dispmanx_resource_write_data_rect(resource->handle,
410 ifmt, stride,
411 pixels, &rect,
412 rect.x, rect.y);
413 DBG("%s: %p %ux%u@%u,%u, ret %d\n", __func__, resource,
414 rect.width, rect.height, rect.x, rect.y, ret);
415 if (ret)
416 break;
417 }
418#else
419 /* vc_dispmanx_resource_write_data() ignores ifmt,
420 * rect.x, rect.width, and uses stride only for computing
421 * the size of the transfer as rect.height * stride.
422 * Therefore we can only write rows starting at x=0.
423 * To be able to write more than one scanline at a time,
424 * the resource must have been created with the same stride
425 * as used here, and we must write full scanlines.
426 */
427
428 r = pixman_region32_extents(&write_region);
429 vc_dispmanx_rect_set(&rect, 0, r->y1, width, r->y2 - r->y1);
430 ret = vc_dispmanx_resource_write_data(resource->handle,
431 ifmt, stride, pixels, &rect);
432 DBG("%s: %p %ux%u@%u,%u, ret %d\n", __func__, resource,
433 width, r->y2 - r->y1, 0, r->y1, ret);
434#endif
435
Neil Robertse5051712013-11-13 15:44:06 +0000436 wl_shm_buffer_end_access(buffer->shm_buffer);
437
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300438 pixman_region32_fini(&write_region);
439
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +0100440#ifndef HAVE_ELEMENT_SET_OPAQUE_RECT
441 if (pixman_region32_not_empty(opaque_region) &&
442 wl_shm_buffer_get_format(buffer->shm_buffer) == WL_SHM_FORMAT_ARGB8888 &&
443 resource->enable_opaque_regions)
444 free(pixels);
445#endif
446
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300447 return ret ? -1 : 0;
448}
449
Pekka Paalanend5fbfb22013-11-22 17:30:39 +0200450static inline void
451rpi_buffer_egl_lock(struct weston_buffer *buffer)
452{
453#ifdef ENABLE_EGL
454 vc_dispmanx_set_wl_buffer_in_use(buffer->resource, 1);
455#endif
456}
457
458static inline void
459rpi_buffer_egl_unlock(struct weston_buffer *buffer)
460{
461#ifdef ENABLE_EGL
462 vc_dispmanx_set_wl_buffer_in_use(buffer->resource, 0);
463#endif
464}
465
Tomeu Vizoso74987582013-10-25 10:34:38 +0200466static void
467rpir_egl_buffer_destroy(struct rpir_egl_buffer *egl_buffer)
468{
469 struct weston_buffer *buffer;
470
471 if (egl_buffer == NULL)
472 return;
473
474 buffer = egl_buffer->buffer_ref.buffer;
475 if (buffer == NULL) {
476 /* The client has already destroyed the wl_buffer, the
477 * compositor has the responsibility to delete the resource.
478 */
479 vc_dispmanx_resource_delete(egl_buffer->resource_handle);
480 } else {
Pekka Paalanend5fbfb22013-11-22 17:30:39 +0200481 rpi_buffer_egl_unlock(buffer);
Tomeu Vizoso74987582013-10-25 10:34:38 +0200482 weston_buffer_reference(&egl_buffer->buffer_ref, NULL);
483 }
484
485 free(egl_buffer);
486}
487
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300488static struct rpir_surface *
489rpir_surface_create(struct rpi_renderer *renderer)
490{
491 struct rpir_surface *surface;
492
493 surface = calloc(1, sizeof *surface);
494 if (!surface)
495 return NULL;
496
Tomeu Vizoso5c3ea3b2013-10-24 15:38:30 +0200497 wl_list_init(&surface->views);
Jason Ekstranda7af7042013-10-12 22:38:11 -0500498 surface->visible_views = 0;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300499 surface->single_buffer = renderer->single_buffer;
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +0100500 surface->enable_opaque_regions = renderer->enable_opaque_regions;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300501 rpi_resource_init(&surface->resources[0]);
502 rpi_resource_init(&surface->resources[1]);
503 surface->front = &surface->resources[0];
504 if (surface->single_buffer)
505 surface->back = &surface->resources[0];
506 else
507 surface->back = &surface->resources[1];
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +0100508
509 surface->front->enable_opaque_regions = renderer->enable_opaque_regions;
510 surface->back->enable_opaque_regions = renderer->enable_opaque_regions;
511
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200512 surface->buffer_type = BUFFER_TYPE_NULL;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300513
514 pixman_region32_init(&surface->prev_damage);
515
516 return surface;
517}
518
519static void
520rpir_surface_destroy(struct rpir_surface *surface)
521{
Jason Ekstranda7af7042013-10-12 22:38:11 -0500522 if (surface->visible_views)
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300523 weston_log("ERROR rpi: destroying on-screen element\n");
524
Jason Ekstranda7af7042013-10-12 22:38:11 -0500525 assert(wl_list_empty(&surface->views));
526
527 if (surface->surface)
528 surface->surface->renderer_state = NULL;
529
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300530 pixman_region32_fini(&surface->prev_damage);
531 rpi_resource_release(&surface->resources[0]);
532 rpi_resource_release(&surface->resources[1]);
Jason Ekstranda7af7042013-10-12 22:38:11 -0500533 DBG("rpir_surface %p destroyed (%u)\n", surface, surface->visible_views);
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300534
Tomeu Vizoso74987582013-10-25 10:34:38 +0200535 rpir_egl_buffer_destroy(surface->egl_back);
536 rpir_egl_buffer_destroy(surface->egl_front);
537 rpir_egl_buffer_destroy(surface->egl_old_front);
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200538
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300539 free(surface);
540}
541
542static int
Jason Ekstrand6bd62942013-06-20 20:38:23 -0500543rpir_surface_damage(struct rpir_surface *surface, struct weston_buffer *buffer,
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300544 pixman_region32_t *damage)
545{
546 pixman_region32_t upload;
547 int ret;
548
549 if (!pixman_region32_not_empty(damage))
550 return 0;
551
552 DBG("rpir_surface %p update resource %p\n", surface, surface->back);
553
554 /* XXX: todo: if no surface->handle, update front buffer directly
555 * to avoid creating a new back buffer */
556 if (surface->single_buffer) {
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +0100557 ret = rpi_resource_update(surface->front, buffer, damage,
558 &surface->surface->opaque);
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300559 } else {
560 pixman_region32_init(&upload);
561 pixman_region32_union(&upload, &surface->prev_damage, damage);
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +0100562 ret = rpi_resource_update(surface->back, buffer, &upload,
563 &surface->surface->opaque);
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300564 pixman_region32_fini(&upload);
565 }
566
567 pixman_region32_copy(&surface->prev_damage, damage);
568 surface->need_swap = 1;
569
570 return ret;
571}
572
Jason Ekstranda7af7042013-10-12 22:38:11 -0500573static struct rpir_view *
574rpir_view_create(struct rpir_surface *surface)
575{
576 struct rpir_view *view;
577
578 view = calloc(1, sizeof *view);
579 if (!view)
580 return NULL;
581
582 view->surface = surface;
583 wl_list_insert(&surface->views, &view->surface_link);
584
585 wl_list_init(&view->link);
586 view->handle = DISPMANX_NO_HANDLE;
587
588 return view;
589}
590
591static void
592rpir_view_destroy(struct rpir_view *view)
593{
594 wl_list_remove(&view->link);
595
596 if (view->handle != DISPMANX_NO_HANDLE) {
597 view->surface->visible_views--;
598 weston_log("ERROR rpi: destroying on-screen element\n");
599 }
600
601 if (view->view)
602 view->view->renderer_state = NULL;
603
604 wl_list_remove(&view->surface_link);
605 if (wl_list_empty(&view->surface->views) && view->surface->surface == NULL)
606 rpir_surface_destroy(view->surface);
607
608 DBG("rpir_view %p destroyed (%d)\n", view, view->handle);
609
610 free(view);
611}
612
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300613static void
614matrix_type_str(struct weston_matrix *matrix, char *buf, int len)
615{
616 static const char types[33] = "TSRO";
617 unsigned mask = matrix->type;
618 int i = 0;
619
620 while (mask && i < len - 1) {
621 if (mask & (1u << i))
622 *buf++ = types[i];
623 mask &= ~(1u << i);
624 i++;
625 }
626 *buf = '\0';
627}
628
629static void
630log_print_matrix(struct weston_matrix *matrix)
631{
632 char typestr[6];
633 float *d = matrix->d;
634
635 matrix_type_str(matrix, typestr, sizeof typestr);
636 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
637 d[0], d[4], d[8], d[12]);
638 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
639 d[1], d[5], d[9], d[13]);
640 weston_log_continue("%14.6e %14.6e %14.6e %14.6e\n",
641 d[2], d[6], d[10], d[14]);
642 weston_log_continue("%14.6e %14.6e %14.6e %14.6e type: %s\n",
643 d[3], d[7], d[11], d[15], typestr);
644}
645
646static void
647warn_bad_matrix(struct weston_matrix *total, struct weston_matrix *output,
648 struct weston_matrix *surface)
649{
650 static int n_warn;
651 char typestr[6];
652
653 if (n_warn++ == 10)
654 weston_log("%s: not showing more warnings\n", __func__);
655
656 if (n_warn > 10)
657 return;
658
659 weston_log("%s: warning: total transformation is not renderable:\n",
660 __func__);
661 log_print_matrix(total);
662
663 matrix_type_str(surface, typestr, sizeof typestr);
664 weston_log_continue("surface matrix type: %s\n", typestr);
665 matrix_type_str(output, typestr, sizeof typestr);
666 weston_log_continue("output matrix type: %s\n", typestr);
667}
668
669/*#define SURFACE_TRANSFORM */
670
671static int
Jason Ekstranda7af7042013-10-12 22:38:11 -0500672rpir_view_compute_rects(struct rpir_view *view,
673 VC_RECT_T *src_rect, VC_RECT_T *dst_rect,
674 VC_IMAGE_TRANSFORM_T *flipmask)
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300675{
Jason Ekstranda7af7042013-10-12 22:38:11 -0500676 struct weston_output *output_base = view->view->surface->output;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300677 struct rpir_output *output = to_rpir_output(output_base);
Jason Ekstranda7af7042013-10-12 22:38:11 -0500678 struct weston_matrix matrix = view->view->transform.matrix;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300679 VC_IMAGE_TRANSFORM_T flipt = 0;
680 int src_x, src_y;
681 int dst_x, dst_y;
682 int src_width, src_height;
683 int dst_width, dst_height;
684 struct weston_vector p1 = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
685 struct weston_vector p2 = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
686 int t;
687 int over;
688
689 /* XXX: take buffer transform into account */
690
691 /* src is in 16.16, dst is in 32.0 fixed point.
692 * Negative values are not allowed in VC_RECT_T.
693 * Clip size to output boundaries, firmware ignores
694 * huge elements like 8192x8192.
695 */
696
697 src_x = 0 << 16;
698 src_y = 0 << 16;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200699
Jason Ekstranda7af7042013-10-12 22:38:11 -0500700 if (view->surface->buffer_type == BUFFER_TYPE_EGL) {
701 struct weston_buffer *buffer =
702 view->surface->egl_front->buffer_ref.buffer;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200703
704 src_width = buffer->width << 16;
705 src_height = buffer->height << 16;
706 } else {
Jason Ekstranda7af7042013-10-12 22:38:11 -0500707 src_width = view->surface->front->width << 16;
708 src_height = view->surface->front->height << 16;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200709 }
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300710
711 weston_matrix_multiply(&matrix, &output->matrix);
712
713#ifdef SURFACE_TRANSFORM
714 if (matrix.type >= WESTON_MATRIX_TRANSFORM_OTHER) {
715#else
716 if (matrix.type >= WESTON_MATRIX_TRANSFORM_ROTATE) {
717#endif
718 warn_bad_matrix(&matrix, &output->matrix,
Jason Ekstranda7af7042013-10-12 22:38:11 -0500719 &view->view->transform.matrix);
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300720 } else {
721 if (matrix.type & WESTON_MATRIX_TRANSFORM_ROTATE) {
722 if (fabsf(matrix.d[0]) < 1e-4f &&
723 fabsf(matrix.d[5]) < 1e-4f) {
724 flipt |= TRANSFORM_TRANSPOSE;
725 } else if (fabsf(matrix.d[1]) < 1e-4 &&
726 fabsf(matrix.d[4]) < 1e-4) {
727 /* no transpose */
728 } else {
729 warn_bad_matrix(&matrix, &output->matrix,
Jason Ekstranda7af7042013-10-12 22:38:11 -0500730 &view->view->transform.matrix);
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300731 }
732 }
733 }
734
Jason Ekstrand918f2dd2013-12-02 21:01:53 -0600735 p2.f[0] = view->view->surface->width;
736 p2.f[1] = view->view->surface->height;
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300737
738 /* transform top-left and bot-right corner into screen coordinates */
739 weston_matrix_transform(&matrix, &p1);
740 weston_matrix_transform(&matrix, &p2);
741
742 /* Compute the destination rectangle on screen, converting
743 * negative dimensions to flips.
744 */
745
746 dst_width = round(p2.f[0] - p1.f[0]);
747 if (dst_width < 0) {
748 dst_x = round(p2.f[0]);
749 dst_width = -dst_width;
750
751 if (!(flipt & TRANSFORM_TRANSPOSE))
752 flipt |= TRANSFORM_HFLIP;
753 else
754 flipt |= TRANSFORM_VFLIP;
755 } else {
756 dst_x = round(p1.f[0]);
757 }
758
759 dst_height = round(p2.f[1] - p1.f[1]);
760 if (dst_height < 0) {
761 dst_y = round(p2.f[1]);
762 dst_height = -dst_height;
763
764 if (!(flipt & TRANSFORM_TRANSPOSE))
765 flipt |= TRANSFORM_VFLIP;
766 else
767 flipt |= TRANSFORM_HFLIP;
768 } else {
769 dst_y = round(p1.f[1]);
770 }
771
772 if (dst_width == 0 || dst_height == 0) {
773 DBG("ignored, zero surface area before clipping\n");
774 return -1;
775 }
776
777#ifdef SURFACE_TRANSFORM
778 /* Dispmanx works as if you flipped the whole screen, when
779 * you flip an element. But, we want to flip an element in place.
780 * XXX: fixme
781 */
782 if (flipt & TRANSFORM_HFLIP)
783 dst_x = output_base->width - dst_x;
784 if (flipt & TRANSFORM_VFLIP)
785 dst_y = output_base->height - dst_y;
786 if (flipt & TRANSFORM_TRANSPOSE) {
787 int_swap(&dst_x, &dst_y);
788 int_swap(&dst_width, &dst_height);
789 }
790#else
791 switch (output_base->transform) {
792 case WL_OUTPUT_TRANSFORM_FLIPPED:
793 flipt = TRANSFORM_HFLIP;
794 break;
795 case WL_OUTPUT_TRANSFORM_NORMAL:
796 flipt = 0;
797 break;
798
799 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
800 flipt = TRANSFORM_HFLIP | TRANSFORM_VFLIP | TRANSFORM_TRANSPOSE;
801 break;
802 case WL_OUTPUT_TRANSFORM_90:
803 flipt = TRANSFORM_VFLIP | TRANSFORM_TRANSPOSE;
804 break;
805 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
806 flipt = TRANSFORM_VFLIP;
807 break;
808 case WL_OUTPUT_TRANSFORM_180:
809 flipt = TRANSFORM_HFLIP | TRANSFORM_VFLIP;
810 break;
811
812 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
813 flipt = TRANSFORM_TRANSPOSE;
814 break;
815 case WL_OUTPUT_TRANSFORM_270:
816 flipt = TRANSFORM_HFLIP | TRANSFORM_TRANSPOSE;
817 break;
818 default:
819 break;
820 }
821#endif
822
823 /* clip destination rectangle to screen dimensions */
824
825 if (dst_x < 0) {
826 t = (int64_t)dst_x * src_width / dst_width;
827 src_width += t;
828 dst_width += dst_x;
829 src_x -= t;
830 dst_x = 0;
831 }
832
833 if (dst_y < 0) {
834 t = (int64_t)dst_y * src_height / dst_height;
835 src_height += t;
836 dst_height += dst_y;
837 src_y -= t;
838 dst_y = 0;
839 }
840
841 over = dst_x + dst_width - output_base->width;
842 if (over > 0) {
843 t = (int64_t)over * src_width / dst_width;
844 src_width -= t;
845 dst_width -= over;
846 }
847
848 over = dst_y + dst_height - output_base->height;
849 if (over > 0) {
850 t = (int64_t)over * src_height / dst_height;
851 src_height -= t;
852 dst_height -= over;
853 }
854
855 src_width = int_max(src_width, 0);
856 src_height = int_max(src_height, 0);
857
Jason Ekstranda7af7042013-10-12 22:38:11 -0500858 DBG("rpir_view %p %dx%d: p1 %f, %f; p2 %f, %f\n", view,
859 view->view->geometry.width,
860 view->view->geometry.height,
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300861 p1.f[0], p1.f[1], p2.f[0], p2.f[1]);
862 DBG("src rect %d;%d, %d;%d, %d;%dx%d;%d\n",
863 src_x >> 16, src_x & 0xffff,
864 src_y >> 16, src_y & 0xffff,
865 src_width >> 16, src_width & 0xffff,
866 src_height >> 16, src_height & 0xffff);
867 DBG("dest rect %d, %d, %dx%d%s%s%s\n",
868 dst_x, dst_y, dst_width, dst_height,
869 (flipt & TRANSFORM_HFLIP) ? " hflip" : "",
870 (flipt & TRANSFORM_VFLIP) ? " vflip" : "",
871 (flipt & TRANSFORM_TRANSPOSE) ? " transp" : "");
872
873 assert(src_x >= 0);
874 assert(src_y >= 0);
875 assert(dst_x >= 0);
876 assert(dst_y >= 0);
877
878 if (dst_width < 1 || dst_height < 1) {
879 DBG("ignored, zero surface area after clipping\n");
880 return -1;
881 }
882
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200883 /* EGL buffers will be upside-down related to what DispmanX expects */
Jason Ekstranda7af7042013-10-12 22:38:11 -0500884 if (view->surface->buffer_type == BUFFER_TYPE_EGL)
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200885 flipt ^= TRANSFORM_VFLIP;
886
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300887 vc_dispmanx_rect_set(src_rect, src_x, src_y, src_width, src_height);
888 vc_dispmanx_rect_set(dst_rect, dst_x, dst_y, dst_width, dst_height);
889 *flipmask = flipt;
890
891 return 0;
892}
893
894static DISPMANX_TRANSFORM_T
895vc_image2dispmanx_transform(VC_IMAGE_TRANSFORM_T t)
896{
897 /* XXX: uhh, are these right? */
898 switch (t) {
899 case VC_IMAGE_ROT0:
900 return DISPMANX_NO_ROTATE;
901 case VC_IMAGE_MIRROR_ROT0:
902 return DISPMANX_FLIP_HRIZ;
903 case VC_IMAGE_MIRROR_ROT180:
904 return DISPMANX_FLIP_VERT;
905 case VC_IMAGE_ROT180:
906 return DISPMANX_ROTATE_180;
907 case VC_IMAGE_MIRROR_ROT90:
908 return DISPMANX_ROTATE_90 | DISPMANX_FLIP_HRIZ;
909 case VC_IMAGE_ROT270:
910 return DISPMANX_ROTATE_270;
911 case VC_IMAGE_ROT90:
912 return DISPMANX_ROTATE_90;
913 case VC_IMAGE_MIRROR_ROT270:
914 return DISPMANX_ROTATE_270 | DISPMANX_FLIP_VERT;
915 default:
916 assert(0 && "bad VC_IMAGE_TRANSFORM_T");
917 return DISPMANX_NO_ROTATE;
918 }
919}
920
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200921static DISPMANX_RESOURCE_HANDLE_T
922rpir_surface_get_resource(struct rpir_surface *surface)
923{
924 switch (surface->buffer_type) {
925 case BUFFER_TYPE_SHM:
926 case BUFFER_TYPE_NULL:
927 return surface->front->handle;
928 case BUFFER_TYPE_EGL:
929 if (surface->egl_front != NULL)
930 return surface->egl_front->resource_handle;
931 default:
932 return DISPMANX_NO_HANDLE;
933 }
934}
935
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +0100936#ifdef HAVE_ELEMENT_SET_OPAQUE_RECT
937static int
938rpir_surface_set_opaque_rect(struct rpir_surface *surface,
939 DISPMANX_UPDATE_HANDLE_T update)
940{
941 int ret;
942
943 if (pixman_region32_not_empty(&surface->surface->opaque) &&
944 surface->opaque_regions) {
945 pixman_box32_t *box;
946 VC_RECT_T opaque_rect;
947
948 box = pixman_region32_extents(&surface->surface->opaque);
949 opaque_rect.x = box->x1;
950 opaque_rect.y = box->y1;
951 opaque_rect.width = box->x2 - box->x1;
952 opaque_rect.height = box->y2 - box->y1;
953
954 ret = vc_dispmanx_element_set_opaque_rect(update,
955 surface->handle,
956 &opaque_rect);
957 if (ret) {
958 weston_log("vc_dispmanx_element_set_opaque_rect failed\n");
959 return -1;
960 }
961 }
962
963 return 0;
964}
965#endif
966
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300967static int
Jason Ekstranda7af7042013-10-12 22:38:11 -0500968rpir_view_dmx_add(struct rpir_view *view, struct rpir_output *output,
969 DISPMANX_UPDATE_HANDLE_T update, int layer)
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300970{
971 /* Do not use DISPMANX_FLAGS_ALPHA_PREMULT here.
972 * If you define PREMULT and ALPHA_MIX, the hardware will not
973 * multiply the source color with the element alpha, leading to
974 * bad colors. Instead, we define PREMULT during pixel data upload.
975 */
976 VC_DISPMANX_ALPHA_T alphasetup = {
977 DISPMANX_FLAGS_ALPHA_FROM_SOURCE |
978 DISPMANX_FLAGS_ALPHA_MIX,
Jason Ekstranda7af7042013-10-12 22:38:11 -0500979 float2uint8(view->view->alpha), /* opacity 0-255 */
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300980 0 /* mask resource handle */
981 };
982 VC_RECT_T dst_rect;
983 VC_RECT_T src_rect;
984 VC_IMAGE_TRANSFORM_T flipmask;
985 int ret;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200986 DISPMANX_RESOURCE_HANDLE_T resource_handle;
987
Jason Ekstranda7af7042013-10-12 22:38:11 -0500988 resource_handle = rpir_surface_get_resource(view->surface);
Tomeu Vizosob4659eb2013-10-07 11:02:20 +0200989 if (resource_handle == DISPMANX_NO_HANDLE) {
990 weston_log("%s: no buffer yet, aborting\n", __func__);
991 return 0;
992 }
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300993
Jason Ekstranda7af7042013-10-12 22:38:11 -0500994 ret = rpir_view_compute_rects(view, &src_rect, &dst_rect, &flipmask);
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300995 if (ret < 0)
996 return 0;
997
Jason Ekstranda7af7042013-10-12 22:38:11 -0500998 view->handle = vc_dispmanx_element_add(
Pekka Paalanend7265bc2013-05-22 18:03:06 +0300999 update,
1000 output->display,
1001 layer,
1002 &dst_rect,
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001003 resource_handle,
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001004 &src_rect,
1005 DISPMANX_PROTECTION_NONE,
1006 &alphasetup,
1007 NULL /* clamp */,
1008 vc_image2dispmanx_transform(flipmask));
Jason Ekstranda7af7042013-10-12 22:38:11 -05001009 DBG("rpir_surface %p add %u, alpha %f resource %d\n", view,
1010 view->handle, view->view->alpha, resource_handle);
1011
1012 if (view->handle == DISPMANX_NO_HANDLE)
1013 return -1;
1014
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +01001015#ifdef HAVE_ELEMENT_SET_OPAQUE_RECT
1016 ret = rpir_surface_set_opaque_rect(surface, update);
1017 if (ret < 0)
1018 return -1;
1019#endif
1020
Jason Ekstranda7af7042013-10-12 22:38:11 -05001021 view->surface->visible_views++;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001022
1023 return 1;
1024}
1025
1026static void
Jason Ekstranda7af7042013-10-12 22:38:11 -05001027rpir_view_dmx_swap(struct rpir_view *view,
1028 DISPMANX_UPDATE_HANDLE_T update)
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001029{
1030 VC_RECT_T rect;
1031 pixman_box32_t *r;
1032
1033 /* XXX: skip, iff resource was not reallocated, and single-buffering */
Jason Ekstranda7af7042013-10-12 22:38:11 -05001034 vc_dispmanx_element_change_source(update, view->handle,
1035 view->surface->front->handle);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001036
1037 /* This is current damage now, after rpir_surface_damage() */
Jason Ekstranda7af7042013-10-12 22:38:11 -05001038 r = pixman_region32_extents(&view->surface->prev_damage);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001039
1040 vc_dispmanx_rect_set(&rect, r->x1, r->y1,
1041 r->x2 - r->x1, r->y2 - r->y1);
Jason Ekstranda7af7042013-10-12 22:38:11 -05001042 vc_dispmanx_element_modified(update, view->handle, &rect);
1043 DBG("rpir_view %p swap\n", view);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001044}
1045
1046static int
Jason Ekstranda7af7042013-10-12 22:38:11 -05001047rpir_view_dmx_move(struct rpir_view *view,
1048 DISPMANX_UPDATE_HANDLE_T update, int layer)
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001049{
Jason Ekstranda7af7042013-10-12 22:38:11 -05001050 uint8_t alpha = float2uint8(view->view->alpha);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001051 VC_RECT_T dst_rect;
1052 VC_RECT_T src_rect;
1053 VC_IMAGE_TRANSFORM_T flipmask;
1054 int ret;
1055
1056 /* XXX: return early, if all attributes stay the same */
1057
Jason Ekstranda7af7042013-10-12 22:38:11 -05001058 if (view->surface->buffer_type == BUFFER_TYPE_EGL) {
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001059 DISPMANX_RESOURCE_HANDLE_T resource_handle;
1060
Jason Ekstranda7af7042013-10-12 22:38:11 -05001061 resource_handle = rpir_surface_get_resource(view->surface);
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001062 if (resource_handle == DISPMANX_NO_HANDLE) {
1063 weston_log("%s: no buffer yet, aborting\n", __func__);
1064 return 0;
1065 }
1066
1067 vc_dispmanx_element_change_source(update,
Jason Ekstranda7af7042013-10-12 22:38:11 -05001068 view->handle,
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001069 resource_handle);
1070 }
1071
Jason Ekstranda7af7042013-10-12 22:38:11 -05001072 ret = rpir_view_compute_rects(view, &src_rect, &dst_rect, &flipmask);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001073 if (ret < 0)
1074 return 0;
1075
1076 ret = vc_dispmanx_element_change_attributes(
1077 update,
Jason Ekstranda7af7042013-10-12 22:38:11 -05001078 view->handle,
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001079 ELEMENT_CHANGE_LAYER |
1080 ELEMENT_CHANGE_OPACITY |
1081 ELEMENT_CHANGE_TRANSFORM |
1082 ELEMENT_CHANGE_DEST_RECT |
1083 ELEMENT_CHANGE_SRC_RECT,
1084 layer,
1085 alpha,
1086 &dst_rect,
1087 &src_rect,
1088 DISPMANX_NO_HANDLE,
1089 /* This really is DISPMANX_TRANSFORM_T, no matter
1090 * what the header says. */
1091 vc_image2dispmanx_transform(flipmask));
Jason Ekstranda7af7042013-10-12 22:38:11 -05001092 DBG("rpir_view %p move\n", view);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001093
1094 if (ret)
1095 return -1;
1096
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +01001097#ifdef HAVE_ELEMENT_SET_OPAQUE_RECT
1098 ret = rpir_surface_set_opaque_rect(surface, update);
1099 if (ret < 0)
1100 return -1;
1101#endif
1102
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001103 return 1;
1104}
1105
1106static void
Jason Ekstranda7af7042013-10-12 22:38:11 -05001107rpir_view_dmx_remove(struct rpir_view *view,
1108 DISPMANX_UPDATE_HANDLE_T update)
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001109{
Jason Ekstranda7af7042013-10-12 22:38:11 -05001110 if (view->handle == DISPMANX_NO_HANDLE)
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001111 return;
1112
Jason Ekstranda7af7042013-10-12 22:38:11 -05001113 vc_dispmanx_element_remove(update, view->handle);
1114 DBG("rpir_view %p remove %u\n", view, view->handle);
1115 view->handle = DISPMANX_NO_HANDLE;
1116 view->surface->visible_views--;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001117}
1118
1119static void
1120rpir_surface_swap_pointers(struct rpir_surface *surface)
1121{
1122 struct rpi_resource *tmp;
1123
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001124 if (surface->buffer_type == BUFFER_TYPE_EGL) {
1125 if (surface->egl_back != NULL) {
1126 assert(surface->egl_old_front == NULL);
1127 surface->egl_old_front = surface->egl_front;
1128 surface->egl_front = surface->egl_back;
1129 surface->egl_back = NULL;
Tomeu Vizoso15767812013-10-24 15:38:31 +02001130 DBG("new front %d\n", surface->egl_front->resource_handle);
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001131 }
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001132 } else {
Jason Ekstranda7af7042013-10-12 22:38:11 -05001133 tmp = surface->front;
1134 surface->front = surface->back;
1135 surface->back = tmp;
Tomeu Vizoso15767812013-10-24 15:38:31 +02001136 DBG("new back %p, new front %p\n", surface->back, surface->front);
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001137 }
Jason Ekstranda7af7042013-10-12 22:38:11 -05001138}
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001139
Jason Ekstranda7af7042013-10-12 22:38:11 -05001140static int
1141is_view_not_visible(struct weston_view *view)
1142{
1143 /* Return true, if surface is guaranteed to be totally obscured. */
1144 int ret;
1145 pixman_region32_t unocc;
1146
1147 pixman_region32_init(&unocc);
1148 pixman_region32_subtract(&unocc, &view->transform.boundingbox,
1149 &view->clip);
1150 ret = !pixman_region32_not_empty(&unocc);
1151 pixman_region32_fini(&unocc);
1152
1153 return ret;
1154}
1155
1156static void
1157rpir_view_update(struct rpir_view *view, struct rpir_output *output,
1158 DISPMANX_UPDATE_HANDLE_T update, int layer)
1159{
1160 int ret;
1161 int obscured;
1162
Jason Ekstranda7af7042013-10-12 22:38:11 -05001163 obscured = is_view_not_visible(view->view);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001164 if (obscured) {
Jason Ekstranda7af7042013-10-12 22:38:11 -05001165 DBG("rpir_view %p totally obscured.\n", view);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001166
Jason Ekstranda7af7042013-10-12 22:38:11 -05001167 wl_list_remove(&view->link);
1168 if (view->handle == DISPMANX_NO_HANDLE) {
1169 wl_list_init(&view->link);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001170 } else {
Jason Ekstranda7af7042013-10-12 22:38:11 -05001171 rpir_view_dmx_remove(view, update);
1172 wl_list_insert(&output->view_cleanup_list,
1173 &view->link);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001174 }
1175
1176 goto out;
1177 }
1178
Jason Ekstranda7af7042013-10-12 22:38:11 -05001179 if (view->handle == DISPMANX_NO_HANDLE) {
1180 ret = rpir_view_dmx_add(view, output, update, layer);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001181 if (ret == 0) {
Jason Ekstranda7af7042013-10-12 22:38:11 -05001182 wl_list_remove(&view->link);
1183 wl_list_init(&view->link);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001184 } else if (ret < 0) {
Jason Ekstranda7af7042013-10-12 22:38:11 -05001185 weston_log("ERROR rpir_view_dmx_add() failed.\n");
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001186 }
1187 } else {
Jason Ekstranda7af7042013-10-12 22:38:11 -05001188 if (view->surface->need_swap)
1189 rpir_view_dmx_swap(view, update);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001190
Jason Ekstranda7af7042013-10-12 22:38:11 -05001191 ret = rpir_view_dmx_move(view, update, layer);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001192 if (ret == 0) {
Jason Ekstranda7af7042013-10-12 22:38:11 -05001193 rpir_view_dmx_remove(view, update);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001194
Jason Ekstranda7af7042013-10-12 22:38:11 -05001195 wl_list_remove(&view->link);
1196 wl_list_insert(&output->view_cleanup_list,
1197 &view->link);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001198 } else if (ret < 0) {
Jason Ekstranda7af7042013-10-12 22:38:11 -05001199 weston_log("ERROR rpir_view_dmx_move() failed.\n");
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001200 }
1201 }
1202
1203out:
Jason Ekstranda7af7042013-10-12 22:38:11 -05001204 view->layer = layer;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001205}
1206
1207static int
1208rpi_renderer_read_pixels(struct weston_output *base,
1209 pixman_format_code_t format, void *pixels,
1210 uint32_t x, uint32_t y,
1211 uint32_t width, uint32_t height)
1212{
1213 struct rpir_output *output = to_rpir_output(base);
1214 struct rpi_resource *buffer = &output->capture_buffer;
1215 VC_RECT_T rect;
1216 uint32_t fb_width, fb_height;
1217 uint32_t dst_pitch;
1218 uint32_t i;
1219 int ret;
1220
Hardeningff39efa2013-09-18 23:56:35 +02001221 fb_width = base->current_mode->width;
1222 fb_height = base->current_mode->height;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001223
1224 DBG("%s(%u, %u, %u, %u), resource %p\n", __func__,
1225 x, y, width, height, buffer);
1226
1227 if (format != PIXMAN_a8r8g8b8) {
1228 weston_log("rpi-renderer error: bad read_format\n");
1229 return -1;
1230 }
1231
1232 dst_pitch = fb_width * 4;
1233
1234 if (buffer->handle == DISPMANX_NO_HANDLE) {
1235 free(output->capture_data);
1236 output->capture_data = NULL;
1237
1238 ret = rpi_resource_realloc(buffer, VC_IMAGE_ARGB8888,
1239 fb_width, fb_height,
1240 dst_pitch, fb_height);
1241 if (ret < 0) {
1242 weston_log("rpi-renderer error: "
1243 "allocating read buffer failed\n");
1244 return -1;
1245 }
1246
1247 ret = vc_dispmanx_snapshot(output->display, buffer->handle,
1248 VC_IMAGE_ROT0);
1249 if (ret) {
1250 weston_log("rpi-renderer error: "
1251 "vc_dispmanx_snapshot returned %d\n", ret);
1252 return -1;
1253 }
1254 DBG("%s: snapshot done.\n", __func__);
1255 }
1256
1257 /*
1258 * If vc_dispmanx_resource_read_data was able to read sub-rectangles,
1259 * we could read directly into 'pixels'. But it cannot, it does not
1260 * use rect.x or rect.width, and does this:
1261 * host_start = (uint8_t *)dst_address + (dst_pitch * p_rect->y);
1262 * In other words, it is only good for reading the full buffer in
1263 * one go.
1264 */
1265 vc_dispmanx_rect_set(&rect, 0, 0, fb_width, fb_height);
1266
1267 if (x == 0 && y == 0 && width == fb_width && height == fb_height) {
1268 ret = vc_dispmanx_resource_read_data(buffer->handle, &rect,
1269 pixels, dst_pitch);
1270 if (ret) {
1271 weston_log("rpi-renderer error: "
1272 "resource_read_data returned %d\n", ret);
1273 return -1;
1274 }
1275 DBG("%s: full frame done.\n", __func__);
1276 return 0;
1277 }
1278
1279 if (!output->capture_data) {
1280 output->capture_data = malloc(fb_height * dst_pitch);
1281 if (!output->capture_data) {
1282 weston_log("rpi-renderer error: "
1283 "out of memory\n");
1284 return -1;
1285 }
1286
1287 ret = vc_dispmanx_resource_read_data(buffer->handle, &rect,
1288 output->capture_data,
1289 dst_pitch);
1290 if (ret) {
1291 weston_log("rpi-renderer error: "
1292 "resource_read_data returned %d\n", ret);
1293 return -1;
1294 }
1295 }
1296
1297 for (i = 0; i < height; i++) {
1298 uint8_t *src = output->capture_data +
1299 (y + i) * dst_pitch + x * 4;
1300 uint8_t *dst = (uint8_t *)pixels + i * width * 4;
1301 memcpy(dst, src, width * 4);
1302 }
1303
1304 return 0;
1305}
1306
1307static void
1308rpir_output_dmx_remove_all(struct rpir_output *output,
1309 DISPMANX_UPDATE_HANDLE_T update)
1310{
Jason Ekstranda7af7042013-10-12 22:38:11 -05001311 struct rpir_view *view;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001312
Jason Ekstranda7af7042013-10-12 22:38:11 -05001313 while (!wl_list_empty(&output->view_list)) {
1314 view = container_of(output->view_list.next,
1315 struct rpir_view, link);
1316 rpir_view_dmx_remove(view, update);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001317
Jason Ekstranda7af7042013-10-12 22:38:11 -05001318 wl_list_remove(&view->link);
1319 wl_list_insert(&output->view_cleanup_list, &view->link);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001320 }
1321}
1322
1323static void
1324output_compute_matrix(struct weston_output *base)
1325{
1326 struct rpir_output *output = to_rpir_output(base);
1327 struct weston_matrix *matrix = &output->matrix;
1328 const float half_w = 0.5f * base->width;
1329 const float half_h = 0.5f * base->height;
1330 float mag;
1331 float dx, dy;
1332
1333 weston_matrix_init(matrix);
1334 weston_matrix_translate(matrix, -base->x, -base->y, 0.0f);
1335
1336#ifdef SURFACE_TRANSFORM
1337 weston_matrix_translate(matrix, -half_w, -half_h, 0.0f);
1338 switch (base->transform) {
1339 case WL_OUTPUT_TRANSFORM_FLIPPED:
1340 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1341 case WL_OUTPUT_TRANSFORM_NORMAL:
1342 /* weston_matrix_rotate_xy(matrix, 1.0f, 0.0f); no-op */
1343 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1344 break;
1345
1346 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
1347 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1348 case WL_OUTPUT_TRANSFORM_90:
1349 weston_matrix_rotate_xy(matrix, 0.0f, 1.0f);
1350 weston_matrix_translate(matrix, half_h, half_w, 0.0f);
1351 break;
1352
1353 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
1354 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1355 case WL_OUTPUT_TRANSFORM_180:
1356 weston_matrix_rotate_xy(matrix, -1.0f, 0.0f);
1357 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1358 break;
1359
1360 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
1361 weston_matrix_scale(matrix, -1.0f, 1.0f, 1.0f);
1362 case WL_OUTPUT_TRANSFORM_270:
1363 weston_matrix_rotate_xy(matrix, 0.0f, -1.0f);
1364 weston_matrix_translate(matrix, half_h, half_w, 0.0f);
1365 break;
1366
1367 default:
1368 break;
1369 }
1370#endif
1371
1372 if (base->zoom.active) {
1373 /* The base->zoom stuff is in GL coordinate system */
1374 mag = 1.0f / (1.0f - base->zoom.spring_z.current);
1375 dx = -(base->zoom.trans_x + 1.0f) * half_w;
1376 dy = -(base->zoom.trans_y + 1.0f) * half_h;
1377 weston_matrix_translate(matrix, dx, dy, 0.0f);
1378 weston_matrix_scale(matrix, mag, mag, 1.0f);
1379 weston_matrix_translate(matrix, half_w, half_h, 0.0f);
1380 }
1381}
1382
1383/* Note: this won't work right for multiple outputs. A DispmanX Element
1384 * is tied to one DispmanX Display, i.e. output.
1385 */
1386static void
1387rpi_renderer_repaint_output(struct weston_output *base,
1388 pixman_region32_t *output_damage)
1389{
1390 struct weston_compositor *compositor = base->compositor;
1391 struct rpir_output *output = to_rpir_output(base);
Jason Ekstranda7af7042013-10-12 22:38:11 -05001392 struct weston_view *wv;
1393 struct rpir_view *view;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001394 struct wl_list done_list;
1395 int layer = 1;
1396
1397 assert(output->update != DISPMANX_NO_HANDLE);
1398
1399 output_compute_matrix(base);
1400
1401 rpi_resource_release(&output->capture_buffer);
1402 free(output->capture_data);
1403 output->capture_data = NULL;
1404
Jason Ekstranda7af7042013-10-12 22:38:11 -05001405 /* Swap resources on surfaces as needed */
1406 wl_list_for_each_reverse(wv, &compositor->view_list, link)
1407 wv->surface->touched = 0;
1408
1409 wl_list_for_each_reverse(wv, &compositor->view_list, link) {
1410 view = to_rpir_view(wv);
1411
1412 if (!wv->surface->touched) {
1413 wv->surface->touched = 1;
1414
Tomeu Vizoso44247742013-10-24 15:38:32 +02001415 if (view->surface->buffer_type == BUFFER_TYPE_EGL ||
1416 view->surface->need_swap)
Jason Ekstranda7af7042013-10-12 22:38:11 -05001417 rpir_surface_swap_pointers(view->surface);
1418 }
1419
Tomeu Vizoso74987582013-10-25 10:34:38 +02001420 if (view->surface->buffer_type == BUFFER_TYPE_EGL) {
1421 struct weston_buffer *buffer;
1422 buffer = view->surface->egl_front->buffer_ref.buffer;
1423 if (buffer != NULL) {
Pekka Paalanend5fbfb22013-11-22 17:30:39 +02001424 rpi_buffer_egl_lock(buffer);
Jason Ekstranda7af7042013-10-12 22:38:11 -05001425 } else {
Tomeu Vizoso74987582013-10-25 10:34:38 +02001426 weston_log("warning: client destroyed current front buffer\n");
1427
1428 wl_list_remove(&view->link);
1429 if (view->handle == DISPMANX_NO_HANDLE) {
1430 wl_list_init(&view->link);
1431 } else {
1432 rpir_view_dmx_remove(view, output->update);
1433 wl_list_insert(&output->view_cleanup_list,
1434 &view->link);
1435 }
Jason Ekstranda7af7042013-10-12 22:38:11 -05001436 }
1437 }
1438 }
1439
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001440 /* update all renderable surfaces */
1441 wl_list_init(&done_list);
Jason Ekstranda7af7042013-10-12 22:38:11 -05001442 wl_list_for_each_reverse(wv, &compositor->view_list, link) {
1443 if (wv->plane != &compositor->primary_plane)
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001444 continue;
1445
Jason Ekstranda7af7042013-10-12 22:38:11 -05001446 view = to_rpir_view(wv);
1447 assert(!wl_list_empty(&view->link) ||
1448 view->handle == DISPMANX_NO_HANDLE);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001449
Jason Ekstranda7af7042013-10-12 22:38:11 -05001450 wl_list_remove(&view->link);
1451 wl_list_insert(&done_list, &view->link);
1452 rpir_view_update(view, output, output->update, layer++);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001453 }
1454
Jason Ekstranda7af7042013-10-12 22:38:11 -05001455 /* Mark all surfaces as swapped */
1456 wl_list_for_each_reverse(wv, &compositor->view_list, link)
1457 to_rpir_surface(wv->surface)->need_swap = 0;
1458
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001459 /* Remove all surfaces that are still on screen, but were
1460 * not rendered this time.
1461 */
1462 rpir_output_dmx_remove_all(output, output->update);
1463
Jason Ekstranda7af7042013-10-12 22:38:11 -05001464 wl_list_insert_list(&output->view_list, &done_list);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001465 output->update = DISPMANX_NO_HANDLE;
1466
1467 /* The frame_signal is emitted in rpi_renderer_finish_frame(),
1468 * so that the firmware can capture the up-to-date contents.
1469 */
1470}
1471
1472static void
1473rpi_renderer_flush_damage(struct weston_surface *base)
1474{
1475 /* Called for every surface just before repainting it, if
1476 * having an shm buffer.
1477 */
1478 struct rpir_surface *surface = to_rpir_surface(base);
Jason Ekstrand6bd62942013-06-20 20:38:23 -05001479 struct weston_buffer *buffer = surface->buffer_ref.buffer;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001480 int ret;
1481
1482 assert(buffer);
Jason Ekstrand6bd62942013-06-20 20:38:23 -05001483 assert(wl_shm_buffer_get(buffer->resource));
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001484
1485 ret = rpir_surface_damage(surface, buffer, &base->damage);
1486 if (ret)
1487 weston_log("%s error: updating Dispmanx resource failed.\n",
1488 __func__);
1489
1490 weston_buffer_reference(&surface->buffer_ref, NULL);
1491}
1492
1493static void
Jason Ekstrand6bd62942013-06-20 20:38:23 -05001494rpi_renderer_attach(struct weston_surface *base, struct weston_buffer *buffer)
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001495{
1496 /* Called every time a client commits an attach. */
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001497 struct rpir_surface *surface = to_rpir_surface(base);
1498
1499 assert(surface);
1500 if (!surface)
1501 return;
1502
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001503 if (surface->buffer_type == BUFFER_TYPE_SHM) {
1504 if (!surface->single_buffer)
1505 /* XXX: need to check if in middle of update */
1506 rpi_resource_release(surface->back);
1507
Jason Ekstranda7af7042013-10-12 22:38:11 -05001508 if (!surface->visible_views)
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001509 /* XXX: cannot do this, if middle of an update */
1510 rpi_resource_release(surface->front);
1511
1512 weston_buffer_reference(&surface->buffer_ref, NULL);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001513 }
1514
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001515 /* If buffer is NULL, Weston core unmaps the surface, the surface
1516 * will not appear in repaint list, and so rpi_renderer_repaint_output
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001517 * will remove the DispmanX element. Later, for SHM, also the front
1518 * buffer will be released in the cleanup_list processing.
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001519 */
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001520 if (!buffer)
1521 return;
1522
1523 if (wl_shm_buffer_get(buffer->resource)) {
1524 surface->buffer_type = BUFFER_TYPE_SHM;
1525 buffer->shm_buffer = wl_shm_buffer_get(buffer->resource);
1526 buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer);
1527 buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer);
1528
1529 weston_buffer_reference(&surface->buffer_ref, buffer);
1530 } else {
1531#if ENABLE_EGL
1532 struct rpi_renderer *renderer = to_rpi_renderer(base->compositor);
Tomeu Vizosoa8e5f292013-10-09 11:29:45 +02001533 struct wl_resource *wl_resource = buffer->resource;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001534
1535 if (!renderer->has_bind_display ||
1536 !renderer->query_buffer(renderer->egl_display,
1537 wl_resource,
1538 EGL_WIDTH, &buffer->width)) {
1539 weston_log("unhandled buffer type!\n");
1540 weston_buffer_reference(&surface->buffer_ref, NULL);
1541 surface->buffer_type = BUFFER_TYPE_NULL;
1542 }
1543
1544 renderer->query_buffer(renderer->egl_display,
1545 wl_resource,
1546 EGL_HEIGHT, &buffer->height);
1547
1548 surface->buffer_type = BUFFER_TYPE_EGL;
1549
1550 if(surface->egl_back == NULL)
1551 surface->egl_back = calloc(1, sizeof *surface->egl_back);
1552
1553 weston_buffer_reference(&surface->egl_back->buffer_ref, buffer);
1554 surface->egl_back->resource_handle =
1555 vc_dispmanx_get_handle_from_wl_buffer(wl_resource);
1556#else
1557 weston_log("unhandled buffer type!\n");
1558 weston_buffer_reference(&surface->buffer_ref, NULL);
1559 surface->buffer_type = BUFFER_TYPE_NULL;
1560#endif
1561 }
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001562}
1563
Ander Conselvan de Oliveiraaa398ae2013-10-25 16:26:33 +03001564static void
1565rpir_surface_handle_surface_destroy(struct wl_listener *listener, void *data)
1566{
1567 struct rpir_surface *surface;
1568 struct weston_surface *base = data;
1569
1570 surface = container_of(listener, struct rpir_surface,
1571 surface_destroy_listener);
1572
1573 assert(surface);
1574 assert(surface->surface == base);
1575 if (!surface)
1576 return;
1577
1578 surface->surface = NULL;
1579 base->renderer_state = NULL;
1580
1581 if (wl_list_empty(&surface->views))
1582 rpir_surface_destroy(surface);
1583}
1584
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001585static int
1586rpi_renderer_create_surface(struct weston_surface *base)
1587{
1588 struct rpi_renderer *renderer = to_rpi_renderer(base->compositor);
1589 struct rpir_surface *surface;
1590
1591 assert(base->renderer_state == NULL);
1592
1593 surface = rpir_surface_create(renderer);
1594 if (!surface)
1595 return -1;
1596
1597 surface->surface = base;
1598 base->renderer_state = surface;
Ander Conselvan de Oliveiraaa398ae2013-10-25 16:26:33 +03001599
1600 surface->surface_destroy_listener.notify =
1601 rpir_surface_handle_surface_destroy;
1602 wl_signal_add(&base->destroy_signal,
1603 &surface->surface_destroy_listener);
1604
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001605 return 0;
1606}
1607
Jason Ekstranda7af7042013-10-12 22:38:11 -05001608static int
1609rpi_renderer_create_view(struct weston_view *base)
1610{
1611 struct rpir_surface *surface = to_rpir_surface(base->surface);
1612 struct rpir_view *view;
1613
1614 assert(base->renderer_state == NULL);
1615
1616 view = rpir_view_create(surface);
1617 if (!view)
1618 return -1;
1619
1620 view->view = base;
1621 base->renderer_state = view;
Tomeu Vizoso96dc9e42013-10-28 10:17:45 +01001622
1623 view->view_destroy_listener.notify =
1624 rpir_view_handle_view_destroy;
1625 wl_signal_add(&base->destroy_signal,
1626 &view->view_destroy_listener);
1627
Jason Ekstranda7af7042013-10-12 22:38:11 -05001628 return 0;
1629}
1630
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001631static void
1632rpi_renderer_surface_set_color(struct weston_surface *base,
1633 float red, float green, float blue, float alpha)
1634{
1635 struct rpir_surface *surface = to_rpir_surface(base);
1636 uint8_t color[4];
1637 VC_RECT_T rect;
1638 int ret;
1639
1640 assert(surface);
1641
1642 ret = rpi_resource_realloc(surface->back, VC_IMAGE_ARGB8888,
1643 1, 1, 4, 1);
1644 if (ret < 0) {
1645 weston_log("Error: %s: rpi_resource_realloc failed.\n",
1646 __func__);
1647 return;
1648 }
1649
1650 color[0] = float2uint8(blue);
1651 color[1] = float2uint8(green);
1652 color[2] = float2uint8(red);
1653 color[3] = float2uint8(alpha);
1654
1655 vc_dispmanx_rect_set(&rect, 0, 0, 1, 1);
1656 ret = vc_dispmanx_resource_write_data(surface->back->handle,
1657 VC_IMAGE_ARGB8888,
1658 4, color, &rect);
1659 if (ret) {
1660 weston_log("Error: %s: resource_write_data failed.\n",
1661 __func__);
1662 return;
1663 }
1664
1665 DBG("%s: resource %p solid color BGRA %u,%u,%u,%u\n", __func__,
1666 surface->back, color[0], color[1], color[2], color[3]);
1667
1668 /*pixman_region32_copy(&surface->prev_damage, damage);*/
1669 surface->need_swap = 1;
1670}
1671
1672static void
Tomeu Vizoso96dc9e42013-10-28 10:17:45 +01001673rpir_view_handle_view_destroy(struct wl_listener *listener, void *data)
Jason Ekstranda7af7042013-10-12 22:38:11 -05001674{
Tomeu Vizoso96dc9e42013-10-28 10:17:45 +01001675 struct rpir_view *view;
1676 struct weston_view *base = data;
1677
1678 view = container_of(listener, struct rpir_view, view_destroy_listener);
Jason Ekstranda7af7042013-10-12 22:38:11 -05001679
1680 assert(view);
1681 assert(view->view == base);
1682 if (!view)
1683 return;
1684
Tomeu Vizoso34dad7d2013-10-25 10:34:37 +02001685 view->view = NULL;
Tomeu Vizoso96dc9e42013-10-28 10:17:45 +01001686 base->renderer_state = NULL;
Tomeu Vizoso34dad7d2013-10-25 10:34:37 +02001687
Tomeu Vizoso96dc9e42013-10-28 10:17:45 +01001688 /* If guaranteed to not be on screen, just destroy it. */
Jason Ekstranda7af7042013-10-12 22:38:11 -05001689 if (wl_list_empty(&view->link))
1690 rpir_view_destroy(view);
1691
1692 /* Otherwise, the view is either on screen and needs
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001693 * to be removed by a repaint update, or it is in the
Jason Ekstranda7af7042013-10-12 22:38:11 -05001694 * view_cleanup_list, and will be destroyed by
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001695 * rpi_renderer_finish_frame().
1696 */
1697}
1698
1699static void
1700rpi_renderer_destroy(struct weston_compositor *compositor)
1701{
1702 struct rpi_renderer *renderer = to_rpi_renderer(compositor);
1703
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001704#if ENABLE_EGL
1705 if (renderer->has_bind_display)
1706 renderer->unbind_display(renderer->egl_display,
1707 compositor->wl_display);
1708#endif
1709
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001710 free(renderer);
1711 compositor->renderer = NULL;
1712}
1713
1714WL_EXPORT int
1715rpi_renderer_create(struct weston_compositor *compositor,
1716 const struct rpi_renderer_parameters *params)
1717{
1718 struct rpi_renderer *renderer;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001719#if ENABLE_EGL
1720 const char *extensions;
1721 EGLBoolean ret;
1722 EGLint major, minor;
1723#endif
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001724
1725 weston_log("Initializing the DispmanX compositing renderer\n");
1726
1727 renderer = calloc(1, sizeof *renderer);
1728 if (renderer == NULL)
1729 return -1;
1730
1731 renderer->single_buffer = params->single_buffer;
Tomeu Vizosoe4f7b922013-12-02 17:18:58 +01001732 renderer->enable_opaque_regions = params->opaque_regions;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001733
1734 renderer->base.read_pixels = rpi_renderer_read_pixels;
1735 renderer->base.repaint_output = rpi_renderer_repaint_output;
1736 renderer->base.flush_damage = rpi_renderer_flush_damage;
1737 renderer->base.attach = rpi_renderer_attach;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001738 renderer->base.surface_set_color = rpi_renderer_surface_set_color;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001739 renderer->base.destroy = rpi_renderer_destroy;
1740
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001741#ifdef ENABLE_EGL
1742 renderer->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
1743 if (renderer->egl_display == EGL_NO_DISPLAY) {
1744 weston_log("failed to create EGL display\n");
1745 return -1;
1746 }
1747
1748 if (!eglInitialize(renderer->egl_display, &major, &minor)) {
1749 weston_log("failed to initialize EGL display\n");
1750 return -1;
1751 }
1752
1753 renderer->bind_display =
1754 (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
1755 renderer->unbind_display =
1756 (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
1757 renderer->query_buffer =
1758 (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
1759
1760 extensions = (const char *) eglQueryString(renderer->egl_display,
1761 EGL_EXTENSIONS);
1762 if (!extensions) {
1763 weston_log("Retrieving EGL extension string failed.\n");
1764 return -1;
1765 }
1766
1767 if (strstr(extensions, "EGL_WL_bind_wayland_display"))
1768 renderer->has_bind_display = 1;
1769
1770 if (renderer->has_bind_display) {
1771 ret = renderer->bind_display(renderer->egl_display,
1772 compositor->wl_display);
1773 if (!ret)
1774 renderer->has_bind_display = 0;
1775 }
1776#endif
1777
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001778 compositor->renderer = &renderer->base;
1779 compositor->read_format = PIXMAN_a8r8g8b8;
1780 /* WESTON_CAP_ROTATION_ANY not supported */
1781
Tomeu Vizoso03681892013-08-06 20:05:57 +02001782 wl_display_add_shm_format(compositor->wl_display, WL_SHM_FORMAT_RGB565);
1783
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001784 return 0;
1785}
1786
1787WL_EXPORT int
1788rpi_renderer_output_create(struct weston_output *base,
1789 DISPMANX_DISPLAY_HANDLE_T display)
1790{
1791 struct rpir_output *output;
1792
1793 assert(base->renderer_state == NULL);
1794
1795 output = calloc(1, sizeof *output);
1796 if (!output)
1797 return -1;
1798
1799 output->display = display;
1800 output->update = DISPMANX_NO_HANDLE;
Jason Ekstranda7af7042013-10-12 22:38:11 -05001801 wl_list_init(&output->view_list);
1802 wl_list_init(&output->view_cleanup_list);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001803 rpi_resource_init(&output->capture_buffer);
1804 base->renderer_state = output;
1805
1806 return 0;
1807}
1808
1809WL_EXPORT void
1810rpi_renderer_output_destroy(struct weston_output *base)
1811{
1812 struct rpir_output *output = to_rpir_output(base);
Jason Ekstranda7af7042013-10-12 22:38:11 -05001813 struct rpir_view *view;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001814 DISPMANX_UPDATE_HANDLE_T update;
1815
1816 rpi_resource_release(&output->capture_buffer);
1817 free(output->capture_data);
1818 output->capture_data = NULL;
1819
1820 update = vc_dispmanx_update_start(0);
1821 rpir_output_dmx_remove_all(output, update);
1822 vc_dispmanx_update_submit_sync(update);
1823
Jason Ekstranda7af7042013-10-12 22:38:11 -05001824 while (!wl_list_empty(&output->view_cleanup_list)) {
1825 view = container_of(output->view_cleanup_list.next,
1826 struct rpir_view, link);
1827 rpir_view_destroy(view);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001828 }
1829
1830 free(output);
1831 base->renderer_state = NULL;
1832}
1833
1834WL_EXPORT void
1835rpi_renderer_set_update_handle(struct weston_output *base,
1836 DISPMANX_UPDATE_HANDLE_T handle)
1837{
1838 struct rpir_output *output = to_rpir_output(base);
1839
1840 output->update = handle;
1841}
1842
1843WL_EXPORT void
1844rpi_renderer_finish_frame(struct weston_output *base)
1845{
1846 struct rpir_output *output = to_rpir_output(base);
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001847 struct weston_compositor *compositor = base->compositor;
Jason Ekstranda7af7042013-10-12 22:38:11 -05001848 struct weston_view *wv;
1849 struct rpir_view *view;
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001850
Jason Ekstranda7af7042013-10-12 22:38:11 -05001851 while (!wl_list_empty(&output->view_cleanup_list)) {
1852 view = container_of(output->view_cleanup_list.next,
1853 struct rpir_view, link);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001854
Jason Ekstranda7af7042013-10-12 22:38:11 -05001855 if (view->view) {
1856 /* The weston_view still exists, but is
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001857 * temporarily not visible, and hence its Element
1858 * was removed. The current front buffer contents
1859 * must be preserved.
1860 */
Jason Ekstranda7af7042013-10-12 22:38:11 -05001861 if (!view->surface->visible_views)
1862 rpi_resource_release(view->surface->back);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001863
Jason Ekstranda7af7042013-10-12 22:38:11 -05001864 wl_list_remove(&view->link);
1865 wl_list_init(&view->link);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001866 } else {
Jason Ekstranda7af7042013-10-12 22:38:11 -05001867 rpir_view_destroy(view);
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001868 }
1869 }
1870
Jason Ekstranda7af7042013-10-12 22:38:11 -05001871 wl_list_for_each(wv, &compositor->view_list, link) {
1872 view = to_rpir_view(wv);
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001873
Jason Ekstranda7af7042013-10-12 22:38:11 -05001874 if (view->surface->buffer_type != BUFFER_TYPE_EGL)
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001875 continue;
1876
Tomeu Vizoso74987582013-10-25 10:34:38 +02001877 rpir_egl_buffer_destroy(view->surface->egl_old_front);
Jason Ekstranda7af7042013-10-12 22:38:11 -05001878 view->surface->egl_old_front = NULL;
Tomeu Vizosob4659eb2013-10-07 11:02:20 +02001879 }
1880
Pekka Paalanend7265bc2013-05-22 18:03:06 +03001881 wl_signal_emit(&base->frame_signal, base);
1882}