-
-
Notifications
You must be signed in to change notification settings - Fork 11.7k
Crash with DockBuilder altering the state of already submitted windows #3111
Description
Version/Branch of Dear ImGui:
Version: 1.76 WIP
Branch: docking (c142540)
Back-end/Renderer/Compiler/OS
Back-ends: imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp (using example_glfw_opengl3 for testing)
Compiler: MSVC
Operating System: Windows10, 64 bit (1903)
My Issue/Question:
I'm testing docking branch and working on the feature to serialize and load the current docking layouts with DockBuilder APIs.
(I recognize these APIs are in beta, but am investigating its possibility)
Before restoring a saved layout, I tried to remove all existing DockNodes with the code like:
// Remove All Active DockNode Trees
ImGuiContext& g = *GImGui;
for (auto* window : g.Windows)
{
if (!window->Active) continue;
if (!window->DockNode) continue;
auto* rootNode = ImGui::DockNodeGetRootNode(window->DockNode);
ImGui::DockBuilderRemoveNode(rootNode->ID);
}When ImGui::DockBuilderRemoveNode() called, my application crashed in this function at https://github.com/ocornut/imgui/blob/docking/imgui.cpp#L14123, throwing read access violation exception.
I found that node has already freed in calling DockBuilderRemoveNodeDockedWindows() so that accessing node is invalid after calling the function.
Is it inappropriate to call ImGui::DockBuilderRemoveNode() with RootNode ID? or is it a bug of DockBuilderRemoveNode()?
Detailed traces how node was freed:
- In
DockBuilderRemoveNodeDockedWindows(),DockContextProcessUndockWindow()is called. (at https://github.com/ocornut/imgui/blob/docking/imgui.cpp#L14221) - In
DockContextProcessUndockWindow(),DockNodeRemoveWindow()is called whenwindow->DockNodeis not NULL. (at https://github.com/ocornut/imgui/blob/docking/imgui.cpp#L12097) - In
DockNodeRemoveWindow(),DockContextRemoveNode()is called. (at https://github.com/ocornut/imgui/blob/docking/imgui.cpp#L12325) - In
DockContextRemoveNode(),IM_DELETE()is called for the node pointer. (at https://github.com/ocornut/imgui/blob/docking/imgui.cpp#L11771)
Note:
In my app, DockSpaces are not created explicitly.
The function below is added to main.cpp in example_glfw_opengl3.
-> There are 4 active windows ("Dear ImGui Demo", "Hello, world!", "saved_dock_state", "window_list").
Also, minimal example for reproduce this issue is provide at the bottom.
void RestoreDockTest()
{
static std::unordered_map<uint32_t, SerializableDockTree> dockTrees;
DrawMainMenu(dockTrees);
{
ImGui::Begin("saved_dock_state");
for (const auto& pair : dockTrees)
{
PrintDockTree(pair.second);
ImGui::Separator();
}
ImGui::End();
}
{
ImGui::Begin("window_list");
ImGui::TextUnformatted("Windows");
ListWindows();
ImGui::End();
}
}Screenshot
Docking state is like this:

Stack Trace
example_glfw_opengl3.exe!ImGui::DockBuilderRemoveNode(unsigned int node_id) 行 14123 C++
example_glfw_opengl3.exe!ApplySavedDockTree(const std::unordered_map<unsigned int,SerializableDockTree,std::hash,std::equal_to,std::allocator<std::pair<unsigned int const ,SerializableDockTree> > > & dockTreeMap) 行 197 C++
example_glfw_opengl3.exe!DrawMainMenu(std::unordered_map<unsigned int,SerializableDockTree,std::hash,std::equal_to,std::allocator<std::pair<unsigned int const ,SerializableDockTree> > > & dockTree) 行 278 C++
example_glfw_opengl3.exe!DockTest() 行 337 C++
example_glfw_opengl3.exe!main(int __formal, char * * __formal) 行 512 C++
Standalone, minimal, complete and verifiable example:
void ReproduceCrash()
{
ImGui::Begin("window1");
if (ImGui::Button("Remove All DockNodes"))
{
// Remove All DockNodes
ImGuiContext& g = *GImGui;
for (auto* window : g.Windows)
{
if (!window->Active) continue;
if (!window->DockNode) continue;
auto* rootNode = ImGui::DockNodeGetRootNode(window->DockNode);
ImGui::DockBuilderRemoveNode(rootNode->ID);
}
}
ImGui::End();
}