From a309d2dcac2c0941a6a04ef1e4d07b7d9c5b9485 Mon Sep 17 00:00:00 2001 From: Christian Fillion Date: Wed, 27 Aug 2025 00:05:30 -0400 Subject: [PATCH] Fonts: fixed assertion failure when ImFontAtlasRectEntry::Generation overflows. (#8906) --- docs/CHANGELOG.txt | 2 ++ imgui_draw.cpp | 2 ++ imgui_internal.h | 10 +++++----- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 435057852..619e0c42e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,8 @@ Other Changes: - Scrollbar, Style: added configurable style.ScrollbarPadding value and corresponding ImGuiStyleVar_ScrollbarPadding enum, instead of hardcoded computed default. (#8895) +- Fonts: fixed an assertion failure when a rectangle entry has been reused + 1024 times (e.g. due to constant change of font types). (#8906) [@cfillion] - Fixed Bullet() fixed tesselation amount which looked out of place in very large sizes. - DrawList: Fixed CloneOutput() unnecessarily taking a copy of the ImDrawListSharedData pointer, which could to issue when deleting the cloned list. (#8894, #1860) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index c4dd43a52..76d8474bf 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -4293,6 +4293,8 @@ void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id) index_entry->IsUsed = false; index_entry->TargetIndex = builder->RectsIndexFreeListStart; index_entry->Generation++; + if (index_entry->Generation == 0) + index_entry->Generation++; // Keep non-zero on overflow const int pack_padding = atlas->TexGlyphPadding; builder->RectsIndexFreeListStart = index_idx; diff --git a/imgui_internal.h b/imgui_internal.h index 3085648dd..873e7881e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3736,12 +3736,12 @@ inline bool operator==(const ImTextureRef& lhs, const ImTextureRef& rhs) { re inline bool operator!=(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID != rhs._TexID || lhs._TexData != rhs._TexData; } // Refer to ImFontAtlasPackGetRect() to better understand how this works. -#define ImFontAtlasRectId_IndexMask_ (0x000FFFFF) // 20-bits: index to access builder->RectsIndex[]. +#define ImFontAtlasRectId_IndexMask_ (0x0007FFFF) // 20-bits signed: index to access builder->RectsIndex[]. #define ImFontAtlasRectId_GenerationMask_ (0x3FF00000) // 10-bits: entry generation, so each ID is unique and get can safely detected old identifiers. #define ImFontAtlasRectId_GenerationShift_ (20) -inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return id & ImFontAtlasRectId_IndexMask_; } -inline int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; } -inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx < ImFontAtlasRectId_IndexMask_ && gen_idx < (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); } +inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_IndexMask_); } +inline unsigned int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (unsigned int)(id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; } +inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx >= 0 && index_idx <= ImFontAtlasRectId_IndexMask_ && gen_idx <= (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); } // Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles) // User are returned ImFontAtlasRectId values which are meant to be persistent. @@ -3751,7 +3751,7 @@ inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) struct ImFontAtlasRectEntry { int TargetIndex : 20; // When Used: ImFontAtlasRectId -> into Rects[]. When unused: index to next unused RectsIndex[] slot to consume free-list. - int Generation : 10; // Increased each time the entry is reused for a new rectangle. + unsigned int Generation : 10; // Increased each time the entry is reused for a new rectangle. unsigned int IsUsed : 1; };