discomfitor pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=45a662c91c524575bdf24a7035b5e1f091f2cd46

commit 45a662c91c524575bdf24a7035b5e1f091f2cd46
Author: Derek Foreman <der...@osg.samsung.com>
Date:   Fri Apr 15 12:34:07 2016 -0500

    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.
---
 src/Makefile_Evas.am                               |   5 +-
 src/modules/evas/engines/wayland_shm/evas_dmabuf.c | 472 +++++++++++++++++++++
 src/modules/evas/engines/wayland_shm/evas_engine.h |   6 +-
 src/modules/evas/engines/wayland_shm/evas_outbuf.c |   1 +
 4 files changed, 482 insertions(+), 2 deletions(-)

diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am
index 8ad7567..c7814a2 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 0000000..7a7c3b8
--- /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 f76cf3c..7436c6b 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 1ba9a7d..f66ca2a 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;

-- 


Reply via email to