changeset 4751:61b8d7420fc9 8.0-b104

RT-32440: Fix for RT-32250 causes multiple regressions in rendering Backed out changeset 17e50e1658a0
author kcr
date Tue, 20 Aug 2013 11:37:49 -0700
parents c218ee2b9ac2
children d65640428833 6c4ef975d190 80b28fd46454
files modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGGroup.java modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGNode.java modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGRegion.java modules/graphics/src/main/java/com/sun/javafx/sg/prism/NodePath.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/ViewPainter.java modules/graphics/src/test/java/com/sun/javafx/sg/prism/OcclusionCullingTest.java
diffstat 6 files changed, 113 insertions(+), 182 deletions(-) [+]
line wrap: on
line diff
--- a/modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGGroup.java	Tue Aug 20 09:38:00 2013 -0700
+++ b/modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGGroup.java	Tue Aug 20 11:37:49 2013 -0700
@@ -56,13 +56,13 @@
     private List<NGNode> children = new ArrayList<>(1);
     private List<NGNode> unmod = Collections.unmodifiableList(children);
     private List<NGNode> removed;
-
+    
     /**
      * This mask has all bits that mark that a region intersects this group.
      * Which means it looks like this: 00010101010101010101010101010101 (first bit for sign)
      */
     private static final int REGION_INTERSECTS_MASK = 0x15555555;
-
+    
     /***************************************************************************
      *                                                                         *
      * Implementation of the PGGroup interface                                 *
@@ -131,10 +131,10 @@
         if (dirtyChildrenAccumulated > DIRTY_CHILDREN_ACCUMULATED_THRESHOLD) {
             return;
         }
-
+        
         removed.add(n);
         dirtyChildrenAccumulated++;
-
+        
         if (dirtyChildrenAccumulated > DIRTY_CHILDREN_ACCUMULATED_THRESHOLD) {
             removed.clear(); //no need to store anything in this case
         }
@@ -193,14 +193,14 @@
     protected void renderContent(Graphics g) {
         renderChildren(g, (NodePath<NGNode>)g.getRenderRoot());
     }
-
+    
     private void renderChildren(Graphics g, NodePath<NGNode> renderRoot) {
         if (children == null) {
             return;
         }
-
+        
         int startPos = 0;
-        if (renderRoot != null && !renderRoot.isEmpty()) {
+        if (renderRoot != null) {
             startPos = children.indexOf(renderRoot.getCurrentNode());
 
             for (int i = 0; i < startPos; ++i) {
@@ -271,7 +271,7 @@
             bot.unref();
         }
     }
-
+    
     @Override
     protected boolean hasOverlappingContents() {
         if (blendMode != Mode.SRC_OVER) {
@@ -288,7 +288,7 @@
     public boolean isEmpty() {
         return children == null || children.isEmpty();
     }
-
+    
     @Override
     protected boolean hasVisuals() {
         return false;
@@ -309,39 +309,39 @@
      *                                                                         *
      **************************************************************************/
     @Override
-    protected RenderRootResult computeRenderRoot(NodePath<NGNode> path, RectBounds dirtyRegion, int cullingIndex, BaseTransform tx,
+    protected NodePath<NGNode> computeRenderRoot(NodePath<NGNode> path, RectBounds dirtyRegion, int cullingIndex, BaseTransform tx,
                                        GeneralTransform3D pvTx) {
 
         // If the NGGroup is completely outside the culling area, then we don't have to traverse down
         // to the children yo.
         if (cullingIndex != -1) {
             final int bits = cullingBits >> (cullingIndex*2);
-            if ((bits & DIRTY_REGION_CONTAINS_OR_INTERSECTS_NODE_BOUNDS) == 0) {
-                return RenderRootResult.NONE;
+            if ((bits & CULLING_REGION_CONTAINS_OR_INTERSECTS_CLIP) == 0) {
+                return null;
             }
-            if ((bits & DIRTY_REGION_CONTAINS_NODE_BOUNDS) != 0) {
-                cullingIndex = -1; // Do not check culling in children,
+            if ((bits & CULLING_REGION_CONTAINS_CLIP) != 0) {
+                cullingIndex = -1; // Do not check culling in children, 
                                    // as culling bits are not set for fully interior groups
             }
         }
-
+        
         if (!isVisible()) {
-            return RenderRootResult.NONE;
+            return null;
         }
 
         if (getOpacity() != 1.0 || (getEffect() != null && getEffect().reducesOpaquePixels()) || needsBlending()) {
-            return RenderRootResult.NONE;
+            return null;
         }
 
         if (getClipNode() != null) {
             final NGNode clip = (NGNode)getClipNode();
             RectBounds clipBounds = clip.computeOpaqueRegion(TEMP_RECT_BOUNDS);
             if (clipBounds == null) {
-                return RenderRootResult.NONE;
+                return null;
             }
             TEMP_TRANSFORM.deriveWithNewTransform(tx).deriveWithConcatenation(getTransform()).deriveWithConcatenation(clip.getTransform());
             if (!checkBoundsInQuad(clipBounds, dirtyRegion, TEMP_TRANSFORM, pvTx)) {
-                return RenderRootResult.NONE;
+                return null;
             }
         }
 
@@ -363,22 +363,18 @@
         double mzz = tx.getMzz();
         double mzt = tx.getMzt();
         final BaseTransform chTx = tx.deriveWithConcatenation(getTransform());
-        RenderRootResult result = RenderRootResult.NONE;
-        int resultIdx;
-        for (resultIdx=children.size()-1; resultIdx>=0; resultIdx--) {
-            final NGNode child = children.get(resultIdx);
-            result = child.computeRenderRoot(path, dirtyRegion, cullingIndex, chTx, pvTx);
-            if (result != RenderRootResult.NONE) break;
+        NodePath childPath = null;
+        for (int i=children.size()-1; i>=0; i--) {
+            final NGNode child = (NGNode) children.get(i);
+            childPath = child.computeRenderRoot(path, dirtyRegion, cullingIndex, chTx, pvTx);
+            if (childPath != null) break;
         }
         // restore previous transform state
         tx.restoreTransform(mxx, mxy, mxz, mxt, myx, myy, myz, myt, mzx, mzy, mzz, mzt);
-        if (result != RenderRootResult.NONE) {
-            if (result == RenderRootResult.HAS_RENDER_ROOT || dirty != DirtyFlag.CLEAN || childDirty) {
-                path.add(this);
-                result = RenderRootResult.HAS_RENDER_ROOT;
-            }
+        if (childPath != null) {
+            childPath.add(this);
         }
-        return result;
+        return childPath;
     }
 
     @Override
@@ -390,7 +386,7 @@
 
         //set culling bits for this group first.
         super.markCullRegions(drc, cullingRegionsBitsOfParent, tx, pvTx);
-
+        
         //cullingRegionsBits == 0 group is outside all dirty regions
         // we can cull all children otherwise check children.
         // If none of the regions intersect this group, skip pre-culling
--- a/modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGNode.java	Tue Aug 20 09:38:00 2013 -0700
+++ b/modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGNode.java	Tue Aug 20 11:37:49 2013 -0700
@@ -242,10 +242,10 @@
      */
     protected int cullingBits = 0x0;
 
-    static final int DIRTY_REGION_INTERSECTS_NODE_BOUNDS = 0x1;
-    static final int DIRTY_REGION_CONTAINS_NODE_BOUNDS = 0x2;
-    static final int DIRTY_REGION_CONTAINS_OR_INTERSECTS_NODE_BOUNDS =
-            DIRTY_REGION_INTERSECTS_NODE_BOUNDS | DIRTY_REGION_CONTAINS_NODE_BOUNDS;
+    static final int CULLING_REGION_INTERSECTS_CLIP = 0x1;
+    static final int CULLING_REGION_CONTAINS_CLIP = 0x2;
+    static final int CULLING_REGION_CONTAINS_OR_INTERSECTS_CLIP =
+            CULLING_REGION_INTERSECTS_CLIP | CULLING_REGION_CONTAINS_CLIP;
 
     private RectBounds opaqueRegion = null;
     private boolean opaqueRegionInvalid = true;
@@ -1154,6 +1154,11 @@
      * scene coordinates. The specified tx can be used to convert local bounds
      * to scene bounds (it includes everything up to but not including my own
      * transform).
+     *
+     * @param dirtyRegionTemp A temporary RectBounds that this method can use for scratch.
+     *                        In the case that no perspective transform occurs, it is best if
+     *                        the returned BaseBounds is this instance.
+     * @param tx Any transform that needs to be applied
      * @param pvTx must not be null, it's the perspective transform of the current
      *        perspective camera or identity transform if parallel camera is used.
      */
@@ -1240,9 +1245,9 @@
         int b = 0;
         if (region != null && !region.isEmpty()) {
             if (region.intersects(bounds)) {
-                b = DIRTY_REGION_INTERSECTS_NODE_BOUNDS;
+                b = CULLING_REGION_INTERSECTS_CLIP;
                 if (region.contains(bounds)) {
-                    b = DIRTY_REGION_CONTAINS_NODE_BOUNDS;
+                    b = CULLING_REGION_CONTAINS_CLIP;
                 }
                 cullingBits = cullingBits |  (b << (2 * regionIndex));
             }
@@ -1332,27 +1337,6 @@
      * Identifying render roots                                                *
      *                                                                         *
      **************************************************************************/
-    protected static enum RenderRootResult {
-        /**
-         * The computation to determine whether a branch of the scene graph has
-         * a render root has concluded that indeed, there is one.
-         */
-        HAS_RENDER_ROOT,
-        /**
-         * A RenderRootResult of this value means that although we discovered a
-         * render root, we also determined that there were no dirty nodes that
-         * followed the render root. Therefore, we don't actually have to render
-         * anything (the only dirty nodes are completely occluded by clean
-         * nodes).
-         */
-        EXISTS_BUT_NOT_DIRTY,
-        /**
-         * Represents the case where we did not find an opaque node that covered
-         * the dirty region, and thus we must start rendering from the actual
-         * root of the hierarchy.
-         */
-        NONE
-    }
 
     /**
      * Called <strong>after</strong> preCullingBits in order to get the node
@@ -1360,23 +1344,19 @@
      * This should only be called on the root node.
      *
      * @param path node path to store the node path
-     * @return NodePath from path parameter with the first node being a child of this node.
-     * If not render root was found, the returned NodePath will be empty.
-     * If no rendering is needed (everything dirty is occluded), null is returned.
+     * @return new node path (preferably == path) with first node being a child of this node. Null if renderRoot == this.
      */
     public final NodePath<NGNode> getRenderRoot(NodePath<NGNode> path, RectBounds dirtyRegion, int cullingIndex, BaseTransform tx,
-             GeneralTransform3D pvTx) {
+                                      GeneralTransform3D pvTx) {
         // The occlusion culling support depends on dirty opts being enabled
         if (PrismSettings.occlusionCullingEnabled) {
-            RenderRootResult result = computeRenderRoot(path, dirtyRegion, cullingIndex, tx, pvTx);
-            if (result == RenderRootResult.EXISTS_BUT_NOT_DIRTY) {
-                return null;
-            }
-            if (result == RenderRootResult.HAS_RENDER_ROOT) {
-                path.removeRoot();
+            NodePath<NGNode> rootPath = computeRenderRoot(path, dirtyRegion, cullingIndex, tx, pvTx);
+            if (rootPath != null) {
+                rootPath.removeRoot();
+                return rootPath.size() != 0 ? rootPath : null;
             }
         }
-        return path;
+        return null;
     }
 
     /**
@@ -1389,7 +1369,7 @@
      * @param pvTx current perspective transform
      * @return New rendering root or null if no such node exists in this subtree
      */
-    protected RenderRootResult computeRenderRoot(NodePath<NGNode> path, RectBounds dirtyRegion, int cullingIndex, BaseTransform tx,
+    protected NodePath<NGNode> computeRenderRoot(NodePath<NGNode> path, RectBounds dirtyRegion, int cullingIndex, BaseTransform tx,
                                        GeneralTransform3D pvTx) {
         return computeNodeRenderRoot(path, dirtyRegion, cullingIndex, tx, pvTx);
     }
@@ -1432,25 +1412,27 @@
      * @param pvTx current perspective transform
      * @return this node if this node's opaque region covers the specified dirty region. Null otherwise.
      */
-    protected final RenderRootResult computeNodeRenderRoot(NodePath<NGNode> path, RectBounds dirtyRegion, int cullingIndex, BaseTransform tx,
+    protected final NodePath<NGNode> computeNodeRenderRoot(NodePath<NGNode> path, RectBounds dirtyRegion, int cullingIndex, BaseTransform tx,
                                        GeneralTransform3D pvTx) {
 
         // Nodes outside of the dirty region can be excluded immediately.
         // This can be used only if the culling information is provided.
         if (cullingIndex != -1) {
             final int bits = cullingBits >> (cullingIndex * 2);
-            if ((bits & DIRTY_REGION_CONTAINS_OR_INTERSECTS_NODE_BOUNDS) == 0x00) {
-                return RenderRootResult.NONE;
+            if ((bits & CULLING_REGION_CONTAINS_OR_INTERSECTS_CLIP) == 0x00) {
+                return null;
             }
         }
 
         if (!isVisible()) {
-            return RenderRootResult.NONE;
+            return null;
         }
 
-
+        // Walk down the tree to the very end. Then working our way back up to
+        // the top of the tree, look for the first node which which marked as
+        // dirty opts 01, and then return it.
         final RectBounds opaqueRegion = getOpaqueRegion();
-        if (opaqueRegion == null) return RenderRootResult.NONE;
+        if (opaqueRegion == null) return null;
 
         final BaseTransform localToParentTx = getTransform();
 
@@ -1458,16 +1440,11 @@
 
         // Now check if the dirty region is fully contained in our opaque region.
         if (checkBoundsInQuad(opaqueRegion, dirtyRegion, localToSceneTx, pvTx)) {
-            // We have the potential render root, now check if it needs to be rendered
-            if (dirty != DirtyFlag.CLEAN) {
                 path.add(this);
-                return RenderRootResult.HAS_RENDER_ROOT;
-            } else {
-                return RenderRootResult.EXISTS_BUT_NOT_DIRTY;
-            }
+                return path;
         }
 
-        return RenderRootResult.NONE;
+        return null;
     }
 
     protected static boolean checkBoundsInQuad(RectBounds untransformedQuad,
@@ -1573,11 +1550,11 @@
             if (g.hasPreCullingBits()) {
                 //preculling bits available
                 final int bits = cullingBits >> (g.getClipRectIndex() * 2);
-                if ((bits & DIRTY_REGION_CONTAINS_OR_INTERSECTS_NODE_BOUNDS) == 0) {
+                if ((bits & CULLING_REGION_CONTAINS_OR_INTERSECTS_CLIP) == 0) {
                     // If no culling bits are set for this region, this group
                     // does not intersect (nor is covered by) the region
                     return;
-                } else if ((bits & DIRTY_REGION_CONTAINS_NODE_BOUNDS) != 0) {
+                } else if ((bits & CULLING_REGION_CONTAINS_CLIP) != 0) {
                     // When this group is fully covered by the region,
                     // turn off the culling checks in the subtree, as everything
                     // gets rendered
--- a/modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGRegion.java	Tue Aug 20 09:38:00 2013 -0700
+++ b/modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGRegion.java	Tue Aug 20 11:37:49 2013 -0700
@@ -436,17 +436,17 @@
         return null;
     }
 
-    @Override protected RenderRootResult computeRenderRoot(NodePath<NGNode> path, RectBounds dirtyRegion,
+    @Override protected NodePath<NGNode> computeRenderRoot(NodePath<NGNode> path, RectBounds dirtyRegion,
                                                            int cullingIndex, BaseTransform tx,
                                                            GeneralTransform3D pvTx) {
 
-        RenderRootResult result = super.computeRenderRoot(path, dirtyRegion, cullingIndex, tx, pvTx);
-        if (result == RenderRootResult.HAS_RENDER_ROOT) {
-            path.add(this);
+        NodePath<NGNode> childPath = super.computeRenderRoot(path, dirtyRegion, cullingIndex, tx, pvTx);
+        if (childPath != null) {
+            childPath.add(this);
         } else {
-            result = computeNodeRenderRoot(path, dirtyRegion, cullingIndex, tx, pvTx);
+            childPath = computeNodeRenderRoot(path, dirtyRegion, cullingIndex, tx, pvTx);
         }
-        return result;
+        return childPath;
     }
 
     @Override protected boolean hasVisuals() {
--- a/modules/graphics/src/main/java/com/sun/javafx/sg/prism/NodePath.java	Tue Aug 20 09:38:00 2013 -0700
+++ b/modules/graphics/src/main/java/com/sun/javafx/sg/prism/NodePath.java	Tue Aug 20 11:37:49 2013 -0700
@@ -30,17 +30,17 @@
 
 /**
  * Simple a reusable storage for root-to-node path.
- *
+ * 
  */
 public class NodePath<N extends NGNode> {
     private List<N> path = new ArrayList<>();
     private int position;
-
+    
     public NodePath() {
     }
-
+    
     // ITERATION methods
-
+    
     public N getCurrentNode() {
         return path.get(position);
     }
@@ -55,27 +55,27 @@
         }
         position--;
     }
-
+    
     public void reset() {
         position = path.size() - 1;
     }
-
+    
     // MODIFICATION methods
-
+    
     public void clear() {
         position = -1;
         path.clear();
     }
-
+    
     public void add(N n) {
         path.add(n);
         position = path.size() - 1;
     }
-
+    
     public int size() {
         return path.size();
     }
-
+    
     /*
      * Remove root and set to beginning.
      */
@@ -84,8 +84,4 @@
         position = path.size() - 1;
     }
 
-    boolean isEmpty() {
-        return size() == 0;
-    }
-
 }
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/ViewPainter.java	Tue Aug 20 09:38:00 2013 -0700
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/ViewPainter.java	Tue Aug 20 11:37:49 2013 -0700
@@ -54,7 +54,6 @@
 
 abstract class ViewPainter implements Runnable {
     private static final NodePath<NGNode> NODE_PATH = new NodePath<>();
-    private static final NodePath<NGNode> EMPTY_NODE_PATH = new NodePath<>();
 
     /*
      * This could be a per-scene lock but there is no guarantee that the
@@ -69,7 +68,7 @@
     protected int penHeight = -1;
     protected int viewWidth;
     protected int viewHeight;
-
+    
     protected final SceneState sceneState;
 
     protected Presentable presentable;
@@ -108,7 +107,7 @@
             dirtyRegionContainer = dirtyRegionPool.checkOut();
         }
     }
-
+        
     protected void setRoot(NGNode node) {
         root = node;
     }
@@ -189,7 +188,7 @@
 
                     // Disable occlusion culling if depth buffer is enabled for the scene.
                     if (sceneState.getScene().getDepthBuffer()) {
-                        doPaint(g, EMPTY_NODE_PATH);
+                        doPaint(g, null);
                     } else {
                         doPaint(g, root.getRenderRoot(NODE_PATH, dirtyRegion, i, tx, projTx));
                         NODE_PATH.clear();
@@ -199,7 +198,7 @@
         } else {
             g.setHasPreCullingBits(false);
             g.setClipRect(null);
-            this.doPaint(g, EMPTY_NODE_PATH);
+            this.doPaint(g, null);
         }
 
         if (PrismSettings.showDirtyRegions) {
@@ -266,8 +265,8 @@
 
     protected boolean validateStageGraphics() {
         if (!sceneState.isValid()) {
-            // indicates something happened between the scheduling of the
-            // job and the running of this job.
+            // indicates something happened between the scheduling of the 
+            // job and the running of this job. 
             return false;
         }
 
@@ -276,17 +275,11 @@
 
         return sceneState.isWindowVisible() && !sceneState.isWindowMinimized();
     }
-
+    
     private void doPaint(Graphics g, NodePath<NGNode> renderRootPath) {
         if (PrismSettings.showDirtyRegions) {
             g.setClipRect(null);
         }
-        if (renderRootPath == null) {
-            // null render path indicates that no rendering is needed.
-            // There may be occluded dirty Nodes however, so we need to clear them
-            root.clearDirtyTree();
-            return;
-        }
         long start = PULSE_LOGGING_ENABLED ? System.currentTimeMillis() : 0;
         try {
             GlassScene scene = sceneState.getScene();
@@ -314,5 +307,5 @@
             }
         }
     }
-
+    
 }
--- a/modules/graphics/src/test/java/com/sun/javafx/sg/prism/OcclusionCullingTest.java	Tue Aug 20 09:38:00 2013 -0700
+++ b/modules/graphics/src/test/java/com/sun/javafx/sg/prism/OcclusionCullingTest.java	Tue Aug 20 11:37:49 2013 -0700
@@ -34,110 +34,88 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 
 /**
  *
  */
 public class OcclusionCullingTest {
-
-
+    
+    
     @Test
     public void testRectangleOcclusion() {
-        final TestNGRectangle root = createRectangle(0, 0, 50, 50);
         NodeTestUtils.TestNGGroup group = createGroup(
-                createRectangle(0, 0, 100, 100), root);
-        NodePath<NGNode> rootPath = new NodePath<>();
+                createRectangle(0, 0, 100, 100), createRectangle(0, 0, 50, 50));
+        NodePath<NGNode> rootPath = new NodePath<NGNode>();
         rootPath = group.getRenderRoot(rootPath, new RectBounds(20, 20, 30, 30), -1, BaseTransform.IDENTITY_TRANSFORM, null);
         TestGraphics g = new TestGraphics();
         g.setRenderRoot(rootPath);
         group.render(g);
-        assertRoot(rootPath, root);
+//        assertSame(group.getChildren().get(1), rootPath.);
+        rootPath.reset();
         checkRootRendering(group, rootPath);
     }
-
+    
     @Test
     public void testGroupOcclusion() {
-        final TestNGRectangle root = createRectangle(0, 0, 50, 50);
         NodeTestUtils.TestNGGroup group = createGroup(createGroup(
-                createRectangle(0, 0, 100, 100)), createGroup(root));
-        NodePath<NGNode> rootPath = new NodePath<>();
+                createRectangle(0, 0, 100, 100)), createGroup(createRectangle(0, 0, 50, 50)));
+        NodePath<NGNode> rootPath = new NodePath<NGNode>();
         rootPath = group.getRenderRoot(rootPath, new RectBounds(20, 20, 30, 30), -1, BaseTransform.IDENTITY_TRANSFORM, null);
         TestGraphics g = new TestGraphics();
         g.setRenderRoot(rootPath);
         group.render(g);
-        assertRoot(rootPath, root);
+//        assertSame(((NGGroup)group.getChildren().get(1)).getChildren().get(0), root);
+        rootPath.reset();
         checkRootRendering(group, rootPath);
     }
-
+    
     @Test
     public void testRegionOcclusion() {
-        final TestNGRegion root = createRegion(50, 50);
         NodeTestUtils.TestNGGroup group = createGroup(
-                createRegion(100, 100), root);
-        NodePath<NGNode> rootPath = new NodePath<>();
+                createRegion(100, 100), createRegion(50, 50));
+        NodePath<NGNode> rootPath = new NodePath<NGNode>();
         rootPath = group.getRenderRoot(rootPath, new RectBounds(20, 20, 30, 30), -1, BaseTransform.IDENTITY_TRANSFORM, null);
         TestGraphics g = new TestGraphics();
         g.setRenderRoot(rootPath);
         group.render(g);
-        assertRoot(rootPath, root);
+//        assertSame(group.getChildren().get(1), root);
+        rootPath.reset();
         checkRootRendering(group, rootPath);
     }
-
+    
     @Test
     public void testPresetRegionOcclusion() {
-        final TestNGRegion root = createRegion(100, 100);
-        final TestNGRegion other = createRegion(50, 50);
         NodeTestUtils.TestNGGroup group = createGroup(
-                root, other);
-        other.setOpaqueInsets(30, 30, 0, 0);
-        NodePath<NGNode> rootPath = new NodePath<>();
+                createRegion(100, 100), createRegion(50, 50));
+        ((NGRegion)group.getChildren().get(1)).setOpaqueInsets(30, 30, 0, 0);
+        NodePath<NGNode> rootPath = new NodePath<NGNode>();
         rootPath = group.getRenderRoot(rootPath, new RectBounds(20, 20, 30, 30), -1, BaseTransform.IDENTITY_TRANSFORM, null);
         TestGraphics g = new TestGraphics();
         g.setRenderRoot(rootPath);
         group.render(g);
-        assertRoot(rootPath, root);
+//        assertSame(group.getChildren().get(0), root);
+        rootPath.reset();
         checkRootRendering(group, rootPath);
     }
 
     @Test
     public void test2SameRectanglesOclusion() {
-        final TestNGRectangle root = createRectangle(10, 10, 100, 100);
         NodeTestUtils.TestNGGroup group = createGroup(
                 createGroup(createRectangle(10, 10, 100, 100), createRectangle(20, 20, 20, 20)),
-                createGroup(root));
-        NodePath<NGNode> rootPath = new NodePath<>();
+                createGroup(createRectangle(10, 10, 100, 100)));
+        NodePath<NGNode> rootPath = new NodePath<NGNode>();
         rootPath = group.getRenderRoot(rootPath, new RectBounds(10, 10, 100, 100), -1, BaseTransform.IDENTITY_TRANSFORM, null);
         TestGraphics g = new TestGraphics();
         g.setRenderRoot(rootPath);
         group.render(g);
-        assertRoot(rootPath, root);
+        rootPath.reset();
+        rootPath.next();
+        assertSame(((NGGroup)group.getChildren().get(1)).getChildren().get(0), rootPath.getCurrentNode());
+        rootPath.reset();
         checkRootRendering(group, rootPath);
     }
 
-    @Test
-    public void test2SameRectanglesOclusionWithRootNotDirty() {
-        final TestNGRectangle root = createRectangle(10, 10, 100, 100);
-        final TestNGGroup rootParent = createGroup(root);
-        NodeTestUtils.TestNGGroup group = createGroup(
-                createGroup(createRectangle(10, 10, 100, 100), createRectangle(20, 20, 20, 20)), rootParent);
-
-        group.dirty =  NGNode.DirtyFlag.CLEAN; // need to clean default dirty flags
-        rootParent.dirty = NGNode.DirtyFlag.CLEAN;
-        rootParent.childDirty = false;
-        root.dirty = NGNode.DirtyFlag.CLEAN;
-        NodePath<NGNode> rootPath = new NodePath<>();
-        rootPath = group.getRenderRoot(rootPath, new RectBounds(10, 10, 100, 100), -1, BaseTransform.IDENTITY_TRANSFORM, null);
-        assertTrue(rootPath.isEmpty());
-
-        final TestNGRectangle dirtySibling = createRectangle(0,0,10,10);
-        rootParent.add(-1,dirtySibling);
-        rootPath = new NodePath<>();
-        rootPath = group.getRenderRoot(rootPath, new RectBounds(10, 10, 100, 100), -1, BaseTransform.IDENTITY_TRANSFORM, null);
-        assertRoot(rootPath, rootParent);
-    }
-
     private void checkRootRendering(TestNGNode group, NodePath<NGNode> root) {
         assertTrue(group.rendered());
         if (group instanceof TestNGGroup) {
@@ -165,15 +143,6 @@
              }
         }
     }
-
-    private void assertRoot(NodePath<NGNode> rootPath, final NGNode root) {
-        rootPath.reset();
-        while(rootPath.hasNext()) {
-            rootPath.next();
-        }
-        assertSame(root, rootPath.getCurrentNode());
-        rootPath.reset();
-    }
-
-
+    
+    
 }