0

I am new to processing. I wrote the following code with bouncing balls but the animation is not smooth. I have ran the application on various computers and I can occasionally see some jitter or tearing of some balls. I do not believe that it is related to the calculation time as it there are not much calculations involved for each frame. Moreover, I have read the processing is already double buffered.

The following is the concerned code:

 final int MAX_BALLS = 50;
 final int DISPLAY_WIDTH = 800;
 final int DISPLAY_HEIGHT = 600;
 final float MIN_SPEED = 1;
 final float MAX_SPEED = 5;
 final float MIN_SIZE = 30;
 final float MAX_SIZE = 50;
 Ball[] balls = new Ball[MAX_BALLS];

 void setup() {
   size(800, 600);
   stroke(255);
   background(0, 0, 0);
   for (int i=0; i<balls.length; i++) {
     balls[i] = new Ball(random(MIN_SIZE, MAX_SIZE), random(0, DISPLAY_WIDTH), random(0, DISPLAY_HEIGHT), random(MIN_SPEED, MAX_SPEED), random(MIN_SPEED, MAX_SPEED), 0, DISPLAY_WIDTH, 0, DISPLAY_HEIGHT);
   }
 } 

 void draw() {
   clear();
   for (int i = 0; i<balls.length; i++)
     balls[i].draw();
 }

 class Ball {
   private float size;
   private float x;
   private float y;
   private float vx;
   private float vy;
   private float minx;
   private float maxx;
   private float miny;
   private float maxy;
   private float r;
   private float g;
   private float b;

   public Ball(float size,float x, float y, float vx, float vy, float minx, float maxx, float miny, float maxy) {
     this.size = size;
     this.x = x;
     this.y = y;
     this.vx = vx;
     this.vy = vy;
     this.minx = minx;
     this.maxx = maxx;
     this.miny = miny;
     this.maxy = maxy;
     r = (int) random(30, 255);
     g = (int) random(30, 255);
     b = (int) random(30, 255);
   }

   void draw() {
     x = x + vx;
     if (x + size/2 > maxx) {
       vx = -vx;
       x = 2 * maxx - (x + size);
     } else if (x - size/2 < minx) {
       vx = -vx;
       x = 2 * minx - (x - size);
     }

     y = y + vy;
     if (y + size/2 > maxy) {
       vy = -vy;
       y = 2 * maxy - (y + size);
     } else if (y -size/2 < miny) {
       vy = -vy;
       y = 2 * miny - (y - size);
     }
     stroke(r,g,b);
     fill(r,g,b);
     ellipse(x, y, size, size);

   }   

 }

How can I get rid of the jitter and tearing? How do I ensure that the graphics card driver is used optimally. Note that I use Linux Mint 17.3 with MATE desktop manager. Same O.S. on all tested PCs and same behavior.

[EDIT 05/01/2016] After generating the circles off-screen and even using an offline image the size of the screen, I still get some tearing. This is the updated code:

 final int MAX_BALLS = 50;
 final float MIN_SPEED = 1;
 final float MAX_SPEED = 5;
 final float MIN_SIZE = 30;
 final float MAX_SIZE = 50;
 Ball[] balls = new Ball[MAX_BALLS];
 PGraphics img;

 void setup() {
   frameRate(60);
   fullScreen();
   img = createGraphics(width, height);
   img.stroke(255);
   img.smooth();
   for (int i=0; i<balls.length; i++) {
     balls[i] = new Ball(random(MIN_SIZE, MAX_SIZE), random(0, width), random(0, height), random(MIN_SPEED, MAX_SPEED), random(MIN_SPEED, MAX_SPEED), 0, width, 0, height);
   }
 } 

 void draw() {
   img.beginDraw();
   img.background(0,0,0);
   img.clear();
   clear();
   for (int i = 0; i<balls.length; i++)
     balls[i].draw();

   img.text((int)frameRate+"fps",10,15);
   img.endDraw();
   image(img, 0, 0); // Put the whole image at once on the screen
 }

 class Ball {
   private float size;
   private float x;
   private float y;
   private float vx;
   private float vy;
   private float minx;
   private float maxx;
   private float miny;
   private float maxy;
   private PGraphics circle;
   private final int MARGIN = 10; // Margin to avoid circle to be drawn slightly outside the square

   public Ball(float size,float x, float y, float vx, float vy, float minx, float maxx, float miny, float maxy) {
     this.size = size;
     this.x = x;
     this.y = y;
     this.vx = vx;
     this.vy = vy;
     this.minx = minx;
     this.maxx = maxx;
     this.miny = miny;
     this.maxy = maxy;

     int r = (int) random(30, 255);
     int g = (int) random(30, 255);
     int b = (int) random(30, 255);

     circle = createGraphics((int) this.size + 2*MARGIN, (int) this.size + 2*MARGIN);
     circle.beginDraw();
     circle.background(0, 0);
     circle.fill(r, g, b);
     circle.ellipse(MARGIN + this.size/2, MARGIN + this.size/2, this.size, this.size);
     circle.endDraw();         
   }

   void draw() {
     x = x + vx;
     if (x + size/2 > maxx) {
       vx = -vx;
       x = 2 * maxx - (x + size);
     } else if (x - size/2 < minx) {
       vx = -vx;
       x = 2 * minx - (x - size);
     }

     y = y + vy;
     if (y + size/2 > maxy) {
       vy = -vy;
       y = 2 * maxy - (y + size);
     } else if (y -size/2 < miny) {
       vy = -vy;
       y = 2 * miny - (y - size);
     }

     img.image(circle, x - this.size/2 - MARGIN, y - this.size/2 - MARGIN);
   }   

 }
Tarik
  • 10,810
  • 2
  • 26
  • 40
  • This code runs fine for me on Windows 10. Can you take a screenshot of exactly what you're talking about? Maybe modify your code to use hard-coded values that cause the behavior instead of using the `random()` function. – Kevin Workman Apr 29 '16 at 20:20
  • @KevinWorkman The behavior is consistent regardless of the size of the balls. I get the tearing every time I run the app. I did not test it on Windows though. I could not screen-capture it. Video recording at 60fps using VLC failed to capture it. It's a horizontal tearing with the lower part of the ball being shifted. – Tarik Apr 29 '16 at 21:14
  • This is going to be pretty hard to debug if we can't repeat the behavior. Which ball? Is the random movement related at all, or can you just hardcode the movement direction? Can you create a smaller example with just one ball? There's a lot of extra code here. Is all of it related to your problem? If not, can you simplify the example at all? – Kevin Workman Apr 29 '16 at 21:24
  • @KevinWorkman It happens randomly on some balls. See also: http://stackoverflow.com/questions/20551224/how-to-enable-vsync-synchronization-in-processing-2-x that seem to deal with a vsync issue. The solution does not work for processing 3 though. – Tarik Apr 29 '16 at 21:35
  • That seems to be a different question. Can you post a [mcve] of the code you're trying to call that generates the error? – Kevin Workman Apr 29 '16 at 22:04
  • @KevinWorkman There is no error per se. It's just that I am getting tearing due to what seems to be a vsynch problem that only affects Linux. I will try running that on Windows and see. I will get you updated. – Tarik Apr 29 '16 at 22:34
  • I meant the solution that doesn't work for Processing 3. Can you post that code here? – Kevin Workman Apr 29 '16 at 22:42
  • @KevinWorkman I tried running it on Windows and what is noticeable is that about every 2-3 seconds the balls kind of freeze for a fraction of a second. I checked the CPU utilization and it is close to nil. – Tarik May 02 '16 at 04:42
  • @KevinWorkman Found the solution. See accepted answer above. – Tarik May 02 '16 at 07:29
  • @KevinWorkman Found out it works at 800x600 resolution but not full screen. Moreover, drawing everything off-screen and then drawing the whole image back to screen did not help either. – Tarik May 02 '16 at 11:19

2 Answers2

1

It turns out that drawing many circles directly on the graphics is causing the issue. Pre-rendering the circles on a separate PGraphics for each circle solves the problem. The following is the revised code:

 final int MAX_BALLS = 50;
 final int DISPLAY_WIDTH = 800;
 final int DISPLAY_HEIGHT = 600;
 final float MIN_SPEED = 1;
 final float MAX_SPEED = 5;
 final float MIN_SIZE = 30;
 final float MAX_SIZE = 50;
 Ball[] balls = new Ball[MAX_BALLS];

 void setup() {
   frameRate(60);
   size(800, 600, FX2D);
   stroke(255);
   background(0, 0, 0);
   smooth();
   for (int i=0; i<balls.length; i++) {
     balls[i] = new Ball(random(MIN_SIZE, MAX_SIZE), random(0, DISPLAY_WIDTH), random(0, DISPLAY_HEIGHT), random(MIN_SPEED, MAX_SPEED), random(MIN_SPEED, MAX_SPEED), 0, DISPLAY_WIDTH, 0, DISPLAY_HEIGHT);
   }
 } 

 void draw() {
   clear();
   for (int i = 0; i<balls.length; i++)
     balls[i].draw();

     text((int)frameRate+"fps",10,15);

 }

 class Ball {
   private float size;
   private float x;
   private float y;
   private float vx;
   private float vy;
   private float minx;
   private float maxx;
   private float miny;
   private float maxy;
   private PGraphics circle;
   private final int MARGIN = 10; // Margin to avoid circle to be drawn slightly outside the square

   public Ball(float size,float x, float y, float vx, float vy, float minx, float maxx, float miny, float maxy) {
     this.size = size;
     this.x = x;
     this.y = y;
     this.vx = vx;
     this.vy = vy;
     this.minx = minx;
     this.maxx = maxx;
     this.miny = miny;
     this.maxy = maxy;

     int r = (int) random(30, 255);
     int g = (int) random(30, 255);
     int b = (int) random(30, 255);

     circle = createGraphics((int) this.size + 2*MARGIN, (int) this.size + 2*MARGIN);
     circle.beginDraw();
     circle.background(0, 0);
     circle.fill(r, g, b);
     circle.ellipse(MARGIN + this.size/2, MARGIN + this.size/2, this.size, this.size);
     circle.endDraw();         
   }

   void draw() {
     x = x + vx;
     if (x + size/2 > maxx) {
       vx = -vx;
       x = 2 * maxx - (x + size);
     } else if (x - size/2 < minx) {
       vx = -vx;
       x = 2 * minx - (x - size);
     }

     y = y + vy;
     if (y + size/2 > maxy) {
       vy = -vy;
       y = 2 * maxy - (y + size);
     } else if (y -size/2 < miny) {
       vy = -vy;
       y = 2 * miny - (y - size);
     }

     image(circle, x - this.size/2 - MARGIN, y - this.size/2 - MARGIN);
   }   

 }
Tarik
  • 10,810
  • 2
  • 26
  • 40
0

I don't see any issue with the code updating and rendering circles.

Using Processing 2 I can already see a difference in performance with different renderers.

I've add this at the end of draw() to get a rough idea of frame rate:

text((int)frameRate+"fps",10,15);

and it setup I've tried

size(800, 600,JAVA2D);
frameRate(60);

and

size(800, 600,P2D);
frameRate(60);

and noticed with JAVA2D the frameRate sticks pretty close to 60fps while with P2D it drops to ~40-45fps

This is on OSX though, not on Linux Mint. Try the FX2D renderer in Processing 3 and see how it compares with the other two renderers.

Additionally, if there is a GPU on the Linux Mint computer, drivers are properly installed and you have the time, you could try to port the code to GLSL and render it in Processing using PShader.

George Profenza
  • 50,687
  • 19
  • 144
  • 218
  • I tried running it on Windows and what is noticeable is that about every 2-3 seconds the balls kind of freeze for a fraction of a second. I checked the CPU utilization and it is close to nil. I tried different renderers on Linux to no avail. – Tarik May 02 '16 at 04:43
  • OK, I just added the FX2D flag on Windows and it is now freezing less often, again for a a tinny fraction of a second. – Tarik May 02 '16 at 05:00
  • Added the frame rate line on Linux and I get 59fps-60fps. Setting the frame rate to 100 fps, I get 99 fps-100 fps. As such, it does not seem to be related to computing power, the calculations being quite simple. – Tarik May 02 '16 at 05:04
  • Nice one! (+1) (Had a d'oh moment remembering [this answer](http://stackoverflow.com/questions/13786807/processing-efficiently-drawing-a-gradient/13788080#13788080)) – George Profenza May 02 '16 at 11:05
  • Well, bad news: when I went from 800x600 to full screen the problem was back again. I then tried drawing everything off-screen on a separate PGraphics object and then drawing that whole image on the screen. No avail. I am back to square one. I removed my question answered mark as it is not deserved anymore :-) – Tarik May 02 '16 at 11:16