Multiple OpenGL contexts, multiple windows, multithreading and Vsync
I am using OpenGL to create a graphical user interface application that can have any number of windows - the "multi document interface" style
If there is a window, the main loop may be as follows:
>Handle events > draw() > swap buffer (Vsync causes blocking until the vertical monitor flushes)
However, when there are 3 windows, consider the main loop:
>Each window handles events > each window draw() > window 1 swap buffer (block until Vsync) > (after a period of time) window 2 swap buffer (block until Vsync) > (after a period of time) window 3 swap buffer (block until Vsync)
Bad... Now the application that renders a frame occurs at 1 / 3 of the correct frame rate
Workaround: utility window
One solution is that only one of the windows is open with Vsync and the rest is closed with Vsync First call swapbuffers () on the Vsync window, then draw one, and then draw the remaining windows and swapbuffers () on each window
This solution may look good, but it's not without a problem:
A window is special, which is inappropriate > a game may lead to screen tearing > some platforms ignore the Vsync setting and force it to turn on > I read that the switching of OpenGL context is an expensive operation and should be avoided
Solution: one thread per window
Because each thread can have an OpenGL context binding, perhaps the answer is that each window has a thread
I still want the GUI to be single threaded, but the main loop in the 3 window case is as follows:
(each window)
>Lock global mutex > handle events > draw() > unlock global mutex > swapbuffers()
This other question means no:
To investigate this statement, I created a simple test program The program creates n windows and N threads. Each thread binds a window, requests each window to enable Vsync, and then reports the frame rate So far, the results are as follows:
> Linux,X11,4.4. 0 NVIDIA 346.47(2015-04-13)
>The frame rate is 60 FPS, no matter how many windows are open
> OSX 10.9. 5(2015-04-13)
There is no upper limit on the frame rate; The swap buffer is not blocked
Solution: there is only one context and a large frame buffer
Another idea that came to my mind is that there is only one OpenGL context, a large framebuffer, and the sizes of all windows are put together
Each frame and window calls glviewport to set their respective framebuffer rectangle before drawing
After all drawings are completed, swapbuffers() is in a unique OpenGL context
I am going to investigate whether this solution is feasible I have some questions:
>Is there such a large frame buffer? > Can I call glviewport multiple times per frame? > Does the window library API I'm using allow me to create OpenGL contexts independently of windows? > If the windows are of different sizes, waste space in the frame buffer?
Camilla Berglund, glfw's maintainer, said:
Solution: there is only one context
This question indicates that the algorithm may work:
Activate OpenGL context on window 1 Draw scene in to window 1 Activate OpenGL context on window 2 Draw scene in to window 2 Activate OpenGL context on window 3 Draw scene in to window 3 For all Windows SwapBuffers
According to the questioner,
It seems that they have only tested on Microsoft Windows, and this solution will be everywhere
Many other sources tell me that makecontextcurrent () is too slow in the draw () routine
It also does not appear to comply with the EGL specification In order to allow another thread eglswapbuffers (), you must eglmakecurrent (null), which means that your eglswapbuffers should now return EGL_ BAD_ CONTEXT.
problem
So my question is: what is the best way to open a multi window application using Vsync? This seems to be a common problem, but I haven't seen a satisfactory solution yet
Similar problems
Similar to this problem: synchronizing multiple OpenGL windows to Vsync, but I want a platform independent solution - at least for each platform
And this question: using swapbuffers() with multiple OpenGL canvas and vertical sync? But really, this problem has nothing to do with Python
Solution
No, it doesn't stop Buffer swap calls return immediately without blocking However, it inserts a synchronization point so that any command to change the background buffer is delayed until a buffer swap occurs The length of OpenGL command queue is limited, which makes program blocks possible
Buffer swapping is also not an OpenGL operation It is a graphics / window system level operation, independent of OpenGL context Just look at the buffer swap function: their only parameter is the handle to drawable (= window) In fact, even if you have multiple OpenGL contexts running on a drawable, you only need to swap the buffers once; You can use drawable without OpenGL context
So the usual approach is:
' first do all the drawing operations foreach w in windows: foreach ctx in w.contexts: ctx.make_current(w) do_opengl_stuff() glFlush() ' with all the drawing commands issued ' loop over all the windows and issue ' the buffer swaps. foreach w in windows: w.swap_buffers()
Since buffer swapping is not blocked, you can issue all buffer swapping for all windows without being delayed by V-sync However, the next OpenGL drawing command that addresses the background buffer for swapping may stop
The solution is to use the FBO that occurs in the actual drawing and combine it with the FBO BLIT loop to the background buffer before the swap buffer loop:
' first do all the drawing operations foreach w in windows: foreach ctx in w.contexts: ctx.make_current(w) glBindFramebuffer(GL_DRAW_BUFFER,ctx.master_fbo) do_opengl_stuff() glFlush() ' blit the FBOs' renderbuffers to the main back buffer foreach w in windows: foreach ctx in w.contexts: ctx.make_current(w) glBindFramebuffer(GL_DRAW_BUFFER,0) blit_renderbuffer_to_backbuffer(ctx.master_renderbuffer) glFlush() ' with all the drawing commands issued ' loop over all the windows and issue ' the buffer swaps. foreach w in windows: w.swap_buffers()