获取 JavaFX 应用程序帧速率的首选方法是什么?
What is the preferred way of getting the frame rate of a JavaFX application?
这是一个很简单的问题:
获取 JavaFX 应用程序帧速率的首选方法是什么?
Google 出现了 2009 年的结果,但该示例属于 JavaFX 1.x 并且开始时以一种奇怪的方式(某种外部仪表)运行。找不到更好的例子,我 post 在这里。
我希望能够查询我的 JavaFX 应用程序(或者如果需要,当前场景)并获取当前 FPS 是多少。
更新:2015 年 2 月 8 日
问题的各种解决方案在下面 post作为答案。我还发现这个问题被下面的博客post引用了:http://tomasmikula.github.io/blog/2015/02/08/measuring-fps-with-reactfx.html
这表明(由于以下解决方案的冗长)测量 FPS 已添加到 ReactFX 2.0 里程碑 2 中。很酷的事情进展方式。
您可以使用 AnimationTimer
.
AnimationTimer
的handle方法每帧调用一次,传入的值是当前时间,以纳秒为单位(最佳近似值)。这样您就可以跟踪自上一帧以来的时间。
这是一个跟踪最后 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,他提出了一种更复杂的方法。我的尝试如下:
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;
}
}
这是一个很简单的问题:
获取 JavaFX 应用程序帧速率的首选方法是什么?
Google 出现了 2009 年的结果,但该示例属于 JavaFX 1.x 并且开始时以一种奇怪的方式(某种外部仪表)运行。找不到更好的例子,我 post 在这里。
我希望能够查询我的 JavaFX 应用程序(或者如果需要,当前场景)并获取当前 FPS 是多少。
更新:2015 年 2 月 8 日
问题的各种解决方案在下面 post作为答案。我还发现这个问题被下面的博客post引用了:http://tomasmikula.github.io/blog/2015/02/08/measuring-fps-with-reactfx.html
这表明(由于以下解决方案的冗长)测量 FPS 已添加到 ReactFX 2.0 里程碑 2 中。很酷的事情进展方式。
您可以使用 AnimationTimer
.
AnimationTimer
的handle方法每帧调用一次,传入的值是当前时间,以纳秒为单位(最佳近似值)。这样您就可以跟踪自上一帧以来的时间。
这是一个跟踪最后 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,他提出了一种更复杂的方法。我的尝试如下:
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;
}
}