wayland_shm: Add dmabuf support

This adds a separate backend to the "shm" engine that allows allocation
of buffers via libdrm that can be turned into dmabuf handles and used
with the wayland dmabuf extension.

Currently only the intel buffer manager is supported.

The benefit of dmabuf buffers is that they don't require a texture upload
like shm buffers do, and when we have plane support they can be dropped
directly into a plane without a memcpy.
This commit is contained in:
Derek Foreman 2016-04-15 12:34:07 -05:00 committed by Mike Blumenkrantz
parent baad0d50db
commit 45a662c91c
4 changed files with 482 additions and 2 deletions

View File

@ -1238,10 +1238,12 @@ modules/evas/engines/wayland_shm/Evas_Engine_Wayland_Shm.h \
modules/evas/engines/wayland_shm/evas_engine.c \
modules/evas/engines/wayland_shm/evas_engine.h \
modules/evas/engines/wayland_shm/evas_shm.c \
modules/evas/engines/wayland_shm/evas_dmabuf.c \
modules/evas/engines/wayland_shm/evas_outbuf.c
if EVAS_STATIC_BUILD_WAYLAND_SHM
lib_evas_libevas_la_SOURCES += $(WAYLAND_SHM_SOURCES)
lib_evas_libevas_la_CPPFLAGS += @evas_engine_wayland_shm_cflags@
lib_evas_libevas_la_CPPFLAGS += @evas_engine_wayland_shm_cflags@ \
-I$(top_srcdir)/src/static_libs/libdrm
lib_evas_libevas_la_LIBADD += @evas_engine_wayland_shm_libs@
else
enginewaylandshmpkgdir = $(libdir)/evas/modules/engines/wayland_shm/$(MODULE_ARCH)
@ -1257,6 +1259,7 @@ modules_evas_engines_wayland_shm_module_la_CPPFLAGS = -I$(top_builddir)/src/lib/
-I$(top_srcdir)/src/lib/evas/include \
-I$(top_srcdir)/src/lib/evas/cserve2 \
-I$(top_srcdir)/src/lib/ecore_wl2 \
-I$(top_srcdir)/src/static_libs/libdrm \
@EVAS_CFLAGS@ \
@evas_engine_wayland_shm_cflags@
modules_evas_engines_wayland_shm_module_la_LIBADD = \

View File

@ -0,0 +1,472 @@
#include "evas_common_private.h"
#include "evas_private.h"
#include "evas_engine.h"
/* When other buffer managers are supported this will become
* #ifdef HAVE_DRM_FOR_WAYLAND_SHM
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <drm_fourcc.h>
#include <intel_bufmgr.h>
#include <i915_drm.h>
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#define SYM(lib, xx) \
do { \
sym_## xx = dlsym(lib, #xx); \
if (!(sym_ ## xx)) { \
fail = 1; \
fprintf(stderr, "RESOLVE FAILED %s\n", #xx);\
} \
} while (0)
static drm_intel_bufmgr *buffer_manager;
static Eina_Bool dmabuf_totally_hosed;
typedef struct _Dmabuf_Surface Dmabuf_Surface;
typedef struct _Dmabuf_Buffer Dmabuf_Buffer;
struct _Dmabuf_Buffer
{
Dmabuf_Surface *surface;
struct wl_buffer *wl_buffer;
int w, h;
int age;
unsigned long stride;
drm_intel_bo *bo;
int fd;
int index;
Eina_Bool locked : 1;
Eina_Bool busy : 1;
Eina_Bool used : 1;
Eina_Bool pending : 1;
Eina_Bool orphaned : 1;
};
struct _Dmabuf_Surface
{
Surface *surface;
struct wl_display *wl_display;
struct zwp_linux_dmabuf_v1 *dmabuf;
struct wl_surface *wl_surface;
int compositor_version;
Dmabuf_Buffer *current;
Dmabuf_Buffer *pre;
Dmabuf_Buffer **buffer;
int nbuf;
int pending;
Eina_Bool alpha : 1;
Eina_Bool failed : 1;
};
static void _internal_evas_dmabuf_surface_destroy(Dmabuf_Surface *surface);
static void _evas_dmabuf_surface_destroy(Surface *s);
static Dmabuf_Buffer *_evas_dmabuf_buffer_init(Dmabuf_Surface *s, int w, int h);
static void _evas_dmabuf_buffer_destroy(Dmabuf_Buffer *b);
drm_intel_bufmgr *(*sym_drm_intel_bufmgr_gem_init)(int fd, int batch_size) = NULL;
int (*sym_drm_intel_gem_bo_unmap_gtt)(drm_intel_bo *bo) = NULL;
int (*sym_drm_intel_gem_bo_map_gtt)(drm_intel_bo *bo) = NULL;
drm_intel_bo *(*sym_drm_intel_bo_alloc_tiled)(drm_intel_bufmgr *mgr, const char *name, int x, int y, int cpp, uint32_t *tile, unsigned long *pitch, unsigned long flags) = NULL;
int (*sym_drm_intel_bo_gem_export_to_prime)(drm_intel_bo *bo, int *fd) = NULL;
void (*sym_drm_intel_bo_unreference)(drm_intel_bo *bo) = NULL;
static drm_intel_bufmgr *
_get_buffer_manager(void)
{
int fd;
void *drm_intel_lib;
int fail;
if (buffer_manager) return buffer_manager;
drm_intel_lib = dlopen("libdrm_intel.so", RTLD_LAZY | RTLD_GLOBAL);
if (!drm_intel_lib) goto err_dlopen;
SYM(drm_intel_lib, drm_intel_bufmgr_gem_init);
SYM(drm_intel_lib, drm_intel_gem_bo_unmap_gtt);
SYM(drm_intel_lib, drm_intel_gem_bo_map_gtt);
SYM(drm_intel_lib, drm_intel_bo_alloc_tiled);
SYM(drm_intel_lib, drm_intel_bo_gem_export_to_prime);
SYM(drm_intel_lib, drm_intel_bo_unreference);
if (fail) goto err_dlsym;
fd = open("/dev/dri/renderD128", O_RDWR);
if (fd < 0) goto err_dlsym;
buffer_manager = sym_drm_intel_bufmgr_gem_init(fd, 32);
if (!buffer_manager) goto err_bufmgr;
return buffer_manager;
err_bufmgr:
close(fd);
err_dlsym:
dlclose(drm_intel_lib);
err_dlopen:
dmabuf_totally_hosed = EINA_TRUE;
return NULL;
}
static void
buffer_release(void *data, struct wl_buffer *buffer EINA_UNUSED)
{
Dmabuf_Buffer *b = data;
if (dmabuf_totally_hosed)
{
_internal_evas_dmabuf_surface_destroy(b->surface);
return;
}
b->busy = EINA_FALSE;
if (b->orphaned) _evas_dmabuf_buffer_destroy(b);
}
static const struct wl_buffer_listener buffer_listener =
{
buffer_release
};
static void
_allocation_complete(Dmabuf_Buffer *b)
{
Surface *s;
int w, h, num_buf;
Eina_Bool recovered;
b->surface->pending--;
b->pending = EINA_FALSE;
if (!b->surface->failed) return;
/* Something went wrong, better try to fall back to a different
* buffer type...
*/
s = b->surface->surface;
w = b->w;
h = b->h;
num_buf = b->surface->nbuf;
dmabuf_totally_hosed = EINA_TRUE;
_evas_dmabuf_surface_destroy(b->surface->surface);
recovered = _evas_surface_init(s, w, h, num_buf);
if (recovered) return;
ERR("Fallback from dmabuf to shm attempted and failed.");
abort();
}
static void
_create_succeeded(void *data,
struct zwp_linux_buffer_params_v1 *params,
struct wl_buffer *new_buffer)
{
Dmabuf_Buffer *b = data;
Eina_Bool failed;
b->wl_buffer = new_buffer;
wl_buffer_add_listener(b->wl_buffer, &buffer_listener, b);
zwp_linux_buffer_params_v1_destroy(params);
if (b->orphaned)
{
_evas_dmabuf_buffer_destroy(b);
_allocation_complete(b);
return;
}
if (!b->busy) return;
if (b != b->surface->pre) return;
failed = b->surface->failed;
_allocation_complete(b);
if (failed) return;
/* This buffer was drawn into before it had a handle */
wl_surface_attach(b->surface->wl_surface, b->wl_buffer, 0, 0);
_evas_surface_damage(b->surface->wl_surface, b->surface->compositor_version,
b->w, b->h, NULL, 0);
wl_surface_commit(b->surface->wl_surface);
b->surface->pre = NULL;
b->busy = EINA_FALSE;
}
static void
_create_failed(void *data, struct zwp_linux_buffer_params_v1 *params)
{
Dmabuf_Buffer *b = data;
zwp_linux_buffer_params_v1_destroy(params);
b->surface->failed = EINA_TRUE;
if (b->orphaned) _evas_dmabuf_buffer_destroy(b);
_allocation_complete(b);
}
static const struct zwp_linux_buffer_params_v1_listener params_listener =
{
_create_succeeded,
_create_failed
};
static void
_evas_dmabuf_buffer_unlock(Dmabuf_Buffer *b)
{
sym_drm_intel_gem_bo_unmap_gtt(b->bo);
b->locked = EINA_FALSE;
}
static void
_evas_dmabuf_buffer_destroy(Dmabuf_Buffer *b)
{
if (b->busy || b->pending)
{
b->orphaned = EINA_TRUE;
return;
}
if (b->locked) _evas_dmabuf_buffer_unlock(b);
sym_drm_intel_bo_unreference(b->bo);
if (b->wl_buffer) wl_buffer_destroy(b->wl_buffer);
b->wl_buffer = NULL;
free(b);
}
static void
_evas_dmabuf_surface_reconfigure(Surface *s, int w, int h, uint32_t flags EINA_UNUSED)
{
Dmabuf_Surface *surface;
int i;
surface = s->surf.dmabuf;
for (i = 0; i < surface->nbuf; i++)
{
Dmabuf_Buffer *b = surface->buffer[i];
int stride = b->stride;
/* If stride is a little bigger than width we still fit */
if ((w >= b->w) && (w <= stride / 4) && (h == b->h))
{
b->w = w;
continue;
}
_evas_dmabuf_buffer_destroy(b);
surface->buffer[i] = _evas_dmabuf_buffer_init(surface, w, h);
}
}
static void *
_evas_dmabuf_surface_data_get(Surface *s, int *w, int *h)
{
Dmabuf_Surface *surface;
Dmabuf_Buffer *b;
surface = s->surf.dmabuf;
b = surface->current;
if (!b) return NULL;
/* We return stride/bpp because it may not match the allocated
* width. evas will figure out the clipping
*/
if (w) *w = b->stride / 4;
if (h) *h = b->h;
if (b->locked) return b->bo->virtual;
if (sym_drm_intel_gem_bo_map_gtt(b->bo) != 0)
return NULL;
b->locked = EINA_TRUE;
return b->bo->virtual;
}
static Dmabuf_Buffer *
_evas_dmabuf_surface_wait(Dmabuf_Surface *s)
{
int iterations = 0, i;
while (iterations++ < 10)
{
for (i = 0; i < s->nbuf; i++)
if (!s->buffer[i]->locked &&
!s->buffer[i]->busy &&
!s->buffer[i]->pending)
return s->buffer[i];
wl_display_dispatch_pending(s->wl_display);
}
/* May be we have a possible render target that just hasn't been
* given a wl_buffer yet - draw there and let the success handler
* figure it out.
*/
for (i = 0; i < s->nbuf; i++)
if (!s->buffer[i]->locked && !s->buffer[i]->busy)
return s->buffer[i];
return NULL;
}
static int
_evas_dmabuf_surface_assign(Surface *s)
{
Dmabuf_Surface *surface;
int i;
surface = s->surf.dmabuf;
surface->current = _evas_dmabuf_surface_wait(surface);
if (!surface->current)
{
WRN("No free DMAbuf buffers, dropping a frame");
for (i = 0; i < surface->nbuf; i++)
surface->buffer[i]->age = 0;
return 0;
}
for (i = 0; i < surface->nbuf; i++)
if (surface->buffer[i]->used) surface->buffer[i]->age++;
return surface->current->age;
}
static void
_evas_dmabuf_surface_post(Surface *s, Eina_Rectangle *rects, unsigned int count)
{
Dmabuf_Surface *surface;
Dmabuf_Buffer *b;
surface = s->surf.dmabuf;
b = surface->current;
if (!b) return;
_evas_dmabuf_buffer_unlock(b);
surface->current = NULL;
b->busy = EINA_TRUE;
b->used = EINA_TRUE;
b->age = 0;
/* If we don't yet have a buffer assignement we need to track the
* most recently filled unassigned buffer and make sure it gets
* displayed.
*/
if (!b->wl_buffer)
{
surface->pre = b;
return;
}
surface->pre = NULL;
wl_surface_attach(surface->wl_surface, b->wl_buffer, 0, 0);
_evas_surface_damage(surface->wl_surface, surface->compositor_version,
b->w, b->h, rects, count);
wl_surface_commit(surface->wl_surface);
}
static Dmabuf_Buffer *
_evas_dmabuf_buffer_init(Dmabuf_Surface *s, int w, int h)
{
Dmabuf_Buffer *out;
struct zwp_linux_buffer_params_v1 *dp;
drm_intel_bufmgr *mgr = _get_buffer_manager();
uint32_t tile = I915_TILING_NONE;
uint32_t flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
if (!mgr) return NULL;
out = calloc(1, sizeof(Dmabuf_Buffer));
if (!out) return NULL;
out->surface = s;
out->bo = sym_drm_intel_bo_alloc_tiled(mgr, "name", w, h, 4, &tile,
&out->stride, 0);
out->w = w;
out->h = h;
if (tile != I915_TILING_NONE) goto err;
if (sym_drm_intel_bo_gem_export_to_prime(out->bo, &out->fd) != 0) goto err;
out->pending = EINA_TRUE;
dp = zwp_linux_dmabuf_v1_create_params(out->surface->dmabuf);
zwp_linux_buffer_params_v1_add(dp, out->fd, 0, 0, out->stride, 0, 0);
zwp_linux_buffer_params_v1_add_listener(dp, &params_listener, out);
zwp_linux_buffer_params_v1_create(dp, out->w, out->h,
DRM_FORMAT_ARGB8888, flags);
s->pending++;
return out;
err:
_evas_dmabuf_buffer_destroy(out);
return NULL;
}
static void
_internal_evas_dmabuf_surface_destroy(Dmabuf_Surface *surface)
{
int i;
for (i = 0; i < surface->nbuf; i++)
_evas_dmabuf_buffer_destroy(surface->buffer[i]);
free(surface->buffer);
free(surface);
}
static void
_evas_dmabuf_surface_destroy(Surface *s)
{
if (!s) return;
_internal_evas_dmabuf_surface_destroy(s->surf.dmabuf);
}
Eina_Bool
_evas_dmabuf_surface_create(Surface *s, int w, int h, int num_buff)
{
Dmabuf_Surface *surf;
int i = 0;
if (dmabuf_totally_hosed) return EINA_FALSE;
if (!s->info->info.wl_dmabuf) return EINA_FALSE;
if (!_get_buffer_manager()) return EINA_FALSE;
if (!(s->surf.dmabuf = calloc(1, sizeof(Dmabuf_Surface)))) goto err;
surf = s->surf.dmabuf;
surf->surface = s;
surf->wl_display = s->info->info.wl_disp;
surf->dmabuf = s->info->info.wl_dmabuf;
surf->wl_surface = s->info->info.wl_surface;
surf->alpha = s->info->info.destination_alpha;
surf->compositor_version = s->info->info.compositor_version;
/* create surface buffers */
surf->nbuf = num_buff;
surf->buffer = calloc(surf->nbuf, sizeof(Dmabuf_Buffer *));
if (!surf->buffer) goto err;
for (i = 0; i < num_buff; i++)
{
surf->buffer[i] = _evas_dmabuf_buffer_init(surf, w, h);
if (!surf->buffer[i])
{
ERR("Could not create buffers");
goto err;
}
}
s->type = SURFACE_DMABUF;
s->funcs.destroy = _evas_dmabuf_surface_destroy;
s->funcs.reconfigure = _evas_dmabuf_surface_reconfigure;
s->funcs.data_get = _evas_dmabuf_surface_data_get;
s->funcs.assign = _evas_dmabuf_surface_assign;
s->funcs.post = _evas_dmabuf_surface_post;
return EINA_TRUE;
err:
_evas_dmabuf_surface_destroy(s);
return EINA_FALSE;
}

View File

@ -73,11 +73,13 @@ extern int _evas_engine_way_shm_log_dom;
# define MAX_BUFFERS 4
typedef struct _Shm_Surface Shm_Surface;
typedef struct _Dmabuf_Surface Dmabuf_Surface;
typedef enum _Surface_Type Surface_Type;
enum _Surface_Type {
SURFACE_EMPTY,
SURFACE_SHM
SURFACE_SHM,
SURFACE_DMABUF
};
typedef struct _Surface Surface;
@ -86,6 +88,7 @@ struct _Surface
Surface_Type type;
union {
Shm_Surface *shm;
Dmabuf_Surface *dmabuf;
} surf;
Evas_Engine_Info_Wayland_Shm *info;
struct
@ -127,6 +130,7 @@ struct _Outbuf
} priv;
};
Eina_Bool _evas_dmabuf_surface_create(Surface *s, int w, int h, int num_buff);
Eina_Bool _evas_shm_surface_create(Surface *s, int w, int h, int num_buff);
Outbuf *_evas_outbuf_setup(int w, int h, Evas_Engine_Info_Wayland_Shm *info);

View File

@ -12,6 +12,7 @@
Eina_Bool
_evas_surface_init(Surface *s, int w, int h, int num_buf)
{
if (_evas_dmabuf_surface_create(s, w, h, num_buf)) return EINA_TRUE;
if (_evas_shm_surface_create(s, w, h, num_buf)) return EINA_TRUE;
return EINA_FALSE;