On Tue, 4 Nov 2025 14:49:56 GMT, Lukasz Kostyra <[email protected]> wrote:
>> This PR fixes NPE thrown when trying to update D3D texture in some rare
>> scenarios.
>>
>> On more stressful cases (like the one using Canvas attached to this issue)
>> it is possible that a D3DTexture.update() call will go through after the
>> Resource Pool already pruned the underlying Texture's resource. This in turn
>> caused an NPE, which propagated to higher levels and disrupted the rendering
>> loop, causing the Canvas to not be drawn anymore. The update() call seems
>> not to be called more than once on an already freed resource, suggesting
>> this is some sort of rare race between the pool and the drawing code.
>>
>> This change prevents the NPE from being thrown. I noticed no visual problems
>> with the test even when the update() call is rejected by the newly added
>> check. Best way to verify it is to add a log call inside added `if
>> (!resource.isValid())` blocks when running the test, it will occasionally
>> get printed but the test itself won't change its behavior like it does
>> without this change.
>
> Lukasz Kostyra has updated the pull request incrementally with one additional
> commit since the last revision:
>
> Review comments - MTLTexture: add isValid() check to update(MediaFrame)
I see this NPE (with this fix applied) when I go to more extremes with the
amount of images shown:
java.lang.NullPointerException: Cannot invoke
"com.sun.prism.Texture.getPixelFormat()" because "<parameter1>" is null
at com.sun.prism.impl.BaseGraphics.drawTexture(BaseGraphics.java:464)
at
com.sun.prism.impl.ps.BaseShaderGraphics.drawTexture(BaseShaderGraphics.java:159)
at
com.sun.javafx.sg.prism.NGImageView.renderContent(NGImageView.java:123)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGImageView.doRender(NGImageView.java:103)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.renderForClip(NGNode.java:2279)
at com.sun.javafx.sg.prism.NGNode.renderRectClip(NGNode.java:2186)
at com.sun.javafx.sg.prism.NGNode.renderClip(NGNode.java:2213)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2057)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.renderForClip(NGNode.java:2279)
at com.sun.javafx.sg.prism.NGNode.renderRectClip(NGNode.java:2186)
at com.sun.javafx.sg.prism.NGNode.renderClip(NGNode.java:2213)
at
com.sun.javafx.sg.prism.CacheFilter.renderNodeToCache(CacheFilter.java:682)
at com.sun.javafx.sg.prism.CacheFilter.render(CacheFilter.java:586)
at com.sun.javafx.sg.prism.NGNode.renderCached(NGNode.java:2343)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2054)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:266)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2063)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1955)
at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:481)
at com.sun.javafx.tk.quantum.ViewPainter.paintImpl(ViewPainter.java:329)
at
com.sun.javafx.tk.quantum.PresentingPainter.run(PresentingPainter.java:92)
at
java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at
java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358)
at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58)
at
java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at
java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at
com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:122)
at java.base/java.lang.Thread.run(Thread.java:1575)
It seems that:
Texture texture = factory.getCachedTexture(image,
Texture.WrapMode.CLAMP_TO_EDGE);
In `NGImageView` can return `null`, but this isn't checked, and just passed
along to `g.drawTexture`.
Also seeing this one:
java.lang.NullPointerException: Cannot invoke
"com.sun.prism.d3d.D3DRTTexture.contentsUseful()" because "<local6>" is null
at
com.sun.prism.d3d.D3DResourceFactory.createPresentable(D3DResourceFactory.java:381)
at
com.sun.javafx.tk.quantum.PresentingPainter.run(PresentingPainter.java:81)
at
java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at
java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358)
at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58)
at
java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at
java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at
com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:122)
at java.base/java.lang.Thread.run(Thread.java:1575)
Here in `D3DResourceFactory` the `createRTTTexture` returns `null`, but before
that is checked (lower) it already derefences the pointer...
D3DRTTexture rtt = createRTTexture(width, height,
WrapMode.CLAMP_NOT_NEEDED, pState.isMSAA());
if (PrismSettings.dirtyOptsEnabled) {
rtt.contentsUseful();
}
if (rtt != null) {
return new D3DSwapChain(context, pResource, rtt,
pState.getRenderScaleX(), pState.getRenderScaleY());
}
-------------
PR Comment: https://git.openjdk.org/jfx/pull/1951#issuecomment-3501392644