diff --git a/libass/ass_cache.c b/libass/ass_cache.c index 4943f944f..f8153c72a 100644 --- a/libass/ass_cache.c +++ b/libass/ass_cache.c @@ -378,15 +378,14 @@ typedef struct cache_item { const CacheDesc *desc; struct cache_item *_Atomic next, *_Atomic *prev; struct cache_item *_Atomic queue_next, *_Atomic *queue_prev; - struct cache_item *_Atomic promote_next; + struct cache_item *promote_next; _Atomic uintptr_t size, ref_count; ass_hashcode hash; _Atomic uintptr_t last_used_frame; #if ENABLE_THREADS - pthread_mutex_t mutex; - pthread_cond_t cond; + struct cache_client *creating_client; #endif } CacheItem; @@ -394,13 +393,30 @@ struct cache { unsigned buckets; CacheItem *_Atomic *map; CacheItem *_Atomic queue_first, *_Atomic *_Atomic queue_last; - CacheItem *_Atomic promote_first; const CacheDesc *desc; _Atomic uintptr_t cache_size; uintptr_t cur_frame; + + size_t n_clients; + struct cache_client **clients; + +#if ENABLE_THREADS + pthread_mutex_t mutex; +#endif +}; + +struct cache_client { + Cache *cache; + CacheItem *promote_first; + size_t idx; + +#if ENABLE_THREADS + pthread_mutex_t mutex; + pthread_cond_t cond; +#endif }; #define CACHE_ALIGN 8 @@ -432,11 +448,95 @@ Cache *ass_cache_create(const CacheDesc *desc) return NULL; } +#if ENABLE_THREADS + if (pthread_mutex_init(&cache->mutex, NULL) != 0) { + free(cache->map); + free(cache); + return NULL; + } +#endif + return cache; } -void *ass_cache_get(Cache *cache, void *key, void *priv) +CacheClient *ass_cache_client_create(Cache *cache) { + CacheClient *client = NULL; + +#if ENABLE_THREADS + pthread_mutex_lock(&cache->mutex); +#endif + + size_t idx; + + for (idx = 0; idx < cache->n_clients; idx++) { + if (!cache->clients[idx]) + break; + } + + if (idx >= cache->n_clients) { + if (!ASS_REALLOC_ARRAY(cache->clients, idx + 1)) + goto fail; + } + + client = calloc(1, sizeof(*client)); + if (!client) + goto fail; + + client->cache = cache; + client->idx = idx; + +#if ENABLE_THREADS + if (pthread_mutex_init(&client->mutex, NULL) != 0) { + free(client); + goto fail; + } + + if (pthread_cond_init(&client->cond, NULL) != 0) { + pthread_cond_destroy(&client->cond); + free(client); + goto fail; + } +#endif + + cache->clients[idx] = client; + if (idx >= cache->n_clients) + cache->n_clients = idx + 1; + +fail: +#if ENABLE_THREADS + pthread_mutex_unlock(&cache->mutex); +#endif + + return client; +} + +void ass_cache_client_done(CacheClient *client) +{ + if (!client) + return; + + Cache *cache = client->cache; + +#if ENABLE_THREADS + pthread_mutex_lock(&cache->mutex); +#endif + + cache->clients[client->idx] = NULL; + +#if ENABLE_THREADS + pthread_mutex_destroy(&client->mutex); + pthread_cond_destroy(&client->cond); + + pthread_mutex_unlock(&cache->mutex); +#endif + + free(client); +} + +void *ass_cache_get(CacheClient *client, void *key, void *priv) +{ + Cache *cache = client->cache; const CacheDesc *desc = cache->desc; size_t key_offs = CACHE_ITEM_SIZE + align_cache(desc->value_size); ass_hashcode hash = desc->hash_func(key, ASS_HASH_INIT); @@ -459,8 +559,8 @@ void *ass_cache_get(Cache *cache, void *key, void *priv) uintptr_t last_used = atomic_exchange_explicit(&item->last_used_frame, cache->cur_frame, memory_order_consume); if (last_used != cache->cur_frame) { - CacheItem *old_first = atomic_exchange_explicit(&cache->promote_first, item, memory_order_acq_rel); - atomic_store_explicit(&item->promote_next, old_first, memory_order_relaxed); + item->promote_next = client->promote_first; + client->promote_first = item; } } @@ -471,18 +571,15 @@ void *ass_cache_get(Cache *cache, void *key, void *priv) #if ENABLE_THREADS if (!atomic_load_explicit(&item->size, memory_order_acquire)) { - pthread_mutex_lock(&item->mutex); + pthread_mutex_lock(&item->creating_client->mutex); while (!item->size) - pthread_cond_wait(&item->cond, &item->mutex); + pthread_cond_wait(&item->creating_client->cond, &item->creating_client->mutex); - pthread_mutex_unlock(&item->mutex); + pthread_mutex_unlock(&item->creating_client->mutex); } if (new_item) { - pthread_mutex_destroy(&new_item->mutex); - pthread_cond_destroy(&new_item->cond); - free(new_item); } #endif @@ -503,23 +600,10 @@ void *ass_cache_get(Cache *cache, void *key, void *priv) new_key = (char *) new_item + key_offs; if (!desc->key_move_func(new_key, key)) { -#if ENABLE_THREADS - fail_move: -#endif free(new_item); goto fail_item; } -#if ENABLE_THREADS - if (pthread_cond_init(&new_item->cond, NULL) != 0) - goto fail_move; - - if (pthread_mutex_init(&new_item->mutex, NULL) != 0) { - pthread_cond_destroy(&new_item->cond); - goto fail_move; - } -#endif - key = new_key; new_item->desc = desc; @@ -529,6 +613,9 @@ void *ass_cache_get(Cache *cache, void *key, void *priv) new_item->ref_count = 1; new_item->queue_next = NULL; new_item->queue_prev = NULL; +#if ENABLE_THREADS + new_item->creating_client = client; +#endif } new_item->next = start; @@ -554,14 +641,14 @@ void *ass_cache_get(Cache *cache, void *key, void *priv) atomic_fetch_add_explicit(&cache->cache_size, size + (size == 1 ? 0 : CACHE_ITEM_SIZE), memory_order_relaxed); #if ENABLE_THREADS - pthread_mutex_lock(&item->mutex); + pthread_mutex_lock(&client->mutex); #endif item->size = size; #if ENABLE_THREADS - pthread_mutex_unlock(&item->mutex); - pthread_cond_broadcast(&item->cond); + pthread_mutex_unlock(&client->mutex); + pthread_cond_broadcast(&client->cond); #endif return value; @@ -579,10 +666,6 @@ static inline void destroy_item(const CacheDesc *desc, CacheItem *item) assert(!item->next && !item->prev); char *value = (char *) item + CACHE_ITEM_SIZE; desc->destruct_func(value + align_cache(desc->value_size), value); -#if ENABLE_THREADS - pthread_mutex_destroy(&item->mutex); - pthread_cond_destroy(&item->cond); -#endif free(item); } @@ -612,19 +695,25 @@ void ass_cache_dec_ref(void *value) void ass_cache_cut(Cache *cache, size_t max_size) { - while (cache->promote_first) { - CacheItem *item = cache->promote_first; + for (size_t i = 0; i < cache->n_clients; i++) { + CacheClient *client = cache->clients[i]; + if (!client) + continue; - *item->queue_prev = item->queue_next; - if (item->queue_next) - item->queue_next->queue_prev = item->queue_prev; - item->queue_next = NULL; + while (client->promote_first) { + CacheItem *item = client->promote_first; + + *item->queue_prev = item->queue_next; + if (item->queue_next) + item->queue_next->queue_prev = item->queue_prev; + item->queue_next = NULL; - item->queue_prev = cache->queue_last; - *cache->queue_last = item; - cache->queue_last = &item->queue_next; + item->queue_prev = cache->queue_last; + *cache->queue_last = item; + cache->queue_last = &item->queue_next; - cache->promote_first = item->promote_next; + client->promote_first = item->promote_next; + } } while (cache->cache_size > max_size && cache->queue_first) { @@ -690,14 +779,23 @@ void ass_cache_empty(Cache *cache) cache->queue_first = NULL; cache->queue_last = &cache->queue_first; - cache->promote_first = NULL; cache->cache_size = 0; + + for (size_t i = 0; i < cache->n_clients; i++) { + CacheClient *client = cache->clients[i]; + if (client) + client->promote_first = NULL; + } } void ass_cache_done(Cache *cache) { ass_cache_empty(cache); free(cache->map); + free(cache->clients); +#if ENABLE_THREADS + pthread_mutex_destroy(&cache->mutex); +#endif free(cache); } diff --git a/libass/ass_cache.h b/libass/ass_cache.h index 29e135744..2f49c3bb4 100644 --- a/libass/ass_cache.h +++ b/libass/ass_cache.h @@ -26,6 +26,7 @@ #include "ass_bitmap.h" typedef struct cache Cache; +typedef struct cache_client CacheClient; typedef uint64_t ass_hashcode; // cache values @@ -97,7 +98,9 @@ typedef struct } CacheDesc; Cache *ass_cache_create(const CacheDesc *desc); -void *ass_cache_get(Cache *cache, void *key, void *priv); +CacheClient *ass_cache_client_create(Cache *cache); +void ass_cache_client_done(CacheClient *client); +void *ass_cache_get(CacheClient *cache, void *key, void *priv); void *ass_cache_key(void *value); void ass_cache_inc_ref(void *value); void ass_cache_dec_ref(void *value); diff --git a/libass/ass_font.c b/libass/ass_font.c index 6c672c564..72296a90e 100644 --- a/libass/ass_font.c +++ b/libass/ass_font.c @@ -479,9 +479,9 @@ static int add_face(ASS_FontSelector *fontsel, ASS_Font *font, uint32_t ch) /** * \brief Create a new ASS_Font according to "desc" argument */ -ASS_Font *ass_font_new(ASS_Renderer *render_priv, ASS_FontDesc *desc) +ASS_Font *ass_font_new(struct render_context *context, ASS_FontDesc *desc) { - ASS_Font *font = ass_cache_get(render_priv->cache.font_cache, desc, render_priv); + ASS_Font *font = ass_cache_get(context->cache.font_cache, desc, context->renderer); if (!font) return NULL; if (font->library) diff --git a/libass/ass_font.h b/libass/ass_font.h index c9a294e8f..c1e38fbfc 100644 --- a/libass/ass_font.h +++ b/libass/ass_font.h @@ -54,7 +54,7 @@ struct ass_font { }; void ass_charmap_magic(ASS_Library *library, FT_Face face); -ASS_Font *ass_font_new(ASS_Renderer *render_priv, ASS_FontDesc *desc); +ASS_Font *ass_font_new(struct render_context *context, ASS_FontDesc *desc); void ass_face_set_size(FT_Face face, double size); int ass_face_get_weight(FT_Face face); void ass_font_get_asc_desc(ASS_Font *font, int face_index, diff --git a/libass/ass_parse.c b/libass/ass_parse.c index 70cefc8d7..413dddee1 100644 --- a/libass/ass_parse.c +++ b/libass/ass_parse.c @@ -111,7 +111,7 @@ void ass_update_font(RenderContext *state) val = 0; // normal desc.italic = val; - state->font = ass_font_new(state->renderer, &desc); + state->font = ass_font_new(state, &desc); } /** diff --git a/libass/ass_render.c b/libass/ass_render.c index 0d06cdc3f..fa0e4f362 100644 --- a/libass/ass_render.c +++ b/libass/ass_render.c @@ -88,7 +88,16 @@ static bool render_context_init(RenderContext *state, ASS_Renderer *priv) if (!text_info_init(&state->text_info)) return false; - if (!(state->shaper = ass_shaper_new(priv->cache.metrics_cache, priv->cache.sized_shaper_font_cache))) + state->cache.font_cache = ass_cache_client_create(priv->cache.font_cache); + state->cache.bitmap_cache = ass_cache_client_create(priv->cache.bitmap_cache); + state->cache.composite_cache = ass_cache_client_create(priv->cache.composite_cache); + state->cache.outline_cache = ass_cache_client_create(priv->cache.outline_cache); + state->cache.sized_shaper_font_cache = ass_cache_client_create(priv->cache.sized_shaper_font_cache); + state->cache.metrics_cache = ass_cache_client_create(priv->cache.metrics_cache); + if (!state->cache.font_cache || !state->cache.bitmap_cache || !state->cache.composite_cache || !state->cache.outline_cache || !state->cache.sized_shaper_font_cache || !state->cache.metrics_cache) + return false; + + if (!(state->shaper = ass_shaper_new(state->cache.metrics_cache, state->cache.sized_shaper_font_cache))) return false; return ass_rasterizer_init(&priv->engine, &state->rasterizer, RASTERIZER_PRECISION); @@ -101,6 +110,13 @@ static void render_context_done(RenderContext *state) if (state->shaper) ass_shaper_free(state->shaper); + ass_cache_client_done(state->cache.composite_cache); + ass_cache_client_done(state->cache.bitmap_cache); + ass_cache_client_done(state->cache.outline_cache); + ass_cache_client_done(state->cache.sized_shaper_font_cache); + ass_cache_client_done(state->cache.metrics_cache); + ass_cache_client_done(state->cache.font_cache); + text_info_done(&state->text_info); } @@ -228,6 +244,8 @@ void ass_renderer_done(ASS_Renderer *render_priv) ass_frame_unref(render_priv->images_root); ass_frame_unref(render_priv->prev_images_root); + render_context_done(&render_priv->state); + ass_cache_done(render_priv->cache.composite_cache); ass_cache_done(render_priv->cache.bitmap_cache); ass_cache_done(render_priv->cache.outline_cache); @@ -241,8 +259,6 @@ void ass_renderer_done(ASS_Renderer *render_priv) FT_Done_FreeType(render_priv->ftlibrary); free(render_priv->eimg); - render_context_done(&render_priv->state); - free(render_priv->settings.default_font); free(render_priv->settings.default_family); @@ -768,12 +784,12 @@ static void blend_vector_clip(RenderContext *state, ASS_Image *head) ASS_Vector pos; BitmapHashKey key; - key.outline = ass_cache_get(render_priv->cache.outline_cache, &ol_key, render_priv); + key.outline = ass_cache_get(state->cache.outline_cache, &ol_key, render_priv); if (!key.outline || !key.outline->valid || !quantize_transform(m, &pos, NULL, true, &key)) { return; } - Bitmap *clip_bm = ass_cache_get(render_priv->cache.bitmap_cache, &key, state); + Bitmap *clip_bm = ass_cache_get(state->cache.bitmap_cache, &key, state); if (!clip_bm) return; @@ -1226,7 +1242,7 @@ get_outline_glyph(RenderContext *state, GlyphInfo *info) if (info->drawing_text.str) { key.type = OUTLINE_DRAWING; key.u.drawing.text = info->drawing_text; - val = ass_cache_get(priv->cache.outline_cache, &key, priv); + val = ass_cache_get(state->cache.outline_cache, &key, priv); if (!val || !val->valid) { return; } @@ -1250,7 +1266,7 @@ get_outline_glyph(RenderContext *state, GlyphInfo *info) k->italic = info->italic; k->flags = info->flags; - val = ass_cache_get(priv->cache.outline_cache, &key, priv); + val = ass_cache_get(state->cache.outline_cache, &key, priv); if (!val || !val->valid) { return; } @@ -1463,7 +1479,7 @@ get_bitmap_glyph(RenderContext *state, GlyphInfo *info, if (!quantize_transform(m, pos, offset, first, &key)) { return; } - info->bm = ass_cache_get(render_priv->cache.bitmap_cache, &key, state); + info->bm = ass_cache_get(state->cache.bitmap_cache, &key, state); if (!info->bm || !info->bm->buffer) { info->bm = NULL; } @@ -1582,12 +1598,12 @@ get_bitmap_glyph(RenderContext *state, GlyphInfo *info, } } - key.outline = ass_cache_get(render_priv->cache.outline_cache, &ol_key, render_priv); + key.outline = ass_cache_get(state->cache.outline_cache, &ol_key, render_priv); if (!key.outline || !key.outline->valid || !quantize_transform(m, pos_o, offset, false, &key)) { return; } - info->bm_o = ass_cache_get(render_priv->cache.bitmap_cache, &key, state); + info->bm_o = ass_cache_get(state->cache.bitmap_cache, &key, state); if (!info->bm_o || !info->bm_o->buffer) { info->bm_o = NULL; *pos_o = *pos; @@ -2668,7 +2684,7 @@ static void render_and_combine_glyphs(RenderContext *state, key.filter = info->filter; key.bitmap_count = info->bitmap_count; key.bitmaps = info->bitmaps; - CompositeHashValue *val = ass_cache_get(render_priv->cache.composite_cache, &key, render_priv); + CompositeHashValue *val = ass_cache_get(state->cache.composite_cache, &key, render_priv); if (!val) continue; diff --git a/libass/ass_render.h b/libass/ass_render.h index 3558f3584..5ab624202 100644 --- a/libass/ass_render.h +++ b/libass/ass_render.h @@ -213,6 +213,15 @@ typedef struct { #include "ass_shaper.h" +typedef struct { + CacheClient *font_cache; + CacheClient *outline_cache; + CacheClient *bitmap_cache; + CacheClient *composite_cache; + CacheClient *sized_shaper_font_cache; + CacheClient *metrics_cache; +} CacheClientStore; + // Renderer state. // Values like current font face, color, screen position, clipping and so on are stored here. struct render_context { @@ -220,6 +229,7 @@ struct render_context { TextInfo text_info; ASS_Shaper *shaper; RasterizerData rasterizer; + CacheClientStore cache; ASS_Event *event; ASS_Style *style; diff --git a/libass/ass_shaper.c b/libass/ass_shaper.c index c0c36b8f7..42dd09b38 100644 --- a/libass/ass_shaper.c +++ b/libass/ass_shaper.c @@ -63,8 +63,8 @@ struct ass_shaper { hb_language_t language; // Glyph and face-size metrics caches, to speed up shaping - Cache *sized_shaper_font_cache; - Cache *metrics_cache; + CacheClient *sized_shaper_font_cache; + CacheClient *metrics_cache; hb_font_funcs_t *font_funcs; hb_buffer_t *buf; @@ -78,7 +78,7 @@ struct ass_shaper { }; struct ass_shaper_metrics_data { - Cache *metrics_cache; + CacheClient *metrics_cache; SizedShaperFontHashKey hash_key; }; @@ -1064,7 +1064,7 @@ bool ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info) /** * \brief Create a new shaper instance */ -ASS_Shaper *ass_shaper_new(Cache *metrics_cache, Cache *sized_shaper_font_cache) +ASS_Shaper *ass_shaper_new(CacheClient *metrics_cache, CacheClient *sized_shaper_font_cache) { assert(metrics_cache); diff --git a/libass/ass_shaper.h b/libass/ass_shaper.h index 60660028b..23e6d0e3e 100644 --- a/libass/ass_shaper.h +++ b/libass/ass_shaper.h @@ -31,7 +31,7 @@ typedef struct ass_shaper ASS_Shaper; #endif void ass_shaper_info(ASS_Library *lib); -ASS_Shaper *ass_shaper_new(Cache *metrics_cache, Cache *sized_shaper_font_cache); +ASS_Shaper *ass_shaper_new(CacheClient *metrics_cache, CacheClient *sized_shaper_font_cache); void ass_shaper_free(ASS_Shaper *shaper); bool ass_create_hb_font(ASS_Font *font, int index); void ass_shaper_set_kerning(ASS_Shaper *shaper, bool kern);