JavaFX FPS cap at 60 FPS

admin

Administrator
Staff member
There seems to be a common understanding that the JavaFX UI thread is caped at 60 updates per second(<a href="https://docs.oracle.com/javafx/2/architecture/jfxpub-architecture.htm" rel="nofollow">1</a>, <a href="https://tbeernot.wordpress.com/2011/11/12/javafx-2-0-bubblemark/" rel="nofollow">2</a>). It is my understanding that update means
Code:
pulse
.

<blockquote>
A pulse is an event that indicates to the JavaFX scene graph that it
is time to synchronize the state of the elements on the scene graph
with Prism. A pulse is throttled at 60 frames per second (fps) maximum
and is fired whenever animations are running on the scene graph. Even
when animation is not running, a pulse is scheduled when something in
the scene graph is changed. For example, if a position of a button is
changed, a pulse is scheduled.
</blockquote>

Source: <a href="https://docs.oracle.com/javafx/2/architecture/jfxpub-architecture.htm" rel="nofollow">https://docs.oracle.com/javafx/2/architecture/jfxpub-architecture.htm</a>

To figure out what happens if there are for example more than 60 calls to
Code:
Platform.runLater
I wrote this small program:

Code:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class HighUITraffic extends Application {

    private int counter = 0;
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");

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

    @Override
    public void start(Stage primaryStage) {


        StackPane root = new StackPane();


        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                long sheduleTime = System.currentTimeMillis();
                System.out.println("Task "+counter+" run at "+sdf.format(new Date(sheduleTime)));
                Platform.runLater(new RunInUI(counter));
                counter++;
            }
        };
        timer.schedule(task, 0, 10); // every 10ms =&gt; 100 per second

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    private static class RunInUI implements Runnable {
        private final int counter;

        public RunInUI(int counter) {
            this.counter = counter;
        }

        @Override
        public void run() {
            long executionTime = System.currentTimeMillis();
            System.out.println("Task "+counter+" executed in Application Thread at "+sdf.format(new Date(executionTime)));
        }
    }
}

My expectation were that either:

<ul>
<li>The
Code:
Runnable
s are stacked out and the UI thread runs interspersed and then executes all
Code:
Runables
that have queued up.</li>
<li>The UI thread executes the first 60 calls and then queues further
Code:
Runable
s up.</li>
</ul>

However what happened after some initial delay where the Runables queued up and where then all handled in one go by the UI thread, was that the two threads run in order:

<blockquote>
Task 1281 run at 2016-01-25T18:37:00.269 Task 1281 executed in
Application Thread at 2016-01-25T18:37:00.269 Task 1282 run at
2016-01-25T18:37:00.274 Task 1282 executed in Application Thread at
2016-01-25T18:37:00.274 Task 1283 run at 2016-01-25T18:37:00.279
</blockquote>

And there were more than 60 calls of either thread during one second (I tried with 100 and 200, without any difference).

Therefore I am confused:

<ul>
<li>Did I misunderstand the concept of capping at 60 pulses?</li>
<li>Do this small execution fragment not weight that much, so that the limit may be exceeded?</li>
</ul>

What I wanted to know in the first place is what does happen with
Code:
Runable
s that are pushed on the UI thread if the thread has reached its cap limit. Are all
Code:
Runable
s that have queued up executed in sequence in one run of the UI thread or is only one
Code:
Runable
executed and the rest has to wait? The second case would cause serious problems, when continuously pushing more
Code:
Runable
s on the UI thread than that limit.