Matthew Hoggan
Matthew Hoggan

Reputation: 7594

OpenGL VBO Safe Practices In A Dynamic Environment (Specific Use Case)

According to http://www.opengl.org/wiki/Vertex_Specification_Best_Practices, Under the dynamic VBO section, it is recommended that you set the VBO type to GL_DYNAMIC_DRAW if your scene changes frequently. In addition, if you have to update the entire buffer you don't use glBufferSubData alone. Instead you use glBufferData, and then you can set its data pointer to NULL followed by calls to glBufferSubData to load the data into the VBO.

I am doing exactly that in a Qt program I am writing which allows the user to resize the OpenGL drawable area (QGLWidget), which forces a set of lines (a wireframe of sorts) to be redrawn based on if more or less data is displayed in the given QGLWidget. I have an initial working example which works after the first initial resize. The program looking like:

Now when I go to resize it a second time the application looks like:

I have narrowed down the problem to re-loading the VBO associated with the lines. The code for loading the vertex attributes associated with the lines is:

  bool batch_renderer::load_data(render_data_type &data, GLsizei buffer_count)
  {
    if (_vbo_id) {
      glBindBuffer(GL_ARRAY_BUFFER, _vbo_id);
      glDeleteBuffers(_byte_count, &_vbo_id);
      _vbo_id = 0;
      _vertex_count = 0;
      _byte_count = 0;
      _byte_count1 = 0;
      _byte_count2 = 0;
      _byte_count3 = 0;
      _byte_count4 = 0;
    }

    _vertex_count = data._data_1.size();
    _byte_count = data.size_of();

    glGenBuffers(buffer_count, &_vbo_id);
    if (bind()) {
      glBufferData(GL_ARRAY_BUFFER, _byte_count, NULL, GL_DYNAMIC_DRAW);
      switch (data._type_count) {
      case (4): {
        _byte_count4 = data.data4_size_of();
        size_t offset = data.t4_offset();
        glBufferSubData(GL_ARRAY_BUFFER, offset, _byte_count4,
          data._data_4.data());
      }
      case (3): {
        _byte_count3 = data.data3_size_of();
        size_t offset = data.t3_offset();
        glBufferSubData(GL_ARRAY_BUFFER, offset, _byte_count3,
          data._data_3.data());
      }
      case (2): {
        _byte_count2 = data.data2_size_of();
        size_t offset = data.t2_offset();
        glBufferSubData(GL_ARRAY_BUFFER, offset, _byte_count2,
          data._data_2.data());
      }
      case (1): {
        _byte_count1 = data.data1_size_of();
        size_t offset = data.t1_offset();
        glBufferSubData(GL_ARRAY_BUFFER, offset, _byte_count1,
          data._data_1.data());
      }
        break;
      default: {
        return false;
      }
      }
    } else {
      return false;
    }

    return true;
  }

If I comment out the source which re-loads the VBO then the progam looks as follows after ANY resize event:

What am I doing wrong here with the VBO to cause the lines to go wrong, and the texture to be all messed up?

Upvotes: 1

Views: 568

Answers (1)

datenwolf
datenwolf

Reputation: 162164

The point of calling glBufferData when updating the whole buffer is to reuse an existing buffer object, telling the driver to discard the old data. Which means that you don't have to do this

if (_vbo_id) {
  glBindBuffer(GL_ARRAY_BUFFER, _vbo_id);
  glDeleteBuffers(_byte_count, &_vbo_id);
  _vbo_id = 0;
  _vertex_count = 0;
  _byte_count = 0;
  _byte_count1 = 0;
  _byte_count2 = 0;
  _byte_count3 = 0;
  _byte_count4 = 0;
}

So before generating the ID, just check if that's unset

if(!_vbo_id)
    glGenBuffers(buffer_count, &_vbo_id);

Note that I found the difference between total buffer object reallocation with glBufferData and updating the whole thing glBufferSubData to be neglectible. But you have to profile your own program to make an informed decision.

Upvotes: 2

Related Questions