From 709be8c49540a6eb64bd068d3961ad095a42c26b Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 18 Mar 2026 15:34:12 +0100 Subject: [PATCH] Discard/GC of ImDrawList buffers for unused windows favor restoring them to ~Size*1.05 instead of Capacity when awakening again. (#9303) + made "GC now" button process even active windows. --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 10 ++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 457518dc3..1a4bafa40 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -120,6 +120,10 @@ Other Changes: as a convenience for when using e.g. InvisibleButton(). - Focus: fixed fallback "Debug" window temporarily taking focus and setting io.WantCaptureKeyboard for one frame on e.g. application boot if no other windows are submitted. (#9243) +- Memory: + - Discard/GC of ImDrawList buffers for unused windows favor restoring them to + ~Size*1.05 instead of Capacity when awakening again. Facilitate releasing ImDrawList + buffers after unusual usage spike. (#9303). - Demo: fixed IMGUI_DEMO_MARKER locations for examples applets. (#9261, #3689) [@pthom] - Backends: - SDLGPU3: removed unnecessary call to SDL_WaitForGPUIdle when releasing diff --git a/imgui.cpp b/imgui.cpp index b7c5d1b22..4793e7833 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4633,11 +4633,12 @@ void ImGui::GcCompactTransientMiscBuffers() // Not freed: // - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data) // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost. +// FIXME: Consider exposing of elaborating GC policy, e.g. being able to trim excessive ImDrawList gaps. (#9303) void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window) { window->MemoryCompacted = true; - window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity; - window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity; + window->MemoryDrawListIdxCapacity = ImMin((int)(window->DrawList->IdxBuffer.Size * 1.05f), window->DrawList->IdxBuffer.Capacity); + window->MemoryDrawListVtxCapacity = ImMin((int)(window->DrawList->VtxBuffer.Size * 1.05f), window->DrawList->VtxBuffer.Capacity); window->IDStack.clear(); window->DrawList->_ClearFreeMemory(); window->DC.ChildWindows.clear(); @@ -5665,7 +5666,8 @@ void ImGui::NewFrame() // Mark all windows as not visible and compact unused memory. IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); - const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; + const bool gc_all = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f); + const float memory_compact_start_time = gc_all ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; for (ImGuiWindow* window : g.Windows) { window->WasActive = window->Active; @@ -5675,7 +5677,7 @@ void ImGui::NewFrame() window->BeginCount = 0; // Garbage collect transient buffers of recently unused windows - if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) + if ((!window->WasActive || gc_all) && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) GcCompactTransientWindowBuffers(window); }