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

问题描述 投票:4回答:3

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

非常感谢。

javafx-8 frame-rate
3个回答
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指出:

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


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并不保证每10ms精确运行一次,因此如果绝对精度很重要,则需要其他技术。但是,如上所述,您无论如何都无法实际显示所有帧,或以超过60fps的速度显示它们。


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();
© www.soinside.com 2019 - 2024. All rights reserved.