Out of memory

Started by Kaiidyn, February 24, 2011, 02:41:09 PM

Previous topic - Next topic

EgonOlsen

#15
preWarm() can only be found in the latest beta, not in the "official" release. Visit the "new version" thread to download the latest beta's jar. About the memory issue...i'm not 100% sure what you are doing, but if you constantly create new textures that are supposed to replace former ones, try this (taken from here: http://www.jpct.net/forum2/index.php/topic,1920.0.html):


TextureManager.getInstance().unloadTexture(fb, TextureManager.getInstance().getTexture("texture"));
TextureManager.getInstance().replaceTexture("texture", t);

Kaiidyn

If the preWarm() is only in the latest beta, then thats the one I'm using, as that does work..
I am in school right now, so cant check the polycount stuff yet, will do that when I get to my brother's place,
will be there around like.. I think that will be around 5pm..
Clean code is simple and direct. Clean code reads like well-written prose. Clean code never obscures the designer's intent but rather is full of crisp abstractions and straightforward lines of control. - Grady Booch

EgonOlsen

Quote from: Kaiidyn on February 25, 2011, 11:18:46 AM
If the preWarm() is only in the latest beta, then thats the one I'm using, as that does work..
Well...remember that the .mp3 file in the 7z is actually a zip, not a ser. Maybe that's the problem.

icarusfactor

I have tried different methods of clearing the data from nulls recycles and garbage collection calls still to no avail. when I try  the removeAndUnload or Unload methods it just make the problem worse or die faster (i.e. the mem usage grows even faster). I have tried the preWarm method does not work at all for my situation.

public void Text2PosTexture( String texID, String text , int startx , int starty, int fontsize , int Rcol , int Gcol, int Bcol )
{
Log.d(TAG, "public void Text2PosTexture( String texID ,String text , int startx , int starty, int fontsize , int Rcol , int Gcol, int Bcol )" );
int[] letterpos = new int[1];
         int spacing=0;
         //Log.d(TAG, "text.length="+text.length());
         int[] fontType = new int[512 * 512];   
         int[] blitletter = new int[32 * 32];   

         Bitmap immutable_thatfont = Bitmap.createBitmap( 1024, 1024, Bitmap.Config.ARGB_4444 );
font_table.fontTarget = immutable_thatfont.copy(Bitmap.Config.ARGB_4444, true);

         //choose font size 
switch( fontsize )
   {
                case 1:
//normal 16
                //Log.d(TAG, "16pt");
                fontType = font_table.pixles_16pt;
                spacing = font_table.MEDIUM_FONT_SPACING;
break;
case 2:
//large 32
                //Log.d(TAG, "32pt");
                fontType = font_table.pixles_32pt;
                spacing = font_table.LARGE_FONT_SPACING;
break;           
default:
//small 14
                //Log.d(TAG, "14pt");
                fontType = font_table.pixles_14pt;
                spacing = font_table.SMALL_FONT_SPACING;

break;
}



   //choose color to change to. H W  RGB
   fontType = modBitmapColor( fontType, 512 , 512 , Rcol , Gcol , Bcol );

         //this will convert every leter of the string to a position on the font
         //table .
       for (int n=0;n<text.length();n++)
        {
         letterpos =  Letter2TexturePos( text.substring(n,n+1) );
           //return a blitmap letter to post to blitarray
           blitletter = getBlitLetter( fontType, 512 , letterpos[1] , letterpos[0]  );
           //all fonts are 32x32 chunks will copy to a 1024x1024(6) texture.
           font_table.blitmapZ =  blitIntArray( font_table.blitmapZ , 6, blitletter , startx + (n * spacing),starty, 32 , 32 );
        }

         // Int[] to Bitmap
         font_table.fontTarget.setPixels( font_table.blitmapZ , 0, 1024 , 0, 0, 1024 , 1024 );
  //place font into texture.
         Texture allfonts = new Texture( font_table.fontTarget  , true  );         
         immutable_thatfont.recycle();
         immutable_thatfont = null;
System.gc();
               
        TextureManager.getInstance().unloadTexture( fb, TextureManager.getInstance().getTexture(  texID  ) );
        TextureManager.getInstance().replaceTexture(texID, allfonts );
}



If i comment out the last two lines everything works fine , but it will only  show the background with no text and the memory stays in the safe zone. if I start replacing the texture with the image of my background and text ,it just goes up and up and up until it crashes.

Which would lead me to believe it is the replacement method and not anything else although I now have really cleaned up the mem usage overall, just not the leak.

EgonOlsen

#19
I can't verify the leak. I wrote myself this little test case. It's based on your method but stripped, because i don't have all this font-stuff. I simply colored the Bitmap in a random color. To tweak memory usage a little bit, i release immutable_thatfont a little earlier (what's the point of that bitmap anyway?) and added Config.unloadImmediately=true at the beginning of onCreate(). However, none of these is actually needed to make it work in the 2.2 emulator.

While playing around with preWarm(), i've noticed one thing that i consider to be a VM flaw: If you add this call at the end of the test2PosTexture-method, it crashes immediately with an OOM-Exception. If you let the method return normally and add the call right after that (like in this example), it works just fine. Looks like as if some memory won't be released even if it could unless the method returns...might be a GC flaw or "optimization".

However, this test case runs as it should for me. What happens in your case and why it goes away if you omit the call to replaceTexture() has to be something else. I *think* that is has to do with the fact that you are running on the edge of memory anyway. You create two large bitmaps, each 4mb in size plus the actual texture using another 4mb plus the memory needed to upload it plus the memory on the gpu (another 4mb) plus the memory that your application itself needs etc. Combine that with the fact that Android/Dalvik starts to act weird when running close to its memory limits especially when working with Bitmaps and you might run into trouble. If you omit the call to replace, there's no new upload and maybe the texture is smaller (don't know, impossible to tell from that code snippet).

Isn't it possible to reduce the size of these monster bitmaps? Lets say to 512*512 or something like that? Why does it has to be that large?

Anyway, here's the test case:

package com.threed.jpct.example;

import java.lang.reflect.Field;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

import com.threed.jpct.Camera;
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Logger;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.RGBColor;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.BitmapHelper;
import com.threed.jpct.util.MemoryHelper;

/**
* @author EgonOlsen
*
*/
public class HelloWorld extends Activity {

private static HelloWorld master = null;

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer fb = null;
private World world = null;
private RGBColor back = new RGBColor(50, 50, 100);

private float touchTurn = 0;
private float touchTurnUp = 0;

private float xpos = -1;
private float ypos = -1;

private Object3D cube0 = null;
private Object3D cube1 = null;
private Object3D cube2 = null;
private Object3D cube3 = null;
private Object3D dummy = null;

// private Texture renderTarget = null;

private TextureManager tm = TextureManager.getInstance();

private int fps = 0;

private int cnt = 0;

protected void onCreate(Bundle savedInstanceState) {

Config.unloadImmediately = true;
Logger.setLogLevel(Logger.LL_VERBOSE);

Logger.log("onCreate");

if (master != null) {
copy(master);
}

super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());

mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
EGLConfig[] configs = new EGLConfig[1];
int[] result = new int[1];
egl.eglChooseConfig(display, attributes, configs, 1, result);
return configs[0];
}
});

renderer = new MyRenderer();
mGLView.setRenderer(renderer);
setContentView(mGLView);
}

@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}

@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}

protected void onStop() {
super.onStop();
}

private void copy(Object src) {
try {
Logger.log("Copying data from master Activity!");
Field[] fs = src.getClass().getDeclaredFields();
for (Field f : fs) {
f.setAccessible(true);
f.set(this, f.get(src));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public boolean onTouchEvent(MotionEvent me) {

if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}

if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}

if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;

xpos = me.getX();
ypos = me.getY();

touchTurn = xd / -100f;
touchTurnUp = yd / -100f;
return true;
}

try {
Thread.sleep(15);
} catch (Exception e) {
// No need for this...
}

return super.onTouchEvent(me);
}

protected boolean isFullscreenOpaque() {
return true;
}

class MyRenderer implements GLSurfaceView.Renderer {

private long time = System.currentTimeMillis();
private boolean stop = false;

public MyRenderer() {
}

public void stop() {
stop = true;
}

public void onSurfaceChanged(GL10 gl, int w, int h) {
if (fb != null) {
fb.dispose();
}
fb = new FrameBuffer(gl, w, h);

if (master == null) {

world = new World();
world.setAmbientLight(255, 255, 255);

Texture texture = new Texture(BitmapHelper.rescale(BitmapHelper.convert(getResources().getDrawable(R.drawable.icon)), 64, 64));
TextureManager.getInstance().addTexture("texture", texture);
texture.setMipmap(false);

dummy = Object3D.createDummyObj();

cube0 = Primitives.getCube(10);
cube0.rotateY(-(float) Math.PI / 4f);
cube0.rotateMesh();
cube0.clearRotation();
cube0.calcTextureWrapSpherical();
cube0.setTexture("texture");
cube0.strip();
cube0.build();

cube1 = cube0.cloneObject();
cube2 = cube0.cloneObject();
cube3 = cube0.cloneObject();

world.addObject(cube0);
world.addObject(cube1);
world.addObject(cube2);
world.addObject(cube3);

cube0.translate(-20, -20, 0);
cube1.translate(20, -20, 0);
cube2.translate(-20, 20, 0);
cube3.translate(20, 20, 0);

cube0.addParent(dummy);
cube1.addParent(dummy);
cube2.addParent(dummy);
cube3.addParent(dummy);

Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 100);

// renderTarget = new Texture(256, 256, RGBColor.RED);

MemoryHelper.compact();

if (master == null) {
Logger.log("Saving master Activity!");
master = HelloWorld.this;
}

// Logger.setLogLevel(Logger.DEBUG);
}
}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}

public void onDrawFrame(GL10 gl) {

try {
if (!stop) {
if (touchTurn != 0) {
dummy.rotateY(touchTurn);
touchTurn = 0;
}

if (touchTurnUp != 0) {
dummy.rotateX(touchTurnUp);
touchTurnUp = 0;
}

cnt++;

/*
* Config.autoMaintainAspectRatio=false;
* fb.setRenderTarget(renderTarget);
* fb.clear(RGBColor.BLUE);
* fb.blit(tm.getTexture("texture"), 0, 0, 0, 0, 64, 64,
* fb.getWidth(), fb.getHeight(), -1, false, null);
* fb.display(); fb.removeRenderTarget();
* Config.autoMaintainAspectRatio=true;
*/

text2PosTexture("texture");
// TextureManager.getInstance().preWarm(fb);

fb.clear(back);
world.renderScene(fb);
world.draw(fb);
fb.display();

if (System.currentTimeMillis() - time >= 1000) {
Logger.log(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
fps++;
} else {
if (fb != null) {
fb.dispose();
fb = null;
}
}
} catch (Exception e) {
Logger.log(e, Logger.MESSAGE);
}
}
}

public void text2PosTexture(String texName) {
Bitmap immutable_thatfont = Bitmap.createBitmap(1024, 1024, Bitmap.Config.ARGB_4444);
Bitmap fontTarget = immutable_thatfont.copy(Bitmap.Config.ARGB_4444, true);
int red = (int) (255d * Math.random());
int green = (int) (255d * Math.random());
int blue = (int) (255d * Math.random());
fontTarget.eraseColor(Color.rgb(red, green, blue));

immutable_thatfont.recycle();
immutable_thatfont = null;

MemoryHelper.compact(); // Actually not needed

Logger.log("Creating new texture!");
Texture allfonts = new Texture(fontTarget, true);
fontTarget.recycle();
fontTarget = null;

Logger.log("Unloading old texture!");
TextureManager.getInstance().unloadTexture(fb, TextureManager.getInstance().getTexture(texName));

Logger.log("Replacing with new texture!");
TextureManager.getInstance().replaceTexture(texName, allfonts);
}
}








icarusfactor

Add both lines to my app and it works like a charm, thanks.

added header

import com.threed.jpct.Config;

in oncreate

Config.unloadImmediately=true;






Kaiidyn

#21
Vertex count: 390150
Triangle count: 130050
Clean code is simple and direct. Clean code reads like well-written prose. Clean code never obscures the designer's intent but rather is full of crisp abstractions and straightforward lines of control. - Grady Booch

EgonOlsen

Pffffft....no wonder that you run into memory problems. That's way too much...try to reduce this or split it into parts that can be loaded on demand.