import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.value.ObservableDoubleValue;
import javafx.beans.value.ObservableLongValue;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class MultipleDirtyScenes extends Application {
    private static final int SCENE_COUNT = 3;

    @Override
    public void start(Stage ignorePrimaryStage) {
        final FramesPerSecondCounter frameRateCounter = new FramesPerSecondCounter();
        frameRateCounter.start();

        for (int i = 0; i < SCENE_COUNT; i++) {

            final Label fpsLabel = new Label();
            fpsLabel.textProperty().bind(Bindings.format("FPS: %.1f", frameRateCounter.fpsProperty())); // FPS will be ~ 60.0/SCENE_COUNT
            final Label lastFrameTimeNsLabel = new Label();
            lastFrameTimeNsLabel.textProperty().bind(Bindings.format("Last frame time (ns): %d", frameRateCounter.lastFrameTimeNsProperty())); // Makes the scene constantly dirty

            final Scene scene = new Scene(new VBox(fpsLabel, lastFrameTimeNsLabel), 480, 100);

            final Stage stage = new Stage();
            stage.setScene(scene);
            stage.show();
        }
    }

    public static void main(String[] args) {
        launch();
    }

    private static class FramesPerSecondCounter extends AnimationTimer {

        private static final int FRAME_TIME_COUNT = 10;
        private final long[] frameTimesNs = new long[FRAME_TIME_COUNT];
        private int frameTimeIndex = 0;
        private final LongProperty lastFrameTimeNs = new SimpleLongProperty();
        private final DoubleProperty fpsValue = new SimpleDoubleProperty();

        @Override
        public void handle(final long nowNs) {
            lastFrameTimeNs.set(nowNs);

            final long oldFrameTimeNs = frameTimesNs[frameTimeIndex];
            frameTimesNs[frameTimeIndex] = nowNs;
            frameTimeIndex = (frameTimeIndex + 1) % frameTimesNs.length;

            final long elapsedNanos = nowNs - oldFrameTimeNs;
            final long elapsedNanosPerFrame = elapsedNanos / frameTimesNs.length;
            final double fps = 1_000_000_000.0 / elapsedNanosPerFrame;
            fpsValue.set(fps);
        }

        public ObservableDoubleValue fpsProperty() {
            return fpsValue;
        }

        public ObservableLongValue lastFrameTimeNsProperty() {
            return lastFrameTimeNs;
        }
    }
} 