[PATCH 3/6] gl-renderer: Add support for a few YUV dmabuf formats

Derek Foreman derekf at osg.samsung.com
Wed Dec 2 15:32:24 PST 2015


On 24/11/15 01:28 PM, Emmanuel Gil Peyrot wrote:
> Namely the single-planar YUYV, the two-planar NV12, and the
> three-planar YUV420, using the shaders already present in Weston.
> 
> Signed-off-by: Emmanuel Gil Peyrot <emmanuel.peyrot at collabora.com>
> 
> Maniphest Tasks: T13
> 
> Differential Revision: https://phabricator.freedesktop.org/D334
> ---
>  src/gl-renderer.c | 280 +++++++++++++++++++++++++++++++++++++++++++++---------
>  1 file changed, 237 insertions(+), 43 deletions(-)
> 
> diff --git a/src/gl-renderer.c b/src/gl-renderer.c
> index 90e4842..cce93ee 100644
> --- a/src/gl-renderer.c
> +++ b/src/gl-renderer.c
> @@ -101,11 +101,36 @@ struct egl_image {
>  	int refcount;
>  };
>  
> +enum import_type {
> +	IMPORT_TYPE_INVALID,
> +	IMPORT_TYPE_DIRECT,
> +	IMPORT_TYPE_GL_CONVERSION
> +};
> +
>  struct dmabuf_image {
>  	struct linux_dmabuf_buffer *dmabuf;
>  	int num_images;
>  	struct egl_image *images[3];
>  	struct wl_list link;
> +
> +	enum import_type import_type;
> +	GLenum target;
> +	struct gl_shader *shader;
> +};
> +
> +struct yuv_plane_descriptor {
> +	int width_divisor;
> +	int height_divisor;
> +	uint32_t format;
> +	int plane_index;
> +};
> +
> +struct yuv_format_descriptor {
> +	uint32_t format;
> +	int input_planes;
> +	int output_planes;
> +	int texture_type;
> +	struct yuv_plane_descriptor plane[4];
>  };
>  
>  struct gl_surface_state {
> @@ -197,7 +222,7 @@ static PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL;
>  static inline const char *
>  dump_format(uint32_t *format)
>  {
> -	// FIXME: this won’t work properly on big-endian.
> +	/* FIXME: this won’t work properly on big-endian. */

mumble mumble unrelated, see patch 2 comment. ;)


>  	return (const char *)format;
>  }
>  
> @@ -1514,6 +1539,171 @@ import_simple_dmabuf(struct gl_renderer *gr,
>  	return image;
>  }
>  
> +struct yuv_format_descriptor yuv_formats[] = {
> +	{
> +		.format = DRM_FORMAT_YUYV,
> +		.input_planes = 1,
> +		.output_planes = 2,
> +		.texture_type = EGL_TEXTURE_Y_XUXV_WL,
> +		{{
> +			.width_divisor = 1,
> +			.height_divisor = 1,
> +			.format = DRM_FORMAT_GR88,
> +			.plane_index = 0
> +		}, {
> +			.width_divisor = 2,
> +			.height_divisor = 1,
> +			.format = DRM_FORMAT_ARGB8888,
> +			.plane_index = 0
> +		}}
> +	}, {
> +		.format = DRM_FORMAT_NV12,
> +		.input_planes = 2,
> +		.output_planes = 2,
> +		.texture_type = EGL_TEXTURE_Y_UV_WL,
> +		{{
> +			.width_divisor = 1,
> +			.height_divisor = 1,
> +			.format = DRM_FORMAT_R8,
> +			.plane_index = 0
> +		}, {
> +			.width_divisor = 2,
> +			.height_divisor = 2,
> +			.format = DRM_FORMAT_GR88,
> +			.plane_index = 1
> +		}}
> +	}, {
> +		.format = DRM_FORMAT_YUV420,
> +		.input_planes = 3,
> +		.output_planes = 3,
> +		.texture_type = EGL_TEXTURE_Y_U_V_WL,
> +		{{
> +			.width_divisor = 1,
> +			.height_divisor = 1,
> +			.format = DRM_FORMAT_R8,
> +			.plane_index = 0
> +		}, {
> +			.width_divisor = 2,
> +			.height_divisor = 2,
> +			.format = DRM_FORMAT_R8,
> +			.plane_index = 1
> +		}, {
> +			.width_divisor = 2,
> +			.height_divisor = 2,
> +			.format = DRM_FORMAT_R8,
> +			.plane_index = 2
> +		}}
> +	}
> +};
> +
> +static struct egl_image *
> +import_dmabuf_single_plane(struct gl_renderer *gr,
> +                           const struct dmabuf_attributes *attributes,
> +                           struct yuv_plane_descriptor *descriptor)
> +{
> +	struct dmabuf_attributes plane;
> +	struct egl_image *image;
> +
> +	plane.width = attributes->width / descriptor->width_divisor;
> +	plane.height = attributes->height / descriptor->height_divisor;
> +	plane.format = descriptor->format;
> +	plane.n_planes = 1;
> +	plane.fd[0] = attributes->fd[descriptor->plane_index];
> +	plane.offset[0] = attributes->offset[descriptor->plane_index];
> +	plane.stride[0] = attributes->stride[descriptor->plane_index];
> +	plane.modifier[0] = attributes->modifier[descriptor->plane_index];
> +
> +	image = import_simple_dmabuf(gr, &plane);
> +	if (!image) {
> +		weston_log("Failed to import plane %d as %.4s\n",
> +		           descriptor->plane_index,
> +		           dump_format(&descriptor->format));
> +		return NULL;
> +	}
> +
> +	return image;
> +}
> +
> +static bool
> +import_yuv_dmabuf(struct gl_renderer *gr,
> +                  struct dmabuf_image *image)
> +{
> +	unsigned i;
> +	int j;
> +	int ret;
> +	struct yuv_format_descriptor *format;
> +	struct dmabuf_attributes *attributes = &image->dmabuf->attributes;
> +
> +	for (i = 0; i < ARRAY_LENGTH(yuv_formats); ++i) {
> +		if (yuv_formats[i].format == attributes->format) {
> +			format = &yuv_formats[i];
> +			goto format_found;
> +		}
> +	}

It's probably unreasonable gotophobia on my part, but I'd rather see
this done without the goto.  (init format to NULL, break instead of
goto, test for null for this following section...)

> +
> +	weston_log("Error during import, and no known conversion for format "
> +	           "%.4s in the renderer", dump_format(&attributes->format));
> +	return false;
> +
> +format_found:
> +	if (attributes->n_planes != format->input_planes) {
> +		weston_log("%.4s dmabuf must contain %d plane%s (%d provided)",

I guess this makes it easy for a sketchy client to fill the logs?  I
don't know if we should worry about that or not...

> +		           dump_format(&format->format),
> +		           format->input_planes,
> +		           (format->input_planes > 1) ? "s" : "",
> +		           attributes->n_planes);
> +		return false;
> +	}
> +
> +	for (j = 0; j < format->output_planes; ++j) {
> +		image->images[j] = import_dmabuf_single_plane(gr, attributes,
> +		                                              &format->plane[j]);
> +		if (!image->images[j]) {
> +			while (j) {
> +				ret = egl_image_unref(image->images[--j]);
> +				assert(ret == 0);
> +			}
> +			return false;
> +		}
> +	}
> +
> +	image->num_images = format->output_planes;
> +
> +	switch (format->texture_type) {
> +	case EGL_TEXTURE_Y_XUXV_WL:
> +		image->shader = &gr->texture_shader_y_xuxv;
> +		break;
> +	case EGL_TEXTURE_Y_UV_WL:
> +		image->shader = &gr->texture_shader_y_uv;
> +		break;
> +	case EGL_TEXTURE_Y_U_V_WL:
> +		image->shader = &gr->texture_shader_y_u_v;
> +		break;
> +	default:
> +		assert(false);
> +	}
> +
> +	return true;
> +}
> +
> +static GLenum
> +choose_texture_target(struct dmabuf_attributes *attributes)
> +{
> +	if (attributes->n_planes > 1)
> +		return GL_TEXTURE_EXTERNAL_OES;
> +
> +	switch (attributes->format & ~DRM_FORMAT_BIG_ENDIAN) {
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +	case DRM_FORMAT_UYVY:
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_AYUV:
> +		return GL_TEXTURE_EXTERNAL_OES;
> +	default:
> +		return GL_TEXTURE_2D;
> +	}
> +}
> +
>  static struct dmabuf_image *
>  import_dmabuf(struct gl_renderer *gr,
>  	      struct linux_dmabuf_buffer *dmabuf)
> @@ -1521,17 +1711,31 @@ import_dmabuf(struct gl_renderer *gr,
>  	struct egl_image *egl_image;
>  	struct dmabuf_image *image;
>  
> -	egl_image = import_simple_dmabuf(gr, &dmabuf->attributes);
> -	if (!egl_image) {
> -		weston_log("Format %.4s unsupported by EGL, aborting\n",
> -		           dump_format(&dmabuf->attributes.format));
> -		return NULL;
> -	}
> -
>  	image = dmabuf_image_create();
>  	image->dmabuf = dmabuf;
> -	image->num_images = 1;
> -	image->images[0] = egl_image;
> +
> +	egl_image = import_simple_dmabuf(gr, &dmabuf->attributes);
> +	if (egl_image) {
> +		image->num_images = 1;
> +		image->images[0] = egl_image;
> +		image->import_type = IMPORT_TYPE_DIRECT;
> +		image->target = choose_texture_target(&dmabuf->attributes);
> +
> +		switch (image->target) {
> +		case GL_TEXTURE_2D:
> +			image->shader = &gr->texture_shader_rgba;
> +			break;
> +		default:
> +			image->shader = &gr->texture_shader_egl_external;
> +		}
> +	} else {
> +		if (!import_yuv_dmabuf(gr, image)) {
> +			dmabuf_image_destroy(image);
> +			return NULL;
> +		}
> +		image->import_type = IMPORT_TYPE_GL_CONVERSION;
> +		image->target = GL_TEXTURE_2D;
> +	}
>  
>  	return image;
>  }
> @@ -1567,22 +1771,28 @@ gl_renderer_import_dmabuf(struct weston_compositor *ec,
>  	return true;
>  }
>  
> -static GLenum
> -choose_texture_target(struct dmabuf_attributes *attributes)
> +static bool
> +import_dmabuf2(struct gl_renderer *gr,

Not really a fan of this function name...

Those minor nits fixed, this looks really cool.

Reviewed-by: Derek Foreman <derekf at osg.samsung.com>

> +               struct dmabuf_image *image)
>  {
> -	if (attributes->n_planes > 1)
> -		return GL_TEXTURE_EXTERNAL_OES;
> +	switch (image->import_type) {
> +	case IMPORT_TYPE_DIRECT:
> +		image->images[0] = import_simple_dmabuf(gr, &image->dmabuf->attributes);
> +		if (!image->images[0])
> +			return false;
> +		break;
> +
> +	case IMPORT_TYPE_GL_CONVERSION:
> +		if (!import_yuv_dmabuf(gr, image))
> +			return false;
> +		break;
>  
> -	switch (attributes->format & ~DRM_FORMAT_BIG_ENDIAN) {
> -	case DRM_FORMAT_YUYV:
> -	case DRM_FORMAT_YVYU:
> -	case DRM_FORMAT_UYVY:
> -	case DRM_FORMAT_VYUY:
> -	case DRM_FORMAT_AYUV:
> -		return GL_TEXTURE_EXTERNAL_OES;
>  	default:
> -		return GL_TEXTURE_2D;
> +		weston_log("Invalid import type for dmabuf\n");
> +		return false;
>  	}
> +
> +	return true;
>  }
>  
>  static void
> @@ -1611,15 +1821,6 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface,
>  		egl_image_unref(gs->images[i]);
>  	gs->num_images = 0;
>  
> -	gs->target = choose_texture_target(&dmabuf->attributes);
> -	switch (gs->target) {
> -	case GL_TEXTURE_2D:
> -		gs->shader = &gr->texture_shader_rgba;
> -		break;
> -	default:
> -		gs->shader = &gr->texture_shader_egl_external;
> -	}
> -
>  	/*
>  	 * We try to always hold an imported EGLImage from the dmabuf
>  	 * to prevent the client from preventing re-imports. But, we also
> @@ -1638,24 +1839,16 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface,
>  		assert(ret == 0);
>  	}
>  
> -	for (i = 0; i < image->num_images; ++i) {
> -		image->images[i] = import_simple_dmabuf(gr, &image->dmabuf->attributes);
> -		if (!image->images[i]) {
> -			image->num_images = 0;
> -			while (i) {
> -				ret = egl_image_unref(image->images[--i])
> -				assert(ret == 0);
> -			}
> -			linux_dmabuf_buffer_send_server_error(dmabuf,
> -							      "EGL dmabuf import failed");
> -			return;
> -		}
> +	if (!import_dmabuf2(gr, image)) {
> +		linux_dmabuf_buffer_send_server_error(dmabuf, "EGL dmabuf import failed");
> +		return;
>  	}
>  
>  	gs->num_images = image->num_images;
>  	for (i = 0; i < gs->num_images; ++i)
>  		gs->images[i] = egl_image_ref(image->images[i]);
>  
> +	gs->target = image->target;
>  	ensure_textures(gs, gs->num_images);
>  	for (i = 0; i < gs->num_images; ++i) {
>  		glActiveTexture(GL_TEXTURE0 + i);
> @@ -1663,6 +1856,7 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface,
>  		gr->image_target_texture_2d(gs->target, gs->images[i]->image);
>  	}
>  
> +	gs->shader = image->shader;
>  	gs->pitch = buffer->width;
>  	gs->height = buffer->height;
>  	gs->buffer_type = BUFFER_TYPE_EGL;
> 



More information about the wayland-devel mailing list