diff options
author | Cedric Bail <cedric.bail@samsung.com> | 2013-06-24 11:41:32 +0900 |
---|---|---|
committer | Cedric Bail <cedric.bail@samsung.com> | 2013-06-24 12:04:18 +0900 |
commit | d06a0982ef3d156059b2264d4494e036cbe409ee (patch) | |
tree | 2895f26d9a65fd81045601505d650b8e2034b79a /src | |
parent | 8e3d94d66130f16cec0d518a0ad6ec5fae04fec9 (diff) |
evas: add support for asynchronously uploading GL texture.
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile_Evas.am | 1 | ||||
-rw-r--r-- | src/lib/evas/cache/evas_cache_image.c | 1 | ||||
-rw-r--r-- | src/lib/evas/include/evas_common_private.h | 2 | ||||
-rw-r--r-- | src/modules/evas/engines/gl_common/evas_gl_common.h | 40 | ||||
-rw-r--r-- | src/modules/evas/engines/gl_common/evas_gl_context.c | 54 | ||||
-rw-r--r-- | src/modules/evas/engines/gl_common/evas_gl_core.c | 1 | ||||
-rw-r--r-- | src/modules/evas/engines/gl_common/evas_gl_image.c | 5 | ||||
-rw-r--r-- | src/modules/evas/engines/gl_common/evas_gl_preload.c | 441 | ||||
-rw-r--r-- | src/modules/evas/engines/gl_common/evas_gl_texture.c | 111 | ||||
-rw-r--r-- | src/modules/evas/engines/gl_x11/evas_engine.c | 72 | ||||
-rw-r--r-- | src/modules/evas/engines/gl_x11/evas_x_main.c | 46 |
11 files changed, 736 insertions, 38 deletions
diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index 7cc9433e0..d4fe9aaba 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 fe779a57f..06d74def4 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 1a3c10c08..f051a72a0 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 8dc08ae37..f0f3bd159 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 f99be54c1..1bdfac37f 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 5145e0dfc..8fadc27a2 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 f8cdd111f..8e721f82b 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 000000000..ffdb9093e --- /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 0c07ffa38..426549cc7 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 7f408fdd9..9441d6dcc 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 d0de1df8a..91015dcb3 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) { |