From b2af884e8948c27b793edbcd566d8eab42cbc6e0 Mon Sep 17 00:00:00 2001 From: "Eunki, Hong" Date: Thu, 3 Nov 2022 14:50:30 +0900 Subject: [PATCH] Move glyph cache manager into font-client-plugin-cache-handler Currently, glyph cache manager owned by each font-face-cache-item. That mean, we can hold glyph cache as {The number of font type} x {The number of font size} x 128(default). This patch make we hold GlyphCacheManager hold as singletone. So we can cache the glyph maximum 128(default) not relative with the number of font type & size. Change-Id: I85e3cec3d160a57496e839f0b87a24655861f981 Signed-off-by: Eunki, Hong --- .../plugin/font-client-plugin-cache-handler.cpp | 27 +++ .../plugin/font-client-plugin-cache-handler.h | 11 +- .../plugin/font-client-plugin-impl.cpp | 4 +- .../plugin/font-face-cache-item.cpp | 60 +++---- .../text-abstraction/plugin/font-face-cache-item.h | 4 +- .../plugin/font-face-glyph-cache-manager.cpp | 111 +++++++++---- .../plugin/font-face-glyph-cache-manager.h | 44 ++++- .../plugin/harfbuzz-proxy-font.cpp | 4 +- .../text-abstraction/plugin/lru-cache-container.h | 183 ++++++++++++++++++--- 9 files changed, 343 insertions(+), 105 deletions(-) diff --git a/dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.cpp b/dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.cpp index acaec1d..61e7a47 100644 --- a/dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.cpp +++ b/dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.cpp @@ -23,6 +23,7 @@ #include // INTERNAL INCLUDES +#include #include #include #include @@ -61,6 +62,28 @@ extern Dali::Integration::Log::Filter* gFontClientLogFilter; namespace { +/** + * @brief Maximum size of glyph cache per each font face. + */ +constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX = 128; +constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u; + +constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX"; + +/** + * @brief Get maximum size of glyph cache size from environment. + * If not settuped, default as 128. + * @note This value fixed when we call it first time. + * @return The max size of glyph cache. + */ +inline size_t GetMaxNumberOfGlyphCache() +{ + using Dali::EnvironmentVariable::GetEnvironmentVariable; + static auto numberString = GetEnvironmentVariable(MAX_NUMBER_OF_GLYPH_CACHE_ENV); + static auto number = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_GLYPH_CACHE_MAX; + return (number < MINIMUM_SIZE_OF_GLYPH_CACHE_MAX) ? MINIMUM_SIZE_OF_GLYPH_CACHE_MAX : number; +} + } // namespace namespace Dali::TextAbstraction::Internal @@ -249,6 +272,7 @@ FontClient::Plugin::CacheHandler::CacheHandler() mFontDescriptionSizeCache(), mEllipsisCache(), mEmbeddedItemCache(), + mGlyphCacheManager(new GlyphCacheManager(GetMaxNumberOfGlyphCache())), mLatestFoundFontDescription(), mLatestFoundFontDescriptionId(0u), mLatestFoundCacheKey(0, 0), @@ -264,6 +288,9 @@ FontClient::Plugin::CacheHandler::~CacheHandler() void FontClient::Plugin::CacheHandler::ClearCache() { + // delete cached glyph informations before clear mFontFaceCache. + mGlyphCacheManager->ClearCache(); + mDefaultFontDescription = FontDescription(); mSystemFonts.clear(); diff --git a/dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.h b/dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.h index 740ac51..20c5995 100644 --- a/dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.h +++ b/dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.h @@ -20,6 +20,7 @@ // INTERNAL INCLUDES #include +#include namespace Dali::TextAbstraction::Internal { @@ -378,6 +379,12 @@ public: // Find & Cache */ GlyphIndex CacheEmbeddedItem(EmbeddedItem&& embeddedItem); +public: // Other public API + GlyphCacheManager* GetGlyphCacheManager() const + { + return mGlyphCacheManager.get(); + } + private: CacheHandler(const CacheHandler&) = delete; CacheHandler& operator=(const CacheHandler&) = delete; @@ -404,7 +411,9 @@ public: // Cache container list std::vector mPixelBufferCache; ///< Caches the pixel buffer of a url. std::vector mEmbeddedItemCache; ///< Cache embedded items. -private: // Member value +private: // Member value + std::unique_ptr mGlyphCacheManager; ///< The glyph cache manager. It will cache this face's glyphs. + FontDescription mLatestFoundFontDescription; ///< Latest found font description and id in FindValidatedFont() FontDescriptionId mLatestFoundFontDescriptionId; diff --git a/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp b/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp index 6715e54..bc62cba 100644 --- a/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp +++ b/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp @@ -1082,7 +1082,7 @@ FontId FontClient::Plugin::CreateFont(const FontPath& path, const float fixedHeight = static_cast(ftFace->available_sizes[fixedSizeIndex].height); // Create the FreeType font face item to cache. - FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables); + FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables); fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem)); } @@ -1124,7 +1124,7 @@ FontId FontClient::Plugin::CreateFont(const FontPath& path, static_cast(ftFace->underline_thickness) * FROM_266); // Create the FreeType font face item to cache. - FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics); + FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics); fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem)); } diff --git a/dali/internal/text/text-abstraction/plugin/font-face-cache-item.cpp b/dali/internal/text/text-abstraction/plugin/font-face-cache-item.cpp index 5ece316..90fbb02 100644 --- a/dali/internal/text/text-abstraction/plugin/font-face-cache-item.cpp +++ b/dali/internal/text/text-abstraction/plugin/font-face-cache-item.cpp @@ -41,28 +41,6 @@ const float POINTS_PER_INCH = 72.f; constexpr float MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE = 1.5f; /** - * @brief Maximum size of glyph cache per each font face. - */ -constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX = 128; -constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u; - -constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX"; - -/** - * @brief Get maximum size of glyph cache size from environment. - * If not settuped, default as 128. - * @note This value fixed when we call it first time. - * @return The max size of glyph cache. - */ -inline const size_t GetMaxNumberOfGlyphCache() -{ - using Dali::EnvironmentVariable::GetEnvironmentVariable; - static auto numberString = GetEnvironmentVariable(MAX_NUMBER_OF_GLYPH_CACHE_ENV); - static auto number = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_GLYPH_CACHE_MAX; - return (number < MINIMUM_SIZE_OF_GLYPH_CACHE_MAX) ? MINIMUM_SIZE_OF_GLYPH_CACHE_MAX : number; -} - -/** * @brief Behavior about cache the rendered glyph cache. */ constexpr bool DEFAULT_ENABLE_CACHE_RENDERED_GLYPH = true; @@ -74,7 +52,7 @@ constexpr auto ENABLE_CACHE_RENDERED_GLYPH_ENV = "DALI_ENABLE_CACHE_RENDERED * @note This value fixed when we call it first time. * @return True if we allow to cache rendered glyph. */ -inline const bool EnableCacheRenderedGlyph() +inline bool EnableCacheRenderedGlyph() { using Dali::EnvironmentVariable::GetEnvironmentVariable; static auto numberString = GetEnvironmentVariable(ENABLE_CACHE_RENDERED_GLYPH_ENV); @@ -100,7 +78,7 @@ constexpr auto RENDERED_GLYPH_COMPRESS_POLICY_ENV = "DALI_RENDERED_GLYPH_COMPRES * @note This value fixed when we call it first time. * @return SPEED if value start with 's' or 'S'. MEMORY if value start with 'm' or 'M'. otherwise, use default */ -inline const GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPolicy() +inline GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPolicy() { using Dali::EnvironmentVariable::GetEnvironmentVariable; static auto policyString = GetEnvironmentVariable(RENDERED_GLYPH_COMPRESS_POLICY_ENV); @@ -115,13 +93,14 @@ inline const GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPo FontFaceCacheItem::FontFaceCacheItem(const FT_Library& freeTypeLibrary, FT_Face ftFace, + GlyphCacheManager* glyphCacheManager, const FontPath& path, PointSize26Dot6 requestedPointSize, FaceIndex face, const FontMetrics& metrics) : mFreeTypeLibrary(freeTypeLibrary), mFreeTypeFace(ftFace), - mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())), + mGlyphCacheManager(glyphCacheManager), mHarfBuzzProxyFont(), mPath(path), mRequestedPointSize(requestedPointSize), @@ -140,6 +119,7 @@ FontFaceCacheItem::FontFaceCacheItem(const FT_Library& freeTypeLibrary, FontFaceCacheItem::FontFaceCacheItem(const FT_Library& freeTypeLibrary, FT_Face ftFace, + GlyphCacheManager* glyphCacheManager, const FontPath& path, PointSize26Dot6 requestedPointSize, FaceIndex face, @@ -150,7 +130,7 @@ FontFaceCacheItem::FontFaceCacheItem(const FT_Library& freeTypeLibrary, bool hasColorTables) : mFreeTypeLibrary(freeTypeLibrary), mFreeTypeFace(ftFace), - mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())), + mGlyphCacheManager(glyphCacheManager), mHarfBuzzProxyFont(), mPath(path), mRequestedPointSize(requestedPointSize), @@ -173,7 +153,7 @@ FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs) : mFreeTypeLibrary(rhs.mFreeTypeLibrary) { mFreeTypeFace = rhs.mFreeTypeFace; - mGlyphCacheManager = std::move(rhs.mGlyphCacheManager); + mGlyphCacheManager = rhs.mGlyphCacheManager; mHarfBuzzProxyFont = std::move(rhs.mHarfBuzzProxyFont); mPath = std::move(rhs.mPath); mRequestedPointSize = rhs.mRequestedPointSize; @@ -188,17 +168,19 @@ FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs) mIsFixedSizeBitmap = rhs.mIsFixedSizeBitmap; mHasColorTables = rhs.mHasColorTables; - rhs.mFreeTypeFace = nullptr; + rhs.mFreeTypeFace = nullptr; + rhs.mGlyphCacheManager = nullptr; } FontFaceCacheItem::~FontFaceCacheItem() { - // delete glyph cache manager before free face. + // delete cached glyph informations before free face. if(mGlyphCacheManager) { - mGlyphCacheManager.reset(); + mGlyphCacheManager->RemoveGlyphFromFace(mFreeTypeFace); } + // delete harfbuzz proxy font before free face. if(mHarfBuzzProxyFont) { mHarfBuzzProxyFont.reset(); @@ -245,7 +227,7 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe if(mIsFixedSizeBitmap) { FT_Select_Size(mFreeTypeFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again. - mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, glyphData, error); + mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, glyphData, error); if(FT_Err_Ok == error) { @@ -287,7 +269,7 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe // TODO : If dpiVertical value changed, this resize feature will be break down. // Otherwise, this glyph will be resized only one times. - mGlyphCacheManager->ResizeBitmapGlyph(glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, static_cast(glyphInfo.width), static_cast(glyphInfo.height)); + mGlyphCacheManager->ResizeBitmapGlyph(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, static_cast(glyphInfo.width), static_cast(glyphInfo.height)); } } } @@ -303,7 +285,7 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap. // i.e. with the SNum-3R font. // @todo: add an option to use the FT_LOAD_DEFAULT if required? - mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphData, error); + mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphData, error); // Keep the width of the glyph before doing the software emboldening. // It will be used to calculate a scale factor to be applied to the @@ -332,7 +314,7 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe { // Get dummy glyph data without embolden. GlyphCacheManager::GlyphCacheData dummyData; - if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyData, error)) + if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyData, error)) { // If the glyph is emboldened by software, the advance is multiplied by a // scale factor to make it slightly bigger. @@ -397,7 +379,7 @@ void FontFaceCacheItem::CreateBitmap( // @todo: add an option to use the FT_LOAD_DEFAULT if required? loadFlag = FT_LOAD_NO_AUTOHINT; } - mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, loadFlag, isBoldRequired, glyphData, error); + mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, glyphData, error); if(FT_Err_Ok == error) { @@ -502,10 +484,10 @@ void FontFaceCacheItem::CreateBitmap( // Note : We will call this API once per each glyph. if(ableUseCachedRenderedGlyph) { - mGlyphCacheManager->CacheRenderedGlyphBuffer(glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy()); + mGlyphCacheManager->CacheRenderedGlyphBuffer(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy()); GlyphCacheManager::GlyphCacheData dummyData; - mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, loadFlag, isBoldRequired, dummyData, error); + mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, dummyData, error); if(DALI_LIKELY(FT_Err_Ok == error && dummyData.mRenderedBuffer)) { @@ -560,7 +542,7 @@ bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const if(mHasColorTables) { GlyphCacheManager::GlyphCacheData dummyData; - mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_COLOR, false, dummyData, error); + mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR, false, dummyData, error); } #endif return FT_Err_Ok == error; @@ -615,7 +597,7 @@ HarfBuzzFontHandle FontFaceCacheItem::GetHarfBuzzFont(const uint32_t& horizontal // Create new harfbuzz font only first time or DPI changed. if(DALI_UNLIKELY(!mHarfBuzzProxyFont || mHarfBuzzProxyFont->mHorizontalDpi != horizontalDpi || mHarfBuzzProxyFont->mVerticalDpi != verticalDpi)) { - mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, horizontalDpi, verticalDpi, mGlyphCacheManager.get())); + mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, horizontalDpi, verticalDpi, mGlyphCacheManager)); } return mHarfBuzzProxyFont->GetHarfBuzzFont(); } diff --git a/dali/internal/text/text-abstraction/plugin/font-face-cache-item.h b/dali/internal/text/text-abstraction/plugin/font-face-cache-item.h index c6bc1b8..10608d7 100644 --- a/dali/internal/text/text-abstraction/plugin/font-face-cache-item.h +++ b/dali/internal/text/text-abstraction/plugin/font-face-cache-item.h @@ -43,6 +43,7 @@ struct FontFaceCacheItem : public FontCacheItemInterface { FontFaceCacheItem(const FT_Library& freeTypeLibrary, FT_Face ftFace, + GlyphCacheManager* glyphCacheManager, const FontPath& path, PointSize26Dot6 requestedPointSize, FaceIndex face, @@ -50,6 +51,7 @@ struct FontFaceCacheItem : public FontCacheItemInterface FontFaceCacheItem(const FT_Library& freeTypeLibrary, FT_Face ftFace, + GlyphCacheManager* glyphCacheManager, const FontPath& path, PointSize26Dot6 requestedPointSize, FaceIndex face, @@ -132,7 +134,7 @@ public: const FT_Library& mFreeTypeLibrary; ///< A handle to a FreeType library instance. FT_Face mFreeTypeFace; ///< The FreeType face. - std::unique_ptr mGlyphCacheManager; ///< The glyph cache manager. It will cache this face's glyphs. + GlyphCacheManager* mGlyphCacheManager; ///< The reference of Glyph cache manager. Owned from font-client-plugin-cache-handler. std::unique_ptr mHarfBuzzProxyFont; ///< The harfbuzz font. It will store harfbuzz relate data. FontPath mPath; ///< The path to the font file name. diff --git a/dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.cpp b/dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.cpp index 1ced0b5..a241635 100644 --- a/dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.cpp +++ b/dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.cpp @@ -36,9 +36,8 @@ namespace constexpr uint32_t THRESHOLD_WIDTH_FOR_RLE4_COMPRESSION = 8; // The smallest width of glyph that we use RLE4 method. } // namespace -GlyphCacheManager::GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyphCache) -: mFreeTypeFace(ftFace), - mGlyphCacheMaxSize(maxNumberOfGlyphCache), +GlyphCacheManager::GlyphCacheManager(std::size_t maxNumberOfGlyphCache) +: mGlyphCacheMaxSize(maxNumberOfGlyphCache), mLRUGlyphCache(mGlyphCacheMaxSize) { DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager Create with maximum size : %d\n", static_cast(mGlyphCacheMaxSize)); @@ -46,17 +45,11 @@ GlyphCacheManager::GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyp GlyphCacheManager::~GlyphCacheManager() { - while(!mLRUGlyphCache.IsEmpty()) - { - auto removedData = mLRUGlyphCache.Pop(); - - // Release Glyph data resource - removedData.ReleaseGlyphData(); - } - mLRUGlyphCache.Clear(); + ClearCache(); } bool GlyphCacheManager::GetGlyphCacheDataFromIndex( + const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool isBoldRequired, @@ -66,7 +59,7 @@ bool GlyphCacheManager::GetGlyphCacheDataFromIndex( // Append some error value here instead of FT_Err_Ok. error = static_cast(-1); - const GlyphCacheKey key = GlyphCacheKey(index, flag, isBoldRequired); + const GlyphCacheKey key = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired); auto iter = mLRUGlyphCache.Find(key); if(iter == mLRUGlyphCache.End()) @@ -82,13 +75,13 @@ bool GlyphCacheManager::GetGlyphCacheDataFromIndex( removedData.ReleaseGlyphData(); } - const bool loadSuccess = LoadGlyphDataFromIndex(index, flag, isBoldRequired, glyphData, error); + const bool loadSuccess = LoadGlyphDataFromIndex(freeTypeFace, index, flag, isBoldRequired, glyphData, error); if(loadSuccess) { // Copy and cached data. mLRUGlyphCache.Push(key, glyphData); - DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Create cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph); + DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Create cache for face : %p, index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", freeTypeFace, index, static_cast(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph); } return loadSuccess; @@ -98,36 +91,37 @@ bool GlyphCacheManager::GetGlyphCacheDataFromIndex( error = FT_Err_Ok; // We already notify that we use this glyph. And now, copy cached data. - glyphData = iter->element; + glyphData = mLRUGlyphCache.GetElement(iter); - DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Find cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph); + DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Find cache for face : %p, index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", freeTypeFace, index, static_cast(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph); return true; } } bool GlyphCacheManager::LoadGlyphDataFromIndex( + const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool isBoldRequired, GlyphCacheData& glyphData, FT_Error& error) { - error = FT_Load_Glyph(mFreeTypeFace, index, flag); + error = FT_Load_Glyph(freeTypeFace, index, flag); if(FT_Err_Ok == error) { - glyphData.mStyleFlags = mFreeTypeFace->style_flags; + glyphData.mStyleFlags = freeTypeFace->style_flags; const bool isEmboldeningRequired = isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD); if(isEmboldeningRequired) { // Does the software bold. - FT_GlyphSlot_Embolden(mFreeTypeFace->glyph); + FT_GlyphSlot_Embolden(freeTypeFace->glyph); } - glyphData.mGlyphMetrics = mFreeTypeFace->glyph->metrics; + glyphData.mGlyphMetrics = freeTypeFace->glyph->metrics; glyphData.mIsBitmap = false; // Load glyph - error = FT_Get_Glyph(mFreeTypeFace->glyph, &glyphData.mGlyph); + error = FT_Get_Glyph(freeTypeFace->glyph, &glyphData.mGlyph); if(glyphData.mGlyph->format == FT_GLYPH_FORMAT_BITMAP) { @@ -137,7 +131,7 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex( // Copy rendered bitmap // TODO : Is there any way to keep bitmap buffer without copy? glyphData.mBitmap = new FT_Bitmap(); - *glyphData.mBitmap = mFreeTypeFace->glyph->bitmap; + *glyphData.mBitmap = freeTypeFace->glyph->bitmap; // New allocate buffer size_t bufferSize = 0; @@ -172,7 +166,7 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex( { glyphData.mIsBitmap = true; glyphData.mBitmap->buffer = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free. - memcpy(glyphData.mBitmap->buffer, mFreeTypeFace->glyph->bitmap.buffer, bufferSize); + memcpy(glyphData.mBitmap->buffer, freeTypeFace->glyph->bitmap.buffer, bufferSize); } else { @@ -195,6 +189,7 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex( } void GlyphCacheManager::ResizeBitmapGlyph( + const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool isBoldRequired, @@ -208,7 +203,7 @@ void GlyphCacheManager::ResizeBitmapGlyph( } FT_Error error; GlyphCacheData originGlyphData; - if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error)) + if(GetGlyphCacheDataFromIndex(freeTypeFace, index, flag, isBoldRequired, originGlyphData, error)) { if(DALI_LIKELY(originGlyphData.mIsBitmap && originGlyphData.mBitmap)) { @@ -216,10 +211,10 @@ void GlyphCacheManager::ResizeBitmapGlyph( if(requiredResize) { // originalGlyphData is copy data. For change cached information, we should access as iterator. - const GlyphCacheKey key = GlyphCacheKey(index, flag, isBoldRequired); + const GlyphCacheKey key = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired); auto iter = mLRUGlyphCache.Find(key); - GlyphCacheData& destinationGlpyhData = iter->element; + GlyphCacheData& destinationGlpyhData = mLRUGlyphCache.GetElement(iter); const ImageDimensions inputDimensions(destinationGlpyhData.mBitmap->width, destinationGlpyhData.mBitmap->rows); const ImageDimensions desiredDimensions(desiredWidth, desiredHeight); @@ -297,6 +292,7 @@ void GlyphCacheManager::ResizeBitmapGlyph( } void GlyphCacheManager::CacheRenderedGlyphBuffer( + const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool isBoldRequired, @@ -310,15 +306,15 @@ void GlyphCacheManager::CacheRenderedGlyphBuffer( } FT_Error error; GlyphCacheData originGlyphData; - if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error)) + if(GetGlyphCacheDataFromIndex(freeTypeFace, index, flag, isBoldRequired, originGlyphData, error)) { if(DALI_LIKELY(!originGlyphData.mIsBitmap && originGlyphData.mRenderedBuffer == nullptr)) { // originalGlyphData is copy data. For change cached information, we should access as iterator. - const GlyphCacheKey key = GlyphCacheKey(index, flag, isBoldRequired); + const GlyphCacheKey key = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired); auto iter = mLRUGlyphCache.Find(key); - GlyphCacheData& destinationGlpyhData = iter->element; + GlyphCacheData& destinationGlpyhData = mLRUGlyphCache.GetElement(iter); destinationGlpyhData.mRenderedBuffer = new TextAbstraction::FontClient::GlyphBufferData(); if(DALI_UNLIKELY(!destinationGlpyhData.mRenderedBuffer)) @@ -399,6 +395,60 @@ void GlyphCacheManager::CacheRenderedGlyphBuffer( } } +void GlyphCacheManager::RemoveGlyphFromFace(const FT_Face freeTypeFace) +{ + uint32_t removedItemCount = 0; + + auto endIter = mLRUGlyphCache.End(); + for(auto iter = mLRUGlyphCache.Begin(); iter != endIter;) + { + // Check whether this cached item has inputed freeTypeFace as key. + auto keyFace = mLRUGlyphCache.GetKey(iter).mFreeTypeFace; + if(keyFace == freeTypeFace) + { + ++removedItemCount; + iter = mLRUGlyphCache.Erase(iter); + } + else + { + ++iter; + } + } + + DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::RemoveGlyphFromFace. Remove all cached glyph with face : %p, removed glyph count : %u\n", freeTypeFace, removedItemCount); +} + +void GlyphCacheManager::ClearCache(const std::size_t remainCount) +{ + if(remainCount == 0u) + { + // Release all data memory first. + auto endIter = mLRUGlyphCache.End(); + for(auto iter = mLRUGlyphCache.Begin(); iter != endIter; ++iter) + { + // Get the reference of data. and release it. + auto& removedData = mLRUGlyphCache.GetElement(iter); + removedData.ReleaseGlyphData(); + } + + // Clear all cache. + mLRUGlyphCache.Clear(); + } + else + { + // While the cache count is bigger than remainCount, remove oldest glyph. + while(mLRUGlyphCache.Count() > remainCount) + { + auto removedData = mLRUGlyphCache.Pop(); + + DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::ClearCache[%zu / %zu]. Remove oldest cache for glyph : %p\n", mLRUGlyphCache.Count(), remainCount, removedData.mGlyph); + + // Release Glyph data resource + removedData.ReleaseGlyphData(); + } + } +} + void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData() { if(mIsBitmap && mBitmap) @@ -419,7 +469,10 @@ void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData() if(mRenderedBuffer) { delete mRenderedBuffer; + mRenderedBuffer = nullptr; } + + mStyleFlags = 0; } } // namespace Dali::TextAbstraction::Internal diff --git a/dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h b/dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h index fd0eb05..15f5e45 100644 --- a/dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h +++ b/dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h @@ -36,7 +36,7 @@ class GlyphCacheManager { public: // Constructor - GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyphCache); + GlyphCacheManager(std::size_t maxNumberOfGlyphCache); // Destructor ~GlyphCacheManager(); @@ -88,6 +88,7 @@ public: /** * @brief Load GlyphCacheData from face. The result will be cached. * + * @param[in] freeTypeFace The freetype face handle. * @param[in] index Index of glyph in this face. * @param[in] flag Flag when we load the glyph. * @param[in] isBoldRequired True if we require some software bold. @@ -96,6 +97,7 @@ public: * @return True if load successfully. False if something error occured. */ bool GetGlyphCacheDataFromIndex( + const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool isBoldRequired, @@ -106,6 +108,7 @@ public: * @brief Load GlyphCacheData from face. The result will not be cached. * @note If we call this API, We should release GlyphCacheData manually. * + * @param[in] freeTypeFace The freetype face handle. * @param[in] index Index of glyph in this face. * @param[in] flag Flag when we load the glyph. * @param[in] isBoldRequired True if we require some software bold. @@ -114,6 +117,7 @@ public: * @return True if load successfully. False if something error occured. */ bool LoadGlyphDataFromIndex( + const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool isBoldRequired, @@ -124,6 +128,7 @@ public: * @brief Resize bitmap glyph. The result will change cached glyph bitmap information. * If glyph is not bitmap glyph, nothing happened. * + * @param[in] freeTypeFace The freetype face handle. * @param[in] index Index of glyph in this face. * @param[in] flag Flag when we load the glyph. * @param[in] isBoldRequired True if we require some software bold. @@ -131,6 +136,7 @@ public: * @param[in] desiredHeight Desired height of bitmap. */ void ResizeBitmapGlyph( + const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool isBoldRequired, @@ -141,6 +147,7 @@ public: * @brief Cache rendered glyph bitmap. The result will change cached glyph information. * If glyph is not single color glyph, or we already cached buffer before, nothing happened. * + * @param[in] freeTypeFace The freetype face handle. * @param[in] index Index of glyph in this face. * @param[in] flag Flag when we load the glyph. * @param[in] isBoldRequired True if we require some software bold. @@ -148,12 +155,28 @@ public: * @param[in] policy Compress behavior policy. */ void CacheRenderedGlyphBuffer( + const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool isBoldRequired, const FT_Bitmap& srcBitmap, const CompressionPolicyType policy); + /** + * @brief Clear all cached glyph informations which has inputed FreeTypeFace. + * + * @note This API iterate all cached glyph. Should be called rarely. + * @param[in] freeTypeFace The freetype face handle. + */ + void RemoveGlyphFromFace(const FT_Face freeTypeFace); + + /** + * @brief Clear all cached glyph informations. + * + * @param[in] remainCount The number of remained cache items after call this API. Default is 0, clear all items. + */ + void ClearCache(const std::size_t remainCount = 0u); + private: // Private struct area. /** @@ -162,25 +185,29 @@ private: struct GlyphCacheKey { GlyphCacheKey() - : mIndex(0u), + : mFreeTypeFace(nullptr), + mIndex(0u), mFlag(0), mIsBoldRequired(false) { } - GlyphCacheKey(const GlyphIndex index, const FT_Int32 flag, const bool boldRequired) - : mIndex(index), + GlyphCacheKey(const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool boldRequired) + : mFreeTypeFace(freeTypeFace), + mIndex(index), mFlag(flag), mIsBoldRequired(boldRequired) { } + + FT_Face mFreeTypeFace; GlyphIndex mIndex; FT_Int32 mFlag; bool mIsBoldRequired : 1; bool operator==(GlyphCacheKey const& rhs) const noexcept { - return mIndex == rhs.mIndex && mFlag == rhs.mFlag && mIsBoldRequired == rhs.mIsBoldRequired; + return mFreeTypeFace == rhs.mFreeTypeFace && mIndex == rhs.mIndex && mFlag == rhs.mFlag && mIsBoldRequired == rhs.mIsBoldRequired; } }; @@ -191,14 +218,15 @@ private: { std::size_t operator()(GlyphCacheKey const& key) const noexcept { - return static_cast(key.mIndex) ^ static_cast(key.mFlag) ^ (static_cast(key.mIsBoldRequired) << 29); + return static_cast(reinterpret_cast(key.mFreeTypeFace)) ^ + static_cast(key.mIndex) ^ + static_cast(key.mFlag) ^ + (static_cast(key.mIsBoldRequired) << 29); } }; private: // Private member value area. - FT_Face mFreeTypeFace; ///< The FreeType face. Owned from font-face-cache-item - std::size_t mGlyphCacheMaxSize; ///< The maximum capacity of glyph cache. using CacheContainer = LRUCacheContainer; diff --git a/dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.cpp b/dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.cpp index 5d62a95..53d3d0f 100644 --- a/dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.cpp +++ b/dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.cpp @@ -76,7 +76,7 @@ private: public: FT_Face mFreeTypeFace; ///< The FreeType face. Owned from font-face-cache-item. - GlyphCacheManager* mGlyphCacheManager; ///< Glyph caching system for this harfbuzz font. Owned from font-face-cache-item. + GlyphCacheManager* mGlyphCacheManager; ///< Glyph caching system for this harfbuzz font. Owned from font-client-plugin-cache-handler. hb_font_t* mHarfBuzzFont; ///< Harfbuzz font handle integrated with FT_Face. }; @@ -126,7 +126,7 @@ static bool GetGlyphCacheData(void* font_data, const GlyphIndex& glyphIndex, Gly if(DALI_LIKELY(impl && impl->mGlyphCacheManager)) { FT_Error error; - return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphData, error); + return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(impl->mFreeTypeFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphData, error); } return false; } diff --git a/dali/internal/text/text-abstraction/plugin/lru-cache-container.h b/dali/internal/text/text-abstraction/plugin/lru-cache-container.h index 5c84f67..50b57b9 100644 --- a/dali/internal/text/text-abstraction/plugin/lru-cache-container.h +++ b/dali/internal/text/text-abstraction/plugin/lru-cache-container.h @@ -46,7 +46,7 @@ class LRUCacheContainer { public: // Constructor - LRUCacheContainer(std::size_t maxNumberOfCache = std::numeric_limits::max()) + LRUCacheContainer(std::size_t maxNumberOfCache = std::numeric_limits::max() - 2u) : mCacheMaxSize(maxNumberOfCache) { } @@ -57,6 +57,9 @@ public: LRUCacheContainer(const LRUCacheContainer& rhs) = default; LRUCacheContainer(LRUCacheContainer&& rhs) = default; + LRUCacheContainer& operator=(const LRUCacheContainer& rhs) = default; + LRUCacheContainer& operator=(LRUCacheContainer&& rhs) = default; + public: // Public struct area. using CacheId = std::size_t; ///< The id of cached element. It can be used until element poped out. @@ -71,24 +74,96 @@ public: */ static constexpr CacheId CACHE_FOOTER_ID = std::numeric_limits::max() - 1u; +public: + // iterator. /** - * @brief Double linked CacheNode that this container used. + * @brief Iterator of this LRU cache. + * + * Add, Get and Clear invalidate the itertator's validation. + * Erase and Pop validate the iterator. + * + * Range based iteration doesn't supported. */ - struct CacheNode + struct iterator { - CacheNode() = default; - ~CacheNode() = default; + public: + iterator(LRUCacheContainer& owner, const CacheId& id) + : owner(owner), + id(id) + { + } - CacheId prev{CACHE_FOOTER_ID}; - CacheId next{CACHE_HEADER_ID}; - ElementType element; + // copy constructor & assign + iterator(const iterator& rhs) + : owner(rhs.owner), + id(rhs.id) + { + } - using CacheIdIterator = typename CacheIdContainer::iterator; + iterator& operator=(const iterator& rhs) + { + this->owner = rhs.owner; + this->id = rhs.id; + return *this; + } - CacheIdIterator cacheIdIterator; ///< Note : It only validate until mCacheId rehashing. - }; + // Move constructor & assign + iterator(iterator&& rhs) + : owner(std::move(rhs.owner)), + id(rhs.id) + { + } + + iterator& operator=(iterator&& rhs) + { + this->owner = std::move(rhs.owner); + this->id = rhs.id; + return *this; + } - using iterator = typename std::vector::iterator; + // Prefix increment + iterator& operator++() + { + id = owner.mData[id].next; + return *this; + } + + // Postfix increment + iterator operator++(int) + { + iterator temp = *this; + ++(*this); + return temp; + } + + // Prefix decrement + iterator& operator--() + { + id = owner.mData[id].prev; + return *this; + } + + // Postfix decrement + iterator operator--(int) + { + iterator temp = *this; + --(*this); + return temp; + } + + bool operator==(const iterator& rhs) + { + return id == rhs.id && (&owner) == (&rhs.owner); + } + bool operator!=(const iterator& rhs) + { + return id != rhs.id || (&owner) != (&rhs.owner); + } + + public: + LRUCacheContainer& owner; // The reference of owner of this iterator. + CacheId id; + }; public: // Public API area. @@ -191,7 +266,35 @@ public: } /** - * @brief Find an element by the key. It will be marked as recent + * @brief Get an key by iterator. It will not be marked as recent + * + * @param[in] iter The iterator of element + * @return A reference of the key + * @warning This method don't check iterator validation + */ + const KeyType& GetKey(iterator iter) + { + const auto id = iter.id; + + return mData[id].cacheIdIterator->first; + } + + /** + * @brief Get an element by iterator. It will not be marked as recent + * + * @param[in] iter The iterator of element + * @return A reference of the element + * @warning This method don't check iterator validation + */ + ElementType& GetElement(iterator iter) + { + const auto id = iter.id; + + return mData[id].element; + } + + /** + * @brief Find an element by the key. It will not be marked as recent * * @param[in] key The key of element * @return A iterator of cache node. If key not exist, return End() @@ -205,11 +308,7 @@ public: const auto id = mCacheId[key]; - // Mark as recently used. - InternalPop(id); - InternalInsertAfterHeader(id); - - return Begin() + id; + return iterator(*this, id); } /** @@ -250,12 +349,34 @@ public: iterator Begin() { - return mData.begin(); + return iterator(*this, mLatestId); } iterator End() { - return mData.end(); + return iterator(*this, CACHE_FOOTER_ID); + } + + /** + * @brief Remove cache item by iterator. + * + * @param[in] iter The iterator what we want to remove. + * @return iterator The next iterator after remove + * @warning This method don't check iterator validation + */ + iterator Erase(iterator iter) + { + const auto id = iter.id; + const auto nextId = mData[id].next; + InternalPop(id); + InternalInsertAfterFooter(id); + + --mNumberOfElements; + + // Erase cache id. + mCacheId.erase(mData[id].cacheIdIterator); + + return iterator(*this, nextId); } /** @@ -269,9 +390,6 @@ public: } private: - // Private struct area. - -private: // Private API area. /** @@ -416,6 +534,25 @@ private: } private: + // Private struct area. + /** + * @brief Double linked CacheNode that this container used. + */ + struct CacheNode + { + CacheNode() = default; + ~CacheNode() = default; + + CacheId prev{CACHE_FOOTER_ID}; + CacheId next{CACHE_HEADER_ID}; + ElementType element; + + using CacheIdIterator = typename CacheIdContainer::iterator; + + CacheIdIterator cacheIdIterator; ///< Note : It only validate until mCacheId rehashing. + }; + +private: // Private member value area. std::size_t mCacheMaxSize{0}; ///< The maximum capacity of cache. std::size_t mNumberOfElements{0}; ///< The number of elements. -- 2.7.4