How to create 100fps animation in JavaFx8

2019-01-15 04:55发布

问题:

I need to create a 100fps animation that display 3d data from a file that contains 100 frames per second. But the AnimationTimer in javaFx allows me to get 60fps only. How to get over it.

Thanks a lot.

回答1:

Removing the JavaFX Frame Rate Cap

You can remove the 60fps JavaFX frame rate cap by setting a system property, e.g.,

java -Djavafx.animation.fullspeed=true MyApp

Which is an undocumented and unsupported setting.

Removing the JavaFX frame rate cap may make your application considerably less efficient in terms of resource usage (e.g. a JavaFX application without a frame rate cap will consume more CPU than an application with the frame rate cap in place).

Configuring the JavaFX Frame Rate Cap

Additionally, there is another undocumented system property you could try:

javafx.animation.framerate

I have not tried it.

Debugging JavaFX Frames (Pulses)

There are other settings like -Djavafx.pulseLogger=true which you could enable to help you debug the JavaFX architecture and validate that your application is actually running at the framerate you expect.

JavaFX 8 has a Pulse Logger (-Djavafx.pulseLogger=true system property) that "prints out a lot of crap" (in a good way) about the JavaFX engine's execution. There is a lot of provided information on a per-pulse basis including pulse number (auto-incremented integer), pulse duration, and time since last pulse. The information also includes thread details and events details. This data allows a developer to see what is taking most of the time.

Warning

Normal warnings for using undocumented features apply, as Richard Bair from the JavaFX team notes:

Just a word of caution, if we haven't documented the command line switches, they're fair game for removal / modification in subsequent releases :-)



回答2:

The bottom line is that JavaFX currently has a target (i.e. maximum) frame rate of 60fps; so you will only be able to display at most 60 frames per second. So, assuming you can read the frames from the file and process them quickly enough, some frames in the file will have to be skipped if you use JavaFX.

The basic approach I would try is as follows:

Create a BlockingQueue<Image> to store the frames. You will need to tune the capacity of this queue: too big and you will run out of memory, too small and you might end up waiting too long for an image to be available.

Create a background thread that reads from your file, creates the images, and pushes them to the queue as fast as you can.

Create a ScheduledService with a period of 10ms, that takes an image from the queue and places it in an AtomicReference<Image>. If the current value of the atomic reference is null, schedule a call on the FX Application thread to display the image in the atomic reference, replacing it back with null. This will ensure you don't flood the FX Application thread with calls more quickly than it can process them.

You might need to use other strategies than using an Image, for performance reasons, but the general timing and threading strategy should work.

Some code:

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();

Note that the ScheduledService does not guarantee to run precisely every 10ms, so if absolute precision is important you will need other techniques. But, as mentioned above, you will not be able to actually display all the frames anyway, or display them at more than 60fps.