在两个线程中使用两个OpenGL-Context时出现问题(每个线程一个)

2j4z5cfb  于 2023-03-12  发布在  其他
关注(0)|答案(2)|浏览(299)

我已经在一个线程中使用了一个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上下文中考虑吗?

vecaoik1

vecaoik11#

如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
    }
}

还需要注意的是,当前的缓冲区Map从来没有取消过,所以你可能应该在memcpy行之后调用glUnmapNamedBuffer,或者你可以使用持久Map的缓冲区。

gev0vcfq

gev0vcfq2#

好吧,我终于得到了这个工作。
正如上面的编辑中提到的,我可以在计算线程中跟踪对glMapNamedBuffer的有问题的调用(glGetNamedBufferSubData也产生了相同的错误)。没有这些调用,主线程工作正常,但当然有不希望的副作用,我没有从计算着色器得到结果。
我现在把这个调用放在主线程中。要使它工作,必须先在计算线程中调用glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0)来解除缓冲区的绑定,然后在主线程中绑定它。这必须在计算着色器完成 * 之后 * 才能完成,所以我把glMemoryBarrier放在解除绑定调用之前--这不起作用。只有在我把glFinish放在这里之后,它才起作用。
还有两个问题,如果有人能给予我一个答案,我将不胜感激:
为什么在计算线程中调用glMapNamedBuffer会中断主线程?
为什么glfinish可以工作而glMemoryBarrier不行?两者都应该等待,直到计算着色器完成,不是吗?

相关问题