-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ideas to manage and clip very large trees (e.g. 100k+ nodes) #3823
Comments
Another idea I recently stumbled upon is the following:
Some other thoughts:
|
Large Tree View Optimization
Performance Disaster
//A tree with 30000 leaf to draw
int nLeafNum = 30000;
ImGui::Begin("Large Tree");
if (ImGui::TreeNodeEx("Large Tree"))
{
for (int i = 0; i < nLeafNum; i++)
{
//the algorithm for rendering
//when index is a multiple of ten
//draw red as a key object
auto strLeafName = std::to_string(i);
bool bIsKey = i % 10 == 0;
ImGui::PushID(0);ImGui::PushStyleColor(ImGuiCol_Text,
bIsKey?ImVec4(1.0f, 0.0f, 0.0f, 1.0f): ImVec4(0.5f, 0.5f, 0.5f, 0.8f));
if (ImGui::TreeNodeEx(strLeafName.c_str(), ImGuiTreeNodeFlags_Leaf))
{
ImGui::TreePop();
}
ImGui::PopStyleColor(1);ImGui::PopID();
}
ImGui::TreePop();
}
ImGui::End();
The magic of ImGui::Dummy
The optimized code int nLeafNum = 30000;
ImGui::Begin("Large Tree Optimaize");
if (ImGui::TreeNodeEx("Large Tree"))
{
//query window and node info
ImVec2 vLastItem = ImGui::GetItemRectMax();
ImVec2 vItemSize = ImGui::GetItemRectSize();
ImVec2 vWindowPos = ImGui::GetWindowPos();
ImVec2 vWindowSize = ImGui::GetWindowSize();
//measure the number of node to draw
int nLeafStart = max(int((vWindowPos.y - vLastItem.y) / vItemSize.y), 0);
int nLeafCanDraw = min(int(vWindowSize.y / vItemSize.y), (int)nLeafNum - nLeafStart);
//blank rect for those node beyond window
if (nLeafStart > 0 && nLeafCanDraw > 0)
{
ImGui::Dummy(ImVec2(10.0f, float(nLeafStart) * vItemSize.y));
}
//all the node we could see
int nDrawLeaf = nLeafStart;
while (nDrawLeaf < nLeafCanDraw+ nLeafStart && nDrawLeaf < nLeafNum)
{
auto strLeafName = std::to_string(nDrawLeaf);
bool bIsKey = nDrawLeaf % 10 == 0;
ImGui::PushID(0); ImGui::PushStyleColor(ImGuiCol_Text, bIsKey ? ImVec4(1.0f, 0.0f, 0.0f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 0.8f));
if (ImGui::TreeNodeEx(strLeafName.c_str(), ImGuiTreeNodeFlags_Leaf))
{
ImGui::TreePop();
}
ImGui::PopStyleColor(1); ImGui::PopID();
nDrawLeaf++;
}
//blank rect for those node exceed window bottom
if (nDrawLeaf < nLeafNum)
{
ImGui::Dummy(ImVec2(10.0f, float(nLeafNum - nDrawLeaf) * vItemSize.y));
}
ImGui::TreePop();
}
ImGui::End();
|
Reposting some of the contents you posted to other thread (now deleted):
I am currently working a little on this problem and may post further ideas later. |
As part of some research some for multi-select and demos I pushed some improvements with would facilitate some form of clipping. (1) 8bab3ea, (2) df38704: i have added a (3) Attached is an experiment to implement clipper by "fast seeking forward" through a tree: This is a zero-caching method. // Use the clipper
// - with indeterminate count (items_count = INT_MAX, and we call SeekCursorForItem() at the end)
DrawCurrentIdx = 0;
DrawClipper.Begin(INT_MAX);
int root_n = 0;
while (DrawClipper.Step())
while (DrawCurrentIdx < DrawClipper.DisplayEnd && root_n < root_node->Childs.Size)
DrawTreeNode(root_node->Childs[root_n++]);
while (root_n < root_node->Childs.Size) // keep going to count
DrawTreeNode(root_node->Childs[root_n++]); void DrawTreeNode(ExampleTreeNode* node)
{
// (..Filtering...)
const bool is_visible = (DrawCurrentIdx >= DrawClipper.DisplayStart && DrawCurrentIdx < DrawClipper.DisplayEnd);
DrawCurrentIdx++;
if (is_visible)
{
ImGui::SetNextItemStorageID((ImGuiID)node->UID); // use node->UID as storage id
//... various decorations
bool node_open = ImGui::TreeNodeEx(node->Name, tree_flags);
//... various decorations
if (node_open)
{
for (ExampleTreeNode* child : node->Childs)
DrawTreeNode(child);
ImGui::TreePop();
}
}
else if (node->Childs.Size > 0)
{
// Clipped
if (ImGui::GetStateStorage()->GetInt(node->UID) != 0) // are we open?
{
ImGui::TreePush(node->Name);
for (ExampleTreeNode* child : node->Childs)
DrawTreeNode(child);
ImGui::TreePop();
}
}
} The wins comes from the fact:
With this method, in the Property Editor tree with 18000 root items:
Again this is a zero-caching method. It's always more efficient if you can linearize your tree by creating an index, but then you need to have an event to notify of tree changes to rebuild the index. It also happens that to implement any form of non-trivial search you will want to have this index anyhow, at which point you can implement something that's much faster. (4) In ce3a8d7 I have pushed a demo to implement multi-select (with shift-selection, mouse drag selection) in a tree. TL;DR; both (3) and (4) will be superseded by the use of an index. Working on that soon. |
I made a simple tree view clipper for large trees, it can be found here https://gist.github.com/nem0/aa343f1c061db651569b5f68900ad63b
It has some downsides, mentioned in the gist.
The text was updated successfully, but these errors were encountered: