glTexSubImage2D在英特尔显卡上速度极慢

时间:2011-10-18 13:40:03

标签: performance opengl

我的显卡是Mobile Intel 4系列。我正在更新每帧更改数据的纹理,这是我的主循环:

for(;;) {
    Timer timer;

    glBindTexture(GL_TEXTURE2D, tex);
    glBegin(GL_QUADS); ... /* draw textured quad */ ... glEnd();
    glTexSubImage2D(GL_TEXTURE2D, 0, 0, 0, 512, 512,
        GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
    swapBuffers();

    cout << timer.Elapsed();
}

每次迭代需要120ms。但是,在glTexSubImage2D之前插入glFlush会使迭代时间延长到2ms。

问题不在于像素格式。我已尝试像素格式BGRA,RGBA和ABGR_EXT以及像素类型UNSIGNED_BYTE,BYTE,UNSIGNED_INT_8_8_8_8和UNSIGNED_INT_8_8_8_8_EXT。纹理的内部像素格式为RGBA。

通话顺序很重要。例如,在四边形绘图之前移动纹理上传可以解决速度缓慢问题。

我也在GeForce GT 420M显卡上尝试过这款显卡,它可以在那里快速运行。我的真实应用确实在glFlush调用修复的非Intel卡上存在性能问题,但我还没有将它们提取到测试用例中。

关于如何调试这个的任何想法?

3 个答案:

答案 0 :(得分:5)

一个问题是glTexImage2D执行纹理对象的完全重新初始化。如果只有数据发生变化,但格式保持不变,请使用glTexSubImage2D加快速度(仅提醒)。

另一个问题是,尽管其名称为立即模式,即glBegin(...)... glEnd(),但绘图调用不是同步的,即调用在GPU完成绘制之前很久就会返回。添加glFinish()将同步。但是也会调用任何修改排队操作仍然需要的数据的东西。因此,在您的情况下,glTexImage2D(和glTexSubImage2D)必须等待绘图完成。

通常最好在绘图函数的开头或通过缓冲区对象在单独的线程中的SwapBuffers块中进行所有易失性资源上传。出于这个原因引入了缓冲区对象,以允许异步但紧凑的操作。

答案 1 :(得分:3)

我假设您实际上是在为一个或多个四边形使用该纹理?

上传纹理是最昂贵的操作之一。由于纹理数据每帧都会更改,因此上传是不可避免的,但是当着色器未使用纹理时,您应该尝试执行此操作。请记住,glBegin(GL_QUADS); ... glEnd();实际上并不绘制四边形,它要求GPU渲染四边形。在渲染完成之前,纹理将被锁定。根据实现情况,这可能会导致纹理上载等待(ala glFlush),但也可能导致上载失败,在这种情况下,您浪费了兆字节的PCIe带宽,驱动程序必须重试。

听起来你已经有了解决方案:在帧的开头上传所有新纹理。那么你的问题是什么?

注意:无论如何,英特尔集成显卡都非常慢。

答案 2 :(得分:1)

当你进行Draw Call(glDrawElements,other)时,驱动程序只需在缓冲区中添加此调用,并让GPU尽可能地使用这些命令。

如果必须在glSwapBuffers完全消耗此缓冲区,这意味着GPU将在此之后空闲,等待您发送新命令。

驱动程序通过让GPU落后一帧来解决这个问题。这是glTexSubImage2D阻塞的第一个原因:驱动程序等待GPU不再使用它(在前一帧中)开始传输,因此您永远不会获得半更新数据。

另一个原因是glTexSubImage2D是同步的。 Il也将在整个转移期间阻止。

  • 您可以通过保留2个纹理来解决第一个问题:一个用于当前帧,一个用于前一帧。在前者上传纹理,但用后者绘制。
  • 您可以使用允许异步传输的GL_TEXTURE_BUFFER缓冲区对象来解决第二个问题。

在您的情况下,我怀疑在glSwapBuffer之前调用glTexSubImage2D在驱动程序中添加了额外的同步,而在glSwapBuffer之前绘制四元组只是将命令附加到缓冲区中。然而,120ms可能是一个驱动程序错误:即使是英特尔GMA也不需要120毫秒来上传512x512纹理。