4

I have created an animation of multiple text nodes. User is supposed to be reading the text as it is received from the server. The issue is that in a few minutes (approximately 5) the performance begins to drop. From 60 fps to 30 fps and below. As a result the text is very hard to read.

Edit 2:

I have created a Minimal, Complete, and Verifiable example:

There are 3 files in the project:

MainFxApp:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Stage;

import java.util.List;

public class MainFxApp extends Application {

    @Override
    public void start(Stage primaryStage) {

        Pane root = new Pane();
        root.setStyle("-fx-background-color: black;");

        MyAnimationTimer myAnimationTimer = new MyAnimationTimer((List<MyText>) (List<?>) root.getChildren());

        MyText newText;
        for (int i = 65; i < 85; i++) {
            newText = new MyText("" + ((char) i));
            newText.setFill(Color.GREEN);
            newText.setFont(Font.font(40));
            myAnimationTimer.addNode(newText);
        }

        Scene scene = new Scene(root, 1200, 600);

        primaryStage.setTitle("Performance test");
        primaryStage.setScene(scene);
        primaryStage.show();

        myAnimationTimer.start();


    }

    public static void main(String[] args) {
        launch(args);
    }
}

MyAnimationTimer:

import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.scene.CacheHint;
import javafx.scene.Node;

import java.util.List;

public class MyAnimationTimer extends AnimationTimer {
    private List<MyText> nodes;
    private double panelWidth;
    private double panelHeight;
    private double basicVelocity; // Distance per nanosecond

    private long lastFrameTime;

    private long timeCount = 0;
    private int frameCount = 0;

    MyAnimationTimer(List<MyText> nodes) {
        super();
        this.nodes = nodes;
        this.panelWidth = 1200;
        this.panelHeight = 600;
        this.setBasicVelocity();
    }

    @Override
    public void start() {
        this.lastFrameTime = System.nanoTime();
        super.start();
    }

    @Override
    public void handle(long now) {

        long deltaT = now - lastFrameTime;
        double deltaX = this.basicVelocity * deltaT;

        for (MyText node : this.nodes) {
            node.setTranslateX(node.getTranslateX() + node.direction * deltaX);
            if (node.getTranslateX() < 0) {
                node.direction = 1;
            } else if (node.getTranslateX() > 1200) {
                node.direction = -1;
            }
        }

        this.lastFrameTime = now;

        this.timeCount += deltaT;
        this.frameCount++;

        if (timeCount > 1000000000) {
            System.out.println(this.frameCount / (double) timeCount * 1000000000);
            this.frameCount = 0;
            this.timeCount = 0;
        }
    }

    void addNode(final MyText node) {  // Not sure about the final thing
        Platform.runLater(() -> {
            node.setCache(true);
            node.setCacheHint(CacheHint.SPEED);
            node.setTranslateY(panelHeight / 2);

            double nodePositionX = panelWidth - 20;

            if (nodes.size() >= 1) {
                Node lastNode = nodes.get(nodes.size() - 1);
                double lastNodeEnd = lastNode.getTranslateX() + 50;
                if (lastNodeEnd > nodePositionX) {
                    nodePositionX = lastNodeEnd;
                }
            }

            node.setTranslateX(nodePositionX);

            nodes.add(node);
        });
    }

    private void setBasicVelocity() {
        Platform.runLater(() -> {
            this.basicVelocity = ((panelWidth / 4) * 3 / (double) 5000 / 1000000.0);
        });
    }
}

MyText:

import javafx.scene.text.Font;
import javafx.scene.text.Text;

class MyText extends Text {
    int direction = -1;

    MyText(String text) {
        super(text);
        this.setFont(new Font("Arial Regular", 40));
    }
}

Even with this simple example the performance drop is significant. There are 20 nodes in the scene and the fps drops bellow 20. My CPU is i5-4440 CPU (3.10GHz × 4). The issue occurs on every platform I have tested it on - JavaFX 8, JavaFX 9 and Ubuntu 16.04.

Edit 3:

The issue seems to be present only on Linux platform.

However even on Windows when I am frequently accessing the JavaFX thread via the Platform.runLater method the animation doesn't seem to be fluent even though it holds onto the 60fps. Does anybody know how it could be improved?

Thanks, Jan

Jan Beneš
  • 742
  • 1
  • 5
  • 24
  • Where is MyText class? – SedJ601 Jun 30 '17 at 18:26
  • Can you see any resource being consumed in a resource monitor? What OS are you using? – Itai Jun 30 '17 at 18:44
  • I apologize for a late response I've been away for a week. I added the MyText source code in edit. I am using Ubuntu 16.04 but I have also tested the code on Windows 10 with identical results. The issue occurs on both JavaFX 8 and JavaFX 9. When I start the app the animation runs at 60 fps and the CPU consumption is approximately 20% on all the 4 threads. When the issue occurs every thread is used approximately by 50% so the CPU time consumption increase is significant. – Jan Beneš Jul 09 '17 at 17:17
  • I have tried your example and cannot reproduce the issue, nor can I see anything obvious such as a memory leak. I will leave the application running for a bit and report if the performance issue occurs. (Win 10, i5-4300U 2.50GHz). CPU usage ~ 1.5%. Built using jdk_1.8.0_60 – d.j.brown Jul 10 '17 at 09:44
  • I've tried it on Windows 10 again and it seems to be holding on to the 60fps. I can't explain why but I can't reproduce the issue on Windows anymore. However the issue on Linux is still present and I have reproduced it every test I have ran. – Jan Beneš Jul 10 '17 at 14:51
  • 1
    Does the memory consumption increase as well? Can you run with `-Dprism.verbose=true`? Also, look at this question: https://stackoverflow.com/questions/40228866/optimizing-memory-leakage-in-javafx/40239829 – Itai Jul 14 '17 at 13:09

1 Answers1

1

It looks like your Linux hardware acceleration has some issues with JavaFX rendering. I met no performance problems on my machine with similar capabilities.

Try to run your code in software only mode:

java -Dprism.order=j2d -jar myfxapp.jar 
Sergey Grinev
  • 34,078
  • 10
  • 128
  • 141
  • In this case it may be a duplicate of this: https://stackoverflow.com/questions/40228866/optimizing-memory-leakage-in-javafx/40239829#40239829 – Itai Jul 14 '17 at 13:06
  • It does solve the issue and it is indeed a duplicate of the thread posted by @sillyfly I am going to flag it as such. Thanks – Jan Beneš Jul 15 '17 at 15:52