Drawing characters/sprites... fast?

Tutorials concerning the OpenGL® ES cross-platform API for full-function 2D and 3D graphics on the Google-Android platform.

Drawing characters/sprites... fast?

Postby astrath » Sun Apr 25, 2010 9:36 pm

I've recently ported my Canvas based 2D game over to OpenGL and could really need some help getting things up to speed. Until now I draw all sprites using the drawTexture2D extension as recommended in the famous Google IO video.

Now this method doesn't support rotating a sprite. Also like mentioned in the Google video, font rendering using drawTexture2D is real slow when you have your whole font in a single texture and crop this texture for each character.

So until now I've tried (on a G2/Magic):

drawing a highscore table consisting of about 80 characters:

- using drawTexture2D and texture cropping => 12-14fps
- filling a standard vertex array (not a VBO) for each character and then immediately drawing it => 7fps ?!?!

So using standard arrays was even way slower then using texture cropping.
Okay, I guess the overhead of all the openGL calls when drawing each character seperately is just too much for the little G2.

My question is, where to go from here?
For 80 rather small sprites, judging from the chart in the Google video, one should be able to attain ~60fps.

If I combine all the characters and other sprites over the course of a frame and then at the end of the frame render them sortet by texture, resulting in only a few draw calls instead of 80+, should that alone give me such a dramatic performance increase (from 7 to 60)?

How have you gies solved this?

To be honest not being able to make 80 draw calls per frame was a bit shocking to me.

Any input is appreciated.

PS: I'm using the application, activity and OpenGLES setup code from a google example, so I guess that shouldn't be the problem.
astrath
Junior Developer
Junior Developer
 
Posts: 12
Joined: Thu Apr 08, 2010 1:03 pm

Top

Postby MichaelEGR » Sun Apr 25, 2010 10:47 pm

Unfortunately I don't have time presently to give a full answer with source code, but perhaps another post of mine will give you some clues on how I handle text rendering using Android fonts in OpenGL ES. The same technique works on the desktop / OpenGL and is what I use in TyphonRT; middleware I soon am releasing.

Now this post does have a little back and forth banter of sorts, but I do comment on how I handle text rendering. One easily gets 60hz+ / no perceivable impact from this approach on a G1 on up.

Here is the post:
http://www.anddev.org/viewtopic.php?p=3 ... ght=#34801

Give that a look and perhaps in the next few days I can offer more insight with some code.
Founder & Principal Architect; EGR Software LLC
http://www.typhonrt.org
http://www.egrsoftware.com
User avatar
MichaelEGR
Senior Developer
Senior Developer
 
Posts: 147
Joined: Thu Jan 21, 2010 5:30 am
Location: San Francisco, CA

Postby zorro » Mon Apr 26, 2010 2:25 pm

The problem with OpenGL in Android is that you need to use as fewer as possible GL render and state changes calls in a frame, bind less textures per frame, etc. If you draw each character with a vertex array, it's only natural that you have 7 fps. Try and use a single array (and a tex cood array, of course) for every text, allocated at the beginning with the size of 'max number of chars'. Then every frame you fill that array with letter vertices and draw the whole text at the end with a single draw call. If your text does not change, after you create the array you can use it until something changes. The key of succes in obtaining maximum performance is to use few calls with as much as possible data.
User avatar
zorro
Experienced Developer
Experienced Developer
 
Posts: 71
Joined: Mon Aug 10, 2009 3:11 pm
Location: Romania

Postby astrath » Mon Apr 26, 2010 4:14 pm

@zorro
Thanks for your tip, I've now implemented what you suggested. I'm now drawing the text of a whole frame in a single draw call, filling preallocated vertex and texture arrays as the characters are being drawn.

Now instead of 7 fps I get around 13-14 fps with 80 quads (=sprites/characters).
In a the-glass-is-half-full kind of way that's a performance increase of ~100%, hehe.
Still this seems rather abysmal, when going down to 33 quads I hit ~23fps.

I've commented out basically anything else, no game loop left, I'm not even clearing the frame buffer at the start of the frame. Just this one openGL draw call remains.

It seems I have got something more fundamental wrong, like the init code, event handling, threading.
Still my Renderer, Application, ... classes are all based on the ones from SpriteMethodTest. Also tried converting every bitmap to 565 or 4444 at load time, nothing helped.

I am now rewriting my code so it uses GLSurfaceView. If that doesn't change anything I'm really stumped.
Guess the problem is something I have to find myselfe, but still thanks for your help gies.

PS: It would still help if someone could tell me what fps he reaches on a HTC Magic (G2) with x number of sprites.
astrath
Junior Developer
Junior Developer
 
Posts: 12
Joined: Thu Apr 08, 2010 1:03 pm

Postby MichaelEGR » Mon Apr 26, 2010 5:24 pm

Yeah zorro is right.. I'll respond later.. meh.. Just finished a 26 hour binge to finish and submit a 40 page proposal with 44 seconds to spare before the deadline... meh... :: falls over ::

I'd recommend staying away from SpriteMethodTest it's not a proper way of handling things and is an ancient GL example from big G.

I have a G1 and Ion / MyTouch3G and my text rendering method produces 60fps w/ no lag.
Founder & Principal Architect; EGR Software LLC
http://www.typhonrt.org
http://www.egrsoftware.com
User avatar
MichaelEGR
Senior Developer
Senior Developer
 
Posts: 147
Joined: Thu Jan 21, 2010 5:30 am
Location: San Francisco, CA

Postby zorro » Mon Apr 26, 2010 9:03 pm

@astrath
Your fps is still very low.. hm.. How many textures do you bind per frame? Also, i've noticed that textures of 1024x1024 or larger on 24bit (smooth alpha) are 'heavier' and reduce fps. Also, do you have much alpha blend in the scene? The arrays for text you are displaying are re-created every frame ?
A lot of questions but maybe we can help you. Also, using GLSurfaceView is right, this is the recommended way...
User avatar
zorro
Experienced Developer
Experienced Developer
 
Posts: 71
Joined: Mon Aug 10, 2009 3:11 pm
Location: Romania

Top

Postby impi » Thu Apr 29, 2010 2:18 pm

I have the exact same problem, like you (but not for text rendering, but for rendering an tilemap, which is the same thing like you do but with tiles :)) and i use the drawtex extension.

I have to mention that i have MORE fps on the emulator than on my G1 if i use opengl for rendering its slower than if i use Canvas!

Full tilemap with canvas @ 32BIT(!) gives me ~45 fps, and no caching here. With opengl i get barely ~15fps.

So i calling 176 times die (GL11Ext)gl.drawTexiOES(...); call. And ONE TIME i call gl.glBindTexture

But it doesn't affect fps if i call glBindTexture 176 times than just 1 time. Thats driving me crazy, because, yeah on the PC i coded already an 2d opengl game an i know that these calls are very expensive, and just 176 drawTex calls should be fine if i do not call bindTexture to often.

Yeah? I don't know whats going on there, there is not much more code than just an for loop in the GLRenderer(just the common pattern with GLSurfaceView and so on...) that renders an loaded texture (16bit/32bit doesnt affect performance, like it does in canvas)...

It just "feels" if the G1 would use an software OpenGL implementation, that uses just pointers for texture switches in the memory... but i know that the G1 supports GL and the drawTex ext...
impi
Developer
Developer
 
Posts: 29
Joined: Wed Apr 28, 2010 11:17 pm

Postby zorro » Thu Apr 29, 2010 2:26 pm

You can find if the render is in software mode by printing the string returned by gl.glGetString (GL10.GL_RENDERER) and if it says 'Pixel Flinger' then it's software. But i doubt it, the tests i've made on G1 always used hardware by default. I guess your problem is the large number of draw calls (176). If you are using the same texture across all 176 render calls, then you could create a vertex and a texture coord array and draw the whole thing in one call. You could try that to see where the bottleneck is.
User avatar
zorro
Experienced Developer
Experienced Developer
 
Posts: 71
Joined: Mon Aug 10, 2009 3:11 pm
Location: Romania

Postby impi » Thu Apr 29, 2010 2:45 pm

Are 176 drawTexi calls really that BAD ?

I mean, clearly you can optimize this, but... 176 calls? no update code here? And then even slower than 32bit canvas EVEN with 176 .drawBitmap(...) calls?

And why glBindTexture doens't affect the performance as it clearly should be a MASSIVE impact ?

i got 15 fps with

glBindTexture(tex);
drawALLTiles();

and with

do 176 times:
glBindTexture(tex);
drawONETile(x,y);

The second one should be so explosive slow and the first one should cleary be faster than the second one, but it isn't so... or did google do some optimizations on that, so that you dont must check if the texture is already binded and if so dont bind it new... ?

And how the hell do you use the drawtexi extension (meant to be the fastest way as the google guy said it in his presentation) with tilemaps if it goes down so fast with performance? Yeah i could cache the whole thing, but it's an mobile phone not an desktop computer with gigabytes of ram.

Thats just pops out of my head, i will go test if i messed up some things and the G1 used software mode for some reason with your String test, thank you for that :)
impi
Developer
Developer
 
Posts: 29
Joined: Wed Apr 28, 2010 11:17 pm

Postby zorro » Fri Apr 30, 2010 6:59 am

When you bind the texture 176 times, the fps does not drop probably because the system is aware that is the same texture. Or maybe the texture is too small in size to matter. But the 176 draw calls do matter. The drawtex extension is the fastest at drawing a quad, but when you draw 176 quads the problem becomes the setup for each call. I had this problem too, and by reducing the number of calls (from 50 to 4) I raised my fps from 8 (I didn't use the extension, just draw a simple quad at each call) to 35. Just try and draw the tiles in a single call with vertex arrays and you'll see the difference. And if you throw in VBO buffers you can again increase your fps. Minimizing your draw calls is the best way to obtain decent fps, at least on android where the main program is running in a virtual machine and the opengl calls are executed by native routines.
User avatar
zorro
Experienced Developer
Experienced Developer
 
Posts: 71
Joined: Mon Aug 10, 2009 3:11 pm
Location: Romania

Postby impi » Sun May 09, 2010 2:51 am

I just tested the GLString with Pixel Flinger (on the G1) and, yes, it reported that it is in SOFTWARE mode!

I don't get this, i just have an GLSurfaceView with an GLSurfaceView.Renderer and draw just two for loops through it with the drawTexiOES extension.

The speed on the Emulator is exactly the same as on the G1 btw. (core i7 vs. G1)

Any wise advises or tricks to force hardware rendering?
impi
Developer
Developer
 
Posts: 29
Joined: Wed Apr 28, 2010 11:17 pm

Postby astrath » Mon May 10, 2010 3:34 pm

impi wrote:I just tested the GLString with Pixel Flinger (on the G1) and, yes, it reported that it is in SOFTWARE mode!



Do you have some custom ROM installed? Maybe try to build it for a different android target version?

I have now moved my own app over to GLSurfaceView, sadly this didn't change a lot regarding the fps. Still ~17 fps with 70 rather small quads drawn with a single call of glDrawElements() from a single texture (512x512). Depressingly using canvas was a bit faster then that. Also using glDrawTexfOES() with cropping the texture (it's a texture atlas) before every single call and calling that 70 times is still a bit faster then the single glDrawElements call. :(
astrath
Junior Developer
Junior Developer
 
Posts: 12
Joined: Thu Apr 08, 2010 1:03 pm

Postby zorro » Tue May 11, 2010 7:19 am

can you post here your drawing loop?
User avatar
zorro
Experienced Developer
Experienced Developer
 
Posts: 71
Joined: Mon Aug 10, 2009 3:11 pm
Location: Romania

Postby astrath » Tue May 11, 2010 11:35 pm

Okay, I have now commented out everything else and added some test rendering code directly inside of onDrawFrame() of GLSurfaceView. With 140 triangles (=70 sprites) I now get ~22fps, with 200 triangels it's 18fps.

What I do is:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.  
  2. FloatBuffer  mVertexBuffer = ByteBuffer.allocateDirect(4 * 9 * numTris)
  3.  
  4. .order(ByteOrder.nativeOrder()).asFloatBuffer();
  5.  
  6. FloatBuffer  colorBuffer = ByteBuffer.allocateDirect(4 * 9 * numTris)
  7.  
  8. .order(ByteOrder.nativeOrder()).asFloatBuffer();
  9.  
  10. FloatBuffer  texBuffer = ByteBuffer.allocateDirect(4 * 6 * numTris)
  11.  
  12. .order(ByteOrder.nativeOrder()).asFloatBuffer();
  13.  
  14.         public void endFrame()
  15.  
  16.         {
  17.  
  18.                 //
  19.  
  20.                 if(tex == -1)
  21.  
  22.                 {
  23.  
  24.                         Resources res = app.mContext.getResources();
  25.  
  26.                         int resID = res.getIdentifier("font_medium", "drawable", "com.example.android.splatterjump");
  27.  
  28.                         tex2 = this.loadBitmap(app.mContext, gl, R.drawable.back).textureName;
  29.  
  30.                         tex = this.loadBitmap(app.mContext, gl, resID).textureName;
  31.  
  32.                 }
  33.  
  34.                
  35.  
  36.                 gl.glClearColor(0.7f,0.0f,0.7f,1.0f);
  37.  
  38.                 gl.glClear(gl.GL_COLOR_BUFFER_BIT);
  39.  
  40.                
  41.  
  42.                
  43.  
  44.                         final int FLOAT_SIZE = 4;
  45.  
  46.                         for(int i = 0; i < numTris; i++)
  47.  
  48.                         {
  49.  
  50.                                 float x = (float) Math.random() * 200.f;
  51.  
  52.                                 float y = (float) Math.random() * 400.f;
  53.  
  54.                         mVertexBuffer.put(i*9+0, x+0.0f);
  55.  
  56.                         mVertexBuffer.put(i*9+1, y+0.0f);
  57.  
  58.                         mVertexBuffer.put(i*9+2, 0.0f);
  59.  
  60.                         mVertexBuffer.put(i*9+3, x+20.0f);
  61.  
  62.                         mVertexBuffer.put(i*9+4, y+0.0f);
  63.  
  64.                         mVertexBuffer.put(i*9+5, 0.0f);
  65.  
  66.                         mVertexBuffer.put(i*9+6, x+0.0f);
  67.  
  68.                         mVertexBuffer.put(i*9+7, y+20.0f);
  69.  
  70.                         mVertexBuffer.put(i*9+8, 0.0f);
  71.  
  72.                         }
  73.  
  74.                 for(int i = 0; i < numTris; i++)
  75.  
  76.                 {
  77.  
  78.                         texBuffer.put(i*6+0, 0.0f);
  79.  
  80.                         texBuffer.put(i*6+1, 0.0f);
  81.  
  82.                         texBuffer.put(i*6+2, 0.05f);
  83.  
  84.                         texBuffer.put(i*6+3, 0.0f);
  85.  
  86.                         texBuffer.put(i*6+4, 0.0f);
  87.  
  88.                         texBuffer.put(i*6+5, 0.05f);
  89.  
  90.                 }
  91.  
  92.  
  93.  
  94.             gl.glEnable(GL10.GL_TEXTURE_2D);
  95.  
  96.  
  97.  
  98.             gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
  99.  
  100.             gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
  101.  
  102.                         gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
  103.  
  104.             gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuffer);
  105.  
  106.                         gl.glBindTexture(GL10.GL_TEXTURE_2D, tex);
  107.  
  108.                         gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3 * numTris);
  109.  
  110.  
Parsed in 0.039 seconds, using GeSHi 1.0.8.4
astrath
Junior Developer
Junior Developer
 
Posts: 12
Joined: Thu Apr 08, 2010 1:03 pm

Top

Return to Android 2D/3D Graphics - OpenGL Tutorials

Who is online

Users browsing this forum: No registered users and 5 guests