使用来自其他线程的qglcontext

时间:2015-12-14 19:34:23

标签: c++ qt opengl qglwidget

有没有办法从其他线程使用glwidget的qglcontext。因为我需要从其他线程进行一些纹理上传。但是在纹理上传之后甚至在它上下文中也必须在我的渲染glwidget的服务中。是否有文件或固定(假设免费)答案?

1 个答案:

答案 0 :(得分:2)

OpenGL不支持多线程渲染,所有OpenGL调用都必须从创建上下文的线程执行。但是如果您只想加载纹理,可以从其他线程加载它,而不是将结果发布到该线程,从OpenGL上下文创建到例如glTexImage2D,作为图像信息。要做到这一点,必须添加一些线程管理(信号e.t.c ...)。

有关详细信息,请查看Concurrency and OpenGL

QGLWidget multithreaded example?

要从其他线程工作,您必须使用它们创建单独的上下文或执行一些共享上下文管理。

来自官方Qt文档:

从Qt版本4.8开始,对线程化GL渲染的支持得到了改进。我们目前支持三种方案:

  1. 线程中的缓冲区交换。 在双缓冲上下文中交换缓冲区可以是同步锁定调用,这在某些GL实现中可能是昂贵的操作。尤其是嵌入式设备。当GPU进行缓冲交换时,让CPU空闲是不理想的。在这些情况下,可以在主线程中进行渲染,并在单独的线程中执行实际的缓冲区交换。这可以通过以下步骤完成:

    1. 渲染完成后,在主线程中调用doneCurrent()

    2. 调用QGLContext::moveToThread(swapThread)将上下文的所有权转移到交换线程。

    3. 通知交换线程它可以获取上下文。

    4. 使用makeCurrent()在交换线程中使呈现上下文变为当前,然后调用swapBuffers()

    5. 在交换线程中调用doneCurrent()

    6. 调用QGLContext::moveToThread(qApp->thread())并通知主线程已完成交换。

  2. 执行此操作将释放主线程,以便它可以继续,例如,处理UI事件或网络请求。即使涉及上下文交换,与GPU完成交换操作时主线程等待相比也是可取的。请注意,这是高度依赖于实现的。

    1. 在线程中上传纹理。 在线程中进行纹理上传对于处理需要显示的大量图像的应用程序非常有用,例如照片库应用程序。 Qt通过现有的bindTexture() API支持此功能。一种简单的方法是创建两个共享QGLWidgets。一个在主GUI线程中变为当前,而另一个在纹理上载线程中变为当前。上传线程中的小部件从不显示,仅用于与主线程共享纹理。对于通过bindTexture()绑定的每个纹理,通知主线程以便它可以开始使用纹理。

    2. 使用QPainte r在线程中绘制QGLWidget。 在Qt 4.8中,可以在单独的线程中使用QGLWidget绘制QPainter。请注意,QGLPixelBuffersQGLFramebufferObjects也可以这样做。由于仅在GL 2绘图引擎中支持,因此需要OpenGL 2.0或OpenGL ES 2.0。

    3. QGLWidgets只能在主GUI线程中创建。这意味着在可以通过另一个线程绘制窗口小部件之前,需要调用doneCurrent()以从主线程释放GL上下文。然后,您需要调用QGLContext::moveToThread()将上下文的所有权转移到要使其成为当前的线程。此外,主窗体线程将在调整窗口小部件时调度调整大小并将事件绘制到QGLWidget,或者部分窗体被暴露或需要重新绘制。因此,有必要处理这些事件,因为QGLWidget中的默认实现将尝试使QGLWidget的上下文成为当前状态,这将再次干扰呈现到窗口小部件中的任何线程。重新实现QGLWidget::paintEvent()QGLWidget::resizeEvent()以通知呈现线程需要调整大小或更新,并注意不要调用基类实现。如果要渲染动画,则可能根本不需要处理绘制事件,因为渲染线程正在进行定期更新。然后重新实现QGLWidget::paintEvent()就可以做任何事了。

      作为执行线程呈现的一般规则:请注意,用户必须同步不同线程中的绑定和释放上下文。 GL渲染上下文在任何时候都只能在一个线程中是最新的。如果您尝试在QPainter上打开QGLWidget并且窗口小部件的渲染上下文在另一个线程中是最新的,则会失败。

      除此之外,还支持在单独的线程中使用原始GL调用进行渲染。