1

我想将我的计算内容放在辅助线程中,同时将主循环保持在主线程中。

例如,我想要一个围绕 OY 旋转并改变纹理的立方体。旋转相机的空闲功能支持立方体旋转。但是我的纹理计算太复杂了,需要一些时间,所以我不能让它们闲置(我不需要每次我的立方体旋转时都改变纹理)。我可以使用foo函数来做一些计算工作,创建纹素缓冲区并在单独的线程中为立方体设置新纹理吗?main函数中的这段代码不会改变旋转立方体的纹理。

glutIdleFunc(idle);
boost::thread_group tgroup;
tgroup.create_thread(boost::bind(&foo));
glutMainLoop();
tgroup.join_all();

在不同的线程中设置新纹理是否存在并发问题?

4

3 回答 3

5

虽然不可能一次从多个线程调用给定上下文上的 OpenGL 操作,但可以使用映射的 PBO 将纹理缓冲区操作卸载到另一个线程。

在您的(主)OpenGL 线程中,为您的纹理数据分配一个大小合适的像素缓冲区对象 (PBO),并将其映射到进程内存中。这种情况下的使用模式将是,PBO 充当数据的短期中介,即上传到该缓冲区,然后 OpenGL 访问该缓冲区,然后删除缓冲区或更改数据;这是 STREAM 使用模式。

GLuint pboID;
glGenBuffers(1, &pboID);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboID);
glBufferData(GL_PIXEL_UNPACk_BUFFER, size, NULL, GL_STREAM_DRAW);
void *buffermap = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
glBindBuffer(GL_PIXEL_UNPACk_BUFFER, 0); /* the memory mapping is preserved */

/* queue that pbo for the worker thread to work upon */

您现在可以将值写入buffermap;指向的内存区域。每个线程都可以做到这一点。完成更新缓冲区后,取消映射并使用它将数据加载到纹理中。当有一个像素解包缓冲区绑定时,数据参数glTex[Sub]Image变成一个从 0 开始的偏移参数,指定缓冲区对象内的偏移位置,数据从该偏移位置获取。

if( workerthread_is_done ) {
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboID);
    glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);

    /* prepare a texture object as usual */
    glTex[Sub]Image2D(…, NULL);

    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}

您现在可以删除 PBO pboID或通过为其提供新数据来重新使用它。

于 2013-09-13T01:17:03.967 回答
3

一般来说,您不能从多个线程调用 OpenGL 函数(不使用多个渲染上下文或上下文切换)。

您必须有一个绑定的渲染上下文才能发出 OpenGL 命令,并且窗口系统会将您限制为每个线程一个上下文。为了解决这个问题,您将需要使用比 GLUT 更高级的框架。基本思想是您需要在上下文之间共享资源,一种用于将数据上传到其中,另一种用于实际渲染。

但我越想这个,你就没有真正的理由去做这些。坚持对所有 GL 命令使用单个线程,也许在另一个线程中从磁盘加载图像文件。并用信号通知您进行渲染的线程以使用glTexImage2D (...). 老实说,您的用例不够复杂,不足以保证多线程渲染。

于 2013-09-13T01:05:00.787 回答
0

您不能多线程 OpenGL 调用。但是,如果您愿意,您也许可以在不使用 OpenGL 代码的情况下预先计算纹理计算,然后将结果放在主线程中供 OpenGL 抓取。

于 2013-09-13T01:11:28.013 回答