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

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

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

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

【问题讨论】:

    标签: qt opengl qt4 qthread


    【解决方案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解决方案):

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

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

    3. 限制纹理的数量或花费在每帧绑定它们的更好时间。 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 再试一次,如果我发现问题我会分享一些代码。再次感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多