Custom JPanel Faster then Custom Canvas

So I normally use a JPanel when I want to do some custom drawing (games not just UI)

The JPanel sometimes gives a strange jitter just on certain images being draw which is why I thought maybe it was not being double buffered or it was doing a bad version of double buffering. So I try canvas and well the jitter is gone but it is slow. ???

I have tried using a canvas with double buffering and it runs at 1/3 the speed of my JPanel version.

The canvas code


    public void render()
    {
    	long startTime = System.currentTimeMillis();
    	g.setColor(Color.BLACK);
    	g.fillRect(0,0,frame.getWidth(),frame.getHeight());
    	g.setColor(Color.yellow);
    	g.drawString("FPS: " + fps, 15, 15);
	long endTime = System.currentTimeMillis();
        g.drawString("Time Render: " + (endTime-startTime) + "ms", 15, 60); 
        g.dispose();
        bufferstrat.show();
}
        

The JPanel Code


	public void paintComponent(Graphics g)
    	{
    		long startTime = System.currentTimeMillis();
        	g.drawString("FPS: " + fps, 15, 15);
        	long endTime = System.currentTimeMillis();
                g.drawString("Time Render: " + (endTime-startTime) + "ms", 15, 60); 
                g.dispose();
                System.out.println(pane.isDoubleBuffered());
        }

The JPanel Is indeed double buffered.

What gives? Am I doing something wrong?

I have to be doing something wrong but everything is setup the exact same. If anything, the JPanel code is more ugly.

how about the constructing part? for example when you add canvas to JPanel before JFrame.

I know that Cavas supports public void createBufferStrategy(int numBuffers) while JPanel does not. I just changed the code in a game project of mine from JPanel to Canvas. I was using the image style double buffering as taught in the book “Killer Java Game Programming” inside a JPanel. I was getting shaky graphics at 30 FPS once my large background images became more complex. With a double buffered Canvas I set my max FPS to 150 (which is faster than my monitor’s refresh rate) and it still ran smoothly

In the Canvas I use createBufferStrategy(2) to create 2 buffers. Then in my paintScreen() method I get the Graphics this way:

Graphics2D g = (Graphics2D) getBufferStrategy().getDrawGraphics();

I draw to g and then call getBufferStrategy().show(); and g.dispose();

My guess is that the setDoubleBuffered and isDoubleBuffered in JPanel is saying that the JPanel is double buffered, but it’s still painting when the OS tells the panel to repaint. It’s just telling you that behind the scenes the OS is using double buffering to draw the JPanel.

An interesting test would be to call setIgnoreRepaint(true) in your JPanel constructor and then call Toolkit.getDefaultToolkit().sync(); right before the g.dispose() in your JPanel code. That should cause the JPanel to repaint when you want it to, not when the OS wants it to.

My experience was that using createBufferStrategy and getDrawGraphics on the Canvas was vastly faster than using the JPanel. But I was also using a clunky Imaged based double buffering technique which was suggested in that book and that may have been slowing me down quite a bit as well. I wouldn’t recommend his technique at all. He’s creating and throwing away a BufferedImage object for every frame. Think of the memory usage of tossing away a megabyte image 60 times a second!

Your measuring the time it takes to set a color, fill a rectangle, set a color and draw a string (this is your canvas code) against the time it takes to draw a string (your JPanel code). If this is what your referring to as running slower then this is probably the cause.

Also (depending on your other code) drawing to a Canvas is active rendering, and overriding a JPanel’s paintComponent() is passive rendering. I wouldn’t use passive rendering for a game because it’s best to have control over when rendering occurs (active rendering)

@ReBirth
The Canvas and JPanel are not added to the same Frame.
Here is my JPanel construction code.


frame = new JFrame();
        frame.setBounds(0, 0, 800, 500);
        frame.setTitle("HappyFunTime");
        Container cp = frame.getContentPane();
        pane = new MyPanel();

        pane.setDoubleBuffered(true);
        frame.setBackground(Color.black);
        pane.setLayout(null);
        cp.add(pane, "Center");

And the Canvas


        frame = new JFrame();
        frame.setBounds(0, 0, 800, 500);
        frame.setTitle("EffectTester");
        Container cp = frame.getContentPane();
        rend = new Canvas();
        cp.add(rend, "Center");
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        rend.createBufferStrategy(2);
        bufferstrat = rend.getBufferStrategy();

@ DruLeeParsec
I am creating two buffers and basically do what you do. I have found out that the actual render time is almost the exact same for both of them but for some reason my canvas fps is much slower.
I tried the setIgnoreRepaint(true); and the performance was the same and still said doublebuffered.

@Z-Man
I removed the draw command that both use do draw everything which is SystemX.render(Graphics g);
The rectangle is trivial when compared to what SystemX is rendering. And when just rendering the rectangle and some text both get 60 fps. Nice and smooth.
I think my JPanel code is active rendering as in, I say when to render. I maybe wrong thought as I am a noob. :?

I was just referring to the original code you posted, I don’t know anything about SystemX (I assume it’s code you wrote?). In the code from your first post your measuring how long rendering takes with System.currentTimeMillis() (I’d use System.nanoTime() by the way, because it’s implementation is more accurate), but your measuring different amounts of rendering. In the render code for your Canvas you are measuring how long it takes for two setColor() calls, a drawString(), and a fillRect(), but in your JPanel render code your only measuring a drawString(). I currently use a Canvas for my games, but I’ve used JPanels and I don’t really notice much of a performance difference between the two. That might just be my computer though. As for the active rendering, with the code you posted I can’t really tell if your JPanel is being actively rendered. Simply overriding the paintComponent() of a JPanel is NOT active rendering, but you can kind of force it to render when you want to.

@Z-man
So SystemX is my particle system and it renders particles that, in the case of the test, are images. The jpanel could get 2-3.5k particles with no fps drop while the canvas would drop at 500 but after testing the nano time the actual rendering time is the same. So I must have implemented something else in there wrong. I know is was miss leading in the original post but I hate posting unessential stuff. The drawing is not what I thought the problem was it was whether I was properly double buffering.

I am still not quite sure if I am actually active rendering. I have over written the JPanel’s paintComponent() and I have set this flag setIgnoreRepaint(true).

With the canvas I was drawing the rect over the whole screen because the canvas was not clearing the old drawing commands. (like in opengl when you forget to clear the screen)
but when I used this method clearRect() it cleared the drawing surface but got even slower. I am now thinking that it is not swapping buffers properly.