diff --git a/imgui.cpp b/imgui.cpp index bd01afed5..d79690088 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -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)) diff --git a/imgui.h b/imgui.h index 71e49efe4..4ae0bd84b 100644 --- a/imgui.h +++ b/imgui.h @@ -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 diff --git a/imgui_internal.h b/imgui_internal.h index f145fd012..fc44490d4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -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 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; }