Hi Robert, I have recently got in deep with opengl and found it not soooo hard to get to grips with. Many thanks to you, for some good reading on how to get to grips and your live wallpaper stuff...
Butt, I somehow get the feeling that without using native code its far too slow to use for games.. I have added "Brainy Cards" in the market an opengl card game which works 1A on my nexus (its just a an engine beta at the moment though, playable but beta) If I try the same game on my Dream / Magic, I only get a 10FPS after turning everything off (lighting etc)
Brainy Cards
Using GL11 extentsion is also a nightmare because it seems that people with phones rooted to higher version accept the GL11 extentsions but they are not really implemented..
Im hoping VBO will speed everything up but most phones dont have GL11 or are not implented correctly
I would be very gratefull if we could do an experience exchange...
Ok so there's some room to optimize here:
First of all, you can enable your client states all at the beginning of the draw call or even in the gl init code if they will never change. You don't need to constantly re-enable vertex array, etc because it will stay enabled until you disable it. That's not where all your performance problems are coming from but is good practice.
The first thing I'd do is optimize the texturing. If I understand correctly, each sprite is a card and you draw up to 52 of them? 52 texture binds is a lot when you could jam all of the cards into a 512x512 texture and bind once before the loop, then have UV coordinates for each card that you use when you draw the card. That's the first thing I'd do - put the whole deck into one texture.
2000 faces is pretty big for the scene you've got. You could do it in nearly the same quality for a fraction of that.
Always with opengl - the optimization rules are:
Less texture binds
Less texture size (why draw 512 when 256 looks as good in a case)
Draw fewer faces
Use lower quality filtering (I can't see where you load textures but start with nearest neighbor or make that a user-configurable option)
The textures are already only 256px (code below show it will use nearest), the client stats helped 2-5fps however, and Im reprogramming to sort the "sprites" so that all textures are drawn together... Packing all the cards into into one texture is also do able but I was hoping to use the same VBO pointers for each card just a different texture, by shifting the normal that would increase my normal pointer objects? would that mean a speed decrease against my speed increase from having one texture..
I read somewhere that qualcomm tricked a little with there neocore and are using some qualcomm extensions not documented any ideas?
Here my bitmap loader..
final Matrix matrix = new Matrix();
matrix.preScale(flipV ? -1 : 1, flipH ? -1 : 1);
// not sure why the textures are flipped..
final Bitmap b = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
gl.glBindTexture(GL11.GL_TEXTURE_2D, textureId);
if (b.getWidth() > 512) {
gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_NEAREST);
gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);
GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, b, 0);
if (gl instanceof GL11 || glError("No MimMap Creation Available will use intern..", gl) != 0) {
gl.glBindTexture(GL11.GL_TEXTURE_2D, textureId);
gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_NEAREST);
gl.glGetError();// reset errors..
GLHelpers.buildMipmap(gl, b);
glError("Intern MimMap Build ...", gl);
}
} else {
L.i("Using low texture");
gl.glBindTexture(GL11.GL_TEXTURE_2D, textureId);
gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, b, 0);
}
b.recycle();
bitmap.recycle();
It appears as though you're specifying bilinear filtering for textures larger than 512 but nearest for smaller. Here's what I recommend:
Change your code so that you can configure from some settings menu what filtering you want. Always use mipmaps (they are faster on your texel unit). Always do the same thing for every texture regardless of size. Mipmapping will usually benefit everything.
Filtering options:
None - mag=GL_NEAREST min=GL_NEAREST_MIPMAP_NEAREST
Linear - mag=GL_LINEAR min=GL_NEAREST_MIPMAP_NEAREST
Bilinear - mag=GL_LINEAR min=GL_LINEAR_MIPMAP_NEAREST
Trilinear - mag=GL_LINEAR min=GL_LINEAR_MIPMAP_LINEAR
Then test with no filtering (the "None" option) and see what kind of fps you get on the 1st gen phones. I bet it will be much better :)
Consolidating things into fewer VBOs won't save you too much unless you are making it so that you get to use overlapping data and bind fewer times.
Are you saying that your card textures are each 256px and that you draw (bind to) all 52 cards for the scene every frame?
Ok thats some good advice, Im working on the changes...
Yep each card had a texture 128*128 , im changing it... (this was my first attempt at opengl :) )
you may be able to help with a different problem Im using your GLWallpaper service for "Brainy Droid Live Wallpaper" and Im getting feedback from user with motorola droid saying there seeing no text in the bubble, It works on most other devices. From the logs I have been sent I have noticed that the texture ids from glGenTextures are strange 120007 & 3288881 for example on other devices I get 1 and 2...
any ideas?
plus my first crash in the new thingy in the developer console:
java.lang.IllegalArgumentException:
at com.google.android.gles_jni.EGLImpl.eglGetConfigAttrib(EGLImpl.java:-2)
at com.bac.utils.droidpaper.BaseConfigChooser$ComponentSizeChooser.findConfigAttrib(GLWallpaperService.java:72)
at com.bac.utils.droidpaper.BaseConfigChooser$ComponentSizeChooser.chooseConfig(GLWallpaperService.java:53)
at com.bac.utils.droidpaper.BaseConfigChooser.chooseConfig(GLWallpaperService.java:112)
at com.bac.utils.droidpaper.EglHelper.start(GLWallpaperService.java:270)
at com.bac.utils.droidpaper.GLThread.guardedRun(GLWallpaperService.java:449)
at com.bac.utils.droidpaper.GLThread.run(GLWallpaperService.java:622)
Although Im not sure if this is related..
Sorry I'm not going to be of much use on the live wallpapers... I have problems with mine as well and I don't believe they are from the code I use. I'm not sure what to do about those right now.
Why does it matter what the value of glGenTextures returns? That's just a handle you keep - you don't ever need to care what the actual value is.
Are you making sure to run your apps in full native res always - meaning in your manifest you have all the resizable, high/medium/low density stuff turned on?
It just seems strange what the Droid gives in comparison.. plus on that device it doesnt work..
I dont use all the stupid resizeable drawables stuff.. it seem silly for opengl, the textures are straight from assets too make sure all goes well.. mmm I could check the if the BitmapFactory has any density silly stuff...
Do you do your drawing with native code or just the animation? or the other way round.. Im thinking about converting my world renderer to native code, but would like to have the Rules/Animation updates in java. Is this recommendable?
Also I have noticed the market still has a Zero value for installations but my ET stats (call home) shows more that 300, do your wallpapers and other apps get the same?
ps. if you would like to give me some feedback http://www.appbrain.com/browse/dev/Brainy+Air+Contagion lists my apps..
Sorry for getting back so late but I was busy converting the code to use VBO's I have also just released a live wallpaper using some of your examples.
The way I see it at the moment is that the only was to get round the speed problems is to ignore all older devices or use native code.. bummer..
So I also have been getting started using c++ so my heads burning at the moment reading up..
I dont see much speed improvement with VBO's whats your experience..
VBOs start to matter when dealing with larger amounts of data. The scenes in Deadly Chambers are usually around 2000 verts and so there is certainly an improvement using VBOs for those.
I think that if you were to do a test drawing a few quads with and without vbos, you wouldn't see much change. If you were to then try drawing 5000 vert/uv/normal/indices with and without vbos, you will see a difference.
Well what I've found is that while it's not too hard to figure out how to get things to render at first, it does take a lot of work and research to figure out the optimizations that will give you good framerates on complex scenes. I'd be happy to try out your app and talk through your rendering code to figure out how you can improve that.
I am 100% positive that you can get 30FPS for the screenshot you posted here. Post an overview of how you're rendering and we'll start there..
This is basically the code im using to draw my world
public void drawWorld(final GL11 gl) throws Exception {
if (dirtyWorld)
initWorld(gl);
gl.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
// setup the camera
if (cameraLookAtSpriteId > -1) {
final GLSprite sprite = sprites.get(cameraLookAtSpriteId);
GLU.gluLookAt(gl, camera.px, camera.py, camera.pz, sprite.position.px, sprite.position.py, sprite.position.pz, 0, 1.0f, 0);
} else if (cameraLookAt != null) {
GLU.gluLookAt(gl, camera.px, camera.py, camera.pz, cameraLookAt.px, cameraLookAt.py, cameraLookAt.pz, 0, 1.0f, 0);
} else {
gl.glTranslatef(camera.px, camera.py, camera.pz);
gl.glRotatef(camera.rx, 1.0f, 0.0f, 0.0f);
gl.glRotatef(camera.ry, 0.0f, 1.0f, 0.0f);
gl.glRotatef(camera.rz, 0.0f, 0.0f, 1.0f);
}
// lighting
if (lighting)
gl.glEnable(GL11.GL_LIGHTING);
else
gl.glDisable(GL11.GL_LIGHTING);
// draw sprites
int lastMaterialId = -1;
gl.glFrontFace(GL11.GL_CCW);
for (int s = 0; s < sprites.size(); s++) {
final GLSprite sprite = sprites.get(s);
if (!sprite.invisible) {
// move to position..
gl.glPushMatrix();
gl.glTranslatef(sprite.position.px + sprite.shift.px, sprite.position.py + sprite.shift.py, sprite.position.pz + sprite.shift.pz);
gl.glRotatef(sprite.position.rx + sprite.shift.rx, 1.0f, 0.0f, 0.0f);
gl.glRotatef(sprite.position.ry + sprite.shift.ry, 0.0f, 1.0f, 0.0f);
gl.glRotatef(sprite.position.rz + sprite.shift.rz, 0.0f, 0.0f, 1.0f);
gl.glScalef(sprite.scale.px, sprite.scale.py, sprite.scale.pz);
if (sprite.glMaterialId > -1 && sprite.glMaterialId < materials.size() && sprite.glMaterialId != lastMaterialId) {
L.i("Using Material..." + materials.get(sprite.glMaterialId));
materials.get(sprite.glMaterialId).set(gl);
lastMaterialId = sprite.glMaterialId;
}
if (sprite.glTextureId > -1)
gl.glBindTexture(GL11.GL_TEXTURE_2D, sprite.glTextureId);
gl.glEnableClientState(GL11.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL11.GL_NORMAL_ARRAY);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, sprite.glVectorVBO);
gl.glVertexPointer(3, GL11.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, sprite.glNormalVBO);
gl.glNormalPointer(GL11.GL_FLOAT, 0, 0);
if (sprite.glTextureVBO > -1) {
gl.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, sprite.glTextureVBO);
gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
}
if (sprite.glColorVBO > -1) {
gl.glEnableClientState(GL11.GL_COLOR_ARRAY);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, sprite.glColorVBO);
gl.glColorPointer(4, GL11.GL_FLOAT, 0, 0);
}
gl.glDrawArrays(GL11.GL_TRIANGLES, 0, sprite.glFaceCount);
if (sprite.glColorVBO > -1)
gl.glDisableClientState(GL11.GL_COLOR_ARRAY);
if (sprite.glTextureVBO > -1)
gl.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
gl.glDisableClientState(GL11.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL11.GL_NORMAL_ARRAY);
gl.glPopMatrix();
}
}
if (gl.glGetError() == 1282)
dirtyWorld();
I init the VBOS like this
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vbVector);
gl.glBufferData(GL11.GL_ARRAY_BUFFER, model.faceCount * 3 * 4, model.vertexData, GL11.GL_STATIC_DRAW);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vbNormal);
gl.glBufferData(GL11.GL_ARRAY_BUFFER, model.faceCount * 3 * 4, model.normalData, GL11.GL_STATIC_DRAW);
I have 52 cards drawn separately with each 12 faces and a different (very low) texture and then 6 objects with round about 100-200 faces for each and a texture.
makes roundabout 6000 vertices 2000 faces...
Im I doing anything wrong?
thx..
ps. one hand washes the other.. http://garydavidson.eu/stats.php shows some pretty usefull stats on deployed apps, all per ajax and updates every 5min (works on android browser too) and beats the developer console crap.. if you wish Ill send you the code..