evas/software_x11: implement tbm native surface type
authorDongyeon Kim <dy5.kim@samsung.com>
Tue, 3 Feb 2015 11:36:41 +0000 (20:36 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Tue, 10 Feb 2015 05:48:53 +0000 (14:48 +0900)
Summary:
This native surface type is based on the tbm surface used for the tizen platform.
For the software_x11 backend, image data is retrieved from tbm surface
and color format converted appropriately.
This will only work when libtbm.so is present in the system.
@feature

Test Plan: Local tests

Reviewers: raster, cedric, jpeg, Hermet

Subscribers: wonsik, cedric

Signed-off-by: Jean-Philippe Andre <jp.andre@samsung.com>
src/Makefile_Evas.am
src/lib/evas/include/evas_common_private.h
src/modules/evas/engines/software_generic/evas_engine.c
src/modules/evas/engines/software_x11/evas_engine.c
src/modules/evas/engines/software_x11/evas_engine.h
src/modules/evas/engines/software_x11/evas_native_tbm.c [new file with mode: 0644]

index 9e8d040..0651d7f 100644 (file)
@@ -980,7 +980,8 @@ SOFTWARE_X11_SOURCES = \
 modules/evas/engines/software_x11/evas_engine.c \
 modules/evas/engines/software_x11/evas_engine.h \
 modules/evas/engines/software_x11/evas_x_egl.c \
-modules/evas/engines/software_x11/evas_x_egl.h
+modules/evas/engines/software_x11/evas_x_egl.h \
+modules/evas/engines/software_x11/evas_native_tbm.c
 SOFTWARE_X11_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
 -I$(top_srcdir)/src/lib/evas/include \
 -I$(top_srcdir)/src/lib/evas/cserve2 \
index d41f24b..56dc039 100644 (file)
@@ -839,6 +839,7 @@ struct _RGBA_Image
       void   *data; //Evas_Native_Surface ns;
       struct {
         void (*bind) (void *data, void *image, int x, int y, int w, int h);
+        void (*unbind) (void *data, void *image);
         void (*free) (void *data, void *image);
         void *data;
       } func;
index 3c13f80..a3b15b4 100644 (file)
@@ -1508,6 +1508,8 @@ eng_image_draw(void *data EINA_UNUSED, void *context, void *surface, void *image
         evas_common_cpu_end_opt();
      }
 
+   if (im->native.func.unbind)
+      im->native.func.unbind(data, image);
    return EINA_FALSE;
 }
 
index fa74a38..acc4412 100644 (file)
@@ -630,28 +630,31 @@ eng_image_native_set(void *data EINA_UNUSED, void *image, void *native)
 
    if (!im || !ns) return im;
 
-   if (ns)
+   if (ns->type == EVAS_NATIVE_SURFACE_X11)
      {
-        if (ns->type == EVAS_NATIVE_SURFACE_X11)
+        if (im->native.data)
           {
-             if (im->native.data)
-               {
-                  //image have native surface already
-                  Evas_Native_Surface *ens = im->native.data;
-
-                  if ((ens->type == ns->type) &&
-                      (ens->data.x11.visual == ns->data.x11.visual) &&
-                      (ens->data.x11.pixmap == ns->data.x11.pixmap))
-                     return im;
-                }
-           }
-      }
-    else
-      {
-         return im;
-      }
-
-    if ((!ns) && (!im->native.data)) return im;
+             //image have native surface already
+             Evas_Native_Surface *ens = im->native.data;
+
+             if ((ens->type == ns->type) &&
+                 (ens->data.x11.visual == ns->data.x11.visual) &&
+                 (ens->data.x11.pixmap == ns->data.x11.pixmap))
+               return im;
+          }
+     }
+   else if (ns->type == EVAS_NATIVE_SURFACE_TBM)
+     {
+        if (im->native.data)
+          {
+             //image have native surface already
+             Evas_Native_Surface *ens = im->native.data;
+
+             if ((ens->type == ns->type) &&
+                 (ens->data.tbm.buffer == ns->data.tbm.buffer))
+               return im;
+          }
+     }
 
    //create new im and clean already existed im even though ns = NULL
    im2 = (RGBA_Image *)evas_cache_image_data(evas_common_image_cache_get(),
@@ -664,21 +667,23 @@ eng_image_native_set(void *data EINA_UNUSED, void *image, void *native)
      }
 
 #ifdef EVAS_CSERVE2
-      if (evas_cserve2_use_get() && evas_cache2_image_cached(&im->cache_entry))
-        evas_cache2_image_close(&im->cache_entry);
-      else
+   if (evas_cserve2_use_get() && evas_cache2_image_cached(&im->cache_entry))
+     evas_cache2_image_close(&im->cache_entry);
+   else
 #endif
-      evas_cache_image_drop(&im->cache_entry);
+     evas_cache_image_drop(&im->cache_entry);
    im = im2;
 
-   if (!ns) return im;
-
 #ifdef BUILD_ENGINE_SOFTWARE_XLIB
    if (ns->type == EVAS_NATIVE_SURFACE_X11)
      {
         return evas_xlib_image_native_set(re->generic.ob, im, ns);
      }
 #endif
+   if (ns->type == EVAS_NATIVE_SURFACE_TBM)
+     {
+        return evas_native_tbm_image_set(re->generic.ob, im, ns);
+     }
 
    return im;
 }
@@ -693,8 +698,9 @@ eng_image_native_get(void *data EINA_UNUSED, void *image)
    n = im->native.data;
    if (!n) return NULL;
    return &(n->ns);
-#endif
+#else
    return NULL;
+#endif
 }
 
 
index c29f62d..b561e2f 100644 (file)
@@ -116,5 +116,6 @@ struct _Outbuf
 
 void evas_software_xlib_x_init(void);
 void evas_software_xcb_init(void);
+void *evas_native_tbm_image_set(void *data, void *image, void *native);
 
 #endif
diff --git a/src/modules/evas/engines/software_x11/evas_native_tbm.c b/src/modules/evas/engines/software_x11/evas_native_tbm.c
new file mode 100644 (file)
index 0000000..ec7d037
--- /dev/null
@@ -0,0 +1,341 @@
+#include "evas_common_private.h"
+#include "evas_xlib_image.h"
+#include "evas_private.h"
+
+#include "Evas_Engine_Software_X11.h"
+#include "evas_engine.h"
+
+#ifdef HAVE_DLSYM
+# include <dlfcn.h>      /* dlopen,dlclose,etc */
+#else
+# warning native_tbm should not get compiled if dlsym is not found on the system!
+#endif
+
+#define EVAS_ROUND_UP_4(num) (((num)+3) & ~3)
+#define EVAS_ROUND_UP_8(num) (((num)+7) & ~7)
+
+#define TBM_SURF_PLANE_MAX 4 /**< maximum number of the planes  */
+
+/* option to map the tbm_surface */
+#define TBM_SURF_OPTION_READ      (1 << 0) /**< access option to read  */
+#define TBM_SURF_OPTION_WRITE     (1 << 1) /**< access option to write */
+
+#define __tbm_fourcc_code(a,b,c,d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
+                             ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
+
+#define TBM_FORMAT_RGBX8888    __tbm_fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */
+#define TBM_FORMAT_RGBA8888    __tbm_fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
+#define TBM_FORMAT_BGRA8888    __tbm_fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */
+#define TBM_FORMAT_NV12                __tbm_fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */
+#define TBM_FORMAT_YUV420      __tbm_fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */
+#define TBM_FORMAT_YVU420      __tbm_fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */
+
+static void *tbm_lib = NULL;
+static int   tbm_ref = 0;
+
+typedef struct _tbm_surface * tbm_surface_h;
+typedef uint32_t tbm_format;
+
+typedef struct _tbm_surface_plane
+{
+    unsigned char *ptr;   /**< Plane pointer */
+    uint32_t size;        /**< Plane size */
+    uint32_t offset;      /**< Plane offset */
+    uint32_t stride;      /**< Plane stride */
+
+    void *reserved1;      /**< Reserved pointer1 */
+    void *reserved2;      /**< Reserved pointer2 */
+    void *reserved3;      /**< Reserved pointer3 */
+} tbm_surface_plane_s;
+
+typedef struct _tbm_surface_info
+{
+    uint32_t width;      /**< TBM surface width */
+    uint32_t height;     /**< TBM surface height */
+    tbm_format format;   /**< TBM surface format*/
+    uint32_t bpp;        /**< TBM surface bbp */
+    uint32_t size;       /**< TBM surface size */
+
+    uint32_t num_planes;                            /**< The number of planes */
+    tbm_surface_plane_s planes[TBM_SURF_PLANE_MAX]; /**< Array of planes */
+
+    void *reserved4;   /**< Reserved pointer4 */
+    void *reserved5;   /**< Reserved pointer5 */
+    void *reserved6;   /**< Reserved pointer6 */
+} tbm_surface_info_s;
+
+
+/* returns 0 on success */
+static int (*sym_tbm_surface_map) (tbm_surface_h surface, int opt, tbm_surface_info_s *info) = NULL;
+static int (*sym_tbm_surface_unmap) (tbm_surface_h surface) = NULL;
+
+static Eina_Bool
+tbm_init(void)
+{
+   if (tbm_lib)
+     {
+        tbm_ref++;
+        return EINA_TRUE;
+     }
+
+   const char *tbm_libs[] =
+   {
+      "libtbm.so.1",
+      "libtbm.so.0",
+      NULL,
+   };
+   int i, fail;
+#define SYM(lib, xx)                            \
+  do {                                          \
+       sym_ ## xx = dlsym(lib, #xx);            \
+       if (!(sym_ ## xx)) {                     \
+            ERR("%s", dlerror());               \
+            fail = 1;                           \
+         }                                      \
+    } while (0)
+
+   for (i = 0; tbm_libs[i]; i++)
+     {
+        tbm_lib = dlopen(tbm_libs[i], RTLD_LOCAL | RTLD_LAZY);
+        if (tbm_lib)
+          {
+             fail = 0;
+             SYM(tbm_lib, tbm_surface_map);
+             SYM(tbm_lib, tbm_surface_unmap);
+             if (fail)
+               {
+                  dlclose(tbm_lib);
+                  tbm_lib = NULL;
+               }
+             else break;
+          }
+     }
+   if (!tbm_lib) return EINA_FALSE;
+
+   tbm_ref++;
+   return EINA_TRUE;
+}
+
+static void
+tbm_shutdown(void)
+{
+   if (tbm_ref > 0)
+     {
+        tbm_ref--;
+
+        if (tbm_ref == 0)
+          {
+             if (tbm_lib)
+               {
+                  dlclose(tbm_lib);
+                  tbm_lib = NULL;
+               }
+          }
+     }
+}
+
+static void
+_evas_video_yv12(unsigned char *evas_data, const unsigned char *source_data, unsigned int w, unsigned int h, unsigned int output_height)
+{
+   const unsigned char **rows;
+   unsigned int i, j;
+   unsigned int rh;
+   unsigned int stride_y, stride_uv;
+
+   rh = output_height;
+
+   rows = (const unsigned char **)evas_data;
+
+   stride_y = EVAS_ROUND_UP_4(w);
+   stride_uv = EVAS_ROUND_UP_8(w) / 2;
+
+   for (i = 0; i < rh; i++)
+     rows[i] = &source_data[i * stride_y];
+
+   for (j = 0; j < (rh / 2); j++, i++)
+     rows[i] = &source_data[h * stride_y +
+                            (rh / 2) * stride_uv +
+                            j * stride_uv];
+
+   for (j = 0; j < (rh / 2); j++, i++)
+     rows[i] = &source_data[h * stride_y + j * stride_uv];
+}
+
+static void
+_evas_video_i420(unsigned char *evas_data, const unsigned char *source_data, unsigned int w, unsigned int h, unsigned int output_height)
+{
+   const unsigned char **rows;
+   unsigned int i, j;
+   unsigned int rh;
+   unsigned int stride_y, stride_uv;
+
+   rh = output_height;
+
+   rows = (const unsigned char **)evas_data;
+
+   stride_y = EVAS_ROUND_UP_4(w);
+   stride_uv = EVAS_ROUND_UP_8(w) / 2;
+
+   for (i = 0; i < rh; i++)
+     rows[i] = &source_data[i * stride_y];
+
+   for (j = 0; j < (rh / 2); j++, i++)
+     rows[i] = &source_data[h * stride_y + j * stride_uv];
+
+   for (j = 0; j < (rh / 2); j++, i++)
+     rows[i] = &source_data[h * stride_y +
+                            (rh / 2) * stride_uv +
+                            j * stride_uv];
+}
+
+static void
+_evas_video_nv12(unsigned char *evas_data, const unsigned char *source_data, unsigned int w, unsigned int h EINA_UNUSED, unsigned int output_height)
+{
+   const unsigned char **rows;
+   unsigned int i, j;
+   unsigned int rh;
+
+   rh = output_height;
+
+   rows = (const unsigned char **)evas_data;
+
+   for (i = 0; i < rh; i++)
+     rows[i] = &source_data[i * w];
+
+   for (j = 0; j < (rh / 2); j++, i++)
+     rows[i] = &source_data[rh * w + j * w];
+}
+
+static void
+_native_bind_cb(void *data EINA_UNUSED, void *image, int x EINA_UNUSED, int y EINA_UNUSED, int w EINA_UNUSED, int h EINA_UNUSED)
+{
+   RGBA_Image *im = image;
+   Native *n = im->native.data;
+
+   if (!im) return;
+   if ((n) && (n->ns.type == EVAS_NATIVE_SURFACE_TBM))
+     {
+        tbm_surface_info_s info;
+
+        if (sym_tbm_surface_map(n->ns.data.tbm.buffer, TBM_SURF_OPTION_READ|TBM_SURF_OPTION_WRITE, &info)) return;
+
+        im->image.data = (DATA32 *)info.planes[0].ptr;
+     }
+}
+
+static void
+_native_unbind_cb(void *data EINA_UNUSED, void *image)
+{
+   RGBA_Image *im = image;
+   Native *n = im->native.data;
+
+   if (!im) return;
+   if ((n) && (n->ns.type == EVAS_NATIVE_SURFACE_TBM))
+     {
+        sym_tbm_surface_unmap(n->ns.data.tbm.buffer);
+     }
+}
+
+static void
+_native_free_cb(void *data EINA_UNUSED, void *image)
+{
+   RGBA_Image *im = image;
+   Native *n = im->native.data;
+
+   if (!im) return;
+   im->native.data        = NULL;
+   im->native.func.bind   = NULL;
+   im->native.func.unbind = NULL;
+   im->native.func.free   = NULL;
+   im->native.func.data   = NULL;
+   im->image.data         = NULL;
+
+   free(n);
+
+   tbm_shutdown();
+}
+
+void *
+evas_native_tbm_image_set(void *data EINA_UNUSED, void *image, void *native)
+{
+   Evas_Native_Surface *ns = native;
+   RGBA_Image *im = image;
+
+   if (!im) return NULL;
+   if ((ns) && (ns->type == EVAS_NATIVE_SURFACE_TBM))
+     {
+        void *pixels_data;
+        int w, h, stride;
+        tbm_format format;
+        tbm_surface_info_s info;
+        Native *n;
+
+        if (!tbm_init())
+          {
+             ERR("Could not initialize TBM!");
+             return NULL;
+          }
+
+        n = calloc(1, sizeof(Native));
+        if (!n) return NULL;
+
+        if (sym_tbm_surface_map(ns->data.tbm.buffer, TBM_SURF_OPTION_READ|TBM_SURF_OPTION_WRITE, &info))
+          {
+             free(n);
+             return im;
+          }
+
+        w = info.width;
+        h = info.height;
+        stride = info.planes[0].stride;
+        format = info.format;
+        pixels_data = info.planes[0].ptr;
+        im->cache_entry.w = stride;
+        im->cache_entry.h = h;
+
+        // Handle all possible format here :"(
+        switch (format)
+          {
+           case TBM_FORMAT_RGBA8888:
+           case TBM_FORMAT_RGBX8888:
+           case TBM_FORMAT_BGRA8888:
+              im->cache_entry.w = stride / 4;
+              evas_cache_image_colorspace(&im->cache_entry, EVAS_COLORSPACE_ARGB8888);
+              im->cache_entry.flags.alpha = (format == TBM_FORMAT_RGBX8888 ? 0 : 1);
+              im->image.data = pixels_data;
+              im->image.no_free = 1;
+              break;
+              /* borrowing code from emotion here */
+           case TBM_FORMAT_YVU420: /* EVAS_COLORSPACE_YCBCR422P601_PL */
+              evas_cache_image_colorspace(&im->cache_entry, EVAS_COLORSPACE_YCBCR422P601_PL);
+              _evas_video_yv12(im->cs.data, pixels_data, w, h, h);
+              evas_common_image_colorspace_dirty(im);
+              break;
+           case TBM_FORMAT_YUV420: /* EVAS_COLORSPACE_YCBCR422P601_PL */
+              evas_cache_image_colorspace(&im->cache_entry, EVAS_COLORSPACE_YCBCR422P601_PL);
+              _evas_video_i420(im->cs.data, pixels_data, w, h, h);
+              evas_common_image_colorspace_dirty(im);
+              break;
+           case TBM_FORMAT_NV12: /* EVAS_COLORSPACE_YCBCR420NV12601_PL */
+              evas_cache_image_colorspace(&im->cache_entry, EVAS_COLORSPACE_YCBCR420NV12601_PL);
+              _evas_video_nv12(im->cs.data, pixels_data, w, h, h);
+              evas_common_image_colorspace_dirty(im);
+              break;
+              /* Not planning to handle those in software */
+           default:
+              sym_tbm_surface_unmap(ns->data.tbm.buffer);
+              free(n);
+              return im;
+          }
+
+        memcpy(n, ns, sizeof(Evas_Native_Surface));
+        im->native.data = n;
+        im->native.func.bind   = _native_bind_cb;
+        im->native.func.unbind = _native_unbind_cb;
+        im->native.func.free   = _native_free_cb;
+
+        sym_tbm_surface_unmap(ns->data.tbm.buffer);
+     }
+   return im;
+}
+