The OpenGL Pipeline Newsletter - Volume 003
Using the Longs Peak Object Model
In OpenGL Pipeline #002, Barthold Lichtenbelt gave the high-level design goals and structure of the new object model being introduced in OpenGL Longs Peak. In this issue we'll assume you're familiar with that article and proceed to give some examples using the actual API, which has mostly stabilized. (We're not promising the final Longs Peak API will look exactly like this, but it should be very close.)
Template and Objects
In traditional OpenGL, objects were created, and their parameters (or “attributes,” in our new terminology) set after creation. Calls like glTexImage2D set many attributes simultaneously, while object-specific calls like glTexParameteri set individual attributes.
In the new object model, many types of objects are immutable (can't be modified), so their attributes must be defined at creation time. We don't know what types of objects will be added to OpenGL in the future, but do know that vendor and ARB extensions are likely to extend the attributes of existing types of objects. Both reasons cause us to want object creation to be flexible and generic, which led to the concept of “attribute objects,” or templates.
A template is a client-side object which contains exactly the attributes required to define a “real” object in the GL server. Each type of object (buffers, images, programs, shaders, syncs, vertex arrays, and so on) has a corresponding template. When a template is created, it contains default attributes for that type of object. Attribute values in templates can be changed.
Creating a “real” object in the GL server is done by passing a template to a creation function, which returns a handle to the new object. In order to avoid pipeline stalls, object creation does not force a round-trip to the server. Instead, the client speculatively returns a handle which may be used in future API calls. (If creation fails, future use of that handle will generate a new error, GL_INVALID_OBJECT.)
Example: Creating an Image Object
In Longs Peak, we are unifying the concepts of “images” -- such as textures, pixel data, and render buffers -- and generic “buffers” such as vertex array buffers. An image object is a type (“subclass,” in OOP terminology) of buffer object that adds additional attributes such as format and dimensionality. Here's an example of creating a simple 2D image object. This is not intended to show every aspect of buffer objects, just to show the overall API. For example, the format parameter below is assumed to refer to a GLformat object describing the internal format of an image.
Once image has been created and its contents defined, it can be attached (along with a texture filter object) to a program object, for use by shaders. The contents of the texture, or any rectangular subregion of it, can be changed at any time using glImageData2D.
API Design Concerns
You may have some concerns when looking at this example. In particular, it doesn't look very object-oriented, and it appears verbose compared to the classic glGenTextures() / glBindTexture() / glTexImage2D() mechanism.
While we do have an object-oriented design underlying Longs Peak, we still have to specify it in a traditional C API. This is mostly because OpenGL is designed as a low-level, cross-platform driver interface, and drivers are rarely written in OO languages; nor do we want to lock ourselves down to any specific OO language. We expect more compact language bindings will quickly emerge for C++, C#, Java, and other object-oriented languages, just as they have for traditional OpenGL. But with the new object model design, it will be easier to create bindings which map naturally between the language semantics and the driver behavior.
Regarding verbosity, the new object model gains us a consistent set of design principles and APIs for manipulating objects. It precisely defines behaviors such as object sharing across contexts and object lifetimes, which are sometimes open to interpretation in traditional OpenGL. And it makes defining new object types in the future very easy. But the genericity of the APIs, in particular those required to set the many types of attributes in a template, may look a bit confusing and bloated at first. Consider
glTemplateAttribt_i(template, GL_WIDTH, 256);
First, why the naming scheme? What does t_i mean? Each attribute in a template has a name by which it's identified, and a value. Both the name and the value may be of multiple types. This example is among the simplest: the attribute name is GL_WIDTH (an enumerated token), and the value is a single integer.
The naming convention we're using is glTemplateAttrib<name type>_<value type>. Here, t means the type is an enumerated token, and i means GLint, as always. The attribute name and value are specified consecutively in the parameter list. Using these conventions we can define many other attribute-setting entry points. For example:
glTemplateAttribt_fv - name is a token, value is an array of GLfloat (fv).
glTemplateAttribti_o - name is a (token,index) tuple, value is an object handle. This might be used in specifying attachments of buffer objects to new-style vertex array objects, for example, where many different buffers can be attached, each corresponding to an indexed attribute.
Object Creation Wrappers
Great, you say, but all that code just to create a texture? There are two saving graces.
First, templates are reusable - you can create an object using a template, then change one or two of the attributes in the template and create another object. So when creating a lot of similar objects, there needn't be lots of templates littering the application.
Second, it's easy to create wrapper functions which look more like traditional OpenGL, so long as you only need to change a particular subset of the attributes using the wrapper. For example, you could have a function which looks a lot like glTexImage2D:
GLbuffer gluCreateTexBuffer2D(GLint miplevel, GLint internalformat,
GLsizei width, GLsizei height, GLenum format, GLenum type,
const GLvoid *pixels)
This function would create a format object from internalformat; create an image template from the format object, width, height, and miplevel (which defaulted to 0 in the original example); create an image object from the template; load the image object using format, type, and pixels; and return a handle to the image object.
We expect to provide some of these wrappers in a new GLU-like utility library - but there is nothing special about such code, and apps can write their own based on their usage patterns.
We've only had space to do a bare introduction to using the new object model, but have described the basic creation and usage concepts as seen from the developer's point of view. In future issues, we'll delve more deeply into the object hierarchy, semantics of the new object model (including sharing objects across contexts), additional object types, and how everything goes together to assemble the geometry, attributes, buffers, programs, and other objects necessary for drawing.
OpenGL Spec Editor