在渲染循环中运行大量模拟时,如何刷新GUI帧



我正在尝试创建一个应用程序,用户只需按下按钮即可运行(耗时的(后端物理模拟。但同时,我希望GUI的其余部分保持功能;冻结";直到模拟结束。现在我知道在OpenGL中组合多个线程进行渲染不是一个好主意,所以我的想法很简单:1(使用一个线程(我想是默认的线程(来运行除物理之外的所有线程。2( 使用另一个线程,只运行物理,正如你所看到的,我在一个函数中限制了它。但即便如此,整个GUI仍然";冻结";因此,在物理学结束之前,它是无用的。我该如何解决该问题?

下面是我认为可以工作的代码。它呈现一个";"测试按钮";为了检查我是否真的能在物理运行时按下它;运行物理学";触发大量计算的按钮。

#include"imgui.h"
#include"imgui_impl_glfw.h"
#include"imgui_impl_opengl3.h"
#include<GL/glew.h>
#include<GLFW/glfw3.h>
#include<cstdio>
#include<cmath>
#include<thread>
//Hypothetical "heavy" computation
void run_physics()
{
for (double z = 0.0; z < 10000.0; z += 0.001)
{
double y = exp(sin(sqrt(z*abs(z)+ cos(z))));
}
return;
}
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow *window = glfwCreateWindow(800,600, "Test", NULL, NULL);
if (window == NULL)
{
printf("Failed to open a glfw window. Exiting...n");
return 0;
}
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
printf("Failed to initialize glew. Exiting...n");
return 0;
}
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
(void)io;
ImGui::StyleColorsDark();
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init("#version 330");
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
while (!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImGui::Begin("Test");
ImGui::Button("Test button");
if (ImGui::Button("Run physics"))
{
//This pauses the render loop until run_physics is done. Good.
//run_physics();
//This also pauses the render loop. Why?
std::thread thr(run_physics);
thr.join();
}
ImGui::End();
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
glfwPollEvents();
}
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwTerminate();
return 0;
}

问题是thr.join()调用,它将阻塞,直到线程退出。

有几种方法可以解决这个问题。例如,可以创建在渲染线程中轮询的原子状态标志。当线程完成时,它会在退出之前设置标志。如果主呈现线程看到它设置的标志,那么它会调用join调用并获取结果,这应该会很快发生。

最新更新