[RFC PATCH weston] rpi: Add support for EGL buffers
Kristian Høgsberg
hoegsberg at gmail.com
Tue Oct 1 10:50:22 PDT 2013
On Tue, Oct 01, 2013 at 12:53:03PM +0200, Tomeu Vizoso wrote:
> The EGL implementation on the RPi allocates a front and a back
> DispmanX resources for each EGLSurface, which we composite along
> the others.
Is this something you want in 1.3? We're the RC phase now and I hope
to release 1.3 this Friday. However, for compositor-rpi.c I defer to
you, Pekka and Daniel. We can apply this to master and ship with 1.3,
but I can't test it or vouch for the quality, so it's up to you guys.
Kristian
>
> Remaining issues:
>
> * The EGL side isn't sending sync messages from SwapBuffers while waiting
> for buffer.release events, so it requires weston to post them inmediately. It
> has been tested with Neil's patch:
> http://article.gmane.org/gmane.comp.freedesktop.wayland.devel/10769
>
> * weston-subsurfaces doesn't work as expected because of the frame callbacks
> starving the client's event loop:
> http://thread.gmane.org/gmane.comp.freedesktop.wayland.devel/10959
>
> src/rpi-renderer.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 225 insertions(+), 28 deletions(-)
>
> diff --git a/src/rpi-renderer.c b/src/rpi-renderer.c
> index 3ba5fc4..7a8558e 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);
>
> @@ -472,8 +504,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 +687,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 +725,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 +760,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 +778,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 +822,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 +913,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 +1234,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;
> + }
>
> - /* XXX: need to check if in middle of update
> - if (!buffer && !surface->single_buffer)
> - rpi_resource_release(surface->back); */
> + renderer->query_buffer(renderer->egl_display,
> + wl_resource,
> + EGL_HEIGHT, &buffer->height);
>
> - /* XXX: cannot do this, if middle of an update
> - if (surface->handle == DISPMANX_NO_HANDLE)
> - rpi_resource_release(surface->front); */
> + surface->buffer_type = BUFFER_TYPE_EGL;
>
> - /* 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.
> - */
> + if(surface->egl_back == NULL)
> + surface->egl_back = calloc(1, sizeof *surface->egl_back);
> +
> + 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 +1389,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 +1404,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 +1427,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 +1535,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 +1559,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
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel
More information about the wayland-devel
mailing list