I'm experiencing an unexpected interaction between system events and the window refresh rate in simple Java2D applications on Linux/XWindows. It is best demonstrated with the small example below.
This program creates a small window in which a half-circle is displayed at different rotations. The graphics are updated at 60 frames per second in order to produce a flickering display. This is achieved through a BufferStrategy
, namely by calling its show
method.
However, I note that when I (a) move the mouse over the window so that the window receives mouse-over events or (b) hold down a key on the keyboard so that the window receives keyboard events, the flickering increases visibly.
As the rate at which BufferStrategy.show()
is called is not affected by these events, as can be seen by the print-outs on the console (they should constantly be at around 60 fps). However, the faster flickering indicates that the rate at which the display is actually updated, does indeed change.
It looks to me that actual, i.e., visible 60 frames per second are not achieved unless mouse or keyboard events are generated.
public class Test {
// pass the path to 'test.png' as command line parameter
public static void main(String[] args) throws Exception {
BufferedImage image = ImageIO.read(new File(args[0]));
// create window
JFrame frame = new JFrame();
Canvas canvas = new Canvas();
canvas.setPreferredSize(new Dimension(100, 100));
frame.getContentPane().add(canvas);
frame.pack();
frame.setVisible(true);
int fps = 0;
long nsPerFrame = 1000000000 / 60; // 60 = target fps
long showTime = System.nanoTime() + nsPerFrame;
long printTime = System.currentTimeMillis() + 1000;
for (int tick = 0; true; tick++) {
BufferStrategy bs = canvas.getBufferStrategy();
if (bs == null) {
canvas.createBufferStrategy(2);
continue;
}
// draw frame
Graphics g = bs.getDrawGraphics();
int framex = (tick % 4) * 64;
g.drawImage(image, 18, 18, 82, 82, framex, 0, framex+64, 64, null);
g.dispose();
bs.show();
// enforce frame rate
long sleepTime = showTime - System.nanoTime();
if (sleepTime > 0) {
long sleepMillis = sleepTime / 1000000;
int sleepNanos = (int) (sleepTime - (sleepMillis * 1000000));
try {
Thread.sleep(sleepMillis, sleepNanos);
} catch (InterruptedException ie) {
/* ignore */
}
}
showTime += nsPerFrame;
// print frame rate achieved
fps++;
if (System.currentTimeMillis() > printTime) {
System.out.println("fps: " + fps);
fps = 0;
printTime += 1000;
}
}
}
}
A sample image to use with this program (the path to which must be passed as a command line argument) is this:
So my (2-part) question is:
Why is this effect happening? How can I reach actual 60 fps?
(Bonus question for commenters: do you experience the same effect also under other operating systems?)
VolatileImage
which may lose the contents based on the OS factors like you said and thus resulting in the behaviour being mentioned. Hence by limiting your looping condition for lost buffers might solve your problem. Again, I'm not really sure if this is really the reason behind the code behaviour. – Columbiumfps
value being changed while you did that?? For me, I could observe thefps
value being changed between the range of 59 to 61 and the refreshing was as required. – Columbiumfps
value stays roughly the same independent of the observed fps (compare to my original question). The difference I observe between with or without mouse motion is not subtle: it's a very visible increase. – Santasantacruz