分离线程中的 Qt4/Opengl bindTexture

Posted

技术标签:

【中文标题】分离线程中的 Qt4/Opengl bindTexture【英文标题】:Qt4/Opengl bindTexture in separated thread 【发布时间】:2012-09-25 09:07:53 【问题描述】:

我正在尝试使用 QGLWidget 实现类似 CoverFlow 的效果,问题是纹理加载过程。

我有一个工作线程(QThread)用于从磁盘加载图像,主线程检查新加载的图像,如果找到,则使用 bindTexture 将它们加载到 QGLContext 中。在绑定纹理时,主线程被阻塞,所以我有一个 fps 下降。

这样做的正确方法是什么?

【问题讨论】:

【参考方案1】:

我发现Qt4中bindTexture的默认行为非常慢:

bindTexture(image,target,format,LinearFilteringBindOption | InvertedYBindOption | MipmapBindOption)

在绑定选项中仅使用 LinearFilteringBindOption 可以大大加快速度,这是我当前的调用:

bindTexture(image, GL_TEXTURE_2D,GL_RGBA,QGLContext::LinearFilteringBindOption);

更多信息here:3800x2850 bmp 文件的加载时间从 2 秒减少到 34 毫秒

当然,如果您需要 mipmapping,这不是解决方案。在这种情况下,我认为要走的路是 Pixel Buffer Objects。

【讨论】:

那么,生成两个大小的非幂次纹理的 mipmap 很慢,为什么您认为 Pixel Buffers 会有所作为? PBO 可以使用 DMA 将像素数据传输到显卡而不涉及 CPU 周期,不是吗?所以在单独的线程中使用 PBO 可以解决问题。 图形驱动程序可以做任何事情来优化上传 PBO,它会做优化上传纹理。对这种大小的图片进行 mipmap 的问题不在于(仅)传输,而是它将扩展到 4096x4096 并缩小 12 倍以形成 mipmap 的事实。切换到 PBO 不会加快这些操作,事实上我根本看不出 PBO 与 mipmapping 有什么关系。 更糟糕的是,如果你上传几张没有 mipmapping 的大图像,你的渲染速度会因为不断缩小巨大的纹理而变慢。另一方面,使用 mipmapping 你会浪费视频内存。在您的情况下,更好的方法是在使用它们制作纹理之前缩小图像,如果您不希望在渲染时需要缩小它们 - 不要创建 mipmap。 所以,如果我使用 mipmapping,我将在绑定发生时使用更多 VRAM 和更多带宽,如果我不这样做,视频卡将缩小纹理如果需要(对渲染性能不利)。解决方案应该是:缩小我的图像 + mipmapping【参考方案2】:

在主线程中绑定(单QGLWidget解决方案):

    决定最大纹理大小。例如,您可以根据最大可能的小部件大小来决定它。假设您知道小部件最多(大约)800x600 像素,并且可见的最大封面具有上下 30 像素的边距和 1:2 纵横比 -> 600-2*30 = 540 -> 封面的最大尺寸是270x540,例如存储在m_maxCoverSize

    在加载器线程中将传入的图像缩放到该大小。绑定更大的贴图是没有意义的,越大的贴图上传到显卡的时间就越长。使用QImage::scaled(m_maxCoverSize, Qt::KeepAspectRatio) 缩放加载的图像并将其传递给主线程。

    限制纹理的数量或花费在每帧绑定它们的更好时间。 IE。记住你开始绑定纹理的时间(例如QTime bindStartTime;)和绑定每个纹理之后:

    if (bindStartTime.elapsed() > BIND_TIME_LIMIT) 休息;

BIND_TIME_LIMIT 取决于您要保持的帧速率。但是当然,如​​果绑定每个纹理比BIND_TIME_LIMIT 花费的时间要长得多,那么您还没有解决任何问题。

尽管在速度较慢的机器/显卡上加载图像,您仍可能会遇到帧率下降的情况。其余的代码应该准备好接受它(例如,使用实际时间来驱动动画)。


替代解决方案在单独的线程中绑定(使用第二个不可见的QGLWidget,参见documentation):

2. 纹理在线程中上传。

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

【讨论】:

问题在于纹理大小,我使用的是 480x700 图像(或多或少),但每个封面都有自己的尺寸(480x741、480x744、640x437)以保持封面的纵横比.我已经缩小了工作线程上的封面,但根据显卡的不同,封面尺寸必须缩放太多...... 抱歉,我的评论不完整。 非常感谢您的回答,我已尝试限制每帧的绑定数量,这有点帮助。昨天我尝试了替代解决方案,但是在后台线程中绑定纹理需要调用 makeCurrent() (获取对共享 QGLContext 的访问权限),因此在绑定纹理时主线程无法访问上下文,我得到了相同的fps下降。也许我做错了什么...... 第二个解决方案需要两个 QGLWidget——一个(不可见)用于绑定纹理,另一个(可见)用于渲染。 我会用第二个 QGLWidget 和一个 QThread 再试一次,如果我发现问题我会分享一些代码。再次感谢。

以上是关于分离线程中的 Qt4/Opengl bindTexture的主要内容,如果未能解决你的问题,请参考以下文章

如何终止或停止 C++ 中的分离线程?

分离线程中的竞争条件

无法从分离的线程访问 C++ unordered_map 中的键值对

Linux线程 | 创建 终止 回收 分离

从分离线程停止主游戏循环的最佳方法

64位线程分离共享内存最小化bank冲突的策略