OpenGL Context
OpenGL Context与窗口系统相关,所以在不同平台上创建方式都不一样。
- Windows上使用的是WGL
- Linux上有GLX和EGL两种,分别对应两种窗口系统XWindow和Wayland
- Mac上使用的是AEL
- IOS上使用的是EAGL,对应的是OpenGL ES
- Android上使用的是EGL
EGL 是OpenGL官方Khronos推出的,既可以使用用于OpenGL Context,也可以用于OpenGL ES Context。Google的ANGLE项目使用了EGL,支持各个平台,提供将OpenGL ES/WebGL转译为Vulkan、DirectX、Metal 等系统图形API,Chrome浏览器中的WebGL使用了ANGLE。
Context与线程
- 一条线程同时只能绑定一个OpenGL Context,Context也只能同时绑定到一个线程,OpenGL在绑定的线程当中相当于全局的。如果线程没有绑定Context,则不能调用OpenGL函数。
- OpenGL Context可以在任意线程创建,创建完成后可以将Context绑定到任意线程,也可以从线程解除绑定,然后从新绑定到新的线程。绑定和解绑需要使用MakeCurrent函数,WGL、GLX、EGL、AGL都有自己对应的MakeCurrent函数。WGL的MakeCurrent函数是wglMakeCurrent。
- OpenGL Context虽然创建的时候需要一个窗口,但实际上Context不要任何窗口相关,也就是说创建完成后可将Context绑定到其他窗口上,这样可以实现一个OpenGL Context在多个不同的窗口上渲染。
- EGL创建Context时可以使用pBuffer,使用eglCreatePbufferSurface函数。这让EGL可以在没有窗口系统的服务器系统,以及Docker中使用。
多线程使用OpenGL
消息队列
消息队列是简单直接的方法,通过创建一条线程,让后将OpenGL Context绑定到线程,其他线程通过发生消息的方式调用OpenGL函数,Android中OpenGL ES默认使用的是这种方法。消息队列可以是阻塞队列Blocking Queue实现。
优点:实现简单,使用方便直观,调试简单。
缺点:内存管理比较麻烦,例如主线程要更新UBO内存,要么等等渲染线程map一个指针回来,要么发生整个内存数据到渲染线程,但这时主线程不知道渲染线程什么时候更新完UBO,这导致难以回收和复用这内存。
例子:CppTest/openGL/OpenGLQueue.cpp · 小康6650/StudyProject - Gitee.com
在不同线程绑定Context
这种方式是每次需要使用OpenGL时则绑定Context,使用完后又解绑。为了防止多个线程同时绑定Context,绑定时需要加锁,解绑时需要解锁。因为同时只有一个线程可以使用OpenGL,一个线程在使用时另一个线程只能等待。所以如果渲染线程很快,可能会导致主线程等待绑定OpenGL而产生卡顿。这种方法适合帧率不高的情况,直播软件OBS Studio使用的就是这种方法。
例子:CppTest/openGL/OpenGLLock.cpp · 小康6650/StudyProject - Gitee.com
不同 context 之间共享数据
这种方式是为每个线程创建一个OpenGL Context,然后在不同Context之间共享OpenGL对象。
这种方式程序需要负责OpenGL同步,防止一个Context在渲染纹理,而另一个线程在读取纹理。