diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1a4bafa40..d8d0c3537 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -123,7 +123,9 @@ Other Changes: - 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). + buffers after unusual usage spike. (#9303) + - Fixed GetForegroundDrawList()/GetBackgroundDrawList() per-viewport buffers not being + collected when unused for io.ConfigMemoryCompactTimer amount of time. (#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 4793e7833..1478018a6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5198,12 +5198,12 @@ static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t draw } // Our ImDrawList system requires that there is always a command - if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount) + if (viewport->BgFgDrawListsLastTimeActive[drawlist_no] != (float)g.Time) { draw_list->_ResetForNewFrame(); draw_list->PushTexture(g.IO.Fonts->TexRef); draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); - viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount; + viewport->BgFgDrawListsLastTimeActive[drawlist_no] = (float)g.Time; } return draw_list; } @@ -6089,7 +6089,7 @@ void ImGui::Render() for (ImGuiViewportP* viewport : g.Viewports) { InitViewportDrawData(viewport); - if (viewport->BgFgDrawLists[0] != NULL) + if (viewport->BgFgDrawLists[0] != NULL && viewport->BgFgDrawListsLastTimeActive[0] == (float)g.Time) AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } @@ -6121,7 +6121,7 @@ void ImGui::Render() FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder); // Add foreground ImDrawList (for each active viewport) - if (viewport->BgFgDrawLists[1] != NULL) + if (viewport->BgFgDrawLists[1] != NULL && viewport->BgFgDrawListsLastTimeActive[1] == (float)g.Time) AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch). @@ -15821,6 +15821,7 @@ static void ImGui::UpdateViewportsNewFrame() main_viewport->FramebufferScale = g.IO.DisplayFramebufferScale; IM_ASSERT(main_viewport->FramebufferScale.x > 0.0f && main_viewport->FramebufferScale.y > 0.0f); + const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; for (ImGuiViewportP* viewport : g.Viewports) { // Lock down space taken by menu bars and status bars @@ -15829,6 +15830,14 @@ static void ImGui::UpdateViewportsNewFrame() viewport->WorkInsetMax = viewport->BuildWorkInsetMax; viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f); viewport->UpdateWorkRect(); + + // Garbage collect transient buffers of recently BG/FG drawlists + for (int n = 0; n < IM_COUNTOF(viewport->BgFgDrawLists); n++) + if (viewport->BgFgDrawListsLastTimeActive[n] < memory_compact_start_time && viewport->BgFgDrawLists[n] != NULL) + { + IM_DELETE(viewport->BgFgDrawLists[n]); + viewport->BgFgDrawLists[n] = NULL; + } } } @@ -16867,6 +16876,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGuiDebugAllocInfo* info = &g.DebugAllocInfo; Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount); + Text("Releasing selected unused buffers after: %.2f secs", g.IO.ConfigMemoryCompactTimer); if (SmallButton("GC now")) { g.GcCompactAll = true; } Text("Recent frames with allocations:"); int buf_size = IM_COUNTOF(info->LastEntriesBuf); diff --git a/imgui_internal.h b/imgui_internal.h index c7afcee78..3dbcfd966 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1958,7 +1958,7 @@ struct IMGUI_API ImGuiMultiSelectState // Every instance of ImGuiViewport is in fact a ImGuiViewportP. struct ImGuiViewportP : public ImGuiViewport { - int BgFgDrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used + float BgFgDrawListsLastTimeActive[2]; // Last frame number the background (0) and foreground (1) draw lists were used ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData @@ -1972,7 +1972,7 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 BuildWorkInsetMin; // Work Area inset accumulator for current frame, to become next frame's WorkInset ImVec2 BuildWorkInsetMax; // " - ImGuiViewportP() { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; } + ImGuiViewportP() { BgFgDrawListsLastTimeActive[0] = BgFgDrawListsLastTimeActive[1] = -1.0f; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; } ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); } // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect)