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