Can we change the VBO data?



  • Hello. I'm talking to OpenGL specialists. Risking a three-dimensional scenario with a group of VAO facilities

    for(auto VAO: vao_list)
    {
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, nullptr);
        glBindVertexArray(0);
    }
    

    It's all working on the paperwork. But one of the objects, the size of the 40K peak, had to change a little: to remove 3 out of 40,000 peaks. It is possible to re-enactment the modified object, but to do so, the image must first be built in the operational memory and then re-evaluate the graphic processor. And if most of the changeable objects in the stage, all of them (copies) will have to be kept in an operation, ruled and reset into a graphic buffer. There's a double job, so it's probably easier to use the glDrawArrays, which paints the masses directly from the operational memory:

    glEnableVertexAttribArray(idx_vertex_coord3d);
    glEnableVertexAttribArray(idx_texture_coord);
    glDrawArrays(GL_TRIANGLES, 0, count_of_vertices);
    glDisableVertexAttribArray(idx_texture_coord);
    glDisableVertexAttribArray(idx_vertex_coord3d);
    

    but in this case, the memory of the graphic processor (so expensive and fast) is simple. The question is, is it possible to partially alter the object located in VAO (in graphic memory) by removing/extracting part of the peaks in it without rewriting VAO in full?



  • VAO and VBO

    I'll tell you about VAO and VBO at first. VAO https://www.opengl.org/wiki/Vertex_Specification#Vertex_Array_Object the facility, but only links to one or more VBOs and other conditions necessary for the construction of the facility. So we'll work with VBO. If several top attributes are stored in several VBOs, all of them will have to be managed separately. The VBO change will be readily available to users of the associated VAO.

    Removal

    Let's start with an extreme simple case: when the peaks are removed from the end, there's no need to change anything. Just drawing a challenge.

    glDrawElements(GL_TRIANGLE_STRIP, num_elements - 3, GL_UNSIGNED_BYTE, nullptr);
    

    instead

    glDrawElements(GL_TRIANGLE_STRIP, num_elements, GL_UNSIGNED_BYTE, nullptr);
    

    If the removed peaks are stored at the beginning or in the middle, the task is disposal is similar to the task of removing elements from the middle of the conventional body in memory. For example, if there is a mass:

    int a[] = {1,2,3,4,5,6};
    

    We cannot remove elements 2 and 3 from it without leaving zero or any special meanings indicating that the data are removed. In order not to leave these values, we can either re-emergize the memory to a new set of necessary lengths and collate the values there or collate 5 and 6 to place 2 and 3 and remember that now the length of the mass is 4 instead of 6. The memory left behind under two elements will be wasted, but with 40000 elements, it can be ignored.

    The same can be applied to the mass in the videotape:

    • We can call glBufferData for a new buffer.
    • It is possible to replace a piece of data from the beginning of the removed piece to the end of the buffer with new values by glBufferSubData without forgetting the decrease of num_elements.
    • Remove the copying inside the buffer https://www.opengl.org/wiki/Buffer_Object#Copying because memory regions should not intersect when copying inside the buffer.

    The methods listed can make sense when changing/removaling a large number of data, but, as you have noticed, they're completely unsuitable three points out of 40000. I mean, you can't completely remove the points, and you're gonna have to find a bypass.

    Possible solution

    Function https://www.opengl.org/wiki/GLAPI/glBufferSubData Allows the decomposition and length of the data to be replaced. It can therefore rewrite both the entire buffer and any part thereof, even one element. If we need to remove 3 of the 40000 points from the model, you don't have to remove them from the buffer! Let's just rewrite their values to the right next door. For example, have a lamb of 8 peaks on the plane:

       2       3     5    7
       /-------\    /\    /----8
      /         \  /  \  /
     /           \/    \/
    /1           4      6 
    

    If we do the top of 5 equal to 6, then we will have the same effect as when the top 5 is removed: there will be a cut, visually connecting the top 4 and 6, and the length of the 5-6 cut will be zero:

       2       3          7
       /-------\          /----8
      /         \        /
     /           \------/
    /1           4      5,6 
    

    In the choice between the overexpenditure of memory by three peaks or the time spent on re-establishing the buffer by 40000 peaks, it is logical to give preference to the first option.

    Usage pattern

    From your description, it seems that you'll be seldom removed, that is, not on every frame. The buffer will be used only to remove the graphs but not to transform the feedback and other such tricks. If so, in creating the buffer, don't forget the last glBufferData argument. GL_DYNAMIC_DRAW♪ However, without performance tests of a specific annex, there is no clear recommendation. With very rare updating of the buffer GL_STATIC_DRAW It could be faster. On the contrary, with a very often best choice, GL_STREAM_DRAW

    Comment from the author

    A little example. Thank you, everything's up. In addition to your response, I would like to add a demo code for one of the options you listed:

    GLfloat* vertices = new GLfloat[9]; // элемент из 3 вершин
    ...
    

    GLuint pos_Buf;
    glGenBuffers(1, &pos_Buf);
    // передаем данные в GPU
    glBindBuffer(GL_ARRAY_BUFFER, pos_Buf);
    glBufferData(GL_ARRAY_BUFFER, 9*sizeof(float), vertices, GL_STATIC_DRAW);
    // освободить память после переноса данных в GPU
    delete [] vertices;
    ...

    // Если надо изменить, например, седьмой элемент массива:
    // Пример изменения данных, размещенных в графической памяти
    // Вариант - glUnmapBuffer
    // ----------------------------------------------------
    glBindBuffer(GL_ARRAY_BUFFER, pos_Buf);
    data = (GLfloat *) glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
    data[7] = 0.4f;
    glUnmapBuffer(GL_ARRAY_BUFFER);
    ...

    // Вариант 2 - glBufferSubData
    // ----------------------------------------------------
    glBindBuffer(GL_ARRAY_BUFFER, pos_Buf);
    GLintptr offset = 7*sizeof(float);
    float pos_SubD[] = {0.0f};
    glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(float), pos_SubD);
    ...

    // В нужный момент этот VBO можно удалить
    glDeleteBuffers(1, &vboId);


Log in to reply
 


Suggested Topics

  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2