From fd9790b418a12b46c3112f00cadef6716094f8c3 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Mon, 18 Nov 2024 11:33:34 +0100 Subject: [PATCH 01/20] Mechanism to define priority fallback fonts through lua AddFallbackFont and ClearFallbackFonts. --- rts/Lua/LuaFonts.cpp | 18 ++++ rts/Lua/LuaFonts.h | 2 + rts/Rendering/Fonts/CFontTexture.cpp | 141 +++++++++++++++++++++++---- rts/Rendering/Fonts/CFontTexture.h | 2 + 4 files changed, 145 insertions(+), 18 deletions(-) diff --git a/rts/Lua/LuaFonts.cpp b/rts/Lua/LuaFonts.cpp index cfaf21c7bf..ddae996a32 100644 --- a/rts/Lua/LuaFonts.cpp +++ b/rts/Lua/LuaFonts.cpp @@ -25,6 +25,8 @@ bool LuaFonts::PushEntries(lua_State* L) REGISTER_LUA_CFUNC(LoadFont); REGISTER_LUA_CFUNC(DeleteFont); + REGISTER_LUA_CFUNC(AddFallbackFont); + REGISTER_LUA_CFUNC(ClearFallbackFonts); return true; } @@ -205,6 +207,22 @@ int LuaFonts::DeleteFont(lua_State* L) return meta_gc(L); } +int LuaFonts::AddFallbackFont(lua_State* L) +{ + RECOIL_DETAILED_TRACY_ZONE; + + const bool f = CFontTexture::AddFallbackFont(luaL_checkstring(L, 1)); + lua_pushboolean(L, f); + return 1; +} + +int LuaFonts::ClearFallbackFonts(lua_State* L) +{ + RECOIL_DETAILED_TRACY_ZONE; + + CFontTexture::ClearFallbackFonts(); + return 0; +} /******************************************************************************/ /******************************************************************************/ diff --git a/rts/Lua/LuaFonts.h b/rts/Lua/LuaFonts.h index 3d5f0f912f..c423b12147 100644 --- a/rts/Lua/LuaFonts.h +++ b/rts/Lua/LuaFonts.h @@ -22,6 +22,8 @@ class LuaFonts { private: // call-outs static int LoadFont(lua_State* L); static int DeleteFont(lua_State* L); + static int AddFallbackFont(lua_State* L); + static int ClearFallbackFonts(lua_State* L); private: // userdata call-outs static int Print(lua_State* L); diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index 753d1e0b41..1d753e3c21 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -88,6 +88,10 @@ class FtLibraryHandler { FtLibraryHandler() : config(nullptr) , lib(nullptr) + #ifdef USE_FONTCONFIG + , gameFontSet(nullptr) + , fallbackPattern(nullptr) + #endif // USE_FONTCONFIG { { const FT_Error error = FT_Init_FreeType(&lib); @@ -129,6 +133,9 @@ class FtLibraryHandler { FcConfigDestroy(config); config = nullptr; } + + gameFontSet = FcFontSetCreate(); + fallbackPattern = FcPatternCreate(); } #endif } @@ -141,6 +148,8 @@ class FtLibraryHandler { return; FcConfigDestroy(config); + FcFontSetDestroy(gameFontSet); + FcPatternDestroy(fallbackPattern); FcFini(); config = nullptr; #endif @@ -243,9 +252,29 @@ class FtLibraryHandler { static inline bool CanUseFontConfig() { return GetFCConfig() != nullptr; } + #ifdef USE_FONTCONFIG + static FcFontSet *GetGameFontSet() { + return singleton->gameFontSet; + } + static FcPattern *GetFallbackPattern() { + return singleton->fallbackPattern; + } + static void ClearGameFontSet() { + FcFontSetDestroy(singleton->gameFontSet); + singleton->gameFontSet = FcFontSetCreate(); + } + static void ClearFallbackPattern() { + FcPatternDestroy(singleton->fallbackPattern); + singleton->fallbackPattern = FcPatternCreate(); + } + #endif private: FcConfig* config; FT_Library lib; + #ifdef USE_FONTCONFIG + FcFontSet *gameFontSet; + FcPattern *fallbackPattern; + #endif static inline std::unique_ptr singleton = nullptr; }; @@ -302,20 +331,12 @@ static inline uint64_t GetKerningHash(char32_t lchar, char32_t rchar) return (static_cast(lchar) << 32) | static_cast(rchar); // 64bit used } -static std::shared_ptr GetFontFace(const std::string& fontfile, const int size) +static std::shared_ptr LoadFontFace(const std::string& fontfile) { RECOIL_DETAILED_TRACY_ZONE; assert(CFontTexture::sync.GetThreadSafety() || Threading::IsMainThread()); auto lock = CFontTexture::sync.GetScopedLock(); - //TODO add support to load fonts by name (needs fontconfig) - - const auto fontKey = fontfile + IntToString(size); - const auto fontIt = fontFaceCache.find(fontKey); - - if (fontIt != fontFaceCache.end() && !fontIt->second.expired()) - return fontIt->second.lock(); - // get the file (no need to cache, takes too little time) std::string fontPath(fontfile); CFileHandler f(fontPath); @@ -355,17 +376,37 @@ static std::shared_ptr GetFontFace(const std::string& fontfile, const throw content_error(fmt::format("FT_New_Face failed: {}", GetFTError(error))); } + return std::make_shared(face.Release(), fontMem); +} + +static std::shared_ptr GetRenderFontFace(const std::string& fontfile, const int size) +{ + RECOIL_DETAILED_TRACY_ZONE; + assert(CFontTexture::sync.GetThreadSafety() || Threading::IsMainThread()); + auto lock = CFontTexture::sync.GetScopedLock(); + + //TODO add support to load fonts by name (needs fontconfig) + + FT_Error error; + + const auto fontKey = fontfile + IntToString(size); + const auto fontIt = fontFaceCache.find(fontKey); + + if (fontIt != fontFaceCache.end() && !fontIt->second.expired()) + return fontIt->second.lock(); + + std::shared_ptr facePtr = LoadFontFace(fontfile); + // set render size - if ((error = FT_Set_Pixel_Sizes(face, 0, size)) != 0) { + if ((error = FT_Set_Pixel_Sizes(facePtr->face, 0, size)) != 0) { throw content_error(fmt::format("FT_Set_Pixel_Sizes failed: {}", GetFTError(error))); } // select unicode charmap - if ((error = FT_Select_Charmap(face, FT_ENCODING_UNICODE)) != 0) { + if ((error = FT_Select_Charmap(facePtr->face, FT_ENCODING_UNICODE)) != 0) { throw content_error(fmt::format("FT_Select_Charmap failed: {}", GetFTError(error))); } - - return (fontFaceCache[fontKey] = std::make_shared(face.Release(), fontMem)).lock(); + return (fontFaceCache[fontKey] = facePtr).lock(); } #endif @@ -403,9 +444,9 @@ static std::shared_ptr GetFontForCharacters(const std::vector GetFontForCharacters(const std::vector GetFontForCharacters(const std::vectornfont; ++i) { + FcPattern* font = set->fonts[i]; + FcChar8 *file; + if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) { + if (fontfile.compare(reinterpret_cast(file)) == 0) { + return true; + } + } + } + + // Load font face + std::shared_ptr facePtr; + try { + facePtr = LoadFontFace(fontfile); + } catch (content_error& ex) { + LOG_L(L_ERROR, "[%s] \"%s\": %s", __func__, fontfile.c_str(), ex.what()); + return false; + } + + // Add fontconfig configuration + FT_Face face = *facePtr; + + // Store pattern + FcPattern* pattern = FcFreeTypeQueryFace(face, reinterpret_cast(fontfile.c_str()), 0, NULL); + if (!FcFontSetAdd(set, pattern)) + { + LOG_L(L_WARNING, "[%s] could not add pattern for %s", __func__, fontfile.c_str()); + return false; + } + // needed?: + //FcConfigSubstitute(FtLibraryHandler::GetFCConfig(), pattern, FcMatchScan); + + // Add to priority fonts pattern + FcChar8* family = nullptr; + if (FcPatternGetString( pattern, FC_FAMILY , 0, &family ) == FcResultMatch) { + FcPattern *fallbackPattern = FtLibraryHandler::GetFallbackPattern(); + FcPatternAddString(fallbackPattern, FC_FAMILY, family); + } else { + LOG_L(L_WARNING, "[%s] could not add priority for %s", __func__, fontfile.c_str()); + return false; + } + return true; +#else + return false; +#endif +} + +void CFontTexture::ClearFallbackFonts() +{ +#if defined(USE_FONTCONFIG) && !defined(HEADLESS) + FtLibraryHandler::ClearFallbackPattern(); + FtLibraryHandler::ClearGameFontSet(); +#endif +} + void CFontTexture::InitFonts() { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Rendering/Fonts/CFontTexture.h b/rts/Rendering/Fonts/CFontTexture.h index 8e66c70b3e..83d2748843 100644 --- a/rts/Rendering/Fonts/CFontTexture.h +++ b/rts/Rendering/Fonts/CFontTexture.h @@ -113,6 +113,8 @@ class CFontTexture static void InitFonts(); static void KillFonts(); static void Update(); + static bool AddFallbackFont(const std::string& fontfile); + static void ClearFallbackFonts(); inline static spring::WrappedSyncRecursiveMutex sync = {}; protected: From 3a65eee90a92dce7dee302d287a66564ec9f24cd Mon Sep 17 00:00:00 2001 From: Saurtron Date: Mon, 18 Nov 2024 19:48:47 +0100 Subject: [PATCH 02/20] LDoc documentation for AddFallbackFont and ClearFallbackFonts. --- rts/Lua/LuaFonts.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rts/Lua/LuaFonts.cpp b/rts/Lua/LuaFonts.cpp index ddae996a32..87d11fece6 100644 --- a/rts/Lua/LuaFonts.cpp +++ b/rts/Lua/LuaFonts.cpp @@ -207,6 +207,16 @@ int LuaFonts::DeleteFont(lua_State* L) return meta_gc(L); } +/*** Adds a fallback font for the font rendering engine. + * + * Fonts added first will have higher priority. + * When a glyph isn't found when rendering a font, the fallback fonts + * will be searched first, otherwise os fonts will be used. + * + * @function gl.AddFallbackFont + * @string filePath VFS path to the file, for example "fonts/myfont.ttf" + * @treturn bool success + */ int LuaFonts::AddFallbackFont(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -216,6 +226,11 @@ int LuaFonts::AddFallbackFont(lua_State* L) return 1; } +/*** Clears all fallback fonts. + * + * @function gl.ClearFallbackFonts + * @treturn nil + */ int LuaFonts::ClearFallbackFonts(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; From a67a9fdb714ecd0409e271a189db58ef96dfc743 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Tue, 19 Nov 2024 03:21:30 +0100 Subject: [PATCH 03/20] First search game fonts and then system fonts as otherwise fontconfig can still sometimes have it's own priorities. --- rts/Rendering/Fonts/CFontTexture.cpp | 39 +++++++++++++++++----------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index 1d753e3c21..906e8ed77e 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -513,23 +513,32 @@ static std::shared_ptr GetFontForCharacters(const std::vector ScopedFcFontSet; - if (fs == nullptr) - return nullptr; - if (res != FcResultMatch) - return nullptr; + int nFonts = 0; + bool loadMore = true; + FcResult res; - // iterate returned font list - for (int i = 0; i < fs->nfont; ++i) { - const FcPattern* font = fs->fonts[i]; + // first search game fonts + FcFontSet *sets[] = { FtLibraryHandler::GetGameFontSet() }; + ScopedFcFontSet fs(FcFontSetSort(FtLibraryHandler::GetFCConfig(), sets, 1, pattern, FcFalse, nullptr, &res), &FcFontSetDestroy); + + if (fs != nullptr && res == FcResultMatch) + nFonts = fs->nfont; + + // iterate returned font list, and perform system font search when in need of more fonts + int i = 0; + while (i < nFonts || loadMore) { + if (i == nFonts) { + // now search system fonts + fs = ScopedFcFontSet(FcFontSort(FtLibraryHandler::GetFCConfig(), pattern, FcFalse, nullptr, &res), &FcFontSetDestroy); + if (fs == nullptr || res != FcResultMatch) + return nullptr; + loadMore = false; + nFonts = fs->nfont; + i = 0; + } + const FcPattern* font = fs->fonts[i++]; FcChar8* cFilename = nullptr; FcResult r = FcPatternGetString(font, FC_FILE, 0, &cFilename); From 1241cccd24bc7ec4da86ffffeedfcd8fa3047f5e Mon Sep 17 00:00:00 2001 From: Saurtron Date: Wed, 27 Nov 2024 11:46:20 +0100 Subject: [PATCH 04/20] Don't add or clear fallback fonts if fontconfig is disabled for any reason. --- rts/Rendering/Fonts/CFontTexture.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index 906e8ed77e..9fcb7ae902 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -690,6 +690,9 @@ CFontTexture::~CFontTexture() bool CFontTexture::AddFallbackFont(const std::string& fontfile) { #if defined(USE_FONTCONFIG) && !defined(HEADLESS) + if (!FtLibraryHandler::UseFontConfig()) + return false; + FcFontSet *set = FtLibraryHandler::GetGameFontSet(); // Check if font already loaded @@ -743,6 +746,9 @@ bool CFontTexture::AddFallbackFont(const std::string& fontfile) void CFontTexture::ClearFallbackFonts() { #if defined(USE_FONTCONFIG) && !defined(HEADLESS) + if (!FtLibraryHandler::UseFontConfig()) + return false; + FtLibraryHandler::ClearFallbackPattern(); FtLibraryHandler::ClearGameFontSet(); #endif From a3d0cfaa4264b5e6e736055f9961f35ba07c6f3e Mon Sep 17 00:00:00 2001 From: Saurtron Date: Wed, 27 Nov 2024 16:08:28 +0100 Subject: [PATCH 05/20] Don't return false on a void function. --- rts/Rendering/Fonts/CFontTexture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index 9fcb7ae902..67c3a89f28 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -747,7 +747,7 @@ void CFontTexture::ClearFallbackFonts() { #if defined(USE_FONTCONFIG) && !defined(HEADLESS) if (!FtLibraryHandler::UseFontConfig()) - return false; + return; FtLibraryHandler::ClearFallbackPattern(); FtLibraryHandler::ClearGameFontSet(); From a1c7135158936010ff596cba9a7e7e638b8ad964 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Mon, 2 Dec 2024 01:33:19 +0100 Subject: [PATCH 06/20] Don't try to destroy gameFontSet and fallbackPattern if null. This is only safe to do on recent fontconfig versions. --- rts/Rendering/Fonts/CFontTexture.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index 53ae80e764..fa5249106e 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -149,8 +149,12 @@ class FtLibraryHandler { return; FcConfigDestroy(config); - FcFontSetDestroy(gameFontSet); - FcPatternDestroy(fallbackPattern); + if (gameFontSet) { + FcFontSetDestroy(gameFontSet); + } + if (fallbackPattern) { + FcPatternDestroy(fallbackPattern); + } FcFini(); config = nullptr; #endif From 74ff5f4e6daceb6c0ec4636e92ff8833530e1e7b Mon Sep 17 00:00:00 2001 From: Saurtron Date: Mon, 2 Dec 2024 03:19:41 +0100 Subject: [PATCH 07/20] Call CanUseFontConfig instead of UseFontConfig inside new methods. --- rts/Rendering/Fonts/CFontTexture.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index fa5249106e..d2242121e7 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -681,7 +681,7 @@ CFontTexture::~CFontTexture() bool CFontTexture::AddFallbackFont(const std::string& fontfile) { #if defined(USE_FONTCONFIG) && !defined(HEADLESS) - if (!FtLibraryHandler::UseFontConfig()) + if (!FtLibraryHandler::CanUseFontConfig()) return false; FcFontSet *set = FtLibraryHandler::GetGameFontSet(); @@ -737,7 +737,7 @@ bool CFontTexture::AddFallbackFont(const std::string& fontfile) void CFontTexture::ClearFallbackFonts() { #if defined(USE_FONTCONFIG) && !defined(HEADLESS) - if (!FtLibraryHandler::UseFontConfig()) + if (!FtLibraryHandler::CanUseFontConfig()) return; FtLibraryHandler::ClearFallbackPattern(); From b6352a260b0a4b4a944a7225836e43bf0543b432 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Sat, 30 Nov 2024 11:39:11 +0100 Subject: [PATCH 08/20] Refresh glyph atlases after adding or clearing fallback fonts. --- cont/LuaUI/callins.lua | 2 + cont/LuaUI/widgets.lua | 13 ++ .../base/springcontent/LuaGadgets/callins.lua | 2 + .../base/springcontent/LuaGadgets/gadgets.lua | 9 ++ rts/Lua/LuaFonts.cpp | 48 +++++++- rts/Lua/LuaHandle.cpp | 22 ++++ rts/Lua/LuaHandle.h | 2 + rts/Rendering/Fonts/CFontTexture.cpp | 115 +++++++++++++++++- rts/Rendering/Fonts/CFontTexture.h | 15 ++- rts/System/EventClient.h | 2 + rts/System/EventHandler.cpp | 9 ++ rts/System/EventHandler.h | 1 + rts/System/Events.def | 2 + 13 files changed, 235 insertions(+), 7 deletions(-) diff --git a/cont/LuaUI/callins.lua b/cont/LuaUI/callins.lua index a7e5bb947b..944da0111d 100644 --- a/cont/LuaUI/callins.lua +++ b/cont/LuaUI/callins.lua @@ -94,6 +94,8 @@ CallInsList = { "DrawScreen", "DrawInMiniMap", + "FontsChanged", + "SunChanged", "Explosion", diff --git a/cont/LuaUI/widgets.lua b/cont/LuaUI/widgets.lua index ac2a3adabc..99604a9a62 100644 --- a/cont/LuaUI/widgets.lua +++ b/cont/LuaUI/widgets.lua @@ -169,6 +169,7 @@ local flexCallIns = { 'DrawScreenEffects', 'DrawScreenPost', 'DrawInMiniMap', + 'FontsChanged', 'SunChanged', 'RecvSkirmishAIMessage', } @@ -2167,6 +2168,18 @@ function widgetHandler:DownloadProgress(id, downloaded, total) end end + +-------------------------------------------------------------------------------- +-- +-- Font call-ins +-- + +function widgetHandler:FontsChaged() + for _,w in ripairs(self.FontsChangedList) do + w:FontsChanged() + end +end + -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- diff --git a/cont/base/springcontent/LuaGadgets/callins.lua b/cont/base/springcontent/LuaGadgets/callins.lua index e820e39f8f..ebd2093ff3 100644 --- a/cont/base/springcontent/LuaGadgets/callins.lua +++ b/cont/base/springcontent/LuaGadgets/callins.lua @@ -167,6 +167,8 @@ CALLIN_LIST = { "DrawProjectile", "DrawMaterial", + "FontsChanged", + "SunChanged", -- unsynced message callins diff --git a/cont/base/springcontent/LuaGadgets/gadgets.lua b/cont/base/springcontent/LuaGadgets/gadgets.lua index 2f72136cdb..3c59411c31 100644 --- a/cont/base/springcontent/LuaGadgets/gadgets.lua +++ b/cont/base/springcontent/LuaGadgets/gadgets.lua @@ -2198,6 +2198,15 @@ end -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- +function gadgetHandler:FontsChanged() + for _,g in r_ipairs(self.FontsChangedList) do + g:FontsChanged() + end +end + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- + gadgetHandler:Initialize() -------------------------------------------------------------------------------- diff --git a/rts/Lua/LuaFonts.cpp b/rts/Lua/LuaFonts.cpp index 87d11fece6..995c306185 100644 --- a/rts/Lua/LuaFonts.cpp +++ b/rts/Lua/LuaFonts.cpp @@ -207,35 +207,79 @@ int LuaFonts::DeleteFont(lua_State* L) return meta_gc(L); } +/*** Helper to parse the glyph clear mode */ +CFontTexture::ClearGlyphMode ParseGlyphClearMode(lua_State* L, int index) +{ + static const constexpr auto fastMode = hashString("fast"); + static const constexpr auto noneMode = hashString("none"); + + const std::string& clearStr = luaL_optstring(L, index, ""); + + CFontTexture::ClearGlyphMode clearMode; + switch(hashString(clearStr)) { + case noneMode: + clearMode = CFontTexture::ClearGlyphMode::none; + break; + case fastMode: + clearMode = CFontTexture::ClearGlyphMode::fast; + break; + default: + clearMode = CFontTexture::ClearGlyphMode::full; + } + return clearMode; +} + /*** Adds a fallback font for the font rendering engine. * * Fonts added first will have higher priority. * When a glyph isn't found when rendering a font, the fallback fonts * will be searched first, otherwise os fonts will be used. * + * This will also clear caches, depending on the 'clearMode': + * - full: Clear all glyphs, textures will be invalidated. + * - fast: Only clears some glyphs, but doesn't free space from atlases. + * - none: Skip clearing anything. + * + * The application should listen for the unsynced 'FontsUpdated' callin so + * modules can clear any already reserved display lists or other relevant + * caches. + * + * Note the cache clearing and calling won't be done at the time of calling this + * method, but later, on the Update cycle (before other Update and Draw callins). + * + * * @function gl.AddFallbackFont * @string filePath VFS path to the file, for example "fonts/myfont.ttf" + * @string clearMode full|fast|none, default: full * @treturn bool success */ int LuaFonts::AddFallbackFont(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; - const bool f = CFontTexture::AddFallbackFont(luaL_checkstring(L, 1)); + const auto font = luaL_checkstring(L, 1); + auto clearMode = ParseGlyphClearMode(L, 2); + + const bool f = CFontTexture::AddFallbackFont(font, clearMode); lua_pushboolean(L, f); return 1; } /*** Clears all fallback fonts. + * + * See note at 'AddFallbackFont' about 'clearMode' usage. * * @function gl.ClearFallbackFonts + * @string clearMode full|fast|none, default: full * @treturn nil */ int LuaFonts::ClearFallbackFonts(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; - CFontTexture::ClearFallbackFonts(); + auto clearMode = ParseGlyphClearMode(L, 1); + + CFontTexture::ClearFallbackFonts(clearMode); return 0; } diff --git a/rts/Lua/LuaHandle.cpp b/rts/Lua/LuaHandle.cpp index 3dac9b6ac2..474e77899d 100644 --- a/rts/Lua/LuaHandle.cpp +++ b/rts/Lua/LuaHandle.cpp @@ -2428,6 +2428,27 @@ void CLuaHandle::ViewResize() RunCallIn(L, cmdStr, 1, 0); } +/*** Called whenever fonts are updated. Signals the game display lists + * and other caches should be discarded. + * + * Gets called before other Update and Draw callins. + * + * @function FontsChanged + */ +void CLuaHandle::FontsChanged() +{ + RECOIL_DETAILED_TRACY_ZONE; + LUA_CALL_IN_CHECK(L); + luaL_checkstack(L, 2, __func__); + + static const LuaHashString cmdStr(__func__); + + if (!cmdStr.GetGlobalFunc(L)) + return; + + RunCallIn(L, cmdStr, 0, 0); +} + /*** * @function SunChanged */ @@ -2869,6 +2890,7 @@ void CLuaHandle::GameProgress(int frameNum) RunCallIn(L, cmdStr, 1, 0); } + void CLuaHandle::Pong(uint8_t pingTag, const spring_time pktSendTime, const spring_time pktRecvTime) { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Lua/LuaHandle.h b/rts/Lua/LuaHandle.h index c9e86055f7..a1988ee322 100644 --- a/rts/Lua/LuaHandle.h +++ b/rts/Lua/LuaHandle.h @@ -237,6 +237,8 @@ class CLuaHandle : public CEventClient void ViewResize() override; + void FontsChanged() override; + void SunChanged() override; void DrawGenesis() override; diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index d2242121e7..4a315a6597 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -23,6 +23,7 @@ #include "Rendering/Textures/Bitmap.h" #include "System/Config/ConfigHandler.h" #include "System/Exceptions.h" +#include "System/EventHandler.h" #include "System/Log/ILog.h" #include "System/FileSystem/FileHandler.h" #include "System/Threading/ThreadPool.h" @@ -648,7 +649,19 @@ CFontTexture::CFontTexture(const std::string& fontfile, int size, int _outlinesi CreateTexture(32, 32); // precache ASCII glyphs & kernings (save them in kerningPrecached array for better lvl2 cpu cache hitrate) + PreloadGlyphs(); +#endif +} + +/*** + * + * Preloads standard alphabet glyphs for a font + */ +void CFontTexture::PreloadGlyphs() +{ +#ifndef HEADLESS + FT_Face face = *shFace; //preload Glyphs LoadWantedGlyphs(32, 127); for (char32_t i = 32; i < 127; ++i) { @@ -665,6 +678,7 @@ CFontTexture::CFontTexture(const std::string& fontfile, int size, int _outlinesi } } #endif + } CFontTexture::~CFontTexture() @@ -677,8 +691,14 @@ CFontTexture::~CFontTexture() #endif } - -bool CFontTexture::AddFallbackFont(const std::string& fontfile) +/*** + * + * Add a fallback font + * + * @param fontfile VFS path for the font + * @param clearMode ClearGlyphMode + */ +bool CFontTexture::AddFallbackFont(const std::string& fontfile, ClearGlyphMode clearMode) { #if defined(USE_FONTCONFIG) && !defined(HEADLESS) if (!FtLibraryHandler::CanUseFontConfig()) @@ -728,13 +748,22 @@ bool CFontTexture::AddFallbackFont(const std::string& fontfile) LOG_L(L_WARNING, "[%s] could not add priority for %s", __func__, fontfile.c_str()); return false; } + + needClearGlyphs = clearMode; + return true; #else return false; #endif } -void CFontTexture::ClearFallbackFonts() +/*** + * + * Clears fontconfig fallbacks + * + * @param clearMode ClearGlyphMode + */ +void CFontTexture::ClearFallbackFonts(ClearGlyphMode clearMode) { #if defined(USE_FONTCONFIG) && !defined(HEADLESS) if (!FtLibraryHandler::CanUseFontConfig()) @@ -742,6 +771,84 @@ void CFontTexture::ClearFallbackFonts() FtLibraryHandler::ClearFallbackPattern(); FtLibraryHandler::ClearGameFontSet(); + needClearGlyphs = clearMode; +#endif +} + +/*** + * + * Clears all glyphs for all fonts + * + * @param clearMode ClearGlyphMode + */ +void CFontTexture::ClearAllGlyphs(ClearGlyphMode clearMode) { +#ifndef HEADLESS + RECOIL_DETAILED_TRACY_ZONE; + if (clearMode == ClearGlyphMode::none) + return; + + bool changed = false; + for (const auto& ft : allFonts) { + auto lf = ft.lock(); + changed |= lf->ClearGlyphs(clearMode); + } + if (changed) + eventHandler.FontsChanged(); + + needClearGlyphs = ClearGlyphMode::none; +#endif +} + +/*** + * + * Clears all glyphs for a font + * + * @param clearMode ClearGlyphMode + */ +bool CFontTexture::ClearGlyphs(ClearGlyphMode clearMode) { +#ifndef HEADLESS + RECOIL_DETAILED_TRACY_ZONE; + + bool changed = false; + + // Invalidate glyphs coming from other fonts, or those with the 'not found' glyph. + for (const auto& g : glyphs) { + if (g.second.face->face != shFace->face || g.second.index == 0) { + if (clearMode == ClearGlyphMode::fast) { + // in 'fast' clear mode, clear just extraneus glyphs. + // this can be easier to manage for the application initially, since + // old glyphs still work, but also means the atlas will keep + // filling up. + glyphs.erase(g.first); + } + changed = true; + } + } + + failedAttemptsToReplace.clear(); + + if (!changed) + return false; + + if (clearMode == ClearGlyphMode::full) { + // clear all glyps + glyphs.clear(); + + // refresh the atlasAlloc to reset coordinates + atlasAlloc.clear(); // just in case + atlasAlloc = CRowAtlasAlloc(); + atlasAlloc.SetMaxSize(globalRendering->maxTextureSize, globalRendering->maxTextureSize); + + // clear atlases + ReallocAtlases(false); + + // preload standard glyphs + PreloadGlyphs(); + + // signal need to update texture + ++curTextureUpdate; + } + return true; #endif } @@ -774,6 +881,8 @@ void CFontTexture::Update() { static std::vector> fontsToUpdate; fontsToUpdate.clear(); + ClearAllGlyphs(needClearGlyphs); + for (const auto& font : allFonts) { auto lf = font.lock(); if (lf->GlyphAtlasTextureNeedsUpdate() || lf->GlyphAtlasTextureNeedsUpload()) diff --git a/rts/Rendering/Fonts/CFontTexture.h b/rts/Rendering/Fonts/CFontTexture.h index 83d2748843..743a333160 100644 --- a/rts/Rendering/Fonts/CFontTexture.h +++ b/rts/Rendering/Fonts/CFontTexture.h @@ -105,6 +105,12 @@ It works only and only with UTF32 chars class CFontTexture { public: + enum class ClearGlyphMode: unsigned int { + none = 0, + fast = 1, + full = 2 + }; + friend class CglFontRenderer; friend class CglShaderFontRenderer; friend class CglNoShaderFontRenderer; @@ -113,8 +119,9 @@ class CFontTexture static void InitFonts(); static void KillFonts(); static void Update(); - static bool AddFallbackFont(const std::string& fontfile); - static void ClearFallbackFonts(); + static bool AddFallbackFont(const std::string& fontfile, ClearGlyphMode clearMode); + static void ClearFallbackFonts(ClearGlyphMode clearMode); + static void ClearAllGlyphs(ClearGlyphMode clearMode); inline static spring::WrappedSyncRecursiveMutex sync = {}; protected: @@ -147,12 +154,15 @@ class CFontTexture private: void CreateTexture(const int width, const int height); void LoadGlyph(std::shared_ptr& f, char32_t ch, unsigned index); + bool ClearGlyphs(ClearGlyphMode clearMode); + void PreloadGlyphs(); protected: float GetKerning(const GlyphInfo& lgl, const GlyphInfo& rgl); protected: static inline std::vector> allFonts = {}; static inline const GlyphInfo dummyGlyph = GlyphInfo(); + static inline ClearGlyphMode needClearGlyphs = ClearGlyphMode::none; std::array kerningPrecached = {}; // contains ASCII kerning @@ -199,6 +209,7 @@ class CFontTexture public: auto GetGlyphs() const -> const decltype(glyphs) { return glyphs; } auto GetGlyphs() -> decltype(glyphs) { return glyphs; } + }; #endif // CFONTTEXTURE_H diff --git a/rts/System/EventClient.h b/rts/System/EventClient.h index 79d978c01e..eb6d540e88 100644 --- a/rts/System/EventClient.h +++ b/rts/System/EventClient.h @@ -361,6 +361,8 @@ class CEventClient virtual void DrawShadowUnitsLua() {} virtual void DrawShadowFeaturesLua() {} + virtual void FontsChanged() {} + virtual void GameProgress(int gameFrame); virtual void DrawLoadScreen(); diff --git a/rts/System/EventHandler.cpp b/rts/System/EventHandler.cpp index a0aee9a820..62cdac82b3 100644 --- a/rts/System/EventHandler.cpp +++ b/rts/System/EventHandler.cpp @@ -996,3 +996,12 @@ void CEventHandler::DrawShadowFeaturesLua() } /******************************************************************************/ +/******************************************************************************/ + +void CEventHandler::FontsChanged() +{ + ZoneScoped; + ITERATE_EVENTCLIENTLIST_NA(FontsChanged); +} + +/******************************************************************************/ diff --git a/rts/System/EventHandler.h b/rts/System/EventHandler.h index 66d7126d7e..6a641f9a1f 100644 --- a/rts/System/EventHandler.h +++ b/rts/System/EventHandler.h @@ -242,6 +242,7 @@ class CEventHandler bool GroupChanged(int groupID); + void FontsChanged(); bool GameSetup(const std::string& state, bool& ready, const std::vector< std::pair >& playerStates); void DownloadQueued(int ID, const string& archiveName, const string& archiveType); diff --git a/rts/System/Events.def b/rts/System/Events.def index 0120eab369..28536101bf 100644 --- a/rts/System/Events.def +++ b/rts/System/Events.def @@ -158,6 +158,8 @@ SETUP_EVENT(DrawShadowUnitsLua, MANAGED_BIT | UNSYNCED_BIT) SETUP_EVENT(DrawShadowFeaturesLua, MANAGED_BIT | UNSYNCED_BIT) + SETUP_EVENT(FontsChanged, MANAGED_BIT | UNSYNCED_BIT) + SETUP_EVENT(RenderUnitPreCreated, MANAGED_BIT | UNSYNCED_BIT) SETUP_EVENT(RenderUnitCreated, MANAGED_BIT | UNSYNCED_BIT) SETUP_EVENT(RenderUnitDestroyed, MANAGED_BIT | UNSYNCED_BIT) From 0fe67861021bc08aabdfbde1b60d4b5a0a2cf4c9 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Sat, 30 Nov 2024 13:41:03 +0100 Subject: [PATCH 09/20] Remove some extraneous extra lines. --- rts/Lua/LuaHandle.cpp | 1 - rts/Rendering/Fonts/CFontTexture.cpp | 1 - rts/Rendering/Fonts/CFontTexture.h | 1 - 3 files changed, 3 deletions(-) diff --git a/rts/Lua/LuaHandle.cpp b/rts/Lua/LuaHandle.cpp index 474e77899d..cf88d0ccca 100644 --- a/rts/Lua/LuaHandle.cpp +++ b/rts/Lua/LuaHandle.cpp @@ -2890,7 +2890,6 @@ void CLuaHandle::GameProgress(int frameNum) RunCallIn(L, cmdStr, 1, 0); } - void CLuaHandle::Pong(uint8_t pingTag, const spring_time pktSendTime, const spring_time pktRecvTime) { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index 4a315a6597..2a4fb4f9ee 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -678,7 +678,6 @@ void CFontTexture::PreloadGlyphs() } } #endif - } CFontTexture::~CFontTexture() diff --git a/rts/Rendering/Fonts/CFontTexture.h b/rts/Rendering/Fonts/CFontTexture.h index 743a333160..f6453a8047 100644 --- a/rts/Rendering/Fonts/CFontTexture.h +++ b/rts/Rendering/Fonts/CFontTexture.h @@ -209,7 +209,6 @@ class CFontTexture public: auto GetGlyphs() const -> const decltype(glyphs) { return glyphs; } auto GetGlyphs() -> decltype(glyphs) { return glyphs; } - }; #endif // CFONTTEXTURE_H From 7e30cae1c41b8760c91b2d3ba5f9eb6a1f10588a Mon Sep 17 00:00:00 2001 From: Saurtron Date: Sat, 30 Nov 2024 21:01:26 +0100 Subject: [PATCH 10/20] Clear kerning precached information too. --- rts/Rendering/Fonts/CFontTexture.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index 2a4fb4f9ee..fed83af262 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -829,6 +829,8 @@ bool CFontTexture::ClearGlyphs(ClearGlyphMode clearMode) { if (!changed) return false; + kerningPrecached = {}; + if (clearMode == ClearGlyphMode::full) { // clear all glyps glyphs.clear(); From 8be1855be9abeffe8f714f7b61ea1cf723969fc7 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Sun, 1 Dec 2024 20:45:21 +0100 Subject: [PATCH 11/20] Remove fast glyph cache clear mode, and all clearMode control from lua. --- rts/Lua/LuaFonts.cpp | 44 ++++------------------------ rts/Rendering/Fonts/CFontTexture.cpp | 40 +++++++++---------------- rts/Rendering/Fonts/CFontTexture.h | 16 ++++------ 3 files changed, 25 insertions(+), 75 deletions(-) diff --git a/rts/Lua/LuaFonts.cpp b/rts/Lua/LuaFonts.cpp index 995c306185..32dfef568d 100644 --- a/rts/Lua/LuaFonts.cpp +++ b/rts/Lua/LuaFonts.cpp @@ -207,50 +207,21 @@ int LuaFonts::DeleteFont(lua_State* L) return meta_gc(L); } -/*** Helper to parse the glyph clear mode */ -CFontTexture::ClearGlyphMode ParseGlyphClearMode(lua_State* L, int index) -{ - static const constexpr auto fastMode = hashString("fast"); - static const constexpr auto noneMode = hashString("none"); - - const std::string& clearStr = luaL_optstring(L, index, ""); - - CFontTexture::ClearGlyphMode clearMode; - switch(hashString(clearStr)) { - case noneMode: - clearMode = CFontTexture::ClearGlyphMode::none; - break; - case fastMode: - clearMode = CFontTexture::ClearGlyphMode::fast; - break; - default: - clearMode = CFontTexture::ClearGlyphMode::full; - } - return clearMode; -} - /*** Adds a fallback font for the font rendering engine. * * Fonts added first will have higher priority. * When a glyph isn't found when rendering a font, the fallback fonts * will be searched first, otherwise os fonts will be used. * - * This will also clear caches, depending on the 'clearMode': - * - full: Clear all glyphs, textures will be invalidated. - * - fast: Only clears some glyphs, but doesn't free space from atlases. - * - none: Skip clearing anything. - * * The application should listen for the unsynced 'FontsUpdated' callin so * modules can clear any already reserved display lists or other relevant * caches. * - * Note the cache clearing and calling won't be done at the time of calling this - * method, but later, on the Update cycle (before other Update and Draw callins). - * + * Note the callin won't be executed at the time of calling this method, + * but later, on the Update cycle (before other Update and Draw callins). * * @function gl.AddFallbackFont * @string filePath VFS path to the file, for example "fonts/myfont.ttf" - * @string clearMode full|fast|none, default: full * @treturn bool success */ int LuaFonts::AddFallbackFont(lua_State* L) @@ -258,28 +229,25 @@ int LuaFonts::AddFallbackFont(lua_State* L) RECOIL_DETAILED_TRACY_ZONE; const auto font = luaL_checkstring(L, 1); - auto clearMode = ParseGlyphClearMode(L, 2); - const bool f = CFontTexture::AddFallbackFont(font, clearMode); + const bool f = CFontTexture::AddFallbackFont(font); lua_pushboolean(L, f); return 1; } /*** Clears all fallback fonts. * - * See note at 'AddFallbackFont' about 'clearMode' usage. + * See the note at 'AddFallbackFont' about the 'FontsUpdated' callin, + * it also applies when calling this method. * * @function gl.ClearFallbackFonts - * @string clearMode full|fast|none, default: full * @treturn nil */ int LuaFonts::ClearFallbackFonts(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; - auto clearMode = ParseGlyphClearMode(L, 1); - - CFontTexture::ClearFallbackFonts(clearMode); + CFontTexture::ClearFallbackFonts(); return 0; } diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index fed83af262..3937801389 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -695,9 +695,8 @@ CFontTexture::~CFontTexture() * Add a fallback font * * @param fontfile VFS path for the font - * @param clearMode ClearGlyphMode */ -bool CFontTexture::AddFallbackFont(const std::string& fontfile, ClearGlyphMode clearMode) +bool CFontTexture::AddFallbackFont(const std::string& fontfile) { #if defined(USE_FONTCONFIG) && !defined(HEADLESS) if (!FtLibraryHandler::CanUseFontConfig()) @@ -748,7 +747,7 @@ bool CFontTexture::AddFallbackFont(const std::string& fontfile, ClearGlyphMode c return false; } - needClearGlyphs = clearMode; + needsClearGlyphs = true; return true; #else @@ -759,10 +758,8 @@ bool CFontTexture::AddFallbackFont(const std::string& fontfile, ClearGlyphMode c /*** * * Clears fontconfig fallbacks - * - * @param clearMode ClearGlyphMode */ -void CFontTexture::ClearFallbackFonts(ClearGlyphMode clearMode) +void CFontTexture::ClearFallbackFonts() { #if defined(USE_FONTCONFIG) && !defined(HEADLESS) if (!FtLibraryHandler::CanUseFontConfig()) @@ -770,7 +767,8 @@ void CFontTexture::ClearFallbackFonts(ClearGlyphMode clearMode) FtLibraryHandler::ClearFallbackPattern(); FtLibraryHandler::ClearGameFontSet(); - needClearGlyphs = clearMode; + + needsClearGlyphs = true; #endif } @@ -780,7 +778,7 @@ void CFontTexture::ClearFallbackFonts(ClearGlyphMode clearMode) * * @param clearMode ClearGlyphMode */ -void CFontTexture::ClearAllGlyphs(ClearGlyphMode clearMode) { +void CFontTexture::ClearAllGlyphs(bool clearMode) { #ifndef HEADLESS RECOIL_DETAILED_TRACY_ZONE; if (clearMode == ClearGlyphMode::none) @@ -789,22 +787,20 @@ void CFontTexture::ClearAllGlyphs(ClearGlyphMode clearMode) { bool changed = false; for (const auto& ft : allFonts) { auto lf = ft.lock(); - changed |= lf->ClearGlyphs(clearMode); + changed |= lf->ClearGlyphs(); } if (changed) eventHandler.FontsChanged(); - needClearGlyphs = ClearGlyphMode::none; + needsClearGlyphs = false; #endif } /*** * * Clears all glyphs for a font - * - * @param clearMode ClearGlyphMode */ -bool CFontTexture::ClearGlyphs(ClearGlyphMode clearMode) { +bool CFontTexture::ClearGlyphs() { #ifndef HEADLESS RECOIL_DETAILED_TRACY_ZONE; @@ -813,25 +809,16 @@ bool CFontTexture::ClearGlyphs(ClearGlyphMode clearMode) { // Invalidate glyphs coming from other fonts, or those with the 'not found' glyph. for (const auto& g : glyphs) { if (g.second.face->face != shFace->face || g.second.index == 0) { - if (clearMode == ClearGlyphMode::fast) { - // in 'fast' clear mode, clear just extraneus glyphs. - // this can be easier to manage for the application initially, since - // old glyphs still work, but also means the atlas will keep - // filling up. - glyphs.erase(g.first); - } changed = true; } } + // Always clear failed attempts in case we have any cache here. failedAttemptsToReplace.clear(); - if (!changed) - return false; - - kerningPrecached = {}; + if (changed) { + kerningPrecached = {}; - if (clearMode == ClearGlyphMode::full) { // clear all glyps glyphs.clear(); @@ -882,7 +869,8 @@ void CFontTexture::Update() { static std::vector> fontsToUpdate; fontsToUpdate.clear(); - ClearAllGlyphs(needClearGlyphs); + if (needsClearGlyphs) + ClearAllGlyphs(); for (const auto& font : allFonts) { auto lf = font.lock(); diff --git a/rts/Rendering/Fonts/CFontTexture.h b/rts/Rendering/Fonts/CFontTexture.h index f6453a8047..7e627546a0 100644 --- a/rts/Rendering/Fonts/CFontTexture.h +++ b/rts/Rendering/Fonts/CFontTexture.h @@ -105,12 +105,6 @@ It works only and only with UTF32 chars class CFontTexture { public: - enum class ClearGlyphMode: unsigned int { - none = 0, - fast = 1, - full = 2 - }; - friend class CglFontRenderer; friend class CglShaderFontRenderer; friend class CglNoShaderFontRenderer; @@ -119,9 +113,9 @@ class CFontTexture static void InitFonts(); static void KillFonts(); static void Update(); - static bool AddFallbackFont(const std::string& fontfile, ClearGlyphMode clearMode); - static void ClearFallbackFonts(ClearGlyphMode clearMode); - static void ClearAllGlyphs(ClearGlyphMode clearMode); + static bool AddFallbackFont(const std::string& fontfile); + static void ClearFallbackFonts(); + static void ClearAllGlyphs(); inline static spring::WrappedSyncRecursiveMutex sync = {}; protected: @@ -154,7 +148,7 @@ class CFontTexture private: void CreateTexture(const int width, const int height); void LoadGlyph(std::shared_ptr& f, char32_t ch, unsigned index); - bool ClearGlyphs(ClearGlyphMode clearMode); + bool ClearGlyphs(); void PreloadGlyphs(); protected: float GetKerning(const GlyphInfo& lgl, const GlyphInfo& rgl); @@ -162,7 +156,7 @@ class CFontTexture static inline std::vector> allFonts = {}; static inline const GlyphInfo dummyGlyph = GlyphInfo(); - static inline ClearGlyphMode needClearGlyphs = ClearGlyphMode::none; + static inline bool needsClearGlyphs = false; std::array kerningPrecached = {}; // contains ASCII kerning From 9aaee441d5ab0fa6239581d93b4494b31cd05906 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Sun, 1 Dec 2024 20:59:03 +0100 Subject: [PATCH 12/20] Remove clearGlyphs parameter also from ClearGlyphMode. --- rts/Rendering/Fonts/CFontTexture.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index 3937801389..7a1439bc59 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -775,14 +775,10 @@ void CFontTexture::ClearFallbackFonts() /*** * * Clears all glyphs for all fonts - * - * @param clearMode ClearGlyphMode */ -void CFontTexture::ClearAllGlyphs(bool clearMode) { +void CFontTexture::ClearAllGlyphs() { #ifndef HEADLESS RECOIL_DETAILED_TRACY_ZONE; - if (clearMode == ClearGlyphMode::none) - return; bool changed = false; for (const auto& ft : allFonts) { From 5713e717b16f982e8f6ff720dd2c9ba268cc4add Mon Sep 17 00:00:00 2001 From: Saurtron Date: Mon, 2 Dec 2024 14:00:24 +0100 Subject: [PATCH 13/20] return after #endif so HEADLESS doesn't have a warning. --- rts/Rendering/Fonts/CFontTexture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index 7a1439bc59..edb4a376ff 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -832,8 +832,8 @@ bool CFontTexture::ClearGlyphs() { // signal need to update texture ++curTextureUpdate; } - return true; #endif + return true; } void CFontTexture::InitFonts() From 51c3aefef3aa3bc96e97708bb5d42db1045e81f3 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Mon, 2 Dec 2024 17:01:24 +0100 Subject: [PATCH 14/20] FontsUpdated was renamed to FontsChanged, update comments. --- rts/Lua/LuaFonts.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rts/Lua/LuaFonts.cpp b/rts/Lua/LuaFonts.cpp index 32dfef568d..c2a074176e 100644 --- a/rts/Lua/LuaFonts.cpp +++ b/rts/Lua/LuaFonts.cpp @@ -213,7 +213,7 @@ int LuaFonts::DeleteFont(lua_State* L) * When a glyph isn't found when rendering a font, the fallback fonts * will be searched first, otherwise os fonts will be used. * - * The application should listen for the unsynced 'FontsUpdated' callin so + * The application should listen for the unsynced 'FontsChanged' callin so * modules can clear any already reserved display lists or other relevant * caches. * @@ -237,7 +237,7 @@ int LuaFonts::AddFallbackFont(lua_State* L) /*** Clears all fallback fonts. * - * See the note at 'AddFallbackFont' about the 'FontsUpdated' callin, + * See the note at 'AddFallbackFont' about the 'FontsChanged' callin, * it also applies when calling this method. * * @function gl.ClearFallbackFonts From 8cf8cc3f032ced50100fbb6a17f360624f32ad07 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Sat, 7 Dec 2024 11:20:58 +0100 Subject: [PATCH 15/20] Refactor atlas clearing into a separate method and don't use ReallocAtlases for that since it's aimed at something different. --- rts/Rendering/Fonts/CFontTexture.cpp | 28 ++++++++++++++++++++++------ rts/Rendering/Fonts/CFontTexture.h | 1 + 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index 1ddf25c6b2..990e0032d5 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -826,13 +826,8 @@ bool CFontTexture::ClearGlyphs() { // clear all glyps glyphs.clear(); - // refresh the atlasAlloc to reset coordinates - atlasAlloc.clear(); // just in case - atlasAlloc = CRowAtlasAlloc(); - atlasAlloc.SetMaxSize(globalRendering->maxTextureSize, globalRendering->maxTextureSize); - // clear atlases - ReallocAtlases(false); + ClearAtlases(32, 32); // preload standard glyphs PreloadGlyphs(); @@ -1253,6 +1248,27 @@ void CFontTexture::ReallocAtlases(bool pre) #endif } +void CFontTexture::ClearAtlases(const int width, const int height) +{ +#ifndef HEADLESS + // refresh the atlasAlloc to reset coordinates + atlasAlloc = CRowAtlasAlloc(); + atlasAlloc.SetMaxSize(globalRendering->maxTextureSize, globalRendering->maxTextureSize); + + // clear atlases + wantedTexWidth = width; + wantedTexHeight = height; + + atlasUpdate.Alloc(wantedTexWidth, wantedTexHeight); + atlasUpdateShadow = {}; + + if (!atlasGlyphs.empty()) + LOG_L(L_WARNING, "[FontTexture::%s] discarding %u glyph bitmaps", __func__, uint32_t(atlasGlyphs.size())); + + atlasGlyphs.clear(); +#endif +} + bool CFontTexture::GlyphAtlasTextureNeedsUpdate() const { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Rendering/Fonts/CFontTexture.h b/rts/Rendering/Fonts/CFontTexture.h index aa945dcf74..b830df1db3 100644 --- a/rts/Rendering/Fonts/CFontTexture.h +++ b/rts/Rendering/Fonts/CFontTexture.h @@ -145,6 +145,7 @@ class CFontTexture void UploadGlyphAtlasTexture(); void UploadGlyphAtlasTextureImpl(); private: + void ClearAtlases(const int width, const int height); void CreateTexture(const int width, const int height); void LoadGlyph(std::shared_ptr& f, char32_t ch, unsigned index); bool ClearGlyphs(); From f85dff198f122a882f2e3e072082f66779802108 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Sat, 7 Dec 2024 16:02:22 +0100 Subject: [PATCH 16/20] Realloc before dispose. --- rts/Rendering/Fonts/CFontTexture.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index 990e0032d5..a7464f8f1f 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -1260,6 +1260,7 @@ void CFontTexture::ClearAtlases(const int width, const int height) wantedTexHeight = height; atlasUpdate.Alloc(wantedTexWidth, wantedTexHeight); + atlasUpdateShadow.Alloc(1, 1); atlasUpdateShadow = {}; if (!atlasGlyphs.empty()) From 36783c9dd1bd2fa4f45e1ed07b2d1c3fa125d4ba Mon Sep 17 00:00:00 2001 From: Saurtron Date: Mon, 9 Dec 2024 16:51:16 +0100 Subject: [PATCH 17/20] Make CFontTexture::ClearGlyphs return the changed status instead of always true, as intended. --- rts/Rendering/Fonts/CFontTexture.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index a7464f8f1f..859c1d24d5 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -805,10 +805,10 @@ void CFontTexture::ClearAllGlyphs() { * Clears all glyphs for a font */ bool CFontTexture::ClearGlyphs() { -#ifndef HEADLESS RECOIL_DETAILED_TRACY_ZONE; bool changed = false; +#ifndef HEADLESS // Invalidate glyphs coming from other fonts, or those with the 'not found' glyph. for (const auto& g : glyphs) { @@ -836,7 +836,7 @@ bool CFontTexture::ClearGlyphs() { ++curTextureUpdate; } #endif - return true; + return changed; } void CFontTexture::InitFonts() From 2a1111416a89047874268103bd4f1d332e1bcf54 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Thu, 12 Dec 2024 15:57:55 +0100 Subject: [PATCH 18/20] Add comment about vfs access use in use by AddFallbackFont. --- rts/Lua/LuaFonts.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rts/Lua/LuaFonts.cpp b/rts/Lua/LuaFonts.cpp index c2a074176e..30a1924fc7 100644 --- a/rts/Lua/LuaFonts.cpp +++ b/rts/Lua/LuaFonts.cpp @@ -221,7 +221,7 @@ int LuaFonts::DeleteFont(lua_State* L) * but later, on the Update cycle (before other Update and Draw callins). * * @function gl.AddFallbackFont - * @string filePath VFS path to the file, for example "fonts/myfont.ttf" + * @string filePath VFS path to the file, for example "fonts/myfont.ttf". Uses VFS.RAW_FIRST access mode. * @treturn bool success */ int LuaFonts::AddFallbackFont(lua_State* L) From 6f5da578c8838efa772ec621b111964a6d823ee9 Mon Sep 17 00:00:00 2001 From: Saurtron Date: Sat, 14 Dec 2024 12:11:43 +0100 Subject: [PATCH 19/20] Rename fallbackPattern to basePattern. --- rts/Rendering/Fonts/CFontTexture.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/rts/Rendering/Fonts/CFontTexture.cpp b/rts/Rendering/Fonts/CFontTexture.cpp index f1f8b90593..67c9e62d41 100644 --- a/rts/Rendering/Fonts/CFontTexture.cpp +++ b/rts/Rendering/Fonts/CFontTexture.cpp @@ -90,7 +90,7 @@ class FtLibraryHandler { , lib(nullptr) #ifdef USE_FONTCONFIG , gameFontSet(nullptr) - , fallbackPattern(nullptr) + , basePattern(nullptr) #endif // USE_FONTCONFIG { const FT_Error error = FT_Init_FreeType(&lib); @@ -116,8 +116,8 @@ class FtLibraryHandler { if (gameFontSet) { FcFontSetDestroy(gameFontSet); } - if (fallbackPattern) { - FcPatternDestroy(fallbackPattern); + if (basePattern) { + FcPatternDestroy(basePattern); } FcFini(); config = nullptr; @@ -208,7 +208,7 @@ class FtLibraryHandler { } gameFontSet = FcFontSetCreate(); - fallbackPattern = FcPatternCreate(); + basePattern = FcPatternCreate(); // init app fonts dir res = FcConfigAppFontAddDir(config, reinterpret_cast("fonts")); @@ -269,16 +269,16 @@ class FtLibraryHandler { static FcFontSet *GetGameFontSet() { return singleton->gameFontSet; } - static FcPattern *GetFallbackPattern() { - return singleton->fallbackPattern; + static FcPattern *GetBasePattern() { + return singleton->basePattern; } static void ClearGameFontSet() { FcFontSetDestroy(singleton->gameFontSet); singleton->gameFontSet = FcFontSetCreate(); } - static void ClearFallbackPattern() { - FcPatternDestroy(singleton->fallbackPattern); - singleton->fallbackPattern = FcPatternCreate(); + static void ClearBasePattern() { + FcPatternDestroy(singleton->basePattern); + singleton->basePattern = FcPatternCreate(); } #endif private: @@ -286,7 +286,7 @@ class FtLibraryHandler { FT_Library lib; #ifdef USE_FONTCONFIG FcFontSet *gameFontSet; - FcPattern *fallbackPattern; + FcPattern *basePattern; #endif static inline std::unique_ptr singleton = nullptr; @@ -449,7 +449,7 @@ static std::shared_ptr GetFontForCharacters(const std::vector Date: Sat, 14 Dec 2024 12:13:48 +0100 Subject: [PATCH 20/20] Rename badly named variable. --- rts/Lua/LuaFonts.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rts/Lua/LuaFonts.cpp b/rts/Lua/LuaFonts.cpp index 87d11fece6..ea27b4caf5 100644 --- a/rts/Lua/LuaFonts.cpp +++ b/rts/Lua/LuaFonts.cpp @@ -221,8 +221,8 @@ int LuaFonts::AddFallbackFont(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; - const bool f = CFontTexture::AddFallbackFont(luaL_checkstring(L, 1)); - lua_pushboolean(L, f); + const bool res = CFontTexture::AddFallbackFont(luaL_checkstring(L, 1)); + lua_pushboolean(L, res); return 1; }