Я испытываю неожиданное взаимодействие между системными событиями и частотой обновления окна в простых приложениях Java2D в Linux/XWindows. Это лучше всего продемонстрировать с помощью небольшого примера ниже.
Эта программа создает небольшое окно, в котором полукруг отображается при разных поворотах. Графика обновляется со скоростью 60 кадров в секунду для создания мерцающего дисплея. Это достигается с помощью BufferStrategy
, а именно путем вызова метода show
.
Тем не менее, я отмечаю, что когда я (а) перемещаю мышь над окном, чтобы окно принимало события мыши или (б) удерживало клавишу на клавиатуре, чтобы окно получало события клавиатуры, мерцание увеличивается заметно.
Поскольку скорость, с которой вызван BufferStrategy.show()
, не влияет на эти события, как видно из распечаток на консоли (они должны постоянно находиться на скорости около 60 кадров в секунду). Тем не менее, более быстрое мерцание указывает на то, что скорость фактического обновления дисплея действительно меняется.
Мне кажется, что фактические, т.е. видимые 60 кадров в секунду, не достигаются, если не генерируются события мыши или клавиатуры.
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;
}
}
}
}
Образец изображения для использования с этой программой (путь к которому должен быть передан как аргумент командной строки):
Итак, мой вопрос (2 части):
Почему этот эффект происходит? Как я могу достичь фактических 60 кадров в секунду?
(Бонусный вопрос для комментаторов: вы испытываете тот же эффект и в других операционных системах?)