我正在使用OpenGL创建一个图形用户界面应用程序,其中可以有任意数量的窗口 - "多文档界面"风格。
如果有一个窗口,主循环可能如下所示:
但是当有3个窗口时考虑主循环:
糟糕...现在呈现应用程序的一个帧正好以适当的帧速率的1/3发生。
一种解决方法是只打开一个带有vsync的窗口,其余的带有vsync的窗口关闭。首先在vsync窗口上调用swapBuffers()并绘制一个,然后在每个窗口上绘制其余的窗口和swapBuffers()。
这种解决方法在大多数情况下可能看起来很好,但它并非没有问题:
由于每个线程可以绑定一个OpenGL上下文,因此答案可能是每个窗口有一个线程。
我仍然希望GUI是单线程的,所以3窗口情况的主循环看起来像这样:
(对于每个窗口)
这会有用吗?这个other question表示它不会:
事实证明,窗户正在战斗。彼此:看起来像 甚至,SwapBuffers调用是同步的并且彼此等待 虽然他们是分开的线程。我正在测量帧到帧 每个窗口的时间和两个窗口,这下降到30帧/秒 3到20 fps等。
为了调查此声明,我创建了simple test program。该程序创建N个窗口和N个线程,每个线程绑定一个窗口,请求每个窗口启用vsync,然后报告帧速率。到目前为止,结果如下:
我想到的另一个想法是:只有一个OpenGL上下文和一个大的帧缓冲区,所有窗口的大小放在一起。
每个帧,每个窗口调用glViewport
以在绘制之前设置它们各自的帧缓冲矩形。
完成所有绘图后,在唯一的OpenGL上下文中使用swapBuffers()。
我即将调查此解决方法是否有效。我有一些问题是:
glViewport
是否可以?GLFW说:
这不是glViewport的工作方式。不是 缓冲区交换如何工作。每个窗口都有一个 帧缓冲区。你不能让他们分享一个。缓冲区交换是 每个窗口帧缓冲区和上下文只能绑定到一个 窗口一次。这是在操作系统级别而不是限制 GLFW。
This question表示此算法可能有效:
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
问题提问者,
启用V-Sync后,SwapBuffers将同步到最慢的监视器和 更快的显示器上的窗口会变慢。
看起来他们只在Microsoft Windows上对此进行了测试,并且不清楚此解决方案是否适用于所有地方。
还有许多消息来源告诉我makeContextCurrent()在draw()例程中太慢了。
看起来这也不符合EGL的规范。为了允许其他帖子eglSwapBuffers()
,您必须eglMakeCurrent(NULL)
,这意味着您的eglSwapBuffers
现在应该返回EGL_BAD_CONTEXT
。
所以,我的问题是:什么是解决使用vsync的多窗口应用程序问题的最佳方法?这似乎是一个常见的问题,但我还没有找到令人满意的解决方案。
与此问题类似:Synchronizing multiple OpenGL windows to vsync但我想要一个与平台无关的解决方案 - 或者至少是每个平台的解决方案。
这个问题:Using SwapBuffers() with multiple OpenGL canvases and vertical sync?但实际上这个问题与Python无关。
答案 0 :(得分:8)
交换缓冲区(vsync导致阻塞,直到垂直监视器刷新)
不,它没有阻止。缓冲区交换调用可能立即返回,不块。然而它的作用是插入一个同步点,以便延迟执行改变后台缓冲区的命令,直到缓冲区交换发生。 OpenGL命令队列的长度有限。因此,一旦命令队列已满,进一步的OpenGL调用将阻止该程序,直到可以将更多命令推送到队列中。
缓冲交换也不是 OpenGL操作。它是一个图形/窗口系统级操作,独立于OpenGL上下文。只需看看缓冲交换函数:它们采用的唯一参数是drawable(= window)的句柄。实际上,即使您在单个drawable上运行多个OpenGL上下文,也只需将缓冲区交换一次;并且你可以在没有OpenGL上下文的情况下完成当前的绘制。
通常的做法是:
' 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()
由于缓冲区交换没有阻塞,您可以为所有窗口发出所有缓冲区交换,而不会因V-Sync而延迟。但是,下一个OpenGL绘图命令可以解决为交换而发出的后台缓冲区的问题。
解决方法是使用FBO进行实际绘图,并将其与在交换缓冲区循环之前将FBO blit连接到后台缓冲区的循环结合使用:
' 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()
答案 1 :(得分:0)
感谢@andrewrk所有论文的研究,我个人喜欢这样:
使用双缓冲区创建第一个窗口和他的opengl上下文。 此窗口上的活动vsync(swapinterval 1)
创建其他窗口并使用双缓冲区附加第一个上下文。 在其他窗口上禁用vsync(swapinterval 0)
对于每一帧
用于反转每个窗口(最后一个带有vsync的窗口)。
wglMakeCurrent(hdc,commonContext);
画。
SwapBuffer
以这种方式,我实现了vsync,所有窗口都基于同样的vsync。
但我在没有航空的情况下遇到了问题:撕裂...