Android OpenGL: Possibly Running Out Of Memory? - android

I have been trying to fix this issue for weeks but I'm at the point where I dont know what to do now.
I think that some Android devices dont have enough memory to load the amount of textures, although it could be something else causing the issue, as I said I really dont know what to do with this.
There are 28 PNG's being loaded all 1024x1024 which come to a total of 4.8megs. Below is the OpenGL method for loading textures
GL10 gl = glGraphics.getGL();
int[] textureIds = new int[1];
gl.glGenTextures(1, textureIds, 0);
textureId = textureIds[0];
InputStream in = null;
try {
in = fileIO.readAsset(fileName);
Bitmap bitmap = BitmapFactory.decodeStream(in);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
setFilters(GL10.GL_LINEAR , GL10.GL_LINEAR);
gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
width = bitmap.getWidth();
height = bitmap.getHeight();
bitmap.recycle();
} catch(IOException e) {
throw new RuntimeException("Couldn't load texture '" + fileName +"'", e);
} finally {
if(in != null)
try { in.close(); } catch (IOException e) { }
}
There are no issues on my Desire HD, but on a HTC Cha Cha a lot of textures dont appear at all and on a Galaxy S two textures just appear white. The Cha Cha throws this error while loading textures
02-04 15:46:28.907: E/Adreno200-ES20(1501): override1= 0xfffffffe,
override2= 0xfff *
Oddly if the Cha Cha is locked (OpenGL textures are destroyed) and then unlocked (reloaded textures) the particular textures that were not there initally are now, however different textures are now not visable.
Is this a memory issue? If so is there a way around this?
Thanks

The correct solution is texture compression, not PNG compression. PVR-TC would get you most of what you need. At 4-bpp, you'd go down to 12MB instead of 117MB. Even just using lower bitdepth images, like RGB-565 formats, (16-bits per pixel) would cut your needs in half.
Also, you don't have to use 1024x1024 textures for phones; that's kind of overkill. You could probably get away with 512x512 images. Coupled with PVR-TC, you'd only need about 3MB for all of that texture data.

Related

Texture compression strategy for Android OpenGL ES that caters for alphas

I have a 2d Android game that is currently causing certain devices to run out of memory. I have a number of PNGs (about 10 MBs in total) that I use in the game at various times. At some points in the game, the majority of these need to be displayed at the same time.
I have tried just decreasing the resolution of my images but I am not happy with the quality after doing so.
I have read a number of posts about how to solve these memory issues and as far as I can see, texture compression is the best approach (feel free to correct me if I am wrong). I have also seen this post that covers how to determine which texture compression formats are supported on a device and I understand this part of things: Android OpenGL Texture Compression
My question is two-fold:
Most of my textures require alphas. I know that by default ETC1 does not support alpha, but I also know that when using ETC1 you can create a separate alpha compression as described here: http://sbcgamesdev.blogspot.com/2013/06/etc1-textures-loading-and-alpha.html. Shown in that link is how to apply the alphas using the NDK. I am battling to understand how to do this using the standard OpenGL ES Java wrappers though. Below is how I currently handle textures (i.e. no texture compression). How would I convert this to handle compressed textures where I need to load the alphas separately?
GLGraphics glGraphics;
FileIO fileIO;
String fileName;
int textureId;
int minFilter;
int magFilter;
public int width;
public int height;
private boolean loaded = false;
public Texture(GLGame glGame, String fileName) {
this.glGraphics = glGame.getGLGraphics();
this.fileIO = glGame.getFileIO();
this.fileName = fileName;
load();
}
public void load() {
GL10 gl = glGraphics.getGL();
int[] textureIds = new int[1];
gl.glGenTextures(1, textureIds, 0);
textureId = textureIds[0];
InputStream inputStream = null;
try {
inputStream = fileIO.readAsset(fileName);
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
setFilters(GL10.GL_NEAREST, GL10.GL_NEAREST);
gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
width = bitmap.getWidth();
height = bitmap.getHeight();
bitmap.recycle();
} catch (IOException e) {
throw new RuntimeException("Couldn't load texture '" + fileName + "'", e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// do nothing
}
}
}
loaded = true;
}
public void reload() {
load();
bind();
setFilters(minFilter, magFilter);
glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0);
}
public void setFilters(int minFilter, int magFilter) {
this.minFilter = minFilter;
this.magFilter = magFilter;
GL10 gl = glGraphics.getGL();
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, minFilter);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, magFilter);
}
public void bind() {
GL10 gl = glGraphics.getGL();
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
}
public void dispose() {
loaded = false;
GL10 gl = glGraphics.getGL();
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
int[] textureIds = { textureId };
gl.glDeleteTextures(1, textureIds, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
}
public boolean isLoaded() {
return loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
My understanding is that I would have to provide 4 compressed textures (one for each format) and a fall back uncompressed PNG for each of my images to support a wide range of devices. My concern is the required disk size increase that this will cause. Is there any solution to this? i.e. use compressed textures in order to lower the memory usage of my game without causing the size of the game on disk to explode?
Rather than providing alpha textures in 4 different compression formats, a better approach is to split the alpha from the images and use ETC1 for the color and possibly even the alpha part of the images. The tricky part is that you must separate the alpha from each image into separate texture files and then write a fragment shader for OpenGL ES that samples from these texture pairs using two samplers and re-combines them. The shader code would be like this:
uniform sampler2D sampler_color;
uniform sampler2D sampler_alpha;
varying vec2 texCoord;
void main()
{
vec3 vColor = texture2D(sampler_color, texCoord);
float fAlpha = texture2D(sampler_alpha, texCoord);
gl_FragColor = vec4(vColor, fAlpha);
}
This will work on over 99% of Android devices and allow all of your alpha textures to be compressed, which not only makes them smaller, but they will load faster too.

Draw part of texture in AndEngine

So i've been using ligbdx for some time, and it was really simple there to do such thing. So what i want to achieve is that when i have a large texture i would like to get a part of that texture by giving x,y (where to start cutting from) and width,height (size of cut part) and later use that part as a sprite or anything that is possible to be drawn on the andengine scene.
In libgdx it ws like that:
//loads file from assets into texture
Texture texture = new Texture(Gdx.files.internal("data/texture5.png"));
//cuts a part of it into drawable element
TextureRegion part = new TexureRegion(texture, x, y, width, height);
and part was just that section of the texture i needed to draw later on the screen. Is it really so hard to do in andengine that nowwhere on the internet i couldnt find any answer for 2h of searching? :)
I have just searched for hours and got nothing. But accidentally i tried this, and it worked. It's an old question but whatever. Maybe this can help.
ITexture texture = null;
try {
texture = new BitmapTexture(engine.getTextureManager(),
new IInputStreamOpener() {
public InputStream open() throws IOException {
return engine.getAssets().open(path);
}
});
texture.load();
} catch (IOException e) {
e.printStackTrace();
}
ITextureRegion texturePart = new TextureRegion(texture, x, y, width, height);

Android OpenGL textures: creating and deleting them on the fly

I am currently implementing a 3D viewer which basically renders a subset of all the images the user has on his SD Card. The closest matching product I would think of would be CoolIris:
It simply show a scrolling board of N tiles on screen, each showing different images, with new tiles entering the screen and showing new images.
Now for my problem: I have the program working and rendering nicely the quads. When a quad goes out of the screen, it gets recycled/released. And new quads keep on being added to the tile board before they enter the screen.
Because there can be hundreds of images, the textures need to be created and deleted on the fly (so that we don't run out of memory). The problem I have is that after I delete textures, newly created textures seem to get some IDs of other textures currently in use.
My rendering loop looks like this:
void render(GL10 gl) {
0. Move the camera
// Tile board maintenance
1. Remove tiles out of screen
2. Add new tiles which are about to enter screen
// Texture handling
3. glDeleteTextures on all unused textures followed by glFlush
4. For newly used images
- Create corresponding Bitmap
- Create the OpenGL texture, followed by glFlush
- Release the Bitmap
// Rendering
5. Render the tile (using a textured quad)
}
To give a better idea of how the data is organised, here is an overview of the classes:
TileBoard {
Image[] allImages;
Tile[] board;
}
Tile {
Image image;
}
Image {
String path;
int textureId;
int referenceCount;
}
Texture creation code:
protected void loadSingleTexture(GL10 gl, long objectId, Bitmap bmp) {
int[] textures = new int[1];
gl.glGenTextures(1, textures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
gl.glFlush();
if (bmp != null) bmp.recycle();
if (listener != null) listener.onTextureLoaded(gl, objectId, textures[0]);
}
Texture deletion code:
// pendingTextureUnloads is a Set<Integer>
if (pendingTextureUnloads.size() > 0) {
int[] textureIds = new int[pendingTextureUnloads.size()];
int i = 0;
Iterator<Integer> it = pendingTextureUnloads.iterator();
while (it.hasNext()) {
textureIds[i] = it.next();
}
gl.glDeleteTextures(textureIds.length, textureIds, 0);
gl.glFlush();
}
I have solved the problem: the issue was that you have to keep the texture array passed to glGenTextures and reuse it.
Here is the modified overview for the ones who will have the same problem:
Image {
String path;
int[] textureIds;
int referenceCount;
}
Texture creation code:
// Notice that I don't allocate the int[] at the beginning but use the one of the image
protected void loadSingleTexture(GL10 gl, Image img, Bitmap bmp) {
gl.glGenTextures(1, img.textureIds, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, img.textureIds[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
gl.glFlush();
if (bmp != null) bmp.recycle();
}
Texture deletion code:
foreach Image img {
gl.glDeleteTextures(img.textureIds.length, img.textureIds, 0);
}
gl.glFlush();
I know you said that you solved your issue, but I think I noticed your error in the first bit of code. Look at the while loop in your texture deletion code, you are not increasing the index i for your array, so you will continuously assign the first index until you reach the last entry in pendingTextureUnloads, the rest of the indices will be 0 (null). That might be problematic.
And by the way, I have got texture generation working by not reusing the array, just returning the index that was generated by glGenTextures. My code is to the line equal to yours, except from creating a new int array in the beginning of the method and returning the int at the first index at the end. Your code for texture generation should work, the error was just in the texture deletion.

Android OpenGL 1.0 different texture for each face of the cube with texture atlas

I have a cube, I can assign for it one texture, but I would like to assign different texture for each face. I have a 512x512 texture atlas, with four tiles, each 256x256. I use the NeHe ports, so for one texture the load is:
public void loadGLTexture(GL10 gl, Context context) {
InputStream is = context.getResources().openRawResource(R.drawable.test);
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
is = null;
} catch (IOException e) {
}
}
I have tried to find solutions, but I was not able to find with texture atlas, only with loading as much texture as I want to use, then assign them to the faces, but because of the performance it's not really good for me. I hope someone can help me!
Thanks in advance!
PS: Which is faster, creating a cube with the coordinates writing in the code, or loading a cube model from an .obj file?

How to load animation frames as textures in OpenGL ES?

I'm new to OpenGL ES and developing a simple 2D game. However, I'm confused as to how I can go about loading multiple animation frames as textures (for the player character). I've tried to load a different image every time the character is rendered, but that's too slow.
Here is my texture loading code thus far:
public void loadGLTexture(GL10 gl, Context context) {
InputStream[] is=new InputStream[3];
is[0]= context.getResources().openRawResource(R.drawable.r1);
is[1]= context.getResources().openRawResource(R.drawable.r2);
is[2]= context.getResources().openRawResource(R.drawable.r3);
try {
bitmap[0]= BitmapFactory.decodeStream(is[0]);
bitmap[1]= BitmapFactory.decodeStream(is[1]);
bitmap[2]= BitmapFactory.decodeStream(is[2]);
} finally {
try {
is[0].close();
is[1].close();
is[2].close();
is = null;
} catch (IOException e) {
}
}
gl.glGenTextures(3, textures,0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap[0], 0);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap[1], 0);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap[2], 0);
bitmap[0].recycle();
bitmap[1].recycle();
bitmap[2].recycle();
}
How can I make all three images accessible through an array?
You need to call glBindTexture before every texImage2D. Currently you are loading all three images into textures[0].
Don't try to load all textures at once. Change your function to load only one texture and just call it three times. You should be able to do:
textures[0]=loadGLTexture(GL10,context,R.drawable.r1);
textures[1]=loadGLTexture(GL10,context,R.drawable.r2);
textures[2]=loadGLTexture(GL10,context,R.drawable.r3);
You can place all the frames of animation on a single texture and use texture coordinates to select which one to use

Categories

Resources