Phys2D, profiling and memory leak

I was playing around with Lwjgl and Phys2D to learn how it word, and i came across something “strange”.

I made an arena with 32 cells inside it that go randomly. The arena is made of line (Phys2D) and the cells are a circle (main body) with several circle inside it (fixed joint to the main body) where i apply force on.

http://bonbonchan.bravehost.com/images/screen.html

After several minutes, there is a dramatical drop of framerate.

I try to find out with the profiler of Netbean where it can come from (i’m not use to it and i don’t really know if i understand it all).

http://bonbonchan.bravehost.com/images/profile.html

The first line is with everything.
The second line is with “world.step();” (the Phys2D objet) commented out. And it seems to be the problem.

May be i have misunderstand something in Phys2D or the profiler. If someone can help me to understand.

ps : i have to change of free hosting -_-,

Looks like a leak to me - can you post the test code? I’ve not had seen memory issues on my demos - but I’ve not used it for an age.

Is this with the latest build btw, the last bug I remember was an Arbiter leak.

Kev

I’m using “phys2d-250607.jar” (the lastest build i think). By the way lwjgl 1.1.2 and Java 1.6.0_b105.

My code was a mess :-[, i remake a test code and it is the same :


package cellsoccer;

import net.phys2d.raw.Body;
import net.phys2d.raw.BodyList;
import net.phys2d.raw.FixedJoint;
import net.phys2d.raw.World;
import net.phys2d.raw.shapes.Circle;
import net.phys2d.raw.shapes.Line;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

public class Test
{   
    public  static final float fullRadius = 4.0f*40.f;
    public  static final float radius = 3.2f;
    public  static final float radius2= 1.8f;
    private static final int FRAMERATE = 30;
    private static final int NBITERATION = 5;
    private static final float DAMPING = 1.0f;
    
    private static final int displayWidth = 640;
    private static final int displayHeight = 480;
    
    private boolean finished;
    
    private World   world;
   
    public static void main(String[] args)
    {
      new Test();
      System.exit(0);
    }
    
    public Test()    
    {
       initlwjgl();
       initworld();
       run();
    }
    
    private void initlwjgl()
    {
      DisplayMode chosenMode = null;
 
       try
       {
         DisplayMode[] modes = Display.getAvailableDisplayModes();
         for (int i=0;i<modes.length;i++)
         {
           if ((modes[i].getWidth() == displayWidth) && (modes[i].getHeight() == displayHeight))
           {
             chosenMode = modes[i];
             break;
           }
         }
       }
       catch (LWJGLException e)
       {     
          Sys.alert("Error", "Unable to determine display modes.");
          System.exit(0);
       }
 
       if (chosenMode == null)
       {
         Sys.alert("Error", "Unable to find appropriate display mode.");
         System.exit(0);
       }
      
       try
       {
         Display.setDisplayMode(chosenMode);
         Display.setTitle("Test Phys2D");
         Display.setFullscreen(false);
         Display.setVSyncEnabled(true);
         Display.create();
       }
       catch (LWJGLException e)
       {     
          Sys.alert("Error", "Cant' create Display");
          System.exit(0);
       }
    }
    
    private void initworld()
    {
      world = new World(new net.phys2d.math.Vector2f(0.0f,0.0f),NBITERATION);
       world.setDamping(DAMPING);
    
       for(int i=0;i<36;i++)
       {
         double a1 = i*Math.PI/18.0;
         double a2 = (i+1)*Math.PI/18.0;
        
         Body b = new Body(
             new Line(fullRadius*(float)Math.cos(a1),fullRadius*(float)Math.sin(a1),
                      fullRadius*(float)Math.cos(a2),fullRadius*(float)Math.sin(a2))
                      , Body.INFINITE_MASS);
      
         b.setUserData(this);
         world.add(b);
       }
    }
  
  private void initCells()
  {
    float x,y;
      
    for(int i=0;i<32;i++)
    {
      x = (float)Math.random()*fullRadius;
      y = (float)Math.random()*fullRadius;
              
      Body body = new Body(new Circle(radius),1.0f);
      body.setPosition(x,y);
      body.setUserData(this);
      
      world.add(body);
      
      for (int j=0;j<8;j++)
      {
        if (Math.random()>0.5)
        {
          double a = j*Math.PI/4.0 + Math.PI/8;
          Body body2 = new Body(new Circle(radius),0.001f);  
          body2.setPosition((float)(radius2*Math.sin(a))+x,(float)(radius2*Math.cos(a))+y);
          body2.setRotation((float)(a));
      
          world.add(body2);
          
          BodyList bodies = world.getBodies();
          for (int k=0;k<bodies.size();k++)
          {
            Body aBody = bodies.get(k);
        
            aBody.addExcludedBody(body2);
            body2.addExcludedBody(aBody);
          }
          
          FixedJoint joint = new FixedJoint(body,body2);
          world.add(joint);
          
          body2.setForce((float)(Math.random()-0.5),(float)(Math.random()-0.5));
        }
      }
    }
  }
    
  private void run()
  {
    int compt = 0;
    
    Display.update();
    
    initCells();
    
    finished = false;
    
    while (!finished)
    {
      Display.update();
 
      if (Display.isCloseRequested())
      {
	finished = true;
      } 
      
      logic();
      render();
      Display.sync(FRAMERATE);
    }
  }
  
  private void render()
  {
    GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
    GL11.glLoadIdentity();
  }
  
  private void logic()
  {
    world.step();
  }
}

And another question : what is the bitmask on the body ? ???

I’ll try and work out whats going on tonight.

The bitmask is to allow collision groups. Each bit represents a group and they’re compared at collision time, I’ve never used it but I believe it was originally intended to allow different collision methods within different groups.

Kev

Just gave it a quick try and there’s no memory increase here - runs with the 16meg default stack. I’m running against the latest code from SVN which appears to have been updated on 20/08/07, so it may just be that last bug fix isn’t in the most recent build.

I’ll kick off a new build now.

Kev

BUILD SUCCESSFUL

http://www.cokeandcode.com/phys2d/

JAR HERE: http://www.cokeandcode.com/phys2d/source/builds/phys2d-060907.jar

Kev

It run for 20mn without any problem now, thanks ;D

I should look in more detail how bitmask works since i have a type of body that collide with nothing.

You can also use exlusison on bodies for that.

Glad it worked out :slight_smile:

Kev

I continue to work with Phys2D :wink:

For exclusion, i use bitmask :


    public  static final long  MASK_NONE       = 0;
    public  static final long  MASK_ROAD_CAR   = 1;
    public  static final long  MASK_CAR_CAR    = 2;
    public  static final long  MASK_OPTION_CAR = 4;

For “road” body, i use MASK_ROAD_CAR.
For “car” body, i use MASK_ROAD_CAR | MASK_CAR_CAR | MASK_OPTION_CAR

It work fine. But i came accros another problem :stuck_out_tongue:


for(int i=0;i<NB_V;i++)
      {
        cars[i].step();
      }
      
world.step();
      
for(int i=0;i<NB_V;i++)
      {
        int val = map.crossStart(cars[i]);
        
        if (val != 0) { System.out.println("Start : "+val); }
      }

In Map.crossStart, I test if a car cross the start line.


public int crossStart(Car car)
    {
      float cx1 = car.getPosition().getX();
      float cy1 = car.getPosition().getY();
      
      float cx2 = car.getLastPosition().getX();
      float cy2 = car.getLastPosition().getY();
      
      Line l = (Line)start.getShape();
      
      float sx1 = l.getX1();
      float sy1 = l.getY1();
      
      float sx2 = l.getX2();
      float sy2 = l.getY2();
      
      float nx = (sx2-sx1);
      float ny = (sy2-sy1);
      
      int signe1 = (int)Math.signum((cx1-sx1)*(ny)-(cy1-sy1)*(nx));
      int signe2 = (int)Math.signum((cx2-sx1)*(ny)-(cy2-sy1)*(nx));
       
      if (signe1 == signe2) { return 0; }
      
      float ln = (float)(Math.sqrt(nx*nx+ny*ny));
      
      nx = nx/ln;
      ny = ny/ln;

      float dist = (cx1-sx1)*nx+(cy1-sy1)*ny;
      
      if ((dist<0)||(dist>ln)) { return 0; }
      
      return signe1;
    }

If i use the Body.getLastPosition to do the test, it didn’t work.


class Car
{
[...]
public ROVector2f getPosition()
    {
      return body.getPosition();
    }
       
public ROVector2f getLastPosition()
    {
      return body.getLastPosition();      
    }

If i store the current possition before World.step(), it work.


class Car
{
[...]
public ROVector2f getPosition()
    {
      return body.getPosition();
    }
    
public void step()
    {
      lastPosition.set(body.getPosition().getX(),body.getPosition().getY());
    }
    
public ROVector2f getLastPosition()
    {        
       return lastPosition;
    }
}

Body.getLastPosition didn’t mean to return the position of the body before the world.step() ?

At the moment lastPosition is set every time adjustPosition() is called on a body. So if the body was moved a few times as part of your step then the lastPosition won’t be the one before the step(). The lastPosition is intended for use when recieving notifications of collisions. The approach you’ve used as a work around is more suitable for what you’re trying to do.

Kev

Ok thanks :slight_smile:

If there is a documentation that i didn’t see, just let me know (i only use Javadoc)

I’m afraid the only documentation so far is the Javadoc. I’m not actively using the project so creating tutorial information isn’t high on the agenda.

Kev