According to this bug report attempting to create a Canvas that is too large for the graphics system fails "silently" by only dumping a NullPointerException stack trace to the console. However, in my application the canvas size can be based on user input so I need to detect this. But since the NPE is caught in a background JavaFX thread, we can't rely on it to detect the issue. Any idea how I could programmatically detect that the Canvas creation failed, from within the application thread?
Results will vary based on hardware, but a large enough size should exercise the problem
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage stage) {
Button button = new Button("Create large canvas");
HBox hbox = new HBox(button);
button.setOnAction(e -> {
hbox.getChildren().add(new Canvas(500,50000));
// Did I get an NPE or can the Canvas render?
});
stage.setScene(new Scene(hbox));
stage.show();
}
}
In my case this leads to (as visible on the console):
java.lang.NullPointerException
at javafx.graphics/com.sun.javafx.sg.prism.NGCanvas$RenderBuf.validate(NGCanvas.java:213)
at javafx.graphics/com.sun.javafx.sg.prism.NGCanvas.initCanvas(NGCanvas.java:641)
at javafx.graphics/com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:604)
at javafx.graphics/com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2072)
at javafx.graphics/com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1964)
...
You need to access information about the specific graphics driver and card that are being used. You can do this by implementing a check for D3D and/or OpenGL.
I've debugged a similar issue to this for a long time. There is no way to detect and handle an exception for this in the rendering pipeline. You'll need to prepare for it and circumvent it.
JavaFX Canvas objects are rendering by applying a texture made up of the stuff you draw. Textures are limited in size based on the specific graphics hardware you are running. You can get a quick readout of a specific system's maximum texture size by running with the VM argument -Dprism.verbose=true. The line "Maximum supported texture size: x" is what you are looking for.
Here are the limits produced by the DirectX drivers (information available at https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits and https://learn.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-limits)
11+: 16384
10: 8192
9.1: 2048
9.3: 4096
OpenGL looks like it is more specific to the hardware and can return the limit by calling: GetIntegerv(GL_MAX_TEXTURE_SIZE) (https://community.khronos.org/t/what-are-the-limits-on-texture-size-for-opengl/36759)
These specify the largest size a single dimension of a texture can be.
You'd obviously need to use D3D and/or OpenGL depending on what you are running on to get these. On Windows, JavaFX will use D3D, otherwise it will use OpenGL. There are probably cases where it uses software rendering as well that you might have to account for. You could hack around this by running with -Dprism.verbose=true and reading the stderr for the max texture size line.
An alternative is to set a limit based on the screen size of the device running the program. Based on the limits I have seen, this should be a pretty safe option. For this you can either subdivide into several different Canvas objects, or make the Canvas size only as large as the visible area (could be done with a ScrollPane using the viewport)
I'd keep an upper limit on the total size as well if you go with subdivisions. If you subdivide and run into this exception again, you've likely run out of vram (you can request a different amount using -Dprism.maxvram=amountyouwant)
Hello from 2023) JavaFX still producing that bug. despite having DirectX v12 DirectX v12
and
-Dprism.maxvram=20G
Still having Prims NPE error
java.lang.NullPointerException: Cannot invoke "com.sun.prism.RTTexture.createGraphics()" because "<local9>" is null
at [email protected]/com.sun.javafx.sg.prism.NGCanvas$RenderBuf.validate(NGCanvas.java:214)
at [email protected]/com.sun.javafx.sg.prism.NGCanvas.initCanvas(NGCanvas.java:644)
at [email protected]/com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:607)
for Canvas larger than 8192px ((
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With