我应该缓存OpenGL状态,比如当前绑定的缓冲区,还是OpenGL会这样做

Should I cache OpenGL state such as currently bound buffers, or does OpenGL do that anyway?

本文关键字:OpenGL 缓冲区 还是 这样做 绑定 状态 缓存 我应该      更新时间:2023-10-16

一个典型的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很可能已经实现了这样的优化,但我现在已经在几个项目中看到了这种模式,所以我有我的怀疑。我只是感兴趣,因为这将是一个非常简单的事情实现,但如果它没有太多/任何区别,那么我将跳过它(避免过早优化)。

这是高度依赖于平台和供应商的。

你问的是"OpenGL会实现…"。正如你已经理解的那样,OpenGL是一个API规范。有许多不同的实现,它们是否检查冗余状态更改完全是一个实现决策,这可以(并且将)因实现而异。

你甚至不应该期望给定的实现对所有状态块都处理相同。

根据过去的经验,这个话题有点接近我的心,我想写一篇小文章,包括一些咆哮。但我决定它不属于这里,所以这里只是一个考虑事项列表,如果给定的OpenGL实现在特定情况下测试冗余状态更改,可能会影响:

  • 实际改变状态的成本有多高?如果它非常便宜,检查多余的更改可能根本不值得。
  • 检查冗余更改的成本有多高?通常不多,但我们正在研究的软件,每一点都很重要。
  • 重要的应用程序/基准是否会频繁地冗余更改此状态?
  • 关于应用程序的责任和OpenGL实现的责任的哲学是什么?

是的,这对每个人都是不幸的。对于想要跨供应商/平台获得理想性能的应用程序编写人员来说,确实没有简单的解决方案。如果你在代码中添加检查,它们将是无用的,并且在OpenGL实现中具有相同检查的平台上增加额外的开销。如果你没有在你的代码中进行检查,并且不能轻易地避免这些冗余的状态更改,那么你可能会在OpenGL实现不进行检查的平台上留下性能表。

状态缓存是一个坏主意的原因很简单:你做错了。你总是有做错的危险。

哦,当然,你纠正了我指出的错误,不同的缓冲区绑定有不同的状态。也许你正在使用一个哈希表,它使查找非常快,即使一个新的扩展出现了,添加了一个新的缓冲区绑定点,当你写缓存时不存在。

但这只是对象绑定特性的冰山一角。

例如,您是否意识到GL_ELEMENT_ARRAY_BUFFER实际上不是上下文状态?它实际上是VAO状态,每次绑定新的VAO时,缓冲区绑定都会发生变化。因此,您的VAO缓存现在也必须更改阴影元素缓冲区绑定。

另外,您是否意识到自动删除对象会将与当前绑定的任何上下文绑定点解除绑定?即使是与另一个对象相关联的对象与上下文相关联的对象也是如此;删除的对象将自动卸载。

但这只对某些对象类型成立。即使这样,它也只适用于对象被删除时当前的上下文。其他上下文不受影响。

我的观点是:适当的状态缓存是非常困难的。如果您弄错了,您将在应用程序中创建大量非常细微的错误。而如果你只是让OpenGL做它自己的事情并构造你的代码使多重绑定不会发生,那么你就不会有问题。