thedmd / imgui-node-editor

Node Editor built using Dear ImGui
MIT License
3.67k stars 539 forks source link

Missing Keyboard Input on High FPS #79

Closed FoxLeader closed 4 years ago

FoxLeader commented 4 years ago

I was trying to use a text input widget inside a node but for some reasons inputs are being discarded or missed very frequently if the window framerate is high, although ImGui itself has no problem managing them (tested with an input widget placed outside the node). Any idea what's going on? A place to start digging the code would be enough. Library is awesome btw!

thedmd commented 4 years ago

Can you show code around your text input?

FoxLeader commented 4 years ago

This is the simplified main loop

void every_frame()
{
    ImGui::CustomEngine::begin();

    menu_bar(); 
    viewport_panel(); // <- Node Editor runs here
    library_panel();
    module_panel();

    ...

    imgui::CustomEngine::end();
}

This is what the begin() function do

imgui::CustomEngine::begin()
{
    ImGui_ImplOpenGL3_NewFrame();
    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();
    ImGuizmo::BeginFrame();
}

And here is where the node editor is running,

bool viewport_panel()
{
    using namespace ax;
    NodeEditor::Begin(mViewport);

    // update and draw all nodes
    for (auto& [id, node] : mNodes)
    {
       ImGui::PushID(node->id);
        NodeEditor::BeginNode(node->id);
        node->update();
        NodeEditor::EndNode();
        ImGui::PopID();
    }

    // draw links
    for (NodeLink& link : mLinks)
        NodeEditor::Link(link.id, link.input, link.output);

    NodeEditor::ShowPopup();

    if (NodeEditor::BeginCreate())
    {
    ...
    }
    NodeEditor::EndCreate();

    if (NodeEditor::BeginDelete())
    {
    ...
    }
    NodeEditor::EndDelete();

    if (NodeEditor::ShowBackgroundContextMenu())
    {
    ...
    }
    else if (NodeEditor::NodeId nodeId; NodeEditor::ShowNodeContextMenu(&nodeId))
    {
    ...
    }

    NodeEditor::End();
    return ImGui::IsItemHovered();
}

This is one of the Nodes that contains an Input Widget

void Node::update()
{
    draw_header(this, DEFAULT_NODE_SIZE, in, out);

    ImGui::SetNextItemWidth(DEFAULT_NODE_SIZE);
    char buffer[1024];
    strcpy(buffer, eventId.c_str());
    if (ImGui::InputTextWithHint("##EventId", "Event Id", buffer, 1024))
    eventId = buffer;
}

It's worth mentioning that GLFW is dispatching event from a DIFFERENT Thread, but neither imgui nor imguizmo have problems with it

    while (!mShutdown)
        glfwWaitEvents();

Thanks for your time!

thedmd commented 4 years ago

I suspect this may be something related to viewports. Does input widget placed in same viewport as node editor behave in same way?

FoxLeader commented 4 years ago

so I managed to reduce everything to this

    ImGui_ImplOpenGL3_NewFrame();
    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();

    NodeEditor::Begin("Test", {300, 200});

    NodeEditor::BeginNode(50);
    static char buffer1[1024];
    ImGui::SetNextItemWidth(200);
    ImGui::InputText("##Buffer1", buffer1, 1024);
    NodeEditor::EndNode();

    NodeEditor::End();

    static char buffer2[1024];
    ImGui::SetNextItemWidth(200);
    ImGui::InputText("##Buffer2", buffer2, 1024);

    ImGui::Render();
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

everything works fine as long as glfw is dispatching events in the same thread. If you put everything on a different thread the input widget Buffer1 is gonna miss a few key hits, while Buffer2 keeps working fine. This doesn't seem to affect mouse clicks, although I'm not 100% sure about it. I'm currently using the official 1.74 release of imgui, if it can be of any help.

thedmd commented 4 years ago

ImGui nor node editor are thread safe. If you want to use it that way you must take care of synchronization by making sure thread only on get exclusive lock on ImGui at a given time. This mean waiting for mutexes.

Your issue is probably caused by ImGui_ImplGlfw_NewFrame() updating input, while Glfw is in the process of updating internal structures and or other thread is using ImGuiIO content. It is racing condition. This issue originate in your app and it should be handled there.

Hope you will solve this problem when you know now where to look.