I'm seeing some really weird issues with Transparent Stages on Windows when their framerate (both stability and speed) is compared with normal decorated/undecorated stages.

The transparent stages seem to obey different rules when it comes to timely firing AnimationTimers.

Where a decorated stage will show an almost exact 60 Hz in all circumstances (usually in a pattern of 24 frames of ~15ms then a double frame of ~30ms averaging out to 1/60th of a second per frame) the transparent stages act completely different. Increasing the javafx.animation.pulse on a decorated stage to say 240 will reduce this variance even further (min/max will be between 16 and 18 ms) while keeping an exact 60 Hz.

However, on a transparent stage this all goes out the window:

Running a test with javafx.animation.pulse left unchanged, it reaches an unsteady 56/57 Hz with the variance being incredibly high and random (5 - 40 ms)... while FOCUSED. Unfocusing the stage and this becomes a more steady 62.5 Hz and the variance reduces to 8 - 24 ms. Refocusing the stage immediately sees variance spiking again and framerate dropping to an unsteady 56/57 Hz.

The same test with javafx.animation.pulse set to 240 on a transparent stage shows some odd things as well. While focused the stage retains a similar behaviour (unsteady 56/57 Hz with a 5-40 ms variance). When unfocused it starts running at 72-78 Hz with about an 8-24 ms variance (same as before but with a higher frame rate).

Setting javafx.animation.pulse to 24 for a transparent stage locks the framerate to an exact 24 Hz with almost no variance. Focused/unfocused suddenly makes no difference any more. For decorated/undecorated stages this does not change the framerate (it remains rock solid at 60 Hz) but the variances become much larger (but show a steady repeated pattern).

So in summary:

1) Decorated/undecorated stages perform very stable, variance can be reduced by increasing javafx.animation.pulse -- this property seems to have the effect of making the animation timings more accurate (reduces variance) while retaining an exact 60 Hz. The variance pattern is very consistent and predictable.

2) Transparent stages perform much more erratic, and are extra erratic when they have focus. They never reach an exact 60 Hz (measured over 300 frames) but either go several frames under (57.5 Hz when focused) or over (62.5 Hz when not focused). Their variance has no pattern to it and seems random.

3) javafx.animation.pulse for Transparent stages seems to control the maximum frame rate (setting it to 24 for example will limit the fps to almost exactly 24 Hz with almost no variance).

Below is the code I used to test this.

--John

public class FrameRateTest extends Application {
  private static int SIZE = 300;

  @Override
  public void start(Stage stage) {
System.out.println("javafx.runtime.version: " + System.getProperties().get("javafx.runtime.version"));

      Label fpsLabel = new Label();

      fpsLabel.setTextFill(Color.ALICEBLUE);
      Canvas canvas = new Canvas(SIZE, 100);

      GraphicsContext g2d = canvas.getGraphicsContext2D();
      g2d.setStroke(Color.WHITE);

      StackPane fpsPane = new StackPane(fpsLabel, canvas);

      AnimationTimer frameRateMeter = new AnimationTimer() {
        private final long[] frameTimes = new long[SIZE];
        private final long[] frameDeltas = new long[SIZE];

        private int frameTimeIndex = 0;
        private int elementsFilled = 0;
        private long then = System.nanoTime();

        @Override
        public void handle(long now) {
long oldFrameTime = frameTimes[frameTimeIndex - elementsFilled % SIZE];
          long delta = now - then;

          frameTimes[frameTimeIndex] = now;
          frameDeltas[frameTimeIndex] = delta;
          frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length;

          if(elementsFilled < SIZE) {
            elementsFilled++;
          }

          long elapsedNanos = now - oldFrameTime;
          long elapsedNanosPerFrame = elapsedNanos / elementsFilled;
          double frameRate = 1_000_000_000.0 / elapsedNanosPerFrame;
          double min = Double.MAX_VALUE;
          double max = 0;

          for(int i = 0; i < elementsFilled; i++) {
            min = Math.min(min, frameDeltas[i]);
            max = Math.max(max, frameDeltas[i]);
          }

          GraphicsContext g2d = canvas.getGraphicsContext2D();

          g2d.clearRect(frameTimeIndex, 0, 1, 100);
g2d.strokeLine(frameTimeIndex, 100, frameTimeIndex, 100 - (delta) / 1000000.0);

fpsLabel.setText(String.format(" %.1f/%.1f ms - %.1f fps", min / 1000000.0, max / 1000000.0, frameRate));

          then = now;
        }
      };

      frameRateMeter.start();

fpsPane.setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)));

      Scene scene = new Scene(fpsPane);
      scene.setFill(Color.BLACK);
      stage = new Stage(StageStyle.TRANSPARENT);
      stage.setWidth(1500);
      stage.setHeight(1500);
      stage.setScene(scene);
      stage.show();
  }

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

Reply via email to