+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.
- 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
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 \
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);
Eina_Bool rotated : 1;
Eina_Bool unload_cancel : 1;
Eina_Bool given_mmap : 1;
+
+ Eina_Bool updated_data : 1;
};
struct _Image_Entry_Frame
#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
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,
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;
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
int csize;
Eina_List *filtered;
+ Eina_List *targets;
unsigned char dirty : 1;
unsigned char cached : 1;
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;
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) \
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
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
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)
}
#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)
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;
}
}
+ 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,
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;
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);
}
}
-
// Set context from input or from resource
if (ctx)
context = ctx->context;
{
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;
}
}
{
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)
{
--- /dev/null
+#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;
+}
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)
{
pt->h = 0;
}
-static void
+void
pt_unref(Evas_GL_Texture_Pool *pt)
{
if (!pt) return;
{
GLuint fmt;
- if (!im->image.data) return;
+ if (!im->cache_entry.flags.loaded) return;
if (tex->alpha != im->cache_entry.flags.alpha)
{
*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__, "");
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)
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
{
extn_have_buffer_age = 0;
}
- if (!strstr(str, "swap_buffers_with_damage"))
- {
- glsym_eglSwapBuffersWithDamage = NULL;
- }
}
else
{
_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);
}
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;
if (re)
{
+ evas_gl_preload_render_relax(eng_preload_make_current, re);
+
#if 0
#ifdef GL_GLES
// Destroy the resource surface
}
if ((initted == 1) && (gl_wins == 0))
{
+ evas_gl_preload_shutdown();
evas_common_image_shutdown();
evas_common_font_shutdown();
initted = 0;
/* 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)
{
}
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
{
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);
{
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;
}
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
evas_common_tilebuf_free_render_rects(re->rects);
re->rects = NULL;
}
+
+ end:
+ evas_gl_preload_render_unlock(eng_preload_make_current, re);
}
static void
}
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;
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
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
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)
{