The EGL implementation on the RPi allocates a front and a back
DispmanX resources for each EGLSurface, which we composite along
the others.
---

v2: Added a stub for vc_dispmanx_get_handle_from_wl_buffer
v3: Release any wl_buffers when their surface is destroyed

 src/rpi-bcm-stubs.h |   7 ++
 src/rpi-renderer.c  | 271 ++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 250 insertions(+), 28 deletions(-)

diff --git a/src/rpi-bcm-stubs.h b/src/rpi-bcm-stubs.h
index d3ffd4b..703cd77 100644
--- a/src/rpi-bcm-stubs.h
+++ b/src/rpi-bcm-stubs.h
@@ -296,6 +296,13 @@ vc_dispmanx_snapshot(DISPMANX_DISPLAY_HANDLE_T display,
        return -1;
 }
 
+struct wl_resource;
+static inline DISPMANX_RESOURCE_HANDLE_T
+vc_dispmanx_get_handle_from_wl_buffer(struct wl_resource *_buffer)
+{
+       return DISPMANX_NO_HANDLE;
+}
+
 /* from /opt/vc/include/EGL/eglplatform.h */
 
 typedef struct {
diff --git a/src/rpi-renderer.c b/src/rpi-renderer.c
index 3ba5fc4..2660031 100644
--- a/src/rpi-renderer.c
+++ b/src/rpi-renderer.c
@@ -35,6 +35,12 @@
 #include "compositor.h"
 #include "rpi-renderer.h"
 
+#ifdef ENABLE_EGL
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include "weston-egl-ext.h"
+#endif
+
 /*
  * Dispmanx API offers alpha-blended overlays for hardware compositing.
  * The final composite consists of dispmanx elements, and their contents:
@@ -84,6 +90,17 @@ struct rpi_resource {
 
 struct rpir_output;
 
+struct rpir_egl_buffer {
+       struct weston_buffer_reference buffer_ref;
+       DISPMANX_RESOURCE_HANDLE_T resource_handle;
+};
+
+enum buffer_type {
+       BUFFER_TYPE_NULL,
+       BUFFER_TYPE_SHM,
+       BUFFER_TYPE_EGL
+};
+
 struct rpir_surface {
        struct weston_surface *surface;
 
@@ -102,7 +119,12 @@ struct rpir_surface {
        struct rpi_resource *back;
        pixman_region32_t prev_damage;
 
+       struct rpir_egl_buffer *egl_front;
+       struct rpir_egl_buffer *egl_back;
+       struct rpir_egl_buffer *egl_old_front;
+
        struct weston_buffer_reference buffer_ref;
+       enum buffer_type buffer_type;
 };
 
 struct rpir_output {
@@ -125,6 +147,15 @@ struct rpi_renderer {
        struct weston_renderer base;
 
        int single_buffer;
+
+#ifdef ENABLE_EGL
+       EGLDisplay egl_display;
+
+       PFNEGLBINDWAYLANDDISPLAYWL bind_display;
+       PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
+       PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
+#endif
+       int has_bind_display;
 };
 
 static inline struct rpir_surface *
@@ -335,6 +366,7 @@ rpir_surface_create(struct rpi_renderer *renderer)
                surface->back = &surface->resources[0];
        else
                surface->back = &surface->resources[1];
+       surface->buffer_type = BUFFER_TYPE_NULL;
 
        pixman_region32_init(&surface->prev_damage);
 
@@ -354,6 +386,24 @@ rpir_surface_destroy(struct rpir_surface *surface)
        rpi_resource_release(&surface->resources[1]);
        DBG("rpir_surface %p destroyed (%u)\n", surface, surface->handle);
 
+       if (surface->egl_back != NULL) {
+               weston_buffer_reference(&surface->egl_back->buffer_ref, NULL);
+               free(surface->egl_back);
+               surface->egl_back = NULL;
+       }
+
+       if (surface->egl_front != NULL) {
+               weston_buffer_reference(&surface->egl_front->buffer_ref, NULL);
+               free(surface->egl_front);
+               surface->egl_front = NULL;
+       }
+
+       if (surface->egl_old_front != NULL) {
+               weston_buffer_reference(&surface->egl_old_front->buffer_ref, 
NULL);
+               free(surface->egl_old_front);
+               surface->egl_old_front = NULL;
+       }
+
        free(surface);
 }
 
@@ -472,8 +522,16 @@ rpir_surface_compute_rects(struct rpir_surface *surface,
 
        src_x = 0 << 16;
        src_y = 0 << 16;
-       src_width = surface->front->width << 16;
-       src_height = surface->front->height << 16;
+
+       if (surface->buffer_type == BUFFER_TYPE_EGL) {
+               struct weston_buffer *buffer = 
surface->egl_front->buffer_ref.buffer;
+
+               src_width = buffer->width << 16;
+               src_height = buffer->height << 16;
+       } else {
+               src_width = surface->front->width << 16;
+               src_height = surface->front->height << 16;
+       }
 
        weston_matrix_multiply(&matrix, &output->matrix);
 
@@ -647,6 +705,10 @@ rpir_surface_compute_rects(struct rpir_surface *surface,
                return -1;
        }
 
+       /* EGL buffers will be upside-down related to what DispmanX expects */
+       if (surface->buffer_type == BUFFER_TYPE_EGL)
+               flipt ^= TRANSFORM_VFLIP;
+
        vc_dispmanx_rect_set(src_rect, src_x, src_y, src_width, src_height);
        vc_dispmanx_rect_set(dst_rect, dst_x, dst_y, dst_width, dst_height);
        *flipmask = flipt;
@@ -681,6 +743,22 @@ vc_image2dispmanx_transform(VC_IMAGE_TRANSFORM_T t)
        }
 }
 
+
+static DISPMANX_RESOURCE_HANDLE_T
+rpir_surface_get_resource(struct rpir_surface *surface)
+{
+       switch (surface->buffer_type) {
+       case BUFFER_TYPE_SHM:
+       case BUFFER_TYPE_NULL:
+               return surface->front->handle;
+       case BUFFER_TYPE_EGL:
+               if (surface->egl_front != NULL)
+                       return surface->egl_front->resource_handle;
+       default:
+               return DISPMANX_NO_HANDLE;
+       }
+}
+
 static int
 rpir_surface_dmx_add(struct rpir_surface *surface, struct rpir_output *output,
                    DISPMANX_UPDATE_HANDLE_T update, int layer)
@@ -700,6 +778,13 @@ rpir_surface_dmx_add(struct rpir_surface *surface, struct 
rpir_output *output,
        VC_RECT_T src_rect;
        VC_IMAGE_TRANSFORM_T flipmask;
        int ret;
+       DISPMANX_RESOURCE_HANDLE_T resource_handle;
+
+       resource_handle = rpir_surface_get_resource(surface);
+       if (resource_handle == DISPMANX_NO_HANDLE) {
+               weston_log("%s: no buffer yet, aborting\n", __func__);
+               return 0;
+       }
 
        ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect,
                                         &flipmask);
@@ -711,17 +796,14 @@ rpir_surface_dmx_add(struct rpir_surface *surface, struct 
rpir_output *output,
                output->display,
                layer,
                &dst_rect,
-               surface->front->handle,
+               resource_handle,
                &src_rect,
                DISPMANX_PROTECTION_NONE,
                &alphasetup,
                NULL /* clamp */,
                vc_image2dispmanx_transform(flipmask));
-       DBG("rpir_surface %p add %u, alpha %f\n", surface, surface->handle,
-           surface->surface->alpha);
-
-       if (surface->handle == DISPMANX_NO_HANDLE)
-               return -1;
+       DBG("rpir_surface %p add %u, alpha %f resource %d\n", surface,
+           surface->handle, surface->surface->alpha, resource_handle);
 
        return 1;
 }
@@ -758,6 +840,20 @@ rpir_surface_dmx_move(struct rpir_surface *surface,
 
        /* XXX: return early, if all attributes stay the same */
 
+       if (surface->buffer_type == BUFFER_TYPE_EGL) {
+               DISPMANX_RESOURCE_HANDLE_T resource_handle;
+
+               resource_handle = rpir_surface_get_resource(surface);
+               if (resource_handle == DISPMANX_NO_HANDLE) {
+                       weston_log("%s: no buffer yet, aborting\n", __func__);
+                       return 0;
+               }
+
+               vc_dispmanx_element_change_source(update,
+                                                 surface->handle,
+                                                 resource_handle);
+       }
+
        ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect,
                                         &flipmask);
        if (ret < 0)
@@ -835,8 +931,31 @@ rpir_surface_update(struct rpir_surface *surface, struct 
rpir_output *output,
        int ret;
        int obscured;
 
-       if (need_swap)
-               rpir_surface_swap_pointers(surface);
+       if (surface->buffer_type == BUFFER_TYPE_EGL) {
+               if (surface->egl_back != NULL) {
+                       assert(surface->egl_old_front == NULL);
+                       surface->egl_old_front = surface->egl_front;
+                       surface->egl_front = surface->egl_back;
+                       surface->egl_back = NULL;
+               }
+               if (surface->egl_front->buffer_ref.buffer == NULL) {
+                       weston_log("warning: client unreffed current front 
buffer\n");
+
+                       wl_list_remove(&surface->link);
+                       if (surface->handle == DISPMANX_NO_HANDLE) {
+                               wl_list_init(&surface->link);
+                       } else {
+                               rpir_surface_dmx_remove(surface, update);
+                               wl_list_insert(&output->surface_cleanup_list,
+                                                  &surface->link);
+                       }
+
+                       goto out;
+               }
+       } else {
+               if (need_swap)
+                       rpir_surface_swap_pointers(surface);
+       }
 
        obscured = is_surface_not_visible(surface->surface);
        if (obscured) {
@@ -1133,40 +1252,72 @@ static void
 rpi_renderer_attach(struct weston_surface *base, struct weston_buffer *buffer)
 {
        /* Called every time a client commits an attach. */
-       static int warned;
        struct rpir_surface *surface = to_rpir_surface(base);
 
        assert(surface);
        if (!surface)
                return;
 
-       if (buffer && !wl_shm_buffer_get(buffer->resource) && !warned) {
-               weston_log("Error: non-wl_shm buffers not supported.\n");
-               warned = 1;
-               return;
+       if (surface->buffer_type == BUFFER_TYPE_SHM) {
+               if (!surface->single_buffer)
+                       /* XXX: need to check if in middle of update */
+                       rpi_resource_release(surface->back);
+
+               if (surface->handle == DISPMANX_NO_HANDLE)
+                       /* XXX: cannot do this, if middle of an update */
+                       rpi_resource_release(surface->front);
+
+               weston_buffer_reference(&surface->buffer_ref, NULL);
        }
 
+       /* If buffer is NULL, Weston core unmaps the surface, the surface
+        * will not appear in repaint list, and so rpi_renderer_repaint_output
+        * will remove the DispmanX element. Later, for SHM, also the front
+        * buffer will be released in the cleanup_list processing.
+        */
+       if (!buffer)
+               return;
+
        if (wl_shm_buffer_get(buffer->resource)) {
+               surface->buffer_type = BUFFER_TYPE_SHM;
                buffer->shm_buffer = wl_shm_buffer_get(buffer->resource);
                buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer);
                buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer);
-       }
 
-       weston_buffer_reference(&surface->buffer_ref, buffer);
+               weston_buffer_reference(&surface->buffer_ref, buffer);
+       } else {
+#if ENABLE_EGL
+               struct rpi_renderer *renderer = 
to_rpi_renderer(base->compositor);
+               struct wl_resource *wl_resource = 
+                       (struct wl_resource *)buffer->resource;
+
+               if (!renderer->has_bind_display ||
+                   !renderer->query_buffer(renderer->egl_display,
+                                           wl_resource,
+                                           EGL_WIDTH, &buffer->width)) {
+                       weston_log("unhandled buffer type!\n");
+                       weston_buffer_reference(&surface->buffer_ref, NULL);
+                       surface->buffer_type = BUFFER_TYPE_NULL;
+               }
+
+               renderer->query_buffer(renderer->egl_display,
+                                      wl_resource,
+                                      EGL_HEIGHT, &buffer->height);
 
-       /* XXX: need to check if in middle of update
-       if (!buffer && !surface->single_buffer)
-               rpi_resource_release(surface->back); */
+               surface->buffer_type = BUFFER_TYPE_EGL;
 
-       /* XXX: cannot do this, if middle of an update
-       if (surface->handle == DISPMANX_NO_HANDLE)
-               rpi_resource_release(surface->front); */
+               if(surface->egl_back == NULL)
+                       surface->egl_back = calloc(1, sizeof 
*surface->egl_back);
 
-       /* If buffer is NULL, Weston core unmaps the surface, the surface
-        * will not appear in repaint list, and so rpi_renderer_repaint_output
-        * will remove the DispmanX element. Later, also the front buffer
-        * will be released in the cleanup_list processing.
-        */
+               weston_buffer_reference(&surface->egl_back->buffer_ref, buffer);
+               surface->egl_back->resource_handle =
+                       vc_dispmanx_get_handle_from_wl_buffer(wl_resource);
+#else
+               weston_log("unhandled buffer type!\n");
+               weston_buffer_reference(&surface->buffer_ref, NULL);
+               surface->buffer_type = BUFFER_TYPE_NULL;
+#endif
+       }
 }
 
 static int
@@ -1256,6 +1407,12 @@ rpi_renderer_destroy(struct weston_compositor 
*compositor)
 {
        struct rpi_renderer *renderer = to_rpi_renderer(compositor);
 
+#if ENABLE_EGL
+       if (renderer->has_bind_display)
+               renderer->unbind_display(renderer->egl_display,
+                                        compositor->wl_display);
+#endif
+
        free(renderer);
        compositor->renderer = NULL;
 }
@@ -1265,6 +1422,11 @@ rpi_renderer_create(struct weston_compositor *compositor,
                    const struct rpi_renderer_parameters *params)
 {
        struct rpi_renderer *renderer;
+#if ENABLE_EGL
+       const char *extensions;
+       EGLBoolean ret;
+       EGLint major, minor;
+#endif
 
        weston_log("Initializing the DispmanX compositing renderer\n");
 
@@ -1283,6 +1445,43 @@ rpi_renderer_create(struct weston_compositor *compositor,
        renderer->base.destroy_surface = rpi_renderer_destroy_surface;
        renderer->base.destroy = rpi_renderer_destroy;
 
+#ifdef ENABLE_EGL
+       renderer->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+       if (renderer->egl_display == EGL_NO_DISPLAY) {
+               weston_log("failed to create EGL display\n");
+               return -1;
+       }
+
+       if (!eglInitialize(renderer->egl_display, &major, &minor)) {
+               weston_log("failed to initialize EGL display\n");
+               return -1;
+       }
+
+       renderer->bind_display =
+               (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
+       renderer->unbind_display =
+               (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
+       renderer->query_buffer =
+               (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
+
+       extensions = (const char *) eglQueryString(renderer->egl_display,
+                                                  EGL_EXTENSIONS);
+       if (!extensions) {
+               weston_log("Retrieving EGL extension string failed.\n");
+               return -1;
+       }
+
+       if (strstr(extensions, "EGL_WL_bind_wayland_display"))
+               renderer->has_bind_display = 1;
+
+       if (renderer->has_bind_display) {
+               ret = renderer->bind_display(renderer->egl_display,
+                                            compositor->wl_display);
+               if (!ret)
+                       renderer->has_bind_display = 0;
+       }
+#endif
+
        compositor->renderer = &renderer->base;
        compositor->read_format = PIXMAN_a8r8g8b8;
        /* WESTON_CAP_ROTATION_ANY not supported */
@@ -1354,6 +1553,8 @@ WL_EXPORT void
 rpi_renderer_finish_frame(struct weston_output *base)
 {
        struct rpir_output *output = to_rpir_output(base);
+       struct weston_compositor *compositor = base->compositor;
+       struct weston_surface *ws;
        struct rpir_surface *surface;
 
        while (!wl_list_empty(&output->surface_cleanup_list)) {
@@ -1376,5 +1577,19 @@ rpi_renderer_finish_frame(struct weston_output *base)
                }
        }
 
+       wl_list_for_each(ws, &compositor->surface_list, link) {
+               surface = to_rpir_surface(ws);
+
+               if (surface->buffer_type != BUFFER_TYPE_EGL)
+                       continue;
+
+               if(surface->egl_old_front == NULL)
+                       continue;
+
+               weston_buffer_reference(&surface->egl_old_front->buffer_ref, 
NULL);
+               free(surface->egl_old_front);
+               surface->egl_old_front = NULL;
+       }
+
        wl_signal_emit(&base->frame_signal, base);
 }
-- 
1.8.3.1

_______________________________________________
wayland-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to