Каков предпочтительный способ получения частоты кадров приложения JavaFX?

Это довольно простой вопрос:

каков предпочтительный способ получения частоты кадров приложения JavaFX?

Google появляется результат с 2009 года, но этот пример относится к JavaFX 1.x и работает странным образом (какой-то внешний счетчик) для начала. Не будучи в состоянии найти лучший пример,я размещаю здесь.

Я хочу иметь возможность запросить мое приложение JavaFX (или, если необходимо, текущую сцену) и получить то, что текущий FPS есть.

обновление: Feb. 8 мая 2015 года

различные решения проблемы размещены ниже в качестве ответов. Я также обнаружил, что на этот вопрос ссылается следующее сообщение в блоге: http://tomasmikula.github.io/blog/2015/02/08/measuring-fps-with-reactfx.html

который говорит ,что (в результате многословия решений ниже) измерение FPS было добавлено в reactfx 2.0 milestone 2. Круто, как вещи путешествуют.

3 ответов


можно использовать AnimationTimer.

на вызывается один раз на каждом кадре, а передаваемое значение-текущее время в наносекундах (наилучшее приближение). Таким образом, вы можете отслеживать, сколько времени прошло с предыдущего кадра.

вот реализация, которая отслеживает время последних 100 кадров и вычисляет частоту кадров, используя их:

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class SimpleFrameRateMeter extends Application {

    private final long[] frameTimes = new long[100];
    private int frameTimeIndex = 0 ;
    private boolean arrayFilled = false ;

    @Override
    public void start(Stage primaryStage) {

        Label label = new Label();
        AnimationTimer frameRateMeter = new AnimationTimer() {

            @Override
            public void handle(long now) {
                long oldFrameTime = frameTimes[frameTimeIndex] ;
                frameTimes[frameTimeIndex] = now ;
                frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length ;
                if (frameTimeIndex == 0) {
                    arrayFilled = true ;
                }
                if (arrayFilled) {
                    long elapsedNanos = now - oldFrameTime ;
                    long elapsedNanosPerFrame = elapsedNanos / frameTimes.length ;
                    double frameRate = 1_000_000_000.0 / elapsedNanosPerFrame ;
                    label.setText(String.format("Current frame rate: %.3f", frameRate));
                }
            }
        };

        frameRateMeter.start();

        primaryStage.setScene(new Scene(new StackPane(label), 250, 150));
        primaryStage.show();
    }

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

Я просто скопировал программу James_D и перешел на использование PerformanceTracker. Варианты я скопировал из программы я скачал раньше называли JavaFXBalls3. Варианты, кажется, не имеют значения.

нажмите любую клавишу, чтобы увидеть, что на этикетке написано. Мой всегда около 60. Может быть, более сложная сцена была бы ниже. AFAIK 60-это максимум для анимации.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

import com.sun.javafx.perf.PerformanceTracker;
import java.security.AccessControlException;
import javafx.animation.AnimationTimer;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;


public class FPS extends Application {
    public static void main(String[] args) { launch(args); }

    private static PerformanceTracker tracker;

    @Override
    public void start(Stage stage) {
        VBox root = new VBox(20);
        Label label1 = new Label();
        Label label2 = new Label();
        root.getChildren().addAll(label1, label2);

        Scene scene = new Scene(root, 200, 100);

        try {
            System.setProperty("prism.verbose", "true");
            System.setProperty("prism.dirtyopts", "false");
            //System.setProperty("javafx.animation.fullspeed", "true");
            System.setProperty("javafx.animation.pulse", "10");
        } catch (AccessControlException e) {}

        scene.setOnKeyPressed((e)->{
            label2.setText(label1.getText());
        });
        stage.setScene(scene);
        stage.show();


        tracker = PerformanceTracker.getSceneTracker(scene);
        AnimationTimer frameRateMeter = new AnimationTimer() {

            @Override
            public void handle(long now) {
                label1.setText(String.format("Current frame rate: %.3f fps", getFPS()));
            }
        };

        frameRateMeter.start();
    }

    private float getFPS () {
        float fps = tracker.getAverageFPS();
        tracker.resetAverageFPS();
        return fps;
    }

}

James_D дал наивную реализацию, которая дает мгновенный FPS, и он предложил более сложный подход. Моя попытка состоит в следующем:--2-->

public class FXUtils
{
    private static long lastUpdate = 0;
    private static int index = 0;
    private static double[] frameRates = new double[100];

    static
    {
        AnimationTimer frameRateMeter = new AnimationTimer()
        {
            @Override
            public void handle(long now)
            {
                if (lastUpdate > 0)
                {
                    long nanosElapsed = now - lastUpdate;
                    double frameRate = 1000000000.0 / nanosElapsed;
                    index %= frameRates.length;
                    frameRates[index++] = frameRate;
                }

                lastUpdate = now;
            }
        };

        frameRateMeter.start();
    }

    /**
     * Returns the instantaneous FPS for the last frame rendered.
     *
     * @return
     */
    public static double getInstantFPS()
    {
        return frameRates[index % frameRates.length];
    }

    /**
     * Returns the average FPS for the last 100 frames rendered.
     * @return
     */
    public static double getAverageFPS()
    {
        double total = 0.0d;

        for (int i = 0; i < frameRates.length; i++)
        {
            total += frameRates[i];
        }

        return total / frameRates.length;
    }
}