如何检测画布大小是否超过图形系统的限制?

huangapple go评论90阅读模式
英文:

How can I detect if the Canvas size is too large for the graphics system?

问题

根据此错误报告,尝试创建一个对于图形系统过大的Canvas将会“静默”地失败,并只会将NullPointerException的堆栈跟踪转储到控制台。然而,在我的应用程序中,画布大小可以基于用户输入,所以我需要检测这个问题。但由于NPE被捕获在后台的JavaFX线程中,我们不能依赖它来检测问题。有什么办法可以在应用程序线程内编程式地检测Canvas创建是否失败?

结果会因硬件而异,但足够大的尺寸应该能够触发这个问题

public class Test extends Application {
	
    public static void main(String[] args) {
        launch(args);
    }
    
    public void start(Stage stage) {
        Button button = new Button("创建大画布");
        HBox hbox = new HBox(button);
        button.setOnAction(e -> { 
            hbox.getChildren().add(new Canvas(500,50000));
            // 我是否会得到NPE或者Canvas能够渲染?
        });
        stage.setScene(new Scene(hbox));
        stage.show();
    }
}

在我的情况下,这会导致如下结果(可在控制台上看到):

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)
    ...
英文:

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)
    ...

答案1

得分: 2

你需要访问有关正在使用的特定图形驱动程序和显卡的信息。您可以通过实施对D3D和/或OpenGL的检查来实现此操作。

我已经对类似的问题进行了长时间的调试。在渲染流程中,没有办法检测并处理这个问题的异常。您需要为此做好准备并规避它。

JavaFX Canvas对象通过应用由您绘制的内容组成的纹理来进行渲染。纹理的大小受限于您运行的特定图形硬件。您可以通过在VM参数-Dprism.verbose=true下运行来快速获取特定系统的最大纹理大小读数。行“Maximum supported texture size: x”就是您要寻找的内容。

以下是DirectX驱动程序产生的限制(可在https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limitshttps://learn.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-limits找到信息):

11+:16384<br>
10:8192<br>
9.1:2048<br>
9.3:4096

OpenGL似乎更具体于硬件,可以通过调用GetIntegerv(GL_MAX_TEXTURE_SIZE)来返回限制(https://community.khronos.org/t/what-are-the-limits-on-texture-size-for-opengl/36759)。

这些限制指定了纹理的单个<em>维度</em>可以达到的最大大小。

显然,您需要根据运行环境使用D3D和/或OpenGL来获取这些信息。在Windows上,JavaFX将使用D3D,否则它将使用OpenGL。可能还有一些情况会使用软件渲染,您可能需要考虑这一点。您可以通过使用-Dprism.verbose=true运行并读取stderr中的最大纹理大小行来绕过此问题。

另一种选择是根据运行程序的设备屏幕大小设置限制。根据我所见的限制,这应该是一个相当安全的选项。为此,您可以将Canvas对象细分为几个不同的对象,或者将Canvas大小仅设置为可见区域的大小(可以使用带有视口的ScrollPane来实现)。

如果选择使用细分,我会对总大小设置一个上限。如果您进行细分并再次遇到此异常,您可能已经耗尽了显存(可以使用-Dprism.maxvram=amountyouwant来请求不同数量的显存)

英文:

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<br>
10: 8192<br>
9.1: 2048<br>
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 <em>dimension</em> 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)

答案2

得分: 1

你好,来自2023年的问候)JavaFX仍然存在该错误,尽管已经使用了DirectX v12
[DirectX v12][1]

  [1]: https://i.stack.imgur.com/MBKcY.png

    -Dprism.maxvram=20G

仍然出现Prism的NPE错误

    java.lang.NullPointerException: 无法调用“com.sun.prism.RTTexture.createGraphics()”,因为“<local9>”为空
    	at javafx.graphics@20.0.1/com.sun.javafx.sg.prism.NGCanvas$RenderBuf.validate(NGCanvas.java:214)
    	at javafx.graphics@20.0.1/com.sun.javafx.sg.prism.NGCanvas.initCanvas(NGCanvas.java:644)
    	at javafx.graphics@20.0.1/com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:607)

用于Canvas大小大于8192像素((
英文:

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 &quot;com.sun.prism.RTTexture.createGraphics()&quot; because &quot;&lt;local9&gt;&quot; is null
	at javafx.graphics@20.0.1/com.sun.javafx.sg.prism.NGCanvas$RenderBuf.validate(NGCanvas.java:214)
	at javafx.graphics@20.0.1/com.sun.javafx.sg.prism.NGCanvas.initCanvas(NGCanvas.java:644)
	at javafx.graphics@20.0.1/com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:607)

for Canvas larger than 8192px ((

huangapple
  • 本文由 发表于 2020年10月5日 19:21:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/64207627.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定