[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