From dbeeeae593a0b35b1a58b7b3f90590546e73aa9d Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Jul 2023 17:31:50 +0200 Subject: [PATCH] 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 8645b84b..92cfa474 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 bdf539db..772661b1 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 373a51bc..5bccb4df 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();