我的显卡是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卡上存在性能问题,但我还没有将它们提取到测试用例中。
关于如何调试这个的任何想法?
答案 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也将在整个转移期间阻止。
在您的情况下,我怀疑在glSwapBuffer之前调用glTexSubImage2D
在驱动程序中添加了额外的同步,而在glSwapBuffer之前绘制四元组只是将命令附加到缓冲区中。然而,120ms可能是一个驱动程序错误:即使是英特尔GMA也不需要120毫秒来上传512x512纹理。