我一直在一个线程中使用一个 OpenGL 上下文(非常简单),如下所示:
int main
{
// Initialize OpenGL (GLFW / GLEW)
Compile_Shaders();
while (glfwWindowShouldClose(WindowHandle) == 0)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glfwPollEvents();
Calculate_Something(); // Compute Shader
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
GLfloat* mapped = (GLfloat*)(glMapNamedBuffer(bufferResult, GL_READ_ONLY));
memcpy(Result, mapped, sizeof(GLfloat) * ResX * ResY);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
Render(Result);
ImGui_Stuff();
glfwSwapBuffers(WindowHandle);
}
}
在计算着色器的计算花费更长的时间之前,这会很好地工作。然后它停止主循环。我一直在尝试使用
glFenceSync
但是glfwSwapBuffers
总是要等到计算着色器完成。
现在我尝试了另一种方法:在另一个线程中为计算着色器生成一个单独的 OpenGL 上下文,如下所示:
void ComputeThreadFunc()
{
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* WindowHandleCompute = glfwCreateWindow(50, 5, "Something", NULL, NULL);
if (WindowHandle == NULL)
{
std::cout << "Failed to open GLFW window." << std::endl;
return;
}
GLuint Framebuffer;
glfwMakeContextCurrent(WindowHandle);
glGenFramebuffers(1, &Framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, Framebuffer);
// Compile compute shader
while (true)
{
Calculate_Something();
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
GLfloat* mapped = (GLfloat*)(glMapNamedBuffer(bufferResult, GL_READ_ONLY));
memcpy(Result, mapped, sizeof(GLfloat) * ResX * ResY);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
Sleep(100); // Tried different values here to make sure the GPU isn't too saturated
}
}
我将主要功能更改为:
int main
{
// Initialize OpenGL (GLFW / GLEW)
std::thread ComputeThread = std::thread(&ComputeThreadFunc);
while (glfwWindowShouldClose(WindowHandle) == 0)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glfwPollEvents();
Render(Result);
ImGui_Stuff();
glfwSwapBuffers(WindowHandle);
}
}
现在我看到的似乎总是在两张图片之间切换(可能是启动后的前两张)。我认为计算着色器/线程给出了正确的结果(无法真正检查,因为主循环不显示它)。
我在这里错过了什么?这两个线程不使用共享资源/缓冲区(据我所知)。我为计算线程生成了一个单独的帧缓冲区。我是否必须生成额外的缓冲区(当然会生成计算着色器所需的所有缓冲区)或以某种方式同步(结果存储在 C++ 数组中,因此 OpenGL 缓冲区可以完全独立)?
这种方法应该普遍有效吗?如果是这样,是否有我没有考虑到的一般考虑因素?如果需要其他代码,请告诉我。
编辑:
所以,我只是玩弄
Sleep(5000)
,看看什么时候会发生上述错误。当我在 glMapNamedBuffer
之前拨打此电话时,主窗口似乎工作了 5 秒钟。放置在这个调用之后它会立即中断。对于多个 OpenGL 上下文,我必须考虑这个调用有什么特别之处吗?
使用 GLFW 创建窗口只能在主线程中进行,如 GLFW 文档的“线程安全”部分所述
其他一些方法如
glfwMakeContextCurrent
也可能从辅助线程调用,所以你要做的是从主线程创建所有窗口,然后在计算线程中使用其中一个窗口。
基本结构:
int main()
{
//Create Window 1
auto window1 = ...
//Create Window 2
auto window2 = ...
//Start thread
std::thread ComputeThread = std::thread(&ComputeThreadFunc, window2);
//Render onto window1
glfwMakeContextCurrent(window1);
while (glfwWindowShouldClose(window1) == 0)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glfwPollEvents();
Render(Result);
ImGui_Stuff();
glfwSwapBuffers(window1);
}
}
void ComputeThreadFunc(GLFWWindow* window2)
{
GLuint Framebuffer;
glfwMakeContextCurrent(window2);
glGenFramebuffers(1, &Framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, Framebuffer);
// Compile compute shader
while (true)
{
Calculate_Something();
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
GLfloat* mapped = (GLfloat*)(glMapNamedBuffer(bufferResult, GL_READ_ONLY));
memcpy(Result, mapped, sizeof(GLfloat) * ResX * ResY);
Sleep(100); // Tried different values here to make sure the GPU isn't too saturated
}
}
另请注意,当前缓冲区映射永远不会取消映射,因此您可能应该在
glUnmapNamedBuffer
行之后调用 memcpy
。或者您可以使用持久映射缓冲区。