navy1978
navy1978

Reputation: 1439

Any type of Animation or Timeline uses too much CPU in JavaFX

I'm using JavaFx that comes with JDK 8.0, on a MacBook Pro with 2,4 GHz Intel Core 2 Duo processor and 4GB of RAM.

I have a strange behavior, using the following class:

import com.sun.javafx.perf.PerformanceTracker;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class HelloWorld extends Application {
    private static PerformanceTracker tracker;
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Hello World!");
        Button btn = new Button();
        btn.setText("Say 'Hello World'");
        btn.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                System.out.println("Hello World!");
            }
        });

        StackPane root = new StackPane();
        root.getChildren().add(btn);
        Scene scene= new Scene(root, 300, 250);
        primaryStage.setScene(scene);
        primaryStage.show();


        Label label1 = new Label();
        Label label2 = new Label();
        ((Pane)root).getChildren().addAll(label1, label2);

        scene.setOnKeyPressed((e)->{
            label2.setText(label1.getText());
        });



        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;
    }
}

This code when executed uses CPU between a percentage of 0.2% to max 10%. If I remove the comment from :

frameRateMeter.start();

I get that the same code uses CPU from 20% to 40%.

This is just an example but, the application I wrote, commenting out the line above, runs using around 40% of CPU and removing the comment runs near 100% of CPU.

Is that normal? I notice that also adding any time of Timeline (also very simple one) that execute continuously, produces a ridiculous use of CPU.

Is really so expensive to use animation in JavaFX or there is something I have missed?

Any help would be really appreciate.

Upvotes: 3

Views: 1601

Answers (2)

GOXR3PLUS
GOXR3PLUS

Reputation: 7255

Here is an easy way to count FPS[frames per second] from AnimationTimer without using any other class:

/**
 * AnimationTimer .
 *
 * @author GOXR3PLUS
 */
public class PaintService extends AnimationTimer {

    /** The next second. */
    long nextSecond = 0L;

    /** The Constant ONE_SECOND_NANOS. */
    private static final long ONE_SECOND_NANOS = 1_000_000_000L;
    /**
     * When this property is <b>true</b> the AnimationTimer is running
     */
    private volatile SimpleBooleanProperty running = new SimpleBooleanProperty(false);


    @Override
    public void start() {
        nextSecond = 0L;
        super.start();
        running.set(true);
    }

    @Override
    public void stop() {
        super.stop();
        running.set(false);
    }

    /**
     * @return True if AnimationTimer is running
     */
    public boolean isRunning() {
        return running.get();
    }

    /**
     * @return Running Property
     */
    public SimpleBooleanProperty runningProperty() {
        return running;
    }


    @Override
    public void handle(long nanos) {

        // -- Show FPS if necessary.
        if (true) { //originally i had a variable (showFPS) here

            framesPerSecond++;

            // Check for 1 second passed
            if (nanos >= nextSecond) {
                fps = framesPerSecond;
                framesPerSecond = 0;
                nextSecond = nanos + ONE_SECOND_NANOS;
            }


            label.setText("FPS: " + fps);
        }


    }

}

Upvotes: 1

Maxim
Maxim

Reputation: 9981

I think you are updating FPS value in Label very frequently:

The class AnimationTimer allows to create a timer, that is called in each frame while it is active.

When you update text in the label1 the JavaFX draw new frames again and again. You can easy check this: write FPS to STDOUT instead of label1:

...
System.out.println(String.format("Current frame rate: %.3f fps", tracker.getAverageFPS()));
// label1.setText(String.format("Current frame rate: %.3f fps", tracker.getAverageFPS()));
...

In this case you should see less FPS rate.

So, try to update FPS value every one or half second use any Java/JavaFX timer i.e.:

Timeline timeline = new Timeline(new KeyFrame(Duration.millis(500), event -> {
    label1.setText(String.format("Current frame rate: %.3f fps", tracker.getAverageFPS()));
}));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();

Average CPU value is about 1.8-2% on my macOS.

Upvotes: 2

Related Questions