【问题标题】:glTexSubImage2D extremely slow on Intel video card英特尔显卡上的 glTexSubImage2D 速度极慢
【发布时间】:2011-10-21 04:13:39
【问题描述】:

我的显卡是 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();
}

每次迭代需要 120 毫秒。但是,在 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 调用修复的性能问题,但我还没有将这些问题提炼到测试用例中。

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

【问题讨论】:

  • 当然它在 GT420 上运行很快。这是一台GT420。但是让它在 GMA 上运行得更快,甚至 nVidia 也会(应该)更快。
  • @genpfault:Windows 7 SP 1,驱动程序版本 8.15.10.2281
  • "关于如何调试这个有什么想法吗?"这里没有错误。性能在任何地方都无法保证。没有什么可调试的。这只是英特尔显卡和 OpenGL 驱动程序的一个怪癖;而已。你找到了一种让它更快的方法,所以你应该使用它。
  • @Nicol:嘿,我们不要纠结于 bug 这个词 :) 如果没有明显的原因发生减速,那么我想知道是什么原因造成的,这样我就可以避免它。
  • @StefanMonov:你假设有一个合理的原因,然后你可以在未来避免。不一定有,NVIDIA(可能还有 ATI)卡没有这个问题就证明了这一点。而且,由于您正在处理英特尔臭名昭著的漏洞百出的 OpenGL 驱动程序,因此更不可能有合理的原因。在这些情况下,您能做的最好的事情就是识别出您做了什么导致奇怪的性能故障,尝试重新排列您的代码以使其不会发生,并记下您所做的事情以供将来参考。

标签: performance opengl


【解决方案1】:

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

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

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

【讨论】:

  • 顺便说一句,他一直使用glTexSubImage2D
  • @ChristianRau:谢谢……啊,我不应该在工作中回答 StackOverflow 问题,太多分心了 ;)
  • 谢谢,我没有意识到 glTexSubImage2D 必须等待绘图完成。有用的信息。但是,这仍然不会导致从 2ms 减慢到 120ms。
【解决方案2】:

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

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

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

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

【讨论】:

  • 谢谢,在帧的开头上传纹理听起来很明智。至于“我的问题是什么”,好吧,我想知道视频卡内部发生了什么导致从 2 毫秒到 120 毫秒的减速。您提到的“上传失败”对我来说是新事物,听起来很有趣,但我认为它不能解释这种速度下降。
【解决方案3】:

当您进行绘图调用 (glDrawElements, other) 时,驱动程序只需将此调用添加到缓冲区中,并让 GPU 在可能的情况下使用这些命令。

如果这个缓冲区必须在glSwapBuffers 处被完全消耗掉,这意味着 GPU 在那之后将处于空闲状态,等待您发送新命令。

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

另一个原因是glTexSubImage2D 是同步的。在整个传输过程中,il 也会阻塞。

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

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

【讨论】:

  • 是的,我可能会进行“2 纹理”优化,因为这听起来不错,但我更感兴趣的是找出导致我的问题中提到的大幅减速的原因。不可能仅仅因为同步。谢谢。
  • 等等,我认为你的技术实际上不会加快速度。 glTexSubImage2D 在数据传输之前不会返回,对吗?因此,当它返回时,数据已经被传输。然后绘图不必等待任何东西。我错过了什么?
  • glTexSubImage2D 会在传输过程中阻塞,是的。但如果没有双缓冲,它也会在 GPU 使用它来渲染前一帧的内容时阻塞。
  • “当 GPU 使用它来渲染前一帧的内容时,它也会阻塞。” - 我不明白。上一帧结束了,它已经被 swapBuffers 刷新了。你能帮我解释一下吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-06
  • 2012-06-18
  • 2019-09-30
  • 1970-01-01
  • 1970-01-01
  • 2015-07-22
相关资源
最近更新 更多