From BlenderWiki

Jump to: navigation, search
Note: This is an archived version of the Blender Developer Wiki. The current and active wiki is available on wiki.blender.org.

OpenGL Compatibility

This document reflects the OpenGL Compatibility Layer as used in swiss-cheese branch on 7/6/2012. Further it only reflects the part authored by Jason Wilkins. Alexandr Kuznetsov is responsible for other parts that are not included here.

The compatibility is current a part of the gpu library under source/blender/gpu. I considered putting it in other places but figured it could be changed if anybody had a better idea. The only problem seems to be that several files in intern (such as cycles) should probably be rewritten to use some of these functions, but the levels seem wrong.

To use these functions include GPU_compatibility.h.

#include <GPU_compatibility.h>

This will also include an internal file called gpu_deprecated.h which will re-defined certain deprecated functions as DO_NOT_USE_name (e.g., DO_NOT_USE_glColor4f). This can be disabled by defining GPU_MANGLE_DEPRECATED to 0.

#define GPU_MANGLE_DEPRECATED 0
#include <GPU_compatibility.h>

The intention is to remove gpu_deprecated.h at a later time when it is no longer needed, however it may always be useful since it is easy to accidentally use glColor instead of the new gpuColor.

GPU Safety

An overriding concern of the design of the compatibility layer is "no cleverness." In other words it is dumb. The original OpenGL defines a rather vague domain specific language for graphics. Figuring out the fastest way to do what the programmer wants to do is complicated and requires the code to be "clever." Lots of decisions have to be made even in cases where the decision results in the same answer over and over again.

The compatibility layer puts a tougher burden on the programmer to be explicit and to structure code in a simple way. The purpose is to "protect the fast path", which means the API attempts to provide a fast way to do things and doing it differently requires extra effort. Unfortunately, the "no cleverness" philosophy means that the fast path is still not as simple to program as the original OpenGL, but the implementation of the API itself is simpler (and hopefully faster).

OpenGL is difficult enough to debug when you do something wrong, so another philosophy behind the compatibility layer is to "fail early." By compiling Blender in debug mode with the WITH_GPU_SAFETY and WITH_ASSERT_ABORT options Blender will promptly crash when it detects that a compatibility API has been used incorrectly.

The macro that handles this is GPU_ASSERT which is just a copy of BLI_assert that is only present if WITH_GPU_SAFETY is defined. (Would it be useful to allow this to work in release code?)

GPU_ASSERT(test);

The checks are numerous and could degrade performance. An attempt has been made to make sure that even if WITH_GPU_SAFETY is not defined then the code will not cause an exception. Unfortunately it still has a tendency to put OpenGL in an undefined state.


Utility

const char* gpuErrorString(GLenum err);

This function is identical to gluErrorString.

Vertex Attribute Streams

The glBegin/glEnd paradigm is useful for small amounts of vertexes. This is especially true if the attributes tend to change very frame. Also, Blender contains hundreds of places where glBegin/glEnd are used. Converting all of them to a different style would result in a larger number of regressions. The replacement interface defined below is meant to be "search and replace" compatible.

The functions defined in the following section are roughly identical to the OpenGL function with the same name. The main differences are:

  • They only effect varying vertex attributes and can only be used between gpuBegin/gpuEnd. OpenGL uses these same functions for both uniform vertex attributes so they could be used anywhere. That is an example of OpenGL being too clever.
  • It is not actually illegal to use OpenGL state setting functions between gpuBegin/gpuEnd because they are not implemented using glBegin/glEnd. gpuEnd will call a batch drawing command, so any state changes will take effect for all primitives between the delimiters (even the ones that were specified before the state change).
  • You have to specify the exact kinds of attributes that will be specified beforehand (more on how to do this after this section). It isn't legal to specify an attribute not in the format and it isn't legal to not specify a format you said you would.
  • Only the functions that Blender actually uses have been defined. New functions could be added, but there does not seem to be a need to make dozens of functions that aren't used.

Stream Delimiters

void gpuBegin(GLenum mode);
void gpuEnd(void);

All the following APIs have to appear between a call to gpuBegin and gpuEnd

If the mode is given as GL_NOOP instead of a valid primitive type then no drawing takes place when gpuEnd is called. This is useful because streams specified with gpuBegin/gpuEnd are retained until the next call to gpuBegin or the next change in format (but calling gpuImmediateLock and gpuImmediateUnlock is OK as long as the format does not change).

Varying Colors

RGBA

void gpuColor3f(GLfloat r, GLfloat g, GLfloat b);
void gpuColor3fv(const GLfloat *v);
void gpuColor3ub(GLubyte r, GLubyte g, GLubyte b);
void gpuColor3ubv(const GLubyte *v);
void gpuColor4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
void gpuColor4fv(const GLfloat *v);
void gpuColor4ub(GLubyte r, GLubyte g, GLubyte b, GLubyte a);
void gpuColor4ubv(const GLubyte *v);
void gpuColor4d(GLdouble r, GLdouble g, GLdouble b, GLdouble a);

Same as OpenGL.

"Packed" Color

#define CPACK_BLACK   0x000000
#define CPACK_WHITE   0xFFFFFF
 
#define CPACK_RED     0xFF0000
#define CPACK_GREEN   0x00FF00
#define CPACK_BLUE    0x0000FF
 
#define CPACK_YELLOW  0xFFFF00
#define CPACK_CYAN    0x00FFFF
#define CPACK_MAGENTA 0xFF00FF
 
void gpuColor3x(GLuint rgb);
void gpuColor4x(GLuint rgb, GLfloat a);

Use HTML like shorthand for colors. Makes a lot of color calls readable (i.e. gpuColor3x(CPACK_RED)). If alpha is needed is it given as a float. It was considered making more alpha formats but I believe it may clutter things up for little benefit. Using 3 and 4 instead of 1 and 2 is meant to parallel gpuColor and reflects the number of components that are set, not the number of arguments to the function.

Gray

void gpuGray3f(GLfloat luminance);
void gpuGray4f(GLfloat luminance, GLfloat alpha);

Giving identical red, green, and blue components is very common. Luminance and alpha are given as floats. The justification for the function parameters and name are the same as for gpuColorNx. I'd also note that consistently using a floating point number for opacity and brightness is more readable.

Varying Normals

void gpuNormal3f(GLfloat x, GLfloat y, GLfloat z);
void gpuNormal3fv(const GLfloat *v);
void gpuNormal3sv(const GLshort *v);

Same as OpenGL.

Varying UVs

void gpuTexCoord2f(GLfloat s, GLfloat t);
void gpuTexCoord2fv(const GLfloat *v);
void gpuTexCoord2iv(const GLint *v);
void gpuTexCoord3f(const GLfloat s, const GLfloat t, const GLfloat u);
void gpuTexCoord3fv (const GLfloat *v);

Same as OpenGL.

Multiple Varying UVs

void gpuMultiTexCoord2f(GLint index, GLfloat s, GLfloat t);
void gpuMultiTexCoord2fv(GLint index, const GLfloat *v);
void gpuMultiTexCoord3fv(GLint index, const GLfloat *v);
void gpuMultiTexCoord4fv(GLint index, const GLfloat *v);

These would be the same as OpenGL except that the index parameter is not a GLenum such as GL_TEXTURE3, but an index into a map that has been set up by indexing functions. This system of setting up these map has turned out to be a little difficult to use. These functions may turn out to not be needed, but I've kept it for now. More on how to set up the texture unit map in a later section.

Varying Generic Attributes

Generic vertex attributes for GLSL are similar to multi-texture coordinates in that the index is not the name of the vertex attribute, but an index into a map that contains the vertex attribute name. This allows an arbitrary set of attributes to be set in a format, but adds a layer of complexity to setting up vertex attributes. Unlike texture coordinates however this bit of complexity probably cannot be factored out.

Floating point and byte attributes have separate maps. The separation is a trade off. I judged it to make the code the user writes and the code in the compatibility layer both simpler. It may take a little getting used to though.

Float Attributes

void gpuVertexAttrib2fv(GLsizei index, const GLfloat *v);
void gpuVertexAttrib3fv(GLsizei index, const GLfloat *v);
void gpuVertexAttrib4fv(GLsizei index, const GLfloat *v);

Byte Attributes

void gpuVertexAttrib4ubv(GLsizei index, const GLubyte *v);

Only really used for GLSL color attributes.

Coordinates

void gpuVertex2f(GLfloat x, GLfloat y);
void gpuVertex2fv(const GLfloat *v);
void gpuVertex3f(GLfloat x, GLfloat y, GLfloat z);
void gpuVertex3fv(const GLfloat *v);
void gpuVertex3d(GLdouble x, GLdouble y, GLdouble z);
void gpuVertex3dv(const GLdouble *v);
void gpuVertex2i(GLint x, GLint y);
void gpuVertex2iv(const GLint *v);
void gpuVertex2sv(const GLshort *v);

These are the same as OpenGL in that they trigger all the current vertex attributes to be copied into current index of the vertex array and the index incremented. It is debatable if it would be more efficient to abandon this one aspect of OpenGL for a more direct approach of having each attribute copy its value directly into the array. Such code is indeed less clever, but it is also much more dangerous and is not "search and replace" compatible.

If we do this we give up:

  • Duplicating colors and normals across primitives without specifying them multiple times.
  • Automatically extending 2 and 3 dimensional values with (0,1)
  • Using a 4f function instead of a 3f function would cause corruption.

All of these could be checked for by WITH_GPU_SAFETY but there are many regressions to be fixed (mostly the first feature).

Retained Geometry

After executing gpuEnd the contents of the built up vertex array are not deleted. This gives us the opportunity to draw the contents again.

Draw

void gpuDraw(GLenum mode);
void gpuDrawElements(GLenum mode);
void gpuDrawRangeElements(GLenum mode);

These functions will draw the contents of the stored buffer using the given mode. The gpuDrawElements/gpuDrawRangeElements require an index be specified using gpuImmediateIndex which is explained below.

If gpuBegin(GL_NOOP) is specified then calling gpuEnd will not draw anything. The purpose of this is to then be able to repeatedly redraw the contents using gpuDraw.

Repeat Previous Draw Command

void gpuRepeat(void);
void gpuRepeatElements(void);
void gpuRepeatRangeElements(void);

Since the mode is stored, using gpuRepeat lets you repeat the drawing without saying the mode again. Unfortunately you still need to remember whether there was an index or not. There are some situations where this is needed to make the code more maintainable, but I'm not sure if it is "too clever" or not.

Uniform Vertex Attributes

One of the trickier aspects of the compatibility layer is the distinction between specifying a uniform or varying vertex attribute. Practically however, the only place this actually comes up is with color. Outside of gpuBegin/gpuEnd one cannot use gpuColor. Behind the sense gpuColor deals exclusively with copying a color to an array. However, when there is no color in the current vertex format, the uniform color value is used instead. The uniform color is set by using variations on gpuCurrentColor. (Maybe this should be called gpuUniformColor?)

Color

RGBA

Same as OpenGL, but only used outside of gpuBegin/gpuEnd.

void gpuCurrentColor3f(GLfloat r, GLfloat g, GLfloat b);
void gpuCurrentColor3fv(const GLfloat *v);
void gpuCurrentColor3ub(GLubyte r, GLubyte g, GLubyte b);
void gpuCurrentColor3ubv(const GLubyte *v);
void gpuCurrentColor4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
void gpuCurrentColor4fv(const GLfloat *v);
void gpuCurrentColor4ub(GLubyte r, GLubyte g, GLubyte b, GLubyte a);
void gpuCurrentColor4ubv(const GLubyte *v);
void gpuCurrentColor4d(GLdouble r, GLdouble g, GLdouble b, GLdouble a);

"Packed" Colors

See gpuColorNx for details.

void gpuCurrentColor3x(GLuint rgb);
void gpuCurrentColor4x(GLuint rgb, GLfloat a);
 
void gpuCurrentGray3f(GLfloat luminance);
void gpuCurrentGray4f(GLfloat luminance, GLfloat alpha);

Color Read

This function reads the cached uniform color (at least it should do that). This means it should be faster than using glGetFloatv(GL_CURRENT_COLOR, v). One thing that WITH_GPU_SAFETY does is that it enforces the fact that the current color is undefined after drawing something that uses varying colors. So the value is only defined after a call to gpuCurrentColor with no intervening drawing that uses a color attribute array.

void gpuGetCurrentColor4fv(GLfloat *color);
void gpuGetCurrentColor4ubv(GLubyte *color);

Color Read/Write

These are shortcuts that let us only vary the alpha without changing the color. Basically just a utility that reduces three lines of code to just one.

void gpuCurrentAlpha(GLfloat a);
void gpuMultCurrentAlpha(GLfloat factor);

Uniform Normal

There is an isolated case of the current normal being used. This lone exception will probably be worked around.

void gpuCurrentNormal3fv(const GLfloat *v);

Vertex Attribute Stream Format

The biggest complication to programming the compatibility layer is that the exact format of the data about to be sent needs to be specified. Deprecated implementation of OpenGL glBegin/glEnd could never be sure what the programmer would send because any particular format might be needed because at any point a new kind of data might be sent.

This is unfortunate since 99% of the time it is possible to statically determine exactly what will be needed before sending anything. The ideal is to specify a format that can be used by as many gpuBegin/gpuEnd pairs as possible. In fact it is probably better, for example, to send a few extra colors than to constantly change between two formats where one has colors and the other does not.

Quick Formats

void gpuImmediateFormat_V2(void);
void gpuImmediateFormat_C4_V2(void);
void gpuImmediateFormat_T2_V2(void);
void gpuImmediateFormat_T2_C4_V2(void);
void gpuImmediateFormat_V3(void);
void gpuImmediateFormat_N3_V3(void);
void gpuImmediateFormat_C4_V3(void);
void gpuImmediateFormat_C4_N3_V3(void);
void gpuImmediateFormat_T2_C4_N3_V3(void);
void gpuImmediateFormat_T3_C4_V3(void);
 
void gpuImmediateUnformat(void);

The following format functions cover the vast majority of situations in Blender. Each of the Format functions sets the format so that it can handle the format spelled out in its suffix. V means vertex components, C means color components (which is always 4), N means normal components (which is always 3), and T means texture coordinate components.

For formats with textures there is only the singular GL_TEXTURE0 texture unit. There are no utility formats for vertex attributes or multi-texture, but in those situations a dynamic vertex format is needed anyway.

When all drawing calls that require the format are completed gpuImmediateUnformat is called.

For most people consulting this I'd suggest you skip the rest of the vertex formatting section because you really don't need more than the above functions :)

Buffer Size

void gpuImmediateMaxVertexCount(GLsizei maxVertexCount);

For gpuBegin/gpuEnd<tt> this number determines how many vertexes the buffer will be allocate to hold. The actual size of the buffer is determined on <tt>gpuImmediateLock and is based on the actual size of the vertex. This means that asking for a larger format may actually cause the buffer to be reallocated.

WITH_GPU_SAFETY does not care if the buffer size is exceeded unless the primitive type is GL_NOOP (more on that in the retained mode section). If a regular primitive type is specified a gpuEnd/gpuBegin is performed to flush and restart the buffer. (Note that this is not done cleanly in the current implementation and primitives may be corrupted, but the buffer size for regular drawing is pretty large so this doesn't happen).

Coordinate/Normal/Color Format

void gpuImmediateElementSizes(GLint vertexSize, GLint normalSize, GLint colorSize);

When not using a quick format, this function gives the number of components for position, normals, and color. (I thought it would be convenient to have this be one function, but it may need to be split into 3 separate functions to be convenient in some situations).

Texture Coordinate Format

void gpuImmediateTextureUnitCount(size_t count);
void gpuImmediateTexCoordSizes(const GLint *sizes);
void gpuImmediateTextureUnitMap(const GLenum *map);

When not using a quick format, these functions setup the texture coordinate map. This is needed because we cannot assume that the first, second, and third texture coordinates in a vertex format are GL_TEXTURE0, GL_TEXTURE1, and GL_TEXTURE2. To set that up we need to send an array { GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2 } or whatever other mapping we need with gpuImmediateTextureUnitMap. Similarly an array of the number of components for each coordinate is given with gpuImmediateTexCoordSizes

Float Attribute Format

void gpuImmediateFloatAttribCount(size_t count);
void gpuImmediateFloatAttribSizes(const GLint *sizes);
void gpuImmediateFloatAttribIndexMap(const GLuint *map);

When not using a quick format, the story here is similar to that for texture units. There may be a need for a gpuImmediateFloatAttribNormalize but Blender does not use a non-default setting for vertex attribute normalization yet.

Byte Attribute Format

void gpuImmediateUbyteAttribCount(size_t count);
void gpuImmediateUbyteAttribSizes(const GLint *sizes);
void gpuImmediateUbyteAttribIndexMap(const GLuint *map);

When not using a quick format, similar to float attributes except need to note that byte attributes are separate because the complication of being able to treat float and byte attributes all in the same map does not seem to be justified. However it is pretty easy to get confused when managing float and byte attributes, but I think that is something to just get used to.

Reset

void gpuImmediateFormatReset(void);

When not using a quick format, sets the format back to defaults (3 vertex components and nothing else).

Lock/Unlock

void gpuImmediateLock(void);
void gpuImmediateUnlock(void);
GLint gpuImmediateLockCount(void);

When not using a quick format, after setting up a vertex format call gpuImmediateLock before drawing anything and call gpuImmediateUnlock when done. It is an error to lock if things are already locked. This restriction may be relaxed because it makes it difficult to know where to put all the locks. However, such a solution may be "too clever" because it lets programmers be sloppy at the expense of efficiency.

gpuImmediateLockCount is useful in situations where one does want to tolerate a little sloppiness for convenience. It allows one to check if things are already locked before setting a vertex format. This has been used with font rendering to allow for a preemptive locking before drawing a large amount of text without forcing every place that uses font drawing to have to add locks.

The vast majority of cases do not use the low level format functions or lock functions, instead they use Format/Unformat.

Vertex Attribute Stream Objects

GPUimmediate* gpuNewImmediate(void);
void gpuImmediateMakeCurrent(GPUimmediate * immediate);
void gpuDeleteImmediate(GPUimmediate * immediate);

Note that the name "Immediate" used so far in this documentation probably needs to change to "Geometry" or "Stream" because the data structure evolved into one that can actually retain geometry for repeated use.

If we view GPUimmediate as an emulator for the deprecated OpenGL immediate mode then these are initialization and shutdown functions needed to manage the state of the immediate mode functions. In that case they are only needed at startup and shutdown. This interface also envisions a time that GHOST might need to do some context management.

However, due to some new functions that will be explained below these functions can also be used to create new places to save and restore geometry that has been specified and stored to be used over and over like a display list.

Retained Vertex Stream Utilities

void gpuPushImmediate(void);
GPUimmediate* gpuPopImmediate(void);
 
void gpuImmediateSingleDraw(GLenum mode, GPUimmediate *immediate);
void gpuImmediateSingleRepeat(GPUimmediate *immediate);
 
void gpuImmediateSingleDrawElements(GLenum mode, GPUimmediate *immediate);
void gpuImmediateSingleRepeatElements(GPUimmediate *immediate);
 
void gpuImmediateSingleDrawRangeElements(GLenum mode, GPUimmediate *immediate);
void gpuImmediateSingleRepeatRangeElements(GPUimmediate *immediate);

gpuPushImmediate/gpuPopImmediate are convenience functions that could be implemented just by saving the current immediate state to a temporary and then restoring it later (but there is no gpuGetImmediate so this will have to do). The purpose is to replace display lists that contain only geometry. A new immediate context can be created, filled with geometry, and then saved.

The next time the same thing needs to be draw again, one of the gpuImmediateSingle functions is called. These functions will switch to the temporary immediate, draw its contents, and then switch back.

Primitive Indexes

Indexed primitives are also supported by this interface. An index has to be created and attached to the current immediate context and then filled with indexes.

Index Objects

GPUindex* gpuNewIndex(void);
void gpuDeleteIndex(GPUindex *index);
void gpuImmediateIndex(GPUindex * index);

The life cycle of an index is to be created using gpuNewIndex>, attached to and detached from the current immediate context using gpuImmediateIndex and then finally deleted using gpuDeleteIndex. It is an error to delete an index that is attached. To detach an index pass NULL to gpuImmediateindex.

Index Properties

void gpuImmediateMaxIndexCount(GLsizei maxIndexCount);
void gpuImmediateIndexRange(GLuint indexMin, GLuint indexMax);
void gpuImmediateIndexComputeRange(void);
void gpuImmediateIndexRestartValue(GLuint restart);

Like the vertex buffer of the immediate context, an index buffer starts off as zero size and needs to be given a larger size before it can be used. This is done by using gpuImmediateMaxIndexCount.

The remaining functions here are not really used outside of the gpu library yet.

Index Specification

Delimiters

void gpuIndexBegin(void);
void gpuIndexEnd(void);

Similar to gpuBegin/gpuEnd, the gpuIndexBegin/gpuIndexEnd mark the boundaries of when functions that specify the contents of the index buffer. Although there is no particular reason not to allow it, it is forbidden to interleave gpuBegin/gpuEnd and gpuIndexBegin/gpuIndexEnd calls.

Note that it makes the most sense to use gpuBegin(GL_NOOP) when specifying vertexes for an indexed primitive. That way nothing will be drawn until calling gpuDrawElements.

Indexes

void gpuIndexRelativev(GLint offset, GLsizei count, const void *indexes);
void gpuIndex(GLuint index);

The gpuIndex function is straight forward, it lets you specify an index and increments iterator to the next index.

However, gpuIndexRelativev requires a little more explaining. It is meant for allowing an constant array of index values to refer to some set of previously specified vertexes. For example, one could specify a pair of triangles in a quad with a constant array of 6 indexes, and by changing offset and count the gpuIndexRelativev function will calculate the correct indexes even as more items were added to the index buffer. (I'm too lazy to write exactly how this is done right now).

Primitive Restart

void gpuIndexRestart(void);

Not implemented right now, but has to do with the GL_ARB_primitive_restart extension.

Client Side Vertex Arrays

extern const GPUarrays GPU_ARRAYS_V2F;
extern const GPUarrays GPU_ARRAYS_C4UB_V2F;
extern const GPUarrays GPU_ARRAYS_V3F;
extern const GPUarrays GPU_ARRAYS_C3F_V3F;
extern const GPUarrays GPU_ARRAYS_C4F_V3F;
extern const GPUarrays GPU_ARRAYS_N3F_V3F;
extern const GPUarrays GPU_ARRAYS_C3F_N3F_V3F;
 
void gpuAppendClientArrays(const GPUarrays* arrays,	GLint first, GLsizei count);
void gpuDrawClientArrays(GLenum mode, const GPUarrays *arrays, GLint first, GLsizei count);
void gpuDrawClientRangeElements(GLenum mode,const GPUarrays *arrays,GLuint indexMin,GLuint indexMax,GLsizei count,const void *indexes);

Client side vertex arrays are arrays that need to be copied before they can be drawn. Rather than duplicate all the functions for setting up state needed to do this copying I've created a data structure, GPUarrays to hold all this data. Since most of the formats are reused multiple times I have also created constant global variables so that most of the variables can be copied with a single statement.

Hopefully the suffixes of the pre-made client array types are self explanatory. The code ends up looking like the following:

GPUarray array = GPU_ARRAYS_N3F_V3F;
array.vertexPointer = v;
array.normalPointer = n;
array.normalStride = 42;
gpuDrawClientArrays(GL_TRIANGLES, &array, ...);

If there is no stride then all that needs to be set up is the pointers. The result is cleaner than just blindly copying the original vertex array interface. However, even this interface only needs to be used in cases where things are more complicated. The utility functions below are even cleaner.

Note: The difference between Single, Draw, and Append will be made clearer when I explain the primitive geometry drawing functions.

Quick Client Side Vertex Array Utilities

void gpuSingleClientArrays_V2F(GLenum mode,const void *vertexPointer,GLint vertexStride,GLint first,GLsizei count);
void gpuSingleClientArrays_V3F(GLenum mode,const void *vertexPointer,GLint vertexStride,GLint first,GLsizei count);
void gpuSingleClientArrays_C3F_V3F(GLenum mode,const void *colorPointer,GLint colorStride,const void *vertexPointer,GLint vertexStride,GLint first,GLsizei count);
void gpuSingleClientArrays_C4F_V3F(GLenum mode,const void *colorPointer,GLint colorStride,const void *vertexPointer,GLint vertexStride,GLint first,GLsizei count);
void gpuSingleClientArrays_N3F_V3F(GLenum mode,const void *normalPointer,GLint normalStride,const void *vertexPointer,GLint vertexStride,GLint first,GLsizei count);
void gpuSingleClientArrays_C3F_N3F_V3F(GLenum mode,const void *colorPointer,GLint colorStride,const void *normalPointer,GLint normalStride,const void *vertexPointer,GLint vertexStride,GLint first,GLsizei count);
void gpuSingleClientArrays_C4UB_V2F(GLenum mode,const void *colorPointer,GLint colorStride,const void *vertexPointer,GLint vertexStride,GLint first,GLsizei count);
void gpuSingleClientElements_V3F(GLenum mode,const void *vertexPointer,GLint vertexStride,GLsizei count,const void *index);
void gpuSingleClientElements_N3F_V3F(GLenum mode,const void *normalPointer,GLint normalStride,const void *vertexPointer,GLint vertexStride,GLsizei count,void *indexes);
void gpuSingleClientRangeElements_V3F(GLenum mode,const void *vertexPointer,GLint vertexStride,GLuint indexMin,GLuint indexMax,GLsizei count,const void *indexes);
void gpuSingleClientRangeElements_N3F_V3F(GLenum mode,const void *normalPointer,GLint normalStride,const void *vertexPointer,GLint vertexStride,GLuint indexMin,GLuint indexMax,GLsizei count,const GLvoid *indexes);

This functions handle locking, unlocking, setting up the GPUarray data structure all in a single function call. They look kind of complicated but they replace a lot of code each place they are used. Note that the Single prefix denotes the notion that these functions lock and unlock and therefore are very heavy. They cannot be used where the immediate context is already locked.

Quadrics

void gpuAppendCone(GPUprim3 *prim3, GLfloat radius, GLfloat height);
void gpuDrawCone(GPUprim3 *prim3, GLfloat radius, GLfloat height);
void gpuSingleCone(GPUprim3 *prim3, GLfloat radius, GLfloat height);
 
void gpuAppendCylinder(GPUprim3 *prim3, GLfloat radiusBase, GLfloat radiusTop, GLfloat height);
void gpuDrawCylinder(GPUprim3 *prim3, GLfloat radiusBase, GLfloat radiusTop, GLfloat height);
void gpuSingleCylinder(GPUprim3 *prim3, GLfloat radiusBase, GLfloat radiusTop, GLfloat height);
 
void gpuAppendSphere(GPUprim3 *prim3, GLfloat radius);
void gpuDrawSphere(GPUprim3 *prim3, GLfloat radius);
void gpuSingleSphere(GPUprim3 *prim3, GLfloat radius);

These functions replace the GLUquadric library. Parameters that would normally be specified using GLU functions are handled by filling in a GPUprim3 (3 for 3D primitive, although there isn't one for 2D primitives...).

Here is the first chance to really explain the Append/Draw/Single naming convention. Append means that gpuVertex/gpuNormal will be called (there will not even be a gpuBegin/gpuEnd). This allows for many calls to be "appended" into a single buffer. The purpose is to allow for large amounts of primitives to exist in a single buffer. Draw just adds gpuBegin/gpuEnd and is meant to be used multiple times inside a locked region. And finally, Single does a full lock and unlock in a single call.

Lines

void gpuAppendLinef(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
void gpuAppendLinei(GLint x1, GLint y1, GLint x2, GLint y2);
 
void gpuDrawLinef(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
void gpuDrawLinei(GLint x1, GLint y1, GLint x2, GLint y2);
 
void gpuSingleLinef(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
void gpuSingleLinei(GLint x1, GLint y1, GLint x2, GLint y2);

The Single/Draw versions of these functions assume you want GL_LINES.

Rectangles

Simple boxes given by a top, bottom, left and right coordinate.

Filled

void gpuAppendFilledRectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
void gpuAppendFilledRecti(GLint x1, GLint y1, GLint x2, GLint y2);
 
void gpuSingleFilledRectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
void gpuSingleFilledRecti(GLint x1, GLint y1, GLint x2, GLint y2);
 
void gpuDrawFilledRectf(GLfloat x1,GLfloat y1,GLfloat x2,GLfloat y2);
void gpuDrawFilledRecti(GLint x1,GLint y1,GLint x2,GLint y2);

Filled rectangles are drawn using two triangles because glRect and GL_QUADS are deprecated. The Single/Draw versions assume you want to use GL_TRIANGLES.

Note that gpuBegin still takes GL_QUADS for now, but these functions are forward thinking.

Wire

void gpuAppendWireRectf(GLfloat x1,GLfloat y1,GLfloat x2,GLfloat y2);
void gpuAppendWireRecti(GLint x1,GLint y1,GLint x2,GLint y2);
 
void gpuDrawWireRectf(GLfloat x1,GLfloat y1,GLfloat x2,GLfloat y2);
void gpuDrawWireRecti(GLint x1,GLint y1,GLint x2,GLint y2);
 
void gpuSingleWireRectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
void gpuSingleWireRecti(GLint x1, GLint y1, GLint x2, GLint y2);

The Single/Draw versions assume you want to use GL_LINES. It does not use GL_LINE_LOOP because you cannot append multiple line loops into a single buffer.

Circles

void gpuAppendArc(GLfloat x,GLfloat y,GLfloat start,GLfloat angle,GLfloat xradius,GLfloat yradius,GLint nsegments);
void gpuDrawArc(GLfloat x,GLfloat y,GLfloat start,GLfloat angle,GLfloat xradius,GLfloat yradius,GLint nsegments);
void gpuSingleArc(GLfloat x,GLfloat y,GLfloat start,GLfloat angle,GLfloat xradius,GLfloat yradius,GLint nsegments);
 
void gpuAppendFastCircleXZ(GLfloat radius);
void gpuDrawFastCircleXZ(GLfloat radius);
void gpuSingleFastCircleXZ(GLfloat radius);
 
void gpuAppendFastCircleXY(GLfloat radius);
void gpuDrawFastCircleXY(GLfloat radius);
void gpuSingleFastCircleXY(GLfloat radius);
 
void gpuAppendFastBall(const GLfloat position[3],float radius,const GLfloat matrix[4][4]);
void gpuDrawFastBall(int mode,const GLfloat position[3],float radius,const GLfloat matrix[4][4]);
void gpuSingleFastBall(int mode,const GLfloat position[3],float radius,const GLfloat matrix[4][4]);
 
void gpuAppendSpiral(const GLfloat position[3],float radius,const GLfloat matrix[4][4],int start);
void gpuDrawSpiral(const GLfloat position[3],GLfloat radius,GLfloat matrix[4][4],int start);
void gpuSingleSpiral(const GLfloat position[3],GLfloat radius,GLfloat matrix[4][4],int start);
 
void gpuAppendDisk(GLfloat x, GLfloat y, GLfloat radius, GLint nsectors);
void gpuDrawDisk(GLfloat x, GLfloat y, GLfloat radius, GLint nsectors);
void gpuSingleDisk(GLfloat x, GLfloat y, GLfloat radius, GLint nsectors);
 
void gpuAppendEllipse(GLfloat x,GLfloat y,GLfloat xradius,GLfloat yradius,GLint nsegments);
void gpuDrawEllipse(GLfloat x,GLfloat y,GLfloat xradius,GLfloat yradius,GLint nsegments);
void gpuSingleEllipse(GLfloat x,GLfloat y,GLfloat xradius,GLfloat yradius,GLint nsegments);
 
void gpuAppendCircle(GLfloat x,GLfloat y,GLfloat radius,GLint nsegments);
void gpuDrawCircle(GLfloat x,GLfloat y,GLfloat radius,GLint nsegments);
void gpuSingleCircle(GLfloat x,GLfloat y,GLfloat radius,GLint nsegments);

These could all be replaced by a single elliptical arc drawing function, however circle drawing is so prevalent in Blender that several different specialized methods make it more efficient (especially for particles). Of particular note are the "Fast" versions that have a preset number of segments and use a lookup table instead of the sin function. (All of these should eventually use a look-up table because none of them use a dynamic number of segments!).

These use GL_LINES instead of GL_LINE_LOOP because it is probably more efficient to send larger buffers than to make an API call for each individual circle. Testing is needed.

Sprites

void gpuBeginSprites(void);
void gpuSprite3fv(const GLfloat v[3]);
void gpuSprite3f(GLfloat x, GLfloat y, GLfloat z);
void gpuSprite2f(GLfloat x, GLfloat y);
void gpuSprite2fv(const GLfloat vec[2]);
void gpuEndSprites(void);


Point drawing that is not point sprite drawing has been deprecated. For that reason I've created a special interface for drawing GL_POINTS. Currently this is just a renaming of the bglBegin/bglEnd functions, but eventually should cover all point drawing in Blender with an interface that can be implemented using non-deprecated functionality.

Cubes

void gpuSingleWireUnitCube(void);
void gpuSingleWireCube(GLfloat size);

Just factored out a simple cube drawing routine and gave it a public interface.