diff --git a/legacy/evas/src/bin/Makefile.am b/legacy/evas/src/bin/Makefile.am index 815defc2b1..0a2d6ef9c8 100644 --- a/legacy/evas/src/bin/Makefile.am +++ b/legacy/evas/src/bin/Makefile.am @@ -45,7 +45,7 @@ if EVAS_CSERVE2 SUBDIRS = loaders libexec_PROGRAMS = evas_cserve2 evas_cserve2_slave dummy_slave -bin_PROGRAMS = evas_cserve2_client +bin_PROGRAMS = evas_cserve2_client evas_cserve2_usage AM_CPPFLAGS = \ -I. \ @@ -56,7 +56,8 @@ AM_CPPFLAGS = \ -DPACKAGE_LIB_DIR=\"$(libdir)\" \ -DPACKAGE_LIBEXEC_DIR=\"$(libexecdir)\" \ @FREETYPE_CFLAGS@ \ -@EINA_CFLAGS@ +@EINA_CFLAGS@ \ +@EET_CFLAGS@ evas_cserve2_SOURCES = \ evas_cserve2.h \ @@ -70,14 +71,24 @@ evas_cserve2_requests.c \ evas_cserve2_fonts.c \ evas_cserve2_main_loop_linux.c +libevas_cserve2_utils_la = $(top_builddir)/src/lib/cserve2/libevas_cserve2_utils.la + evas_cserve2_LDADD = \ @FREETYPE_LIBS@ \ @EINA_LIBS@ \ -@EFL_SHM_OPEN_LIBS@ +@EFL_SHM_OPEN_LIBS@ \ +@EET_LIBS@ \ +$(libevas_cserve2_utils_la) evas_cserve2_client_SOURCES = \ evas_cserve2_client.c +evas_cserve2_usage_SOURCES = \ +evas_cserve2_usage.c + +evas_cserve2_usage_LDADD = \ +@EINA_LIBS@ + evas_cserve2_slave_SOURCES = \ evas_cserve2_slave.c \ evas_cserve2_utils.c diff --git a/legacy/evas/src/bin/dummy_slave.c b/legacy/evas/src/bin/dummy_slave.c index a150d67e1f..398d86eceb 100644 --- a/legacy/evas/src/bin/dummy_slave.c +++ b/legacy/evas/src/bin/dummy_slave.c @@ -89,8 +89,8 @@ error_send(int fd, Error_Type err) return response_send(fd, ERROR, &err, sizeof(Error_Type)); } -void * -cserve2_shm_map(const char *name, size_t length, off_t offset) +static void * +_cserve2_shm_map(const char *name, size_t length, off_t offset) { void *map; int fd; @@ -106,11 +106,13 @@ cserve2_shm_map(const char *name, size_t length, off_t offset) return map; } -void -cserve2_shm_unmap(void *map, size_t length) +/* +static void +_cserve2_shm_unmap(void *map, size_t length) { munmap(map, length); } +*/ static Error_Type image_open(const char *file __UNUSED__, const char *key __UNUSED__, Slave_Msg_Image_Opened *result) @@ -128,8 +130,8 @@ image_open(const char *file __UNUSED__, const char *key __UNUSED__, Slave_Msg_Im static Error_Type image_load(const char *shmfile, Slave_Msg_Image_Load *params) { - char *map = cserve2_shm_map(shmfile, params->shm.mmap_size, - params->shm.mmap_offset); + char *map = _cserve2_shm_map(shmfile, params->shm.mmap_size, + params->shm.mmap_offset); if (map == MAP_FAILED) return CSERVE2_RESOURCE_ALLOCATION_FAILED; diff --git a/legacy/evas/src/bin/evas_cserve2.h b/legacy/evas/src/bin/evas_cserve2.h index a1008acbb4..98c65471f6 100644 --- a/legacy/evas/src/bin/evas_cserve2.h +++ b/legacy/evas/src/bin/evas_cserve2.h @@ -21,6 +21,8 @@ #endif #define INF(...) EINA_LOG_DOM_INFO(_evas_cserve2_bin_log_dom, __VA_ARGS__) +#define DEBUG_LOAD_TIME 1 + extern int _evas_cserve2_bin_log_dom; typedef struct _Slave Slave; @@ -119,11 +121,12 @@ struct _Slave_Msg_Font_Load { void *ftdata1; // Freetype file source info comes here void *ftdata2; // Freetype font info comes here unsigned int rend_flags; - unsigned int hint; unsigned int size; unsigned int dpi; const char *name; const char *file; + void *data; + int datasize; }; struct _Slave_Msg_Font_Loaded { @@ -131,11 +134,58 @@ struct _Slave_Msg_Font_Loaded { void *ftdata2; }; +struct _Slave_Msg_Font_Glyphs_Load { + struct { + void *ftdata1; + void *ftdata2; + unsigned int rend_flags; + unsigned int hint; + } font; + struct { + unsigned int nglyphs; + unsigned int *glyphs; + } glyphs; + struct { + Shm_Handle *shm; + unsigned int usage; + unsigned int nglyphs; + } cache; +}; + +struct _Slave_Msg_Glyph { + unsigned int index; + unsigned int offset; + unsigned int size; + unsigned int rows; + unsigned int width; + unsigned int pitch; + unsigned int num_grays; + unsigned int pixel_mode; +}; + +typedef struct _Slave_Msg_Glyph Slave_Msg_Glyph; + +struct _Slave_Msg_Font_Cache { + unsigned int nglyphs; + Slave_Msg_Glyph *glyphs; + Shm_Handle *shm; + unsigned int usage; +}; + +typedef struct _Slave_Msg_Font_Cache Slave_Msg_Font_Cache; + +struct _Slave_Msg_Font_Glyphs_Loaded { + unsigned int ncaches; + Slave_Msg_Font_Cache **caches; +}; + typedef struct _Slave_Msg_Font_Load Slave_Msg_Font_Load; typedef struct _Slave_Msg_Font_Loaded Slave_Msg_Font_Loaded; +typedef struct _Slave_Msg_Font_Glyphs_Load Slave_Msg_Font_Glyphs_Load; +typedef struct _Slave_Msg_Font_Glyphs_Loaded Slave_Msg_Font_Glyphs_Loaded; typedef void *(*Font_Request_Msg_Create)(void *data, int *size); -typedef void (*Font_Request_Msg_Free)(void *data); +typedef void (*Font_Request_Msg_Free)(void *msg, void *data); typedef void (*Font_Request_Response)(Client *c, void *data, void *resp, unsigned int rid); typedef void (*Font_Request_Error)(Client *c, void *data, Error_Type error, unsigned int rid); @@ -161,6 +211,8 @@ typedef enum { CSERVE2_REQ_LAST } Font_Request_Type; +typedef struct _Glyph_Entry Glyph_Entry; + typedef void (*Fd_Watch_Cb)(int fd, Fd_Flags flags, void *data); typedef void (*Timeout_Cb)(void); /* void* for compat? */ typedef void (*Main_Loop_Child_Dead_Cb)(int pid, int status); /* void* for compat? */ @@ -211,6 +263,9 @@ off_t cserve2_shm_map_offset_get(const Shm_Handle *shm); off_t cserve2_shm_offset_get(const Shm_Handle *shm); size_t cserve2_shm_map_size_get(const Shm_Handle *shm); size_t cserve2_shm_size_get(const Shm_Handle *shm); +void *cserve2_shm_map(Shm_Handle *shm); +void cserve2_shm_unmap(Shm_Handle *shm); +size_t cserve2_shm_size_normalize(size_t size); void cserve2_command_run(Client *client, Message_Type type); @@ -225,10 +280,16 @@ void cserve2_cache_image_load(Client *client, unsigned int client_image_id, unsi void cserve2_cache_image_preload(Client *client, unsigned int client_image_id, unsigned int rid); void cserve2_cache_image_unload(Client *client, unsigned int client_image_id); -int cserve2_cache_font_load(Client *client, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int hint, unsigned int size, unsigned int dpi, unsigned int rid); +int cserve2_cache_font_load(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int rid); +int cserve2_cache_font_unload(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int rid); +int cserve2_cache_font_glyphs_load(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int hint, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs, unsigned int rid); +int cserve2_cache_font_glyphs_used(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs, unsigned int rid); +void cserve2_cache_stats_get(Client *client, unsigned int rid); +void cserve2_cache_font_debug(Client *client, unsigned int rid); Font_Request *cserve2_request_add(Font_Request_Type type, unsigned int rid, Client *client, Font_Request_Funcs *funcs, void *data); +void cserve2_request_waiter_add(Font_Request *req, unsigned int rid, Client *client); void cserve2_request_cancel(Font_Request *req, Client *client, Error_Type err); void cserve2_request_cancel_all(Font_Request *req, Error_Type err); void cserve2_requests_init(void); @@ -240,5 +301,7 @@ void cserve2_cache_requests_response(Slave_Command type, void *msg, void *data); void cserve2_font_init(void); void cserve2_font_shutdown(void); void *cserve2_font_slave_cb(Slave_Thread_Data *sd, Slave_Command *cmd, const void *cmddata, void *data); +void cserve2_font_source_ft_free(void *fontsource); +void cserve2_font_ft_free(void *fontinfo); #endif /* _EVAS_CSERVE2_H */ diff --git a/legacy/evas/src/bin/evas_cserve2_cache.c b/legacy/evas/src/bin/evas_cserve2_cache.c index 62bc9b516a..23a6902d3b 100644 --- a/legacy/evas/src/bin/evas_cserve2_cache.c +++ b/legacy/evas/src/bin/evas_cserve2_cache.c @@ -4,7 +4,12 @@ #include +#ifdef DEBUG_LOAD_TIME + #include +#endif + #include "evas_cserve2.h" +#include "evas_cs2_utils.h" typedef struct _Request_Funcs Request_Funcs; typedef struct _Request Request; @@ -19,7 +24,6 @@ typedef struct _File_Watch File_Watch; typedef struct _Font_Source Font_Source; typedef struct _Font_Entry Font_Entry; typedef struct _Font_Cache Font_Cache; -typedef struct _Glyph_Entry Glyph_Entry; typedef void *(*Request_Msg_Create)(Entry *e, int *size); typedef void (*Request_Response)(Entry *e, void *resp); @@ -49,6 +53,10 @@ struct _Entry { Eina_List *references; Request *request; Entry_Type type; +#ifdef DEBUG_LOAD_TIME + struct timeval load_start; + struct timeval load_finish; +#endif }; struct _File_Data { @@ -85,6 +93,7 @@ struct _Image_Data { }; struct _Font_Source { + const char *key; const char *name; const char *file; int references; @@ -95,32 +104,67 @@ struct _Font_Entry { Entry base; Font_Request *request; unsigned int rend_flags; - unsigned int hint; unsigned int size; unsigned int dpi; Font_Source *src; void *ft; + Fash_Glyph *glyphs; + Eina_Inlist *caches; + Font_Cache *last_cache; + Eina_Bool unused : 1; +#ifdef DEBUG_LOAD_TIME + struct timeval load_start; + struct timeval load_finish; + int gl_load_time; +#endif }; struct _Font_Cache { + EINA_INLIST; Font_Entry *fe; - struct { - const char *name; - void *data; - unsigned int size; - unsigned int usage; - } shm; + Shm_Handle *shm; + unsigned int usage; + int inuse; Eina_Inlist *glyphs; + unsigned int nglyphs; }; struct _Glyph_Entry { EINA_INLIST; Font_Entry *fe; - Font_Cache *fi; + Font_Cache *fc; unsigned int index; unsigned int offset; + unsigned int size; + unsigned int rows; + unsigned int width; + unsigned int pitch; + unsigned int num_grays; + unsigned int pixel_mode; }; +struct _Glyphs_Request { + Client *client; + Font_Entry *fe; + unsigned int current; + unsigned int nglyphs; + unsigned int *glyphs; + unsigned int nrender; + unsigned int *render; + unsigned int nanswer; + Glyph_Entry **answer; + unsigned int hint; +}; + +typedef struct _Glyphs_Request Glyphs_Request; + +struct _Glyphs_Group { + Font_Cache *fc; + Eina_List *glyphs; +}; + +typedef struct _Glyphs_Group Glyphs_Group; + struct _Reference { Client *client; Entry *entry; @@ -158,8 +202,39 @@ static Eina_Hash *file_watch = NULL; static Eina_List *image_entries_lru = NULL; +static Eina_List *font_shm_lru = NULL; + static int max_unused_mem_usage = 5 * 1024; /* in kbytes */ static int unused_mem_usage = 0; +static int max_font_usage = 10 * 4 * 1024; /* in kbytes */ +static int font_mem_usage = 0; + +static inline void +_entry_load_start(Entry *e) +{ +#ifdef DEBUG_LOAD_TIME + gettimeofday(&e->load_start, NULL); +#endif +} + +static inline void +_entry_load_finish(Entry *e) +{ +#ifdef DEBUG_LOAD_TIME + gettimeofday(&e->load_finish, NULL); +#endif +} + +static int +_timeval_sub(const struct timeval *tv2, const struct timeval *tv1) +{ + int t1, t2; + + t1 = tv1->tv_usec + tv1->tv_sec * 1000000; + t2 = tv2->tv_usec + tv2->tv_sec * 1000000; + + return t2 - t1; +} static void _image_opened_send(Client *client, File_Data *entry, unsigned int rid) @@ -237,13 +312,15 @@ _image_preloaded_send(Client *client, unsigned int rid) } static void -_font_loaded_send(Client *client, Font_Entry *fe __UNUSED__, unsigned int rid) +_font_loaded_send(Client *client, unsigned int rid) { int size; Msg_Font_Loaded msg; DBG("Sending FONT_LOADED reply for RID: %d.", rid); - memset(&msg, 0, sizeof(msg)); + + size = sizeof(msg); + memset(&msg, 0, size); msg.base.rid = rid; msg.base.type = CSERVE2_FONT_LOADED; @@ -272,6 +349,9 @@ _open_request_build(File_Data *f, int *bufsize) memcpy(buf + sizeof(msg) + pathlen, f->key, keylen); *bufsize = size; + + _entry_load_start(&f->base); + return buf; } @@ -310,6 +390,7 @@ _open_request_response(File_Data *e, Slave_Msg_Image_Opened *resp) { Waiter *w; + _entry_load_finish(&e->base); e->w = resp->w; e->h = resp->h; e->frame_count = resp->frame_count; @@ -397,6 +478,9 @@ _load_request_build(Image_Data *i, int *bufsize) memcpy(ptr, i->file->loader_data, loaderlen); *bufsize = size; + + _entry_load_start(&i->base); + return buf; } @@ -405,6 +489,8 @@ _load_request_response(Image_Data *e, Slave_Msg_Image_Loaded *resp) { Waiter *w; + _entry_load_start(&e->base); + e->alpha_sparse = resp->alpha_sparse; if (!e->doload) DBG("Entry %d loaded by speculative preload.", e->base.id); @@ -581,28 +667,24 @@ _file_watch_free(void *data) static int _font_entry_cmp(const Font_Entry *k1, int k1_length __UNUSED__, const Font_Entry *k2, int k2_length __UNUSED__) { - if (k1->src->name == k2->src->name) + if (k1->src->key == k2->src->key) { if (k1->size == k2->size) { if (k1->rend_flags == k2->rend_flags) - { - if (k1->hint == k2->hint) - return k1->dpi - k2->dpi; - return k1->hint - k2->hint; - } + return k1->dpi - k2->dpi; return k1->rend_flags - k2->rend_flags; } return k1->size - k2->size; } - return strcmp(k1->src->name, k2->src->name); + return strcmp(k1->src->key, k2->src->key); } static int _font_entry_key_hash(const Font_Entry *key, int key_length __UNUSED__) { int hash; - hash = eina_hash_djb2(key->src->name, eina_stringshare_strlen(key->src->name) + 1); + hash = eina_hash_djb2(key->src->key, eina_stringshare_strlen(key->src->key) + 1); hash ^= eina_hash_int32(&key->rend_flags, sizeof(int)); hash ^= eina_hash_int32(&key->size, sizeof(int)); hash ^= eina_hash_int32(&key->dpi, sizeof(int)); @@ -613,18 +695,100 @@ _font_entry_key_hash(const Font_Entry *key, int key_length __UNUSED__) static void _font_entry_free(Font_Entry *fe) { + fash_gl_free(fe->glyphs); + fe->src->references--; + if (fe->ft) cserve2_font_ft_free(fe->ft); + if (fe->src->references <= 0) + eina_hash_del_by_key(font_sources, fe->src->key); free(fe); } +static void +_glyph_free_cb(void *data) +{ + Glyph_Entry *gl = data; + free(gl); +} + static void _font_source_free(Font_Source *fs) { - if (fs->name) eina_stringshare_del(fs->name); - if (fs->file) eina_stringshare_del(fs->file); + eina_stringshare_del(fs->key); + eina_stringshare_del(fs->name); + eina_stringshare_del(fs->file); + if (fs->ft) cserve2_font_source_ft_free(fs->ft); free(fs); } +static void +_font_shm_promote(Font_Cache *fc) +{ + Eina_List *l; + l = eina_list_data_find_list(font_shm_lru, fc); + font_shm_lru = eina_list_demote_list(font_shm_lru, l); +} + +static int +_font_shm_size_get(Font_Cache *fc) +{ + int size; + + size = sizeof(*fc) + cserve2_shm_size_get(fc->shm); + + return size; +} + +static void +_font_shm_free(Font_Cache *fc) +{ + Font_Entry *fe = fc->fe; + fe->caches = eina_inlist_remove(fe->caches, EINA_INLIST_GET(fc)); + if (fc == fe->last_cache) + fe->last_cache = NULL; + + while (fc->glyphs) + { + Glyph_Entry *gl = EINA_INLIST_CONTAINER_GET(fc->glyphs, Glyph_Entry); + fc->glyphs = eina_inlist_remove(fc->glyphs, fc->glyphs); + fash_gl_del(fe->glyphs, gl->index); + } + + cserve2_shm_unref(fc->shm); + free(fc); + + if (!fe->caches) + eina_hash_del_by_key(font_entries, fe); +} + +static void +_font_shm_lru_flush(void) +{ + Eina_List *l, *l_next; + + l = font_shm_lru; + l_next = eina_list_next(l); + + while (l && font_mem_usage > max_font_usage) + { + int size; + Font_Cache *fc; + + fc = eina_list_data_get(l); + if (fc->fe->unused && fc->inuse == 0) + { + font_shm_lru = eina_list_remove_list(font_shm_lru, l); + size = _font_shm_size_get(fc); + size += fc->nglyphs * sizeof(Glyph_Entry); + _font_shm_free(fc); + font_mem_usage -= size; + } + + l = l_next; + l_next = eina_list_next(l); + } +} + void cserve2_cache_init(void) { @@ -645,6 +809,11 @@ cserve2_cache_init(void) void cserve2_cache_shutdown(void) { + Font_Cache *fc; + + EINA_LIST_FREE(font_shm_lru, fc) + _font_shm_free(fc); + eina_hash_free(image_entries); eina_hash_free(image_ids); eina_hash_free(file_entries); @@ -915,7 +1084,9 @@ _entry_reference_del(Entry *entry, Reference *ref) else if (entry->type == CSERVE2_FONT_ENTRY) { Font_Entry *fe = (Font_Entry *)entry; - eina_hash_del_by_key(font_entries, fe); + fe->unused = EINA_TRUE; + if (!fe->caches) + eina_hash_del_by_key(font_entries, fe); } else ERR("Wrong type of entry."); @@ -955,11 +1126,25 @@ _entry_free_cb(void *data) } static void -_font_entry_reference_del(Client *client __UNUSED__, Reference *ref) +_font_entry_reference_del(Client *client, Font_Entry *fe) { - Entry *entry = ref->entry; + Eina_List *l; + Reference *ref; - _entry_reference_del(entry, ref); + EINA_LIST_FOREACH(client->fonts.referencing, l, ref) + { + if (ref->entry == (Entry *)fe) + { + ref->count--; + if (ref->count > 0) + break; + + client->fonts.referencing = eina_list_remove_list( + client->fonts.referencing, l); + _entry_reference_del(&fe->base, ref); + return; + } + } } void @@ -982,7 +1167,7 @@ cserve2_cache_client_del(Client *client) EINA_LIST_FREE(client->fonts.referencing, ref) { - _font_entry_reference_del(client, ref); + _entry_reference_del(ref->entry, ref); } } @@ -1082,21 +1267,20 @@ _cserve2_font_source_find(const char *name) } static Font_Entry * -_cserve2_font_entry_find(const char *name, unsigned int namelen, unsigned int size, unsigned int rend_flags, unsigned int hint, unsigned int dpi) +_cserve2_font_entry_find(const char *name, unsigned int namelen, unsigned int size, unsigned int rend_flags, unsigned int dpi) { Font_Entry tmp_fe; Font_Source tmp_fs; Font_Entry *fe; - tmp_fs.name = eina_stringshare_add_length(name, namelen); + tmp_fs.key = eina_stringshare_add_length(name, namelen); tmp_fe.src = &tmp_fs; tmp_fe.size = size; tmp_fe.rend_flags = rend_flags; - tmp_fe.hint = hint; tmp_fe.dpi = dpi; fe = eina_hash_find(font_entries, &tmp_fe); - eina_stringshare_del(tmp_fs.name); + eina_stringshare_del(tmp_fs.key); return fe; } @@ -1110,7 +1294,6 @@ _font_load_request_build(void *data, int *size) msg->ftdata1 = fe->src->ft; msg->ftdata2 = fe->ft; msg->rend_flags = fe->rend_flags; - msg->hint = fe->hint; msg->size = fe->size; msg->dpi = fe->dpi; msg->name = fe->src->name; @@ -1118,13 +1301,15 @@ _font_load_request_build(void *data, int *size) *size = 0; + _entry_load_start(&fe->base); + return msg; } static void -_font_load_request_free(void *data) +_font_load_request_free(void *msg, void *data) { - free(data); + free(msg); } static void @@ -1133,18 +1318,33 @@ _font_load_request_response(Client *client __UNUSED__, void *data, void *resp, u Slave_Msg_Font_Loaded *msg = resp; Font_Entry *fe = data; + DBG("request %d answered.", rid); + if (!fe->src->ft) fe->src->ft = msg->ftdata1; if (!fe->ft) - fe->ft = msg->ftdata2; + { + fe->ft = msg->ftdata2; + _entry_load_finish(&fe->base); + } if (fe->request) fe->request = NULL; + + _font_loaded_send(client, rid); } static void _font_load_request_failed(Client *client __UNUSED__, void *data __UNUSED__, Error_Type error __UNUSED__, unsigned int rid __UNUSED__) { + Font_Entry *fe = data; + DBG("request %d error answered.", rid); + + cserve2_client_error_send(client, rid, error); + + if (fe->request) fe->request = NULL; + + _font_entry_reference_del(client, fe); } static Font_Request_Funcs _font_load_funcs = { @@ -1154,6 +1354,648 @@ static Font_Request_Funcs _font_load_funcs = { .error = (Font_Request_Error)_font_load_request_failed }; +static Eina_Bool +_glyphs_request_check(Glyphs_Request *req) +{ + int i; + Font_Entry *fe = req->fe; + + req->answer = malloc(sizeof(*req->answer) * req->nglyphs); + req->nanswer = 0; + + for (i = req->current; i < req->nglyphs; i++) + { + Glyph_Entry *ge; + ge = fash_gl_find(fe->glyphs, req->glyphs[i]); + if (ge) + { + req->answer[req->nanswer++] = ge; + ge->fc->inuse++; + } + else + break; + } + + req->current = i; + + // No glyphs need to be rendered. + return (req->nanswer == req->nglyphs); +} + +/* organize answer (cache1{gl1, gl2,}, cache2{gl3,gl4,gl5}, cache3{gl6}) + */ +static Eina_List * +_glyphs_group_create(Glyphs_Request *req) +{ + Eina_List *groups = NULL; + int i; + + for (i = 0; i < req->nanswer; i++) + { + Eina_List *l; + Glyphs_Group *iter, *gg = NULL; + Font_Cache *fc = req->answer[i]->fc; + + EINA_LIST_FOREACH(groups, l, iter) + { + if (iter->fc == fc) + { + gg = iter; + break; + } + } + + if (!gg) + { + gg = calloc(1, sizeof(*gg)); + gg->fc = fc; + groups = eina_list_append(groups, gg); + } + gg->glyphs = eina_list_append(gg->glyphs, req->answer[i]); + } + + return groups; +} + +static void +_glyphs_loaded_send(Glyphs_Request *req, unsigned int rid) +{ + Msg_Font_Glyphs_Loaded msg; + unsigned int size; + Eina_List *ll, *answers = NULL; + const char *shmname; + unsigned int shmsize; + unsigned int intsize; + char *resp, *buf; + Glyphs_Group *iter; + + memset(&msg, 0, sizeof(msg)); + msg.base.rid = rid; + msg.base.type = CSERVE2_FONT_GLYPHS_LOADED; + + answers = _glyphs_group_create(req); + msg.ncaches = eina_list_count(answers); + size = sizeof(msg); + + // calculate size of message + // ncaches * sizeof(cache) + nglyphs1 * sizeof(glyph) + + // nglyphs2 * sizeof(glyph)... + + intsize = sizeof(unsigned int); + + EINA_LIST_FOREACH(answers, ll, iter) + { + shmname = cserve2_shm_name_get(iter->fc->shm); + shmsize = eina_stringshare_strlen(shmname) + 1; + // shm namelen + name + size += intsize + shmsize; + + // nglyphs + size += intsize; + // nglyphs * (index + offset + size + rows + width + pitch + + // num_grays + pixel_mode) + size += eina_list_count(iter->glyphs) * 8 * intsize; + } + + resp = malloc(size); + memcpy(resp, &msg, sizeof(msg)); + buf = resp + sizeof(msg); + + EINA_LIST_FREE(answers, iter) + { + Glyph_Entry *gl; + unsigned int nglyphs; + + shmname = cserve2_shm_name_get(iter->fc->shm); + shmsize = eina_stringshare_strlen(shmname) + 1; + memcpy(buf, &shmsize, intsize); + buf += intsize; + memcpy(buf, shmname, shmsize); + buf += shmsize; + + nglyphs = eina_list_count(iter->glyphs); + memcpy(buf, &nglyphs, intsize); + buf += intsize; + + iter->fc->inuse -= eina_list_count(iter->glyphs); + + EINA_LIST_FREE(iter->glyphs, gl) + { + memcpy(buf, &gl->index, intsize); + buf += intsize; + memcpy(buf, &gl->offset, intsize); + buf += intsize; + memcpy(buf, &gl->size, intsize); + buf += intsize; + memcpy(buf, &gl->rows, intsize); + buf += intsize; + memcpy(buf, &gl->width, intsize); + buf += intsize; + memcpy(buf, &gl->pitch, intsize); + buf += intsize; + memcpy(buf, &gl->num_grays, intsize); + buf += intsize; + memcpy(buf, &gl->pixel_mode, intsize); + buf += intsize; + } + + /* We are removing SHMs from the beginning of the list, so this + * gives a higher priority to them */ + _font_shm_promote(iter->fc); + eina_list_free(iter->glyphs); + free(iter); + } + + cserve2_client_send(req->client, &size, sizeof(size)); + cserve2_client_send(req->client, resp, size); + + free(resp); +} + +/* + * taken from evas_path.c. It would be good to clean up those utils to + * have cserve link against them easily without dragging unneeded dependencies + */ +#ifdef _WIN32 +# define EVAS_PATH_SEPARATOR "\\" +#else +# define EVAS_PATH_SEPARATOR "/" +#endif + +static char * +_file_path_join(const char *path, const char *end) +{ + char *res = NULL; + size_t len; + + if ((!path) && (!end)) return NULL; + if (!path) return strdup(end); + if (!end) return strdup(path); + len = strlen(path); + len += strlen(end); + len += strlen(EVAS_PATH_SEPARATOR); + res = malloc(len + 1); + if (!res) return NULL; + strcpy(res, path); + strcat(res, EVAS_PATH_SEPARATOR); + strcat(res, end); + return res; +} + +static Glyphs_Request * +_glyphs_request_create(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs) +{ + char *fullname; + Glyphs_Request *req = calloc(1, sizeof(*req)); + + if (sourcelen == 0) + source = NULL; + if (namelen == 0) + name = NULL; + + fullname = _file_path_join(source, name); + req->fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size, + rend_flags, dpi); + free(fullname); + if (!req->fe) + { + ERR("No font entry found: source %s, name %s, rendflags: %d, hint: %d," + " size: %d, dpi: %d", source, name, rend_flags, hint, size, dpi); + free(req); + return NULL; + } + + req->client = client; + + req->nglyphs = nglyphs; + req->current = 0; + req->glyphs = glyphs; + req->hint = hint; + + return req; +} + +static void +_glyphs_request_free(Glyphs_Request *req) +{ + free(req->glyphs); + free(req->render); + free(req->answer); + free(req); +} + +/* add glyphs that are already in cache to the "answers" array, and the ones + * that are not cached to the "render" array. + */ +static void +_glyphs_load_request_prepare(Glyphs_Request *req) +{ + int i, max; + req->nrender = 0; + Font_Entry *fe = req->fe; + + if (!fe) + { + ERR("No font entry for this request."); + return; + } + + // Won't render more than this number of glyphs + max = req->nglyphs - req->nanswer; + req->render = malloc(sizeof(*req->render) * max); + + for (i = req->current; i < req->nglyphs; i++) + { + Glyph_Entry *ge; + ge = fash_gl_find(fe->glyphs, req->glyphs[i]); + if (ge) + { + req->answer[req->nanswer++] = ge; + ge->fc->inuse++; + } + else + req->render[req->nrender++] = req->glyphs[i]; + } +} + +static void * +_glyphs_load_request_build(void *data, int *size) +{ + Glyphs_Request *req = data; + Slave_Msg_Font_Glyphs_Load *msg = NULL; + Font_Entry *fe = req->fe; + Font_Cache *fc; + + _glyphs_load_request_prepare(req); + + msg = calloc(1, sizeof(*msg)); + + msg->font.ftdata1 = fe->src->ft; + msg->font.ftdata2 = fe->ft; + msg->font.hint = req->hint; + msg->font.rend_flags = fe->rend_flags; + msg->glyphs.nglyphs = req->nrender; + msg->glyphs.glyphs = req->render; + + // Trying to reuse last filled cache. + fc = fe->last_cache; + if (fc) + { + msg->cache.shm = fc->shm; + msg->cache.usage = fc->usage; + msg->cache.nglyphs = fc->nglyphs; + } + +#ifdef DEBUG_LOAD_TIME + gettimeofday(&fe->load_start, NULL); +#endif + + return msg; +} + +static void +_glyphs_load_request_free(void *msg, void *data) +{ + _glyphs_request_free(data); + free(msg); +} + +static void +_glyphs_load_request_response(Client *client, void *data, void *resp, unsigned int rid) +{ + Glyphs_Request *req = data; + Slave_Msg_Font_Glyphs_Loaded *msg = resp; + Font_Entry *fe = req->fe; + Font_Cache *fc = NULL; + int i = 0; + + if (fe->last_cache && fe->last_cache->shm == msg->caches[0]->shm) + fc = fe->last_cache; + + while (i < msg->ncaches) + { + int j; + Slave_Msg_Font_Cache *c = msg->caches[i++]; + + if (!fc) + { + fc = malloc(sizeof(*fc)); + fe->caches = eina_inlist_append(fe->caches, EINA_INLIST_GET(fc)); + fe->last_cache = fc; + fc->fe = fe; + fc->shm = c->shm; + fc->glyphs = NULL; + fc->nglyphs = 0; + fc->inuse = 0; + font_shm_lru = eina_list_append(font_shm_lru, fc); + font_mem_usage += _font_shm_size_get(fc); + } + fc->usage = c->usage; + for (j = 0; j < c->nglyphs; j++) + { + Glyph_Entry *gl = malloc(sizeof(*gl)); + gl->fe = fe; + gl->fc = fc; + gl->index = c->glyphs[j].index; + gl->offset = c->glyphs[j].offset; + gl->size = c->glyphs[j].size; + gl->rows = c->glyphs[j].rows; + gl->width = c->glyphs[j].width; + gl->pitch = c->glyphs[j].pitch; + gl->num_grays = c->glyphs[j].num_grays; + gl->pixel_mode = c->glyphs[j].pixel_mode; + font_mem_usage += sizeof(*gl); + fc->glyphs = eina_inlist_append(fc->glyphs, EINA_INLIST_GET(gl)); + fc->nglyphs++; + fash_gl_add(fe->glyphs, gl->index, gl); + req->answer[req->nanswer++] = gl; + gl->fc->inuse++; + } + + free(c); // FIXME: We are freeing this here because we only do a + // simple free on the response message. Later we need to + // setup a free callback for the slave response. + fc = NULL; + } + +#ifdef DEBUG_LOAD_TIME + int load_time; + gettimeofday(&fe->load_finish, NULL); + load_time = _timeval_sub(&fe->load_finish, &fe->load_start); + fe->gl_load_time += load_time; +#endif + + _glyphs_loaded_send(req, rid); + _font_shm_lru_flush(); +} + +static void +_glyphs_load_request_failed(Client *client, void *data, Error_Type error, unsigned int rid) +{ +} + +static Font_Request_Funcs _glyphs_load_funcs = { + .msg_create = (Font_Request_Msg_Create)_glyphs_load_request_build, + .msg_free = (Font_Request_Msg_Free)_glyphs_load_request_free, + .response = (Font_Request_Response)_glyphs_load_request_response, + .error = (Font_Request_Error)_glyphs_load_request_failed +}; + +static Eina_Bool +_font_entry_stats_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata) +{ + Font_Entry *fe = data; + Msg_Stats *msg = fdata; + Font_Cache *fc; + int load_time; + int nrefs = eina_list_count(fe->base.references); + + // accounting size + EINA_INLIST_FOREACH(fe->caches, fc) + { + /* This is not real requested usage, but an approximation. We don't + * know how many times each glyph would be used by each client, but + * assume that a similar set of glyphs from a given font would be used + * by each client, thus counting them one time per client referencing + * them. + */ + msg->fonts.requested_usage += fc->usage * nrefs; + msg->fonts.real_usage += cserve2_shm_size_get(fc->shm); + } + +#ifdef DEBUG_LOAD_TIME + // accounting fonts load time + load_time = _timeval_sub(&fe->base.load_finish, &fe->base.load_start); + msg->fonts.fonts_load += load_time; + if (fe->caches) msg->fonts.fonts_used_load += load_time; + + // accounting glyphs load time + msg->fonts.glyphs_load += fe->gl_load_time; +#endif + + return EINA_TRUE; +} + +static void +_cserve2_cache_image_stats_get(Msg_Stats *msg) +{ +} + +static void +_cserve2_cache_font_stats_get(Msg_Stats *msg) +{ + eina_hash_foreach(font_entries, _font_entry_stats_cb, msg); +} + +struct _debug_info +{ + unsigned int size; + unsigned int nfonts; +}; + +static Eina_Bool +_font_entry_debug_size_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata) +{ + struct _debug_info *di = fdata; + unsigned int size = di->size; + Font_Entry *fe = data; + Font_Cache *fc; + unsigned int intsize = sizeof(unsigned int); + + // filelen + size += intsize; + + // file + if (fe->src->file) + size += strlen(fe->src->file) + 1; + + // namelen + size += intsize; + + // name + if (fe->src->name) + size += strlen(fe->src->name) + 1; + + // rend_flags, size, dpi + size += 3 * intsize; + + // unused + size += intsize; + + // ncaches + size += intsize; + + EINA_INLIST_FOREACH(fe->caches, fc) + { + Glyph_Entry *gl; + + // shmnamelen + shmname + size += intsize; + size += strlen(cserve2_shm_name_get(fc->shm)) + 1; + + // size + usage + size += 2 * intsize; + + // nglyphs + size += intsize; + + EINA_INLIST_FOREACH(fc->glyphs, gl) + { + // index, offset, size + size += 3 * intsize; + + // rows, width, pitch + size += 3 * intsize; + + // num_grays, pixel_mode + size += 2 * intsize; + } + } + + di->size = size; + di->nfonts++; + + return EINA_TRUE; +} + +static Eina_Bool +_font_entry_debug_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata) +{ + char *buf = fdata; + Font_Entry *fe = data; + Font_Cache *fc; + unsigned int len; + unsigned int unused; + unsigned int ncaches; + unsigned int intsize = sizeof(unsigned int); + + // filelen + file + len = 0; + if (fe->src->file) + len = strlen(fe->src->file) + 1; + memcpy(buf, &len, intsize); + buf += intsize; + memcpy(buf, fe->src->file, len); + buf += len; + + // namelen + name + len = 0; + if (fe->src->name) + len = strlen(fe->src->name) + 1; + memcpy(buf, &len, intsize); + buf += intsize; + memcpy(buf, fe->src->name, len); + buf += len; + + // rend_flags, size, dpi + memcpy(buf, &fe->rend_flags, intsize); + buf += intsize; + memcpy(buf, &fe->size, intsize); + buf += intsize; + memcpy(buf, &fe->dpi, intsize); + buf += intsize; + + // unused + unused = fe->unused; + memcpy(buf, &unused, intsize); + buf += intsize; + + // ncaches + ncaches = eina_inlist_count(fe->caches); + memcpy(buf, &ncaches, intsize); + buf += intsize; + + EINA_INLIST_FOREACH(fe->caches, fc) + { + Glyph_Entry *gl; + const char *shmname; + unsigned int shmsize; + + // shmnamelen + shmname + shmname = cserve2_shm_name_get(fc->shm); + len = strlen(shmname) + 1; + memcpy(buf, &len, intsize); + buf += intsize; + memcpy(buf, shmname, len); + buf += len; + + // size, usage, nglyphs + shmsize = cserve2_shm_size_get(fc->shm); + memcpy(buf, &shmsize, intsize); + buf += intsize; + memcpy(buf, &fc->usage, intsize); + buf += intsize; + memcpy(buf, &fc->nglyphs, intsize); + buf += intsize; + + EINA_INLIST_FOREACH(fc->glyphs, gl) + { + // index, offset, size + memcpy(buf, &gl->index, intsize); + buf += intsize; + memcpy(buf, &gl->offset, intsize); + buf += intsize; + memcpy(buf, &gl->size, intsize); + buf += intsize; + + // rows, width, pitch + memcpy(buf, &gl->rows, intsize); + buf += intsize; + memcpy(buf, &gl->width, intsize); + buf += intsize; + memcpy(buf, &gl->pitch, intsize); + buf += intsize; + + // num_grays, pixel_mode + memcpy(buf, &gl->num_grays, intsize); + buf += intsize; + memcpy(buf, &gl->pixel_mode, intsize); + buf += intsize; + } + } + + return EINA_TRUE; +} + +static void * +_cserve2_cache_font_debug(unsigned int rid, unsigned int *size) +{ + Msg_Font_Debug msg; + char *buf, *pos; + struct _debug_info di; + di.size = sizeof(msg); + + memset(&msg, 0, sizeof(msg)); + + msg.base.type = CSERVE2_FONT_DEBUG; + msg.base.rid = rid; + + // First calculate how much size is needed for this message: + + // nfonts + di.size += sizeof(unsigned int); + + // size needed for each font entry + eina_hash_foreach(font_entries, _font_entry_debug_size_cb, &di); + + // Now really create the message + buf = malloc(di.size); + pos = buf; + + // msg base + memcpy(buf, &msg, sizeof(msg)); + pos += sizeof(msg); + + // nfonts + memcpy(pos, &di.nfonts, sizeof(unsigned int)); + pos += sizeof(unsigned int); + + eina_hash_foreach(font_entries, _font_entry_debug_cb, pos); + + *size = di.size; + return buf; +} + int cserve2_cache_file_open(Client *client, unsigned int client_file_id, const char *path, const char *key, unsigned int rid) { @@ -1445,50 +2287,165 @@ cserve2_cache_image_unload(Client *client, unsigned int client_image_id) } int -cserve2_cache_font_load(Client *client, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int hint, unsigned int size, unsigned int dpi, unsigned int rid) +cserve2_cache_font_load(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int rid) { Reference *ref; Font_Source *fs; - Font_Entry *fe = _cserve2_font_entry_find(name, namelen, size, - rend_flags, hint, dpi); + Font_Entry *fe; + char *fullname; + if (sourcelen == 0) + source = NULL; + if (namelen == 0) + name = NULL; + + fullname = _file_path_join(source, name); + fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size, + rend_flags, dpi); if (fe) { - DBG("found font entry %s, rendflags: %d, hint: %d, size: %d, dpi: %d", - name, rend_flags, hint, size, dpi); + DBG("found font entry %s, rendflags: %d, size: %d, dpi: %d", + name, rend_flags, size, dpi); ref = _entry_reference_add((Entry *)fe, client, 0); client->fonts.referencing = eina_list_append( client->fonts.referencing, ref); - _font_loaded_send(client, fe, rid); + fe->unused = EINA_FALSE; + + if (fe->request) + cserve2_request_waiter_add(fe->request, rid, client); + else + _font_loaded_send(client, rid); + free(fullname); return 0; } fe = calloc(1, sizeof(*fe)); fe->rend_flags = rend_flags; - fe->hint = hint; fe->size = size; fe->dpi = dpi; fe->base.type = CSERVE2_FONT_ENTRY; + fe->glyphs = fash_gl_new(_glyph_free_cb); + ref = _entry_reference_add((Entry *)fe, client, 0); + client->fonts.referencing = eina_list_append( + client->fonts.referencing, ref); + fe->unused = EINA_FALSE; - fs = _cserve2_font_source_find(name); + fs = _cserve2_font_source_find(fullname); if (!fs) { fs = calloc(1, sizeof(*fs)); - fs->name = eina_stringshare_add_length(name, namelen); - fs->file = eina_stringshare_ref(fs->name); - eina_hash_direct_add(font_sources, fs->name, fs); + if (source) + { + fs->key = eina_stringshare_add(fullname); + fs->name = eina_stringshare_add_length(name, namelen); + fs->file = eina_stringshare_add_length(source, sourcelen); + } + else + { + fs->file = eina_stringshare_add_length(name, namelen); + fs->key = eina_stringshare_ref(fs->file); + } + eina_hash_direct_add(font_sources, fs->key, fs); } - fe->src = fs; fs->references++; + DBG("adding FONT_LOAD '%s' request.", fs->name); fe->request = cserve2_request_add(CSERVE2_REQ_FONT_LOAD, rid, client, &_font_load_funcs, fe); eina_hash_direct_add(font_entries, fe, fe); + free(fullname); + + return 0; +} + +int +cserve2_cache_font_unload(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int rid) +{ + Font_Entry *fe; + char *fullname; + + if (sourcelen == 0) + source = NULL; + if (namelen == 0) + name = NULL; + + fullname = _file_path_join(source, name); + fe = _cserve2_font_entry_find(fullname, strlen(fullname) + 1, size, + rend_flags, dpi); + free(fullname); + + if (!fe) + { + ERR("Unreferencing font not found: '%s:%s'.", source, name); + return -1; + } + + _font_entry_reference_del(client, fe); + + return 0; +} + +int +cserve2_cache_font_glyphs_load(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs, unsigned int rid) +{ + Glyphs_Request *req; + + req = _glyphs_request_create(client, source, sourcelen, name, namelen, + hint, rend_flags, size, dpi, glyphs, nglyphs); + if (!req) + { + free(glyphs); + return -1; + } + + if (_glyphs_request_check(req)) + { + INF("Glyphs already loaded. Sending answer."); + _glyphs_loaded_send(req, rid); + _glyphs_request_free(req); + } + else + { + cserve2_request_add(CSERVE2_REQ_FONT_GLYPHS_LOAD, rid, + client, &_glyphs_load_funcs, req); + } + return 0; +} + +int +cserve2_cache_font_glyphs_used(Client *client, const char *source, unsigned int sourcelen, const char *name, unsigned int namelen, unsigned int hint, unsigned int rend_flags, unsigned int size, unsigned int dpi, unsigned int *glyphs, unsigned int nglyphs, unsigned int rid __UNUSED__) +{ + Glyphs_Group *gg; + Eina_List *groups; + Glyphs_Request *req; + + DBG("Received report of used glyphs from client %d", client->id); + req = _glyphs_request_create(client, source, sourcelen, name, namelen, + hint, rend_flags, size, dpi, glyphs, nglyphs); + if (!req) + { + free(glyphs); + return 0; + } + + _glyphs_load_request_prepare(req); + groups = _glyphs_group_create(req); + + // Promote SHMs which are still cached and in use + // TODO: We can use later the information from request_prepare to preload + // glyphs which are not cached anymore, but are in use on the client. + EINA_LIST_FREE(groups, gg) + { + _font_shm_promote(gg->fc); + eina_list_free(gg->glyphs); + } + + _glyphs_request_free(req); return 0; } @@ -1522,3 +2479,36 @@ cserve2_cache_requests_response(Slave_Command type, void *msg, void *data) req->entry->request = NULL; free(req); } + +void +cserve2_cache_stats_get(Client *client, unsigned int rid) +{ + Msg_Stats msg; + int size; + + memset(&msg, 0, sizeof(msg)); + + msg.base.type = CSERVE2_STATS; + msg.base.rid = rid; + + _cserve2_cache_image_stats_get(&msg); + _cserve2_cache_font_stats_get(&msg); + + size = sizeof(msg); + cserve2_client_send(client, &size, sizeof(size)); + cserve2_client_send(client, &msg, size); +} + +void +cserve2_cache_font_debug(Client *client, unsigned int rid) +{ + void *msg; + unsigned int size; + + msg = _cserve2_cache_font_debug(rid, &size); + + cserve2_client_send(client, &size, sizeof(size)); + cserve2_client_send(client, msg, size); + + free(msg); +} diff --git a/legacy/evas/src/bin/evas_cserve2_client.c b/legacy/evas/src/bin/evas_cserve2_client.c index d8add9cc3d..b2a33acd61 100644 --- a/legacy/evas/src/bin/evas_cserve2_client.c +++ b/legacy/evas/src/bin/evas_cserve2_client.c @@ -11,7 +11,6 @@ #include "evas_cserve2.h" -static const char *SOCK_PATH = "/tmp/cserve2.socket"; static unsigned int _rid_count = 0; static struct sockaddr_un socket_local; diff --git a/legacy/evas/src/bin/evas_cserve2_fonts.c b/legacy/evas/src/bin/evas_cserve2_fonts.c index 36dc140b28..cb5355bd02 100644 --- a/legacy/evas/src/bin/evas_cserve2_fonts.c +++ b/legacy/evas/src/bin/evas_cserve2_fonts.c @@ -2,23 +2,49 @@ # include "config.h" #endif +#ifdef BUILD_FONT_LOADER_EET +#include +#endif + #include #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_SIZES_H +#include FT_TYPES_H #include FT_MODULE_H +#include FT_OUTLINE_H #include "evas_cserve2.h" +#define CACHESIZE 4 * 1024 + +/* The tangent of the slant angle we do on runtime. This value was + * retrieved from engines/common/evas_font.h */ +#define _EVAS_FONT_SLANT_TAN 0.221694663 + +#define CHECK_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +#define MIN_GLYPHS 50 +#define MAX_CACHE_SIZE 1 * 1024 * 1024 // 1MB + +#define EVAS_FONT_ROUND_26_6_TO_INT(x) \ + (((x + 0x20) & -0x40) >> 6) + static FT_Library cserve2_ft_lib = 0; static int initialised = 0; +typedef struct _Font_Info Font_Info; +typedef struct _Font_Source_Info Font_Source_Info; + struct _Font_Info { + Font_Source_Info *fsi; FT_Size size; - int real_size; + int real_size; // this is probably useless, not used even on client + int fsize; + int dpi; int max_h; unsigned int runtime_rend; + int shmsize; }; struct _Font_Source_Info @@ -26,11 +52,11 @@ struct _Font_Source_Info FT_Face face; int orig_upem; int current_size; + int current_dpi; + void *data; + int datasize; }; -typedef struct _Font_Info Font_Info; -typedef struct _Font_Source_Info Font_Source_Info; - static void * _font_slave_error_send(Error_Type error) { @@ -40,29 +66,80 @@ _font_slave_error_send(Error_Type error) return e; } +static void +_font_slave_size_use(Font_Info *fi) +{ + Font_Source_Info *fsi = fi->fsi; +// if ((fsi->current_size != fi->fsize) +// || (fsi->current_dpi != fi->dpi)) + { + FT_Activate_Size(fi->size); + fsi->current_size = fi->fsize; + fsi->current_dpi = fi->dpi; + } +} + static Font_Source_Info * -_font_slave_source_load(const char *file) +_font_slave_source_load(const char *file, const char *name) { int error; - Font_Source_Info *fsi = malloc(sizeof(*fsi)); + Font_Source_Info *fsi = calloc(1, sizeof(*fsi)); - error = FT_New_Face(cserve2_ft_lib, file, 0, &(fsi->face)); - if (error) + if (!name) { - free(fsi); - return NULL; + error = FT_New_Face(cserve2_ft_lib, file, 0, &(fsi->face)); + if (error) + { + free(fsi); + return NULL; + } } +#ifdef BUILD_FONT_LOADER_EET + else + { + Eet_File *ef; + void *fdata; + int fsize = 0; + + ef = eet_open(file, EET_FILE_MODE_READ); + if (!ef) + { + free(fsi); + return NULL; + } + fdata = eet_read(ef, name, &fsize); + eet_close(ef); + if (!fdata) + { + free(fsi); + return NULL; + } + fsi->data = fdata; + fsi->datasize = fsize; + + error = FT_New_Memory_Face(cserve2_ft_lib, fsi->data, fsi->datasize, + 0, &(fsi->face)); + if (error) + { + free(fsi->data); + free(fsi); + return NULL; + } + } +#endif error = FT_Select_Charmap(fsi->face, ft_encoding_unicode); if (error) { FT_Done_Face(fsi->face); + free(fsi->data); free(fsi); return NULL; } fsi->orig_upem = fsi->face->units_per_EM; fsi->current_size = 0; + fsi->current_dpi = 0; return fsi; } @@ -79,46 +156,55 @@ _font_slave_int_load(const Slave_Msg_Font_Load *msg, Font_Source_Info *fsi) if (!error) FT_Activate_Size(fi->size); + fi->fsize = msg->size; + fi->dpi = msg->dpi; fi->real_size = msg->size * 64; + fi->fsi = fsi; error = FT_Set_Char_Size(fsi->face, 0, fi->real_size, msg->dpi, msg->dpi); if (error) - { - fi->real_size = msg->size; - error = FT_Set_Pixel_Sizes(fsi->face, 0, fi->real_size); - } + error = FT_Set_Pixel_Sizes(fsi->face, 0, fi->real_size); if (error) { - int i; + int i, maxd = 0x7fffffff; int chosen_size = 0; - int chosen_width = 0; + int chosen_size2 = 0; for (i = 0; i < fsi->face->num_fixed_sizes; i++) { - int s; - int d, cd; + int s, cd; - s = fsi->face->available_sizes[i].height; - cd = chosen_size - msg->size; + s = fsi->face->available_sizes[i].size; + cd = chosen_size - fi->real_size; if (cd < 0) cd = -cd; - d = s - msg->size; - if (d < 0) d = -d; - if (d < cd) + if (cd < maxd) { - chosen_width = fsi->face->available_sizes[i].width; + maxd = cd; chosen_size = s; + chosen_size2 = fsi->face->available_sizes[i].y_ppem; + if (maxd == 0) break; } - if (d == 0) break; } fi->real_size = chosen_size; - error = FT_Set_Pixel_Sizes(fsi->face, chosen_width, fi->real_size); + error = FT_Set_Pixel_Sizes(fsi->face, 0, fi->real_size); if (error) { - ERR("Could not choose the font size for font: '%s'.", msg->name); - FT_Done_Size(fi->size); - free(fi); - return NULL; + error = FT_Set_Char_Size(fsi->face, 0, fi->real_size, fi->dpi, fi->dpi); + if (error) + { + /* hack around broken fonts */ + fi->real_size = (chosen_size2 / 64) * 60; + error = FT_Set_Char_Size(fsi->face, 0, fi->real_size, fi->dpi, fi->dpi); + if (error) + { + ERR("Could not choose the font size for font: '%s:%s'.", + msg->file, msg->name); + FT_Done_Size(fi->size); + free(fi); + return NULL; + } + } } } @@ -159,11 +245,12 @@ _font_slave_load(const void *cmddata, void *data __UNUSED__) Font_Source_Info *fsi; Font_Info *fi; + DBG("slave received FONT_LOAD: '%s'", msg->name); fsi = msg->ftdata1; /* Loading Font Source */ if (!fsi) - fsi = _font_slave_source_load(msg->file); + fsi = _font_slave_source_load(msg->file, msg->name); // FIXME: Return correct error message if (!fsi) @@ -175,8 +262,8 @@ _font_slave_load(const void *cmddata, void *data __UNUSED__) fi = _font_slave_int_load(msg, fsi); if (!fi) { - FT_Done_Face(fsi->face); - free(fsi); + if (!msg->ftdata1) + cserve2_font_source_ft_free(fsi); return NULL; } @@ -187,6 +274,241 @@ _font_slave_load(const void *cmddata, void *data __UNUSED__) return response; } +static Shm_Handle * +_font_slave_memory_alloc(Font_Info *fi) +{ + Shm_Handle *shm = cserve2_shm_request(fi->shmsize); + + return shm; +} + +/* This function will load the "index" glyph to the glyph slot of the font. + * In order to use or render it, one should access it from the glyph slot, + * or get the glyph using FT_Get_Glyph(). + */ +static Eina_Bool +_font_slave_glyph_load(Font_Info *fi, unsigned int index, unsigned int hint) +{ + Font_Source_Info *fsi = fi->fsi; + FT_Error error; + const FT_Int32 hintflags[3] = + { FT_LOAD_NO_HINTING, FT_LOAD_FORCE_AUTOHINT, FT_LOAD_NO_AUTOHINT }; + static FT_Matrix transform = {0x10000, _EVAS_FONT_SLANT_TAN * 0x10000, + 0x00000, 0x10000}; + + error = FT_Load_Glyph(fsi->face, index, + FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP | + hintflags[hint]); + if (error) + { + ERR("Could not load glyph %d", index); + return EINA_FALSE; + } + + /* Transform the outline of Glyph according to runtime_rend. */ + if (fi->runtime_rend & FONT_REND_SLANT) + FT_Outline_Transform(&fsi->face->glyph->outline, &transform); + /* Embolden the outline of Glyph according to rundtime_rend. */ + if (fi->runtime_rend & FONT_REND_WEIGHT) + FT_Outline_Embolden(&fsi->face->glyph->outline, + (fsi->face->size->metrics.x_ppem * 5 * 64) / + 100); + + return EINA_TRUE; +} + +/* This function will render the glyph currently in the glyph slot into the + * given Font Cache. + */ +static Eina_Bool +_font_slave_glyph_render(Font_Info *fi, Slave_Msg_Font_Cache *c, unsigned int index) +{ + Font_Source_Info *fsi = fi->fsi; + unsigned int glyphsize; + char *cachedata = cserve2_shm_map(c->shm); + FT_Glyph glyph; + FT_BitmapGlyph bglyph; + + FT_Get_Glyph(fsi->face->glyph, &glyph); + FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); + bglyph = (FT_BitmapGlyph)glyph; + + glyphsize = bglyph->bitmap.pitch * bglyph->bitmap.rows; + + if (c->usage + glyphsize > cserve2_shm_size_get(c->shm)) + { + FT_Done_Glyph(glyph); + return EINA_FALSE; + } + + memcpy(cachedata + c->usage, bglyph->bitmap.buffer, glyphsize); + + // TODO: Check if we have problems with alignment + c->glyphs[c->nglyphs].index = index; + c->glyphs[c->nglyphs].offset = c->usage; + c->glyphs[c->nglyphs].size = glyphsize; + c->glyphs[c->nglyphs].rows = bglyph->bitmap.rows; + c->glyphs[c->nglyphs].width = bglyph->bitmap.width; + c->glyphs[c->nglyphs].pitch = bglyph->bitmap.pitch; + c->glyphs[c->nglyphs].num_grays = bglyph->bitmap.num_grays; + c->glyphs[c->nglyphs].pixel_mode = bglyph->bitmap.pixel_mode; + c->usage += glyphsize; + c->nglyphs++; + + FT_Done_Glyph(glyph); + + return EINA_TRUE; +} + +static void +_font_slave_int_metrics_get(Font_Info *fi, unsigned int hint, unsigned int c, int *width, int *height, int *depth) +{ + unsigned int idx; + Font_Source_Info *fsi = fi->fsi; + FT_BBox outbox; + FT_Glyph glyph; + + idx = FT_Get_Char_Index(fsi->face, c); + if (!idx) + goto end; + + if (!_font_slave_glyph_load(fi, idx, hint)) + goto end; + + FT_Get_Glyph(fsi->face->glyph, &glyph); + FT_Glyph_Get_CBox(glyph, + ((hint == 0) ? FT_GLYPH_BBOX_UNSCALED : + FT_GLYPH_BBOX_GRIDFIT), + &outbox); + if (width) *width = EVAS_FONT_ROUND_26_6_TO_INT(outbox.xMax - outbox.xMin); + if (height) + *height = EVAS_FONT_ROUND_26_6_TO_INT(outbox.yMax - outbox.xMin); + if (depth) *depth = 1; // FIXME: Do we need to check this? + FT_Done_Glyph(glyph); + return; + +end: + if (width) *width = 0; + if (height) *height = 0; + if (depth) *depth = 0; +} + +static unsigned int +_font_slave_int_shm_prev_calculate(unsigned int size, unsigned int nglyphs) +{ + unsigned int average = size / nglyphs; + unsigned int newsize; + + newsize = MIN_GLYPHS * average; + newsize = cserve2_shm_size_normalize(newsize); + + if (newsize > MAX_CACHE_SIZE) + return MAX_CACHE_SIZE; + + return newsize; +} + +static unsigned int +_font_slave_int_shm_calculate(Font_Info *fi, unsigned int hint) +{ + const char *c; + int i; + int size = 0; + int average; + + for (c = CHECK_CHARS, i = 0; *c != '\0'; c++, i++) + { + int w, h, depth; + _font_slave_int_metrics_get(fi, hint, *c, &w, &h, &depth); + size += w * h * depth; + } + + average = size / i; // average glyph size + size = MIN_GLYPHS * average; + + size = cserve2_shm_size_normalize(size); + + if (size > MAX_CACHE_SIZE) + return MAX_CACHE_SIZE; // Assumes no glyph will be bigger than this + + return size; +} + +static Slave_Msg_Font_Glyphs_Loaded * +_font_slave_glyphs_load(const void *cmddata, void *data __UNUSED__) +{ + const Slave_Msg_Font_Glyphs_Load *msg = cmddata; + Slave_Msg_Font_Glyphs_Loaded *response; + Font_Info *fi; + unsigned int i; + unsigned int total_glyphs; + Eina_List *caches = NULL; + Slave_Msg_Font_Cache *c = NULL; + + fi = msg->font.ftdata2; + + _font_slave_size_use(fi); + + if (msg->cache.shm) + { + c = malloc(sizeof(*c) + sizeof(Slave_Msg_Glyph) * + (msg->glyphs.nglyphs)); + c->nglyphs = 0; + c->glyphs = (void *)(c + 1); + c->shm = msg->cache.shm; + c->usage = msg->cache.usage; + caches = eina_list_append(caches, c); + total_glyphs = msg->cache.nglyphs; + } + + if (!fi->shmsize) + fi->shmsize = _font_slave_int_shm_calculate(fi, msg->font.hint); + + i = 0; + + while (i < msg->glyphs.nglyphs) + { + Eina_Bool r = EINA_TRUE; + + if (!c) + { + Shm_Handle *shm; + shm = _font_slave_memory_alloc(fi); + c = malloc(sizeof(*c) + sizeof(Slave_Msg_Glyph) * + (msg->glyphs.nglyphs - i)); + c->nglyphs = 0; + c->glyphs = (void *)(c + 1); + c->shm = shm; + c->usage = 0; + caches = eina_list_append(caches, c); + total_glyphs = 0; + } + + if (_font_slave_glyph_load(fi, msg->glyphs.glyphs[i], msg->font.hint)) + r = _font_slave_glyph_render(fi, c, msg->glyphs.glyphs[i]); + if (!r) // SHM is full + { + fi->shmsize = _font_slave_int_shm_prev_calculate + (c->usage, total_glyphs); + c = NULL; + continue; + } + i++; + total_glyphs++; + } + + response = malloc(sizeof(*response) + + sizeof(c) * eina_list_count(caches)); + response->ncaches = eina_list_count(caches); + response->caches = (void *)(response + 1); + + i = 0; + EINA_LIST_FREE(caches, c) + response->caches[i++] = c; + + return response; +} + void * cserve2_font_slave_cb(Slave_Thread_Data *sd __UNUSED__, Slave_Command *cmd, const void *cmddata, void *data) { @@ -198,7 +520,7 @@ cserve2_font_slave_cb(Slave_Thread_Data *sd __UNUSED__, Slave_Command *cmd, cons response = _font_slave_load(cmddata, data); break; case FONT_GLYPHS_LOAD: - // command for FONT_GLYPHS_LOAD + response = _font_slave_glyphs_load(cmddata, data); break; default: ERR("Invalid command for font slave: %d", *cmd); @@ -224,6 +546,10 @@ cserve2_font_init(void) error = FT_Init_FreeType(&cserve2_ft_lib); if (error) return; + +#ifdef BUILD_FONT_LOADER_EET + eet_init(); +#endif } void @@ -239,4 +565,27 @@ cserve2_font_shutdown(void) FT_Done_FreeType(cserve2_ft_lib); cserve2_ft_lib = 0; + +#ifdef BUILD_FONT_LOADER_EET + eet_shutdown(); +#endif +} + +void +cserve2_font_source_ft_free(void *fontsource) +{ + Font_Source_Info *fsi = fontsource; + + FT_Done_Face(fsi->face); + free(fsi->data); + free(fsi); +} + +void +cserve2_font_ft_free(void *fontinfo) +{ + Font_Info *fi = fontinfo; + + FT_Done_Size(fi->size); + free(fi); } diff --git a/legacy/evas/src/bin/evas_cserve2_main.c b/legacy/evas/src/bin/evas_cserve2_main.c index 582fbe034a..a0c909a108 100644 --- a/legacy/evas/src/bin/evas_cserve2_main.c +++ b/legacy/evas/src/bin/evas_cserve2_main.c @@ -33,13 +33,6 @@ static Eina_Hash *client_list = NULL; static Eina_Inlist *slaves_idle = NULL; static Eina_Inlist *slaves_working = NULL; -struct _Glyph_Request { - unsigned int index; - unsigned int offset; -}; - -typedef struct _Glyph_Request Glyph_Request; - void cserve2_client_error_send(Client *client, unsigned int rid, int error_code) { @@ -270,52 +263,83 @@ static void _cserve2_client_font_load(Client *client) { Msg_Font_Load *msg = (Msg_Font_Load *)client->msg.buf; - char name[PATH_MAX]; + char name[PATH_MAX], source[PATH_MAX], *buf; - memcpy(name, msg + 1, msg->pathlen); + buf = ((char *)msg) + sizeof(*msg); + memcpy(source, buf, msg->sourcelen); + buf += msg->sourcelen; + memcpy(name, buf, msg->pathlen); INF("Received %s command: RID=%d", (msg->base.type == CSERVE2_FONT_LOAD) ? "FONT_LOAD" : "FONT_UNLOAD", msg->base.rid); - INF("Font: %s, rend_flags: %d, hint: %d, size: %d, dpi: %d", - name, msg->rend_flags, msg->hint, msg->size, msg->dpi); + INF("Font: %s, rend_flags: %d, size: %d, dpi: %d", + name, msg->rend_flags, msg->size, msg->dpi); - cserve2_cache_font_load(client, name, msg->pathlen, msg->rend_flags, - msg->hint, msg->size, msg->dpi, msg->base.rid); + if (msg->base.type == CSERVE2_FONT_LOAD) + cserve2_cache_font_load(client, source, msg->sourcelen, name, + msg->pathlen, msg->rend_flags, msg->size, + msg->dpi, msg->base.rid); + else + cserve2_cache_font_unload(client, source, msg->sourcelen, name, + msg->pathlen, msg->rend_flags, msg->size, + msg->dpi, msg->base.rid); } static void _cserve2_client_font_glyphs_request(Client *client) { Msg_Font_Glyphs_Request *msg = (Msg_Font_Glyphs_Request *)client->msg.buf; - char fontpath[PATH_MAX]; - Glyph_Request *glyphs; - unsigned int i; - const char *bufpos = client->msg.buf; + char source[PATH_MAX], fontpath[PATH_MAX], *buf; + unsigned int *glyphs; - memcpy(fontpath, msg + 1, msg->pathlen); + buf = ((char *)msg) + sizeof(*msg); + memcpy(source, buf, msg->sourcelen); + buf += msg->sourcelen; + memcpy(fontpath, buf, msg->pathlen); + buf += msg->pathlen; - bufpos = bufpos + sizeof(msg) + msg->pathlen; glyphs = malloc(sizeof(*glyphs) * msg->nglyphs); - - for (i = 0; i < msg->nglyphs; i++) - { - memcpy(&glyphs[i], bufpos, sizeof(*glyphs)); - bufpos += sizeof(*glyphs); - } + memcpy(glyphs, buf, sizeof(*glyphs) * msg->nglyphs); if (msg->base.type == CSERVE2_FONT_GLYPHS_LOAD) { INF("Received CSERVE2_FONT_GLYPHS_LOAD command: RID=%d", msg->base.rid); + cserve2_cache_font_glyphs_load(client, source, msg->sourcelen, + fontpath, msg->pathlen, + msg->hint, msg->rend_flags, msg->size, + msg->dpi, glyphs, msg->nglyphs, + msg->base.rid); } else { INF("Received CSERVE2_FONT_GLYPHS_USED command: RID=%d", msg->base.rid); + /* + cserve2_cache_font_glyphs_used(client, source, msg->sourcelen, + fontpath, msg->pathlen, + msg->hint, msg->rend_flags, msg->size, + msg->dpi, glyphs, msg->nglyphs, + msg->base.rid); + */ } } +static void +_cserve2_client_stats_request(Client *client) +{ + Msg_Base *msg = (Msg_Base *)client->msg.buf; + cserve2_cache_stats_get(client, msg->rid); +} + +static void +_cserve2_client_font_debug_request(Client *client) +{ + Msg_Base *msg = (Msg_Base *)client->msg.buf; + cserve2_cache_font_debug(client, msg->rid); +} + void cserve2_command_run(Client *client, Message_Type type) { @@ -347,6 +371,12 @@ cserve2_command_run(Client *client, Message_Type type) case CSERVE2_FONT_GLYPHS_USED: _cserve2_client_font_glyphs_request(client); break; + case CSERVE2_STATS: + _cserve2_client_stats_request(client); + break; + case CSERVE2_FONT_DEBUG: + _cserve2_client_font_debug_request(client); + break; default: WRN("Unhandled message"); } diff --git a/legacy/evas/src/bin/evas_cserve2_requests.c b/legacy/evas/src/bin/evas_cserve2_requests.c index dd1dd07bf1..c0777e46b4 100644 --- a/legacy/evas/src/bin/evas_cserve2_requests.c +++ b/legacy/evas/src/bin/evas_cserve2_requests.c @@ -81,7 +81,7 @@ typedef struct _Waiter Waiter; struct _Request_Queue { Eina_Inlist *waiting; - Eina_Inlist *processing; + Eina_Inlist *processing; // TODO: Check if is there any use for this list. }; typedef struct _Request_Queue Request_Queue; @@ -89,6 +89,8 @@ typedef struct _Request_Queue Request_Queue; static Request_Queue *requests = NULL; // static Eina_List *processing = NULL; +static void _cserve2_requests_process(void); + static void _request_waiter_add(Font_Request *req, Client *client, unsigned int rid) { @@ -109,15 +111,17 @@ cserve2_request_add(Font_Request_Type type, unsigned int rid, Client *client, Fo req = NULL; + /* Check if this request was already being processed. */ EINA_INLIST_FOREACH(requests[type].processing, r) { - if (r->data == data) + if (r->data != data) continue; req = r; break; } + /* Check if this request was already waiting to be processed. */ if (!req) { EINA_INLIST_FOREACH(requests[type].waiting, r) @@ -130,22 +134,33 @@ cserve2_request_add(Font_Request_Type type, unsigned int rid, Client *client, Fo } } + /* create new request */ if (!req) { DBG("Add request for rid: %d", rid); req = malloc(sizeof(*req)); + req->type = type; req->data = data; req->waiters = NULL; req->processing = EINA_FALSE; + req->funcs = funcs; requests[type].waiting = eina_inlist_append(requests[type].waiting, EINA_INLIST_GET(req)); } _request_waiter_add(req, client, rid); + _cserve2_requests_process(); + return req; } +void +cserve2_request_waiter_add(Font_Request *req, unsigned int rid, Client *client) +{ + _request_waiter_add(req, client, rid); +} + void cserve2_request_cancel(Font_Request *req, Client *client, Error_Type err) { @@ -167,13 +182,13 @@ cserve2_request_cancel(Font_Request *req, Client *client, Error_Type err) // TODO: When we have speculative preload, there may be no waiters, // so we need a flag or something else to make things still load. - if (!req->waiters) + if ((!req->waiters) && (!req->processing)) { Eina_Inlist **reqlist = &requests[req->type].waiting; *reqlist = eina_inlist_remove(*reqlist, EINA_INLIST_GET(req)); // TODO: If the request is being processed, it can't be deleted. Must // be marked as delete_me instead. - req->funcs->msg_free(req->msg); + req->funcs->msg_free(req->msg, req->data); free(req); } @@ -195,9 +210,12 @@ cserve2_request_cancel_all(Font_Request *req, Error_Type err) free(w); } + if (req->processing) + return; + requests[req->type].waiting = eina_inlist_remove( requests[req->type].waiting, EINA_INLIST_GET(req)); - req->funcs->msg_free(req->msg); + req->funcs->msg_free(req->msg, req->data); free(req); } @@ -226,7 +244,7 @@ _cserve2_request_failed(Font_Request *req, Error_Type type) free(w); } - req->funcs->msg_free(req->msg); + req->funcs->msg_free(req->msg, req->data); requests[req->type].processing = eina_inlist_remove( requests[req->type].processing, EINA_INLIST_GET(req)); free(req); @@ -252,7 +270,7 @@ _slave_read_cb(Slave *s __UNUSED__, Slave_Command cmd, void *msg, void *data) free(w); } - req->funcs->msg_free(req->msg); + req->funcs->msg_free(req->msg, req->data); // FIXME: We shouldn't free this message directly, it must be freed by a // callback. free(msg); @@ -265,6 +283,8 @@ _slave_read_cb(Slave *s __UNUSED__, Slave_Command cmd, void *msg, void *data) idle = &_workers[sw->type].idle; *working = eina_list_remove(*working, sw); *idle = eina_list_append(*idle, sw); + + _cserve2_requests_process(); } static void @@ -338,6 +358,8 @@ _cserve2_request_dispatch(Slave_Worker *sw, Slave_Command ctype, Font_Request *r int size; char *slave_msg = req->funcs->msg_create(req->data, &size); + + DBG("dispatching message of type %d to slave.", req->type); if (!slave_msg) { ERR("Could not create slave message for request type %d.", req->type); @@ -352,8 +374,8 @@ _cserve2_request_dispatch(Slave_Worker *sw, Slave_Command ctype, Font_Request *r return EINA_TRUE; } -void -cserve2_requests_process(void) +static void +_cserve2_requests_process(void) { int rtype, j; @@ -366,7 +388,7 @@ cserve2_requests_process(void) for (j = 0; _request_match[j].rtype != CSERVE2_REQ_LAST; j++) { - if (_request_match[j].rtype == (unsigned int)j) + if (_request_match[j].rtype == rtype) { type = _request_match[j].stype; ctype = _request_match[j].ctype; @@ -387,7 +409,7 @@ cserve2_requests_process(void) idle = &_workers[type].idle; working = &_workers[type].working; - while (requests[j].waiting && + while (requests[rtype].waiting && (eina_list_count(*working) < max_workers)) { Slave_Worker *sw; diff --git a/legacy/evas/src/bin/evas_cserve2_shm.c b/legacy/evas/src/bin/evas_cserve2_shm.c index 36291f947c..5e60138b42 100644 --- a/legacy/evas/src/bin/evas_cserve2_shm.c +++ b/legacy/evas/src/bin/evas_cserve2_shm.c @@ -27,10 +27,30 @@ struct _Shm_Handle off_t image_offset; size_t map_size; size_t image_size; + int refcount; + void *data; }; static int id = 0; +size_t +cserve2_shm_size_normalize(size_t size) +{ + long pagesize; + size_t normalized; + + pagesize = sysconf(_SC_PAGESIZE); + if (pagesize < 1) + { + ERR("sysconf() reported weird value for PAGESIZE, assuming 4096."); + pagesize = 4096; + } + + normalized = ((size + pagesize - 1) / pagesize) * pagesize; + + return normalized; +} + Shm_Handle * cserve2_shm_request(size_t size) { @@ -38,7 +58,6 @@ cserve2_shm_request(size_t size) Shm_Handle *shm; char shmname[NAME_MAX]; size_t map_size; - long pagesize; int fd; map = calloc(1, sizeof(Shm_Mapping)); @@ -59,14 +78,7 @@ cserve2_shm_request(size_t size) } } while (fd == -1); - pagesize = sysconf(_SC_PAGESIZE); - if (pagesize < 1) - { - ERR("sysconf() reported weird value for PAGESIZE, assuming 4096."); - pagesize = 4096; - } - - map_size = ((size + pagesize - 1) / pagesize) * pagesize; + map_size = cserve2_shm_size_normalize(size); if (ftruncate(fd, map_size) == -1) { @@ -106,6 +118,9 @@ cserve2_shm_unref(Shm_Handle *shm) Shm_Mapping *map = shm->mapping; map->segments = eina_inlist_remove(map->segments, EINA_INLIST_GET(shm)); + + if (shm->data) + munmap(shm->data, shm->image_size); free(shm); if (map->segments) @@ -145,3 +160,36 @@ cserve2_shm_size_get(const Shm_Handle *shm) { return shm->image_size; } + +void * +cserve2_shm_map(Shm_Handle *shm) +{ + int fd; + const char *name; + + if (shm->refcount++) + return shm->data; + + name = cserve2_shm_name_get(shm); + + fd = shm_open(name, O_RDWR, S_IWUSR); + if (fd == -1) + return MAP_FAILED; + + shm->data = mmap(NULL, shm->image_size, PROT_WRITE, MAP_SHARED, + fd, shm->image_offset); + + close(fd); + + return shm->data; +} + +void +cserve2_shm_unmap(Shm_Handle *shm) +{ + if (--shm->refcount) + return; + + munmap(shm->data, shm->image_size); + shm->data = NULL; +} diff --git a/legacy/evas/src/bin/evas_cserve2_slave.c b/legacy/evas/src/bin/evas_cserve2_slave.c index b6e46101e5..a11c6351ec 100644 --- a/legacy/evas/src/bin/evas_cserve2_slave.c +++ b/legacy/evas/src/bin/evas_cserve2_slave.c @@ -187,8 +187,8 @@ error_send(int fd, Error_Type err) return response_send(fd, ERROR, &err, sizeof(Error_Type)); } -void * -cserve2_shm_map(const char *name, size_t length, off_t offset) +static void * +_cserve2_shm_map(const char *name, size_t length, off_t offset) { void *map; int fd; @@ -204,8 +204,8 @@ cserve2_shm_map(const char *name, size_t length, off_t offset) return map; } -void -cserve2_shm_unmap(void *map, size_t length) +static void +_cserve2_shm_unmap(void *map, size_t length) { munmap(map, length); } @@ -316,8 +316,8 @@ image_load(const char *file, const char *key, const char *shmfile, Slave_Msg_Ima Evas_Loader_Module_Api *api; int err; Error_Type ret = CSERVE2_NONE; - char *map = cserve2_shm_map(shmfile, params->shm.mmap_size, - params->shm.mmap_offset); + char *map = _cserve2_shm_map(shmfile, params->shm.mmap_size, + params->shm.mmap_offset); if (map == MAP_FAILED) return CSERVE2_RESOURCE_ALLOCATION_FAILED; @@ -352,7 +352,7 @@ image_load(const char *file, const char *key, const char *shmfile, Slave_Msg_Ima result->alpha_sparse = ilp.alpha_sparse; done: - cserve2_shm_unmap(map, params->shm.mmap_size); + _cserve2_shm_unmap(map, params->shm.mmap_size); return ret; } diff --git a/legacy/evas/src/bin/evas_cserve2_usage.c b/legacy/evas/src/bin/evas_cserve2_usage.c new file mode 100644 index 0000000000..fcf6720281 --- /dev/null +++ b/legacy/evas/src/bin/evas_cserve2_usage.c @@ -0,0 +1,230 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "evas_cs2.h" + +static int socketfd = -1; +static unsigned int _rid_count = 1; +static int _evas_cserve2_usage_log_dom = -1; + +static struct sockaddr_un socksize; +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX sizeof(socksize.sun_path) +#endif + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_evas_cserve2_usage_log_dom, __VA_ARGS__) +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_evas_cserve2_usage_log_dom, __VA_ARGS__) +#ifdef WRN +#undef WRN +#endif +#define WRN(...) EINA_LOG_DOM_WARN(_evas_cserve2_usage_log_dom, __VA_ARGS__) +#ifdef INF +#undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_evas_cserve2_usage_log_dom, __VA_ARGS__) + +static void +_socket_path_set(char *path) +{ + char *env; + char buf[UNIX_PATH_MAX]; + + env = getenv("EVAS_CSERVE2_SOCKET"); + if (env && env[0]) + { + strncpy(path, env, UNIX_PATH_MAX - 1); + return; + } + + snprintf(buf, sizeof(buf), "/tmp/.evas-cserve2-%x.socket", (int)getuid()); + /* FIXME: check we can actually create this socket */ + strcpy(path, buf); +} + +static Eina_Bool +_server_connect(void) +{ + int s, len; + struct sockaddr_un remote; + + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + { + ERR("socket"); + return EINA_FALSE; + } + + remote.sun_family = AF_UNIX; + _socket_path_set(remote.sun_path); + len = strlen(remote.sun_path) + sizeof(remote.sun_family); + if (connect(s, (struct sockaddr *)&remote, len) == -1) + { + ERR("connect"); + return EINA_FALSE; + } + + fcntl(s, F_SETFL, O_NONBLOCK); + + socketfd = s; + + DBG("connected to cserve2 server."); + return EINA_TRUE; +} + +static void +_server_disconnect(void) +{ + close(socketfd); + socketfd = -1; +} + +static Eina_Bool +_server_send(const void *data, int size) +{ + int sent = 0; + ssize_t ret; + const char *msg = data; + + while (sent < size) + { + ret = send(socketfd, msg + sent, size - sent, MSG_NOSIGNAL); + if (ret < 0) + { + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + return EINA_FALSE; + } + sent += ret; + } + + return EINA_TRUE; +} + +static int sr_size = 0; +static int sr_got = 0; +static char *sr_buf = NULL; + +static void * +_server_read(int *size) +{ + int n; + void *ret; + + if (sr_size) + goto get_data; + + n = recv(socketfd, &sr_size, sizeof(sr_size), 0); + if (n < 0) + return NULL; + + sr_buf = malloc(sr_size); + +get_data: + n = recv(socketfd, sr_buf + sr_got, sr_size - sr_got, 0); + if (n < 0) + return NULL; + + sr_got += n; + if (sr_got < sr_size) + return NULL; + + *size = sr_size; + sr_size = 0; + sr_got = 0; + ret = sr_buf; + sr_buf = NULL; + + return ret; +} + +static void +_usage_msg_send(void) +{ + Msg_Base msg; + int size; + + memset(&msg, 0, sizeof(msg)); + msg.type = CSERVE2_STATS; + msg.rid = _rid_count++; + + size = sizeof(msg); + + if (!_server_send(&size, sizeof(size))) + { + ERR("Could not send usage msg size to server."); + return; + } + if (!_server_send(&msg, size)) + { + ERR("Could not send usage msg body to server."); + return; + } +} + +static void +_usage_msg_read(void) +{ + Msg_Stats *msg = NULL; + int size; + + printf("Requesting server statistics.\n\n"); + while (!msg) + msg = _server_read(&size); + + if (msg->base.type != CSERVE2_STATS) + { + ERR("Invalid message received from server." + "Something went badly wrong."); + return; + } + + printf("Printing server usage.\n"); + printf("======================\n\n"); + printf("Font Usage Statistics:\n"); + printf("Requested usage: %d bytes\n", msg->fonts.requested_usage); + printf("Real usage: %d bytes\n", msg->fonts.real_usage); + printf("Fonts load time: %dus\n", msg->fonts.fonts_load); + printf("Fonts used load time: %dus\n", msg->fonts.fonts_used_load); + printf("Glyphs load time: %dus\n", msg->fonts.glyphs_load); + + printf("\n"); +} + +int +main(void) +{ + eina_init(); + + _evas_cserve2_usage_log_dom = eina_log_domain_register + ("evas_cserve2_usage", EINA_COLOR_BLUE); + + if (!_server_connect()) + { + ERR("Could not connect to server."); + return -1; + } + + _usage_msg_send(); + + _usage_msg_read(); + + _server_disconnect(); + + eina_shutdown(); +} diff --git a/legacy/evas/src/lib/cserve2/evas_cs2.h b/legacy/evas/src/lib/cserve2/evas_cs2.h index 77e7f8ca10..d58b866b15 100644 --- a/legacy/evas/src/lib/cserve2/evas_cs2.h +++ b/legacy/evas/src/lib/cserve2/evas_cs2.h @@ -22,6 +22,8 @@ typedef enum { CSERVE2_FONT_GLYPHS_LOAD, CSERVE2_FONT_GLYPHS_LOADED, CSERVE2_FONT_GLYPHS_USED, + CSERVE2_STATS, + CSERVE2_FONT_DEBUG, CSERVE2_ERROR } Message_Type; @@ -127,9 +129,9 @@ struct _Msg_Close { */ struct _Msg_Font_Load { Msg_Base base; + unsigned int sourcelen; // font id unsigned int pathlen; // font id unsigned int rend_flags; // font id - unsigned int hint; // font id unsigned int size; // font id unsigned int dpi; // font id }; @@ -146,18 +148,19 @@ struct _Msg_Font_Loaded { /** * @struct _Msg_Font_Glyphs_Request * - * Message from client to request load of glyphs, of inform usage of them. + * Message from client to request load of glyphs, or inform usage of them. * * The path strings follow the struct inside the message, as well as * the list of glyphs to be loaded. */ struct _Msg_Font_Glyphs_Request { Msg_Base base; + unsigned int sourcelen; // font id unsigned int pathlen; // font id unsigned int rend_flags; // font id - unsigned int hint; // font id unsigned int size; // font id unsigned int dpi; // font id + unsigned int hint; unsigned int nglyphs; }; @@ -180,6 +183,12 @@ struct _Msg_Font_Glyphs_Request { * - struct { * unsigned int index; * unsigned int offset; + * unsigned int size; + * unsigned int rows; + * unsigned int width; + * unsigned int pitch; + * unsigned int num_grays; + * unsigned int pixel_mode; * } glarray[]; */ struct _Msg_Font_Glyphs_Loaded { @@ -187,6 +196,56 @@ struct _Msg_Font_Glyphs_Loaded { unsigned int ncaches; }; +struct _Msg_Stats { + Msg_Base base; + struct { + unsigned int requested_usage; + unsigned int real_usage; + int fonts_load; // total time spent loading fonts + int fonts_used_load; // total time spent loading fonts that are + // really used + int glyphs_load; // total time spent loading glyphs + } fonts; +}; + +/* + * @struct _Msg_Font_Debug + * + * Message from server containing all font cache info. + * + * Content of the message follows: + * + * * number of font entries; + * * each font entry: + * - unsigned int filelen + * - const char file + * - unsigned int namelen + * - const char name + * - unsigned int rend_flags; + * - unsigned int size; + * - unsigned int dpi; + * - unsigned int unused; + * - ncaches: + * - each cache: + * * usigned int shmnamelen; + * * const char shmname; + * * unsigned int size; + * * unsigned int usage; + * * unsigned int nglyphs; + * * each glyph: + * - unsigned int index; + * - unsigned int offset; + * - unsigned int size; + * - unsigned int rows; + * - unsigned int width; + * - unsigned int pitch; + * - unsigned int num_grays; + * - unsigned int pixel_mode; + */ +struct _Msg_Font_Debug { + Msg_Base base; +}; + struct _Msg_Error { Msg_Base base; int error; @@ -206,6 +265,8 @@ typedef struct _Msg_Font_Load Msg_Font_Load; typedef struct _Msg_Font_Loaded Msg_Font_Loaded; typedef struct _Msg_Font_Glyphs_Request Msg_Font_Glyphs_Request; typedef struct _Msg_Font_Glyphs_Loaded Msg_Font_Glyphs_Loaded; +typedef struct _Msg_Stats Msg_Stats; +typedef struct _Msg_Font_Debug Msg_Font_Debug; typedef struct _Msg_Error Msg_Error; #endif