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.
Assert Pointer and Delegate
While bored the other day, I made a template class that wraps around a pointer, and will assert when it is null.
//
//
#include <assert.h>
//
//
template<typename T>
struct AssertPointer
{
//
AssertPointer()
:
mPointer( NULL )
{
}
//
AssertPointer( T * inPointer )
:
mPointer( inPointer )
{
}
//
operator T * ()
{
assert( mPointer );
return mPointer;
}
//
operator T * const () const
{
assert( mPointer );
return mPointer;
}
//
T * operator -> ()
{
assert( mPointer );
return mPointer;
}
//
T * const operator -> () const
{
assert( mPointer );
return mPointer;
}
//
T * GetPointerValue()
{
return mPointer;
}
//
T * const GetPointerValue() const
{
return mPointer;
}
private:
//
T * mPointer;
};When declaring your pointer, use the pointer type as the template argument. For example, if you want a pointer to an integer, AssertPointer<int>, or a pointer to an integer pointer, AssertPointer<int *>.
For class or structure pointers, the -> operator is overloaded so you can access its members as you would a normal pointer.
If you need access to the raw pointer value without the assert, just use the GetPointerValue() functions.
I may use it to replace the Delegate structure I created for callbacks in Athena.
template<typename T, class U = void>
struct Delegate
{
//
Delegate()
:
func( NULL ),
object( NULL )
{
}
//
Delegate( T inFunc, U * inObject = NULL )
:
func( inFunc ),
object( inObject )
{
}
//
T func;
U * object;
};It is used to store a class object and a function pointer within that class in a single structure. I can use the AssertPointer class and pass the class template type to its template argument, giving me better feedback when I get null pointers being used in my delegates.
iOS Vector and Matrix Math: Part 2
Continuing on from my previous post iOS Vector and Matrix Math. I have managed to use matrix multiplication to transform a vector.
Vector Transform
Transforms a vector by a matrix
float matrix[16] =
{
1.0f, 2.0f, 3.0f, 4.0f,
7.0f, 6.0f, 9.0f, 10.0f,
11.0f, 12.0f, 13.0f, 14.0f,
15.0f, 16.0f, 17.0f, 18.0f
};
float vectorIn[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
float vectorOut[4];
vDSP_mmul( &vectorIn[0], 1, &matrix[0], 1, &vectorOut[0], 1, 1, 4, 4 );iOS Vector and Matrix Math
Continued on post iOS Vector and Matrix Math: Part 2.
For the past week or two I've been trying to reproduce the water effect of PixelJunk Shooter on the iPhone, however I seem to have reached a bottleneck with the collision detection used for repulsion when compressing.
I tried changing from Length to Length Squared, to Manhattan Distance, but it still grinded my framerate to 1FPS.
I decided to see if the Accelerate Framework in the iOS SDK had the vector math added to it yet, and to my surprise it had. It may have been added a while ago and I missed it. So I began to replace my math functions with the vector ones.
The strides are for the single precision functions are the number of floats. So for a stride of 8 bytes, you need to use 2.
float vec[2];
vDSP_vclr( &vec[0], 1, 2 );This function sets the contents of a vector to zero. The equivalent function would be this
void vDSP_vclr( float * inVec, const int inStride, const unsigned int inCount )
{
for( unsigned int idx = 0; idx < inCount; ++idx )
{
*inVec = 0.0f;
inVec += inStride;
}
}I haven't used the double precision version of the vector library yet so I don't know if the stride is the count of doubles, or of floats.
I have managed to go from 17 FPS for 650 particles to 30 FPS (I originally had it at 300 particles at 30FPS but it turned out I was profiling in debug). A definite improvement from using the Accelerate library functions for 2d vector math. One of my main issues for getting 1FPS originally was I was island producer was hit testing each particle with itself so it always had the maximum number of particles on each island.
The next couple of pages on this post cover some of the functions (vector and matrix) and their equivalent C++ function.
Re: Using C++ enums like C# enums
I found out that only the Microsoft allows you to forward declare enums, but standard compliant compilers do not allow it since its size is not yet known.
I could use an int rather than the enum variable to get around the problem, but I prefer to be able to see what the enum is set to, so here is the macro to make the code work in non-Microsoft compilers.
#define ATHENA_START_CSHARP_ENUM( x ) \
struct x \
{ \
public: \
enum EnumType
#define ATHENA_END_CSHARP_ENUM( x ) \
; \
\
inline x() \
{ \
} \
\
template<typename T> \
inline x( const T & inValue ) \
: \
value( (EnumType)inValue ) \
{ \
} \
\
inline x( const EnumType inValue ) \
: \
value( inValue ) \
{ \
} \
\
inline void operator = ( const EnumType inValue ) \
{ \
value = inValue; \
} \
\
inline EnumType operator ~ () const \
{ \
return (EnumType)~value; \
} \
\
inline EnumType operator & ( const EnumType inValue ) const \
{ \
return (EnumType)( value & inValue ); \
} \
\
inline operator EnumType () const \
{ \
return value; \
} \
\
private: \
EnumType value; \
}
POSIX Thread: Setting Priority and Stack Size
The POSIX Thread library, also known as pthread, has always annoyed me with trying to set thread priority.
Some documentation for pthread_attr_setschedparam() say use PTHREAD_MIN_PRIORITY and PTHREAD_MAX_PRIORITY, however they never seem to exist.
Some sites say use 0 and 31 for them respectively for min and max.
Instead I found a function that will retrieve the min and max priority for a given scheduling policy, allowing you to set the thread priority before it is created.
void SetAttributeMaxPriority( pthread_attr_t & inThreadAttr )
{
const int kPolicy = SCHED_RR;
int minPriority = sched_get_priority_min( kPolicy );
int maxPriority = sched_get_priority_max( kPolicy );
//
sched_param schedParam;
schedParam.sched_priority = maxPriority;
pthread_attr_setschedparam( &inThreadAttr, &schedParam );
//
pthread_attr_setschedpolicy( &inThreadAttr, kPolicy );
}
The stack size also needs to be a multiple of the page size, while this is not really an issue unless you go really low or don't use a power of 2 number, I added some code to reinforce it.
void SetStackSize( pthread_attr_t & inThreadAttr, const int inStackSize )
{
//
int stackSize = ( inStackSize < PTHREAD_STACK_MIN ) ? PTHREAD_STACK_MIN : inStackSize;
//
int pageSize = sysconf( _SC_PAGESIZE );
if( pageSize == -1 )
{
pageSize = PTHREAD_STACK_MIN;
}
int multipleRemainder = mParams.stackSize % pageSize;
if( multipleRemainder != 0 )
{
stackSize += ( pageSize - multipleRemainder );
}
//
pthread_attr_setstacksize( &inThreadAttr, stackSize );
}
I wasn't sure if _SC_PAGESIZE would work on every system, so just incase I made page size equal PTHREAD_STACK_MIN if an error was returned.



