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.
This commit is contained in:
Cedric Bail 2013-06-24 11:41:32 +09:00
parent 8e3d94d661
commit d06a0982ef
13 changed files with 741 additions and 38 deletions

View File

@ -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.

1
NEWS
View File

@ -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

View 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 \

View 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);

View 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

View File

@ -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

View 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);

View 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;

View 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)
{

View File

@ -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;
}

View 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)

View 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

View 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)
{