Fonts: Fallback glyph is now lazily loaded on demand (yay!). Moving ImFontBaked:: functions outside of class.

This commit is contained in:
ocornut
2025-02-10 22:32:09 +01:00
parent 78a17038c2
commit d8a612f73b
2 changed files with 48 additions and 36 deletions

View File

@@ -3652,7 +3652,8 @@ struct ImFontBaked
// [Internal] Members: Cold // [Internal] Members: Cold
float Ascent, Descent; // 4+4 // out // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled) float Ascent, Descent; // 4+4 // out // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled)
unsigned int MetricsTotalSurface:26;// 3 // out // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) unsigned int MetricsTotalSurface:26;// 3 // out // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs)
unsigned int WantDestroy:1; // 1 // // Queued for destroy unsigned int WantDestroy:1; // 0 // // Queued for destroy
unsigned int LockLoadingFallback:1; // 0 // //
int LastUsedFrame; // 4 // // Record of that time this was bounds int LastUsedFrame; // 4 // // Record of that time this was bounds
ImGuiID BakedId; // 4 // ImGuiID BakedId; // 4 //
ImFont* ContainerFont; // 4-8 // in // Parent font ImFont* ContainerFont; // 4-8 // in // Parent font
@@ -3665,8 +3666,6 @@ struct ImFontBaked
IMGUI_API ImFontGlyph* FindGlyphNoFallback(ImWchar c); // Return NULL if glyph doesn't exist IMGUI_API ImFontGlyph* FindGlyphNoFallback(ImWchar c); // Return NULL if glyph doesn't exist
IMGUI_API float GetCharAdvance(ImWchar c); IMGUI_API float GetCharAdvance(ImWchar c);
IMGUI_API bool IsGlyphLoaded(ImWchar c); IMGUI_API bool IsGlyphLoaded(ImWchar c);
IMGUI_API ImFontGlyph* BuildLoadGlyph(ImWchar c);
IMGUI_API void BuildGrowIndex(int new_size);
}; };
// Font flags // Font flags

View File

@@ -2499,6 +2499,7 @@ void ImTextureData::DestroyPixels()
// - ImFontAtlasBuildAddFont() // - ImFontAtlasBuildAddFont()
// - ImFontAtlasBuildSetupFontBakedEllipsis() // - ImFontAtlasBuildSetupFontBakedEllipsis()
// - ImFontAtlasBuildSetupFontBakedBlanks() // - ImFontAtlasBuildSetupFontBakedBlanks()
// - ImFontAtlasBuildSetupFontBakedFallback()
// - ImFontAtlasBuildSetupFontSpecialGlyphs() // - ImFontAtlasBuildSetupFontSpecialGlyphs()
// - ImFontAtlasBuildDiscardBakes() // - ImFontAtlasBuildDiscardBakes()
// - ImFontAtlasBuildDiscardFontBakedGlyph() // - ImFontAtlasBuildDiscardFontBakedGlyph()
@@ -2527,7 +2528,8 @@ void ImTextureData::DestroyPixels()
// - ImFontAtlasPackAddRect() // - ImFontAtlasPackAddRect()
// - ImFontAtlasPackGetRect() // - ImFontAtlasPackGetRect()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// - ImFont::BuildLoadGlyph() // - ImFontBaked_BuildGrowIndex()
// - ImFontBaked_BuildLoadGlyph()
// - ImFontAtlasDebugLogTextureRequests() // - ImFontAtlasDebugLogTextureRequests()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// - ImFontAtlasGetFontLoaderForStbTruetype() // - ImFontAtlasGetFontLoaderForStbTruetype()
@@ -3242,7 +3244,7 @@ int ImFontAtlas::AddCustomRectFontGlyphForSize(ImFont* font, float font_size, Im
ImFontAtlasTextureBlockQueueUpload(this, TexData, r->x, r->y, r->w, r->h); ImFontAtlasTextureBlockQueueUpload(this, TexData, r->x, r->y, r->w, r->h);
if (baked->IsGlyphLoaded(codepoint)) if (baked->IsGlyphLoaded(codepoint))
ImFontAtlasBuildDiscardFontBakedGlyph(this, font, baked, (ImFontGlyph*)(void*)baked->FindGlyph(codepoint)); ImFontAtlasBuildDiscardFontBakedGlyph(this, font, baked, baked->FindGlyph(codepoint));
ImFontGlyph glyph; ImFontGlyph glyph;
glyph.Codepoint = codepoint; glyph.Codepoint = codepoint;
@@ -3367,7 +3369,7 @@ void ImFontAtlasBuildPreloadAllGlyphRanges(ImFontAtlas* atlas)
IM_ASSERT(ranges != NULL); IM_ASSERT(ranges != NULL);
for (; ranges[0]; ranges += 2) for (; ranges[0]; ranges += 2)
for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560 for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560
baked->FindGlyphNoFallback((ImWchar)c); baked->FindGlyph((ImWchar)c);
} }
} }
@@ -3587,25 +3589,23 @@ static ImFontGlyph* ImFontAtlasBuildSetupFontBakedEllipsis(ImFontAtlas* atlas, I
return glyph; return glyph;
} }
static void ImFontAtlasBuildSetupFontBakedSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked) // Load fallback in order to obtain its index
// (this is called from in hot-path so we avoid extraneous parameters to minimize impact on code size)
static void ImFontAtlasBuildSetupFontBakedFallback(ImFontBaked* baked)
{ {
// Mark space as always hidden (not strictly correct/necessary. but some e.g. icons fonts don't have a space. it tends to look neater in previews)
ImFontGlyph* space_glyph = (ImFontGlyph*)(void*)baked->FindGlyphNoFallback((ImWchar)' ');
if (space_glyph != NULL)
space_glyph->Visible = false;
// Load fallback in order to obtain its index
// FIXME-NEWATLAS: could we use a scheme where this is lazily loaded?
IM_ASSERT(baked->FallbackGlyphIndex == -1); IM_ASSERT(baked->FallbackGlyphIndex == -1);
IM_ASSERT(baked->FallbackAdvanceX == 0.0f);
ImFont* font = baked->ContainerFont;
ImFontGlyph* fallback_glyph = NULL; ImFontGlyph* fallback_glyph = NULL;
if (font->FallbackChar != 0) if (font->FallbackChar != 0)
fallback_glyph = baked->FindGlyphNoFallback(font->FallbackChar); fallback_glyph = baked->FindGlyphNoFallback(font->FallbackChar);
if (fallback_glyph == NULL) if (fallback_glyph == NULL)
{ {
ImFontGlyph* space_glyph = baked->FindGlyphNoFallback((ImWchar)' ');
ImFontGlyph glyph; ImFontGlyph glyph;
glyph.Codepoint = 0; glyph.Codepoint = 0;
glyph.AdvanceX = space_glyph ? space_glyph->AdvanceX : IM_ROUND(baked->Size * 0.40f); glyph.AdvanceX = space_glyph ? space_glyph->AdvanceX : IM_ROUND(baked->Size * 0.40f);
fallback_glyph = ImFontAtlasBakedAddFontGlyph(atlas, baked, NULL, &glyph); fallback_glyph = ImFontAtlasBakedAddFontGlyph(font->ContainerAtlas, baked, NULL, &glyph);
} }
baked->FallbackGlyphIndex = baked->Glyphs.index_from_ptr(fallback_glyph); // Storing index avoid need to update pointer on growth and simplify inner loop code baked->FallbackGlyphIndex = baked->Glyphs.index_from_ptr(fallback_glyph); // Storing index avoid need to update pointer on growth and simplify inner loop code
baked->FallbackAdvanceX = fallback_glyph->AdvanceX; baked->FallbackAdvanceX = fallback_glyph->AdvanceX;
@@ -3711,7 +3711,7 @@ ImFontBaked* ImFontAtlasBuildAddFontBaked(ImFontAtlas* atlas, ImFont* font, floa
loader_data_p += loader->FontBakedSrcLoaderDataSize; loader_data_p += loader->FontBakedSrcLoaderDataSize;
} }
ImFontAtlasBuildSetupFontBakedSpecialGlyphs(atlas, baked->ContainerFont, baked); ImFontAtlasBuildSetupFontBakedBlanks(atlas, baked);
return baked; return baked;
} }
@@ -4260,13 +4260,26 @@ static bool ImFontAtlasBuildAcceptCodepointForSource(ImFontConfig* src, ImWchar
return true; return true;
} }
ImFontGlyph* ImFontBaked::BuildLoadGlyph(ImWchar codepoint) static void ImFontBaked_BuildGrowIndex(ImFontBaked* baked, int new_size)
{ {
ImFont* font = ContainerFont; IM_ASSERT(baked->IndexAdvanceX.Size == baked->IndexLookup.Size);
ImFontBaked* baked = this; if (new_size <= baked->IndexLookup.Size)
return;
baked->IndexAdvanceX.resize(new_size, -1.0f);
baked->IndexLookup.resize(new_size, IM_FONTGLYPH_INDEX_UNUSED);
}
static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint)
{
ImFont* font = baked->ContainerFont;
ImFontAtlas* atlas = font->ContainerAtlas; ImFontAtlas* atlas = font->ContainerAtlas;
if (atlas->Locked || (font->Flags & ImFontFlags_NoLoadGlyphs)) if (atlas->Locked || (font->Flags & ImFontFlags_NoLoadGlyphs))
{
// Lazily load fallback glyph
if (baked->FallbackGlyphIndex == -1 && baked->LockLoadingFallback == 0)
ImFontAtlasBuildSetupFontBakedFallback(baked);
return NULL; return NULL;
}
//char utf8_buf[5]; //char utf8_buf[5];
//IMGUI_DEBUG_LOG("[font] BuildLoadGlyph U+%04X (%s)\n", (unsigned int)codepoint, ImTextCharToUtf8(utf8_buf, (unsigned int)codepoint)); //IMGUI_DEBUG_LOG("[font] BuildLoadGlyph U+%04X (%s)\n", (unsigned int)codepoint, ImTextCharToUtf8(utf8_buf, (unsigned int)codepoint));
@@ -4293,8 +4306,14 @@ ImFontGlyph* ImFontBaked::BuildLoadGlyph(ImWchar codepoint)
loader_user_data_p += loader->FontBakedSrcLoaderDataSize; loader_user_data_p += loader->FontBakedSrcLoaderDataSize;
} }
// Lazily load fallback glyph
if (baked->LockLoadingFallback)
return NULL;
if (baked->FallbackGlyphIndex == -1)
ImFontAtlasBuildSetupFontBakedFallback(baked);
// Mark index as not found, so we don't attempt the search twice // Mark index as not found, so we don't attempt the search twice
baked->BuildGrowIndex(codepoint + 1); ImFontBaked_BuildGrowIndex(baked, codepoint + 1);
baked->IndexAdvanceX[codepoint] = baked->FallbackAdvanceX; baked->IndexAdvanceX[codepoint] = baked->FallbackAdvanceX;
baked->IndexLookup[codepoint] = IM_FONTGLYPH_INDEX_NOT_FOUND; baked->IndexLookup[codepoint] = IM_FONTGLYPH_INDEX_NOT_FOUND;
return NULL; return NULL;
@@ -4302,10 +4321,10 @@ ImFontGlyph* ImFontBaked::BuildLoadGlyph(ImWchar codepoint)
// The point of this indirection is to not be inlined in debug mode in order to not bloat inner loop.b // The point of this indirection is to not be inlined in debug mode in order to not bloat inner loop.b
IM_MSVC_RUNTIME_CHECKS_OFF IM_MSVC_RUNTIME_CHECKS_OFF
static float BuildLoadGlyphGetAdvanceOrFallback(ImFontBaked* font, unsigned int codepoint) static float BuildLoadGlyphGetAdvanceOrFallback(ImFontBaked* baked, unsigned int codepoint)
{ {
ImFontGlyph* glyph = font->BuildLoadGlyph((ImWchar)codepoint); ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint);
return glyph ? glyph->AdvanceX : font->FallbackAdvanceX; return glyph ? glyph->AdvanceX : baked->FallbackAdvanceX;
} }
IM_MSVC_RUNTIME_CHECKS_RESTORE IM_MSVC_RUNTIME_CHECKS_RESTORE
@@ -4861,15 +4880,6 @@ void ImFontBaked::ClearOutputData()
MetricsTotalSurface = 0; MetricsTotalSurface = 0;
} }
void ImFontBaked::BuildGrowIndex(int new_size)
{
IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size);
if (new_size <= IndexLookup.Size)
return;
IndexAdvanceX.resize(new_size, -1.0f);
IndexLookup.resize(new_size, IM_FONTGLYPH_INDEX_UNUSED);
}
ImFont::ImFont() ImFont::ImFont()
{ {
memset(this, 0, sizeof(*this)); memset(this, 0, sizeof(*this));
@@ -4950,7 +4960,7 @@ ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked
// Update lookup tables // Update lookup tables
int codepoint = glyph.Codepoint; int codepoint = glyph.Codepoint;
baked->BuildGrowIndex(codepoint + 1); ImFontBaked_BuildGrowIndex(baked, codepoint + 1);
baked->IndexAdvanceX[codepoint] = glyph.AdvanceX; baked->IndexAdvanceX[codepoint] = glyph.AdvanceX;
baked->IndexLookup[codepoint] = (ImU16)glyph_idx; baked->IndexLookup[codepoint] = (ImU16)glyph_idx;
const int page_n = codepoint / 8192; const int page_n = codepoint / 8192;
@@ -5002,7 +5012,7 @@ ImFontGlyph* ImFontBaked::FindGlyph(ImWchar c)
if (i != IM_FONTGLYPH_INDEX_UNUSED) if (i != IM_FONTGLYPH_INDEX_UNUSED)
return &Glyphs.Data[i]; return &Glyphs.Data[i];
} }
ImFontGlyph* glyph = BuildLoadGlyph(c); ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c);
return glyph ? glyph : &Glyphs.Data[FallbackGlyphIndex]; return glyph ? glyph : &Glyphs.Data[FallbackGlyphIndex];
} }
@@ -5017,7 +5027,10 @@ ImFontGlyph* ImFontBaked::FindGlyphNoFallback(ImWchar c)
if (i != IM_FONTGLYPH_INDEX_UNUSED) if (i != IM_FONTGLYPH_INDEX_UNUSED)
return &Glyphs.Data[i]; return &Glyphs.Data[i];
} }
return BuildLoadGlyph(c); LockLoadingFallback = true; // This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites.
ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c);
LockLoadingFallback = false;
return glyph;
} }
bool ImFontBaked::IsGlyphLoaded(ImWchar c) bool ImFontBaked::IsGlyphLoaded(ImWchar c)
@@ -5060,7 +5073,7 @@ float ImFontBaked::GetCharAdvance(ImWchar c)
} }
// Same as BuildLoadGlyphGetAdvanceOrFallback(): // Same as BuildLoadGlyphGetAdvanceOrFallback():
const ImFontGlyph* glyph = BuildLoadGlyph(c); const ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c);
return glyph ? glyph->AdvanceX : FallbackAdvanceX; return glyph ? glyph->AdvanceX : FallbackAdvanceX;
} }
IM_MSVC_RUNTIME_CHECKS_RESTORE IM_MSVC_RUNTIME_CHECKS_RESTORE