Particle systems in IOS Game X

This is part six of a series on making iOS games.

Intro

With hundreds or thousands of particles on the screen, a particle effect is a great way to bring your frame rate precipitously down. There are so many different aspects of the particle engine that can effect performance.

Michael Daley has a whole chapter on this (see resources below – and he is right – its a big time sink).

I started off just extending the normal sprite classes which I created in the game template (which I talked about in the last post in this series), and then gradually moved to including more and more optimisations.

I (like everyone else) pre-create a number of particle engines, so that the expensive operation of memory allocation does not have to happen during the game, more as a precaution, than something I’ve rigorously tested with the profiler in Instruments.

Point sprites

Point sprites are a common thing to use with particles effects, to try an optimise things. With each particle as a point sprite you dont need to pass as much data to OpenGL, theoretically 1/6 of the data, since you are only passing one vertex per sprite, not two triangles.
In order to use point sprites you need to set the point size in the traditional vertex shader. I couldn’t see any docs on how to manage the point size in the GLKit base effect – this would be something like:  

gl_PointSize=32.0;

in the normal OpenGL ES 2.0 vertex shader.
So I experimented a bit and created a particle engine using the usual OpenGL ES 2.0 vertex and fragment shaders to do point sprites, and the same (as the game template in the previous post) GLKit base effect for the other sprites such as the player, background tiles etc.

Mixing GLKit and standard OpenGL ES 2.0 shaders

Errors in your shaders can stop the proper functioning of GLKitBaseEffect, even though you would expect them to be unrelated to each other. So watch out.
For example I was referencing an attribute in my vertex shader :

colorSlot = glGetAttribLocation(programHandle, "SourceColor");

which was not actually there. This made my base effect fall over when preparing to draw. ALthough the GL ERROR: 0×0501 only appeared once the GLKit base effect did not function at all.

Source: JMS

Switching states

I remember seeing a WWDC presentation (2010 I think) on OpenGL which described how there is a certain hierarchy in Apple’s iOS OpenGL implementation, the hierarchy goes from states which you should change the least to those states which have minimal impact on performance. Shaders being at the top (expensive part) of the list. So switching shaders can hammer down your performance when you switch them too often.

I assume the base effect is the same as normal shaders in terms of switching penalties. I think VBOs/VAOs came next on the expense list.

At one point in my engine, I had two base effects and one traditional vertex/fragment shader pair. I had optimised with batching and experimented with not passing colours, but these optimisations were sometimes made irrelevant by the cost of switching base effects/ shaders.

The consequence is that now I use only one base effect for all drawing.

My particle implementation uses a C linked list to hold the particles, and C functions to update the data in the vertices. There were significant slow downs using NSMutableArrays, and NSObject subclasses to store the particles.

No to Point Sprites

After hammering in a point sprite implementation (so creating a vertex ((with the point size set)) and fragment shader and loading the single “blob” texture), I decided not to use it because:

  1.  One has to use real shaders, as far as i know there is no gl_points attribute in GLKBaseEffect, which makes things a bit more complex. Trying to shoehorn different types of drawing into one set of shaders (since the speed hit caused by changing glPrograms would probably outweigh any point sprite advantage) is complex (Rideout has an example in his book – see below in the resources).
  2. I sometimes like to have multiple textures in an explosion or other particle effects  (point sprites use the whole texture, so switching textures most likely would hit performance strongly (although I did not check)), and its much quicker swapping in a texture coords from another part of the texture atlas.
  3. Point sprites instead of a “small” (see below) vertex quads (2 triangles) weren’t actually that much faster on the iPod touch 4g
  4. My current implementation is fast enough.

So with my current graphics engine, the non particles sprites (such as tiles, monsters and players) are held in Foundation classes/collections for ease of use and manipulation (all in one VBO/VAO) and the particle engines each have their own VBOs/VAO. The particle data (not the vertex data – just the plain old particle data such as colour, velocity etc) is held in C struts in a C linked list

Effects

One of the trickiest parts of creating effects with a particle engine, is figuring out the starting state of the particle’s attributes, and how they change. There is a great resource called Particle Designer by 71 squared that can help solve this problem. I didn’t use 71 squared’s particle engine code (OpenGLES 1), but in the application itself there are many, many example particle engines set up (uploaded by generous users) such as explosions, fireballs, lasers etc.
Trying to recreate these effects in my own particle implementation was a great way to debug and extend the engine. Particle Designer’s examples are a great place to start – but it doesn’t allow you to have all its particle states change via an acceleration. For example you can set a start and end colour, and so calculate the colour delta, based on the particle’s time to live, but you can’t (not that I could see anyway) add an accelerating colour change. You can’t experiment with multiple textures either (its implementation uses point sprites anyway). Even without out these options, its amazing how many effects are possible, and thus can be copied.

Particle gotchas on iOS

  • Avoid ObjC message sending where possible in the game loop. Especially in the gl calls – so dont do this:
glBufferSubData(GL_ARRAY_BUFFER, 0,([monsters count] + [tiles count] + [particles count]) * 4 * sizeof(Vertex), vertices);

That took my frame rate down significantly, much more than I expected –
I’m not sure why – maybe this value is repeatedly checked (executed).

  •  Particle size can matter more than the number of particles. Double the particles, and you might half the frame rate – double the particle size and you can get 1/10 the fps
  •  I havent see it talked about much, but having interesting and changing textures in a particle engine can create some really nice effects reducing the need to experiment so much with emit/velocity decay/colour change to particle variables.
  • Moving to a smaller sized vertexes improved both update and draw speed a lot, so I made sure I was passing only two vertex dimensions which were shorts rather than floats, and you can replace the colours with other types smaller than floats
  • For example using vertex structure:

    typedef struct { 
        short Position[2]; 
        GLubyte Color[4];
        float TexCoord[2];
    } SmallishVertex;
    

    and setting up the colour with:

    glEnableVertexAttribArray(GLKVertexAttribColor); 
    glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE,sizeof(SmallishVertex), (const GLvoid *) offsetof(SmallishVertex, Color));
    

    gives a quite a significant performance increase, don’t forget the 4th argument is GL_TRUE, this normalises the colours, as they are no longer floats. If you don’t then your sprites will have strange colours, as the red, green, blue, alpha will become binary in nature – off or on.

    Corrections

    I said in a previous post that using GLKViewDrawableMultisample4X made little difference to my performance. I was wrong. I’m not sure whether other things were bringing down the performance at the time, so that setting this made no difference, but with my current setup, it makes a huge difference on all platforms I tested (I don’t have the book ends of performance though – the iPhone3GS and the “new” iPad).
    In addition, setting the drawableDepthFormat to GLKViewDrawableDepthFormat16, made no difference on an iPad2 but a big difference on the iPod touch 4G.

    Here are some typical draw&update times for 1600 background tiles, 60 monsters and 4 flames (with about 500 particles each):

    iPad2

    With GLKViewDrawableMultisample4X set: 0.01
    unset: 0.005
    not using GLKViewDrawableDepthFormat16: 0.005 (same)

    iPod touch 4G

    With GLKViewDrawableMultisample4X set: 0.18
    unset: 0.01
    not using GLKViewDrawableDepthFormat16: 0.0085

    Summary

    Its a lot of work getting particle systems to work nicely. There are a bunch things that may seem innocuous which can effect your performance. Point sprites were a bit of a pain to get working because of having to use proper shaders, and for me they didn’t make a big enough improvement to warrant using. I reckon you should stick to the GLKit base effect if you can, its much simpler than the standard OpenGL way. Lots of hard-to-find errors can occur when using standard OpenGL shaders, though of course you can  do much more with them. The example effects in Particle Designer are a fantastic resource. Make sure you test on many devices, different architecture or settings can be penalised differently on different hardware.

    Resources

    Tim Omernick’s Stanford talk and the code – his code in my opinion is a really nice starting point for learning particle systems – although it does not seem to work on my iPod touch that well (OpenGL ES 1.0 ).

    https://github.com/epatel/Fireworks-by-Tim.

    And the same as the last post

    iPhone 3D programming – Rideout – widely described as the best book on OpenGL for the iPhone. I think its good too, but wading through C++ is very dull, OpenGL ES 1 and 2.

    Learning iOS game programming – Michael Daley – good reference – (OpenGL ES 1.0 only).

    In the past he had some nice videos on the topic – currently he has some useful tools to sell. http://www.71squared.com/

    iOS 5 by tutorials, Ray Wenderlich – chapter 8, is a nice place to start things for OpenGL ES 2.0. (nothing on multiple sprites/particles).

    Apple WWDC talks on OpenGL 2010 and 2011

    Apple’s: OpenGL ES Programming Guide for iOS: Best Practices for Working with Vertex Data.

    Jeff LaMarche’s website has some good openGL stuff: http://iphonedevelopment.blogspot.com/

     

     

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>