【发布时间】:2015-06-19 11:23:15
【问题描述】:
我正在使用 OpenGL 创建一个图形用户界面应用程序,其中可以有任意数量的窗口 - “多文档界面”样式。
如果只有一个窗口,主循环可能如下所示:
- 处理事件
- 画()
- 交换缓冲区(vsync 导致此阻塞直到垂直监视器刷新)
但是当有 3 个窗口时考虑主循环:
- 每个窗口处理事件
- 每个窗口draw()
- 窗口 1 交换缓冲区(阻塞直到 vsync)
- (一段时间后)窗口 2 交换缓冲区(阻塞直到 vsync)
- (稍后)窗口 3 交换缓冲区(阻塞直到 vsync)
糟糕...现在以正常帧速率的 1/3 呈现应用程序的一帧。
解决方法:实用程序窗口
一种解决方法是只打开一个垂直同步窗口,关闭其余窗口垂直同步。首先在 vsync 窗口上调用 swapBuffers() 并绘制那个,然后在每个窗口上绘制其余的窗口和 swapBuffers()。
这种解决方法在大多数情况下可能看起来不错,但也不是没有问题:
- 一窗特别不雅
- 竞争条件仍可能导致屏幕撕裂
- 某些平台会忽略垂直同步设置并强制其开启
- 我了解到,切换绑定的 OpenGL 上下文是一项昂贵的操作,应该避免。
解决方法:每个窗口一个线程
由于每个线程可以绑定一个 OpenGL 上下文,或许答案是每个窗口有一个线程。
但是,我仍然希望 GUI 是单线程的,因此 3 窗口情况的主循环如下所示:
(对于每个窗口)
- 锁定全局互斥锁
- 处理事件
- 画()
- 解锁全局互斥锁
- swapBuffers()
这行得通吗?这个other question 表示它不会:
事实证明,窗户正在互相“战斗”:看起来像 SwapBuffers 调用是同步的并相互等待,甚至 尽管它们在不同的线程中。我正在逐帧测量 每个窗口的时间和两个窗口,这下降到 30 fps,与 3 到 20 fps 等。
为了调查此声明,我创建了一个simple test program。该程序创建N个窗口和N个线程,每个线程绑定一个窗口,请求每个窗口开启vsync,然后报告帧率。到目前为止,结果如下:
- Linux、X11、4.4.0 NVIDIA 346.47 (2015-04-13)
- 无论打开多少个窗口,帧速率都是 60fps。
- OSX 10.9.5 (2015-04-13)
- 帧率没有上限;交换缓冲区没有阻塞。
解决方法:只有一个上下文,一个大帧缓冲区
我想到的另一个想法:只有一个 OpenGL 上下文,和一个大帧缓冲区,所有窗口的大小放在一起。
每一帧,每个窗口在绘制前调用glViewport设置各自的framebuffer矩形。
所有绘制完成后,在唯一的 OpenGL 上下文中使用 swapBuffers()。
我将调查此解决方法是否可行。我的一些问题是:
- 有这么大的帧缓冲区可以吗?
- 可以每帧多次调用
glViewport吗? - 我使用的窗口库 API 是否允许我创建独立于窗口的 OpenGL 上下文?
- 如果窗口大小不同,会浪费帧缓冲区中的空间吗?
Camilla Berglund,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
根据提问者的说法,
启用垂直同步后,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 无关。
【问题讨论】:
-
所以你的意思是,根据你的实验,每个线程(和窗口)一个上下文可以正常工作?
标签: multithreading opengl