1

I have been developing on my Mac a JAVA Applications . The logic is as follows:

  1. A Server sends to a client application some orders to Draw basic shapes
  2. The client applications draws the the basic shapes into a Jpanel

Every Time a Shape arrives the program calls repaint()

 public void paintShape(Shape p) 
{
    //this.paintComponent(this.getGraphics());
   arrayofShapes.add(p);
   this.repaint();
    //this.updateUI();
    //this.update(this.getGraphics());

}
public void paintComponent(Graphics g) 
{ 
    super.paintComponent(g);
    g2d = (Graphics2D) g.create();
    g2d.setStroke(new BasicStroke(2));
    g2d.setColor(pickedColor);
    for(final Shape p : arrayofShapes)
    {
        g2d.draw(p);
    }
    //g2d.dispose();

}

Everything works smoothly(on real time) , so I decided to test the same application on a Windows computer. The result is a laggy application. These are the conclusion that I have reached.

  • RepaintManager is accumulating repaint() calls. I see how the shapes arrive at destination but in some cases more than 5 repaint calls are accumulated into one, which make the application very lagged/not real Time.

I have tried instead of calling repaint every time a shape arrives to do it with a Timer every few milliseconds, the result is the same. Code :

ActionListener listener = new ActionListener(){
          public void actionPerformed(ActionEvent event){
            repaint();
          }
        };

Timer displayTimer = new Timer(5, listener);
displayTimer.start();

In addition i have tested some random code that allows you to paint with the mouse, same logic as mine with paintComponent. In this case it work smoothly without sense of lag.... Example: http://javagraphics.blogspot.com.es/2010/06/shapes-implementing-freehand-pencil.html

I do not understand why paintComponent is so slow on my Windows Computer(same Jar). What could be affecting the performance of my program?

I have read all the answers regarding paint Components but any of them has solved this issue.

Any advice on how could I solve the problem and actually archive Real-Time?

Thank you in advance

Update Videos:

Mac Video:https://youtu.be/OhNXdGXoQpk real Time no problem handling heavy load

Windows Video https://youtu.be/yol2miHudZc clearly laggy

I apologize for the low quality

Update BufferedImage:

After introducing the BufferedImage the result is still a slow painting Application. It creates another problem, since one of the orders is to delete all shapes, it adds some complexity since I have to do a :

 g2d.clearRect(0, 0, screenSize.width, screenSize.height);

HW/OS/JavaVersion

Windows

  • Processor i5-4300u 2.5ghz
  • Ram 12gb
  • Java version 1.7.0_71

MAC

  • Processor i7 2.9ghz
  • Ram 8gb
  • Java version 1.7.0_67

Java VisualVM

Video of live VisualVM:https://youtu.be/cRNX4b3rlZk

I do not see anything strange that could explain why the lag occurs but I'm far from being an expert(Again sorry for low quality)

Thank you for all your responses

  • How are you seeing "how the shapes arrive at destination"? Try printing to stdout when `paintShape` is called. Does this occur when it should? This whole thing seems to be network/communication problem rather than something to do with painting. – Adrian Leonhard Apr 03 '15 at 17:59
  • I have never had a problem with Windows. My little client/server app runs on the same machine. The client and server code runs on a separate Thread so the EDT is not blocked while waiting for a message from the server. – camickr Apr 03 '15 at 19:34
  • Hi thank you for your answers I have uploaded two videos to youtube(see question update) with one for each OS (same connection, same jar) – Ignacio Ferrero Apr 03 '15 at 20:05
  • 2
    Couln't it just be the your hardware of your Mac is highly superior to the one on which you run your Windows? You have set the timer to run every 5ms (so 200fps) which is a relatively high repaint frequency that could be somehow slower on some machines. Also, there could be differences in terms of memory of you JVM (maybe try increasing heap size on Windows). Without more info on the environment (hardware, Java version, OS version, etc...) it's hard to know what the problem is. Finally for real help, post an [MCVE](http://stackoverflow.com/help/mcve) – Guillaume Polet Apr 03 '15 at 21:38
  • Hi thank you for your responses, the timer was only a possible solution but didn´t help at all. I have introduced the HW data in my response. I will run some heap analysis test to look for an explanation. – Ignacio Ferrero Apr 04 '15 at 09:23

3 Answers3

2

There's no need to create() a new graphics context each time; just cast g to Graphics2D. This is safe on all concrete implementations. This also obviates the need to dispose() of the created context. As noted here, preserve any context variables that may be critical for later painting.

public void paintComponent(Graphics g) { 
    super.paintComponent(g);
    g2d = (Graphics2D) g;
    Stroke oldStroke = g2d.getStroke();
    g2d.setStroke(new BasicStroke(2));
    g2d.setColor(pickedColor);
    for(final Shape p : arrayofShapes) {
        g2d.draw(p);
    }
    g2d.setStroke(oldStroke);
}

Also, compare the profiles on both platforms to look for disparities. For reference, the example cited here comfortably handles selections containing hundreds of shapes on either platform.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • 2
    Dr. John: I remember reading that unwanted side effects are possible if a one changes the Stroke or AffineTransform of a Graphics2D object given to the paintComponent method, and that two possible solutions are to make a copy of the Graphics2D object or to store the original transform and stroke, and then re-set them at the end of the `paintComponent` method. Now I'm trying to Google to see if this is a false memory and will get back to you with any pertinent search hits. – Hovercraft Full Of Eels Apr 03 '15 at 21:34
  • 1
    The [Java JComponent API paintComponent method](http://docs.oracle.com/javase/8/docs/api/javax/swing/JComponent.html) states `"you should not make permanent changes to the passed in Graphics. For example, you should not alter the clip Rectangle or modify the transform. If you need to do these operations you may find it easier to create a new Graphics from the passed in Graphics and manipulate it."` It doesn't mention Stroke specifically, but I myself have experienced side effects when manipulating a non-copied Graphics object. – Hovercraft Full Of Eels Apr 03 '15 at 21:44
  • 1
    Dr. Pete: Good point about preserving context; I'm still wary about `create()` invalidating something on a particular platform/driver. – trashgod Apr 04 '15 at 00:34
1

I would recommend that you do static drawing to a BufferedImage, and then draw the BufferedImage in your paintComponent method.

e.g.,

private BufferedImage bufferedImage = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_ARGB);

public void paintShape(Shape p) {
    Graphics2D g2 = bufferedImage.createGraphics();
    g2d.setStroke(MY_STROKE); // make this a constant
    g2d.setColor(pickedColor);
    g2d.draw(p);
    g2d.dispose();
    this.repaint();
}

public void paintComponent(Graphics g) { 
    super.paintComponent(g);
    if (bufferedImage != null) {
       g2.drawImage(bufferedImage, 0, 0, null);
    }
    // do dynamic drawing such as drawing of moving sprites here
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thank you for your answer I will try your solution(check the videos I updated, still no answer why so different behaviors) – Ignacio Ferrero Apr 03 '15 at 20:10
  • @IgnacioFerrero: I agree with Guillaume that it's likely a hardware/platform performance issue. What happens though when you tried to draw to the BufferedImage? – Hovercraft Full Of Eels Apr 03 '15 at 21:45
  • @IgnacioFerrero: Compositing on a `BufferedImage` can be useful, and it won't trigger potential graphics driver problems suggested [here](http://stackoverflow.com/a/29439841/230513); just be careful not to provoke any resampling with mismatched sizes. – trashgod Apr 04 '15 at 00:38
  • Hi I have implemented the solution but it does not help at all , I have edited my answer with the results – Ignacio Ferrero Apr 04 '15 at 09:24
1

After more than two days of debugging I have found out that the problem has nothing to do with paintComponent()

With the same server generating random shapes

At the Windows app, some shapes are received at the same time which is impossible since I am sending shapes every 15 ms. That's why it accumulates shapes(result =lag).

On the other Hand at the Mac app, every shape has different time reception (result = real Time)

Thank you for the kind responses, and sorry for inconvenience I may have cause

  • Some versions of Windows have a timer [resolution](http://mindprod.com/jgloss/time.html#ACCURACY) that is below 15 ms. – trashgod Apr 05 '15 at 02:02