2016-11-17 27 views
12

Я испытываю неожиданное взаимодействие между системными событиями и частотой обновления окна в простых приложениях Java2D в Linux/XWindows. Это лучше всего продемонстрировать с помощью небольшого примера ниже.Java2D: взаимодействие между событиями 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; 
      } 
     } 
    } 
} 

Образец изображения для использования с этой программой (путь к которому должен быть передан в качестве аргумента командной строки) заключается в следующем:

enter image description here

Так что мой (2-часть) Вопрос в том, :

Почему этот эффект происходит? Как я могу достичь актуально 60 fps?

(Bonus вопрос комментаторам: вы испытываете тот же эффект также под другими операционными системами)

+0

Я попытался запустить свой код несколько раз в моей машине, которая работает на окнах 10. Иногда я наблюдал, что мерцающий замедляется. Вы когда-нибудь сталкивались с этой проблемой? Вы пытались защитить свой код от потери кадров? –

+0

@sbaitmangalkar Спасибо за попытку кода. Потеря кадров - это отдельный вопрос, который я сейчас не рассматриваю. Но просто чтобы быть ясным: когда вы запускаете программу, вы не видите ускорение анимации, как только вы перемещаете курсор мыши над окном? – Thomas

+1

Буферы в стратегии буферов - это в основном 'VolatileImage', которые могут потерять содержимое на основе факторов ОС, как вы сказали, и, таким образом, в результате упомянутого поведения. Следовательно, ограничивая условие цикла для потерянных буферов, вы можете решить свою проблему. Опять же, я не уверен, действительно ли это причина поведения кода. –

ответ

6

Q: Почему этот эффект происходит?

Короткого ответ: Нужно позвонить canvas.getToolkit().sync() или Toolkit.getDefaultToolkit().sync() после BufferStrategy#show

Длинного ответа: без явных Toolkit#sync пикселей не делает его на экран, пока команда X11 буфер не заполнен или другое событие, например, перемещение мыши, заставляет флеш.

Цитата из http://www.java-gaming.org/index.php/topic,15000.

... даже если мы (Java2D) немедленно выдаем наши команды визуализации, то драйвер может не выполнить их сразу. Классическим примером является X11 - попробуйте синхронизировать цикл Graphics.fillRects - посмотрите, сколько вы можете выдать через пару секунд. Без инструментария.sync() (который в случае X11-конвейера делает XFlush()) после каждого вызова или в конце цикла, что вы на самом деле измеряете, так это то, как быстро Java2D может вызывать метод XFillRect X11 lib, который просто расширяет эти вызовы до тех пор, пока не будет команда буфер заполнен, только затем они отправляются на сервер X для выполнения.

Q: Как я могу достичь фактических 60 кадров в секунду?

: Промыть буфера команд и рассмотрим включение OpenGL ускорения для Java2D с System.setProperty("sun.java2d.opengl", "true"); в коде или -Dsun.java2d.opengl=true опции командной строки.

Смотрите также: