Using Vertex Array Objects
With my recent post about what iOS devices supported what extensions, I noticed a lot of people got to my website trying to work out how to use Vertex Array Objects (VAO) with the GL_OES_vertex_array_object extension.
They're easy to use so I thought I would write up a quick how-to use them. There is a limitation with the VAO however, it cannot be shared between contexts and can be problematic for multithreading.
If you plan to use one, either generate and create for each context you will be using it on. Or generate and create on the context you will be mostly using it on. It might be a mistake, since it says they didn't want to create the first non-shared named object in OpenGL ES, however at the same time they say no to sharing.
Like with any OpenGL object, first a name must be generated for the object, make sure you always initialise your name variables to 0, since this refers to a non-object.
GLuint gVAO = 0;
//
//
int main()
{
//
GenerateVAO();
CreateVAO();
//
do
{
BindVAO();
DrawSomething();
UnbindVAO();
}
while( 1 );
//
DestroyVAO();
}
//
//
void GenerateVAO()
{
// Generate Name for VAO
glGenVertexArraysOES( 1, &gVAO );
}
//
//
void DestroyVAO()
{
if( gVAO )
{
glDeleteVertexArraysOES( 1, &gVAO );
gVAO = 0;
}
}I've also put the syntax in there for deleting the VAO when you do not need it anymore.
Now for filling in the VAO. The VAO is basically a block of memory for storing vertex data schematic. For fixed function, the schematic includes information about data type, count, stride, buffer offset and whether it is enabled. This is for every pointer, glVertexPointer, glNormalPointer, glColorPointer, glTexCoordPointer, etc.
And for the shader program pipeline, it contains the schematic for each glVertexAttribPointer.
//
//
void CreateVAO()
{
// Bind VAO
glBindVertexArrayOES( gVAO );
// Bind Vertex Buffer
glBindBuffer( GL_ARRAY_BUFFER, gSomeVBO );
// Fill in the VAO with our vertex schematic
if( hasShaders )
{
glEnableVertexAttribArray( 0 );
glVertexAttribPointer( 0, // Attribute bind location
3, // Data type count
GL_FLOAT, // Data type
GL_FALSE, // Normalise this data types?
sizeof(float) * 3, // Stride to the next vertex
0 ); // Vertex Buffer starting offset
}
else
{
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 3, // Data type count
GL_FLOAT, // Data type
sizeof(float) * 3, // Stride to the next vertex
0 ); // Vertex Buffer starting offset
}
// Unbind VAO
glBindVertexArrayOES( 0 );
}
You'll notice that when I am finished with with the VAO, I unbind it. This is because any changes to the vertex pointers will override any we have set within this function, so to protect the VAO we have just filled in, we unbind it.
When using a VAO, you will not need to bind the GL_ARRAY_BUFFER again the next time you use the VAO, since whenever you make a call to gl*Pointer() or glVertexAttribPointer(), the vertex buffer is stored with the vertex schematic. Because of this, you can have multiple streams of data from different vertex buffers.
//
//
void CreateVAO()
{
// Bind VAO
glBindVertexArrayOES( gVAO );
// Vertex Buffer 1
{
// Bind Vertex Buffer
glBindBuffer( GL_ARRAY_BUFFER, gSomeVBO1 );
// Fill in the VAO with our vertex schematic
if( hasShaders )
{
//
glEnableVertexAttribArray( 0 );
glVertexAttribPointer( 0, // Attribute bind location
3, // Data type count
GL_FLOAT, // Data type
GL_FALSE, // Normalise this data types?
sizeof(float) * 5, // Stride to the next vertex
0 ); // Vertex Buffer starting offset
//
glEnableVertexAttribArray( 1 );
glVertexAttribPointer( 1, // Attribute bind location
2, // Data type count
GL_FLOAT, // Data type
GL_FALSE, // Normalise this data types?
sizeof(float) * 5, // Stride to the next vertex
sizeof(float) * 3 ); // Vertex Buffer starting offset
}
else
{
//
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 3, // Data type count
GL_FLOAT, // Data type
sizeof(float) * 5, // Stride to the next vertex
0 ); // Vertex Buffer starting offset
//
glClientActiveTexture( GL_TEXTURE0 );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glTexCoordPointer( 2, // Data type count
GL_FLOAT, // Data type
sizeof(float) * 5, // Stride to the next vertex
(const GLvoid *)(sizeof(float) * 3) ); // Vertex Buffer starting offset
}
}
// Vertex Buffer 2
{
// Bind Vertex Buffer
glBindBuffer( GL_ARRAY_BUFFER, gSomeVBO2 );
// Fill in the VAO with our vertex schematic
if( hasShaders )
{
glEnableVertexAttribArray( 2 );
glVertexAttribPointer( 2, // Attribute bind location
2, // Data type count
GL_FLOAT, // Data type
GL_FALSE, // Normalise this data types?
sizeof(float) * 2, // Stride to the next vertex
0 ); // Vertex Buffer starting offset
}
else
{
glClientActiveTexture( GL_TEXTURE1 );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glTexCoordPointer( 2, // Data type count
GL_FLOAT, // Data type
sizeof(float) * 2, // Stride to the next vertex
0 ); // Vertex Buffer starting offset
}
}
// Unbind VAO
glBindVertexArrayOES( 0 );
}
In the code above, the first texture coordinate set is interlaced with the position data (offset by the sizeof three floats), and is bound using the first vertex buffer (gSomeVBO1). But the second texture coordinate is contained within a second vertex buffer (gSomeVBO2).
Finally, using the VAO is easy.
//
//
void BindVAO()
{
glBindVertexArrayOES( gVAO );
}
//
//
void UnbindVAO()
{
glBindVertexArrayOES( 0 );
}The call to glBindVertexArrayOES() with the VAO object name is just like calling everything we did in the create function. The only limitation is that one VAO is needed per vertex buffer, even if you have the same vertex schematic.
However, this is a great place for optimisation by sharing the same vertex buffer for vertex data with the same schematic. Then whenever you make a call to glDrawArrays() you can specify which vertex to start from. glDrawElements() is a little more complicated, you will need to increase all your elements in your index buffer so they point to the correct position within the shared buffer.
Or alternatively for glDrawElements(), you could share the same index buffer, and just specify the index buffer offset.
Website Updates
I made some updates to a couple of entries I did a while ago. Fixed an error in my syntax where I calculated the reciprocal, but then divided by the reciprocal instead of multiplying.
I also changed the entries to show equivalent functions instead of my bad descriptions of what the parameters do.
iOS OpenGL ES Extension Support
This weekend I was adding improved extension support to the Athena engine for iOS. There is a page on the Apple site that tells what device has what extensions but it hasn't been updated since iOS 4.2.
So I ran through each version of GLES on a 1st Generate iPod Touch (MBX), an iPhone 4 (SGX) and an iPad 2 (A5), printing the returned string from glGetString( GL_EXTENSIONS ) for each device.
There are a some new extensions in iOS5, some of which are iPad2 specific. But you can see from the image, each newer device supports all the previous extensions.
I haven't had chance to run it on an iPad1 yet, so it will be interesting to see if there is any difference. I've only wrote a rough idea of what version of what SDK is required to use them.
Anything with '***' does not need the extension to work and has an implementation for that version of OpenGL ES.
For more information on each extension, most of them can be viewed here. Some of the newer ones in the EXT namespace are viewable on the what's new page of the beta for iOS5, though the page calls them by the APPLE namespace still.
iOS Multithreading OpenGL
I've been meaning to do this in my engine for a while, but just never had a time.
Multithreading in OpenGL is simple enough as long as you are not accessing the same object as something on another thread.
In my case, I'm only using it for my loading thread so I will not be accessing any of the objects created until loading is complete, and I will not be rendering on the separate thread either, at least not yet, I could use it for render targets.
So first of all, the creation of a OpenGL context on iOS. The examples I'm writing here are just simple main() loops so simplify what has to be done.
int main()
{
EAGLContext * mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
// Make context active for this thread
[EAGLContext setCurrentContext:mContext];
//
while( 1 )
{
// do some rendering
}
//
return 0;
}Simple enough, this code most people who have written any iPhone OpenGL apps will recognise, it creates an ES2 context and bind it to the active thread.
However, now we want a separate thread that does OpenGL commands. We could use the same context, but a call binding a buffer on one thread could override to bind on the other causing invalid or wrong calls, so we would need to synchronise the threads.
Instead we create a second context that shares objects with our first one.
//
//
EAGLContext * mMainContext = nil;
EAGLContext * mThreadContext = nil;
pthread_attr_t mThreadAttributes;
pthread_t mThread;
//
//
int main()
{
mMainContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
mThreadContext = [[EAGLContext alloc] initWithAPI:[mMainContext API]
sharegroup:[mMainContext sharegroup]];
// Make context active for this thread
[EAGLContext setCurrentContext:mMainContext];
// Create second thread for OpenGL stuff
pthread_attr_init( &mThreadAttributes );
pthread_create( &mThread,
&mThreadAttributes,
OGLThread,
NULL );
//
while( 1 )
{
// do some rendering
}
//
return 0;
}
//
//
void * OGLThread( void * inParam )
{
//
[EAGLContext setCurrentContext:mThreadContext];
//
while( loadingSomething )
{
// other opengl commands
}
// Flush changes
glFlush();
//
return 0;
}With this second context, any calls to binding objects to the context will not affect the other thread's context. the [API] just makes it so that I'm using the same ES version as the first context, the [sharegroup] is the shared resource group that will be used by both contexts.
However you must take care not to update an object that is in use in the main thread, and whenever you make a change that will affect the other thread, call glFlush() on the thread that modified, and then rebind on the other thread so the changes take place.
In my own engine I hold shadow states of all options set, so one problem with this system would be I would need to pass in the Context depending on the thread. Instead I wrapped a class around the context that holds the shadow states and the context, as well as a pthread_t object for the thread it is assigned to.
Then whenever I do anything that will alter my shadow states, I make a call to pthread_self() to get the current thread, and find the relevant context.
struct MyContext
{
EAGLContext * context;
ShadowStates states;
pthread_t thread;
};
MyContext mMainContext;
MyContext mThreadContext;
MyContext * GetCurrentContext()
{
int ret = pthread_equal( pthread_self(), mMainContext.thread );
if( ret == 0 ) // 0 means not equal when using pthread_equal()
{
return &mThreadContext;
}
return &mMainContext;
}There is only one object so far that I have found doesn't share between contexts, and that is a Vertex Array Object (VAO).
For now I'm just making my code think the extension isn't present when multithreading is enabled, but I need to come up with a solution to generate the VAO for each thread it gets used on.
If-Else If
I never really thought about if statements until sometime last year, when I noticed that an if statement only has an else, there is no such thing as an else if.
I was writing an if statement at the weekend and I was writing an else for that got me thinking about it again.
Another revelation is that Alex from Clockwork Orange is also Caligula from Caligula, and Linderman from Heroes.
if( value == true )
doSomething();
else if( anotherValue == true )
doSomethingElseIf();
else
doSomethingElse();Take this for example, break up the else if, shifting the if onto a new line and indent, the nature of the if statement is revealed.
if( value == true )
doSomething();
else
if( anotherValue == true )
doSomethingElseIf();
else
doSomethingElse();An if statement only uses the single statement after the if and then the single statement after the else, so the else if is only an else with another if as its single statement.
The way we are allowed to perform multiple statements is to either wrap it in a function, or to use brackets to give scope.
if( value == true )
{
doSomething();
doSomething2();
}
else
if( anotherValue == true )
doSomethingElseIf();
else
{
doSomethingElse();
doSomethingElse2();
}Of course rather than just indenting, its easier to read with the else and if treated as else if.
if( value == true )
{
doSomething();
doSomething2();
}
else if( anotherValue == true )
doSomethingElseIf();
else
{
doSomethingElse();
doSomethingElse2();
}This can be applied to other statements too, such as for loops, do while loops, and while loops.
// Else-For
if( value == true )
{
doSomething();
}
else for( int i = 0; i < 2; ++i )
{
doSomethingFor( i );
}
// Else-Do
if( value == true )
{
doSomething();
}
else do
{
anotherValue = doSomethingDo();
} while( anotherValue );
// Else-While
if( value == true )
{
doSomething();
}
else while( anotherValue )
{
anotherValue = doSomethingWhile();
}
Sum of Two Bits
A while back I read a blog that claimed that no one knows how to build a processor because of the complexity and computers do all the work today, so I've decided to see if I can make one using only discrete components.
Before working on the actual instruction parsing, I will work on the individual instructions. So the first instruction I'm going to make is ADD. The image to the left adds two bits together.
Out is only set when A or B are set, but never at the same time, so it is A XOR B.
Carry is only set when A and B are both set, so it is A AND B.
Easy. However, a carry can happen, so this diagram needs to take a third input for the carry for the previous stage. In the first stage, Cin (Carry In) will be zero.
Since it is built on the previous adder, A XOR B and A AND B will still be a part of this circuit.
Out is just an extension of the previous stage, combining the output of the previous adder with the carry, ( A XOR B ) XOR Cin.
Since Out was just another XOR operation, another carry has the possibility of being generated. It is impossible for both carry's to be true at the same time, so we can just OR them together. Cout is ( A AND B ) OR ( ( A XOR B ) AND Cin ).
A quick console app to test the logic
//
//
#include <stdio.h>
//
//
unsigned int AddTwoBits( const unsigned int inA, const unsigned int inB, const int inCin, unsigned int * outCout )
{
unsigned int out = ( inA ^ inB ) ^ inCin;
// Output new carry if needed
unsigned int cout = ( inA & inB ) | ( ( inA ^ inB ) & inCin );
if( outCout )
{
(*outCout) = cout;
}
//
printf( "%d\t%d\t%d\t%d\t%d\n", inA, inB, inCin, out, cout );
//
return out;
}
//
//
int main()
{
printf( "A\tB\tCin\tOut\tCout\n" );
AddTwoBits( 0, 0, 0, NULL );
AddTwoBits( 1, 0, 0, NULL );
AddTwoBits( 0, 1, 0, NULL );
AddTwoBits( 1, 1, 0, NULL );
AddTwoBits( 0, 0, 1, NULL );
AddTwoBits( 1, 0, 1, NULL );
AddTwoBits( 0, 1, 1, NULL );
AddTwoBits( 1, 1, 1, NULL );
}And the output gives what I expected.
A B Cin Out Cout
0 0 0 0 0
1 0 0 1 0
0 1 0 1 0
1 1 0 0 1
0 0 1 1 0
1 0 1 0 1
0 1 1 0 1
1 1 1 1 1Now I just need to create the logic using discreet components.




