我正在将GLFW和ImGui用于涉及打开多个窗口的项目。到目前为止,我已经进行了设置,以便每次必须打开一个新窗口时,我都会生成一个线程,该线程创建自己的GLFW窗口和OpenGL上下文。线程函数看起来像这样:

window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
// Check for creation error...
glfwMakeContextCurrent(window);

ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();   // Is this supposed to be done per-thread?
// Calling specific impl-specific ImGui setup methods for GLFW & OpenGL3...
// Set up OpenGL stuff ...

while (!glfwWindowShouldClose(window))
{
    // Some heavy-duty processing happens here...

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

    // ImGui code is here...

    // Rendering some stuff in the window here...

    // Render ImGui last...
    ImGui::Render();
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

    glfwSwapBuffers(window);
}

// Calling impl-specific ImGui shutdown here...
glfwDestroyWindow(window);

我知道GLFW要求您从主线程(称为glfwInit())中轮询事件,所以我的主线程上有一个循环来执行此操作:

while (!appMustExit)
{
    glfwWaitEvents();
}
// appMustExit is set from another thread that waits for console input

所以我遇到的问题是我的ImGui控件不响应任何类型的输入,并且如果我单击“关闭”按钮,glfwWindowShouldClose()永远不会返回true。看来输入状态仅在调用glfwPollEvents()的线程上可用,这使我相信,即使仍然使用单独的线程进行渲染,您也无法结合ImGui和GLFW!

如何解决此问题,以允许ImGui和这些窗口响应GLFW事件?

我之前的尝试使用单个线程在每个窗口上进行迭代并更新/渲染它,但是我希望使用线程来帮助应用程序在打开多个窗口的情况下更好地扩展。

更新:我想澄清一下,该应用程序涉及实时处理复杂的机器视觉,并且ImGui代码部分与控制和响应该机器视觉代码紧密集成。因此,我希望能够在与此处理相同的线程上调用ImGui函数,这也意味着该线程必须能够响应glfw输入。

最佳答案

我能够找到一种方法,通过自由使用thead_local说明符将Dear ImGui更改为(希望)线程安全的库。

imconfig.h中,我必须创建一个新的线程本地的ImGuiContext指针:

struct ImGuiContext;
extern thread_local ImGuiContext* threadCTX;
#define GImGui threadCTX

imgui_impl_glfw.cpp中,我不得不将所有本地/静态变量都更改为thread_local版本:
thread_local GLFWwindow*    g_Window = NULL;    // Per-Thread Main window
thread_local GlfwClientApi  g_ClientApi = GlfwClientApi_Unknown;
thread_local double         g_Time = 0.0;
thread_local bool           g_MouseJustPressed[5] = { false, false, false, false, false };
thread_local GLFWcursor*    g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 };

// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
static thread_local GLFWmousebuttonfun   g_PrevUserCallbackMousebutton = NULL;
static thread_local GLFWscrollfun        g_PrevUserCallbackScroll = NULL;
static thread_local GLFWkeyfun           g_PrevUserCallbackKey = NULL;
static thread_local GLFWcharfun          g_PrevUserCallbackChar = NULL;

同样,在imgui_impl_opengl3.h中,我对OpenGL对象句柄做了相同的操作:
static thread_local char         g_GlslVersionString[32] = "";
static thread_local GLuint       g_FontTexture = 0;
static thread_local GLuint       g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
static thread_local int          g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0;                                // Uniforms location
static thread_local int          g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
static thread_local unsigned int g_VboHandle = 0, g_ElementsHandle = 0;

有了这些少量更改,我现在能够创建GLFW窗口和OpenGL上下文,初始化Dear ImGui,并在每个线程上调用glfwPollEvents,而它们彼此之间不会相互影响。本质上,创建GLFW窗口的每个线程都可以被当作“主”线程使用。

该解决方案可能会遇到一些挫折,但是对于我的用例来说似乎工作得很好,其中每个窗口都在其自己的线程中运行其事件循环,具有自己的OpenGL和ImGui上下文,并且这些窗口彼此之间不会交互或共享资源。

10-08 06:54