diff --git a/imgui.cpp b/imgui.cpp index b128dc73..fa3e3b65 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9224,7 +9224,7 @@ void ImGui::ShowMetricsWindow(bool* opened) ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); continue; } - ImGui::BulletText("Draw %d %s vtx, tex = %p, clip_rect = (%.0f,%.0f)..(%.0f,%.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); + ImGui::BulletText("Draw %-4d %s vtx, tex = %p, clip_rect = (%.0f,%.0f)..(%.0f,%.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); if (show_clip_rects && ImGui::IsItemHovered()) { ImRect clip_rect = pcmd->ClipRect; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 3ec8168d..ce6f636b 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -94,7 +94,7 @@ using namespace IMGUI_STB_NAMESPACE; // ImDrawList //----------------------------------------------------------------------------- -static ImVec4 GNullClipRect(-8192.0f, -8192.0f, +8192.0f, +8192.0f); // Large values that are easy to encode in a few bits+shift +static const ImVec4 GNullClipRect(-8192.0f, -8192.0f, +8192.0f, +8192.0f); // Large values that are easy to encode in a few bits+shift void ImDrawList::Clear() { @@ -134,11 +134,15 @@ void ImDrawList::ClearFreeMemory() _Channels.clear(); } +// Use macros because C++ is a terrible language, we want guaranteed inline, no code in header, and no overhead in Debug mode +#define GetCurrentClipRect() (_ClipRectStack.Size ? _ClipRectStack.Data[_ClipRectStack.Size-1] : GNullClipRect) +#define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : NULL) + void ImDrawList::AddDrawCmd() { ImDrawCmd draw_cmd; - draw_cmd.ClipRect = _ClipRectStack.Size ? _ClipRectStack.back() : GNullClipRect; - draw_cmd.TextureId = _TextureIdStack.Size ? _TextureIdStack.back() : NULL; + draw_cmd.ClipRect = GetCurrentClipRect(); + draw_cmd.TextureId = GetCurrentTextureId(); IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w); CmdBuffer.push_back(draw_cmd); @@ -155,27 +159,52 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) current_cmd->UserCallback = callback; current_cmd->UserCallbackData = callback_data; - // Force a new command after us (we function this way so that the most common calls AddLine, AddRect, etc. always have a command to add to without doing any check). - AddDrawCmd(); + AddDrawCmd(); // Force a new command after us (see comment below) } +// Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack. +// The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only. void ImDrawList::UpdateClipRect() { - ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; - if (!current_cmd || (current_cmd->ElemCount != 0) || current_cmd->UserCallback != NULL) + // If current command is used with different settings we need to add a new command + const ImVec4 curr_clip_rect = GetCurrentClipRect(); + ImDrawCmd* curr_cmd = CmdBuffer.Size > 0 ? &CmdBuffer.Data[CmdBuffer.Size-1] : NULL; + if (!curr_cmd || (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) != 0) || curr_cmd->UserCallback != NULL) { AddDrawCmd(); + return; } + + // Try to merge with previous command if it matches, else use current command + ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? &CmdBuffer.Data[CmdBuffer.Size-2] : NULL; + if (prev_cmd && memcmp(&prev_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) == 0 && prev_cmd->TextureId == GetCurrentTextureId()) + CmdBuffer.pop_back(); else + curr_cmd->ClipRect = curr_clip_rect; +} + +void ImDrawList::UpdateTextureID() +{ + // If current command is used with different settings we need to add a new command + const ImTextureID curr_texture_id = GetCurrentTextureId(); + ImDrawCmd* curr_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; + if (!curr_cmd || (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != curr_texture_id) || curr_cmd->UserCallback != NULL) { - ImVec4 current_clip_rect = _ClipRectStack.Size ? _ClipRectStack.back() : GNullClipRect; - if (CmdBuffer.Size >= 2 && ImLengthSqr(CmdBuffer.Data[CmdBuffer.Size-2].ClipRect - current_clip_rect) < 0.00001f) - CmdBuffer.pop_back(); - else - current_cmd->ClipRect = current_clip_rect; + AddDrawCmd(); + return; } + + // Try to merge with previous command if it matches, else use current command + ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? &CmdBuffer.Data[CmdBuffer.Size-2] : NULL; + if (prev_cmd && prev_cmd->TextureId == curr_texture_id && memcmp(&prev_cmd->ClipRect, &GetCurrentClipRect(), sizeof(ImVec4)) == 0) + CmdBuffer.pop_back(); + else + curr_cmd->TextureId = curr_texture_id; } +#undef GetCurrentClipRect +#undef GetCurrentTextureId + // Scissoring. The values in clip_rect are x1, y1, x2, y2. void ImDrawList::PushClipRect(const ImVec4& clip_rect) { @@ -199,16 +228,6 @@ void ImDrawList::PopClipRect() UpdateClipRect(); } -void ImDrawList::UpdateTextureID() -{ - ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; - const ImTextureID texture_id = _TextureIdStack.Size ? _TextureIdStack.back() : NULL; - if (!current_cmd || (current_cmd->ElemCount != 0 && current_cmd->TextureId != texture_id) || current_cmd->UserCallback != NULL) - AddDrawCmd(); - else - current_cmd->TextureId = texture_id; -} - void ImDrawList::PushTextureID(const ImTextureID& texture_id) { _TextureIdStack.push_back(texture_id);