GLFW & ImGui:从主线程以外的线程创建 ImGui 控件
GLFW & ImGui: Creating ImGui controls from thread other than main
我在一个涉及打开多个 windows 的项目中使用 GLFW 和 ImGui。到目前为止,我已经设置了它,以便每次必须打开一个新的 window 时,我都会生成一个线程来创建它自己的 GLFW window 和 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()
永远不会 returns 如果我单击“关闭”按钮。似乎输入状态仅在调用 glfwPollEvents()
的线程上可用,这使我相信您不能在仍然使用单独的线程进行渲染的同时组合 ImGui 和 GLFW!
我该如何解决这个问题,让 ImGui 和这些 windows 响应 GLFW 事件?
我之前尝试使用单个线程遍历每个 window 和 update/render,但我希望使用线程来帮助应用程序更好地扩展许多 windows 打开.
更新:我想澄清一下,这个应用程序涉及实时处理复杂的机器视觉,并且ImGui代码部分与控制和响应这台机器高度集成视觉代码。因此我希望能够在与此处理相同的线程上调用 ImGui 函数,这也意味着此线程必须能够响应 glfw 输入。
为什么首先要创建多个线程?
您可以完美地创建多个 GLFW windows 和多个 Dear ImGui 上下文,并在同一个线程中管理所有内容。从不同的线程工作只会让一切变得更难处理。
在 Dear ImGui 的特定情况下,您可以使用 'docking' 分支中的多视口功能,它本身支持在主视口和 [=15= 之外提取任何亲爱的 imgui windows ] GLFW windows 给你。这一切都由一个亲爱的 imgui 上下文处理,所以你可以,例如从一个 window 拖放到另一个
我能够找到一种方法将 Dear ImGui 更改为(希望如此)线程安全的库,并自由使用 thead_local
说明符。
在 imconfig.h
中,我必须创建一个新的线程本地 ImGuiContext 指针:
struct ImGuiContext;
extern thread_local ImGuiContext* threadCTX;
#define GImGui threadCTX
在 imgui_impl_glfw.cpp
中,我必须将所有 local/static 变量更改为 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 window 和 OpenGL 上下文,初始化 Dear ImGui,并在每个线程上调用 glfwPollEvents
,而不会相互影响。本质上,每个创建 GLFW window 的线程都可以像 'main' 线程一样使用。
这个解决方案可能有一些挫折,但它似乎适用于我的用例,其中每个 window 在它自己的线程中运行它的事件循环,有它自己的 OpenGL 和 ImGui 上下文,并且windows 不相互交流或共享资源。
我在一个涉及打开多个 windows 的项目中使用 GLFW 和 ImGui。到目前为止,我已经设置了它,以便每次必须打开一个新的 window 时,我都会生成一个线程来创建它自己的 GLFW window 和 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()
永远不会 returns 如果我单击“关闭”按钮。似乎输入状态仅在调用 glfwPollEvents()
的线程上可用,这使我相信您不能在仍然使用单独的线程进行渲染的同时组合 ImGui 和 GLFW!
我该如何解决这个问题,让 ImGui 和这些 windows 响应 GLFW 事件?
我之前尝试使用单个线程遍历每个 window 和 update/render,但我希望使用线程来帮助应用程序更好地扩展许多 windows 打开.
更新:我想澄清一下,这个应用程序涉及实时处理复杂的机器视觉,并且ImGui代码部分与控制和响应这台机器高度集成视觉代码。因此我希望能够在与此处理相同的线程上调用 ImGui 函数,这也意味着此线程必须能够响应 glfw 输入。
为什么首先要创建多个线程? 您可以完美地创建多个 GLFW windows 和多个 Dear ImGui 上下文,并在同一个线程中管理所有内容。从不同的线程工作只会让一切变得更难处理。
在 Dear ImGui 的特定情况下,您可以使用 'docking' 分支中的多视口功能,它本身支持在主视口和 [=15= 之外提取任何亲爱的 imgui windows ] GLFW windows 给你。这一切都由一个亲爱的 imgui 上下文处理,所以你可以,例如从一个 window 拖放到另一个
我能够找到一种方法将 Dear ImGui 更改为(希望如此)线程安全的库,并自由使用 thead_local
说明符。
在 imconfig.h
中,我必须创建一个新的线程本地 ImGuiContext 指针:
struct ImGuiContext;
extern thread_local ImGuiContext* threadCTX;
#define GImGui threadCTX
在 imgui_impl_glfw.cpp
中,我必须将所有 local/static 变量更改为 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 window 和 OpenGL 上下文,初始化 Dear ImGui,并在每个线程上调用 glfwPollEvents
,而不会相互影响。本质上,每个创建 GLFW window 的线程都可以像 'main' 线程一样使用。
这个解决方案可能有一些挫折,但它似乎适用于我的用例,其中每个 window 在它自己的线程中运行它的事件循环,有它自己的 OpenGL 和 ImGui 上下文,并且windows 不相互交流或共享资源。