From b7cdb5a31ed849fb7603c67dfbe5d5764b222744 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Jul 2023 15:00:51 +0200 Subject: [PATCH 1/5] Scrollbar: layout needs to take account of window border size, so a border size will slightly reduce scrollbar size. (#2522) Rework/revert intent of c1a61d25a. --- docs/CHANGELOG.txt | 3 +++ imgui_widgets.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 32d83b973..1b873c692 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,6 +47,9 @@ Other changes: - InputText: Fixed a case where deactivation frame would write to underlying buffer or call CallbackResize although unnecessary, in a frame where the return value was false. +- Scrollbar: layout needs to take account of window border size, so a border size + will slightly reduce scrollbar size. Generally we tried to make it that window + border size has no incidence on layout but this can't work with thick borders. (#2522) - IO: Added io.ClearEventsQueue() to clear incoming inputs events. (#4921) May be useful in conjunction with io.ClearInputsKeys() if you need to clear both current inputs state and queued events (e.g. when using blocking native diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9b4b03e41..412984a9c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -875,9 +875,9 @@ ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis) const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar) IM_ASSERT(scrollbar_size > 0.0f); if (axis == ImGuiAxis_X) - return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x, outer_rect.Max.y); + return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size); else - return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x, inner_rect.Max.y); + return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size); } void ImGui::Scrollbar(ImGuiAxis axis) From cc4c37dbac16897383cd1c5ae41a632ba833470a Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Jul 2023 16:46:35 +0200 Subject: [PATCH 2/5] Nav: PageUp/PageDown always set NavJustMovedTo even when landing on same spot (which can trigger a selection). This is mostly expected for range-select. + Rename ImGuiNavMoveFlags_Tabbing -> ImGuiNavMoveFlags_IsTabbing --- imgui.cpp | 32 +++++++++++++++++--------------- imgui_internal.h | 9 +++++---- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 066c5c7a4..9858255c2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2918,7 +2918,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); if (is_nav_request) data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0)); - if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && g.NavTabbingDir == -1) + if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1) data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount)); // Add focused/active item @@ -7665,7 +7665,7 @@ void ImGui::FocusItem() return; } - ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSelect; + ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSelect; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; SetNavWindow(window); NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags); @@ -7700,7 +7700,7 @@ void ImGui::SetKeyboardFocusHere(int offset) SetNavWindow(window); - ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi; + ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. if (offset == -1) @@ -11070,7 +11070,7 @@ static void ImGui::NavProcessItem() // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy) if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0) { - const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) != 0; + const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0; if (is_tabbing) { NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags); @@ -11176,7 +11176,7 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); - if (move_flags & ImGuiNavMoveFlags_Tabbing) + if (move_flags & ImGuiNavMoveFlags_IsTabbing) move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId; g.NavMoveSubmitted = g.NavMoveScoringItems = true; @@ -11724,7 +11724,7 @@ void ImGui::NavUpdateCreateTabbingRequest() g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavDisableHighlight == true && g.ActiveId == 0) ? 0 : +1; else g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1; - ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_Activate; + ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down; NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. @@ -11744,7 +11744,7 @@ void ImGui::NavMoveRequestApplyResult() ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL; // Tabbing forward wrap - if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && result == NULL) + if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL) if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID) result = &g.NavTabbingResultFirst; @@ -11752,7 +11752,7 @@ void ImGui::NavMoveRequestApplyResult() const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; if (result == NULL) { - if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavHighlight; if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0) NavRestoreHighlightAfterMove(); @@ -11793,9 +11793,11 @@ void ImGui::NavMoveRequestApplyResult() } if (g.ActiveId != result->ID) ClearActiveID(); - if (g.NavId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0) + + // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) + // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior. + if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0) { - // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = g.NavMoveKeyMods; @@ -11808,14 +11810,14 @@ void ImGui::NavMoveRequestApplyResult() // Restore last preferred position for current axis // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..) - if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) == 0) + if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0) { preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis]; g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel; } // Tabbing: Activates Inputable, otherwise only Focus - if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable) == 0) + if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->InFlags & ImGuiItemFlags_Inputable) == 0) g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate; // Activate @@ -11824,7 +11826,7 @@ void ImGui::NavMoveRequestApplyResult() g.NavNextActivateId = result->ID; g.NavNextActivateFlags = ImGuiActivateFlags_None; g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavHighlight; - if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState; } @@ -11924,14 +11926,14 @@ static float ImGui::NavUpdatePageUpPageDown() nav_scoring_rect_offset_y = -page_offset_y; g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) g.NavMoveClipDir = ImGuiDir_Up; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove; } else if (IsKeyPressed(ImGuiKey_PageDown, true)) { nav_scoring_rect_offset_y = +page_offset_y; g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) g.NavMoveClipDir = ImGuiDir_Down; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove; } else if (home_pressed) { diff --git a/imgui_internal.h b/imgui_internal.h index 0b4ec8dfb..71e6daf67 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1493,10 +1493,11 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_Forwarded = 1 << 7, ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result ImGuiNavMoveFlags_FocusApi = 1 << 9, // Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details) - ImGuiNavMoveFlags_Tabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight - ImGuiNavMoveFlags_Activate = 1 << 11, // Activate/select target item. - ImGuiNavMoveFlags_NoSelect = 1 << 12, // Don't trigger selection by not setting g.NavJustMovedTo - ImGuiNavMoveFlags_NoSetNavHighlight = 1 << 13, // Do not alter the visible state of keyboard vs mouse nav highlight + ImGuiNavMoveFlags_IsTabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight + ImGuiNavMoveFlags_IsPageMove = 1 << 11, // Identify a PageDown/PageUp request. + ImGuiNavMoveFlags_Activate = 1 << 12, // Activate/select target item. + ImGuiNavMoveFlags_NoSelect = 1 << 13, // Don't trigger selection by not setting g.NavJustMovedTo + ImGuiNavMoveFlags_NoSetNavHighlight = 1 << 14, // Do not alter the visible state of keyboard vs mouse nav highlight }; enum ImGuiNavLayer From c649aca20ae7411e934df637cdc52c90029cbacc Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Jul 2023 16:04:26 +0200 Subject: [PATCH 3/5] ImDrawData: changed CmdLists from raw array to ImVector<> owned by ImDrawData itself. Faclitate user-manipulation of the array (#6406, #4879, #1878) + deep swap. (#6597, #6475, #6167, #5776, #5109, #4763, #3515, #1860) + Metrics: avoid misleadingly iterating all layers of DrawDataBuilder as everything is flattened into Layers[0] at this point. # Conflicts: # imgui.cpp # imgui_internal.h --- docs/CHANGELOG.txt | 11 +++++++++ imgui.cpp | 56 +++++++++++++++++++++++----------------------- imgui.h | 21 ++++++++--------- imgui_draw.cpp | 9 ++++++++ imgui_internal.h | 13 +++++------ 5 files changed, 65 insertions(+), 45 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1b873c692..b8a435f19 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -39,9 +39,20 @@ Breaking changes: - IO: Obsoleted io.ClearInputCharacters() (added in 1.47) as it now ambiguous and often incorrect/misleading considering the existence of a higher-level input queue. (#4921) +- ImDrawData: CmdLists[] arra is now owned, changed from 'ImDrawList**' to + 'ImVector'. Majority of users shouldn't be affected, but you + cannot compare to NULL nor reassign manually anymore. + Instead use AddDrawList(). (#6406, #4879, #1878) Other changes: +- ImDrawData: CmdLists[] array is now an ImVector<> owned by ImDrawData rather + than a pointer to internal state. + - This makes it easier for user to create their own or append to an existing draw data. + (#6406, #4879, #1878) + - This makes it easier to perform a deep-swap instead of a deep-copy, as array + ownership is now clear. (#6597, #6475, #6167, #5776, #5109, #4763, #3515, #1860) + - Syntax and allocation count are otherwise identical. - Fixed CTRL+Tab dimming background assert when target window has a callback in the last ImDrawCmd. (#4857, #5937) - InputText: Fixed a case where deactivation frame would write to underlying diff --git a/imgui.cpp b/imgui.cpp index 9858255c2..a4cc8b6da 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -399,6 +399,7 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878) - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15). - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete). - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior. @@ -4823,7 +4824,7 @@ static void AddWindowToDrawData(ImGuiWindow* window, int layer) ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = g.Viewports[0]; g.IO.MetricsRenderWindows++; - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList); + AddDrawListToDrawData(viewport->DrawDataBuilder.Layers[layer], window->DrawList); for (int i = 0; i < window->DC.ChildWindows.Size; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; @@ -4845,36 +4846,36 @@ static inline void AddRootWindowToDrawData(ImGuiWindow* window) void ImDrawDataBuilder::FlattenIntoSingleLayer() { - int n = Layers[0].Size; - int size = n; + int n = Layers[0]->Size; + int full_size = n; for (int i = 1; i < IM_ARRAYSIZE(Layers); i++) - size += Layers[i].Size; - Layers[0].resize(size); + full_size += Layers[i]->Size; + Layers[0]->resize(full_size); for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++) { - ImVector& layer = Layers[layer_n]; - if (layer.empty()) + ImVector* layer = Layers[layer_n]; + if (layer->empty()) continue; - memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*)); - n += layer.Size; - layer.resize(0); + memcpy(Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*)); + n += layer->Size; + layer->resize(0); } } -static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector* draw_lists) +static void SetupViewportDrawData(ImGuiViewportP* viewport) { ImGuiIO& io = ImGui::GetIO(); ImDrawData* draw_data = &viewport->DrawDataP; draw_data->Valid = true; - draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; - draw_data->CmdListsCount = draw_lists->Size; + draw_data->CmdListsCount = draw_data->CmdLists.Size; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = viewport->Pos; draw_data->DisplaySize = viewport->Size; draw_data->FramebufferScale = io.DisplayFramebufferScale; - for (int n = 0; n < draw_lists->Size; n++) + draw_data->OwnerViewport = viewport; + for (int n = 0; n < draw_data->CmdLists.Size; n++) { - ImDrawList* draw_list = draw_lists->Data[n]; + ImDrawList* draw_list = draw_data->CmdLists[n]; draw_list->_PopUnusedDrawCmd(); draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; @@ -5099,9 +5100,9 @@ void ImGui::Render() for (int n = 0; n != g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataBuilder.Clear(); + viewport->DrawDataBuilder.Setup(&viewport->DrawDataP); if (viewport->DrawLists[0] != NULL) - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); + AddDrawListToDrawData(viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } // Draw modal/window whitening backgrounds @@ -5136,9 +5137,9 @@ void ImGui::Render() // Add foreground ImDrawList (for each active viewport) if (viewport->DrawLists[1] != NULL) - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); + AddDrawListToDrawData(viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); - SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); + SetupViewportDrawData(viewport); ImDrawData* draw_data = &viewport->DrawDataP; g.IO.MetricsRenderVertices += draw_data->TotalVtxCount; g.IO.MetricsRenderIndices += draw_data->TotalIdxCount; @@ -13862,7 +13863,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) // DrawLists int drawlist_count = 0; for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) - drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); + drawlist_count += g.Viewports[viewport_i]->DrawDataP.CmdLists.Size; if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count)) { Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); @@ -13870,9 +13871,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) { ImGuiViewportP* viewport = g.Viewports[viewport_i]; - for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) - for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) - DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); + for (int draw_list_i = 0; draw_list_i < viewport->DrawDataP.CmdLists.Size; draw_list_i++) + DebugNodeDrawList(NULL, viewport, viewport->DrawDataP.CmdLists[draw_list_i], "DrawList"); } TreePop(); } @@ -14204,9 +14204,10 @@ void ImGui::DebugNodeColumns(ImGuiOldColumns* columns) } // [DEBUG] Display contents of ImDrawList -void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label) +void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label) { ImGuiContext& g = *GImGui; + IM_UNUSED(viewport); // Used in docking branch ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; int cmd_count = draw_list->CmdBuffer.Size; if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL) @@ -14495,9 +14496,8 @@ void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) (flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "", (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : ""); - for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) - for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) - DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); + for (int draw_list_i = 0; draw_list_i < viewport->DrawDataP.CmdLists.Size; draw_list_i++) + DebugNodeDrawList(NULL, viewport, viewport->DrawDataP.CmdLists[draw_list_i], "DrawList"); TreePop(); } } @@ -14525,7 +14525,7 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) TextDisabled("Note: some memory buffers have been compacted/freed."); ImGuiWindowFlags flags = window->Flags; - DebugNodeDrawList(window, window->DrawList, "DrawList"); + DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList"); BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y); BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", diff --git a/imgui.h b/imgui.h index 36572cc51..9dcfc364f 100644 --- a/imgui.h +++ b/imgui.h @@ -25,7 +25,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.89.8 WIP" -#define IMGUI_VERSION_NUM 18972 +#define IMGUI_VERSION_NUM 18973 #define IMGUI_HAS_TABLE /* @@ -2743,18 +2743,19 @@ struct ImDrawList // as this is one of the oldest structure exposed by the library! Basically, ImDrawList == CmdList) struct ImDrawData { - bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - int CmdListsCount; // Number of ImDrawList* to render - int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size - int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size - ImDrawList** CmdLists; // Array of ImDrawList* to render. The ImDrawList are owned by ImGuiContext and only pointed to from here. - ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications) - ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications) - ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. + bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. + int CmdListsCount; // Number of ImDrawList* to render (should always be == CmdLists.size) + int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size + int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size + ImVector CmdLists; // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here. + ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications) + ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications) + ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. + ImGuiViewport* OwnerViewport; // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not). // Functions ImDrawData() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); } // The ImDrawList are owned by ImGuiContext! + IMGUI_API void Clear(); IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! IMGUI_API void ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. }; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 026c20a11..d218ddf38 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1808,6 +1808,15 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx) // [SECTION] ImDrawData //----------------------------------------------------------------------------- +void ImDrawData::Clear() +{ + Valid = false; + CmdListsCount = TotalIdxCount = TotalVtxCount = 0; + CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them. + DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f); + OwnerViewport = NULL; +} + // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! void ImDrawData::DeIndexAllBuffers() { diff --git a/imgui_internal.h b/imgui_internal.h index 71e6daf67..373a51bc1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -772,11 +772,11 @@ struct IMGUI_API ImDrawListSharedData struct ImDrawDataBuilder { - ImVector Layers[2]; // Global layers for: regular, tooltip + ImVector* Layers[2]; // Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData. + ImVector LayerData1; - void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } - void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } - int GetDrawListCount() const { int count = 0; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) count += Layers[n].Size; return count; } + ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); } + void Setup(ImDrawData* draw_data) { Layers[0] = &draw_data->CmdLists; Layers[1] = &LayerData1; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n]->resize(0); } IMGUI_API void FlattenIntoSingleLayer(); }; @@ -1605,8 +1605,7 @@ struct ImGuiViewportP : public ImGuiViewport int DrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used ImDrawList* DrawLists[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; - + ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). ImVec2 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f. @@ -3240,7 +3239,7 @@ namespace ImGui IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); - IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label); + IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label); IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); IMGUI_API void DebugNodeFont(ImFont* font); IMGUI_API void DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph); From 1a9ddd239622fd7aad874cdda690c89a98187b88 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Jul 2023 16:57:38 +0200 Subject: [PATCH 4/5] ImDrawData: added AddDrawList() helper function. (#6406, #4879, #1878) --- docs/CHANGELOG.txt | 2 +- imgui.cpp | 2 +- imgui.h | 1 + imgui_draw.cpp | 11 +++++++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b8a435f19..d02c15b88 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,7 +49,7 @@ Other changes: - ImDrawData: CmdLists[] array is now an ImVector<> owned by ImDrawData rather than a pointer to internal state. - This makes it easier for user to create their own or append to an existing draw data. - (#6406, #4879, #1878) + Added a ImDrawData::AddDrawList() helper function to do that. (#6406, #4879, #1878) - This makes it easier to perform a deep-swap instead of a deep-copy, as array ownership is now clear. (#6597, #6475, #6167, #5776, #5109, #4763, #3515, #1860) - Syntax and allocation count are otherwise identical. diff --git a/imgui.cpp b/imgui.cpp index a4cc8b6da..8645b84b1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4873,7 +4873,7 @@ static void SetupViewportDrawData(ImGuiViewportP* viewport) draw_data->DisplaySize = viewport->Size; draw_data->FramebufferScale = io.DisplayFramebufferScale; draw_data->OwnerViewport = viewport; - for (int n = 0; n < draw_data->CmdLists.Size; n++) + for (int n = 0; n < draw_data->CmdLists.Size; n++) // Similar to AddDrawList() but we are already added in the array { ImDrawList* draw_list = draw_data->CmdLists[n]; draw_list->_PopUnusedDrawCmd(); diff --git a/imgui.h b/imgui.h index 9dcfc364f..27451dd63 100644 --- a/imgui.h +++ b/imgui.h @@ -2756,6 +2756,7 @@ struct ImDrawData // Functions ImDrawData() { Clear(); } IMGUI_API void Clear(); + IMGUI_API void AddDrawList(ImDrawList* draw_list); // Helper to add an external draw list into an existing ImDrawData. IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! IMGUI_API void ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. }; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index d218ddf38..bdf539dbc 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1817,6 +1817,17 @@ void ImDrawData::Clear() OwnerViewport = NULL; } +void ImDrawData::AddDrawList(ImDrawList* draw_list) +{ + IM_ASSERT(Valid); + IM_ASSERT(CmdLists.Size == CmdListsCount); + draw_list->_PopUnusedDrawCmd(); + CmdLists.push_back(draw_list); + CmdListsCount++; + TotalVtxCount += draw_list->VtxBuffer.Size; + TotalIdxCount += draw_list->IdxBuffer.Size; +} + // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! void ImDrawData::DeIndexAllBuffers() { From dbeeeae593a0b35b1a58b7b3f90590546e73aa9d Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Jul 2023 17:31:50 +0200 Subject: [PATCH 5/5] ImDrawData: Slight refactor so internal logic uses same logic as AddDrawList(). (#6406, #4879, #1878) # Conflicts: # imgui.cpp # imgui_internal.h --- imgui.cpp | 81 +++++++++++++----------------------------------- imgui_draw.cpp | 49 +++++++++++++++++++++++++---- imgui_internal.h | 3 +- 3 files changed, 66 insertions(+), 67 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8645b84b1..92cfa4747 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1012,7 +1012,6 @@ static void FindHoveredWindow(); static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); -static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); // Settings @@ -4784,47 +4783,12 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im } } -static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list) -{ - if (draw_list->CmdBuffer.Size == 0) - return; - if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL) - return; - - // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. - // May trigger for you if you are using PrimXXX functions incorrectly. - IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); - IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); - if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) - IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); - - // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) - // If this assert triggers because you are drawing lots of stuff manually: - // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. - // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents. - // - If you want large meshes with more than 64K vertices, you can either: - // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. - // Most example backends already support this from 1.71. Pre-1.71 backends won't. - // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them. - // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. - // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time: - // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); - // Your own engine or render API may use different parameters or function calls to specify index sizes. - // 2 and 4 bytes indices are generally supported by most graphics API. - // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching - // the 64K limit to split your draw commands in multiple draw lists. - if (sizeof(ImDrawIdx) == 2) - IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); - - out_list->push_back(draw_list); -} - static void AddWindowToDrawData(ImGuiWindow* window, int layer) { ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = g.Viewports[0]; g.IO.MetricsRenderWindows++; - AddDrawListToDrawData(viewport->DrawDataBuilder.Layers[layer], window->DrawList); + ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList); for (int i = 0; i < window->DC.ChildWindows.Size; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; @@ -4844,42 +4808,41 @@ static inline void AddRootWindowToDrawData(ImGuiWindow* window) AddWindowToDrawData(window, GetWindowDisplayLayer(window)); } -void ImDrawDataBuilder::FlattenIntoSingleLayer() +static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder) { - int n = Layers[0]->Size; + int n = builder->Layers[0]->Size; int full_size = n; - for (int i = 1; i < IM_ARRAYSIZE(Layers); i++) - full_size += Layers[i]->Size; - Layers[0]->resize(full_size); - for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++) + for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++) + full_size += builder->Layers[i]->Size; + builder->Layers[0]->resize(full_size); + for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++) { - ImVector* layer = Layers[layer_n]; + ImVector* layer = builder->Layers[layer_n]; if (layer->empty()) continue; - memcpy(Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*)); + memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*)); n += layer->Size; layer->resize(0); } } -static void SetupViewportDrawData(ImGuiViewportP* viewport) +static void InitViewportDrawData(ImGuiViewportP* viewport) { ImGuiIO& io = ImGui::GetIO(); ImDrawData* draw_data = &viewport->DrawDataP; + + viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists; + viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1; + viewport->DrawDataBuilder.Layers[0]->resize(0); + viewport->DrawDataBuilder.Layers[1]->resize(0); + draw_data->Valid = true; - draw_data->CmdListsCount = draw_data->CmdLists.Size; + draw_data->CmdListsCount = 0; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = viewport->Pos; draw_data->DisplaySize = viewport->Size; draw_data->FramebufferScale = io.DisplayFramebufferScale; draw_data->OwnerViewport = viewport; - for (int n = 0; n < draw_data->CmdLists.Size; n++) // Similar to AddDrawList() but we are already added in the array - { - ImDrawList* draw_list = draw_data->CmdLists[n]; - draw_list->_PopUnusedDrawCmd(); - draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; - draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; - } } // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering. @@ -5100,9 +5063,9 @@ void ImGui::Render() for (int n = 0; n != g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataBuilder.Setup(&viewport->DrawDataP); + InitViewportDrawData(viewport); if (viewport->DrawLists[0] != NULL) - AddDrawListToDrawData(viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); + AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } // Draw modal/window whitening backgrounds @@ -5133,14 +5096,14 @@ void ImGui::Render() for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataBuilder.FlattenIntoSingleLayer(); + FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder); // Add foreground ImDrawList (for each active viewport) if (viewport->DrawLists[1] != NULL) - AddDrawListToDrawData(viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); + AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); - SetupViewportDrawData(viewport); ImDrawData* draw_data = &viewport->DrawDataP; + IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount); g.IO.MetricsRenderVertices += draw_data->TotalVtxCount; g.IO.MetricsRenderIndices += draw_data->TotalIdxCount; } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index bdf539dbc..772661b13 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1817,15 +1817,52 @@ void ImDrawData::Clear() OwnerViewport = NULL; } +// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list +// as long at it is expected that the result will be later merged into draw_data->CmdLists[]. +void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list) +{ + if (draw_list->CmdBuffer.Size == 0) + return; + if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL) + return; + + // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. + // May trigger for you if you are using PrimXXX functions incorrectly. + IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); + IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); + if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) + IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); + + // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) + // If this assert triggers because you are drawing lots of stuff manually: + // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. + // Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents. + // - If you want large meshes with more than 64K vertices, you can either: + // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. + // Most example backends already support this from 1.71. Pre-1.71 backends won't. + // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them. + // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. + // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time: + // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); + // Your own engine or render API may use different parameters or function calls to specify index sizes. + // 2 and 4 bytes indices are generally supported by most graphics API. + // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching + // the 64K limit to split your draw commands in multiple draw lists. + if (sizeof(ImDrawIdx) == 2) + IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); + + // Add to output list + records state in ImDrawData + out_list->push_back(draw_list); + draw_list->_PopUnusedDrawCmd(); + draw_data->CmdListsCount++; + draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; + draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; +} + void ImDrawData::AddDrawList(ImDrawList* draw_list) { - IM_ASSERT(Valid); IM_ASSERT(CmdLists.Size == CmdListsCount); - draw_list->_PopUnusedDrawCmd(); - CmdLists.push_back(draw_list); - CmdListsCount++; - TotalVtxCount += draw_list->VtxBuffer.Size; - TotalIdxCount += draw_list->IdxBuffer.Size; + ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list); } // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! diff --git a/imgui_internal.h b/imgui_internal.h index 373a51bc1..5bccb4dfb 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -776,8 +776,6 @@ struct ImDrawDataBuilder ImVector LayerData1; ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); } - void Setup(ImDrawData* draw_data) { Layers[0] = &draw_data->CmdLists; Layers[1] = &LayerData1; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n]->resize(0); } - IMGUI_API void FlattenIntoSingleLayer(); }; //----------------------------------------------------------------------------- @@ -2802,6 +2800,7 @@ namespace ImGui inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. + IMGUI_API void AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list); // Init IMGUI_API void Initialize();