使用大画布时 JavaFX 中的 NullPointers

Posted

技术标签:

【中文标题】使用大画布时 JavaFX 中的 NullPointers【英文标题】:NullPointers in JavaFX when using a large canvas 【发布时间】:2021-04-10 09:29:34 【问题描述】:

我在我的应用程序中使用 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)
(...)

和类似的(都与javafx.graphics/com.sun.javafx.sg.prism.NGCanvas有关)。现在,过去有一些关于此类事情的讨论(例如here),一半的互联网基本上只是用关闭硬件加速来回答这个问题,这就是像-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 的转换确实在该模式下工作。当进一步推进时(仅用于实验目的),最终确实会在完全相同的问题中运行,这再次表明存在内存问题。

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

【问题讨论】:

你试过8192x8192吗? 您使用的是哪个版本的 JavaFX 和哪个操作系统? 更新了主帖,因为第一个答案很长... 大画布尺寸存在未解决的问题,f.i. bugs.openjdk.java.net/browse/JDK-8090178 - 有些人可能有破解(或者不是,对我来说是深水:) 【参考方案1】:

由于您已确认创建的纹理没有超出硬件的最大尺寸,因此您可能会用完 VRAM。据我所知,默认最大值为 512MB。您可以通过在 VM args 中传递以下内容来请求不同的最大值。

-Dprism.maxvram=xx

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

【讨论】:

有一个 VRAM 限制,天哪。我不知道JavaFX对此也有限制,类似于堆栈...谢谢:-) 这确实有效......现在下一个自然问题是,我们是否有办法检测或捕获抛出的异常,以便我们可以向用户提供适当的错误报告并询问他们是否他们想尝试使用更多的 vram(当然在下次启动时)。 (而不是仅仅将默认值提高得非常高,并可能失去与某些硬件的兼容性。)

以上是关于使用大画布时 JavaFX 中的 NullPointers的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX - 游戏循环画布高 CPU 使用率

如何围绕另一个圆圈移动一个点使用Javafx画布?

使用 javafx 创建 Truetype 字体文件

如何在 JavaFX 中使用 HBox 将某些元素放置在画布上的特定位置? [关闭]

JavaFX:仿win画图

JavaFX区分拖动和单击