From 0a7024c198f3bd02ba8da1385977856b50d2efe9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 27 May 2015 15:35:49 +0100 Subject: [PATCH] Store common stacked settings contiguously in memory to reduce cache misses & unnecessary heap allocations --- imgui.cpp | 70 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 01c7db7af..b7abfb116 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1091,7 +1091,7 @@ struct ImGuiDrawContext ImVec2 CursorPos; ImVec2 CursorPosPrevLine; ImVec2 CursorStartPos; - ImVec2 CursorMaxPos; // Implicitly calculate the size of our contents, always extending. Saved into window->SizeContents at the end of the frame + ImVec2 CursorMaxPos; // Implicitly calculate the size of our contents, always extending. Saved into window->SizeContents at the end of the frame float CurrentLineHeight; float CurrentLineTextBaseOffset; float PrevLineHeight; @@ -1108,17 +1108,21 @@ struct ImGuiDrawContext ImGuiStorage* StateStorage; ImGuiLayoutType LayoutType; - bool ButtonRepeat; // == ButtonRepeatStack.back() [false] + // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. + bool ButtonRepeat; // == ButtonRepeatStack.back() [empty == false] + bool AllowKeyboardFocus; // == AllowKeyboardFocusStack.back() [empty == true] + float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window + float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f] ImVector ButtonRepeatStack; - ImVector AllowKeyboardFocus; - ImVector ItemWidth; // 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window - ImVector TextWrapPos; - ImVector GroupStack; + ImVector AllowKeyboardFocusStack; + ImVector ItemWidthStack; + ImVector TextWrapPosStack; + ImVectorGroupStack; ImGuiColorEditMode ColorEditMode; - int StackSizesBackup[6]; // Store size of various stacks for asserting + int StackSizesBackup[6]; // Store size of various stacks for asserting - float ColumnsStartX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) - float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. + float ColumnsStartX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) + float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. int ColumnsCurrent; int ColumnsCount; ImVec2 ColumnsStartPos; @@ -1126,7 +1130,7 @@ struct ImGuiDrawContext float ColumnsCellMaxY; bool ColumnsShowBorders; ImGuiID ColumnsSetID; - ImVector ColumnsOffsetsT; // Columns offset normalized 0.0 (far left) -> 1.0 (far right) + ImVector ColumnsOffsetsT; // Columns offset normalized 0.0 (far left) -> 1.0 (far right) ImGuiDrawContext() { @@ -1801,7 +1805,7 @@ bool ImGuiWindow::FocusItemRegister(bool is_active, bool tab_stop) ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus.back(); + const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus; FocusIdxAllCounter++; if (allow_keyboard_focus) FocusIdxTabCounter++; @@ -3256,7 +3260,7 @@ void ImGui::EndChildFrame() // Save and compare stack sizes on Begin()/End() to detect usage errors static void CheckStacksSize(ImGuiWindow* window, bool write) { - // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) + // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) ImGuiState& g = *GImGui; int* p_backup = &window->DC.StackSizesBackup[0]; { int current = (int)window->IDStack.size(); if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot PopID() @@ -3786,15 +3790,16 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ window->DC.MenuBarOffsetX = ImMax(window->DC.ColumnsStartX, style.ItemSpacing.x); window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; window->DC.ChildWindows.resize(0); - window->DC.ItemWidth.resize(0); - window->DC.ItemWidth.push_back(window->ItemWidthDefault); window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.ItemWidthStack.resize(0); + window->DC.ItemWidthStack.push_back(window->ItemWidthDefault); + window->DC.ItemWidth = window->ItemWidthDefault; window->DC.ButtonRepeat = false; window->DC.ButtonRepeatStack.resize(0); - window->DC.AllowKeyboardFocus.resize(0); - window->DC.AllowKeyboardFocus.push_back(true); - window->DC.TextWrapPos.resize(0); - window->DC.TextWrapPos.push_back(-1.0f); // disabled + window->DC.AllowKeyboardFocus = true; + window->DC.AllowKeyboardFocusStack.resize(0); + window->DC.TextWrapPos = -1.0f; // disabled + window->DC.TextWrapPosStack.resize(0); window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect; window->DC.ColumnsCurrent = 0; window->DC.ColumnsCount = 1; @@ -4004,7 +4009,8 @@ static void FocusWindow(ImGuiWindow* window) void ImGui::PushItemWidth(float item_width) { ImGuiWindow* window = GetCurrentWindow(); - window->DC.ItemWidth.push_back(item_width == 0.0f ? window->ItemWidthDefault : item_width); + window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); + window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); } static void PushMultiItemsWidths(int components, float w_full = 0.0f) @@ -4015,21 +4021,23 @@ static void PushMultiItemsWidths(int components, float w_full = 0.0f) w_full = ImGui::CalcItemWidth(); const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.FramePadding.x*2.0f + style.ItemInnerSpacing.x) * (components-1)) / (float)components)); const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.FramePadding.x*2.0f + style.ItemInnerSpacing.x) * (components-1))); - window->DC.ItemWidth.push_back(w_item_last); + window->DC.ItemWidthStack.push_back(w_item_last); for (int i = 0; i < components-1; i++) - window->DC.ItemWidth.push_back(w_item_one); + window->DC.ItemWidthStack.push_back(w_item_one); + window->DC.ItemWidth = window->DC.ItemWidthStack.back(); } void ImGui::PopItemWidth() { ImGuiWindow* window = GetCurrentWindow(); - window->DC.ItemWidth.pop_back(); + window->DC.ItemWidthStack.pop_back(); + window->DC.ItemWidth = window->DC.ItemWidthStack.back(); } float ImGui::CalcItemWidth() { ImGuiWindow* window = GetCurrentWindow(); - float w = window->DC.ItemWidth.back(); + float w = window->DC.ItemWidth; if (w < 0.0f) { // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well. @@ -4073,13 +4081,15 @@ void ImGui::PopFont() void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) { ImGuiWindow* window = GetCurrentWindow(); - window->DC.AllowKeyboardFocus.push_back(allow_keyboard_focus); + window->DC.AllowKeyboardFocus = allow_keyboard_focus; + window->DC.AllowKeyboardFocusStack.push_back(allow_keyboard_focus); } void ImGui::PopAllowKeyboardFocus() { ImGuiWindow* window = GetCurrentWindow(); - window->DC.AllowKeyboardFocus.pop_back(); + window->DC.AllowKeyboardFocusStack.pop_back(); + window->DC.AllowKeyboardFocus = window->DC.AllowKeyboardFocusStack.empty() ? true : window->DC.AllowKeyboardFocusStack.back(); } void ImGui::PushButtonRepeat(bool repeat) @@ -4096,16 +4106,18 @@ void ImGui::PopButtonRepeat() window->DC.ButtonRepeat = window->DC.ButtonRepeatStack.empty() ? false : window->DC.ButtonRepeatStack.back(); } -void ImGui::PushTextWrapPos(float wrap_x) +void ImGui::PushTextWrapPos(float wrap_pos_x) { ImGuiWindow* window = GetCurrentWindow(); - window->DC.TextWrapPos.push_back(wrap_x); + window->DC.TextWrapPos = wrap_pos_x; + window->DC.TextWrapPosStack.push_back(wrap_pos_x); } void ImGui::PopTextWrapPos() { ImGuiWindow* window = GetCurrentWindow(); - window->DC.TextWrapPos.pop_back(); + window->DC.TextWrapPosStack.pop_back(); + window->DC.TextWrapPos = window->DC.TextWrapPosStack.back(); } void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) @@ -4661,7 +4673,7 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) if (text_end == NULL) text_end = text + strlen(text); // FIXME-OPT - const float wrap_pos_x = window->DC.TextWrapPos.back(); + const float wrap_pos_x = window->DC.TextWrapPos; const bool wrap_enabled = wrap_pos_x >= 0.0f; if (text_end - text > 2000 && !wrap_enabled) {