我已经在一个线程中使用了一个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);
}
}
现在我看到的似乎总是在两个图像之间切换(可能是启动后的前两个)。我认为compute-shader/-thread给出了正确的结果(无法真正检查,因为主循环没有显示它)。
我错过了什么?这两个线程没有使用共享资源/缓冲区(据我所知)。我为计算线程生成了一个单独的帧缓冲区。我是否必须生成额外的缓冲区(当然会生成计算着色器所需的所有缓冲区)或以某种方式同步(结果存储在C++数组中,因此OpenGL缓冲区可以完全分离)?
这种方法是否可以普遍使用?如果可以,是否有我没有考虑到的一般性因素?如果需要额外的代码,请让我知道。
编辑:
所以,我只是在Sleep(5000)
上试验一下,看看上面的错误到底是什么时候发生的。当我在glMapNamedBuffer
之前调用这个函数时,主窗口似乎工作了5秒钟。当我在这个函数之后调用它时,它立即中断。这个函数有什么特殊的地方需要我在多个OpenGL上下文中考虑吗?
2条答案
按热度按时间vecaoik11#
如GLFW文档的“线程安全”部分所述,只能在主线程中使用GLFW创建窗口
其他一些方法,如
glfwMakeContextCurrent
,也可以从辅助线程调用,因此您需要做的是从主线程创建所有窗口,但随后在计算线程中使用其中一个窗口。基本结构:
还需要注意的是,当前的缓冲区Map从来没有取消过,所以你可能应该在
memcpy
行之后调用glUnmapNamedBuffer
,或者你可以使用持久Map的缓冲区。gev0vcfq2#
好吧,我终于得到了这个工作。
正如上面的编辑中提到的,我可以在计算线程中跟踪对
glMapNamedBuffer
的有问题的调用(glGetNamedBufferSubData
也产生了相同的错误)。没有这些调用,主线程工作正常,但当然有不希望的副作用,我没有从计算着色器得到结果。我现在把这个调用放在主线程中。要使它工作,必须先在计算线程中调用
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0)
来解除缓冲区的绑定,然后在主线程中绑定它。这必须在计算着色器完成 * 之后 * 才能完成,所以我把glMemoryBarrier
放在解除绑定调用之前--这不起作用。只有在我把glFinish
放在这里之后,它才起作用。还有两个问题,如果有人能给予我一个答案,我将不胜感激:
为什么在计算线程中调用
glMapNamedBuffer
会中断主线程?为什么
glfinish
可以工作而glMemoryBarrier
不行?两者都应该等待,直到计算着色器完成,不是吗?