RFR: 8368629: Texture.update sometimes invoked for a disposed Texture [v5]
John Hendrikx
jhendrikx at openjdk.org
Fri Nov 7 09:07:20 UTC 2025
On Tue, 4 Nov 2025 14:49:56 GMT, Lukasz Kostyra <lkostyra at openjdk.org> 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
More information about the openjfx-dev
mailing list