从 GPU 大量传输到 RAM 后渲染缓慢?
Slow rendering after large transfer from GPU to RAM?
我试图渲染一个点云(>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();
然后,我有时需要一个函数来恢复数据以保存更改,这就是我所做的:
// 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);
之后,每次我需要渲染那个点云时程序都会挂起。
我删除了所有其他代码来测试这段代码,这段数据传输代码正是导致延迟的原因
每次我尝试渲染导致它挂起的数据时,感觉GPU内部都有大量的数据副本。但是我把数据从GPU传回RAM后,原来在GPU里面的数据没有动过,我以为渲染应该和以前一样流畅,结果不是。
OpenGL 状态是否发生了变化导致了这种延迟?
您对 OpenGL 做出了承诺。然后你违背了诺言。所以 OpenGL 惩罚了你。
您告诉 OpenGL you were going to use this buffer object:
GL_STATIC_DRAW
这意味着您告诉 OpenGL 您将写入缓冲区 (DRAW
) 但不会从中读取。你说你很少写信给它 (STATIC
)。因此,OpenGL 实现尽职尽责地将缓冲区对象的存储放在最适合 GPU 访问但不适合 CPU 访问的位置。
然后你从中读取。你做了你向 OpenGL 承诺的事情,但你不会去做。
现在,人们(很多 的人)总是违背这些对 OpenGL 实现的承诺。这么多的实现基本上不再相信它们了。也就是说,他们不会听你说你要用缓冲区做什么,而是注意你实际做什么。
实施看到您从缓冲区读取。因此,它假定您打算定期执行此操作。因此,它现在 将缓冲区的存储空间 转移到更适合 CPU 读取的位置......但 不太理想 GPU 读取。
现代 OpenGL 有一个 buffer object creation API where lying is no longer permitted。您指定可以在缓冲区上使用哪些操作,OpenGL 实现将通过使所有其他访问失败来让您坚持这一点。
如果您使用缓冲区存储 API,那么尝试使用 glGetBufferSubData
从缓冲区读取很可能 而不是 导致 API 只是随机播放内存。防止存储改组是这个 API 的一半。如果您仍然从您的实施中获得这种减慢效果,您可能无法在 OpenGL 中阻止它。
最有效的替代方法(除了使用 Vulkan,您可以完全控制内存)就是根本不尝试从 OpenGL 读取它。也就是说,不要将 GPU 存储视为需要数据时可以读取的东西。如果您需要使用 CPU 上的数据, 将其保留在那里 。也就是说,在 CPU 内存中也保留它的副本。
我试图渲染一个点云(>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();
然后,我有时需要一个函数来恢复数据以保存更改,这就是我所做的:
// 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);
之后,每次我需要渲染那个点云时程序都会挂起。
我删除了所有其他代码来测试这段代码,这段数据传输代码正是导致延迟的原因
每次我尝试渲染导致它挂起的数据时,感觉GPU内部都有大量的数据副本。但是我把数据从GPU传回RAM后,原来在GPU里面的数据没有动过,我以为渲染应该和以前一样流畅,结果不是。
OpenGL 状态是否发生了变化导致了这种延迟?
您对 OpenGL 做出了承诺。然后你违背了诺言。所以 OpenGL 惩罚了你。
您告诉 OpenGL you were going to use this buffer object:
GL_STATIC_DRAW
这意味着您告诉 OpenGL 您将写入缓冲区 (DRAW
) 但不会从中读取。你说你很少写信给它 (STATIC
)。因此,OpenGL 实现尽职尽责地将缓冲区对象的存储放在最适合 GPU 访问但不适合 CPU 访问的位置。
然后你从中读取。你做了你向 OpenGL 承诺的事情,但你不会去做。
现在,人们(很多 的人)总是违背这些对 OpenGL 实现的承诺。这么多的实现基本上不再相信它们了。也就是说,他们不会听你说你要用缓冲区做什么,而是注意你实际做什么。
实施看到您从缓冲区读取。因此,它假定您打算定期执行此操作。因此,它现在 将缓冲区的存储空间 转移到更适合 CPU 读取的位置......但 不太理想 GPU 读取。
现代 OpenGL 有一个 buffer object creation API where lying is no longer permitted。您指定可以在缓冲区上使用哪些操作,OpenGL 实现将通过使所有其他访问失败来让您坚持这一点。
如果您使用缓冲区存储 API,那么尝试使用 glGetBufferSubData
从缓冲区读取很可能 而不是 导致 API 只是随机播放内存。防止存储改组是这个 API 的一半。如果您仍然从您的实施中获得这种减慢效果,您可能无法在 OpenGL 中阻止它。
最有效的替代方法(除了使用 Vulkan,您可以完全控制内存)就是根本不尝试从 OpenGL 读取它。也就是说,不要将 GPU 存储视为需要数据时可以读取的东西。如果您需要使用 CPU 上的数据, 将其保留在那里 。也就是说,在 CPU 内存中也保留它的副本。