I have been using one OpenGL-context in one thread (very simplyfied) like this:
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);
}
}
This works well until the calculations of the compute shader take longer. Then it stalls the main-loop. I have been trying to use glFenceSync but glfwSwapBuffers always has to wait until the compute shader is done.
Now I tried another approach: Generating a seperate OpenGL-context in another thread for the compute shader like this:
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
}
}
I changed the main-function to:
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);
}
}
Now what I see always seems to switch between two images (maybe the first two after startup). I think the compute-shader/-thread gives correct results (can't really check, because the main-loop doesn't display it).
What am I missing here? The two threads don't use shared ressources/buffers (that I know of). I generated a seperate framebuffer for the compute-thread. Do I have to generate additional buffers (all the buffers the compute-shader needs are of course generated) or synchronize somehow (the result is stored in a C++-array, so the OpenGL-buffers can be completely seperate)?
Should this approach work in general? And if so, are there general considerations that I did not take into account? If additional code is needed, please let me know.
Edit:
So, I just played around with Sleep(5000) to see when exactly the above error occurs. When I place this call before glMapNamedBuffer the main window seems to work for 5 seconds. Placed after this call it immediatly breaks. Is there anything special about this call I have to consider with multiple OpenGL-contexts?
Window creation with GLFW is only possible in the main thread as stated in the "Thread Safety" section of the GLFW docs
Some other methods like
glfwMakeContextCurrentmay also be called from secondary threads, so what you have to do is to create all windows from the main thread, but then use one of the windows in the calculation thread.Basic structure:
Also note, that currently the buffer mapping is never unmapped, so you should probably call
glUnmapNamedBufferafter thememcpyline. Or you may use persistently mapped buffers.