如何在JavaFx 8中创建100 fps动画

时间:2015-03-02 21:05:53

标签: javafx-8 frame-rate

我需要创建一个100fps的动画,它可以显示包含每秒100帧的文件的3d数据。但javaFx中的AnimationTimer只允许我获得60fps。如何克服它。

非常感谢。

3 个答案:

答案 0 :(得分:6)

删除JavaFX帧率上限

您可以通过设置系统属性(例如

)来删除60fps JavaFX帧速率上限
java -Djavafx.animation.fullspeed=true MyApp

这是undocumented and unsupported setting

删除JavaFX帧速率上限可能会使您的应用程序在资源使用方面效率大大降低(例如,没有帧速率上限的JavaFX应用程序将比具有帧速率上限的应用程序消耗更多CPU)。

配置JavaFX帧率上限

此外,您还可以尝试另一种未记录的系统属性:

javafx.animation.framerate

我没试过。

调试JavaFX帧(脉冲)

您可以启用other settings like -Djavafx.pulseLogger=true来帮助您调试JavaFX体系结构并验证您的应用程序实际上是否以您期望的帧速率运行。

  

JavaFX 8有一个脉冲记录器(-Djavafx.pulseLogger=true系统属性),它可以打印出很多废话" (以一种很好的方式)关于JavaFX引擎的执行。在每个脉冲的基础上提供了许多信息,包括脉冲数(自动递增的整数),脉冲持续时间和自上一次脉冲以来的时间。该信息还包括线程详细信息和事件详细信息。这些数据允许开发人员查看大部分时间的内容。

警告

使用未记录的功能的正常警告适用,如Richard Bair from the JavaFX team注意:

  

请注意,如果我们没有记录命令行开关,那么在后续版本中删除/修改它们是公平的游戏: - )

答案 1 :(得分:0)

底线是JavaFX目前的目标(即最大)帧速率为60fps;所以你每秒最多只能显示60帧。因此,假设您可以从文件中读取帧并快速处理它们,那么如果您使用JavaFX,则必须跳过文件中的某些帧。

我尝试的基本方法如下:

创建BlockingQueue<Image>来存储框架。您将需要调整此队列的容量:太大而且内存不足,太小而您可能最终等待图像可用时间过长。

创建一个后台线程,从您的文件中读取,创建图像,并尽快将它们推送到队列。

创建一个周期为10毫秒的ScheduledService,从队列中获取图像并将其放在AtomicReference<Image>中。如果原子引用的当前值为null,则在FX Application线程上安排调用以在原子引用中显示图像,并将其替换为null。这将确保您不会更快地使FX应用程序线程充满呼叫,而不是处理它们。

出于性能原因,您可能需要使用除Image之外的其他策略,但一般时序和线程策略应该有效。

一些代码:

final Duration frameFrequency = Duration.millis(10)
final int queueCapacity = 8 ; // may need tuning
BlockingQueue<Image> frameQueue = new ArrayBlockingQueue<>(queueCapacity);

AtomicReference<Image> nextFrame = new AtomicReference<>();

ImageView display = new ImageView();

Thread fileReadThread = new Thread(() -> {
    // pseudocode...
    while (moreImagesToRead()) {
        Image image = readImageFromFile();
        frameQueue.put(image);
    }
});
fileReadThread.setDaemon(true);
fileReadThread.start();

ScheduledService<Void> service = new ScheduledService<Void>(frameFrequency) {
    @Override
    protected Task<Void> createTask() {
        return new Task<Void>() {
            public Void call() throws InterruptedException {
                Image image = frameQueue.take();
                if (nextFrame.getAndSet(image) == null) {
                    Platform.runLater(() -> {
                        Image img = nextFrame.getAndSet(null);
                        // display img:
                        display.setImage(img);
                    });
                }
            }
        };
    }
};
service.start();

请注意,ScheduledService并不保证每10毫秒精确运行一次,因此如果绝对精度很重要,则需要其他技术。但是,如上所述,您无论如何都无法实际显示所有帧,或以超过60fps的速度显示它们。

答案 2 :(得分:0)

Fullspeed = true会在没有控制的情况下为您提供较高的帧速率(并由于渲染过多而降低了应用程序的性能),帧速率确实无法正常工作。 使用:

-Djavafx.animation.pulse=value

您可以使用以下代码检查帧率。我通过将脉搏频率设置为2、60和120(我有一个240Hz的显示器)来检查它是否也确实起作用,您会发现随机数变化的速度有所不同。

 private final long[] frameTimes = new long[100];
    private int frameTimeIndex = 0 ;
    private boolean arrayFilled = false ;
    Label label = new Label();
        root.getChildren().add(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" +", Random number:" + Math.random(), frameRate));
                }
            }
        };

        frameRateMeter.start();