OpenGL更新缓冲区如何影响速度

Posted

技术标签:

【中文标题】OpenGL更新缓冲区如何影响速度【英文标题】:OpenGL How does updating buffers affect speed 【发布时间】:2019-12-15 22:32:22 【问题描述】:

我有一个缓冲区,我映射到要发送的顶点属性。下面是代码的基本功能:

glBindBuffer(GL_ARRAY_BUFFER, _bufferID);
_buffer = (VertexData*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);

for(Renderable* renderable : renderables)
    const glm::vec3& size = renderable->getSize();
    const glm::vec3& position = renderable->getPosition();
    const glm::vec4& color = renderable->getColor();
    const glm::mat4& modelMatrix = renderable->getModelMatrix();
    glm::vec3 vertexNormal = glm::vec3(0, 1, 0);


    _buffer->position = glm::vec3(modelMatrix * glm::vec4(position.x, position.y, position.z, 1));
    _buffer->color = color;
    _buffer->texCoords = glm::vec2(0, 0);
    _buffer->normal = vertexNormal;
    _buffer++;

然后我在一次绘制调用中绘制所有可渲染对象。我很好奇为什么触摸_buffer 变量会导致程序大幅减速。例如,如果我每帧都调用std::cout << _buffer->position.x;,我的 fps 会下降到通常的 1/4 左右。

我想知道它为什么会这样。我想知道的原因是因为我希望能够在移动对象时批量提供翻译对象。从本质上讲,我希望缓冲区始终位于同一个位置并且不会更改,但我可以更改它而不会对性能造成巨大损失。我认为这是不可能的,但我想知道为什么。下面是一个例子,如果这不会导致大问题,我会想做什么:

if(renderables.at(index)->hasChangedPosition())
    _buffer+=index;
    _buffer->position = renderables.at(index)->getPosition();

我知道我可以通过着色器制服发送变换,但你不能在一次绘制调用中为批处理对象执行此操作。

【问题讨论】:

@genpfault 的回答是正确的,但您也提到了控制台 IO (std::cout << ...)。控制台 IO 非常缓慢且阻塞。应该避免在一个紧密的循环中。 另外,您可以通过具有实例化的统一数组发送变换,这就是我假设您所说的“批处理”的意思,因为您还说您正在绘制所有内容一次。请参阅 gl_InstanceID。 (khronos.org/registry/OpenGL-Refpages/gl4/html/…) 这可能无法提供最佳性能,因为交错数组往往会提供更好的缓存行为。 @3Dave 非常感谢您提供更多信息! 【参考方案1】:

为什么完全触摸 _buffer 变量会导致程序严重变慢

...好吧,您确实请求了GL_WRITE_ONLY 缓冲区; GL 驱动程序完全有可能使用自定义的 fault 处理程序设置支持 glMapBuffer() 返回的指针的内存页面,该处理程序实际上会发送到 GPU 以获取请求的字节,这可能......不快。

而如果您只写入到所提供的地址,驱动程序/操作系统在glUnmapBuffer() 调用之前无需执行任何操作,此时它可以设置良好、快速的 DMA 传输一次性将新的缓冲区内容爆破到 GPU 内存中。

【讨论】:

以上是关于OpenGL更新缓冲区如何影响速度的主要内容,如果未能解决你的问题,请参考以下文章

如何在 OpenCL 内核中更新 OpenCL-OpenGL 共享缓冲区数据?

调整缓冲区大小时 OpenGL 失败

OpenGL更新顶点数组/缓冲区

OpenGL +无法使用buffersubdata更新缓冲区数据

OpenGL 纹理坐标没有影响

OpenGL 帧缓冲区缓慢且自发停止。广泛使用时甚至会导致系统崩溃