Fonts: fixed support for multiple atlases.

Moved FontAtlasOwnedByContext to OwnerContext

# Conflicts:
#	imgui.cpp
#	imgui_internal.h
This commit is contained in:
ocornut
2025-05-09 22:17:48 +02:00
parent fad5280d4c
commit 91ed6e67b4
3 changed files with 59 additions and 37 deletions

View File

@@ -3962,13 +3962,13 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
InputTextState.Ctx = this; InputTextState.Ctx = this;
Initialized = false; Initialized = false;
FontAtlasOwnedByContext = shared_font_atlas ? false : true;
Font = NULL; Font = NULL;
FontBaked = NULL; FontBaked = NULL;
FontSize = FontSizeBeforeScaling = FontScale = CurrentDpiScale = 0.0f; FontSize = FontSizeBeforeScaling = FontScale = CurrentDpiScale = 0.0f;
FontRasterizerDensity = 1.0f; FontRasterizerDensity = 1.0f;
IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
IO.Fonts->RefCount++; if (shared_font_atlas == NULL)
IO.Fonts->OwnerContext = this;
Time = 0.0f; Time = 0.0f;
FrameCount = 0; FrameCount = 0;
FrameCountEnded = FrameCountRendered = -1; FrameCountEnded = FrameCountRendered = -1;
@@ -4226,8 +4226,9 @@ void ImGui::Initialize()
// ImDrawList/ImFontAtlas are designed to function without ImGui, and 99% of it works without an ImGui context. // ImDrawList/ImFontAtlas are designed to function without ImGui, and 99% of it works without an ImGui context.
// But this link allows us to facilitate/handle a few edge cases better. // But this link allows us to facilitate/handle a few edge cases better.
ImFontAtlas* atlas = g.IO.Fonts;
g.DrawListSharedData.Context = &g; g.DrawListSharedData.Context = &g;
ImFontAtlasAddDrawListSharedData(g.IO.Fonts, &g.DrawListSharedData); RegisterFontAtlas(atlas);
g.Initialized = true; g.Initialized = true;
} }
@@ -4240,17 +4241,15 @@ void ImGui::Shutdown()
IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?"); IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?");
// The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
if (ImFontAtlas* atlas = g.IO.Fonts) for (ImFontAtlas* atlas : g.FontAtlases)
{ {
ImFontAtlasRemoveDrawListSharedData(atlas, &g.DrawListSharedData); UnregisterFontAtlas(atlas);
atlas->RefCount--; if (atlas->OwnerContext == &g)
if (g.FontAtlasOwnedByContext)
{ {
atlas->Locked = false; atlas->Locked = false;
IM_DELETE(atlas); IM_DELETE(atlas);
} }
} }
g.IO.Fonts = NULL;
g.DrawListSharedData.TempBuffer.clear(); g.DrawListSharedData.TempBuffer.clear();
// Cleanup of other data are conditional on actually having initialized Dear ImGui. // Cleanup of other data are conditional on actually having initialized Dear ImGui.
@@ -4412,7 +4411,8 @@ void ImGui::GcCompactTransientMiscBuffers()
g.MultiSelectTempDataStacked = 0; g.MultiSelectTempDataStacked = 0;
g.MultiSelectTempData.clear_destruct(); g.MultiSelectTempData.clear_destruct();
TableGcCompactSettings(); TableGcCompactSettings();
g.IO.Fonts->CompactCache(); for (ImFontAtlas* atlas : g.FontAtlases)
atlas->CompactCache();
} }
// Free up/compact internal window buffers, we can use this when a window becomes unused. // Free up/compact internal window buffers, we can use this when a window becomes unused.
@@ -5210,29 +5210,27 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos)
static void ImGui::UpdateTexturesNewFrame() static void ImGui::UpdateTexturesNewFrame()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImFontAtlas* atlas = g.IO.Fonts; for (ImFontAtlas* atlas : g.FontAtlases)
if (g.FontAtlasOwnedByContext) if (atlas->OwnerContext == &g)
{ {
atlas->RendererHasTextures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0; atlas->RendererHasTextures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0;
ImFontAtlasUpdateNewFrame(atlas, g.FrameCount); ImFontAtlasUpdateNewFrame(atlas, g.FrameCount);
} }
} }
// Build a single texture list // Build a single texture list
// We want to avoid user reading from atlas->TexList[] in order to facilitate better support for multiple atlases.
static void ImGui::UpdateTexturesEndFrame() static void ImGui::UpdateTexturesEndFrame()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImFontAtlas* atlas = g.IO.Fonts;
g.PlatformIO.Textures.resize(0); g.PlatformIO.Textures.resize(0);
g.PlatformIO.Textures.reserve(atlas->TexList.Size); for (ImFontAtlas* atlas : g.FontAtlases)
for (ImTextureData* tex : atlas->TexList) for (ImTextureData* tex : atlas->TexList)
{ {
// We provide this information so backends can decide whether to destroy textures. // We provide this information so backends can decide whether to destroy textures.
// This means in practice that if N imgui contexts are created with a shared atlas, we assume all of them have a backend initialized. // This means in practice that if N imgui contexts are created with a shared atlas, we assume all of them have a backend initialized.
tex->RefCount = (unsigned short)atlas->RefCount; tex->RefCount = (unsigned short)atlas->RefCount;
g.PlatformIO.Textures.push_back(tex); g.PlatformIO.Textures.push_back(tex);
} }
} }
// Called once a frame. Followed by SetCurrentFont() which sets up the remaining data. // Called once a frame. Followed by SetCurrentFont() which sets up the remaining data.
@@ -5810,8 +5808,8 @@ void ImGui::EndFrame()
UpdateTexturesEndFrame(); UpdateTexturesEndFrame();
// Unlock font atlas // Unlock font atlas
ImFontAtlas* atlas = g.IO.Fonts; for (ImFontAtlas* atlas : g.FontAtlases)
atlas->Locked = false; atlas->Locked = false;
// Clear Input data for next frame // Clear Input data for next frame
g.IO.MousePosPrev = g.IO.MousePos; g.IO.MousePosPrev = g.IO.MousePos;
@@ -5890,7 +5888,8 @@ void ImGui::Render()
#ifndef IMGUI_DISABLE_DEBUG_TOOLS #ifndef IMGUI_DISABLE_DEBUG_TOOLS
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
ImFontAtlasDebugLogTextureRequests(g.IO.Fonts); for (ImFontAtlas* atlas : g.FontAtlases)
ImFontAtlasDebugLogTextureRequests(atlas);
#endif #endif
CallContextHooks(&g, ImGuiContextHookType_RenderPost); CallContextHooks(&g, ImGuiContextHookType_RenderPost);
@@ -8596,9 +8595,9 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
void ImGui::UpdateFontsNewFrame() void ImGui::UpdateFontsNewFrame()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImFontAtlas* atlas = g.IO.Fonts;
if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0) if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
atlas->Locked = true; for (ImFontAtlas* atlas : g.FontAtlases)
atlas->Locked = true;
// We do this really unusual thing of calling *push_front()*, the reason behind that we want to support the PushFont()/NewFrame()/PopFont() idiom. // We do this really unusual thing of calling *push_front()*, the reason behind that we want to support the PushFont()/NewFrame()/PopFont() idiom.
ImFontStackData font_stack_data = { ImGui::GetDefaultFont(), ImGui::GetDefaultFont()->DefaultSize }; ImFontStackData font_stack_data = { ImGui::GetDefaultFont(), ImGui::GetDefaultFont()->DefaultSize };
@@ -8614,6 +8613,25 @@ void ImGui::UpdateFontsEndFrame()
PopFont(); PopFont();
} }
void ImGui::RegisterFontAtlas(ImFontAtlas* atlas)
{
ImGuiContext& g = *GImGui;
if (g.FontAtlases.Size == 0)
IM_ASSERT(atlas == g.IO.Fonts);
atlas->RefCount++;
g.FontAtlases.push_back(atlas);
ImFontAtlasAddDrawListSharedData(atlas, &g.DrawListSharedData);
}
void ImGui::UnregisterFontAtlas(ImFontAtlas* atlas)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(atlas->RefCount > 0);
ImFontAtlasRemoveDrawListSharedData(atlas, &g.DrawListSharedData);
g.FontAtlases.find_erase(atlas);
atlas->RefCount--;
}
// Use ImDrawList::_SetTexture(), making our shared g.FontStack[] authoritative against window-local ImDrawList. // Use ImDrawList::_SetTexture(), making our shared g.FontStack[] authoritative against window-local ImDrawList.
// - Whereas ImDrawList::PushTexture()/PopTexture() is not to be used across Begin() calls. // - Whereas ImDrawList::PushTexture()/PopTexture() is not to be used across Begin() calls.
// - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did... // - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did...
@@ -8670,6 +8688,7 @@ void ImGui::UpdateCurrentFontSize()
void ImGui::SetFontRasterizerDensity(float rasterizer_density) void ImGui::SetFontRasterizerDensity(float rasterizer_density)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures);
if (g.FontRasterizerDensity == rasterizer_density) if (g.FontRasterizerDensity == rasterizer_density)
return; return;
g.FontRasterizerDensity = rasterizer_density; g.FontRasterizerDensity = rasterizer_density;
@@ -16104,12 +16123,12 @@ void ImGui::ShowMetricsWindow(bool* p_open)
} }
// Details for Fonts // Details for Fonts
ImFontAtlas* atlas = g.IO.Fonts; for (ImFontAtlas* atlas : g.FontAtlases)
if (TreeNode("Fonts", "Fonts (%d), Textures (%d)", atlas->Fonts.Size, atlas->TexList.Size)) if (TreeNode((void*)atlas, "Fonts (%d), Textures (%d)", atlas->Fonts.Size, atlas->TexList.Size))
{ {
ShowFontAtlas(atlas); ShowFontAtlas(atlas);
TreePop(); TreePop();
} }
// Details for Popups // Details for Popups
if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))

View File

@@ -3684,6 +3684,7 @@ struct ImFontAtlas
void* FontLoaderData; // Font backend opaque storage void* FontLoaderData; // Font backend opaque storage
unsigned int FontBuilderFlags; // [FIXME: Should be called FontLoaderFlags] Shared flags (for all fonts) for font loader. THIS IS BUILD IMPLEMENTATION DEPENDENT (e.g. . Per-font override is also available in ImFontConfig. unsigned int FontBuilderFlags; // [FIXME: Should be called FontLoaderFlags] Shared flags (for all fonts) for font loader. THIS IS BUILD IMPLEMENTATION DEPENDENT (e.g. . Per-font override is also available in ImFontConfig.
int RefCount; // Number of contexts using this atlas int RefCount; // Number of contexts using this atlas
ImGuiContext* OwnerContext; // Context which own the atlas will be in charge of updating and destroying it.
// [Obsolete] // [Obsolete]
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS

View File

@@ -2132,10 +2132,10 @@ struct ImGuiContextHook
struct ImGuiContext struct ImGuiContext
{ {
bool Initialized; bool Initialized;
bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it.
ImGuiIO IO; ImGuiIO IO;
ImGuiPlatformIO PlatformIO; ImGuiPlatformIO PlatformIO;
ImGuiStyle Style; ImGuiStyle Style;
ImVector<ImFontAtlas*> FontAtlases; // List of font atlases used by the context (generally only contains g.IO.Fonts aka the main font atlas)
ImFont* Font; // == FontStack.back().Font ImFont* Font; // == FontStack.back().Font
ImFontBaked* FontBaked; // == Font->GetFontBaked(FontSize) ImFontBaked* FontBaked; // == Font->GetFontBaked(FontSize)
float FontSize; // == FontSizeBeforeScaling * io.FontGlobalScale * font->Scale * g.CurrentWindow->FontWindowScale. Current text height. float FontSize; // == FontSizeBeforeScaling * io.FontGlobalScale * font->Scale * g.CurrentWindow->FontWindowScale. Current text height.
@@ -3108,6 +3108,8 @@ namespace ImGui
IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags); IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags);
// Fonts, drawing // Fonts, drawing
IMGUI_API void RegisterFontAtlas(ImFontAtlas* atlas);
IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas);
IMGUI_API void SetCurrentFont(ImFont* font, float font_size); IMGUI_API void SetCurrentFont(ImFont* font, float font_size);
IMGUI_API void SetFontRasterizerDensity(float rasterizer_density); IMGUI_API void SetFontRasterizerDensity(float rasterizer_density);
inline float GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; } inline float GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; }