0

My class extends a JavaFX Stage. In a development environment it never fails to load and process, but my users regularly report that sometimes it doesn't properly show which looks like this on Windows 10.

The class itself is a fairly standard affair, I trigger it with Platform.runLater() and the thread processes as expected. Every log output is written out and it is possible to interact with the screen as programmed, or close it, which does not trigger any error or exception.

The stage constructor is pretty basic:

  log.debug("Constructing the screen.");

  setTitle("Screen");
  setAlwaysOnTop(true);
  initStyle(StageStyle.UNDECORATED);

  try {
        FXMLLoader fxmlLoader = new FXMLLoader();
        fxmlLoader.setLocation(getClass().getResource("....fxml"));

        Pane panel = fxmlLoader.load();
        controller = fxmlLoader.getController();
        controller.postInit(currentIdle); // some elements processed

        Scene scene = new Scene(panel);
        setScene(scene);

        scene.setOnKeyPressed(controller::handleKeyEvent);
        setOnHiding((WindowEvent e) -> processor::leaveScreen);
        log.debug("Showing the screen.");
        show();
        log.debug("Screen shown.");

        requestFocus();
        log.debug("Finished constructing the screen.");

} catch (IOException e) {
    // ...doesn't go here
}

From the logs everything seems to work as intended, including the controls, keyboard shortcuts and the leaveScreen() hook, and there are no exceptions thrown, except that the user sometimes cannot see the content and controls on the screen.

What am I looking at? Is this a problem with my code, or a JavaFX bug? A problem with graphics on the user laptop? Can you think of a way to prevent or at least detect this state and force a refresh somehow?

Alternatively, under what circumstances is it possible that a JavaFX stage passes its show() method without an error, but does not actually show its contents?

Environments affected: All combinations of latest JDK 14 GA, Corretto 11, (custom jlink-created image) and JavaFX 11.0.2 or 14.0.1 - it does not make a difference. It also happens regardless of whether the application is installed with a jlinked JRE using jpackage or Advanced Installer.

ranavir
  • 11
  • 5
  • 1
    "My class extends a JavaFX Stage. ... I trigger it with `Platform.runLater()` ". None of this is really standard; I've never subclassed `Stage` and `Platform.runLater()` should only be used if you're doing something pretty complex with multithreading (and even then, there are higher-level APIs that are usually better choices). It sounds like you are setting things up in a highly non-standard way. Can you create a [mcve]? – James_D Apr 23 '20 at 13:01
  • Perhaps I didn't do a good enough job explaining the situation. As far as I could find, Platform.runLater() is a standard solution to when JavaFX enforces that the stage initiates from its thread and not mine. If I attempt to show the stage from my thread, it won't even construct due to this safeguard: `java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-5 at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:291) ... at javafx.stage.Stage.(Stage.java:254)` – ranavir Apr 23 '20 at 13:56
  • Yes, that's true. So presumably you are creating some background threads, and the stage that is intermittently appearing blank is not the initial stage that's displayed? The most probable cause is likely data synchronization between your background thread(s) and the FX Application thread. – James_D Apr 23 '20 at 13:59
  • Extending Stage is actually just a cosmetic decision. It shouldn't matter if I instantiate it as a subclass or as a separate instance: https://stackoverflow.com/questions/29866798/subclassing-javafx-stage-scene Point is, I am not able to provide a minimal reproducible example, because I never saw it happen on my machine. It is something that some users occasionally encounter and even then it's very infrequent. – ranavir Apr 23 '20 at 14:00
  • I'd argue that `Stage` is not really a class designed to be subclassed by application developers. But as you say, that's probably beside the point. My best guess (and with the information you've provided, all anyone can really do is guess) is that you have synchronization issues between your background threads and the FX Application thread. – James_D Apr 23 '20 at 14:02
  • Yes, I am dealing with multiple threads in the application, but there is no data synchronization going on that should block the JavaFX thread. On the contrary, the JavaFX stage seems communicative and responsive. It just does not render any graphics... on some machines... sometimes. – ranavir Apr 23 '20 at 14:02
  • Let me perhaps rephrase the question: Under what circumstances is it possible that a JavaFX stage passes its show() method without an error, but does not actually show any contents? – ranavir Apr 23 '20 at 14:05
  • If the content depends on data modified in a background thread, there's no a-priori guarantee the FX Application thread will see changes to those data. So if you haven't taken appropriate steps to ensure data liveness across multiple threads, it's certainly possible that could cause the issues you're seeing. And of course, if you modify any UI elements in the background threads, there is no guarantee those modifications will be seen in the FX Application Thread. (Not all JavaFX classes provide the `IllegalStateException` guard.) – James_D Apr 23 '20 at 14:08
  • 1
    All I can really offer here is "My best guess is that you have threading issues.". Could it also be a JavaFX bug, or native graphics issue? Sure, of course those are possible. But if forced to bet where the issue is, without any further information, I'd bet on some kind of incorrect use of threads. – James_D Apr 23 '20 at 14:14

2 Answers2

1

Warm thanks, guys, for your honorable attempts to help. It is now a bit cold at nine months later, but I believe that it deserves a close in case anyone stumbles upon the problem later. My conclusions:

  1. The problem is not caused by my application, and
  2. that I have found a workaround.

We gradually managed to confidently isolate the problem to hardware, i.e. we noticed it would happen more frequently if plugging/unplugging display peripherals and docking stations was involved. It was also sometimes triggered by sleeping / running out of battery oddly.

This led us to believe that the problem was somewhere between Prism's hardware acceleration and the graphic cards. In the end we managed to avoid this kind of rendering freeze by changing the application's GraphicsPipeline from com.sun.prism.d3d.D3DPipeline to com.sun.prism.sw.SWPipeline. For us this is not a problem, because the application doesn't render complex 3D graphics, and after switching to software rendering the problem disappeared.

This can be achieved by setting -Dprism.order=sw to the JVM parameters at startup. You can confirm the pipeline in use by logging your com.sun.prism.GraphicsPipeline.getPipeline().getClass().getName().

ranavir
  • 11
  • 5
  • Just chiming in to say that I’m seeing the occasional white stage in my app as well, and it has a lot of background threads running. Forcing the SW pipeline seemed to make it happen less often but it hasn’t disappeared completely. I have no clue what’s going on, and I can’t reproduce the behavior. – Alex Suzuki May 02 '22 at 19:42
0

Hard to help without more info, but 2 things I've had to use in the past for windows issues were

Thread.currentThread().setContextClassLoader(App.class.getClassLoader());

placed at the begin of the start(Stage stage) method where App is obviously replaced with the name of your class extending Application

The other 'fix' I've used is

System.setProperty("glass.accessible.force", "false");

as a workaround for issues with clicking on out of focus Nodes

b3tuning
  • 337
  • 2
  • 8