summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerek Foreman <derekf@osg.samsung.com>2016-04-15 12:34:07 -0500
committerMike Blumenkrantz <zmike@osg.samsung.com>2016-04-19 15:11:10 -0400
commit45a662c91c524575bdf24a7035b5e1f091f2cd46 (patch)
tree05f3f4270afae160c48eae9e8706471b24dcd737
parentbaad0d50dbb64791984d2328a85a7592b24ea295 (diff)
downloadefl-45a662c91c524575bdf24a7035b5e1f091f2cd46.tar.gz
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.
-rw-r--r--src/Makefile_Evas.am5
-rw-r--r--src/modules/evas/engines/wayland_shm/evas_dmabuf.c472
-rw-r--r--src/modules/evas/engines/wayland_shm/evas_engine.h6
-rw-r--r--src/modules/evas/engines/wayland_shm/evas_outbuf.c1
4 files changed, 482 insertions, 2 deletions
diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am
index 8ad756717d..c7814a238f 100644
--- a/src/Makefile_Evas.am
+++ b/src/Makefile_Evas.am
@@ -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 = \
diff --git a/src/modules/evas/engines/wayland_shm/evas_dmabuf.c b/src/modules/evas/engines/wayland_shm/evas_dmabuf.c
new file mode 100644
index 0000000000..7a7c3b8245
--- /dev/null
+++ b/src/modules/evas/engines/wayland_shm/evas_dmabuf.c
@@ -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;
+}
diff --git a/src/modules/evas/engines/wayland_shm/evas_engine.h b/src/modules/evas/engines/wayland_shm/evas_engine.h
index f76cf3ca5a..7436c6bf83 100644
--- a/src/modules/evas/engines/wayland_shm/evas_engine.h
+++ b/src/modules/evas/engines/wayland_shm/evas_engine.h
@@ -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);
diff --git a/src/modules/evas/engines/wayland_shm/evas_outbuf.c b/src/modules/evas/engines/wayland_shm/evas_outbuf.c
index 1ba9a7dc37..f66ca2ac82 100644
--- a/src/modules/evas/engines/wayland_shm/evas_outbuf.c
+++ b/src/modules/evas/engines/wayland_shm/evas_outbuf.c
@@ -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;