Skip to content

Crash with DockBuilder altering the state of already submitted windows #3111

@hsimyu

Description

@hsimyu

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:

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:
image

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();
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions