有没有办法在Qt GUI应用程序的工作线程中使用OpenGL安全地绑定纹理?

ksm*_*ing 6 c++ opengl qt worker-thread

我目前正在开发一个GUI软件项目,用于使用Qt可视化3D场景.GUI允许用户将批量的3D数据文件(例如.obj)与.mtl支持和.stl以及2D图像文件一起加载到场景中,作为在QGLWidget派生的窗口小部件上呈现的SceneObject类对象.

然而,当我在主GUI线程上批量加载它们时,加载时间过长会导致GUI冻结,这很难看.我尝试在单独的线程上执行加载,但有一个很大的问题:当加载.obj纹理或图像文件时,我还将在加载每个图像或纹理后立即使用OpenGL glBindtexture()执行绑定,这样我只需要保存每个SceneObject实例中的纹理ID.当我尝试在工作线程中执行加载时,整个程序就会崩溃.

我已经读过,每个线程只能访问一个OGL上下文,并且跨线程的上下文切换是实现我想要做的事情的一种危险方式.另一种可能的方法是在加载完成后在GUI线程上执行纹理绑定,但这意味着我的SceneObject类完全重新设计:(

任何人都可以给我一些关于如何实现加载线程以将资源加载到OpenGL场景的建议吗?

dat*_*olf 5

我还将在加载每个图像或纹理后立即使用OpenGL glBindtexture()执行绑定,这样我只需要在每个SceneObject实例中保存纹理ID.当我尝试在工作线程中执行加载时,整个程序就会崩溃.

OpenGL上下文一次只能在一个线程中处于活动状态.通常,多线程OpenGL操作通常会成为一个主要的噩梦,为了正确.在您的情况下,您打算做的是委派资源加载.在过去,在有缓冲区对象之前,您已经通过创建帮助程序上下文并与主上下文共享其"列表"来完成此操作.

今天我们有更好的东西:缓冲对象.缓冲区对象允许您以异步方式将数据发送到OpenGL.它伴随着喜欢

glGenBuffers(...);
glBindBuffer(...);
glBufferData(..., size, usage);
void *p = glMapBuffer(...);
memcpy(p, data, size);
glUnmapBuffer(...);
glTexImage / glDrawPixels / etc.
Run Code Online (Sandbox Code Playgroud)

要理解的重要部分是,glMapBuffer分配的地址空间是跨线程共享的.因此,您可以告诉主线程中的OpenGL上下文映射缓冲区对象,并通过分配向您的工作线程发送信号.然后,工作线程填充数据,并在完成时向OpenGL上下文线程发送信号以取消映射.

编辑多线程

所以要做到这一点,你要在两侧实现一些信号处理程序(伪代码)

signal OpenGLThread::mapPixelBufferObjectForWrite(ImageLoader il):
    glGenBuffers(1, &self.bufferId)
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self.bufferId)
    glBufferData(GL_PIXEL_UNPACK_BUFFER, il.unpackedImageSize, NULL, GL_STATIC_DRAW)
    BufferObjectMapping map(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY))
    send_signal(target_thread = workerthread, target_signal_handler = workerthread::loadImageToBuffer(map, il), signal_on_finish = self.unmapPixelBufferObjectWriteFinishedGenTexture(map))

signal IOThread::loadImageToBuffer(BufferObjectMapping map, ImageLoader il):
    /* ... */

signal OpenGLThread::unmapPixelBufferObjectWriteFinishedGenTexture(BufferObjectMapping map, ImageLoader il):
    if(map.mapping_target == GL_PIXEL_UNPACK_BUFFER)
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self.bufferId)
    glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER)
    glGenTextures(1, &il.textureId)
    glBindTexture(il.target, il.textureId)
    for mipmaplevel in il.levels
        glTexImage2D(il.target, mipmaplevel, il.internalformat, il.width, il.height, il.border, il.format, il.type, mipmaplevel.offset)
Run Code Online (Sandbox Code Playgroud)