OpenGL wrapping layer
Overview of the base OpenGL wrapper API.
Contents
The purpose of the GL library is to simplify interaction with the OpenGL API using type-safe C++11 features, abstracting away extension and platform differences, tracking the state for optimum performance and selecting the best available code path for given system.
Magnum provides wrappers for most native OpenGL objects like buffers, textures, meshes, queries, transform feedback objects, shaders etc., but makes it possible to use raw GL calls or combine Magnum with third-party OpenGL libraries if the user wants to.
OpenGL object wrapper instances
By default, all underlying OpenGL objects are created in wrapper class constructor and deleted in wrapper class destructor. Constructing an object using default constructor requires active GL::
Besides the default behavior, it is possible to construct the object without creating the underlying OpenGL object using the NoCreate tag. Constructing the object this way does not require any active context and its state is then equivalent to the moved-from state. It is useful in case you need to construct the object before creating context (such as class members) or if you know you would overwrite it later with another object:
GL::Mesh mesh{NoCreate}; GL::Buffer vertices{NoCreate}, indices{NoCreate}; std::tie(mesh, vertices, indices) = importSomeMesh();
If you need to preserve the underlying OpenGL object after destruction, you can call release()
. It returns ID of the underlying object, the instance is then equivalent to moved-from state and you are responsible for proper deletion of the returned OpenGL object (note that it is possible to just query ID of the underlying without releasing it using id()
). It is also possible to do the opposite — wrapping existing OpenGL object ID into Magnum object instance using wrap()
.
/* Transferring the instance to external library */ { GL::Buffer buffer; buffer.setData(someData, GL::BufferUsage::StaticDraw); GLuint id = buffer.release(); externalLib.setSomeBuffer(id); /* The library is responsible for deletion */ } /* Acquiring an instance from external library */ { GLuint id = externalLib.someBuffer(); GL::Buffer buffer = GL::Buffer::wrap(id, GL::ObjectFlag::DeleteOnDestruction); /* The buffer instance now handles deletion */ }
The NoCreate
constructor, wrap()
and release()
functions are available for all OpenGL classes except GL::
Note that interaction with third-party OpenGL code as shown above usually needs special attention:
State tracking and interaction with third-party code
It is possible (and encouraged) to combine Magnum with third-party libraries or even raw OpenGL calls — trying out features that are not yet implemented in Magnum, using some specialized GUI library etc. But bear in mind that in order to improve performance and avoid redundant state changes, Magnum internally tracks OpenGL state such as currently bound objects, activated renderer features etc. When combining Magnum with third-party code, the internal state tracker may get confused and you need to reset it using GL::
GL::Buffer buffer; GL::Mesh mesh; // ... mesh.draw(someShader); { /* Entering a section with 3rd-party OpenGL code -- clean up all state that could cause accidental modifications of our objects from outside */ GL::Context::current().resetState(GL::Context::State::EnterExternal); /* Raw OpenGL calls */ glBindBuffer(GL_ARRAY_BUFFER, buffer.id()); glBufferStorage(GL_ARRAY_BUFFER, 32768, nullptr, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT); // ... /* Exiting a section with 3rd-party OpenGL code -- reset our state tracker */ GL::Context::current().resetState(GL::Context::State::ExitExternal); } /* Use the buffer through Magnum again */ auto data = buffer.map(0, 32768, GL::Buffer::MapFlag::Read|GL::Buffer::MapFlag::Write); // ...
Note that by design it's not possible to reset all state touched by Magnum to previous values — it would involve impractically large amount of queries and state switches with serious performance impact. It's thus expected that third-party code either does no state tracking or provides similar means to reset their state tracker (for example Qt has QQuickWindow::
Extension-dependent functionality
While the majority of Magnum API stays the same on all platforms and driver capabilities, large portion of the functionality needs to be realized under the hood using various different OpenGL API calls based on available extensions. If required extension is not available, there are two possible outcomes — either given API is simply not available or it is emulated using older functionality.
In the first case, to avoid performance overhead, Magnum does not check that you use only the APIs that are implemented in the driver — you are expected to do the checks. Documentation of each type, function and enum value explicitly states whether the functionality is available everywhere or whether particular GL version/extension is required. The information is also aggregated on Version and extension requirements documentation page. Use GL::
GL::TextureFormat format; if(GL::Context::current().isExtensionSupported<GL::Extensions::ARB::depth_buffer_float>()) format = GL::TextureFormat::DepthComponent32F; else format = GL::TextureFormat::DepthComponent24;
Some functionality can be emulated by Magnum — it detects available extensions and selects best possible code path for optimal performance. On startup, the application prints list of extensions that were used to improve the default functionality. The most prominent feature is ARB_
GL::Texture2D texture; /* - on OpenGL 4.5+/ARB_direct_state_access this calls glTextureStorage2D() - if EXT_direct_state_access is available, calls glTextureStorage2DEXT() - on OpenGL 4.2+/ARB_texture_storage and OpenGL ES 3.0+ calls glTexStorage2D() - on OpenGL ES 2.0 with EXT_texture_storage calls glTexStorage2DEXT() - otherwise emulated using a sequence of four glTexImage2D() calls */ texture.setStorage(4, GL::TextureFormat::RGBA8, {256, 256});