gl-renderer: add dmabuf import

Import dmabuf as an EGLImage, and hold on to the EGLImage until we are
signalled a content change. On content change, destroy the EGLImage and
re-import to trigger GPU cache flushes.

We hold on to the EGLImage as long as possible just in case the client
does other imports that might later make re-importing fail.

As dmabuf protocol uses drm_fourcc codes, we need libdrm for
drm_fourcc.h. However, we are not doing any libdrm function calls, so
there is no new need to link to libdrm.

RFCv1 changes:
- fix error if dmabuf exposed unsupported
- always use GL_TEXTURE_EXTERNAL_OES with dmabuf

v2 changes:
- improve support check and error handling
- hold on to the imported EGLImage to avoid the dmabuf becoming
  unimportable in the future
- send internal errors with linux_dmabuf_buffer_send_server_error()
- import EGL_EXT_image_dma_buf_import extension headers
- use heuristics to decide between GL_TEXTURE_2D and
  GL_TEXTURE_EXTERNAL_OES
- add comment about Mesa requirements
- change y-invert from per-plane boolean to per-buffer flag

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Signed-off-by: Louis-Francis Ratté-Boulianne <lfrb@collabora.com>
Reviewed-by: Daniel Stone <daniels@collabora.com>
dev
Pekka Paalanen 11 years ago
parent 534defdf43
commit a352580285
  1. 1
      Makefile.am
  2. 1
      configure.ac
  3. 233
      src/gl-renderer.c
  4. 16
      src/weston-egl-ext.h

@ -205,6 +205,7 @@ gl_renderer_la_LIBADD = $(COMPOSITOR_LIBS) $(EGL_LIBS)
gl_renderer_la_CFLAGS = \ gl_renderer_la_CFLAGS = \
$(COMPOSITOR_CFLAGS) \ $(COMPOSITOR_CFLAGS) \
$(EGL_CFLAGS) \ $(EGL_CFLAGS) \
$(GL_RENDERER_CFLAGS) \
$(AM_CFLAGS) $(AM_CFLAGS)
gl_renderer_la_SOURCES = \ gl_renderer_la_SOURCES = \
src/gl-renderer.h \ src/gl-renderer.h \

@ -87,6 +87,7 @@ if test x$enable_egl = xyes; then
AC_DEFINE([ENABLE_EGL], [1], [Build Weston with EGL support]) AC_DEFINE([ENABLE_EGL], [1], [Build Weston with EGL support])
PKG_CHECK_MODULES(EGL, [egl >= 7.10 glesv2]) PKG_CHECK_MODULES(EGL, [egl >= 7.10 glesv2])
PKG_CHECK_MODULES([EGL_TESTS], [egl >= 7.10 glesv2 wayland-client wayland-egl]) PKG_CHECK_MODULES([EGL_TESTS], [egl >= 7.10 glesv2 wayland-client wayland-egl])
PKG_CHECK_MODULES([GL_RENDERER], [libdrm])
fi fi
AC_ARG_ENABLE(xkbcommon, AC_ARG_ENABLE(xkbcommon,

@ -36,9 +36,12 @@
#include <float.h> #include <float.h>
#include <assert.h> #include <assert.h>
#include <linux/input.h> #include <linux/input.h>
#include <drm_fourcc.h>
#include "gl-renderer.h" #include "gl-renderer.h"
#include "vertex-clipping.h" #include "vertex-clipping.h"
#include "linux-dmabuf.h"
#include "linux-dmabuf-server-protocol.h"
#include "shared/helpers.h" #include "shared/helpers.h"
#include "weston-egl-ext.h" #include "weston-egl-ext.h"
@ -96,6 +99,10 @@ struct egl_image {
struct gl_renderer *renderer; struct gl_renderer *renderer;
EGLImageKHR image; EGLImageKHR image;
int refcount; int refcount;
/* Only used for dmabuf imported buffer */
struct linux_dmabuf_buffer *dmabuf;
struct wl_list link;
}; };
struct gl_surface_state { struct gl_surface_state {
@ -166,6 +173,9 @@ struct gl_renderer {
int has_configless_context; int has_configless_context;
int has_dmabuf_import;
struct wl_list dmabuf_images;
struct gl_shader texture_shader_rgba; struct gl_shader texture_shader_rgba;
struct gl_shader texture_shader_rgbx; struct gl_shader texture_shader_rgbx;
struct gl_shader texture_shader_egl_external; struct gl_shader texture_shader_egl_external;
@ -212,6 +222,7 @@ egl_image_create(struct gl_renderer *gr, EGLenum target,
struct egl_image *img; struct egl_image *img;
img = zalloc(sizeof *img); img = zalloc(sizeof *img);
wl_list_init(&img->link);
img->renderer = gr; img->renderer = gr;
img->refcount = 1; img->refcount = 1;
img->image = gr->create_image(gr->egl_display, EGL_NO_CONTEXT, img->image = gr->create_image(gr->egl_display, EGL_NO_CONTEXT,
@ -244,7 +255,11 @@ egl_image_unref(struct egl_image *image)
if (image->refcount > 0) if (image->refcount > 0)
return image->refcount; return image->refcount;
if (image->dmabuf)
linux_dmabuf_buffer_set_user_data(image->dmabuf, NULL, NULL);
gr->destroy_image(gr->egl_display, image->image); gr->destroy_image(gr->egl_display, image->image);
wl_list_remove(&image->link);
free(image); free(image);
return 0; return 0;
@ -1402,6 +1417,203 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer,
gs->y_inverted = buffer->y_inverted; gs->y_inverted = buffer->y_inverted;
} }
static void
gl_renderer_destroy_dmabuf(struct linux_dmabuf_buffer *dmabuf)
{
struct egl_image *image = dmabuf->user_data;
egl_image_unref(image);
}
static struct egl_image *
import_dmabuf(struct gl_renderer *gr,
struct linux_dmabuf_buffer *dmabuf)
{
struct egl_image *image;
EGLint attribs[30];
int atti = 0;
image = linux_dmabuf_buffer_get_user_data(dmabuf);
if (image)
return egl_image_ref(image);
/* This requires the Mesa commit in
* Mesa 10.3 (08264e5dad4df448e7718e782ad9077902089a07) or
* Mesa 10.2.7 (55d28925e6109a4afd61f109e845a8a51bd17652).
* Otherwise Mesa closes the fd behind our back and re-importing
* will fail.
* https://bugs.freedesktop.org/show_bug.cgi?id=76188
*/
attribs[atti++] = EGL_WIDTH;
attribs[atti++] = dmabuf->width;
attribs[atti++] = EGL_HEIGHT;
attribs[atti++] = dmabuf->height;
attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
attribs[atti++] = dmabuf->format;
/* XXX: Add modifier here when supported */
if (dmabuf->n_planes > 0) {
attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
attribs[atti++] = dmabuf->dmabuf_fd[0];
attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
attribs[atti++] = dmabuf->offset[0];
attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
attribs[atti++] = dmabuf->stride[0];
}
if (dmabuf->n_planes > 1) {
attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT;
attribs[atti++] = dmabuf->dmabuf_fd[1];
attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
attribs[atti++] = dmabuf->offset[1];
attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
attribs[atti++] = dmabuf->stride[1];
}
if (dmabuf->n_planes > 2) {
attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT;
attribs[atti++] = dmabuf->dmabuf_fd[2];
attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
attribs[atti++] = dmabuf->offset[2];
attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
attribs[atti++] = dmabuf->stride[2];
}
attribs[atti++] = EGL_NONE;
image = egl_image_create(gr, EGL_LINUX_DMA_BUF_EXT, NULL,
attribs);
if (!image)
return NULL;
/* The cache owns one ref. The caller gets another. */
image->dmabuf = dmabuf;
wl_list_insert(&gr->dmabuf_images, &image->link);
linux_dmabuf_buffer_set_user_data(dmabuf, egl_image_ref(image),
gl_renderer_destroy_dmabuf);
return image;
}
static bool
gl_renderer_import_dmabuf(struct weston_compositor *ec,
struct linux_dmabuf_buffer *dmabuf)
{
struct gl_renderer *gr = get_renderer(ec);
struct egl_image *image;
int i;
assert(gr->has_dmabuf_import);
for (i = 0; i < dmabuf->n_planes; i++) {
/* EGL import does not have modifiers */
if (dmabuf->modifier[i] != 0)
return false;
}
/* reject all flags we do not recognize or handle */
if (dmabuf->flags & ~ZLINUX_BUFFER_PARAMS_FLAGS_Y_INVERT)
return false;
image = import_dmabuf(gr, dmabuf);
if (!image)
return false;
/* Cache retains a ref. */
egl_image_unref(image);
return true;
}
static GLenum
choose_texture_target(struct linux_dmabuf_buffer *dmabuf)
{
if (dmabuf->n_planes > 1)
return GL_TEXTURE_EXTERNAL_OES;
switch (dmabuf->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 void
gl_renderer_attach_dmabuf(struct weston_surface *surface,
struct weston_buffer *buffer,
struct linux_dmabuf_buffer *dmabuf)
{
struct gl_renderer *gr = get_renderer(surface->compositor);
struct gl_surface_state *gs = get_surface_state(surface);
int i;
if (!gr->has_dmabuf_import) {
linux_dmabuf_buffer_send_server_error(dmabuf,
"EGL dmabuf import not supported");
return;
}
buffer->width = dmabuf->width;
buffer->height = dmabuf->height;
buffer->y_inverted =
!!(dmabuf->flags & ZLINUX_BUFFER_PARAMS_FLAGS_Y_INVERT);
for (i = 0; i < gs->num_images; i++)
egl_image_unref(gs->images[i]);
gs->num_images = 0;
gs->target = choose_texture_target(dmabuf);
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
* need to re-import every time the contents may change because
* GL driver's caching may need flushing.
*
* Here we release the cache reference which has to be final.
*/
gs->images[0] = linux_dmabuf_buffer_get_user_data(dmabuf);
if (gs->images[0]) {
int ret;
ret = egl_image_unref(gs->images[0]);
assert(ret == 0);
}
gs->images[0] = import_dmabuf(gr, dmabuf);
if (!gs->images[0]) {
linux_dmabuf_buffer_send_server_error(dmabuf,
"EGL dmabuf import failed");
return;
}
gs->num_images = 1;
ensure_textures(gs, 1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(gs->target, gs->textures[0]);
gr->image_target_texture_2d(gs->target, gs->images[0]->image);
gs->pitch = buffer->width;
gs->height = buffer->height;
gs->buffer_type = BUFFER_TYPE_EGL;
gs->y_inverted = buffer->y_inverted;
}
static void static void
gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
{ {
@ -1409,6 +1621,7 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
struct gl_renderer *gr = get_renderer(ec); struct gl_renderer *gr = get_renderer(ec);
struct gl_surface_state *gs = get_surface_state(es); struct gl_surface_state *gs = get_surface_state(es);
struct wl_shm_buffer *shm_buffer; struct wl_shm_buffer *shm_buffer;
struct linux_dmabuf_buffer *dmabuf;
EGLint format; EGLint format;
int i; int i;
@ -1434,6 +1647,8 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
else if (gr->query_buffer(gr->egl_display, (void *) buffer->resource, else if (gr->query_buffer(gr->egl_display, (void *) buffer->resource,
EGL_TEXTURE_FORMAT, &format)) EGL_TEXTURE_FORMAT, &format))
gl_renderer_attach_egl(es, buffer, format); gl_renderer_attach_egl(es, buffer, format);
else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource)))
gl_renderer_attach_dmabuf(es, buffer, dmabuf);
else { else {
weston_log("unhandled buffer type!\n"); weston_log("unhandled buffer type!\n");
weston_buffer_reference(&gs->buffer_ref, NULL); weston_buffer_reference(&gs->buffer_ref, NULL);
@ -2155,6 +2370,7 @@ static void
gl_renderer_destroy(struct weston_compositor *ec) gl_renderer_destroy(struct weston_compositor *ec)
{ {
struct gl_renderer *gr = get_renderer(ec); struct gl_renderer *gr = get_renderer(ec);
struct egl_image *image, *next;
wl_signal_emit(&gr->destroy_signal, gr); wl_signal_emit(&gr->destroy_signal, gr);
@ -2166,6 +2382,14 @@ gl_renderer_destroy(struct weston_compositor *ec)
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT); EGL_NO_CONTEXT);
wl_list_for_each_safe(image, next, &gr->dmabuf_images, link) {
int ret;
ret = egl_image_unref(image);
assert(ret == 0);
}
eglTerminate(gr->egl_display); eglTerminate(gr->egl_display);
eglReleaseThread(); eglReleaseThread();
@ -2249,6 +2473,11 @@ gl_renderer_setup_egl_extensions(struct weston_compositor *ec)
gr->has_configless_context = 1; gr->has_configless_context = 1;
#endif #endif
#ifdef EGL_EXT_image_dma_buf_import
if (strstr(extensions, "EGL_EXT_image_dma_buf_import"))
gr->has_dmabuf_import = 1;
#endif
renderer_setup_egl_client_extensions(gr); renderer_setup_egl_client_extensions(gr);
return 0; return 0;
@ -2431,6 +2660,10 @@ gl_renderer_create(struct weston_compositor *ec, EGLenum platform,
if (gl_renderer_setup_egl_extensions(ec) < 0) if (gl_renderer_setup_egl_extensions(ec) < 0)
goto fail_with_error; goto fail_with_error;
wl_list_init(&gr->dmabuf_images);
if (gr->has_dmabuf_import)
gr->base.import_dmabuf = gl_renderer_import_dmabuf;
wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565); wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565);
wl_signal_init(&gr->destroy_signal); wl_signal_init(&gr->destroy_signal);

@ -100,5 +100,21 @@ typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (
#define GL_UNPACK_SKIP_PIXELS_EXT 0x0CF4 #define GL_UNPACK_SKIP_PIXELS_EXT 0x0CF4
#endif #endif
/* Define needed tokens from EGL_EXT_image_dma_buf_import extension
* here to avoid having to add ifdefs everywhere.*/
#ifndef EGL_EXT_image_dma_buf_import
#define EGL_LINUX_DMA_BUF_EXT 0x3270
#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
#define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272
#define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273
#define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274
#define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275
#define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276
#define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277
#define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278
#define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279
#define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A
#endif
#endif #endif

Loading…
Cancel
Save