JDK-8123457 : Canvas with animated Nodes on top has z-ordering issues
  • Type: Bug
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: 7u21,8
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2013-05-31
  • Updated: 2015-06-17
  • Resolved: 2013-07-31
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7u40Resolved
Related Reports
Duplicate :  
Description
When a Canvas is used as a background and Nodes are added over the top and then animated (translated) we see flickering that looks to be z-ordering issues (i.e. the Nodes appear to be disappearing behind the Canvas and then reappearing later).

See this code for a simple working example: 

public class Animate1App extends Application {

    public static void main(String[] args) throws Exception {
        launch(args);
    }

    public void start(Stage stage) throws Exception {

        final StackPane rootPane = new StackPane();

        Canvas background = new Canvas(800, 600);
        GraphicsContext g = background.getGraphicsContext2D();
        g.setFill(Color.YELLOW);
        g.fillRect(0, 0, 800, 600);
        rootPane.getChildren().add(background);

        Rectangle box1 = new Rectangle(40, 40);
        box1.setFill(Color.RED);
        createTranslation(box1, 5, -500, -500, 500, 500).play();
        rootPane.getChildren().add(box1);

        Rectangle box2 = new Rectangle(40, 40);
        box2.setFill(Color.BLUE);
        createTranslation(box2, 3, 500, -500, -500, 500).play();
        rootPane.getChildren().add(box2);

        Rectangle box3 = new Rectangle(40, 40);
        box3.setFill(Color.GREEN);
        createTranslation(box3, 3, -500, 0, 500, 0).play();
        rootPane.getChildren().add(box3);

        Scene scene = new Scene(rootPane, 1200, 800);
        stage.setScene(scene);
        stage.show();
    }

    protected TranslateTransition createTranslation(Node node, int seconds, int fromX, int fromY, int toX, int toY) {
        TranslateTransition transition = new TranslateTransition(Duration.seconds(seconds), node);
        transition.setCycleCount(Animation.INDEFINITE);
        transition.setAutoReverse(true);
        transition.setFromX(fromX);
        transition.setFromY(fromY);
        transition.setToX(toX);
        transition.setToY(toY);
        return transition;
    }
} 

Complete project is available at: https://code.google.com/p/zenjava-playtime/source/browse/trunk/javafx-performance/animate1/src/main/java/com/zenjava/jfx/performance/animate1/Animate1App.java

Comments
I no longer see this bug on mac
31-07-2013

Reopening to close as a duplicate of RT-30223.
31-07-2013

Fixed pushed to 2u/dev workspace with the following changeset: changeset: 16717:33a5e5ee548a date: Wed Jul 31 06:37:03 2013 -0700 summary: Fix RT-30223, RT-30826, RT-31044 - Canvas clears clip on Windows/D3D
31-07-2013

I can reproduce this on Windows, but not on OpenGL. Richard's previous comment indicated that he had reproduced it on Mac - is that still reproducible on Mac with the latest graphics scrum?
31-07-2013

The problem is that on D3D the setRenderTarget() method resets the scissor clip, as per this article: http://msdn.microsoft.com/en-us/library/windows/desktop/bb147354(v=vs.85).aspx In the case of a Canvas, we always validate a graphics object at the start of the render process and that causes the new graphics to invoke a setRenderTarget() on itself even if it doesn't do any rendering. The D3D context has its scissor clip reset, but the state tracking in Prism believes that the old scissor clip is still set, so it fails to revalidate the scissor clip and subsequent rendering to the destination is executed with a null clip. Regardless of the wisdom of always validating a graphics object in NGCanvas, we need to adjust our state appropriately to show that the clip was reset on D3D, as other cases could also cause a setRenderTarget() to occur and thus lose the clip.
31-07-2013

I donot think it is fixed yet. On b99 (Windows 7), I have a problem with Canvas which probably has the same root cause that has been present for months. In my case the Canvas is in a BorderPane, which is part of a StackPane. The StackPane is used to overlay things over the Canvas. When the overlay fades out, everything flickers. When no fade out is running the content is rendered correctly.
29-07-2013

I don't see this on my retina MBP using my up-to-date personal build. Is it still reproducible on the graphics scrum builds?
12-06-2013

Not sure how the NGCanvas is going wrong here. It issues a draw texture call which encompasses the entire bounds of the canvas, but it looks like the GL scissor has already been set correctly prior to this call, so I'm not sure how the NGCanvas is drawing over unexpected places
31-05-2013

If I use a Rectangle instead of a Canvas, things draw as expected.
31-05-2013

Filed RT-30829
31-05-2013

The bug where depth test is changed on the graphics is due to the NGNode setting the depth test flag but never restoring it on the graphics object. I will file a separate bug.
31-05-2013

If I had to guess, I would think that what is happening is that the clip area is wrong by the time we get to the last rectangle. I'm noticing that the last child (green) is the "winner". And that would be the case if the area being drawn included the entire bounds of the canvas. The first dirty region could have drawn the red + canvas. Then the next dirty region drew the blue + canvas (overwriting the red). The last dirty region drew the green + canvas (overdrawing the red and blue).
31-05-2013

One interesting thing (that has no effect in this case, but still odd) is in AbstractPainter#paintImpl. This block: if (g.isDepthTest()) { doPaint(g, null); } else { doPaint(g, root.getRenderRoot(NODE_PATH, dirtyRegion, i, tx, projTx)); NODE_PATH.clear(); } The first dirty region that is processed goes into the else block, exactly as one would expect for *all* dirty regions that we're going to process because depth testing is never enabled. Yet on the second dirty region we process, we enter the first condition, indicating that the graphics object is corrupted somewhere during the first dirty region and the second. Commenting out that chunk has no effect in what is drawn, but still, it is weird.
31-05-2013

Running with -Dprism.dirtyopts=false causes it to draw correctly. So we know the culprit is related to dirty regions and occlusion culling
31-05-2013

This is likely one of two things: 1) a dirty region bug. Running with -Dprism.dirtyopts=false will confirm or refute this. 2) some sort of state management bug, possibly related to blending.
31-05-2013

I modified the example so that the seconds are 50, 30, 30 instead of 5, 3, 3 for the three transitions (slowing it down helped to see the effect)
31-05-2013

I can see this on mac.
31-05-2013