使用大画布时 JavaFX 中的 NullPointers

Dra*_*orn 5 java graphics textures javafx nullpointerexception

我在我的应用程序中使用 Canvas 对象,确切地说,是 StackPane 中两个大小相同的对象。当这些对象变大时,JavaFX 在尝试绘制/渲染时开始崩溃:

java.lang.NullPointerException: Cannot invoke "com.sun.prism.RTTexture.createGraphics()" because "<local9>" is null
at javafx.graphics/com.sun.javafx.sg.prism.NGCanvas$RenderBuf.validate(NGCanvas.java:214)
at javafx.graphics/com.sun.javafx.sg.prism.NGCanvas.initCanvas(NGCanvas.java:644)
at javafx.graphics/com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:607)
at javafx.graphics/com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2072)
(...)
Run Code Online (Sandbox Code Playgroud)

和类似的(都与javafx.graphics/com.sun.javafx.sg.prism.NGCanvas相关)。现在,过去已经有一些关于类似内容的讨论(例如这里),一半的互联网基本上只是通过关闭硬件加速来回答这个问题,这就是标志所做-Dprism.order=sw的。

我认为这不是答案。

我的图形硬件支持 16384x16384 纹理(至少在 OpenGL 驱动程序中)和 8GB VRAM。我什至写了一个小测试程序来验证是否是“真正的”支持,并且有效,因为我过去曾被一些糟糕的集成笔记本电脑的图形设备烧伤,该设备声称支持此兼容 OpenGL-v3,但实际上是这样的。当有人尝试分配 8k+ 纹理时崩溃。我的 PC 中只有一张显卡,因此 JavaFX 应用程序不可能使用错误的设备。

当我将画布对象的大小从 4156x4156 调整为 8252x8252 时,我的应用程序崩溃了。即使我们接受它将使用下一个更高的二次方尺寸来渲染这些东西,我也声称我的硬件应该能够处理这个设置。即使多个 16384x16384 纹理也可以轻松地与其他内容一起装入内存。

我使用的是 Windows 10,使用版本 15.0.1 的 OpenJDK 和 JavaFX,尽管 Canvas 很旧,这不应该是问题。

有谁知道这里到底发生了什么?

编辑:正如评论中所建议的,我做了一些更精确的测试。使用两个画布对象的硬编码尺寸进行进一步的手动测试揭示了以下内容:

将两者设置为 4096x4096 和 4097x4097 都有效。请记住,后者应在内部使用 8192x8192 纹理,因为这就是显卡的工作原理。内部是指在显卡上,而不是其他任何地方。

然而,这里变得有趣了:

将两个画布对象之一设置为 8192x8192(并将另一个保留为 2x2)也可以正常工作。将BOTH设置为 8192x8192 则不会,从而导致上述 NULL 问题。

这表明这毕竟是一个内存问题,可能与分配有关。然而,正如前面提到的,图形设备应该能够处理这个问题,而且考虑到将两者都设置为 4097x4097 是可行的,而且似乎也能够处理这个问题。我怀疑它是在 Java 方面,并且可能与堆栈窗格有关。 有人知道 StackPane 是否有某种内部像素阵列或其他机制吗?可能检测到变化吗?显然,这将是非常不幸的,因为它会对硬件加速纹理的使用产生反作用。

此外,我还使用-Dprism.order=sw. 从 4156x4156 到 8252x8252 的转换在该模式下确实有效。当进一步推进时(仅用于实验目的),最终确实会遇到完全相同的问题,再次表明存在内存问题。

我可以选择仅使用一个画布,并在该画布内更频繁地重新绘制,以减少内存占用(猜测性能权衡在这里是可以的),但我首先想了解这里到底出了什么问题......

小智 3

由于您已经确认您创建的纹理没有超过硬件的最大大小,因此听起来您可能会耗尽 VRAM。据我所知,默认最大值为 512MB。您可以通过在虚拟机参数中传递以下内容来请求不同的最大值。

-Dprism.maxvram=xx

示例:对于 2 GB,您可以使用 -Dprism.maxvram=2G。