典型的OpenGL调用可能如下所示:
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_SOME_BUFFER, buffer);
...
我已经读过缓冲区和其他类似函数的绑定可能非常昂贵。是否值得保存当前绑定的缓冲区,并在绑定之前检查它?比如这个:
void StateManager::bindBuffer(GLenum bufType, GLuint bufID) {
if (this->m_currentBuffer[bufType] != bufID) {
glBindBuffer(bufType, bufID);
this->m_currentBuffer[bufType] = bufID;
}
}
这背后的想法是,如果bufID
已经绑定,则错过了对glBindBuffer
的昂贵调用。这是一个值得的方法吗?我假设OpenGL可能已经实现了这样的优化,但我现在已经看到这个模式在一些项目中使用,所以我有疑虑。我只是感兴趣,因为它实现起来非常简单,但如果它没有太多/任何区别,那么我将跳过它(避免过早优化)。
答案 0 :(得分:4)
这是高度平台和供应商的依赖。
您是否会问" OpenGL是否会实施......"。您已经明白了,OpenGL是一个API规范。有许多不同的实现,它们是否检查冗余状态更改完全是一个实现决策,它可以(并且将)从实现到实现不同。
你甚至不应该期望给定的实现对所有状态都处理相同的事情。
由于这个话题根据过去的经验有点接近我的心,我很想写一篇小文章,包括一些咆哮。但我认为它不属于这里,所以这里只是一个考虑因素列表,可能会影响给定的OpenGL实现是否在特定情况下测试冗余状态变化:
是的,这对每个人来说都是不幸的。对于想要在供应商/平台上获得理想性能的应用程序编写者而言,实际上并不是一个简单的解决方案。如果对代码添加检查,那么在OpenGL实现中具有相同检查的平台上,它们将毫无用处,并且会增加额外的开销。如果您的代码中没有检查,并且不能轻易避免首先进行这些冗余状态更改,则可以在OpenGL实现不检查的平台上保留性能。
答案 1 :(得分:3)
状态缓存是一个坏主意的原因很简单:你做错了。你总是有做错的危险。
哦,当然,你纠正了我指出的错误,即不同的缓冲区绑定有不同的状态。也许你正在使用一个哈希表,使得查找非常快,即使你编写缓存时出现了adds a new buffer binding point的新扩展名。
但就对象绑定特性而言,这仅仅是冰山一角。
例如,您是否意识到GL_ELEMENT_ARRAY_BUFFER
实际上不是上下文状态?它实际上是 VAO状态,每次绑定新的VAO时,缓冲区绑定都会发生变化。所以你的VAO缓存现在也必须改变阴影元素缓冲区绑定。
另外,您是否知道从当前绑定的任何上下文绑定点自动删除对象解除绑定?即使对于附加到绑定到上下文的另一个对象的对象也是如此;已删除的对象将自动分离。
除了某些对象类型之外,这是唯一的。即使这样,当删除对象时,当前的上下文也是如此。其他情况不会受到影响。
我的观点是:正确缓存状态真的很难。如果你弄错了,你将在你的应用程序中创建大量非常微妙的错误。然而,如果你只是让OpenGL做它的事情并构造你的代码,以便不会发生多重绑定,那么你没有问题。