evas: add support for asynchronously uploading GL texture.
authorCedric Bail <cedric.bail@samsung.com>
Mon, 24 Jun 2013 02:41:32 +0000 (11:41 +0900)
committerCedric Bail <cedric.bail@samsung.com>
Mon, 24 Jun 2013 03:04:18 +0000 (12:04 +0900)
NOTE: when using Evas_Object image preload infrastructure the GL texture
upload was uploaded from the main loop during the rendering stage. This
could lead to some frame drop during fast animation due to the time needed
to upload that texture.

This patch fix this problem by uploading a small texture quickly (16x16)
and waiting for going back to the main loop to be able to use the same GL
context from another thread to do the texture upload asynchronously without
blocking the main loop.

13 files changed:
ChangeLog
NEWS
src/Makefile_Evas.am
src/lib/evas/cache/evas_cache_image.c
src/lib/evas/include/evas_common_private.h
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_core.c
src/modules/evas/engines/gl_common/evas_gl_image.c
src/modules/evas/engines/gl_common/evas_gl_preload.c [new file with mode: 0644]
src/modules/evas/engines/gl_common/evas_gl_texture.c
src/modules/evas/engines/gl_x11/evas_engine.c
src/modules/evas/engines/gl_x11/evas_x_main.c

index 783c42e..d2abf99 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2013-06-24  Cedric Bail
+
+       * Evas: add support for asynchronously uploading GL texture.
+
 2013-06-22  Thiep Ha
 
         * Edje: Move cursor to correct position when selection handlers are pressed.
diff --git a/NEWS b/NEWS
index 9da4640..cd6dda8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -48,6 +48,7 @@ Additions:
      - Add multiple font draws support to engines
      - Add Cserve2 scalecache support
      - Add evas_object_image_source_clip_set()/get()
+     - Asynchronous preload of GL texture.
     * ecore_x:
      - Add window profile support.
        ECORE_X_ATOM_E_WINDOW_PROFILE_SUPPORTED
index 7cc9433..d4fe9aa 100644 (file)
@@ -438,6 +438,7 @@ modules/evas/engines/gl_common/evas_gl_file_cache.c \
 modules/evas/engines/gl_common/evas_gl_shader.c \
 modules/evas/engines/gl_common/evas_gl_rectangle.c \
 modules/evas/engines/gl_common/evas_gl_texture.c \
+modules/evas/engines/gl_common/evas_gl_preload.c \
 modules/evas/engines/gl_common/evas_gl_image.c \
 modules/evas/engines/gl_common/evas_gl_font.c \
 modules/evas/engines/gl_common/evas_gl_polygon.c \
index fe779a5..06d74de 100644 (file)
@@ -396,6 +396,7 @@ _evas_cache_image_async_end(void *data)
    ie->cache->pending = eina_list_remove(ie->cache->pending, ie);
    ie->preload = NULL;
    ie->flags.preload_done = ie->flags.loaded;
+   ie->flags.updated_data = 1;
    while ((tmp = ie->targets))
      {
         evas_object_inform_call_image_preloaded((Evas_Object*) tmp->target);
index 1a3c10c..f051a72 100644 (file)
@@ -519,6 +519,8 @@ struct _Image_Entry_Flags
    Eina_Bool rotated       : 1;
    Eina_Bool unload_cancel : 1;
    Eina_Bool given_mmap    : 1;
+
+   Eina_Bool updated_data  : 1;
 };
 
 struct _Image_Entry_Frame
index 8dc08ae..f0f3bd1 100644 (file)
 #define GL_MULTISAMPLE_BUFFER_BIT7_QCOM               0x80000000
 #endif
 
+#define EVAS_GL_TILE_SIZE 16
+
 #define SHAD_VERTEX 0
 #define SHAD_COLOR  1
 #define SHAD_TEXUV  2
@@ -181,6 +183,9 @@ typedef struct _Evas_GL_Image                 Evas_GL_Image;
 typedef struct _Evas_GL_Font_Texture          Evas_GL_Font_Texture;
 typedef struct _Evas_GL_Polygon               Evas_GL_Polygon;
 typedef struct _Evas_GL_Polygon_Point         Evas_GL_Polygon_Point;
+typedef struct _Evas_GL_Texture_Async_Preload Evas_GL_Texture_Async_Preload;
+
+typedef Eina_Bool (*evas_gl_make_current_cb)(void *engine_data, void *doit);
 
 typedef enum {
   SHADER_RECT,
@@ -494,6 +499,7 @@ struct _Evas_GL_Texture
    Evas_GL_Texture_Alloca *apt, *aptt;
    RGBA_Font_Glyph *fglyph;
    int              x, y, w, h;
+   int              tx, ty;
    double           sx1, sy1, sx2, sy2;
    int              references;
 
@@ -503,8 +509,12 @@ struct _Evas_GL_Texture
       int              source;
    } double_buffer;
 
+   Eina_List       *targets;
+
    Eina_Bool        alpha : 1;
    Eina_Bool        dyn : 1;
+   Eina_Bool        uploaded : 1;
+   Eina_Bool        was_preloaded : 1;
 };
 
 struct _Evas_GL_Image
@@ -540,6 +550,7 @@ struct _Evas_GL_Image
    int csize;
 
    Eina_List       *filtered;
+   Eina_List       *targets;
 
    unsigned char    dirty : 1;
    unsigned char    cached : 1;
@@ -563,6 +574,14 @@ struct _Evas_GL_Polygon_Point
    int x, y;
 };
 
+struct _Evas_GL_Texture_Async_Preload
+{
+   Evas_GL_Texture *tex;
+   RGBA_Image *im;
+
+   Eina_Bool unpack_row_length;
+};
+
 #if 0
 extern Evas_GL_Program_Source shader_rect_frag_src;
 extern Evas_GL_Program_Source shader_rect_vert_src;
@@ -748,7 +767,19 @@ extern unsigned int   (*secsym_eglUnmapImageSEC)             (void *a, void *b,
 extern unsigned int   (*secsym_eglGetImageAttribSEC)         (void *a, void *b, int c, int *d);
 #endif
 
-//#define GL_ERRORS 1
+Eina_Bool evas_gl_preload_push(Evas_GL_Texture_Async_Preload *async);
+void evas_gl_preload_pop(Evas_GL_Texture *tex);
+int evas_gl_preload_init(void);
+int evas_gl_preload_shutdown(void);
+void evas_gl_preload_render_lock(evas_gl_make_current_cb make_current, void *engine_data);
+void evas_gl_preload_render_unlock(evas_gl_make_current_cb make_current, void *engine_data);
+void evas_gl_preload_render_relax(evas_gl_make_current_cb make_current, void *engine_data);
+void evas_gl_preload_target_register(Evas_GL_Texture *tex, Eo *target);
+void evas_gl_preload_target_unregister(Evas_GL_Texture *tex, Eo *target);
+
+void pt_unref(Evas_GL_Texture_Pool *pt);
+
+#define GL_ERRORS 1
 
 #ifdef GL_ERRORS
 # define GLERR(fn, fl, ln, op) \
@@ -763,4 +794,11 @@ extern unsigned int   (*secsym_eglGetImageAttribSEC)         (void *a, void *b,
 Eina_Bool evas_gl_common_module_open(void);
 void      evas_gl_common_module_close(void);
 
+static inline void
+_tex_sub_2d(int x, int y, int w, int h, int fmt, int type, const void *pix)
+{
+   glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, fmt, type, pix);
+   GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+}
+
 #endif
index f99be54..1bdfac3 100644 (file)
@@ -1261,8 +1261,15 @@ _evas_gl_common_context_push(int rtype,
                              Eina_Bool clip,
                              int cx, int cy, int cw, int ch)
 {
+   GLuint current_tex = 0;
+   GLuint current_texm = 0;
    int pn = 0;
 
+   if (tex)
+     current_tex = tex->ptt ? tex->ptt->texture : tex->pt->texture;
+   if (texm)
+     current_texm = texm->ptt ? texm->ptt->texture : tex->pt->texture;
+
 #ifdef GLPIPES
  again:
 #endif
@@ -1277,8 +1284,8 @@ _evas_gl_common_context_push(int rtype,
         for (i = pn; i >= 0; i--)
           {
              if ((gc->pipe[i].region.type == rtype)
-                 && (!tex || gc->pipe[i].shader.cur_tex == tex->pt->texture)
-                 && (!texm || gc->pipe[i].shader.cur_texm == texm->pt->texture)
+                 && (!tex || gc->pipe[i].shader.cur_tex == current_tex)
+                 && (!texm || gc->pipe[i].shader.cur_texm == current_texm)
                  && (gc->pipe[i].shader.cur_prog == prog)
                  && (gc->pipe[i].shader.smooth == smooth)
                  && (gc->pipe[i].shader.blend == blend)
@@ -1318,8 +1325,8 @@ _evas_gl_common_context_push(int rtype,
      }
 #else
    if (!((gc->pipe[pn].region.type == rtype)
-         && (!tex || gc->pipe[pn].shader.cur_tex == tex->pt->texture)
-         && (!texm || gc->pipe[pn].shader.cur_texm == texm->pt->texture)
+         && (!tex || gc->pipe[pn].shader.cur_tex == current_tex)
+         && (!texm || gc->pipe[pn].shader.cur_texm == current_texm)
          && (gc->pipe[pn].shader.cur_prog == prog)
          && (gc->pipe[pn].shader.smooth == smooth)
          && (gc->pipe[pn].shader.blend == blend)
@@ -1553,8 +1560,10 @@ evas_gl_common_context_image_push(Evas_Engine_GL_Context *gc,
                                   int r, int g, int b, int a,
                                   Eina_Bool smooth, Eina_Bool tex_only)
 {
+   Evas_GL_Texture_Pool *pt;
    int pnum, nv, nc, nu, ns, i;
    GLfloat tx1, tx2, ty1, ty2;
+   GLfloat offsetx, offsety;
    Eina_Bool blend = EINA_FALSE;
    GLuint prog = gc->shared->shader[SHADER_IMG].prog;
    int pn = 0, sam = 0;
@@ -1693,6 +1702,25 @@ evas_gl_common_context_image_push(Evas_Engine_GL_Context *gc,
           }
      }
 
+   if (tex->ptt)
+     {
+        pt = tex->ptt;
+        offsetx = tex->tx;
+        offsety = tex->ty;
+
+        // Adjusting sx, sy, sw and sh to real size of tiny texture
+        sx = sx * (EVAS_GL_TILE_SIZE - 2) / tex->w;
+        sw = sw * (EVAS_GL_TILE_SIZE - 2) / tex->w;
+        sy = sy * (EVAS_GL_TILE_SIZE - 1) / tex->h;
+        sh = sh * (EVAS_GL_TILE_SIZE - 1) / tex->h;
+     }
+   else
+     {
+        pt = tex->pt;
+        offsetx = tex->x;
+        offsety = tex->y;
+     }
+
    pn = _evas_gl_common_context_push(RTYPE_IMAGE,
                                      gc, tex, NULL,
                                      prog,
@@ -1702,7 +1730,7 @@ evas_gl_common_context_image_push(Evas_Engine_GL_Context *gc,
                                      0, 0, 0, 0, 0);
 
    gc->pipe[pn].region.type = RTYPE_IMAGE;
-   gc->pipe[pn].shader.cur_tex = tex->pt->texture;
+   gc->pipe[pn].shader.cur_tex = pt->texture;
    gc->pipe[pn].shader.cur_prog = prog;
    gc->pipe[pn].shader.smooth = smooth;
    gc->pipe[pn].shader.blend = blend;
@@ -1731,17 +1759,17 @@ evas_gl_common_context_image_push(Evas_Engine_GL_Context *gc,
 
    if ((tex->im) && (tex->im->native.data) && (!tex->im->native.yinvert))
      {
-        tx1 = ((double)(tex->x) + sx) / (double)tex->pt->w;
-        ty1 = 1.0 - ((double)(tex->y) + sy) / (double)tex->pt->h;
-        tx2 = ((double)(tex->x) + sx + sw) / (double)tex->pt->w;
-        ty2 = 1.0 - ((double)(tex->y) + sy + sh) / (double)tex->pt->h;
+        tx1 = ((double)(offsetx) + sx) / (double)pt->w;
+        ty1 = 1.0 - ((double)(offsety) + sy) / (double)pt->h;
+        tx2 = ((double)(offsetx) + sx + sw) / (double)pt->w;
+        ty2 = 1.0 - ((double)(offsety) + sy + sh) / (double)pt->h;
      }
    else
      {
-        tx1 = ((double)(tex->x) + sx) / (double)tex->pt->w;
-        ty1 = ((double)(tex->y) + sy) / (double)tex->pt->h;
-        tx2 = ((double)(tex->x) + sx + sw) / (double)tex->pt->w;
-        ty2 = ((double)(tex->y) + sy + sh) / (double)tex->pt->h;
+        tx1 = ((double)(offsetx) + sx) / (double)pt->w;
+        ty1 = ((double)(offsety) + sy) / (double)pt->h;
+        tx2 = ((double)(offsetx) + sx + sw) / (double)pt->w;
+        ty2 = ((double)(offsety) + sy + sh) / (double)pt->h;
      }
 
    PUSH_VERTEX(pn, x    , y    , 0);
index 5145e0d..8fadc27 100644 (file)
@@ -117,7 +117,6 @@ _internal_resource_make_current(void *eng_data, EVGL_Context *ctx)
           }
      }
 
-
    // Set context from input or from resource
    if (ctx)
       context = ctx->context;
index f8cdd11..8e721f8 100644 (file)
@@ -13,7 +13,7 @@ evas_gl_common_image_all_unload(Evas_Engine_GL_Context *gc)
           {
              if (!im->tex->pt->dyn.img)
                {
-                evas_gl_common_texture_free(im->tex, EINA_TRUE);
+                  evas_gl_common_texture_free(im->tex, EINA_TRUE);
                   im->tex = NULL;
                }
           }
@@ -574,11 +574,12 @@ evas_gl_common_image_update(Evas_Engine_GL_Context *gc, Evas_GL_Image *im)
      {
       case EVAS_COLORSPACE_ARGB8888:
          if ((im->tex) &&
-             ((im->dirty) || (ie->animated.animated)))
+             ((im->dirty) || (ie->animated.animated) || (ie->flags.updated_data)))
           {
              evas_cache_image_load_data(&im->im->cache_entry);
              evas_gl_common_texture_update(im->tex, im->im);
              evas_cache_image_unload_data(&im->im->cache_entry);
+             ie->flags.updated_data = 0;
           }
        if (!im->tex)
           {
diff --git a/src/modules/evas/engines/gl_common/evas_gl_preload.c b/src/modules/evas/engines/gl_common/evas_gl_preload.c
new file mode 100644 (file)
index 0000000..ffdb909
--- /dev/null
@@ -0,0 +1,441 @@
+#include "evas_gl_private.h"
+
+static Eina_Thread async_loader_thread;
+static Eina_Condition async_loader_cond;
+static Eina_Lock async_loader_lock;
+
+static Evas_GL_Texture_Async_Preload *async_current = NULL;
+static Eina_List *async_loader_tex = NULL;
+static Eina_List *async_loader_todie = NULL;
+static Eina_Bool async_loader_exit = EINA_FALSE;
+static Eina_Bool async_loader_running = EINA_FALSE;
+static Eina_Bool async_loader_standby = EINA_FALSE;
+static Eina_Bool async_current_cancel = EINA_FALSE;
+static int async_loader_init = 0;
+
+static void *async_engine_data = NULL;
+static evas_gl_make_current_cb async_gl_make_current = NULL;
+
+Eina_Bool
+evas_gl_preload_push(Evas_GL_Texture_Async_Preload *async)
+{
+   if (!async_loader_init) return EINA_FALSE;
+
+   eina_lock_take(&async_loader_lock);
+   async_loader_tex = eina_list_append(async_loader_tex, async);
+   eina_lock_release(&async_loader_lock);
+
+   return EINA_TRUE;
+}
+
+void
+evas_gl_preload_pop(Evas_GL_Texture *tex)
+{
+   Evas_GL_Texture_Async_Preload *async;
+   Eina_List *l;
+
+   if (!async_loader_init) return ;
+
+   eina_lock_take(&async_loader_lock);
+
+   if (async_gl_make_current && async_current && async_current->tex == tex)
+     {
+        Eina_Bool running = async_loader_running;
+        evas_gl_make_current_cb tmp_cb = async_gl_make_current;
+        void *tmp_data = async_engine_data;
+
+        async_current_cancel = EINA_TRUE;
+        eina_lock_release(&async_loader_lock);
+
+        if (running) evas_gl_preload_render_lock(tmp_cb, tmp_data);
+
+        evas_gl_common_texture_free(async_current->tex, EINA_FALSE);
+        evas_cache_image_drop(&async_current->im->cache_entry);
+        free(async_current);
+
+        async_current = NULL;
+
+        if (running) evas_gl_preload_render_unlock(tmp_cb, tmp_data);
+
+        return ;
+     }
+
+   EINA_LIST_FOREACH(async_loader_tex, l, async)
+     if (async->tex == tex)
+       {
+          async_loader_tex = eina_list_remove_list(async_loader_tex, l);
+          
+          evas_gl_common_texture_free(async->tex, EINA_FALSE);
+          evas_cache_image_drop(&async->im->cache_entry);
+          free(async);
+
+          break;
+       }
+
+   eina_lock_release(&async_loader_lock);
+}
+
+static void
+_evas_gl_preload_main_loop_wakeup(void)
+{
+   Evas_GL_Texture_Async_Preload *async;
+   evas_gl_make_current_cb cb = async_gl_make_current;
+   void *data = async_engine_data;
+   Eina_Bool running = async_loader_running;
+
+   if (running) evas_gl_preload_render_lock(cb, data);
+   EINA_LIST_FREE(async_loader_todie, async)
+     {
+        Eo *target;
+
+        EINA_LIST_FREE(async->tex->targets, target)
+          eo_do(target, evas_obj_image_pixels_dirty_set(EINA_TRUE));
+        async->im->cache_entry.flags.preload_done = 0;
+        async->tex->was_preloaded = EINA_TRUE;
+
+        async->tex->ptt->allocations = eina_list_remove(async->tex->ptt->allocations, async->tex->aptt);
+        pt_unref(async->tex->ptt);
+        async->tex->ptt = NULL;
+        free(async->tex->aptt);
+        async->tex->aptt = NULL;
+
+        evas_gl_common_texture_free(async->tex, EINA_FALSE);
+        evas_cache_image_drop(&async->im->cache_entry);
+        free(async);
+     }
+   if (running) evas_gl_preload_render_unlock(cb, data);
+}
+
+static void
+_evas_gl_preload_main_loop_wakeup_cb(void *target EINA_UNUSED,
+                                     Evas_Callback_Type type EINA_UNUSED,
+                                     void *event_info EINA_UNUSED)
+{
+   _evas_gl_preload_main_loop_wakeup();
+}
+
+static Eina_Bool
+_evas_gl_preload_lock(void)
+{
+   eina_lock_take(&async_loader_lock);
+   if (async_loader_standby)
+     {
+        async_gl_make_current(async_engine_data, NULL);
+
+        async_loader_running = EINA_FALSE;
+
+        eina_condition_signal(&async_loader_cond);
+
+        eina_condition_wait(&async_loader_cond);
+        if (async_loader_exit) return EINA_FALSE;
+
+        async_gl_make_current(async_engine_data, async_engine_data);
+     }
+   async_loader_running = EINA_TRUE;
+   eina_lock_release(&async_loader_lock);
+
+   return EINA_TRUE;
+}
+
+static void *
+_evas_gl_preload_tile_async(void *data EINA_UNUSED, Eina_Thread t EINA_UNUSED)
+{
+   eina_lock_take(&async_loader_lock);
+   while (!async_loader_exit)
+     {
+        Evas_GL_Texture_Async_Preload *async;
+        GLuint fmt;
+
+        if (!async_loader_standby && async_loader_tex)
+          goto get_next;
+
+     retry:
+        eina_condition_wait(&async_loader_cond);
+        if (async_loader_exit) break ;
+
+     get_next:
+        // Get a texture to upload
+        async = eina_list_data_get(async_loader_tex);
+        async_loader_tex = eina_list_remove_list(async_loader_tex, async_loader_tex);
+        if (!async) continue;
+
+        async_loader_running = EINA_TRUE;
+        async_current = async;
+
+        eina_lock_release(&async_loader_lock);
+
+        // Switch context to this thread
+        if (!async_gl_make_current(async_engine_data, async_engine_data))
+          {
+             eina_lock_take(&async_loader_lock);
+             async_loader_tex = eina_list_append(async_loader_tex, async_current);
+             async_loader_running = EINA_FALSE;
+             async_current = NULL;
+
+             if (async_loader_standby)
+               eina_condition_signal(&async_loader_cond);
+
+             goto retry;
+          }
+
+        // FIXME: loop until all subtile are uploaded or the image is about to be deleted
+
+        // TEMPORARY CODE JUST TO SEE IF IT WORK
+        fmt = async->tex->pt->format;
+        glBindTexture(GL_TEXTURE_2D, async->tex->pt->texture);
+        GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+        if (async->unpack_row_length)
+          {
+             glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+             GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+          }
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+        GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+
+        //  +-+
+        //  +-+
+        //
+        _tex_sub_2d(async->tex->x, async->tex->y,
+                    async->im->cache_entry.w, async->im->cache_entry.h,
+                    fmt, async->tex->pt->dataformat,
+                    async->im->image.data);
+        //  xxx
+        //  xxx
+        //  ---
+        _tex_sub_2d(async->tex->x, async->tex->y + async->im->cache_entry.h,
+                    async->im->cache_entry.w, 1,
+                    fmt, async->tex->pt->dataformat,
+                    async->im->image.data + ((async->im->cache_entry.h - 1) * async->im->cache_entry.w));
+        //  xxx
+        //  xxx
+        // o
+        _tex_sub_2d(async->tex->x - 1, async->tex->y + async->im->cache_entry.h,
+                    1, 1,
+                    fmt, async->tex->pt->dataformat,
+                    async->im->image.data + ((async->im->cache_entry.h - 1) * async->im->cache_entry.w));
+        //  xxx
+        //  xxx
+        //     o
+        _tex_sub_2d(async->tex->x + async->im->cache_entry.w, async->tex->y + async->im->cache_entry.h,
+                    1, 1,
+                    fmt, async->tex->pt->dataformat,
+                    async->im->image.data + ((async->im->cache_entry.h - 1) * async->im->cache_entry.w) + (async->im->cache_entry.w - 1));
+        if (async->unpack_row_length)
+          {
+             glPixelStorei(GL_UNPACK_ROW_LENGTH, async->im->cache_entry.w);
+             GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+             // |xxx
+             // |xxx
+             //
+             _tex_sub_2d(async->tex->x - 1, async->tex->y,
+                         1, async->im->cache_entry.h,
+                         fmt, async->tex->pt->dataformat,
+                         async->im->image.data);
+             //  xxx|
+             //  xxx|
+             //
+             _tex_sub_2d(async->tex->x + async->im->cache_entry.w, async->tex->y,
+                         1, async->im->cache_entry.h,
+                         fmt, async->tex->pt->dataformat,
+                         async->im->image.data + (async->im->cache_entry.w - 1));
+          }
+        else
+          {
+             DATA32 *tpix, *ps, *pd;
+             int i;
+
+             tpix = alloca(async->im->cache_entry.h * sizeof(DATA32));
+             pd = tpix;
+             ps = async->im->image.data;
+             for (i = 0; i < (int)async->im->cache_entry.h; i++)
+               {
+                  *pd = *ps;
+                  pd++;
+                  ps += async->im->cache_entry.w;
+               }
+             // |xxx
+             // |xxx
+             //
+             _tex_sub_2d(async->tex->x - 1, async->tex->y,
+                         1, async->im->cache_entry.h,
+                         fmt, async->tex->pt->dataformat,
+                         tpix);
+             pd = tpix;
+             ps = async->im->image.data + (async->im->cache_entry.w - 1);
+             for (i = 0; i < (int)async->im->cache_entry.h; i++)
+               {
+                  *pd = *ps;
+                  pd++;
+                  ps += async->im->cache_entry.w;
+               }
+             //  xxx|
+             //  xxx|
+             //
+             _tex_sub_2d(async->tex->x + async->im->cache_entry.w, async->tex->y,
+                         1, async->im->cache_entry.h,
+                         fmt, async->tex->pt->dataformat,
+                         tpix);
+          }
+
+        // Switch back to current texture
+        if (async->tex->ptt->texture != async->tex->gc->pipe[0].shader.cur_tex)
+          {
+             glBindTexture(GL_TEXTURE_2D, async->tex->gc->pipe[0].shader.cur_tex);
+             GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+          }
+
+        // Shall we block now ?
+        if (!_evas_gl_preload_lock())
+          break;
+
+        // Release context
+        async_gl_make_current(async_engine_data, NULL);
+
+        evas_async_events_put(NULL, 0, NULL, _evas_gl_preload_main_loop_wakeup_cb);
+
+        eina_lock_take(&async_loader_lock);
+        async_current = NULL;
+        async_loader_todie = eina_list_append(async_loader_todie, async);
+        async_loader_running = EINA_FALSE;
+
+        if (async_loader_standby)
+          eina_condition_signal(&async_loader_cond);
+     }
+   eina_lock_release(&async_loader_lock);
+
+   return NULL;
+}
+
+// In the main loop
+// Push stuff on the todo queue
+// Upload the mini texture
+// Use the mini texture
+// Once download of the big texture, destroy mini texture and image data
+
+
+// Put the async preloader on standby
+void
+evas_gl_preload_render_lock(evas_gl_make_current_cb make_current, void *engine_data)
+{
+   if (!async_loader_init) return ;
+   eina_lock_take(&async_loader_lock);
+   if (async_loader_running)
+     {
+        async_loader_standby = EINA_TRUE;
+        eina_condition_wait(&async_loader_cond);
+
+        make_current(engine_data, engine_data);
+
+        async_engine_data = NULL;
+        async_gl_make_current = NULL;
+     }
+
+   eina_lock_release(&async_loader_lock);
+}
+
+// Let the async preloader run !
+void
+evas_gl_preload_render_unlock(evas_gl_make_current_cb make_current, void *engine_data)
+{
+   if (!async_loader_init) return ;
+   if (!make_current) return ;
+
+   eina_lock_take(&async_loader_lock);
+   if (!async_loader_running && (async_loader_tex || async_current))
+     {
+        make_current(engine_data, NULL);
+
+        async_gl_make_current = make_current;
+        async_engine_data = engine_data;
+
+        async_loader_standby = EINA_FALSE;
+        eina_condition_signal(&async_loader_cond);
+     }
+   eina_lock_release(&async_loader_lock);
+}
+
+// add a way to destroy surface and temporarily stop the rendering of the image
+void
+evas_gl_preload_render_relax(evas_gl_make_current_cb make_current, void *engine_data)
+{
+   if (engine_data != async_engine_data) return ;
+
+   evas_gl_preload_render_lock(make_current, engine_data);
+}
+
+static Eina_Bool
+_evas_gl_preload_target_die(void *data, Eo *obj,
+                            const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Evas_GL_Texture *tex = data;
+
+   evas_gl_preload_target_unregister(tex, obj);
+
+   return EO_CALLBACK_CONTINUE;
+}
+
+void
+evas_gl_preload_target_register(Evas_GL_Texture *tex, Eo *target)
+{
+   eo_do(target,
+         eo_event_callback_add(EO_EV_DEL, _evas_gl_preload_target_die, tex));
+   tex->targets = eina_list_append(tex->targets, target);
+   tex->references++;
+}
+
+void
+evas_gl_preload_target_unregister(Evas_GL_Texture *tex, Eo *target)
+{
+   Eina_List *l;
+   const Eo *o;
+
+   eo_do(target,
+         eo_event_callback_del(EO_EV_DEL, _evas_gl_preload_target_die, tex));
+
+   EINA_LIST_FOREACH(tex->targets, l, o)
+     if (o == target)
+       {
+          void *data = async_engine_data;
+          evas_gl_make_current_cb cb = async_gl_make_current;
+          Eina_Bool running = async_loader_running;
+
+          if (running) evas_gl_preload_render_lock(cb, data);
+          tex->targets = eina_list_remove_list(tex->targets, l);
+          evas_gl_common_texture_free(tex, EINA_FALSE);
+          if (running) evas_gl_preload_render_unlock(cb, data);
+
+          break;
+       }
+}
+
+int
+evas_gl_preload_init(void)
+{
+   if (async_loader_init++) return async_loader_init;
+
+   eina_lock_new(&async_loader_lock);
+   eina_condition_new(&async_loader_cond, &async_loader_lock);
+
+   if (!eina_thread_create(&async_loader_thread, EINA_THREAD_BACKGROUND, 0, _evas_gl_preload_tile_async, NULL))
+     {
+        // FIXME: handle error case
+     }
+
+   return async_loader_init;
+}
+
+int
+evas_gl_preload_shutdown(void)
+{
+   if (--async_loader_init) return async_loader_init;
+
+   async_loader_exit = EINA_TRUE;
+   eina_condition_signal(&async_loader_cond);
+
+   eina_thread_join(async_loader_thread);
+
+   eina_condition_free(&async_loader_cond);
+   eina_lock_free(&async_loader_lock);
+
+   return async_loader_init;
+}
index 0c07ffa..426549c 100644 (file)
@@ -194,13 +194,6 @@ evas_gl_common_texture_light_free(Evas_GL_Texture *tex)
    free(tex);
 }
 
-static void
-_tex_sub_2d(int x, int y, int w, int h, int fmt, int type, const void *pix)
-{
-   glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, fmt, type, pix);
-   GLERR(__FUNCTION__, __FILE__, __LINE__, "");
-}
-
 static Evas_GL_Texture_Pool *
 _pool_tex_new(Evas_Engine_GL_Context *gc, int w, int h, int intformat, GLenum format)
 {
@@ -711,7 +704,7 @@ evas_gl_texture_pool_empty(Evas_GL_Texture_Pool *pt)
    pt->h = 0;
 }
 
-static void
+void
 pt_unref(Evas_GL_Texture_Pool *pt)
 {
    if (!pt) return;
@@ -814,7 +807,7 @@ evas_gl_common_texture_update(Evas_GL_Texture *tex, RGBA_Image *im)
 {
    GLuint fmt;
 
-   if (!im->image.data) return;
+   if (!im->cache_entry.flags.loaded) return;
 
    if (tex->alpha != im->cache_entry.flags.alpha)
      {
@@ -830,8 +823,101 @@ evas_gl_common_texture_update(Evas_GL_Texture *tex, RGBA_Image *im)
                                        *matching_format[lformat].intformat,
                                        *matching_format[lformat].format);
      }
+   // If image was preloaded then we need a ptt
    if (!tex->pt) return;
 
+   // if preloaded, then async push it in after uploading a miniature of it
+   if (im->cache_entry.flags.preload_done && tex->w > 2 * EVAS_GL_TILE_SIZE && tex->h > 2 * EVAS_GL_TILE_SIZE)
+     {
+        Evas_GL_Texture_Async_Preload *async;
+        int *in;
+        int out[EVAS_GL_TILE_SIZE * EVAS_GL_TILE_SIZE];
+        float xstep, ystep;
+        float x, y;
+        int i, j;
+        int lformat;
+        int u, v;
+
+        if (tex->ptt) return ;
+
+        xstep = (float)tex->w / (EVAS_GL_TILE_SIZE - 2);
+        ystep = (float)tex->h / (EVAS_GL_TILE_SIZE - 1);
+        in = (int*) im->image.data;
+
+        for (y = 0, j = 0; j < EVAS_GL_TILE_SIZE - 1; y += ystep, j++)
+          {
+             out[j * EVAS_GL_TILE_SIZE] = in[(int)y * im->cache_entry.w];
+             for (x = 0, i = 1; i < EVAS_GL_TILE_SIZE - 1; x += xstep, i++)
+               out[j * EVAS_GL_TILE_SIZE + i] = in[(int)y * im->cache_entry.w + (int)x];
+             out[j * EVAS_GL_TILE_SIZE + i] = in[(int)y * im->cache_entry.w + (int)(x - xstep)];
+          }
+
+        memcpy(&out[j * EVAS_GL_TILE_SIZE], &out[(j - 1) * EVAS_GL_TILE_SIZE], EVAS_GL_TILE_SIZE * sizeof (int));
+
+        // out is a miniature of the texture, upload that now and schedule the data for later.
+
+        // Creating the mini picture texture
+        lformat = _evas_gl_texture_search_format(tex->alpha, tex->gc->shared->info.bgra);
+        tex->ptt = _pool_tex_find(tex->gc, EVAS_GL_TILE_SIZE, EVAS_GL_TILE_SIZE,
+                                  *matching_format[lformat].intformat,
+                                  *matching_format[lformat].format,
+                                  &u, &v, &tex->aptt,
+                                  tex->gc->shared->info.tune.atlas.max_alloc_size);
+        if (!tex->ptt)
+          goto upload;
+        tex->aptt->tex = tex;
+
+        tex->tx = u + 1;
+        tex->ty = v;
+        tex->ptt->references++;
+
+        // Bind and upload ! Vooom !
+        fmt = tex->ptt->format;
+        glBindTexture(GL_TEXTURE_2D, tex->ptt->texture);
+        GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+        if (tex->gc->shared->info.unpack_row_length)
+          {
+             glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+             GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+          }
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+        GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+
+        _tex_sub_2d(u, tex->ty, EVAS_GL_TILE_SIZE, EVAS_GL_TILE_SIZE, fmt, tex->ptt->dataformat, out);
+
+        // Switch back to current texture
+        if (tex->ptt->texture != tex->gc->pipe[0].shader.cur_tex)
+          {
+             glBindTexture(GL_TEXTURE_2D, tex->gc->pipe[0].shader.cur_tex);
+             GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+          }
+
+        // Now prepare uploading the main texture before returning;
+        async = malloc(sizeof (Evas_GL_Texture_Async_Preload));
+        if (!async)
+          {
+             goto upload;
+          }
+
+        async->tex = tex;
+        async->tex->references++;
+        async->im = im;
+        evas_cache_image_ref(&async->im->cache_entry);
+        async->unpack_row_length = tex->gc->shared->info.unpack_row_length;
+
+        if (evas_gl_preload_push(async))
+          return ;
+
+        // Failed to start asynchronous upload, likely due to preload not being supported by the backend
+        async->tex->references--;
+        evas_cache_image_drop(&async->im->cache_entry);
+        free(async);
+
+     upload:
+        pt_unref(tex->ptt);
+        tex->ptt = NULL;
+     }
+
    fmt = tex->pt->format;
    glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
    GLERR(__FUNCTION__, __FILE__, __LINE__, "");
@@ -939,6 +1025,13 @@ void
 evas_gl_common_texture_free(Evas_GL_Texture *tex, Eina_Bool force EINA_UNUSED)
 {
    if (!tex) return;
+   if (force)
+     {
+        evas_gl_preload_pop(tex);
+
+        while (tex->targets)
+          evas_gl_preload_target_unregister(tex, eina_list_data_get(tex->targets));
+     }
    tex->references--;
    if (tex->references != 0) return;
    if (tex->fglyph)
index 7f408fd..9441d6d 100644 (file)
@@ -73,6 +73,8 @@ typedef int             (*glsym_func_int) ();
 typedef unsigned int    (*glsym_func_uint) ();
 typedef const char     *(*glsym_func_const_char_ptr) ();
 
+static Eina_Bool eng_preload_make_current(void *data, void *doit);
+
 #ifdef GL_GLES
 
 #ifndef EGL_NATIVE_PIXMAP_KHR
@@ -702,10 +704,6 @@ gl_extn_veto(Render_Engine *re)
           {
              extn_have_buffer_age = 0;
           }
-        if (!strstr(str, "swap_buffers_with_damage"))
-          {
-             glsym_eglSwapBuffersWithDamage = NULL;
-          }
      }
    else
      {
@@ -808,6 +806,7 @@ static void
 _re_winfree(Render_Engine *re)
 {
    if (!re->win->surf) return;
+   evas_gl_preload_render_relax(eng_preload_make_current, re);
    eng_window_unsurf(re->win);
 }
 
@@ -877,6 +876,7 @@ eng_setup(Evas *eo_e, void *in)
              evas_common_font_init();
              evas_common_draw_init();
              evas_common_tilebuf_init();
+             evas_gl_preload_init();
              gl_extn_veto(re);
 //             evgl_engine_init(re, &evgl_funcs);
              initted = 1;
@@ -1033,6 +1033,8 @@ eng_output_free(void *data)
 
    if (re)
      {
+        evas_gl_preload_render_relax(eng_preload_make_current, re);
+
 #if 0
 #ifdef GL_GLES
         // Destroy the resource surface
@@ -1070,6 +1072,7 @@ eng_output_free(void *data)
      }
    if ((initted == 1) && (gl_wins == 0))
      {
+        evas_gl_preload_shutdown();
         evas_common_image_shutdown();
         evas_common_font_shutdown();
         initted = 0;
@@ -1213,6 +1216,42 @@ _merge_rects(Tilebuf *tb, Tilebuf_Rect *r1, Tilebuf_Rect *r2, Tilebuf_Rect *r3)
 /* vsync games - not for now though */
 #define VSYNC_TO_SCREEN 1
 
+static Eina_Bool
+eng_preload_make_current(void *data, void *doit)
+{
+   Render_Engine *re = data;
+
+   if (doit)
+     {
+#ifdef GL_GLES
+        if (!eglMakeCurrent(re->win->egl_disp, re->win->egl_surface[0], re->win->egl_surface[0], re->win->egl_context[0]))
+          return EINA_FALSE;
+#else
+        if (!glXMakeCurrent(re->info->info.display, re->win->win, re->win->context))
+          {
+             ERR("glXMakeCurrent(%p, 0x%x, %p) failed", re->info->info.display, (unsigned int)re->win->win, (void *)re->win->context);
+             GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+             return EINA_FALSE;
+          }
+#endif
+     }
+   else
+     {
+#ifdef GL_GLES
+        if (!eglMakeCurrent(re->win->egl_disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
+          return EINA_FALSE;
+#else
+        if (!glXMakeCurrent(re->info->info.display, None, NULL))
+          {
+             ERR("glXMakeCurrent(%p, None, NULL) failed", re->info->info.display);
+             GLERR(__FUNCTION__, __FILE__, __LINE__, "");
+             return EINA_FALSE;
+          }
+#endif
+     }
+   return EINA_TRUE;
+}
+
 static void *
 eng_output_redraws_next_update_get(void *data, int *x, int *y, int *w, int *h, int *cx, int *cy, int *cw, int *ch)
 {
@@ -1353,6 +1392,7 @@ eng_output_redraws_next_update_get(void *data, int *x, int *y, int *w, int *h, i
           }
         if (first_rect)
           {
+             evas_gl_preload_render_lock(eng_preload_make_current, re);
 #ifdef GL_GLES
              // dont need to for egl - eng_window_use() can check for other ctxt's
 #else
@@ -1433,11 +1473,11 @@ eng_output_flush(void *data, Evas_Render_Mode render_mode)
 {
    Render_Engine *re;
 
-   if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) return;
+   if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) goto end;
 
    re = (Render_Engine *)data;
-   if (!_re_wincheck(re)) return;
-   if (!re->win->draw.drew) return;
+   if (!_re_wincheck(re)) goto end;
+   if (!re->win->draw.drew) goto end;
    
    re->win->draw.drew = 0;
    eng_window_use(re->win);
@@ -1454,7 +1494,7 @@ eng_output_flush(void *data, Evas_Render_Mode render_mode)
      {
         re->info->callback.pre_swap(re->info->callback.data, re->evas);
      }
-   if ((glsym_eglSwapBuffersWithDamage) && (re->mode != MODE_FULL))
+   if ((glsym_eglSwapBuffersRegion) && (re->mode != MODE_FULL))
      {
         EGLint num = 0, *rects = NULL, i = 0;
         Tilebuf_Rect *r;
@@ -1505,9 +1545,9 @@ eng_output_flush(void *data, Evas_Render_Mode render_mode)
                     }
                   i += 4;
                }
-             glsym_eglSwapBuffersWithDamage(re->win->egl_disp,
-                                            re->win->egl_surface[0],
-                                            rects, num);
+             glsym_eglSwapBuffersRegion(re->win->egl_disp,
+                                        re->win->egl_surface[0],
+                                        num, rects);
           }
      }
    else
@@ -1574,6 +1614,9 @@ eng_output_flush(void *data, Evas_Render_Mode render_mode)
         evas_common_tilebuf_free_render_rects(re->rects);
         re->rects = NULL;
      }
+
+ end:
+   evas_gl_preload_render_unlock(eng_preload_make_current, re);
 }
 
 static void
@@ -2771,9 +2814,10 @@ eng_image_data_put(void *data, void *image, DATA32 *image_data)
 }
 
 static void
-eng_image_data_preload_request(void *data EINA_UNUSED, void *image, const Eo *target)
+eng_image_data_preload_request(void *data, void *image, const Eo *target)
 {
    Evas_GL_Image *gim = image;
+   Render_Engine *re = data;
    RGBA_Image *im;
 
    if (!gim) return;
@@ -2781,6 +2825,9 @@ eng_image_data_preload_request(void *data EINA_UNUSED, void *image, const Eo *ta
    im = (RGBA_Image *)gim->im;
    if (!im) return;
    evas_cache_image_preload_data(&im->cache_entry, target, NULL, NULL, NULL);
+   if (!gim->tex)
+     gim->tex = evas_gl_common_texture_new(re->win->gl_context, gim->im);
+   evas_gl_preload_target_register(gim->tex, (Eo*) target);
 }
 
 static void
@@ -2794,6 +2841,7 @@ eng_image_data_preload_cancel(void *data EINA_UNUSED, void *image, const Eo *tar
    im = (RGBA_Image *)gim->im;
    if (!im) return;
    evas_cache_image_preload_cancel(&im->cache_entry, target);
+   evas_gl_preload_target_unregister(gim->tex, (Eo*) target);
 }
 
 static Eina_Bool
index d0de1df..91015dc 100644 (file)
@@ -454,11 +454,57 @@ eng_window_free(Evas_GL_X11_Window *gw)
    free(gw);
 }
 
+static Eina_Bool
+eng_window_make_current(void *data, void *doit)
+{
+   Evas_GL_X11_Window *gw = data;
+
+#ifdef GL_GLES
+   if (doit)
+     {
+        if (!eglMakeCurrent(gw->egl_disp, gw->egl_surface[0], gw->egl_surface[0], gw->egl_context[0]))
+          return EINA_FALSE;
+     }
+   else
+     {
+        if (!eglMakeCurrent(gw->egl_disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
+          return EINA_FALSE;
+     }
+#else
+   if (doit)
+     {
+        if (gw->glxwin)
+          {
+             if (!glXMakeContextCurrent(gw->disp, gw->glxwin, gw->glxwin, gw->context))
+               {
+                  ERR("glXMakeContextCurrent(%p, %p, %p, %p)", (void *)gw->disp, (void *)gw->glxwin, (void *)gw->glxwin, (void *)gw->context);
+                  return EINA_FALSE;
+               }
+          }
+        else
+          {
+             if (!glXMakeCurrent(gw->disp, gw->win, gw->context))
+               {
+                  ERR("glXMakeCurrent(%p, 0x%x, %p) failed", gw->disp, (unsigned int)gw->win, (void *)gw->context);
+                  return EINA_FALSE;
+               }
+          }
+     }
+   else
+     {
+        if (!glXMakeCurrent(gw->disp, None, NULL))
+          return EINA_FALSE;
+     }
+#endif
+   return EINA_TRUE;
+}
+
 void
 eng_window_use(Evas_GL_X11_Window *gw)
 {
    Eina_Bool force_use = EINA_FALSE;
 
+   evas_gl_preload_render_lock(eng_window_make_current, gw);
 #ifdef GL_GLES
    if (_evas_gl_x11_window)
      {