diff --git a/imgui.cpp b/imgui.cpp index b355f679..ea7b752c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2952,6 +2952,7 @@ void ImGui::GcCompactTransientMiscBuffers() ImGuiContext& g = *GImGui; g.ItemFlagsStack.clear(); g.GroupStack.clear(); + TableGcCompactSettings(); } // Free up/compact internal window buffers, we can use this when a window becomes unused. @@ -3906,6 +3907,11 @@ void ImGui::NewFrame() if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) GcCompactTransientWindowBuffers(window); } + + // Garbage collect transient buffers of recently unused tables + for (int i = 0; i < g.TablesLastTimeActive.Size; i++) + if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) + TableGcCompactTransientBuffers(g.Tables.GetByIndex(i)); if (g.GcCompactAll) GcCompactTransientMiscBuffers(); g.GcCompactAll = false; diff --git a/imgui_internal.h b/imgui_internal.h index b4f2b915..6517a4f4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -597,6 +597,8 @@ struct IMGUI_API ImChunkStream T* end() { return (T*)(void*)(Buf.Data + Buf.Size); } int offset_from_ptr(const T* p) { IM_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; } T* ptr_from_offset(int off) { IM_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); } + void swap(ImChunkStream& rhs) { rhs.Buf.swap(Buf); } + }; //----------------------------------------------------------------------------- @@ -1401,6 +1403,7 @@ struct ImGuiContext ImGuiTable* CurrentTable; ImPool Tables; ImVector CurrentTableStack; + ImVector TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC) ImVector DrawChannelsTempMergeBuffer; // Tab bars @@ -2040,6 +2043,7 @@ struct ImGuiTable bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1) bool IsResetDisplayOrderRequest; bool IsUnfrozen; // Set when we got past the frozen row. + bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis ImGuiTable() @@ -2266,7 +2270,7 @@ namespace ImGui IMGUI_API float GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset); // Tables - IMGUI_API ImGuiTable* FindTableByID(ImGuiID id); + IMGUI_API ImGuiTable* TableFindByID(ImGuiID id); IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f); IMGUI_API void TableBeginUpdateColumns(ImGuiTable* table); IMGUI_API void TableUpdateDrawChannels(ImGuiTable* table); @@ -2291,6 +2295,9 @@ namespace ImGui IMGUI_API void TableSetColumnAutofit(ImGuiTable* table, int column_n); IMGUI_API void PushTableBackground(); IMGUI_API void PopTableBackground(); + IMGUI_API void TableRemove(ImGuiTable* table); + IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); + IMGUI_API void TableGcCompactSettings(); // Tables: Settings IMGUI_API void TableLoadSettings(ImGuiTable* table); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 96bfa849..dd0b421d 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -134,7 +134,7 @@ inline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_w return flags; } -ImGuiTable* ImGui::FindTableByID(ImGuiID id) +ImGuiTable* ImGui::TableFindByID(ImGuiID id) { ImGuiContext& g = *GImGui; return g.Tables.GetByKey(id); @@ -342,9 +342,16 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG outer_window->DC.CurrentTableIdx = table_idx; if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. inner_window->DC.CurrentTableIdx = table_idx; + if ((table_last_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0) table->IsResetDisplayOrderRequest = true; + // Mark as used + if (table_idx >= g.TablesLastTimeActive.Size) + g.TablesLastTimeActive.resize(table_idx + 1, -1.0f); + g.TablesLastTimeActive[table_idx] = (float)g.Time; + table->MemoryCompacted = false; + // Setup memory buffer (clear data if columns count changed) const int stored_size = table->Columns.size(); if (stored_size != 0 && stored_size != columns_count) @@ -2573,10 +2580,15 @@ static void InitTableSettings(ImGuiTableSettings* settings, ImGuiID id, int colu settings->WantApply = true; } +static size_t TableSettingsCalcChunkSize(int columns_count) +{ + return sizeof(ImGuiTableSettings) + (size_t)columns_count * sizeof(ImGuiTableColumnSettings); +} + ImGuiTableSettings* ImGui::TableSettingsCreate(ImGuiID id, int columns_count) { ImGuiContext& g = *GImGui; - ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(sizeof(ImGuiTableSettings) + (size_t)columns_count * sizeof(ImGuiTableColumnSettings)); + ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(TableSettingsCalcChunkSize(columns_count)); InitTableSettings(settings, id, columns_count, columns_count); return settings; } @@ -2759,7 +2771,7 @@ static void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, InitTableSettings(settings, id, columns_count, settings->ColumnsCountMax); // Recycle return settings; } - settings->ID = 0; // Invalidate storage if we won't fit because of a count change + settings->ID = 0; // Invalidate storage, we won't fit because of a count change } return ImGui::TableSettingsCreate(id, columns_count); } @@ -2842,6 +2854,57 @@ void ImGui::TableSettingsInstallHandler(ImGuiContext* context) g.SettingsHandlers.push_back(ini_handler); } +//------------------------------------------------------------------------- +// TABLE - Garbage Collection +//------------------------------------------------------------------------- + +// Remove Table (currently only used by TestEngine) +void ImGui::TableRemove(ImGuiTable* table) +{ + //IMGUI_DEBUG_LOG("TableRemove() id=0x%08X\n", table->ID); + ImGuiContext& g = *GImGui; + int table_idx = g.Tables.GetIndex(table); + //memset(table->RawData.Data, 0, table->RawData.size_in_bytes()); + //memset(table, 0, sizeof(ImGuiTable)); + g.Tables.Remove(table->ID, table); + g.TablesLastTimeActive[table_idx] = -1.0f; +} + +// Free up/compact internal Table buffers for when it gets unused +void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) +{ + //IMGUI_DEBUG_LOG("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID); + ImGuiContext& g = *GImGui; + IM_ASSERT(table->MemoryCompacted == false); + table->DrawSplitter.ClearFreeMemory(); + table->SortSpecsData.clear(); + table->SortSpecs.Specs = NULL; + table->IsSortSpecsDirty = true; + table->ColumnsNames.clear(); + table->MemoryCompacted = true; + for (int n = 0; n < table->ColumnsCount; n++) + table->Columns[n].NameOffset = -1; + g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f; +} + +// Compact and remove unused settings data (currently only used by TestEngine) +void ImGui::TableGcCompactSettings() +{ + ImGuiContext& g = *GImGui; + int required_memory = 0; + for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) + if (settings->ID != 0) + required_memory += (int)TableSettingsCalcChunkSize(settings->ColumnsCount); + if (required_memory == g.SettingsTables.Buf.Size) + return; + ImChunkStream new_chunk_stream; + new_chunk_stream.Buf.reserve(required_memory); + for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) + if (settings->ID != 0) + memcpy(new_chunk_stream.alloc_chunk(TableSettingsCalcChunkSize(settings->ColumnsCount)), settings, TableSettingsCalcChunkSize(settings->ColumnsCount)); + g.SettingsTables.swap(new_chunk_stream); +} + //------------------------------------------------------------------------- // TABLE - Debugging //-------------------------------------------------------------------------