OpenGL in Qt 5.1 – Part 2

This article continues our series on what is new in Qt 5.1 with respect to OpenGL. The first installment can be found here.

Vertex Array Objects

Qt has QOpenGLBuffer (and before that QGLBuffer) to help manage various types of OpenGL buffer objects such as per-vertex attribute data and element index buffers. OpenGL also has a simple related container type call Vertex Array Objects (VAOs) to help with managing sets of vertex buffer objects.

KDAB has added code to Qt 5.1 that encapsulates VAOs with the QOpenGLVertexArrayObject class. Binding an instance of this class causes OpenGL to “remember” any vertex specification state that you then set up. We can later restore this vertex specification state very quickly by simply re-binding the VAO itself. This allows us to very rapidly switch between vertex states for “objects” that we wish to draw in our rendering function:

void Scene::initialize()
{
    // Assumes we have a current QOpenGLContext and that
    // m_shaderProgram is a QOpenGLShaderProgram

    // Create VAO for first object to render
    m_vao1 = new QOpenGLVertexArrayObject( this );
    m_vao1->create();
    m_vao1->bind();

    // Setup VBOs and IBO (use QOpenGLBuffer to buffer data,
    // specify format, usage hint etc). These will be
    // remembered by the currently bound VAO
    m_positionBuffer.create();
    m_positionBuffer.setUsagePattern( QOpenGLBuffer::StreamDraw );
    m_positionBuffer.bind();
    m_positionBuffer.allocate( positionData,
                               vertexCount * 3 * sizeof( float ) );
    m_shaderProgram.enableAttributeArray( "vertexPosition" );
    m_shaderProgram.setAttributeBuffer( "vertexPosition", GL_FLOAT, 0, 3 );

    m_colorBuffer.create();
    m_colorBuffer.setUsagePattern( QOpenGLBuffer::StaticDraw );
    m_colorBuffer.bind();
    m_colorBuffer.allocate( colorData,
                            vertexCount * 3 * sizeof( float ) );
    m_shaderProgram.enableAttributeArray( "vertexColor" );
    m_shaderProgram.setAttributeBuffer( "vertexColor", GL_FLOAT, 0, 3 );

    // Repeat for buffers of normals, texture coordinates,
    // tangents, ...
    ...

    // Create VAO for second object to render
    m_vao2 = new QOpenGLVertexArrayObject( this );
    m_vao2->create();
    m_vao2->bind();

    // Setup VBOs and IBO for next object
    ...

    // Rinse and repeat for other objects
    m_skyBoxVAO = new QOpenGLVertexArrayObject( this );
    ...
}

void Scene::render()
{
    // Clear buffers
    m_funcs->glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    // Bind shader program, textures for first set of objects
    m_phongShaderProgram->bind();
    ...

    // Switch to the vertex data for first object and draw it
    m_vao1->bind();
    m_funcs->glDrawElements(...);

    // Switch to the vertex data for second object and draw it
    m_vao2->bind();
    m_funcs->glDrawElements(...);

    // Maybe change shader program, textures etc
    // and draw other objects
    m_skyboxShaderProgram->bind();
    ...
    m_skyboxVAO->bind();
    m_funcs->glDrawElements(...);
    ...
}

VAOs were introduced with OpenGL 3 but are required for OpenGL 3.1 and for OpenGL >=3.2 with the Core profile. In addition, VAOs are available via the GL_ARB_vertex_array_object or GL_OES_vertex_array_object extensions on OpenGL 2 and OpenGL ES 2 respectively. The QOpenGLVertexArrayObject will use the core feature if available or fall-back to the appropriate extension if available.

The use of VAOs can greatly simplify your rendering code and make it faster due to the OpenGL driver having to potentially do less sanity checking than if doing more buffer operations.

Read Part 3…

 

Share on FacebookTweet about this on TwitterShare on Google+
m4s0n501

15 thoughts on “OpenGL in Qt 5.1 – Part 2

  1. Hmm, I don’t see any glVertexAttribPointer() nor glEnableVertexAttribArray() calls in your example, however those are the ones that set up the state recorded in a VAO. Or is there same magic going on to establish that positions are sourced from the m_positionBuffer VBO?

    • Oops thanks for pointing that out! Copy and paste error on my part. Now fixed with calls to QOpenGLShaderProgram::enableAttributeBuffer() and QOpenGLShaderProgram::setAttributeBuffer().

  2. What is the point of this class really?

    It just seems like a wrapper over glGenVertexArrays​, glDeleteVertexArrays​, and glBindVertexArray with the fall back to glGenVertexArrays​ARB, glDeleteVertexArraysARB​, and glBindVertexArrayARB.

    This class does NOTHING to address that one cannot tell from a local block of code if a VAO is bound or not. The class does not even give a nice wrapper function to identify which VAO is bound. What could have been done, but was utterly missed was making the glVertexAttribPointer like calls as member functions of the VAO using pointers to QOpenGLBuffer. That atleast would have been a logical extensions. Instead all that the class is a wrapper over the above GL calls.

    Going further, the class QOpenGLBuffer is busted. In OpenGL, a fixed buffer object can be bound to any binding location, but the QOpenGLBuffer::bind() method does not take any argument. Instead, the location to which is binds with bind() is set at ctor. You can get around this by.. using doing your self via glBindBuffer(target, qglbufferobject->bufferId()). Brilliant does not really do anything fore you folks. Worse, yet for indexed buffer binding points (such as that used for UBO’s (aka bindable uniforms) and transform feedback are indexed: a name and index are used. Does not matter, these bind points are not enumerated anyways. Going further, the map member function is castrated as the map functions for GL buffer objects in OpenGL since version 3.x and also now in GLES3 have a lot more options than that which is exposed. So what happens, a developer directly uses bufferId() and GL calls directly. It gets worse, the interface borks buffer-reallocation for streaming because it does not think about what glBufferData(…, NULL) signals to a driver when the size is the same.

    These wrappers are not really going to help anyone as they drop functionality and do not make it genuinely any easier anyways. I admit writing wrappers is satisfying, but these Qt wrappers offer no real value and no real purpose. I suspect they simple reflect how QSG uses GL, which is fine.. but then these should have been left as internal classes… they are not fit for serious GL work.

    • What is the point of this class really?

      It just seems like a wrapper over glGenVertexArrays​, glDeleteVertexArrays​, and glBindVertexArray with the fall back to glGenVertexArrays​ARB, glDeleteVertexArraysARB​, and glBindVertexArrayARB.

      The point of this class is simply to be a convenience for those wishing to use VAOs where available.

      This class does NOTHING to address that one cannot tell from a local block of code if a VAO is bound or not. The class does not even give a nice wrapper function to identify which VAO is bound. What could have been done, but was utterly missed was making the glVertexAttribPointer like calls as member functions of the VAO using pointers to QOpenGLBuffer. That atleast would have been a logical extensions. Instead all that the class is a wrapper over the above GL calls.

      Keeping the class very simple was a deliberate design decision. QOpenGLShaderProgram, already offers wrappers for glVertexAttribPointer(). Furthermore, we decided not to try to emulate direct state access (DSA) with these classes as 1) it is not available everywhere to be able to do it properly 2) it is too easy for people to by-pass if DSA functions are not available. This is the classic OpenGL middle-ware problem. Let us hope that OpenGL 5 will removed the bind/edit concept in favour of DSA. At that point we can look at adding much nicer encapsulation without fear of very hard to track down side effects.

      Going further, the class QOpenGLBuffer is busted. In OpenGL, a fixed buffer object can be bound to any binding location, but the QOpenGLBuffer::bind() method does not take any argument…

      I fully agree. The QOpenGLBuffer class needs some work. It’s API is a direct copy of the legacy QGLBuffer class from early Qt4 times. That is not what this article is about and is something that can easily be fixed with the addition of overloaded bind() member functions that take a binding point and optionally a binding point index. If when we look at extending QOpenGLBuffer we find that a completely new API is needed then we will do so with a new class and deprecate the existing one. The issue here is that nobody has really looked into what could/should be improved in QOpenGLBuffer. If you have ideas (or even better time) then your contributions would be appreciated.

      It gets worse, the interface borks buffer-reallocation for streaming because it does not think about what glBufferData(…, NULL) signals to a driver when the size is the same.

      Could you elaborate on this please? I think you are referring to orphaning of buffer data in the driver but I don’t understand the implications of what you are alluding to.

      These wrappers are not really going to help anyone as they drop functionality and do not make it genuinely any easier anyways. I admit writing wrappers is satisfying, but these Qt wrappers offer no real value and no real purpose. I suspect they simple reflect how QSG uses GL, which is fine.. but then these should have been left as internal classes… they are not fit for serious GL work.

      Yes, as I mentioned in the first article, the traditional target of these classes has been the common subset of OpenGL 2.x and ES 2 that was used by Qt itself. We are in the process of expanding their coverage to modern OpenGL. The simple fact is that nobody has done so with QOpenGLBuffer and some of the other classes yet. This is an on-going process.

      The classes are perfectly fine for serious work and I know for a fact they are used in very large Qt + OpenGL projects around the world. It all depends upon the feature set that you wish to use. Once again, traditionally this was GL 2.x/ES2 level features but is now being extended. However, even if wanting to use all of the modern bells and whistles of OpenGL 4.3 it is still possible to use these wrappers and to mix in raw OpenGL calls where needed. Part 1 was about making the GL 3/4 calls readily available for such situations (amongst others).

  3. The class is even worse: one must call create() by hand [the idea to ensure that the a GL context is current]. The documentation is silent on weather or not one must call destroy() or if a GL context must be current at dtor if destroy() was not called.

    Not good people, using the GL entry points directly [and one's own function pointers] will get the job done more reliable.

    And what the heck, it is not like this class has any real code in it:

    create() –> glGenVertexArrays(1, &m_id)
    bind() –> glBindVertexArray(m_id)
    destroy() –> glDeleteVertexArrays(1, &m_id)
    release() –> glBindVertexArray(0)
    isCreated() –> return m_id!=0

    you have got be kidding, making a blog on this.

    • The class follows the other Qt OpenGL classes by having an explicit create() function. I don’t know why this is such a surprise to you as it is no different than calling glGen*() with any OpenGL resource. Having the class designed this way simply means it is possible to use it on the stack or as a value member function (not a pointer) when we can’t be sure that we will have a current context at that time.

      As for the documentation issue around the destructor/destroy, sure this can be improved. If you feel so strongly about it please submit a patch to gerrit. This is simply an oversight.

      As for the complexity of the code within the class, yes it is simple but it is just a thin wrapper as I replied in your other post. It’s purpose is mainly to resolve entry points for the core feature or one of the appropriate extensions. It also provides a simple RAII subclass to help with binding/releasing in code that can throw exceptions or have early returns etc.

      Once again, it’s there if people wish to use it. If you don’t want to, then also fine, don’t use it.

      I find your attitude and tone of comment very negative and un-helpful. Although you obviously have a good grasp of OpenGL objects and APIs, others may not even know what a VAO is. This blog is not meant to be an in-depth treatise on VAOs, simply a quick overview saying what a VAO is with links to other resources for interested parties and a few words introducing a new class in Qt. So it may have nothing of value to you but please consider others with less experience or different backgrounds and needs.

      Please try to keep your comments polite. If you see no value in a class, then don’t use it. Others may find it useful to them.

      • > I find your attitude and tone of comment very negative and un-helpful.
        > Although you obviously have a good grasp of OpenGL objects and APIs,
        > others may not even know what a VAO is. This blog is not meant to be an
        > in-depth treatise on VAOs, simply a quick overview saying what a VAO is
        > with links to other resources for interested parties and a few words
        > introducing a new class in Qt. So it may have nothing of value to you but
        > please consider others with less experience or different backgrounds and
        > needs.

        No offense, but how you find my attitude is irrelavent. What you think of me is also irrelavent.

        If the purpose of the blog is to educate people about VAO’s that is good. On that point a material is missing about VAO’s in this blog that are can be major-gotchas that before someone uses VAO’s should be made aware. The biggest one being that VAO’s do NOT share across GL contexts even when said GL contexts are in the same share group. This is a major, major gotcha of VAO’s and the above example given in the blog gives the typcial useage of VAO’s: make them at object creation and just use them.. but it utterly fails to mention this point which means in a multi-GL context program the class in this example is bug-trap. Unless one reads up on VAO’s from outside of this blog, one would never know.

        That the docs for such a razor thin class (which in reality does nothing except permute symbols really) do not exist means that the class was written before it was specified.

        From a professional point of view, I cannot recommend anyone to use the Qt GL wrapper classes:

        QGLFramebufferObject has no ability to do MRT, has serious issues on how to specify the internal format of a texture in GLES2 (this is because in unextended GLES2, internal format must be one of GL_RGB, GL_RGBA, GL_ALPHA, GL_LUMINANCE or GL_LUMINANCE_ALPHA and the values for external format and pixel type determine the actual storage). It’s main use is that it inherits from QPaintDevice so that one can use the GL2 paint engine on it to make a QPainter draw to it’s texture. Sighs.

        QGLShaderProgram does not support tessellation or tessellation control shaders (atleast according to http://doc-snapshot.qt-project.org/qt5-dev/qtopengl/qglshaderprogram.html) This is because instead of using the GL enumeration directly, the interface insists on using it’s own enums. Atleast the Qt interface has a dedicated class for shaders (QGLShader). The docs for QGLShaderProgram are again sketchy: does QGLShaderProgram::enableAttributeArray immediately enable that attribute array, does it update an internal data structure? If the function was static, I atleast would know better. In GL how an attribute is packed and weather or not it is enabled is disjoint from the program. If a user is expected to mix native GL calls with Qt wrapped GL calls, they need to know the affect on the GL state vector for these calls. Heck how it interacts with VAO’s is not even documented. The code of this blog indicates that the _member_ functions for QGLShaderProgram that affect attribute GL-state vector state are just wrappers over the GL calls… but why are they not static member functions then?!

        We’ve already made fun of QGLBuffer enough.

        So what is left that is possibly worth using? QOpenGLContext. Even QGLWidget is a bid of a mad house since (atleast as of Qt 4.8) the GL context is not even created at ctor of the QGLWidget [there are reasons for this, but they reasoning behind it points, in my opinion, to defects in how Qt abstracted bits].

        What I see in the Qt GL wrapper classes is just that: wrappers that do not make coding any easier, leave a wild card of not knowing effects on the GL state vector [unless one read's the source code instad of the docs!] and offer zero additional functionality over using GL function calls directly [with the one exception of QGLFramebufferObject::paintDevice()) ].

        • If the purpose of the blog is to educate people about VAO’s that is good. On that point a material is missing about VAO’s in this blog that are can be major-gotchas that before someone uses VAO’s should be made aware. The biggest one being that VAO’s do NOT share across GL contexts even when said GL contexts are in the same share group. This is a major, major gotcha of VAO’s and the above example given in the blog gives the typcial useage of VAO’s: make them at object creation and just use them.. but it utterly fails to mention this point which means in a multi-GL context program the class in this example is bug-trap. Unless one reads up on VAO’s from outside of this blog, one would never know.

          Yes, there is a note in the class documentation for QOpenGLVertexArrayObject about VAOs not being shareable between contexts.

          As mentioned, the QGL* classes are deprecated and will see no further development. The QOpenGL* classes are the newer ones that should be used so better to look there for support of features. Having said that, many of them are simple ports of the older QGL classes particularly the QOpenGLFramebufferObject and QOpenGLBuffer classes. These are due to be looked at but not in time for Qt 5.1. I also fully agree with your comments about the Qt frame buffer wrapper classes, these are very much in need of extension to support MRTs, different texture formats and texture targets. If you are able to help with such efforts then your assistance would be much appreciated.

          • I have to agree with Grumpy OpenGL. I can see what you tried to achieve, but to be honest, for any serious use anyone will rather have direct access to GL API rather than poor wrappers and weird abstraction. There are many things that are wrong just by design – one that really caught my attention is coupling of GL buffer layout (pointers) with shaders – this is really bad idea. You end up with situation where you can’t easily switch shaders because buffers and shaders are tied by your own class abstraction. GL do not enforce this, whats more it allows you to nicely decouple these two concepts. Yet you force the worst possible solution with your design.

            Thats just one single thing from a small part of code – I’m sure there are many gotchas like that, and Grumpy OpenGL already pointed few more. This is very limiting and will become frustrating once people learn the basics (drawing a single triangle, or a model, hooray).

            What would you suggest for someone who wants to use Qt for its nice cross-platform UI, but also have possibility to re-use GL context for his own stuff, that uses standard GL calling convention. I don’t want to inherit from some weird classes to be able to call glXXX methods, and I don’t want to use m_funcs->glXXX() either. Can it be achieved with Qt? If not with pure Qt, is it possible to use lib like GLFW3 together with Qt and make them work well together?

            Otherwise it severely limits uses that don’t base purely on Qt.

    • I think these wrappers make the code more portable. Writing an OpenGL application that runs on Linux, Windows and mobile devices is pure hell if you want to use newer than ten years old stuff. Let’s take this VAO as an example. It’s practically mandatory in OpenGL 3.3, but *might* not even be available in OpenGL ES. And then there’s Windows. The new graphics stack in Qt5 is not compatible with GLEW. My app doesn’t even compile anymore due to that. So, now I need to use QGLFunctions instead, but it doesn’t include VAO. Not funny. I assume that the only way is to use this new VAO wrapper if I want to proceed with Qt.

  4. I fully agree with Grumpy OpenGL.And the biggest problem which stems from this thing that choosing NOT to use Qt built in OpenGL one gets into one big mess.Why you guys didn’t leave an option just to use “pure” (GLEW) based OpenGL interface.Last time I tried to do it with Qt 5 I got tons of errors coming from Qt’s GLES header conflicting GLEW declaration. Furthermore ,one can’t easily fix it.I can’t just disable OpenGL ES backend by some macro, but have to recompile whole SDK with opengl -desktop mode which leads to even bigger headache.Please make something in the upcoming version so that it’s possible to use external OpenGL API with less pain.

  5. I just want to use QT to develop opengl prgram with pure Opengl code. Why QT does make things more complex. QT should allow users to code pure Opengl code as an optional choose

Leave a Reply

Your email address will not be published. Required fields are marked *


− 6 = three