[PATCH weston v3] rpi: Add support for EGL buffers

Kristian Høgsberg hoegsberg at gmail.com
Tue Oct 8 20:33:45 CEST 2013


On Mon, Oct 07, 2013 at 11:02:20AM +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.
> ---
> 
> v2: Added a stub for vc_dispmanx_get_handle_from_wl_buffer
> v3: Release any wl_buffers when their surface is destroyed

Thanks Tomeu, committed and pushed this.  I get a couple of warnings
when compiling:

rpi-renderer.c:1297:10: note: expected 'struct wl_buffer *' but argument is of type 'struct wl_resource *'
rpi-renderer.c:1305:12: warning: passing argument 2 of 'renderer->query_buffer' from incompatible pointer type [enabled by default]
            EGL_HEIGHT, &buffer->height);
            ^
rpi-renderer.c:1305:12: note: expected 'struct wl_buffer *' but argument is of type 'struct wl_resource *'

Kristian

>  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
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel


More information about the wayland-devel mailing list