80

Im facing an issue which I believe to be VAO-dependant, but Im not sure..

I am not sure about the correct usage of a VAO, what I used to do during GL initialization was a simple

glGenVertexArrays(1,&vao)

followed by a

glBindVertexArray(vao)

and later, in my drawing pipeline, I just called glBindBuffer(), glVertexAttribPointer(), glEnableVertexAttribArray() and so on.. without caring about the initally bound VAO

is this a correct practice?

ECrownofFire
  • 462
  • 4
  • 11
user815129
  • 2,050
  • 1
  • 17
  • 29
  • 31
    I am not sure why this question got a -1.. it doesnt show any effort and.or is unclear? I spent 3 days struggling with the specs, wikis and forums before posting this and didnt get any real reason I should use VAO.. meh.. – user815129 Jan 19 '12 at 09:08
  • 18
    Because for the cool bro's it's all obvious as soon as they have their cut-and-pastes handy, why bother with understanding stuff?? – mlvljr Nov 02 '13 at 19:33

4 Answers4

96

VAOs act similarly to VBOs and textures with regard to how they are bound. Having a single VAO bound for the entire length of your program will yield no performance benefits because you might as well just be rendering without VAOs at all. In fact it may be slower depending on how the implementation intercepts vertex attribute settings as they're being drawn.

The point of a VAO is to run all the methods necessary to draw an object once during initialization and cut out all the extra method call overhead during the main loop. The point is to have multiple VAOs and switch between them when drawing.

In terms of best practice, here's how you should organize your code:

initialization:
    for each batch
        generate, store, and bind a VAO
        bind all the buffers needed for a draw call
        unbind the VAO

main loop/whenever you render:
    for each batch
        bind VAO
        glDrawArrays(...); or glDrawElements(...); etc.
    unbind VAO

This avoids the mess of binding/unbinding buffers and passing all the settings for each vertex attribute and replaces it with just a single method call, binding a VAO.

Robert Rouhani
  • 13,882
  • 5
  • 42
  • 57
  • ah yes, infact I didnt use VAO at all before.. until GL specs began complaining an error might (and will be) thrown if no VAO is bound.. Thank you – user815129 Jan 19 '12 at 09:06
  • 1
    is it really necessary to unbind the VAO? Ive found different practices around – user815129 Jan 19 '12 at 10:41
  • 3
    It's not necessary, but I usually do it in case some other object decides to render without VAOs (debug drawing/font rendering library/etc.), as that would (as far as I know) replace the attribute settings for the currently bound VAO. – Robert Rouhani Jan 19 '12 at 18:18
  • Why should the object be drawn once during initialization? Is that drawing another glDrawArrays() call? – cjcurrie Jan 28 '13 at 01:40
  • As with another answer I wrote around the same time, I was under the impression that a draw call was required, when in fact all that is necessary is binding all the buffers. Fixed the answer. – Robert Rouhani Jan 28 '13 at 19:02
  • 2
    Why is the unbind occurring only once and outside the loop? Shouldn't the unbind happen on a per-batch basis? – batbrat Feb 08 '13 at 07:50
  • 12
    Binding a new VAO replaces the old VAO. Unbinding inside the loop would just be extra work. – Robert Rouhani Feb 08 '13 at 19:51
  • 1
    @RobertRouhani Is there any benefit to that last unbind VAO, then? (Does not unbinding affect anything?) – Mateen Ulhaq Nov 06 '19 at 00:26
28

No, that's not how you use VAO. You should use VAO in same way how you are using VBO or textures, or shaders. First set it up. And during rendering only Bind them, without modifying it.

So with VAO you do following:

void Setup() {
    glGenVertexArrays(..);
    glBindVertexArray(..);
    // now setup all your VertexAttribPointers that will be bound to this VAO
   glBindBuffer(..);
   glVertexAttribPointer(..);
   glEnableVertexAttribArray(..);
}

void Render() {
    glBindVertexArray(vao);
    // that's it, now call one of glDraw... functions
    // no need to set up vertex attrib pointers and buffers!
    glDrawXYZ(..)
}

See also these links:

Sri Harsha Chilakapati
  • 10,962
  • 6
  • 46
  • 87
Mārtiņš Možeiko
  • 12,423
  • 2
  • 42
  • 42
10

is this a correct practice?

Yes, this is perfectly legal and valid. Is it good? Well...

There has been some informal performance testing on this sort of thing. And it seems, at least on NVIDIA hardware where this was tested, the "proper" use of VAOs (ie: what everyone else advocated) actually is slower in many cases. This is especially true if changing VAOs does not change which buffers are bound.

No similar performance testing has taken place on AMD hardware, to my knowledge. In general, unless something changes with them, this is an acceptable use of VAOs.

Nicol Bolas
  • 378,677
  • 53
  • 635
  • 829
  • 2
    Surely once each VAO has enough various state that it tracks, that switching the buffers and attrib ptrs manually during the render loop would require many calls, that we would start to see improved performance from the use of the VAO? – Steven Lu Sep 08 '13 at 20:49
  • Unfortunately, the link is dead. – Nick Caplinger Feb 23 '17 at 16:53
3

Robert's answer above worked for me when I tried it. For what it's worth here is the code, in Go, of using multiple Vertex Attribute Objects:

// VAO 1

vao1 := gl.GenVertexArray()
vao1.Bind()

vbo1 := gl.GenBuffer()
vbo1.Bind(gl.ARRAY_BUFFER)

verticies1 := []float32{0, 0, 0, 0, 1, 0, 1, 1, 0}
gl.BufferData(gl.ARRAY_BUFFER, len(verticies1)*4, verticies1, gl.STATIC_DRAW)

pa1 := program.GetAttribLocation("position")
pa1.AttribPointer(3, gl.FLOAT, false, 0, nil)
pa1.EnableArray()
defer pa1.DisableArray()

vao1.Unbind()

// VAO 2

vao2 := gl.GenVertexArray()
vao2.Bind()

vbo2 := gl.GenBuffer()
vbo2.Bind(gl.ARRAY_BUFFER)

verticies2 := []float32{-1, -1, 0, -1, 0, 0, 0, 0, 0}
gl.BufferData(gl.ARRAY_BUFFER, len(verticies2)*4, verticies2, gl.STATIC_DRAW)

pa2 := program.GetAttribLocation("position")
pa2.AttribPointer(3, gl.FLOAT, false, 0, nil)
pa2.EnableArray()
defer pa2.DisableArray()

vao2.Unbind()

Then in your main loop you can use them as such:

for !window.ShouldClose() {
    gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

    vao1.Bind()
    gl.DrawArrays(gl.TRIANGLES, 0, 3)
    vao1.Unbind()

    vao2.Bind()
    gl.DrawArrays(gl.TRIANGLES, 0, 3)
    vao2.Unbind()

    window.SwapBuffers()
    glfw.PollEvents()

    if window.GetKey(glfw.KeyEscape) == glfw.Press {
        window.SetShouldClose(true)
    }
}

If you want to see the full source, it is available as a Gist and derived from the examples in go-gl:

https://gist.github.com/mdmarek/0f73890ae2547cdba3a7

Thanks everyone for the original answers, I had the same question as ECrownofFire.

Marek
  • 51
  • 4