Something is limiting the FPS to the monitor refresh rate

Hi,

This code is orignally from Pro Java 6 3D Game Development, I removed parts that I didn’t want and added some other bits just to test things out.

The program creates a frame with a canvas inside and then displays the FPS in the botton left hand corner. The FPS is calculated very simply by just seeing how many frames are rendered in 1 second, and then displaying the number of frames.

I thought that the FPS should be high but something is limiting it so that it only renders at my monitors refresh rate (tested at 60 and 75). Does anyone have any ideas why this is, and how I can use active rendering in JOGL that runs as fast as possible? This is really bugging me :frowning:

//CubeGL.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*; 

import javax.media.opengl.*;

public class CubeGL extends JFrame implements WindowListener
{
	private static final int PWIDTH = 512;   // size of panel
	private static final int PHEIGHT = 512; 

	private CubeCanvasGL canvas;

	public CubeGL() 
	{
		super("GAMEEEEE (Active)");

		Container c = getContentPane();
		c.setLayout( new BorderLayout() );
		c.add( makeRenderPanel(), BorderLayout.CENTER);

		addWindowListener(this);

		pack();
		setVisible(true);
	} // end of CubeGL()


	private JPanel makeRenderPanel()
	// construct the canvas, inside a JPanel
	{
		JPanel renderPane = new JPanel();
		renderPane.setLayout( new BorderLayout() );
		renderPane.setOpaque(false);
		renderPane.setPreferredSize( new Dimension(PWIDTH, PHEIGHT));

		canvas = makeCanvas();
		renderPane.add(canvas, BorderLayout.CENTER);

		canvas.setFocusable(true);
		canvas.requestFocus();    // the canvas now has focus, so receives key events

		// detect window resizes, and reshape the canvas accordingly
		renderPane.addComponentListener( new ComponentAdapter() {
			public void componentResized(ComponentEvent evt)
			{ Dimension d = evt.getComponent().getSize();
			// System.out.println("New size: " + d);
			canvas.reshape(d.width, d.height);
			} // end of componentResized()
		});

		return renderPane;
	}  // end of makeRenderPanel()


	private CubeCanvasGL makeCanvas()
	{
		// get a configuration suitable for an AWT Canvas (for CubeCanvasGL)
		GLCapabilities caps = new GLCapabilities();

		AWTGraphicsDevice dev = new AWTGraphicsDevice(null);
		AWTGraphicsConfiguration awtConfig = (AWTGraphicsConfiguration)
		GLDrawableFactory.getFactory().chooseGraphicsConfiguration(caps, null, dev);

		GraphicsConfiguration config = null;
		if (awtConfig != null)
			config = awtConfig.getGraphicsConfiguration();

		return new CubeCanvasGL(this, PWIDTH, PHEIGHT, config, caps);
	} // end of makeCanvas()


	// ----------------- window listener methods -------------

	public void windowActivated(WindowEvent e) 
	{ canvas.resumeGame();  }

	public void windowDeactivated(WindowEvent e) 
	{  canvas.pauseGame();  }

	public void windowDeiconified(WindowEvent e) 
	{  canvas.resumeGame();  }

	public void windowIconified(WindowEvent e) 
	{  canvas.pauseGame(); }

	public void windowClosing(WindowEvent e)
	{  canvas.stopGame();  }

	public void windowClosed(WindowEvent e) {}
	public void windowOpened(WindowEvent e) {}

//	-----------------------------------------

	public static void main(String[] args)
	{ 
		new CubeGL();    // ms --> nanosecs 
	} // end of main()
} // end of CubeGL class
//CubeCanvasGL.java
import java.awt.*;

import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import com.sun.opengl.util.j2d.TextRenderer;

public class CubeCanvasGL extends Canvas implements Runnable
{
	private static final double Z_DIST = 7.0;      // for the camera position

	// used at game termination
	private volatile boolean gameOver = false;

	int cubeDList;   // display list for displaying the cube

	@SuppressWarnings("unused")
	private CubeGL top;   // reference back to top-level JFrame

	private Thread animator;             // the thread that performs the animation
	private volatile boolean isRunning = false;   // used to stop the animation thread
	private volatile boolean isPaused = false;

	// OpenGL
	private GLDrawable drawable;   // the rendering 'surface'
	private GLContext context;     // the rendering context (holds rendering state info)
	private GL gl;
	private GLU glu;

	// window sizing
	private boolean isResized = false;
	private int panelWidth, panelHeight;

	// frames per second counter
	private TextRenderer renderer;
	private int numberOfFrames = 0;
	private int fps = 0;


	public CubeCanvasGL(CubeGL top, int width, int height,
			GraphicsConfiguration config, GLCapabilities caps)
	{ 
		super(config);

		this.top = top;
		panelWidth = width;
		panelHeight = height;

		// get a rendering surface and a context for this canvas
		drawable = GLDrawableFactory.getFactory().getGLDrawable(this, caps, null);
		context = drawable.createContext(null);

		// initialise text renderer
		renderer = new TextRenderer(new Font("SansSerif", Font.BOLD, 36));
	} // end of CubeCanvasGL()



	public void addNotify()
	// wait for the canvas to be added to the JPanel before starting
	{ 
		super.addNotify();      // make the component displayable
		drawable.setRealized(true);   // the canvas can now be rendering into

		// initialise and start the animation thread 
		if (animator == null || !isRunning) {
			animator = new Thread(this);
			animator.start();
		}
	} // end of addNotify()


	// ------------- game life cycle methods ------------
	// called by the JFrame's window listener methods

	public void resumeGame()
	// called when the JFrame is activated / deiconified
	{  isPaused = false;  } 

	public void pauseGame()
	// called when the JFrame is deactivated / iconified
	{ isPaused = true;   } 

	public void stopGame() 
	// called when the JFrame is closing
	{  isRunning = false;   }

	// ----------------------------------------------

	public void reshape(int w, int h)
	/* called by the JFrame's ComponentListener when the window is resized
     (similar to the reshape() callback in GLEventListener) */
	{
		isResized = true;
		if (h == 0)
			h = 1;   // to avoid division by 0 in aspect ratio in resizeView()
		panelWidth = w; panelHeight = h;
	}  // end of reshape()


	public void update(Graphics g) { }

	public void paint(Graphics g) { }


	public void run()
	// initialise rendering and start frame generation
	{
		// makeContentCurrent();

		initRender();
		renderLoop();

		// discard the rendering context and exit
		// context.release();
		context.destroy();
		System.exit(0);
	} // end of run()


	private void makeContentCurrent()
	// make the rendering context current for this thread
	{
		try {
			while (context.makeCurrent() == GLContext.CONTEXT_NOT_CURRENT) {
				System.out.println("Context not yet current...");
				Thread.sleep(100);
			}
		}
		catch (InterruptedException e)
		{ e.printStackTrace(); }
	}  // end of makeContentCurrent()


	private void initRender()
	/* rendering initialisation (similar to the init() callback
     in GLEventListener) */
	{
		makeContentCurrent();

		gl = context.getGL();
		glu = new GLU();

		resizeView();

		gl.glClearColor(0.17f, 0.65f, 0.92f, 0.0f);  // sky colour background

		// z- (depth) buffer initialisation for hidden surface removal
		gl.glEnable(GL.GL_DEPTH_TEST); 

		// set up 2d stuff
		gl.glMatrixMode(GL.GL_PROJECTION); 
		gl.glLoadIdentity();
		gl.glOrtho(0.0f, panelWidth, panelHeight, 0.0f, -1.0f, 1.0f);
		// left, right, bottom, top, near, far
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();    
		gl.glDisable(GL.GL_DEPTH_TEST);

		/* release the context, otherwise the AWT lock on X11
       will not be released */
		context.release();
	}  // end of initRender()


	private void resizeView()
	{
		gl.glViewport(0, 0, panelWidth, panelHeight);  // size of drawing area 

		gl.glMatrixMode(GL.GL_PROJECTION);  
		gl.glLoadIdentity(); 
		glu.gluPerspective(45.0, (float)panelWidth/(float)panelHeight,  1, 100); // 5, 100); 
		// fov, aspect ratio, near & far clipping planes
	}  // end of resizeView()


	private void renderLoop()
	/* Repeatedly update, render, add to canvas, and sleep, keeping to a fixed
     period as closely as possible. Gather and report statistics.
	 */
	{
		isRunning = true;

		long lastFPSCheck = System.currentTimeMillis();

		while(isRunning) {
			System.out.println(numberOfFrames);
			long currentTimeMillis = System.currentTimeMillis();
			// Check if one second has passed, if so, update the fps
			if ((currentTimeMillis - lastFPSCheck)>1000) {
				fps = numberOfFrames;
				numberOfFrames = 0;
				lastFPSCheck = currentTimeMillis;
			}
			numberOfFrames++;
			
			makeContentCurrent();
			gameUpdate();

			renderScene();           // rendering
			drawable.swapBuffers();  // put the scene onto the canvas
			// swap front and back buffers, making the new rendering visible

			/* release the context, otherwise the AWT lock on X11
         will not be released */
			context.release();
		}
	} // end of renderLoop()


	private void gameUpdate() { 
		if (!isPaused && !gameOver) {
		}
	}  // end of gameUpdate()



	// ------------------ rendering methods -----------------------------


	private void renderScene() 
	{ 
		if (GLContext.getCurrent() == null) {
			System.out.println("Current context is null");
			System.exit(0);
		}

		if (isResized) {    // resize the drawable if necessary
			resizeView();
			isResized = false;
		}

		// clear colour and depth buffers
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

		gl.glMatrixMode(GL.GL_MODELVIEW);  
		gl.glLoadIdentity(); 

		glu.gluLookAt(0,0,Z_DIST, 0,0,0, 0,1,0);   // position camera

		//Draw the FPS
		renderer.beginRendering(drawable.getWidth(), drawable.getHeight());
		// optionally set the colour
		renderer.setColor(1.0f, 0.2f, 0.2f, 0.8f);
		renderer.draw("FPS: " + fps, 20, 20);
		// ... more draw commands, colour changes, etc.
		renderer.endRendering();

		//Draw a triangle
		gl.glBegin(GL.GL_TRIANGLES);						// Drawing Using Triangles
		gl.glVertex3f( 0.0f, 1.0f, 0.0f);				// Top
		gl.glVertex3f(-1.0f,-1.0f, 0.0f);				// Bottom Left
		gl.glVertex3f( 1.0f,-1.0f, 0.0f);				// Bottom Right
		gl.glEnd();							// Finished Drawing The Triangle

		if (gameOver)
			System.out.println("Game Over");
	} // end of renderScene()
}

Sorry that there’s alot of code, it uses active rendering and shouldn’t be too hard to understand. If anyone has any idea please please let me know, this happened before and I couldn’t work out why, it’s so annoying! :frowning:

If vsync is enabled the maximum number of frames per second equals the monitor’s refresh rate.

I recommend to set vsync to “on by default” in the drivers settings panel. For benchmarking just set swapInterval to 0. (Eg bind some key to toggle that value.)

Thank you,

gl.setSwapInterval(0);

…worked perfectly.