Intercepting OpenGL Calls

To capture the OpenGL commands is fairly easy, all you need to do is link a library that implements the OpenGL function before it links the actual library.
Screen Shot 2014-04-28 at 09.46.51

Originally I had planned to use the environment variable DYLD_INSERT_LIBRARIES to achieve this, however I could not get it to work with my Cocoa application. So I decided to just compile it with it and let the user decide whether or not they want to run the executable and insert the library at runtime, or add it to their project to link first.

The next challenge is that now you have intercepted the call, you still need to forward it onto where it was going originally. To do this, I used the link library constructor to retrieve and store this pointer for later use to reduce overhead.

#include <stdio.h>
#include <dlfcn.h>
 
//
typedef void (*_fn_glViewport) (GLint x, GLint y, GLsizei width, GLsizei height);
_fn_glViewport g_glViewport = NULL;
 
//
__attribute__((constructor)) void DllMain()
{
	g_glViewport = (_fn_glViewport)dlsym(RTLD_NEXT, "glViewport");
}
 
//
void glViewport (GLint x, GLint y, GLsizei width, GLsizei height)
{
	printf("Intercepted glViewport call\n");
	g_glViewport(x, y, width, height);
}

The RTLD_NEXT parameter tells dlsym to retrieve the method from the next linked dependencies. Ideally you probably want to check the pointer to make sure it isn’t NULL before calling it, however since glViewport comes from OpenGL 1.0 and still exists in the latest version, I don’t bother.

DX11 OpenGL Wrapper

In an effort to get more up to date with DirectX11, I’ve been out of the DirectX loop now since DirectX10 was released, I have been writing a wrapper around OpenGL to implement it.

During this I have come more familiar with the OpenGL API than I’ve been before, using and testing things I haven’t before such as queries.

However in doing this, I found out that my Radeon graphics card doesn’t support GL_TIMESTAMP despite it saying it supports the extension. It does suppose GL_TIME_ELAPSED but that is not handy for getting the start point of a call, only how long it took, and the only way I can think around it is to always using the measurement method until you need to query an elapsed time or a timestamp and do your own calculations.

Luckily in DX11 there is a probably on the timestamp query that will let you know if the data retrieved is reliable, so for the moment I am just always returning false.

OpenGL Shader Debugging

One thing that always annoys me with using glGetShaderInfoLog(), is that when compiling OpenGL shaders multiple sources can be used. Because of this, the line numbers get shifted, and trying to find line 123 where a syntax error exists is a length process.

So I made a function that will take the sources list and output the lines with line numbers.

void OutputWithLineNumbers( const char ** inSources, const unsigned int inCount )
{
	bool unendedLineNo = false;
	int lineNumber = 1;
 
	for( unsigned int i = 0; i < inCount; ++i )
	{
		const char * cur = inSources[i];
		const char * next = NULL;
 
		do
		{
			//
			if( !unendedLineNo )
			{
				printf( "%04d: ", lineNumber );
			}
 
			//
			next = strchr( cur, '\r' );
 
			//
			if( next )
			{
				size_t len = ( next - cur );
				char * lineText = (char *)alloca( len );
				memcpy( lineText, cur, len );
				lineText[len] = 0;
 
				printf( "%s\n", lineText );
 
				unendedLineNo = false;
				++lineNumber;
			}
			else
			{
				printf( "%s", cur );
				unendedLineNo = true;
			}
 
			//
			cur = next + 2;
		} while( next );
	}
 
	if( unendedLineNo )
	{
		printf( "\n" );
	}
}

This code requires the source files to have a carriage return as well as a new line character.

iOS: Using Separate Shaders

One of the lovely additions to iOS5 is there is now the ability to use separate vertex and fragment shaders (GL_EXT_separate_shader_objects).

The first step in using separate shaders is to create an object to bind them onto that will be our overall shader program, known as a pipeline object.

GLuint gProgramPipeline = 0;
GLuint gVertexProgram = 0;
GLuint gFragmentProgram = 0;
 
//
//
int main()
{
	//
	CreatePipeline();
	CreateShaders();
 
	//
	BindShadersToPipeline();
 
	//
	do
	{
		//
		UpdateUniforms();
 
		//
		BindPipeline();
		DrawSomething();
		UnbindPipeline();
	}
	while( true );
 
	//
	DestroyShaders();
	DestroyPipeline();
}
 
//
//
void CreatePipeline()
{
	glGenProgramPipelinesEXT( 1, &gProgramPipeline );
}
 
//
//
void DestroyPipeline()
{
	if( gProgramPipeline )
	{
		glDeleteProgramPipelinesEXT( 1, &gProgramPipeline );
		gProgramPipeline = 0;
	}
}

This pipeline object acts similar to vertex array object (VAO) only with shader programs, however instead of using glUseProgram, a separate function glUseProgramStagesEXT() must be used to bind the shader program to the pipeline, specifying the role of the shader program.

//
//
void BindShadersToPipeline()
{
	glUseProgramStagesEXT( gProgramPipeline, GL_VERTEX_SHADER_BIT_EXT, gVertexProgram );
	glUseProgramStagesEXT( gProgramPipeline, GL_FRAGMENT_SHADER_BIT_EXT, gFragmentProgram );
}

Updating uniforms is slightly different as well.

//
//
void UpdateUniforms()
{
	//
	GLint uniformLoc = glGetUniformLocation( gVertexProgram, "myVertUniform" );
	glProgramUniform1fEXT( gVertexProgram,
			       uniformLoc,
			       1.248f );
 
	uniformLoc = glGetUniformLocation( gFragmentProgram, "myFragUniform" );
	glProgramUniform1fEXT( gFragmentProgram,
			       uniformLoc,
			       6.1218f );
 
	//
	uniformLoc = glGetUniformLocation( gVertexProgram, "myCommonUniform" );
	float commonValue = 2.0f;
 
	glProgramUniform1fEXT( gVertexProgram,
			       uniformLoc,
			       commonValue );
 
	uniformLoc = glGetUniformLocation( gFragmentProgram, "myCommonUniform" );
	glProgramUniform1fEXT( gVertexProgram,
			       uniformLoc,
			       commonValue );
}

Finally to use the program pipeline, we just need to bind it.

//
//
void BindPipeline()
{
	glBindProgramPipelineEXT( gProgramPipeline );
}
 
//
//
void UnbindPipeline()
{
	glBindProgramPipelineEXT( 0 );
}

fef

Using Vertex Array Objects

Karen-Gillan-001With 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.