FPS is 60 when running through Netbeans IDE, But drops to 30 when running jar

Hey guys,
I am making a 2D game using SWING/paintComponent and have run in to an FPS problem. My FPS is fine at runs at 60 when I run it through Netbeans, though if I run a jar file then it will drop to 30. Would I be better off using AWT/Canvas?
I have cross-posted this problem. The original thread is http://www.java-forums.org/new-java/81086-fps-dropping-exactly-half-when-running-game-through-jar-file.html , but I have been advised to seek help from java-gaming instead.
Thanks for any help :slight_smile:

  • It only happens in fullscreen exclusive mode, and does not happen when windowed.
  • Double buffering has no affect on it.
  • Turning VYsnc off in my NVIDIA control panel will fix the problem and it will run at over 320fps.

If I run this exact code through Netbeans, it does exactly what I need…

  • It takes 15ms from the time repaint() gets called, till paintComponent has finished.
  • paintComponent takes 8ms of that 15ms time.
  • Paintcomponent will be called 60 times per second.

If I run this exact code from a jar file, the FPS drops by half

  • It takes 30ms from the time repaint() gets called, till paintComponent has finished.
  • paintComponent takes 15ms of that 30ms time.
  • Paintcomponent will be called 30times per second.
public void gameLoop(){
    double startMS;
    double currentMS;
    while (true){
        startMS = System.currentTimeMillis();
        currentMS = System.currentTimeMillis() - startMS ;
        while (currentMS < 1000){ // Display FPS after 1 second has passed
            if (gameScreen.getIsPainting() == false){
                render();
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException ex) {
                Logger.getLogger(GameEngine.class.getName()).log(Level.SEVERE, null, ex);
            }
            currentMS = System.currentTimeMillis() - startMS ;
        }
        if (SHOW_FPS == true){
            SwingUtilities.invokeLater(new Runnable(){
                @Override
                public void run(){
                    gameScreen.getTfFPS().setText(String.valueOf(GameScreen.paintingAmount));
                    gameScreen.revalidate();
                    GameScreen.paintingAmount = 0; // Reset painting counter to get amount of times painting every second
                }
            });
        }
         
    }
}
 
public void render(){
    gameScreen.setIsPainting(true);
    gameScreen.repaint();
}
private void init(){
    frame.setUndecorated(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(false);
    gameScreen = new GameScreen(this);
    gameEngine.init();
    input = new Input();
    input.initInput();
    frame.add(input);
    frame.add(gameScreen);
    frame.setVisible(true);
    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice gd = ge.getDefaultScreenDevice();
    DisplayMode dm = new DisplayMode(640,480,32,60);
    gd.setFullScreenWindow(frame);
    gd.setDisplayMode(dm);
}

If you want consistent performance, rather use LibGDX. You can go on using Java2D with Swing and/or AWT until you really understand the basic concepts and then give a try to third party libraries.

I will definitely be using LibGDX in the future, but for now while I am still learning (and have an exam coming up) I want to stick with Java2D so I don’t confuse anything in the exam :slight_smile:

Are you required to use that sleep call? Generally,

Thread.Sleep (1)

is a terrible way to keep the fps down because that function will actually run faster or slower depending on the computer.

This is a cut down version of my game loop. In the full version, I compare the current system time to the last frame and play one frame every 15ms. Though I had to put a check if it was currently painting because I was getting screen tearing from moving objects while it was painting… So I don’t think it should matter because it should get limited to my monitors refresh rate of 60?

While I suspect it’s an issue with the way that you’re regulating your animation loop (or calculating your fps), I can’t be sure from code snippets.

Strip down your code to the simplest example that still exhibits the problem & post it in its entirety.

Im going to reccomend that one, you dont compare as much info, adding another boolean check every time you run through the loop wastes processing power and its doing that thousands of times a second, Note this is either a calculation error you are getting OR something is up with your threads, try multithreading if it continues. Such as setting the renderer in one thread then setting everything else in the other.

Never multithread a game loop. Ever.

Okay so I have recreated it with as little code as possible. Running this in Netbeans will result in a constant 60fps, while creating a jar file and running it will result in a constant 30fps. It looks like the jar file seems to think my monitors refresh rate is just 30 and not 60.

The reason I am checking to see if it is currently painting was because the repainting is done on the EDT with SWING and I would get tearing from moving game objects while it was trying to paint. I have never used Canvas before, and I think that I could make it render without using the EDT…am I right about that? I think I should be switching to Canvas instead of JPanel, but it would be good to know why this code will half the refresh rate in a jar file compared to the IDE. If I turn VSync off in the NVIDIA control panel on my desktop, then it will run at 900+fps.


import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class Testing extends JPanel {
    
    boolean isPainting;
    int currentFPS;
    JTextField tfFPS;
    JPanel panel;
    
    public Testing(){
        super(true);
        tfFPS = new JTextField();
        JFrame frame = new JFrame();
        this.setIgnoreRepaint(true);
        frame.setUndecorated(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        DisplayMode dm = new DisplayMode(640,480,32,60);
        gd.setFullScreenWindow(frame);
        gd.setDisplayMode(dm);
        frame.add(this);
        this.add(tfFPS);
        this.setVisible(true);
        frame.setVisible(true);
        panel = this;
    }
    
    private void gameLoop(){
        double startMS;
        double currentMS;
        while (true){
            startMS = System.currentTimeMillis();
            currentMS = System.currentTimeMillis() - startMS;
            while (currentMS < 1000){ // while under 1 second, add 1 to fps and display FPS rate after 1 second
                if (isPainting == false){ // Loop one gameLoop only if it is currently not repainting
                    isPainting = true;
                    repaint();
                    currentFPS++;
                }
                try {
                    Thread.sleep(1);
                } catch (InterruptedException ex) {
                    System.out.println(ex);
                }
                currentMS = System.currentTimeMillis() - startMS;
            }
            SwingUtilities.invokeLater(new Runnable(){
                @Override
                public void run(){
                    tfFPS.setVisible(true);
                    tfFPS.setText(String.valueOf(currentFPS));
                    currentFPS = 0;
                    panel.revalidate();
                }
            });
        }
    }
    
    @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        isPainting = false;
    }
    
    public static void main(String args[]){
        Testing t = new Testing();
        t.gameLoop();
    }
}

Try active rendering from JFrame class.


import javax.swing.JFrame;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;

/**
 * Base code for active rendering
 */
public class Game extends JFrame implements Runnable
{

    private BufferStrategy buffer;
    private boolean running = true;

    public Game()
    {
        super("My Game");
        setSize(640, 480);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
        // Create 2 buffers
        createBufferStrategy(2);
        buffer = getBufferStrategy();

        // Run game loop
        new Thread(this).run();
    }

    /**
     * The Game Loop
     */
    public void run()
    {
        long now = getCurrentTime();
        long gameTime = getCurrentTime();

        long frames = 0;
        long fps = 0;

        long lastFpsCount = getCurrentTime();

        long updateRate = 1000 / 60; // Update 60 times per second

        while (running)
        {
            now = getCurrentTime();

            while (now + updateRate > gameTime)
            {
                // update your logic here
                update();
                gameTime += updateRate;
              
                // render
                do 
                {
                    do
                    {
                        Graphics g = buffer.getDrawGraphics();
                        render(g);
                        g.dispose();
                      
                        frames++;
                        if (now - lastFpsCount > 1000)
                        {
                            fps = frames;
                            frames = 0;
                            lastFpsCount = getCurrentTime();
                            setTitle("FPS: " + fps);
                        }
                    }
                    while (buffer.contentsRestored());

                    buffer.show();
                }
                while (buffer.contentsLost());
            }
        }
    }
  
    /**
     * Update the game
     */
    public void update()
    {
        System.out.println("updated");
    }
  
    /**
     * Render the game
     */
    public void render(Graphics g)
    {
        System.out.println("rendered");
    }

    /**
     * Returns the current time in milliseconds
     */
    public long getCurrentTime()
    {
        return System.nanoTime() / 1000000;
    }

    public static void main(String[] args)
    {
        new Game();
    }
}

It’s giving me 64 fps. You can also separate the rendering from update logic.

Thanks, I didn’t know I could do that with a JFrame :slight_smile:
Why would people use a Canvas instead of doing this when you can’t add components like textfields to a Canvas?
How would I go about adding a JTextField to this? I can’t seem to get it to show

The methods [icode]createBufferStrategy(int)[/icode] and [icode]getBufferStrategy()[/icode] belongs to the [icode]Window[/icode] interface and [icode]JFrame[/icode] does just implement them. [icode]Canvas[/icode] is another implementation of that interface and is used my most people since it can be added as a component on both an applet as well.

For adding components when in active rendering mode, I don’t know how to do it but I recommend Chapter 3 (UI) of Developing Games In Java which explains a way to do it.

Ah i see…According to this http://docs.oracle.com/javase/7/docs/api/java/awt/Canvas.html - Canvas isn’t a subclass of Window, but it does have it’s own buffer strategy methods.
To paint a JTextField, i needed to call the paint method on the textfield itself :slight_smile: