Skip to content

Commit

Permalink
Fix for draw list splitter generating incorrect vertex offsets when v…
Browse files Browse the repository at this point in the history
…ertex count exceeds index size (ocornut#3129)
  • Loading branch information
ShironekoBen authored and thedmd committed Jun 4, 2020
1 parent 79fbab5 commit 0d94847
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 2 deletions.
73 changes: 73 additions & 0 deletions examples/example_win32_directx11/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,75 @@ void CreateRenderTarget();
void CleanupRenderTarget();
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

#include <random>
void ShowBackendCheckerWindow(bool* p_open)
{
if (!ImGui::Begin("Dear ImGui Backend Checker", p_open))
{
ImGui::End();
return;
}

ImGuiIO& io = ImGui::GetIO();
ImGui::Text("Dear ImGui %s Backend Checker", ImGui::GetVersion());
ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL");
ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL");
ImGui::Text("RendererHasVtxOffset: %s", ((io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) != 0) ? "True" : "False");
ImGui::Text("sizeof(ImDrawIdx): %d", sizeof(ImDrawIdx));
ImGui::Separator();

//if (ImGui::TreeNode("0001: Renderer: Large Mesh Support"))
{
ImDrawList* draw_list = ImGui::GetWindowDrawList();
{
static int vtx_count = 64722;// 60000;
ImGui::SliderInt("VtxCount##1", &vtx_count, 0, 100000);

static int map[100000 / 4];
for (int n = 0; n < vtx_count / 4; n++)
map[n] = n;
std::srand(13);
std::random_shuffle(std::begin(map), std::end(map), [](auto n) { return std::rand() % n; });

ImDrawListSplitter splitter;
splitter.Split(draw_list, 100000 / 4);

ImVec2 p = ImGui::GetCursorScreenPos();
for (int n = 0; n < vtx_count / 4; n++)
{
splitter.SetCurrentChannel(draw_list, map[n]);

float off_x = (float)(n % 100) * 3.0f;
float off_y = (float)(n % 100) * 1.0f;
ImU32 col = IM_COL32(((n * 17) & 255), ((n * 59) & 255), ((n * 83) & 255), 255);
draw_list->AddRectFilled(ImVec2(p.x + off_x, p.y + off_y), ImVec2(p.x + off_x + 50, p.y + off_y + 50), col);
}

splitter.Merge(draw_list);

ImGui::Dummy(ImVec2(300 + 50, 100 + 50));
ImGui::Text("VtxBuffer.Size = %d", draw_list->VtxBuffer.Size);
}
{
static int vtx_count = 60000;
ImGui::SliderInt("VtxCount##2", &vtx_count, 0, 100000);
ImVec2 p = ImGui::GetCursorScreenPos();
for (int n = 0; n < vtx_count / (10 * 4); n++)
{
float off_x = (float)(n % 100) * 3.0f;
float off_y = (float)(n % 100) * 1.0f;
ImU32 col = IM_COL32(((n * 17) & 255), ((n * 59) & 255), ((n * 83) & 255), 255);
draw_list->AddText(ImVec2(p.x + off_x, p.y + off_y), col, "ABCDEFGHIJ");
}
ImGui::Dummy(ImVec2(300 + 50, 100 + 20));
ImGui::Text("VtxBuffer.Size = %d", draw_list->VtxBuffer.Size);
}
//ImGui::TreePop();
}

ImGui::End();
}

// Main code
int main(int, char**)
{
Expand Down Expand Up @@ -99,6 +168,10 @@ int main(int, char**)
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();

static bool show_check_window = true;
if (show_check_window)
ShowBackendCheckerWindow(&show_check_window);

// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);
Expand Down
14 changes: 12 additions & 2 deletions imgui_draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ void ImDrawList::UpdateClipRect()

// Try to merge with previous command if it matches, else use current command
ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL;
if (curr_cmd->ElemCount == 0 && prev_cmd && memcmp(&prev_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) == 0 && prev_cmd->TextureId == GetCurrentTextureId() && prev_cmd->UserCallback == NULL)
if (curr_cmd->ElemCount == 0 && prev_cmd && memcmp(&prev_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) == 0 && prev_cmd->VtxOffset == _VtxCurrentOffset && prev_cmd->TextureId == GetCurrentTextureId() && prev_cmd->UserCallback == NULL)
CmdBuffer.pop_back();
else
curr_cmd->ClipRect = curr_clip_rect;
Expand All @@ -480,7 +480,7 @@ void ImDrawList::UpdateTextureID()

// Try to merge with previous command if it matches, else use current command
ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL;
if (curr_cmd->ElemCount == 0 && prev_cmd && prev_cmd->TextureId == curr_texture_id && memcmp(&prev_cmd->ClipRect, &GetCurrentClipRect(), sizeof(ImVec4)) == 0 && prev_cmd->UserCallback == NULL)
if (curr_cmd->ElemCount == 0 && prev_cmd && prev_cmd->TextureId == curr_texture_id && memcmp(&prev_cmd->ClipRect, &GetCurrentClipRect(), sizeof(ImVec4)) == 0 && prev_cmd->VtxOffset == _VtxCurrentOffset && prev_cmd->UserCallback == NULL)
CmdBuffer.pop_back();
else
curr_cmd->TextureId = curr_texture_id;
Expand Down Expand Up @@ -1403,6 +1403,8 @@ void ImDrawListSplitter::Merge(ImDrawList* draw_list)
if (int sz = ch._IdxBuffer.Size) { memcpy(idx_write, ch._IdxBuffer.Data, sz * sizeof(ImDrawIdx)); idx_write += sz; }
}
draw_list->_IdxWritePtr = idx_write;
if ((draw_list->CmdBuffer.Size > 0) && (draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1].VtxOffset != draw_list->_VtxCurrentOffset))
draw_list->AddDrawCmd(); // If the vertex offset has changed we need a new draw command
draw_list->UpdateClipRect(); // We call this instead of AddDrawCmd(), so that empty channels won't produce an extra draw call.
draw_list->UpdateTextureID();
_Count = 1;
Expand All @@ -1413,13 +1415,21 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx)
IM_ASSERT(idx >= 0 && idx < _Count);
if (_Current == idx)
return;
ImDrawCmd* old_curr_cmd = (draw_list->CmdBuffer.Size > 0) ? &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1] : NULL; // The "old" current command
// Overwrite ImVector (12/16 bytes), four times. This is merely a silly optimization instead of doing .swap()
memcpy(&_Channels.Data[_Current]._CmdBuffer, &draw_list->CmdBuffer, sizeof(draw_list->CmdBuffer));
memcpy(&_Channels.Data[_Current]._IdxBuffer, &draw_list->IdxBuffer, sizeof(draw_list->IdxBuffer));
_Current = idx;
memcpy(&draw_list->CmdBuffer, &_Channels.Data[idx]._CmdBuffer, sizeof(draw_list->CmdBuffer));
memcpy(&draw_list->IdxBuffer, &_Channels.Data[idx]._IdxBuffer, sizeof(draw_list->IdxBuffer));
draw_list->_IdxWritePtr = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size;
// If the vertex offset or clip rect changed then we need a new draw call (FIXME: What about user callbacks?)
if (draw_list->CmdBuffer.Size > 0)
{
ImDrawCmd* new_curr_cmd = &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1]; // The "new" current command
if ((new_curr_cmd->VtxOffset != draw_list->_VtxCurrentOffset) || (old_curr_cmd && (memcmp(&new_curr_cmd->ClipRect, &old_curr_cmd->ClipRect, sizeof(ImVec4)) != 0)))
draw_list->AddDrawCmd();
}
}

//-----------------------------------------------------------------------------
Expand Down

0 comments on commit 0d94847

Please sign in to comment.