23/12/2010 OpenGL Samples Pack 4.1.4.0 released, focus on DSA

The OpenGL Samples Pack 4.1.4.0 is released. The main change for this release is the new samples featuring the Direct State Access (DSA) API provided by GL_EXT_direct_state_access extension.

It is now available in both AMD (since catalyst 10.7) and nVidia (since more than 2 years) drivers with an excellent support as I haven't found any OpenGL drivers bug yet even with more sophisticated projects than these samples. As it remains an EXT extension, all the samples using this feature use the post-fix 'ext'.

A lot of OpenGL programmers are looking forward for this extension to reach OpenGL core and sooner or later it will happen as the previous OpenGL specifications have shown that the ARB believe it as well. However, this extension is not free of design issues as I have explained in an old OpenGL wishlist. Another area of problems with DSA is actually the lack of compleness that I would like to detail in this post.

GL_EXT_direct_state_access is written against OpenGL 3.0 specification

Originally, the direct state access extension has been written against the OpenGL 2.1 specification and published slightly after the OpenGL 3.0 specification and seen as the only light of hope for OpenGL after the big OpenGL 3.0 controversy. In January 2009, the extension has been updated against OpenGL 3.0 which has been a huge improvement but OpenGL 3.2 was already release and some gaps remained and still remain.

1. DSA issues in OpenGL 4.1 specification

1.1. glBindProgramPipeline doesn't follow the DSA principes

Since OpenGL 3.1 the ARB is considering the DSA approach when creating new entry point but unfortunatly some issues have appeared like the glBindProgramPipeline example I demonstrated. Basically, to create the real program pipeline object, the object name generated with glGenProgramPipelines need to be binded according to the specification. In practice both AMD and nVidia don't follow the specification and rely on the DSA principles for this case but still, the specification is a issue.

1.2. Subroutines have a awkward behaviours mechanisium.

When I first tried the subroutine API, the least I can say is that I struggle. First, the all 'index' and 'location' is really confusing but also the API is not actually following the DSA principles. glUniformSubroutinesuiv has actually a odd behaviour as it's a context state but each time a program is binded this state value is invalided... which makes me feel it should be a program state using DSA. The (odd) explaination for this behaviours is to provide the possibility to switch context without resetting subroutines... None of the rest of the API behave like that which makes it not consistent. My workaround is to set the subroutines each time the corresponding program is binded...

1.3. Multisample textures don't support DSA.

Multisample texture is a new feature of OpenGL 3.2 that creates an entry point glTexImage2DMultisample. The new functionnalities provided by the multisampled textures are a superset of what the multisampled renderbuffer provides so that in a way we can consider deprecated the renderbuffer functionnality. The only issue with that: No entry point is available in GL_EXT_direct_state_access, the ARB pretty much forgot to add a dependency to this extension in GL_ARB_texture_multisample so that a DSA entry point (glTextureImage2DMultisampleEXT) for glTexImage2DMultisample would be available if direct state access extension is supported.

1.4. The VAO API is conceptually an issue for DSA

I am really evil with VAO but is it really my fault? The VAO keeps demonstrating how wrongly designed it has been and when I noticed that OpenGL ES has now a KHR VAO extension... well, it doesn't really made me happy to let this none sense survive in OpenGL. When I think about the VAO, my brain ends up like double locked threads.

The first issue is the GL_ELEMENT_ARRAY_BUFFER binding point which is a VAO state. There is not precedent for glBind* functions being DSA function as all glBind* functions are context state expect this (stupid) case. My work around is to consider the GL_ELEMENT_ARRAY_BUFFER state like a context state that I need to bind after the VAO object... like subroutines actually.

1.5. glVertexAttribDivisor has no DSA version

OpenGL 3.0 was released with a serie of extensions that wasn't part of OpenGL 3.0 specification which includes GL_ARB_instanced_arrays, the promoted version of GL_NVX_instanced_arrays, an extension which provides almost the fastest way to do geometry instancing and written against OpenGL 2.1. Unfortunately, this extension reached OpenGL spefication only in OpenGL 3.3 but has been integrated 'as it is' which implies no GL_EXT_direct_state_access dependency and DSA support which in practice makes the use of DSA with VAO no possible.

1.6. Transform feedback buffer

OpenGL 4.0 has brought significant improvements to transform feedback functionnalities even if I still think we need more. OpenGL 4.0 integrates GL_ARB_transform_feedback2 which add a transform feedback object moving some context states to this new object. OpenGL handles these states using glBindBufferBase a function that doesn't have DSA version, like other glBind* functions...

2. A bit of perspective on DSA

2.1. The ideal

An interesting point with the DSA approach is the hability of designing a graphics engine without the constraint of handeling the update and the draw phases together but instead seeing them as 2 separated use cases so that the update phase doesn't have conscequences on the draw phase stage. This design philosophy makes the graphics engine design much simpler, remove the CPU overhead for this necessary management but also avoid the question of rebinding or resetting states. Finally and maybe most importantly, DSA is really less error prone !

2.2. The practice.

Unfortunately, in practice a full DSA design approach isn't fully possible simply because the OpenGL DSA support isn't completed yet as demonstrate in section 1. However, there are already some good parts which will singnificantly improve designs.

glProgramUniform allows to set the uniform values without having to bind a program or 'play' with the selector glActiveShaderProgram introduced with OpenGL 4.1 for separate programs. This is such a great improvement which makes possible to avoid using delayed calls to set uniform variables without constantly binding programs.

The other really interesting point is the hability to update buffer and texture objects without affecting the binded buffers for a draw call. This is a great opportunity for data streaming/vitual texture and actually any scenario where the programmer wants to build an advanced and customized buffer and/or texture manager.

2.3. Let's have a quick look at DSA!
Update an uniform:
  • //Without DSA
  • glUseProgram(ProgramName);
  • glUniformMatrix4fv(UniformMVP, 1, GL_FALSE, <MVP[0][0]);
  • //With DSA
  • glProgramUniformMatrix4fv(ProgramName, UniformMVP, 1, GL_FALSE, <MVP[0][0]); //OpenGL 4.1 function
Creating a texture with DSA:
  • glGenTextures(1, &TextureName);
  • glTextureParameteriEXT(TextureName, GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
  • glTextureParameteriEXT(TextureName, GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1000);
  • glTextureParameteriEXT(TextureName, GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
  • glTextureParameteriEXT(TextureName, GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
  • glTextureParameteriEXT(TextureName, GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE);
  • glTextureParameteriEXT(TextureName, GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA);
  • gli::texture2D Texture = gli::load(TEXTURE_DIFFUSE_DXT5);
  • for(gli::texture2D::level_type Level = 0; Level < Texture.levels(); ++Level)
  • {
  • glCompressedTextureImage2DEXT(
  • TextureName,
  • GL_TEXTURE_2D,
  • GLint(Level),
  • GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
  • GLsizei(Texture[Level].dimensions().x),
  • GLsizei(Texture[Level].dimensions().y),
  • 0,
  • GLsizei(Texture[Level].capacity()),
  • Texture[Level].data());
  • }
Framebuffer object example:
  • //Without DSA
  • glGenTextures(1, &ColorTextureName);
  • glBindTexture(GL_TEXTURE_2D, ColorTextureName);
  • glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
  • glBindTexture(GL_TEXTURE_2D, 0);
  • glGenFramebuffers(1, &FramebufferResolveName);
  • glBindFramebuffer(GL_FRAMEBUFFER, FramebufferResolveName);
  • glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ColorTextureName, 0);
  • if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
  • return false;
  • glBindFramebuffer(GL_FRAMEBUFFER, 0);
  • //With DSA
  • glGenTextures(1, &ColorTextureName);
  • glTextureImage2DEXT(ColorTextureName, GL_TEXTURE_2D, 0, GL_RGBA8, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
  • glGenFramebuffers(1, &FramebufferResolveName);
  • glNamedFramebufferTexture2DEXT(FramebufferResolveName, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ColorTextureName, 0);
  • if(glCheckNamedFramebufferStatusEXT(FramebufferResolveName, GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
  • return false;
OpenGL Samples Pack 4.1.4.1 released >
< GLM 0.9.0.6 released
Copyright Christophe Riccio 2002-2013 all rights reserved
Designed for Chrome 9, Firefox 4, Opera 11 and Safari 5