2
\$\begingroup\$

I read the khronos docs on Vertex Array Objects, and it says that VAOs do not copy any data but refer to VBOs and EBOs/IBOs. So i created an abstraction class vbo which creates a vertex buffer from a list of vertices. The vbo class destructor calls gldeletebuffer on the vbo. Now i am creating a vao class, which takes in a vbo in setvbo functiin. But if i am just copying the renderer_id of the vbo, then when the vbo is deleted, the destructor calls gldeletebuffer on the vbo, which is being used by the vao. The vbo basically outlives the vao. What is the correct object oriented way to abstract a VAO class?

\$\endgroup\$

1 Answer 1

1
+50
\$\begingroup\$

I do it like this:

class VAO {

 public:

  VAO() = default;
  VAO(const VAO& other) = delete;
  VAO(VAO&& other) { *this = std::move(other); }
  ~VAO() { if (m_id) glDeleteVertexArrays(1, &m_id); }
  VAO& operator=(const VAO& other) = delete;
  VAO& operator=(VAO&& other) {
    if (this != &other) {
      if (m_id) glDeleteVertexArrays(1, &m_id);
      m_id = std::exchange(other.m_id, 0);
    }
    return *this;
  }

  /**
   * @brief Binds the VAO.
   */
  void bind() const { glBindVertexArray(m_id); }

  /**
   * @brief Specifies how OpenGL should interpret the vertex buffer data whenever a draw call is made.
   * @param vbo The vertex buffer object to be binded.
   * @param layout Specifies the index of the generic vertex attribute to be modified. Must match the layout in the shader.
   * @param components Specifies the number of components per generic vertex attribute. Must be 1, 2, 3, 4.
   * @param type Type of the data.
   * @param stride Specifies the byte offset between consecutive generic vertex attributes.
   * @param offset Specifies a offset of the first component of the first generic vertex attribute in the array in the data store.
   * @param normalize Specifies whether fixed-point data values should be normalized.
   */
  void linkAttrib(const VBO& vbo, GLuint layout, GLuint components, GLenum type, GLsizei stride, void* offset, GLboolean normalize = GL_FALSE) const;

  /**
   * @brief Specifies how OpenGL should interpret the vertex buffer data whenever a draw call is made. IT DOESN'T BIND ANYTHING!
   * @param layout Specifies the index of the generic vertex attribute to be modified. Must match the layout in the shader.
   * @param components Specifies the number of components per generic vertex attribute. Must be 1, 2, 3, 4.
   * @param type Type of the data.
   * @param stride Specifies the byte offset between consecutive generic vertex attributes.
   * @param offset Specifies a offset of the first component of the first generic vertex attribute in the array in the data store.
   * @param normalize Specifies whether fixed-point data values should be normalized.
   */
  void linkAttribFast(GLuint layout, GLuint components, GLenum type, GLsizei stride, void* offset, GLboolean normalize = GL_FALSE) const;

  /**
   * @brief Generates the vertex array buffer.
   */
  void genVertexArray();

  /**
   * @brief Unbinds the VAO.
   */
  void unbind() const { glBindVertexArray(0); }

 private:

  GLuint m_id {};
};

Also the VBO class:

class VBO {

 public:

  VBO() = default;
  VBO(const VBO& other) = delete;
  VBO(VBO&& other) { *this = std::move(other); }
  ~VBO() { if (m_id) glDeleteBuffers(1, &m_id); }
  VBO& operator=(const VBO& other) = delete;
  VBO& operator=(VBO&& other) {
    if (this != &other) {
      if (m_id) glDeleteBuffers(1, &m_id);
      m_id = std::exchange(other.m_id, 0);
    }
    return *this;
  }

  /**
   * @brief Binds the VBO.
   */
  void bind() const { glBindBuffer(GL_ARRAY_BUFFER, m_id); }

  /**
   * @brief Generates the buffer for the VBO.
   */
  void genBuffer();

  /**
   * @brief Sets up the data for the buffer.
   * @param vertices A pointer to an array of floats to use as data.
   * @param size The size in bytes of the data.
   * @param usage The usage type, default GL_STATIC_DRAW.
   */
  void setup(const GLfloat* vertices, GLsizeiptr size, GLenum usage = GL_STATIC_DRAW);

  /**
   * @brief Sets up the data for the buffer.
   * @tparam T Things stored *contiguously* in memory.
   * @param vertices A pointer to an array of Ts to use as data.
   * @param size The size in bytes of the data.
   * @param usage The usage type, default GL_STATIC_DRAW.
   */
  template <typename T>
  void setup(const T* vertices, GLsizeiptr size, GLenum usage = GL_STATIC_DRAW) {
    glBindBuffer(GL_ARRAY_BUFFER, m_id);
    glBufferData(GL_ARRAY_BUFFER, size, vertices, usage);
    glCheckError();
  }

  /**
   * @brief Sets up the data for the buffer.
   * @tparam T You might want this to some numeric, vector, etc type for good result,
   * @param vertices A std::vector of something to use as data.
   * @param usage The usage type, default GL_STATIC_DRAW.
   */
  template <typename T>
  void setup(const std::vector<T>& vertices, GLenum usage = GL_STATIC_DRAW) {
    glBindBuffer(GL_ARRAY_BUFFER, m_id);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(T), vertices.data(), usage);
    glCheckError();
  }

  /**
   * @brief Sets up the data for the buffer avoiding the cost of reallocating
   *  the data store.
   * @tparam T Things stored *contiguously* in memory.
   * @param vertices A pointer to an array of Ts to use as data.
   * @param size The size in bytes of the data.
   * @param offset The offset into the buffer object's data store where data
   *  replacement will begin, measured in bytes.
   */
  template <typename T>
  void setupSubData(const T* vertices, GLsizeiptr size, GLintptr offset = 0) {
    glBindBuffer(GL_ARRAY_BUFFER, m_id);
    glBufferSubData(GL_ARRAY_BUFFER, offset, size, vertices);
    glCheckError();
  }

  /**
   * @brief Sets up the data for the buffer avoiding the cost of reallocating
   *  the data store.
   * @tparam T You might want this to some numeric, vector, etc type for good result,
   * @param vertices A std::vector of floats to use as data.
   * @param offset The offset into the buffer object's data store where data
   *  replacement will begin, measured in bytes.
   */
  template <typename T>
  void setupSubData(const std::vector<T>& vertices, GLintptr offset = 0) {
    glBindBuffer(GL_ARRAY_BUFFER, m_id);
    glBufferSubData(GL_ARRAY_BUFFER, offset, vertices.size() * sizeof(T), vertices.data());
    glCheckError();
  }

  /**
   * @brief Unbinds the VBO.
   */
  void unbind() const { glBindBuffer(GL_ARRAY_BUFFER, 0); }

 private:

  GLuint m_id {};
};

As you see, I don't save any vbo id in the vao.

\$\endgroup\$
2
  • \$\begingroup\$ Is it alright if vbo is deleted after binding to vao? \$\endgroup\$
    – ihsan
    Commented Jan 10, 2023 at 17:21
  • \$\begingroup\$ According to this and this, the vbo won't get really deleted until no longer in use. \$\endgroup\$
    – Alex CB
    Commented Jan 10, 2023 at 17:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .