QT QOpenGLWidget:如何在不使用数据块复制的情况下修改VBO中的各个顶点值?

时间:2018-10-15 13:00:38

标签: c++ qt opengl vbo

我不知道是否有可能

  • 我有一个QVector3D顶点数组,可以复制到VBO中
  • 有时我只想修改(x1,y1)和(x2,y2)值之间的一系列顶点的z值-有关顶点严格​​遵循彼此
  • 我的“好”主意是仅通过直接访问VBO来修改z值。

我进行了很多搜索,但是我看到的所有解决方案都使用memcpy,如下所示:

m_vboPos.bind();
GLfloat* PosBuffer = (GLfloat*) (m_vboPos.map(QOpenGLBuffer::WriteOnly));
if (PosBuffer != (GLfloat*) NULL) {
    memcpy(PosBuffer, m_Vertices.constData(), m_Vertices.size() * sizeof(QVector3D));
m_vboPos.unmap();
m_vboPos.release();

但这是要复制个数据块

我不认为使用memcpy在每个相关顶点中仅更改1个浮点值会非常有效(我在VBO中拥有数百万个顶点)。

我想进行优化,因为复制数百万个顶点需要(太)长时间:是否有一种方法可以实现我的目标(没有memcpy?),因为这里和那里只有一个浮点数? (已经尝试过但无法做到,我必须丢失一些东西)

2 个答案:

答案 0 :(得分:1)

此电话

GLfloat* PosBuffer = (GLfloat*) (m_vboPos.map(QOpenGLBuffer::WriteOnly));

将在内部调用glMapBuffer,这意味着它只是将缓冲区内容映射到进程的地址空间中(另请参见OpenGL Wiki on Buffer Object Mapping

由于您将其映射为只写,因此您可以根据需要简单地覆盖缓冲区的每一位。无需使用memcpy,您可以使用任何方式写入内存,例如你可以直接做

PosBuffer[3*vertex_id + 2] = 42.0f; // assuming 3 floats per vertex
  

我不认为使用memcpy在每个相关顶点中仅更改1个浮点值会非常有效(我在VBO中拥有数百万个顶点)。

是的,对每个4个字节分别进行一百万次memcpy()调用不是一个好主意。一个现代的编译器实际上可以内联它,因此,它可能仅等效于单个分配。但是您也可以直接进行分配,因为memcpy在这里没有给您带来任何好处。

但是,尚不清楚所有这些对性能的影响。 glMapBuffer可能会返回指向的指针

  • VBO在系统内存中的一些本地副本,以后将其内容复制到GPU。由于它不知道您更改了哪些值,哪些不更改,因此可能必须重新传输整个缓冲区。
  • GART区域内的某些系统内存已映射到GPU,因此从缓冲区读取数据时,GPU将直接访问该内存。
  • VRAM中的某些I / O映射区域。在这种情况下,内存区域的缓存行为可能会大不相同,并且在每12个字节的块中更改4个字节可能不是最理想的方法。只是将整个子块重新复制为一个大垃圾可能会产生更好的性能。

映射本身也不是免费的,它涉及更改页表,GL驱动程序可能必须同步其线程,或者在最坏的情况下,与GPU同步(以防止覆盖GPU的内容)仍在用于仍在进行中的上一个抽奖电话中。

  

有时我只想修改(x1,y1)和(x2,y2)值之间的一系列顶点的z值-有关顶点严格​​遵循彼此

因此,您有一个要修改的缓冲区的连续子区域。我建议看一下两种选择:

  1. 使用glMapBufferRange(如果在OpenGL版本中可用)来仅映射您关心的区域。

  2. 完全忘记缓冲区映射,请尝试glBufferSubData()。不是单独出现在每个顶点的每个z组件上,而是整个修改顶点范围内的一大垃圾。这将意味着您在某个位置的内存中有缓冲区内容的本地副本,只需进行更新,然后将结果发送到GL。

哪种选择更好,将取决于许多不同的因素,如果您不关心实际方案中的基准,我也不会排除其中一个因素,而是取决于您关心的实际实现。还可以查看Buffer Object Streaming in OpenGL的一般策略。对于您的用例,persistently mapped buffer可能不是一个好的选择。

答案 1 :(得分:1)

glMap方法效果很好,而且非常快!

非常感谢genpfault,速度增益是如此之大,以至于3D渲染不再是断断续续的。

这是我的新代码,经过简化以提供易于理解的答案:

vertexbuffer.bind();
GLfloat* posBuffer = (GLfloat*) (vertexbuffer.map(QOpenGLBuffer::WriteOnly));

if (posBuffer != (GLfloat*) NULL) {
    int index = NumberOfVertices(area.y + 1, image.cols); // index of first vertex on line area.y
    for (row = ...) for (col = ...) {
        if (mask.at<uchar>(row, col) != 0)
            posBuffer[3 * index + 2] = depthmap.at<uchar>(row, col) * depth;
        index++;
    }
}
vertexbuffer.unmap();
vertexbuffer.release();