SpriteBatch -> 2x performance improvement anyone?

  ... information about changes and new features of AndEngine,

How do you like the SpriteBatch feature?

I waited for this for my whole life!!
49
49%
I'll use it in my next project!
28
28%
I don't know... yet.
22
22%
I don't need it, can I have a beer instead?
1
1%
 
Total votes : 100

SpriteBatch -> 2x performance improvement anyone?

Postby Nicolas Gramlich » Tue Jun 14, 2011 9:26 pm

Hello Community,

today I sat down and implemented the SpriteBatch class.

But what is a SpriteBatch...
Imagine you have a big bunch of Sprites that share many properties, i.e. they originate from the same Texture or they might even use the same TextureRegion. Up to now, you attached all those sprites to the Scene or another Entity, basically ending up with lots of children. While this is perfectly fine, it can be done faster, because these Sprites might share many properties, which redundantly get applied to OpenGL, which is quite costly. This is where the SpriteBatch class comes in. The SpriteBatch class omits many of such redundant calls and most importantly combines all the geometry (triangle-vertices) and texturecoordinates into one big Buffer instead of many many small ones, greatly improving performance.

So let's have a look at the code of the SpriteBenchmark. There are three ways to achieve the following result of 1000 Sprites being drawn on the screen:
spritebenchmark.png
SpriteBenchmark
spritebenchmark.png (215.13 KiB) Viewed 13242 times


The first approach is the ordinary, easiest but slowest way:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. private void drawUsingSprites(final Scene scene) {
  2.   for(int i = 0; i < SPRITE_COUNT; i++) {
  3.     final Sprite face = new Sprite(this.mRandom.nextFloat() * (CAMERA_WIDTH - 32), this.mRandom.nextFloat() * (CAMERA_HEIGHT - 32), this.mFaceTextureRegion);
  4.     face.setBlendFunction(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
  5.     face.setIgnoreUpdate(true);
  6.     scene.attachChild(face);
  7.   }
  8. }
Parsed in 0.031 seconds, using GeSHi 1.0.8.4


The second approach is a bit smarter, as it makes use of the fact that all Sprites have the same size, so that it can reuse one RectangleVertexBuffer for all the Sprites!
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. private void drawUsingSprites(final Scene scene) {
  2.   /* As we are creating quite a lot of the same Sprites, we can let them share a VertexBuffer to significantly increase performance. */
  3.   final RectangleVertexBuffer sharedVertexBuffer = new RectangleVertexBuffer(GL11.GL_STATIC_DRAW);
  4.   sharedVertexBuffer.update(this.mFaceTextureRegion.getWidth(), this.mFaceTextureRegion.getHeight());
  5.  
  6.   for(int i = 0; i < SPRITE_COUNT; i++) {
  7.     final Sprite face = new Sprite(this.mRandom.nextFloat() * (CAMERA_WIDTH - 32), this.mRandom.nextFloat() * (CAMERA_HEIGHT - 32), this.mFaceTextureRegion, sharedVertexBuffer);
  8.     face.setBlendFunction(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
  9.     face.setIgnoreUpdate(true);
  10.     scene.attachChild(face);
  11.   }
  12. }
Parsed in 0.033 seconds, using GeSHi 1.0.8.4


The third and last approach is even better, as it uses the new SpriteBatch class, which omits many redundant OpenGL calls and internally uses one big Buffer for all Sprites:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. private void drawUsingSpriteBatch(final Scene scene) {
  2.   final int width = this.mFaceTextureRegion.getWidth();
  3.   final int height = this.mFaceTextureRegion.getHeight();
  4.  
  5.   final SpriteBatch spriteBatch = new SpriteBatch(this.mTexture, SPRITE_COUNT);
  6.   spriteBatch.setBlendFunction(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
  7.   for(int i = 0; i < SPRITE_COUNT; i++) {
  8.     final float x = this.mRandom.nextFloat() * (CAMERA_WIDTH - 32);
  9.     final float y = this.mRandom.nextFloat() * (CAMERA_HEIGHT - 32);
  10.     spriteBatch.draw(this.mFaceTextureRegion, x, y, width, height);
  11.   }
  12.   spriteBatch.submit();
  13.  
  14.   scene.attachChild(spriteBatch);
  15. }
Parsed in 0.036 seconds, using GeSHi 1.0.8.4


And now the most interesting part... why go the extra mile of code? (Note: some of the devices are actually hitting the 60 FPS cap easily:!:
spritebenchmark_performance.png
SpriteBenchmark Performance Comparison
spritebenchmark_performance.png (20.69 KiB) Viewed 13192 times


A very similar result can be achieved for the EntityModifierBenchmark, where the sprites are changing every single frame:
entitymodifier_performance.png
EntityModifierBenchmark Performance Comparison
entitymodifier_performance.png (21.25 KiB) Viewed 13192 times


:o Quite nice, hm? :)
Depending on your game you might receive a >2-3x performance improvement almost for free :!:

Some things I should know about using a SpriteBatch :?:
  • SpriteBatch can help you greatly improve performance.
  • SpriteBatch is not a hack, but individual Sprites are nicer from the object-oriented design side.
  • You will usually have few (often just one) SpriteBatch in your game.
  • Your SpriteBatch will usually contain many small, very similar Sprites , i.e. something like a tiled grid (background)
  • The less you change the SpriteBatch the better it performs. While it is best to never change it, it even increases performance for completely dynamic SpriteBatches (See: EntityModifierBenchmark)!
  • Your SpriteBatch could be anything else you come up with. Just give it a try!
  • Consider SpriteBatch a way of optimization, but it might pay off to keep it in mind from the beginning.
  • Profit :!:

There are a few restrictions though.
  • All Sprites in the SpriteBatch have to use the same Texture
  • All Sprites in the SpriteBatch share the same color/transparency (Indivial color/transparency from the sprites is ignored)
  • All Sprites in the SpriteBatch share the same BlendFunction (Indivial BlendFunction from the sprites is ignored)

Resources:

Best Regards,
Nicolas
Nicolas Gramlich
Site Admin
 
Posts: 1734
Joined: Mon Jun 07, 2010 6:20 pm
Location: Schriesheim, Germany

Re: SpriteBatch -> 2x performance improvement anyone?

Postby dironto » Wed Jun 15, 2011 12:25 am

Hi Nicolas,

This is great! Guess that this 'old' comparison between andengine/libgdx doesn't count anymore ;-)

2010:
Nicolas send me a his benchmark suite for libgdx, AndEngine and Rokon. Each engine renders a scene of 32×32 pixel sprites in a grid, a total of 336 sprites, filling up nearly all of the screen. I performed the tests on 2 devices, a Hero with 1.5 and a Nexus One with 2.2
Rokon didn’t start on neither my Hero nor my Nexus one.
AndEngine:
- Hero: ~17fps
- Nexus One: ~41fps
Libgdx:
- Hero: ~51fps
- Nexus One: ~51fps


I do have a question, In my current (first, but not last!) project most of my sprites are attached to box2d physic objects. Is there any way I could use the new spritebatch class to draw those sprites 'efficient' ? For example I have a bunch of 'wooden' boxes you can interact with.

Thanks!
Regards,
Ronald
dironto
 
Posts: 65
Joined: Mon May 23, 2011 12:24 am
Location: Amsterdam, The Netherlands

Re: SpriteBatch -> 2x performance improvement anyone?

Postby Nicolas Gramlich » Wed Jun 15, 2011 11:54 am

Hi,

have a look at the SpriteBatchExample. It shows how to have a 'dynamic' SpriteBatch, which I never tested, but it still should be slightly faster than "the normal way".
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. final Sprite faceSprite1 = new Sprite(-50, 0, this.mFaceTextureRegion);
  2. faceSprite1.setScale(2);
  3. final Sprite faceSprite2 = new Sprite(50, 0, this.mFaceTextureRegion);
  4. faceSprite2.setRotation(45);
  5.  
  6. /* Create the face and add it to the scene. */
  7. final SpriteBatch spriteBatch1 = new SpriteBatch(this.mTexture, 2) {
  8.         @Override
  9.         public void onDrawSpriteBatch() {
  10.                 this.draw(faceSprite1);
  11.                 this.draw(faceSprite2);
  12.         }
  13. };
Parsed in 0.037 seconds, using GeSHi 1.0.8.4


Depending on your game and how many static/dynamic objects there are, it might be a good idea to only put the static objects (which are not moving) into one SpriteBatch and keep the dynamic (moving) other ones as they.

Best Regards,
Nicolas
Nicolas Gramlich
Site Admin
 
Posts: 1734
Joined: Mon Jun 07, 2010 6:20 pm
Location: Schriesheim, Germany

Re: SpriteBatch -> 2x performance improvement anyone?

Postby asktomsk » Wed Jun 15, 2011 12:54 pm

Nicolas, could same tune be applied for drawing TMX tiled maps?
My 1st released game Moopa
Image
asktomsk
 
Posts: 56
Joined: Sat Apr 02, 2011 8:42 am
Location: Russia

Re: SpriteBatch -> 2x performance improvement anyone?

Postby Nicolas Gramlich » Wed Jun 15, 2011 1:38 pm

Hi,

asktomsk wrote:Nicolas, could same tune be applied for drawing TMX tiled maps?

Yes, but we'll have to see how good we can still use culling of "out-of-screen" tiles.

Best Regards,
Nicolas
Nicolas Gramlich
Site Admin
 
Posts: 1734
Joined: Mon Jun 07, 2010 6:20 pm
Location: Schriesheim, Germany

Re: SpriteBatch -> 2x performance improvement anyone?

Postby Nicolas Gramlich » Wed Jun 15, 2011 2:54 pm

Hi,

@dironto
screw what I said, a SpriteBatch with dynamic Sprites works almost as good with static sprites. (See second chart above)
You'd use a convenience class SpriteGroup for the purpose of automatically updating dynamic Sprites.

Best Regards,
Nicolas
Nicolas Gramlich
Site Admin
 
Posts: 1734
Joined: Mon Jun 07, 2010 6:20 pm
Location: Schriesheim, Germany

Re: SpriteBatch -> 2x performance improvement anyone?

Postby tajny » Thu Jun 16, 2011 12:25 am

This is brilliant.

Is it possible to combine SpriteBatch with GenericPool ?

Paweł P.

--------------------------------------
Portfolio

Run Santa Run Link
Xelorians Link
User avatar
tajny
 
Posts: 216
Joined: Mon May 30, 2011 10:23 pm
Location: Poland

Re: SpriteBatch -> 2x performance improvement anyone?

Postby Nicolas Gramlich » Thu Jun 16, 2011 1:31 am

tajny wrote:This is brilliant.

Is it possible to combine SpriteBatch with GenericPool ?


Erm, yeah that should work.
Nicolas Gramlich
Site Admin
 
Posts: 1734
Joined: Mon Jun 07, 2010 6:20 pm
Location: Schriesheim, Germany

Re: SpriteBatch -> 2x performance improvement anyone?

Postby oeN » Thu Jun 16, 2011 1:29 pm

does anyone compiled the code yet and is willing to share the .jar?
Blog: link
My Android Apps: link
oeN
 
Posts: 15
Joined: Wed Jun 08, 2011 8:21 pm

Re: SpriteBatch -> 2x performance improvement anyone?

Postby Nicolas Gramlich » Thu Jun 16, 2011 3:40 pm

Hi,

oeN wrote:does anyone compiled the code yet and is willing to share the .jar?


Simply grab the latest andengine.jar from the AndEngineExamples project.
http://andengineexamples.googlecode.com ... engine.jar

Best Regards,
Nicolas
Nicolas Gramlich
Site Admin
 
Posts: 1734
Joined: Mon Jun 07, 2010 6:20 pm
Location: Schriesheim, Germany

Next

Return to Updates

Who is online

Users browsing this forum: No registered users and 3 guests