将多个对象异步上传到GPU

时间:2017-05-16 21:14:41

标签: c++ opengl asynchronous

我在好奇号火星车上的立体相机上有一堆3D模型在火星上行驶。这些模型是从磁盘,多个模型同时异步加载的。现在我需要将这组模型异步上传到GPU(在运行时),以防止渲染循环中的停顿,这正在发生。

现在上传模型的方式:

glGenVertexArrays(1, &_vaoID);
glGenBuffers(1, &_vbo);
glGenBuffers(1, &_ibo);

glBindVertexArray(_vaoID);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(Vertex), _vertices.data(), GL_STATIC_DRAW);

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex),
    reinterpret_cast<const GLvoid*>(offsetof(Vertex, location)));
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
    reinterpret_cast<const GLvoid*>(offsetof(Vertex, tex)));
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
    reinterpret_cast<const GLvoid*>(offsetof(Vertex, normal)));

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices.size() * sizeof(int), _indices.data(), GL_STATIC_DRAW);

glBindVertexArray(0);

呈现方式:

glBindVertexArray(_vaoID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
glDrawElements(_mode, static_cast<GLsizei>(_indices.size()), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);

现在我每次在renderloop上向GPU上传大约20个模型(当有从磁盘加载的模型并准备上传到GPU时)并且它的方式很多,应用程序停止运行大约50-400ms,具体取决于模型的顶点/法线/索引的数量。

VBO之间的乒乓(更新一个,从一个读取)可能在当前管道中不起作用,因为每个模型都有一个随机数量的顶点/法线/索引,需要连接到一个特定的纹理。

我正在寻找改善效果的任何解决方案。

修改1

我现在已经成功创建了指向我的VBO和IBO的指针,但是当我们将缓冲区返回到主线程时,我应该如何取消映射缓冲区。我的第一个想法是单独取消映射VBO和IBO,如下所示: `

for (int i = 0; i < _vertices.size(); i++) {
    _vertexBufferData[i] = _vertices.at(i);
}
glUnmapBuffer(GL_ARRAY_BUFFER);

for (int k = 0; k < _indices.size(); k++) {
    _indexBufferData[k] = _indices.at(k);
}
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);

但我得到一个错误,说缓冲区已经解除绑定或未映射。我只需要做第一个unmap吗?

1 个答案:

答案 0 :(得分:2)

您描述的问题称为Buffer Object Streaming

简而言之,假设您有一个必须加载特定模型的触发器。然后:

  1. 按照您的描述创建VAO和VBO,但尚未加载任何数据。您可以使用glBufferStorage
  2. 将缓冲区映射到内存中并启动一个线程以使用数据填充该缓冲区。该线程将耗尽所有耗时的磁盘i / o并填充映射的内存区域。
  3. 当工作线程完成后,通知主线程。
  4. 在主线程中,一旦收到通知,取消映射缓冲区并将VAO标记为已加载以供后续渲染。
  5. 显然在1到4之间,你的主线程会像往常一样继续渲染,而不会渲染待处理的VAO。