diff --git a/ChangeLog b/ChangeLog index 783c42eea8..d2abf99d25 100644 --- 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 9da46409a0..cd6dda8868 100644 --- 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 diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index 7cc9433e06..d4fe9aabab 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -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 \ diff --git a/src/lib/evas/cache/evas_cache_image.c b/src/lib/evas/cache/evas_cache_image.c index fe779a57f1..06d74def49 100644 --- a/src/lib/evas/cache/evas_cache_image.c +++ b/src/lib/evas/cache/evas_cache_image.c @@ -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); diff --git a/src/lib/evas/include/evas_common_private.h b/src/lib/evas/include/evas_common_private.h index 1a3c10c086..f051a72a0a 100644 --- a/src/lib/evas/include/evas_common_private.h +++ b/src/lib/evas/include/evas_common_private.h @@ -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 diff --git a/src/modules/evas/engines/gl_common/evas_gl_common.h b/src/modules/evas/engines/gl_common/evas_gl_common.h index 8dc08ae37f..f0f3bd1593 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_common.h +++ b/src/modules/evas/engines/gl_common/evas_gl_common.h @@ -162,6 +162,8 @@ #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 diff --git a/src/modules/evas/engines/gl_common/evas_gl_context.c b/src/modules/evas/engines/gl_common/evas_gl_context.c index f99be54c10..1bdfac37f3 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_context.c +++ b/src/modules/evas/engines/gl_common/evas_gl_context.c @@ -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); diff --git a/src/modules/evas/engines/gl_common/evas_gl_core.c b/src/modules/evas/engines/gl_common/evas_gl_core.c index 5145e0dfc8..8fadc27a27 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_core.c +++ b/src/modules/evas/engines/gl_common/evas_gl_core.c @@ -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; diff --git a/src/modules/evas/engines/gl_common/evas_gl_image.c b/src/modules/evas/engines/gl_common/evas_gl_image.c index f8cdd111f8..8e721f82bc 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_image.c +++ b/src/modules/evas/engines/gl_common/evas_gl_image.c @@ -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 index 0000000000..ffdb9093e7 --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_preload.c @@ -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; +} diff --git a/src/modules/evas/engines/gl_common/evas_gl_texture.c b/src/modules/evas/engines/gl_common/evas_gl_texture.c index 0c07ffa383..426549cc74 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_texture.c +++ b/src/modules/evas/engines/gl_common/evas_gl_texture.c @@ -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) diff --git a/src/modules/evas/engines/gl_x11/evas_engine.c b/src/modules/evas/engines/gl_x11/evas_engine.c index 7f408fdd91..9441d6dccd 100644 --- a/src/modules/evas/engines/gl_x11/evas_engine.c +++ b/src/modules/evas/engines/gl_x11/evas_engine.c @@ -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 diff --git a/src/modules/evas/engines/gl_x11/evas_x_main.c b/src/modules/evas/engines/gl_x11/evas_x_main.c index d0de1df8ae..91015dcb3f 100644 --- a/src/modules/evas/engines/gl_x11/evas_x_main.c +++ b/src/modules/evas/engines/gl_x11/evas_x_main.c @@ -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) {