Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8291958

vsync happens once for each stage instead of once per pulse

XMLWordPrintable

    • x86_64
    • linux

      ADDITIONAL SYSTEM INFORMATION :
      openjdk 11.0.15
      Problem shows up on both JavaFX 11 and JavaFX 18.0.2
      Problem shows up on CentOS 7 and RHEL 7. Does not show up on Windows 10.
      Currently testing on the following graphics card with the proprietary nvidia drivers, although it shows up with other nvidia cards:
      Graphics Vendor: NVIDIA Corporation
             Renderer: Quadro GP100/PCIe/SSE2
              Version: 4.6.0 NVIDIA 515.57

      A DESCRIPTION OF THE PROBLEM :
      The pulse loop waits for one vsync per a dirty scene. If I have multiple windows open, each with their own scene, and each scene is continuously updating (always dirty), the frame rate is 60 fps / number of windows.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Create an application with multiple stages and update the contents of each stage every call to an `AnimationTimer` (make each stage constantly dirty).

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Smooth, 60 frames per second animations. Adding multiple windows with simple contents should not increase the time it takes until the next pulse.
      ACTUAL -
      Frame rate is 60 frames per second *divided by the number of windows*. Adding multiple windows with simple, but animated contents significantly decreases the frame rate.

      ---------- BEGIN SOURCE ----------
      package multiplescenes;

      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;
              }
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Use *both* `-Dprism.vsync=false` and environment variable `__GL_SYNC_TO_VBLANK=0` (see https://bugs.openjdk.org/browse/JDK-8168366).

      FREQUENCY : always


        1. capture1.pptx
          4.93 MB
          Praveen Narayanaswamy
        2. MultipleDirtyScenes.java
          3 kB
          Praveen Narayanaswamy

            kcr Kevin Rushforth
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: