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

Reply via email to