i'm new to android programming and i'm trying to create a relatively big 2D game. I have to use lots of images and objects in my game so I decided to use OpenGL ES. I have several texture atlases, all of them saved as png's because of the transparency. I also know, but i'm not sure why, that I have to use images, which height and width is multiple of two. I test my game on an old HTC Hero running Android 2.3.3. When my picture atlases are 512x512 each, my game has a frame rate of between 50 to 60 fps. When I use 1024x1024 non transparent png, there is no problem - the FPS is again between 50 to 60 fps. But when i decide to use a 1024x1024 transperent PNG's my frame rate drops to 4,5 fps. Could this be a problem related to the age of the device i'm using for testing? These are the OpenGL functions I use each loop to draw batches:
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
//drawing happens here
gl.glDisable(GL10.GL_BLEND);
Thanks in advance :)
I think what you're seeing is that your texture becomes to large to keep in VRAM close to the GPU, so that it has to be fetched from normal RAM and sent over the bus with every frame. From the numbers you posted the maximum usable amount is 1 MB (1024x1024x8bpp for RGB565), so you should stay well below that. You can probably switch to RGBA4444 without seeing the drop in performance. Or go 512x1024x16bpp (RGBA8888) if you need the largest size possible with 8bit colors and transparency.
PS. I wouldn't be surprised if you also see a drop when you start using VBOs in conjunction with the 1024x1024x8bpp, as they also reside in that limited amount of RAM.
Related
I have a problem that has been torturing me for a month, and maybe more. There is a large background of about 9000x13000 pixels. And I am trying to bring this background to the screen. Since my video card only supports textures with a resolution of not more than 8192x8192 pixels, a black area is displayed instead of the texture. I compressed the image to 5000x7000 pixels. Now it is displayed, but the FPS has fallen to 50 (except for one image there is nothing). In addition, the game is also designed to work on mobile devices, and there the limitations of video cards can be 2048x2048 textures, and maybe less. I tried to break my big texture into 117 pieces (each not more than 1024x1024 pixels) and pull them one after another. Now the load of the game is about a minute and when the textures are output the FPS drops to 5! I tried to use filters
texture.setFilter(
Texture.TextureFilter.MipMapNearestNearest,
Texture.TextureFilter.Nearest
);
but the FPS did not go above 10. It also set the useMipMap flag to true when loading. At the moment I'm doing this:
Texture texture=new Texture(file, true);
texture.setFilter(
Texture.TextureFilter.MipMapNearestNearest,
Texture.TextureFilter.Nearest
);
public void render()
{
batch.begin();
batch.draw(texture,0,0,Gdx.graphics.getScreenWidth(),Gdx.graphics.getScreenHeight());
batch.end();
}
Tell me, how can I optimize the output of a large texture?
This texture is a map, so when approaching large screens it should be clearly detailed.
I will be grateful for any help
I didn't test it with libgdx, but I think it worth to try to have several variations of the texture/textures which are selected to be displayed depending on the current zoom level in your game in addition of having an original texture sliced into pieces. Think about loading pictures in Google Maps, when you zoom in.
The idea
Let's define the zoom level, as a number in the following way:
1 is maximum zoom
2 is zoomed out by 2
3 is zoomed out by 4
4 is zoomed out by 8
...
Then here is how the textures are defined:
Slice original texture into pieces of 2048x2048 (~ 7x5 = 35 pieces, texture with size 2048 is supported by the majority of the mobile devices). Use those for the zoom level 1. In this case you will have maximum 4 textures loaded at the same time.
Scale down the original texture by 2 and slice into pieces again. This will give you about 9 textures. Use those for zoom level between 1 and 2.
Scale down one more time => 3 textures. Use those for levels between 2 and 3.
And one more time => for level 3 and greater. You can stop here.
Now when the user zoomed out, you use the texture #4. When the user zoomed in to level 3 or less, you still show the texture #4, while the set of zoomed textures is loading. Then you do a smooth transition using fadeIn/out for better visual impression.
More hints
For faster loading of the textures, consider using compressed textures. The file size of compressed texture is usually comparable to PNG, but it can be loaded much faster into video memory and uses the same amount of RAM as the original file. (Because it does not need to be uncompressed into raw bitmap). Especially consider ASTC. ASTC is not supported by older devices, but I think your requirements are to high to make your game run on them.
Load textures into memory only if user zoomed into the appropriate level. Unload textures after user zoomed out. This should be fast enough, because of the texture compression. I would keep the texture #4 always in memory for smoother UX. Probably it makes sense to keep intermediate levels because of the same reason (e.g. keep textures from set #3, while zoomed in into level 2).
For better loading/unloading of the textures, think more about user behaviour (like what is typical usage of the zoom). Or probably you can use some kind of LRU Cache and unload textures "automatically".
I'm having some trouble using a sprite as background for my scene. I'm setting the background as follows:
Sprite bg = new Sprite(SCENE_WIDTH/2 , SCENE_HEIGHT/2, this.mParallaxBackRegion,getVertexBufferObjectManager());
bg.setCullingEnabled(true);
mScene.setBackground(new SpriteBackground(bg));
Loading of the texture:
this.mParallaxBack = new AssetBitmapTexture(this.getTextureManager(), this.getAssets(), "gfx/_fixed.png", TextureOptions.BILINEAR);
this.mParallaxBackRegion = TextureRegionFactory.extractFromTexture(this.mParallaxBack);
this.mParallaxBack.load();
The png I'm loading is a completely black 960x640 image (same as my scene size), for testing purposes. However, setting the background causes my fps to drop from 60 (when not using the background) to 45 on my HTC Desire. I've tried multiple ways of setting the background, but all seem to be causing the same performance hit. Why does this affect the performance so drastically, and is there something I can do about this?
That is strange that you should get such a big performance hit. But here's one thing to try. Since i seems you likely have other things drawn to the screen besides this background, add the background to the same Texture. OpenGL works faster when there are fewer textures. When openGL has to draw from another texture its switches context *(citation needed) which is a slow operation. So having all your sprites on a single texture makes GL draw calls more efficient.
This alone is not likely to explain the performance problem.
It could also be your antialiasing setting: TextureOptions.BILINEAR.
Bilinear is the highest quality setting. Try using DEFAULT or NEAREST_PREMULTIPLYALPHA and see if that doesn't help.
Also, set your background to ignore updates.
One last thought:
The HTC desire is a fairly old phone at this date (released in 2010) the performance will be in part due to it being an old phone with an old version of android. I test on an HTC incredible, about the same vintage, and you have to be very conservative with your image sizes on that device.
Did you know that you can make your andengine native size 1/2 of the screen size and scale it up using a RatioResolutionPolicy? I used that approach for my first andengine (GLES1) project and had great success on that generation of devices.https://play.google.com/store/apps/details?id=com.plonzogame&hl=en
I have around 2 GiB of textures (all are 256x256 tiles) in uncompressed RGBA 8888 format (the RGB 565 texture format is not an option, because there are lots smooth gradients and shades of gray, which have green tint with 565 format). So I load them on demand, when they should become visible and delete the old ones. The problem is, that there is an annoying FPS drop when I upload them OpenGL. Currently using OpenGL ES 1.1.
I decode the textures in a separate thread (i.e. BitmapFactory.decodeStream(...)) and then send the Bitmap to GL thread and upload it as a texture. When this happens the GL thread is sometimes slowed-down a bit for this upload. I measured the texture upload time and mostly it varys from 1-8ms, in average it is ~2ms. But from time-to-time it is 40-70ms. What can cause this drop?
I also generate mipmaps on the GPU (disabling mipmaps does not affect this behaviour) and here are all the texture parameters:
GLES11.glTexParameterf(GLES11.GL_TEXTURE_2D, GLES11.GL_TEXTURE_MIN_FILTER, GLES11.GL_LINEAR_MIPMAP_NEAREST);
GLES11.glTexParameterf(GLES11.GL_TEXTURE_2D, GLES11.GL_TEXTURE_MAG_FILTER, GLES11.GL_LINEAR);
GLES11.glTexParameterf(GLES11.GL_TEXTURE_2D, GLES11.GL_TEXTURE_WRAP_S, GLES11.GL_CLAMP_TO_EDGE);
GLES11.glTexParameterf(GLES11.GL_TEXTURE_2D, GLES11.GL_TEXTURE_WRAP_T, GLES11.GL_CLAMP_TO_EDGE);
GLES11.glTexParameterf(GLES11.GL_TEXTURE_2D, GLES11.GL_GENERATE_MIPMAP, GLES11.GL_TRUE);
GLES11.glTexEnvx(GLES11.GL_TEXTURE_ENV, GLES11.GL_TEXTURE_ENV_MODE, GLES11.GL_MODULATE);
GLUtils.texImage2D(GLES11.GL_TEXTURE_2D, 0, GLES11.GL_RGBA, bmp, 0);
How can I do this better? E.g. how are Open GL video players done, when they need to load and display many frames per second? Or current web browser which render pages as tiles? Is EGL_image a good option worth looking at? Will be OpenGL ES 2.0 different?
EDIT:
The slow down of loading was because of GC-ing on the GL thread.
In addition to making sure the GC does not kick in, you could do all the bitmap generation and texture upload on a separate thread as described in my answer to a similar question: Threading textures load process for android opengl game
This would prevent you from small frame drops when the upload itself takes to long.
Finally I solved it, but not with native code, nor with EGL_image, but thanks to this video. Fortunatelly I had almost all textures in 256x256 and I am targeting Android 3.2 and newer, so for this scenario there is an easy solution:
BitmapOptions.inBitmap which will reuse one Bitmap for every new tile, so no more furios GC-ing. I had to enlarge the few bitmaps that where smaller than 256x256.
We are to develop a scrolling/zooming scene in OpenGL ES on Android, very much like a level in Angry Birds but more like a level in World Of Goo. More like the latter as the world will not consist of repeated layers as featured in Angry Birds but of a large image. As the scene needs to scroll/zoom and therefore a lot of it will not be visible, I was wondering about the most efficient way to implement the rendering, focusing on the environment only (ie not the objects within the world but background layers).
We will be using an orthographic projection.
The first that comes to mind is creating a large 4 vertices rectangle at world size, which has the background texture mapped to it, and translate/scale this using glTranslatef / glScalef. However, I was wondering if the non visible area outside of the screens boundaries is still being rendered by OpenGL as it is not being culled (you would lose the visible area as well as there are only 4 vertices). Therefore, would it be more efficient to subdivide this rectangle, so non visible smaller rectangles can be culled?
Another option would be creating a 4 vertice rectangle that would fill the screen, then move the background by adjusting its texture coordinates. However, I guess we would run into problems when building bigger worlds, considering the texture size limit. It seems like a nice implementation for repeated backgrounds like AngryBirds has.
Maybe there is another way..?
If someone has an idea on how it might be done in AngryBirds / World of Goo, please share as I'd love to hear. They seem to have implemented a system that allows for the world to be moved and zoomed very (WorldOfGoo = VERY) smoothly.
This is probably your best bet for implementation.
In my experience, keeping a large texture in memory is very expensive on Android. I would get quite a few OutOfMemoryError exceptions for the background texture before I moved to tiling.
I think the biggest rendering bottleneck would be with memory transfer speeds and fill rate instead of any graphics computation.
Edit: Check out 53:28 of this presentation from Google I/O 2009.
You could split the background rectangle into smaller rectangles, so that OpenGL only renders the visible rectangles. You won't have a big ass rectangle with a big ass texture loaded but smallers rectangles with smaller textures that you could load/unload, depending on what is visible on screen...
Afaik there would be no performance drop due to large areas being rendered off-screen, subdividing and culling is normally done just to reduce vertex count, but you would actually be adding to it here.
Putting that aside for now; from the way you phrased the question I am unsure whether you have a large background texture or a small repeating one. If it is large, then you will need to subdivide because of texture size limitations anyway, so the question is moot! If it is small, then I would suggest the second method, fit a quad to the screen and move the background by changing the texture coordinates.
I feel like I may have missed something, though, as I am unsure why you mentioned the texture size limitation issue when talking about the the texture coordinate method and not the large quad method. Surely for both this is not a problem for repeating textures as you can use GL_REPEAT texture wrap mode...
But for both it is a problem for a single large texture unless you subdivide, which would make the texture coordinate tactic way more complicated than necessary. In this case subdividing the mesh along texture subdivisions would be best, and culling off-screen sections. Deciding which parts to cull should be trivial with this technique.
Cheers.
This is just a quick question before I dive deeper into converting my current rendering system to openGL. I heard that textures needed to be in base 2 sizes in order to be stored for rendering. Is this true?
My application is very tight on memory, but most of the bitmaps are not powers of two. Does storing non-base 2 textures consume more memory?
It's true depending on the OpenGL ES version, OpenGL ES 1.0/1.1 have the power of two restriction. OpenGL ES 2.0 doesn't have the limitation, but it restrict the wrap modes for non power of two textures.
Creating bigger textures to match POT dimensions does waste texture memory.
Suresh, the power of 2 limitation was built into OpenGL back in the (very) early days of computer graphics (before affordable hardware acceleration), and it was done for performance reasons. Low-level rendering code gets a decent performance boost when it can be hard-coded for power-of-two textures. Even in modern GPU's, POT textures are faster than NPOT textures, but the speed difference is much smaller than it used to be (though it may still be noticeable on many ES devices).
GuyNoir, what you should do is build a texture atlas. I just solved this problem myself this past weekend for my own Android game. I created a class called TextureAtlas, and its constructor calls glTexImage2D() to create a large texture of any size I choose (passing null for the pixel values). Then I can call add(id, bitmap), which calls glTexSubImage2D(), repeatedly to pack in the smaller images. The TextureAtlas class tracks the used and free space within the larger texture and the rectangles each bitmap is stored in. Then the rendering code can call get(id) to get the rectangle for an image within the atlas (which it can then convert to texture coordinates).
Side note #1: Choosing the best way to pack in various texture sizes is NOT a trivial task. I chose to start with simple logic in the TextureAtlas class (think typewriter + carriage return + line feed) and make sure I load the images in the best order to take advantage of that logic. In my case, that was to start with the smallest square-ish images and work my way up to the medium square-ish images. Then I load any short+wide images, force a CR+LF, and then load any tall+skinny images. I load the largest square-ish images last.
Side note #2: If you need multiple texture atlases, try to group images inside each that will be rendered together to minimize the number of times you need to switch textures (which can kill performance). For example, in my Android game I put all the static game board elements into one atlas and all the frames of various animation effects in a second atlas. That way I can bind atlas #1 and draw everything on the game board, then I can bind atlas #2 and draw all the special effects on top of it. Two texture selects per frame is very efficient.
Side note #3: If you need repeating/mirroring textures, they need to go into their own textures, and you need to scale them (not add black pixels to fill in the edges).
No, it must be a 2base. However, you can get around this by adding black bars to the top and/or bottom of your image, then using the texture coordinates array to restrict where the texture will be mapped from your image. For example, lets say you have a 13 x 16 pixel texture. You can add 3 pixels of black to the right side then do the following:
static const GLfloat texCoords[] = {
0.0, 0.0,
0.0, 13.0/16.0,
1.0, 0.0,
1.0, 13.0/16.0
};
Now, you have a 2base image file, but a non-2base texture. Just make sure you use linear scaling :)
This is a bit late but Non-power of 2 textures are supported under OpenGL ES 1/2 through extensions.
The main one is GL_OES_texture_npot. There is also GL_IMG_texture_npot and GL_APPLE_texture_2D_limited_npot for iOS devices
Check for these extensions by calling glGetString(GL_EXTENSIONS) and searching for the extension you need.
I would also advise keeping your textures to sizes that are multiples of 4 as some hardware stretches textures if not.