ImDrawList: Fixed bug with PopClipRect() sometimes altering TextureId + fixed merging of draw calls with same TextureId

This commit is contained in:
ocornut 2015-12-16 01:01:34 +00:00
parent 370de414ef
commit 0873da85ec
2 changed files with 44 additions and 25 deletions

View File

@ -9224,7 +9224,7 @@ void ImGui::ShowMetricsWindow(bool* opened)
ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
continue; 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()) if (show_clip_rects && ImGui::IsItemHovered())
{ {
ImRect clip_rect = pcmd->ClipRect; ImRect clip_rect = pcmd->ClipRect;

View File

@ -94,7 +94,7 @@ using namespace IMGUI_STB_NAMESPACE;
// ImDrawList // 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() void ImDrawList::Clear()
{ {
@ -134,11 +134,15 @@ void ImDrawList::ClearFreeMemory()
_Channels.clear(); _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() void ImDrawList::AddDrawCmd()
{ {
ImDrawCmd draw_cmd; ImDrawCmd draw_cmd;
draw_cmd.ClipRect = _ClipRectStack.Size ? _ClipRectStack.back() : GNullClipRect; draw_cmd.ClipRect = GetCurrentClipRect();
draw_cmd.TextureId = _TextureIdStack.Size ? _TextureIdStack.back() : NULL; draw_cmd.TextureId = GetCurrentTextureId();
IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w); IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w);
CmdBuffer.push_back(draw_cmd); CmdBuffer.push_back(draw_cmd);
@ -155,27 +159,52 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
current_cmd->UserCallback = callback; current_cmd->UserCallback = callback;
current_cmd->UserCallbackData = callback_data; 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(); // Force a new command after us (see comment below)
AddDrawCmd();
} }
// 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() void ImDrawList::UpdateClipRect()
{ {
ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; // If current command is used with different settings we need to add a new command
if (!current_cmd || (current_cmd->ElemCount != 0) || current_cmd->UserCallback != NULL) 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(); AddDrawCmd();
return;
} }
else
{ // Try to merge with previous command if it matches, else use current command
ImVec4 current_clip_rect = _ClipRectStack.Size ? _ClipRectStack.back() : GNullClipRect; ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? &CmdBuffer.Data[CmdBuffer.Size-2] : NULL;
if (CmdBuffer.Size >= 2 && ImLengthSqr(CmdBuffer.Data[CmdBuffer.Size-2].ClipRect - current_clip_rect) < 0.00001f) if (prev_cmd && memcmp(&prev_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) == 0 && prev_cmd->TextureId == GetCurrentTextureId())
CmdBuffer.pop_back(); CmdBuffer.pop_back();
else else
current_cmd->ClipRect = current_clip_rect; 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)
{
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. // Scissoring. The values in clip_rect are x1, y1, x2, y2.
void ImDrawList::PushClipRect(const ImVec4& clip_rect) void ImDrawList::PushClipRect(const ImVec4& clip_rect)
{ {
@ -199,16 +228,6 @@ void ImDrawList::PopClipRect()
UpdateClipRect(); 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) void ImDrawList::PushTextureID(const ImTextureID& texture_id)
{ {
_TextureIdStack.push_back(texture_id); _TextureIdStack.push_back(texture_id);