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;
Initialized = false;
FontAtlasOwnedByContext = shared_font_atlas ? false : true;
Font = NULL;
FontBaked = NULL;
FontSize = FontSizeBeforeScaling = FontScale = CurrentDpiScale = 0.0f;
FontRasterizerDensity = 1.0f;
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;
FrameCount = 0;
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.
// But this link allows us to facilitate/handle a few edge cases better.
ImFontAtlas* atlas = g.IO.Fonts;
g.DrawListSharedData.Context = &g;
ImFontAtlasAddDrawListSharedData(g.IO.Fonts, &g.DrawListSharedData);
RegisterFontAtlas(atlas);
g.Initialized = true;
}
@@ -4240,17 +4241,15 @@ void ImGui::Shutdown()
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)
if (ImFontAtlas* atlas = g.IO.Fonts)
for (ImFontAtlas* atlas : g.FontAtlases)
{
ImFontAtlasRemoveDrawListSharedData(atlas, &g.DrawListSharedData);
atlas->RefCount--;
if (g.FontAtlasOwnedByContext)
UnregisterFontAtlas(atlas);
if (atlas->OwnerContext == &g)
{
atlas->Locked = false;
IM_DELETE(atlas);
}
}
g.IO.Fonts = NULL;
g.DrawListSharedData.TempBuffer.clear();
// Cleanup of other data are conditional on actually having initialized Dear ImGui.
@@ -4412,7 +4411,8 @@ void ImGui::GcCompactTransientMiscBuffers()
g.MultiSelectTempDataStacked = 0;
g.MultiSelectTempData.clear_destruct();
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.
@@ -5210,29 +5210,27 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos)
static void ImGui::UpdateTexturesNewFrame()
{
ImGuiContext& g = *GImGui;
ImFontAtlas* atlas = g.IO.Fonts;
if (g.FontAtlasOwnedByContext)
{
atlas->RendererHasTextures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0;
ImFontAtlasUpdateNewFrame(atlas, g.FrameCount);
}
for (ImFontAtlas* atlas : g.FontAtlases)
if (atlas->OwnerContext == &g)
{
atlas->RendererHasTextures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0;
ImFontAtlasUpdateNewFrame(atlas, g.FrameCount);
}
}
// 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()
{
ImGuiContext& g = *GImGui;
ImFontAtlas* atlas = g.IO.Fonts;
g.PlatformIO.Textures.resize(0);
g.PlatformIO.Textures.reserve(atlas->TexList.Size);
for (ImTextureData* tex : atlas->TexList)
{
// 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.
tex->RefCount = (unsigned short)atlas->RefCount;
g.PlatformIO.Textures.push_back(tex);
}
for (ImFontAtlas* atlas : g.FontAtlases)
for (ImTextureData* tex : atlas->TexList)
{
// 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.
tex->RefCount = (unsigned short)atlas->RefCount;
g.PlatformIO.Textures.push_back(tex);
}
}
// Called once a frame. Followed by SetCurrentFont() which sets up the remaining data.
@@ -5810,8 +5808,8 @@ void ImGui::EndFrame()
UpdateTexturesEndFrame();
// Unlock font atlas
ImFontAtlas* atlas = g.IO.Fonts;
atlas->Locked = false;
for (ImFontAtlas* atlas : g.FontAtlases)
atlas->Locked = false;
// Clear Input data for next frame
g.IO.MousePosPrev = g.IO.MousePos;
@@ -5890,7 +5888,8 @@ void ImGui::Render()
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
ImFontAtlasDebugLogTextureRequests(g.IO.Fonts);
for (ImFontAtlas* atlas : g.FontAtlases)
ImFontAtlasDebugLogTextureRequests(atlas);
#endif
CallContextHooks(&g, ImGuiContextHookType_RenderPost);
@@ -8596,9 +8595,9 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
void ImGui::UpdateFontsNewFrame()
{
ImGuiContext& g = *GImGui;
ImFontAtlas* atlas = g.IO.Fonts;
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.
ImFontStackData font_stack_data = { ImGui::GetDefaultFont(), ImGui::GetDefaultFont()->DefaultSize };
@@ -8614,6 +8613,25 @@ void ImGui::UpdateFontsEndFrame()
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.
// - 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...
@@ -8670,6 +8688,7 @@ void ImGui::UpdateCurrentFontSize()
void ImGui::SetFontRasterizerDensity(float rasterizer_density)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures);
if (g.FontRasterizerDensity == rasterizer_density)
return;
g.FontRasterizerDensity = rasterizer_density;
@@ -16104,12 +16123,12 @@ void ImGui::ShowMetricsWindow(bool* p_open)
}
// Details for Fonts
ImFontAtlas* atlas = g.IO.Fonts;
if (TreeNode("Fonts", "Fonts (%d), Textures (%d)", atlas->Fonts.Size, atlas->TexList.Size))
{
ShowFontAtlas(atlas);
TreePop();
}
for (ImFontAtlas* atlas : g.FontAtlases)
if (TreeNode((void*)atlas, "Fonts (%d), Textures (%d)", atlas->Fonts.Size, atlas->TexList.Size))
{
ShowFontAtlas(atlas);
TreePop();
}
// Details for Popups
if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))

View File

@@ -3684,6 +3684,7 @@ struct ImFontAtlas
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.
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]
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS

View File

@@ -2132,10 +2132,10 @@ struct ImGuiContextHook
struct ImGuiContext
{
bool Initialized;
bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it.
ImGuiIO IO;
ImGuiPlatformIO PlatformIO;
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
ImFontBaked* FontBaked; // == Font->GetFontBaked(FontSize)
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);
// 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 SetFontRasterizerDensity(float rasterizer_density);
inline float GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; }