This is quite a simple question:
What is the preferred way of getting the frame rate of a JavaFX application?
Google turns up a result from 2009, but that e
James_D gave a naive implementation that gives the instantaneous FPS and he suggested a more sophisticated approach. My attempt at that is the following:
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;
}
}
You can use an AnimationTimer.
The AnimationTimer
's handle method is called once on each frame, and the value passed in is the current time in nanoseconds (a best approximation). So you can track how long since the previous frame.
Here's an implementation that tracks the times of the last 100 frames and computes the frame rate using them:
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);
}
}
I just copied James_D program and changed to using the PerformanceTracker. The options I copied from the program I had downloaded earlier called JavaFXBalls3
. The options don't seem to make a difference.
Press any key to to see what the label says. Mine's always near 60. Maybe a more complicated scene would be lower. AFAIK 60 is the max for animation.
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;
}
}