evas: add ETC1 texture format support to Evas.
authorCedric BAIL <cedric.bail@samsung.com>
Thu, 27 Mar 2014 04:12:12 +0000 (13:12 +0900)
committerCedric BAIL <cedric.bail@free.fr>
Tue, 1 Apr 2014 13:00:54 +0000 (22:00 +0900)
src/modules/evas/engines/gl_common/evas_gl_common.h
src/modules/evas/engines/gl_common/evas_gl_context.c
src/modules/evas/engines/gl_common/evas_gl_image.c
src/modules/evas/engines/gl_common/evas_gl_texture.c

index e5b4632..f35d9c9 100644 (file)
 #ifndef GL_LUMINANCE16_ALPHA16
 # define GL_LUMINANCE16_ALPHA16 0x8048
 #endif
+#ifndef GL_ETC1_RGB8_OES
+# define GL_ETC1_RGB8_OES 0x8D64
+#endif
+
 
 #ifndef GL_UNPACK_ROW_LENGTH
 # define GL_UNPACK_ROW_LENGTH 0x0cf2
@@ -358,6 +362,7 @@ struct _Evas_GL_Shared
       Eina_Bool sec_image_map : 1;
       Eina_Bool bin_program : 1;
       Eina_Bool unpack_row_length : 1;
+      Eina_Bool etc1 : 1;
       // tuning params - per gpu/cpu combo?
 #define MAX_CUTOUT             512
 #define DEF_CUTOUT                  512
@@ -839,7 +844,14 @@ extern void       (*glsym_glProgramParameteri)    (GLuint a, GLuint b, GLint d);
 extern void       (*glsym_glReleaseShaderCompiler)(void);
 extern void      *(*glsym_glMapBuffer)            (GLenum a, GLenum b);
 extern GLboolean  (*glsym_glUnmapBuffer)          (GLenum a);
-
+extern void       (*glsym_glCompressedTexImage2d) (GLenum target,
+                                                   GLint level,
+                                                   GLenum internalformat,
+                                                   GLsizei width,
+                                                   GLsizei height,
+                                                   GLint border,
+                                                   GLsizei imageSize,
+                                                   const GLvoid * data);
 #ifdef GL_GLES
 extern void          *(*secsym_eglCreateImage)               (void *a, void *b, GLenum c, void *d, const int *e);
 extern unsigned int   (*secsym_eglDestroyImage)              (void *a, void *b);
index cb02c6e..7bee00c 100644 (file)
@@ -30,6 +30,10 @@ GLboolean  (*glsym_glUnmapBuffer)          (GLenum a) = NULL;
 void       (*glsym_glStartTiling)          (GLuint a, GLuint b, GLuint c, GLuint d, GLuint e) = NULL;
 void       (*glsym_glEndTiling)            (GLuint a) = NULL;
 
+void       (*glsym_glCompressedTexImage2d) (GLenum target, GLint level, GLenum internalformat,
+                                            GLsizei width, GLsizei height, GLint border, GLsizei imageSize,
+                                            const GLvoid * data) = NULL;
+
 #ifdef GL_GLES
 // just used for finding symbols :)
 typedef void (*_eng_fn) (void);
@@ -211,6 +215,7 @@ gl_symbols(void)
 
    FINDSYM(secsym_eglGetImageAttribSEC, "eglGetImageAttribSEC", secsym_func_uint);
 #endif
+   FINDSYM(glsym_glCompressedTexImage2d, "glCompressedTexImage2D", glsym_func_void);
 }
 
 static void shader_array_flush(Evas_Engine_GL_Context *gc);
@@ -623,6 +628,8 @@ evas_gl_common_context_new(void)
                  (strstr((char *)ext, "GL_EXT_texture_format_BGRA8888")))
                shared->info.bgra = 1;
 #endif
+             if (glsym_glCompressedTexImage2d && strstr((char *)ext, "OES_compressed_ETC1_RGB8_texture"))
+               shared->info.etc1 = 1;
 #ifdef GL_GLES
              // FIXME: there should be an extension name/string to check for
              // not just symbols in the lib
index a89d79d..a74f45a 100644 (file)
@@ -128,6 +128,13 @@ static const Evas_Colorspace known_cspace[] = {
    EVAS_COLORSPACE_ARGB8888
 };
 
+static const Evas_Colorspace known_etc1_cspace[] = {
+   EVAS_COLORSPACE_ETC1,
+   EVAS_COLORSPACE_GRY8,
+   EVAS_COLORSPACE_AGRY88,
+   EVAS_COLORSPACE_ARGB8888
+};
+
 static Evas_GL_Image *
 _evas_gl_common_image(Evas_Engine_GL_Context *gc, RGBA_Image *im_im, Evas_Image_Load_Opts *lo, int *error)
 {
@@ -177,19 +184,25 @@ _evas_gl_common_image(Evas_Engine_GL_Context *gc, RGBA_Image *im_im, Evas_Image_
      }
    if (im_im->cache_entry.cspaces)
      {
+        const Evas_Colorspace *cspaces;
         unsigned int i;
 
+        if (gc->shared->info.etc1)
+          cspaces = known_etc1_cspace;
+        else
+          cspaces = known_cspace;
+
         for (i = 0; im_im->cache_entry.cspaces[i] != EVAS_COLORSPACE_ARGB8888; i++)
           {
              unsigned int j;
 
              for (j = 0;
-                  known_cspace[j] != EVAS_COLORSPACE_ARGB8888;
+                  cspaces[j] != EVAS_COLORSPACE_ARGB8888;
                   j++)
-               if (known_cspace[j] == im_im->cache_entry.cspaces[i])
+               if (cspaces[j] == im_im->cache_entry.cspaces[i])
                  break;
 
-             if (known_cspace[j] == im_im->cache_entry.cspaces[i])
+             if (cspaces[j] == im_im->cache_entry.cspaces[i])
                break;
           }
 
@@ -318,6 +331,10 @@ evas_gl_common_image_new_from_data(Evas_Engine_GL_Context *gc, unsigned int w, u
       case EVAS_COLORSPACE_GRY8:
       case EVAS_COLORSPACE_AGRY88:
         break;
+      case EVAS_COLORSPACE_ETC1:
+        if (gc->shared->info.etc1) break;
+        ERR("We don't know what to do with ETC1 on this hardware. You need to add a software converter here.");
+        break;
       case EVAS_COLORSPACE_YCBCR422P601_PL:
       case EVAS_COLORSPACE_YCBCR422P709_PL:
         if (im->tex) evas_gl_common_texture_free(im->tex, EINA_TRUE);
@@ -362,6 +379,10 @@ evas_gl_common_image_new_from_copied_data(Evas_Engine_GL_Context *gc, unsigned i
       case EVAS_COLORSPACE_GRY8:
       case EVAS_COLORSPACE_AGRY88:
         break;
+      case EVAS_COLORSPACE_ETC1:
+        if (gc->shared->info.etc1) break;
+        ERR("We don't know what to do with ETC1 on this hardware. You need to add a software converter here.");
+        break;
       case EVAS_COLORSPACE_YCBCR422P601_PL:
       case EVAS_COLORSPACE_YCBCR422P709_PL:
         if (im->tex) evas_gl_common_texture_free(im->tex, EINA_TRUE);
@@ -413,6 +434,10 @@ evas_gl_common_image_new(Evas_Engine_GL_Context *gc, unsigned int w, unsigned in
       case EVAS_COLORSPACE_GRY8:
       case EVAS_COLORSPACE_AGRY88:
          break;
+      case EVAS_COLORSPACE_ETC1:
+        if (gc->shared->info.etc1) break;
+        ERR("We don't know what to do with ETC1 on this hardware. You need to add a software converter here.");
+        break;
       case EVAS_COLORSPACE_YCBCR422P601_PL:
       case EVAS_COLORSPACE_YCBCR422P709_PL:
       case EVAS_COLORSPACE_YCBCR422601_PL:
@@ -546,9 +571,10 @@ evas_gl_common_image_content_hint_set(Evas_GL_Image *im, int hint)
    if (!im->gc->shared->info.sec_image_map) return;
    if (!im->gc->shared->info.bgra) return;
    // does not handle yuv yet.
-   if (im->cs.space != EVAS_COLORSPACE_ARGB8888 ||
-       im->cs.space != EVAS_COLORSPACE_GRY8 ||
-       im->cs.space != EVAS_COLORSPACE_AGRY88) return;
+   if (im->cs.space != EVAS_COLORSPACE_ARGB8888 &&
+       im->cs.space != EVAS_COLORSPACE_GRY8 &&
+       im->cs.space != EVAS_COLORSPACE_AGRY88 &&
+       im->cs.space != EVAS_COLORSPACE_ETC1) return;
    if (im->content_hint == EVAS_IMAGE_CONTENT_HINT_DYNAMIC)
      {
         if (im->cs.data)
@@ -725,6 +751,7 @@ evas_gl_common_image_update(Evas_Engine_GL_Context *gc, Evas_GL_Image *im)
       case EVAS_COLORSPACE_ARGB8888:
       case EVAS_COLORSPACE_GRY8:
       case EVAS_COLORSPACE_AGRY88:
+      case EVAS_COLORSPACE_ETC1:
          if ((im->tex) &&
              ((im->dirty) || (ie->animated.animated) || (ie->flags.updated_data)))
           {
index baa27a2..c43a924 100644 (file)
@@ -32,6 +32,8 @@ static const GLenum lum_alpha_ifmt = GL_LUMINANCE_ALPHA;
 static const GLenum rgba8_ifmt     = GL_RGBA;
 static const GLenum rgba8_fmt      = GL_BGRA;
 
+static const GLenum etc1_fmt       = GL_ETC1_RGB8_OES;
+
 static struct {
    struct {
       int num, pix;
@@ -58,7 +60,9 @@ static const struct {
   { EINA_FALSE, EINA_FALSE, EVAS_COLORSPACE_GRY8, &lum_fmt, &lum_ifmt },
   { EINA_FALSE, EINA_TRUE, EVAS_COLORSPACE_GRY8, &lum_fmt, &lum_ifmt },
   { EINA_TRUE, EINA_FALSE, EVAS_COLORSPACE_AGRY88, &lum_alpha_fmt, &lum_alpha_ifmt },
-  { EINA_TRUE, EINA_TRUE, EVAS_COLORSPACE_AGRY88, &lum_alpha_fmt, &lum_alpha_ifmt }
+  { EINA_TRUE, EINA_TRUE, EVAS_COLORSPACE_AGRY88, &lum_alpha_fmt, &lum_alpha_ifmt },
+  { EINA_FALSE, EINA_FALSE, EVAS_COLORSPACE_ETC1, &etc1_fmt, &etc1_fmt },
+  { EINA_FALSE, EINA_TRUE, EVAS_COLORSPACE_ETC1, &etc1_fmt, &etc1_fmt }
 };
 
 static const GLenum matching_rgba[] = { GL_RGBA4, GL_RGBA8, GL_RGBA12, GL_RGBA16, 0x0 };
@@ -380,7 +384,7 @@ _pool_tex_alloc(Evas_GL_Texture_Pool *pt, int w, int h EINA_UNUSED, int *u, int
 
 static Evas_GL_Texture_Pool *
 _pool_tex_find(Evas_Engine_GL_Context *gc, int w, int h,
-               int intformat, int format, int *u, int *v,
+               GLenum intformat, GLenum format, int *u, int *v,
                Evas_GL_Texture_Alloca **apt, int atlas_w)
 {
    Evas_GL_Texture_Pool *pt = NULL;
@@ -390,7 +394,8 @@ _pool_tex_find(Evas_Engine_GL_Context *gc, int w, int h,
    if (atlas_w > gc->shared->info.max_texture_size)
       atlas_w = gc->shared->info.max_texture_size;
    if ((w > gc->shared->info.tune.atlas.max_w) ||
-       (h > gc->shared->info.tune.atlas.max_h))
+       (h > gc->shared->info.tune.atlas.max_h) ||
+       (intformat == etc1_fmt))
      {
         pt = _pool_tex_new(gc, w, h, intformat, format);
         if (!pt) return NULL;
@@ -432,6 +437,7 @@ Evas_GL_Texture *
 evas_gl_common_texture_new(Evas_Engine_GL_Context *gc, RGBA_Image *im)
 {
    Evas_GL_Texture *tex;
+   GLsizei w, h;
    int u = 0, v = 0;
    int lformat;
 
@@ -442,9 +448,24 @@ evas_gl_common_texture_new(Evas_Engine_GL_Context *gc, RGBA_Image *im)
 #define TEX_VREP 1
 
    lformat = _evas_gl_texture_search_format(im->cache_entry.flags.alpha, gc->shared->info.bgra, im->cache_entry.space);
-   tex->pt = _pool_tex_find(gc,
-                            im->cache_entry.w + TEX_HREP + 2,
-                            im->cache_entry.h + TEX_VREP,
+   if (im->cache_entry.space == EVAS_COLORSPACE_ETC1)
+     {
+        // Add border for avoiding artifact
+        w = im->cache_entry.w + 2;
+        h = im->cache_entry.h + 2;
+
+        // Adjust w and h for etc1 format (multiple of 4 pixels on both axis)
+        w = ((w >> 2) + (w & 0x3 ? 1 : 0)) << 2;
+        h = ((h >> 2) + (h & 0x3 ? 1 : 0)) << 2;
+     }
+   else
+     {
+        /* This need to be adjusted if we do something else than strip allocation */
+        w = im->cache_entry.w + TEX_HREP + 2; /* one pixel stop gap and two pixels for the border */
+        h = im->cache_entry.h + TEX_VREP; /* only one added border for security down */
+     }
+
+   tex->pt = _pool_tex_find(gc, w, h,
                             *matching_format[lformat].intformat,
                             *matching_format[lformat].format,
                             &u, &v, &tex->apt,
@@ -457,6 +478,10 @@ evas_gl_common_texture_new(Evas_Engine_GL_Context *gc, RGBA_Image *im)
    tex->apt->tex = tex;
    tex->x = u + 1;
    tex->y = v;
+
+   if (im->cache_entry.space == EVAS_COLORSPACE_ETC1)
+     tex->y++;
+
    tex->pt->references++;
    evas_gl_common_texture_update(tex, im);
 
@@ -1032,7 +1057,7 @@ evas_gl_common_texture_update(Evas_GL_Texture *tex, RGBA_Image *im)
         tex->alpha = im->cache_entry.flags.alpha;
 
         lformat = _evas_gl_texture_search_format(tex->alpha, tex->gc->shared->info.bgra, im->cache_entry.space);
-        // FIXME: why a 'render' new here ???
+        // FIXME: why a 'render' new here ??? Should already have been allocated, quite a weird path.
         tex->pt = _pool_tex_render_new(tex->gc, tex->w, tex->h,
                                        *matching_format[lformat].intformat,
                                        *matching_format[lformat].format);
@@ -1046,7 +1071,41 @@ evas_gl_common_texture_update(Evas_GL_Texture *tex, RGBA_Image *im)
       case EVAS_COLORSPACE_ARGB8888: bytes_count = 4; break;
       case EVAS_COLORSPACE_GRY8: bytes_count = 1; break;
       case EVAS_COLORSPACE_AGRY88: bytes_count = 2; break;
-      default: return;
+      case EVAS_COLORSPACE_ETC1:
+        {
+           /*
+             ETC1 can't be scaled down on the fly and interpolated, like it is
+             required for preloading, so we don't take that path. Also as the content
+             already have duplicated border and we use a specific function to
+             upload the compressed data, there is no need to use the normal path at
+             all.
+           */
+           GLsizei width, height;
+
+           width = im->cache_entry.w + 2;
+           height = im->cache_entry.h + 2;
+           width = ((width >> 2) + (width & 0x3 ? 1 : 0)) << 2;
+           height = ((height >> 2) + (height & 0x3 ? 1 : 0)) << 2;
+
+           glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
+           GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+
+           glCompressedTexImage2D(GL_TEXTURE_2D, 0, tex->pt->format,
+                                  width, height, 0,
+                                  ((width * height) >> 4) * 8, im->image.data);
+           GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+
+           if (tex->pt->texture != tex->gc->pipe[0].shader.cur_tex)
+             {
+                glBindTexture(GL_TEXTURE_2D, tex->gc->pipe[0].shader.cur_tex);
+                GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+             }
+
+           return;
+        }
+      default:
+         ERR("Don't know how to upload texture in colorspace %i.", im->cache_entry.space);
+         return;
      }
 
    // if preloaded, then async push it in after uploading a miniature of it