从 GPU 到 RAM 的大量传输后渲染缓慢?

Zho*_*Hua 2 c++ opengl performance glm-math

我试图渲染一个点云(> 1 亿点),我将所有数据传输到 GPU 并仅使用缓冲区名称来访问它。一切正常,直到我实现了一项要求我将所有数据传输回 RAM 的功能。

这就是我将数据传递给 GPU 的方式:

glBindVertexArray(vao);

// some data
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * GetSize()  * 3, &vertices[0], GL_STATIC_DRAW); // pos

// some attributes
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0); // pos

...
// some other code
...

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

glDisableVertexAttribArray(0);

// Clear the contents in the RAM
vertices.clear();
vertices.shrink_to_fit();
Run Code Online (Sandbox Code Playgroud)

然后,我需要一个函数来恢复数据以保存更改,这就是我所做的:

// restore data back to RAM
// this code was called for >4000 times

glBindBuffer(GL_ARRAY_BUFFER, c.vbo);
c.vertices.clear();
c.vertices.resize(c.sizeg);
glGetBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec3) * c.sizeg, &c.vertices[0].position.x);
Run Code Online (Sandbox Code Playgroud)

之后,每次我需要渲染点云时,程序都会挂起。

我删除了所有其他代码以仅测试此代码,而这段数据传输代码正是导致延迟的原因

感觉每次我尝试渲染导致它挂起的数据时,GPU 内部都有大量的数据副本。但是,当我将数据从GPU回传到RAM后,原本在GPU内部的数据没有受到影响,我认为渲染应该和以前一样流畅,但事实并非如此。

导致这种滞后的 OpenGL 状态是否发生了变化?

Nic*_*las 7

您向 OpenGL 做出了承诺。然后你违背了你的诺言。所以OpenGL因此惩罚了你。

你告诉 OpenGL你将如何使用这个缓冲区对象

GL_STATIC_DRAW
Run Code Online (Sandbox Code Playgroud)

这意味着您告诉 OpenGL 您将写入缓冲区 ( DRAW) 但不从中读取。你说你很少写信给它(STATIC)。因此,OpenGL 实现尽职尽责地将缓冲区对象的存储放在最适合 GPU 访问的位置,但不适用于 CPU 访问。

然后你从中阅读。你做了你答应OpenGL你不会做的事情。

现在,人们(很多人)一直在打破对 OpenGL 实现的这些承诺。如此多的实现基本上不再相信它们。也就是说,他们不会听你你要对缓冲区做什么,而是关注你实际做了什么

实现看到您从缓冲区读取。所以它假设你打算有规律地这样做。因此,它现在缓冲区的存储传输到更适合 CPU 读取的位置……但不太适合GPU 读取。

现代 OpenGL 有一个缓冲区对象创建 API,其中不再允许说谎。您指定可以在缓冲区上使用哪些操作,OpenGL 实现将通过使所有其他访问失败来阻止您这样做。

如果使用缓冲存储API,那么它很可能是试图从缓冲区读取glGetBufferSubData不会导致API只是洗牌周围的内存。防止存储改组是这个 API 的一半。如果您仍然从您的实现中获得这种减慢效果,那么您可能无法在 OpenGL 中阻止它。

最有效的替代方法(除了使用 Vulkan 可以完全控制内存)是根本不尝试从 OpenGL 读取它。也就是说,不要将 GPU 存储视为可以在需要某些数据时回读的内容。如果您需要使用 CPU 上的数据,请将其保留在那里。也就是说,在 CPU 内存中也保留一份它的副本。