diff -r 26bf7b829a87 modules/controls/src/main/java/com/sun/javafx/scene/control/skin/VirtualFlow.java
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/VirtualFlow.java	Mon Jul 01 13:27:40 2013 +0200
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/VirtualFlow.java	Mon Jul 08 09:10:38 2013 +0200
@@ -791,10 +791,7 @@
         // isNeedsLayout() is commented out due to RT-21417. This does not
         // appear to impact performance (indeed, it may help), and resolves the
         // issue identified in RT-21417.
-        if (getScene() != null/* && !isNeedsLayout()*/) {
-            getScene().addToDirtyLayoutList(this);
-            setNeedsLayout(true);
-        }
+        setNeedsLayout(true);
     }
     
     @Override protected void layoutChildren() {
diff -r 26bf7b829a87 modules/graphics/src/main/java/com/sun/javafx/scene/LayoutFlags.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/LayoutFlags.java	Mon Jul 08 09:10:38 2013 +0200
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.javafx.scene;
+
+public enum LayoutFlags {
+    CLEAN,
+    DIRTY_BRANCH,
+    NEEDS_LAYOUT
+}
diff -r 26bf7b829a87 modules/graphics/src/main/java/javafx/scene/Node.java
--- a/modules/graphics/src/main/java/javafx/scene/Node.java	Mon Jul 01 13:27:40 2013 +0200
+++ b/modules/graphics/src/main/java/javafx/scene/Node.java	Mon Jul 08 09:10:38 2013 +0200
@@ -137,6 +137,7 @@
 import com.sun.javafx.scene.CssFlags;
 import com.sun.javafx.scene.DirtyBits;
 import com.sun.javafx.scene.EventHandlerProperties;
+import com.sun.javafx.scene.LayoutFlags;
 import com.sun.javafx.scene.NodeEventDispatcher;
 import com.sun.javafx.scene.NodeHelper;
 import com.sun.javafx.scene.SceneHelper;
@@ -4815,6 +4816,23 @@
 
         return tmin;
     }
+    
+    /**
+     * 
+     */
+    public final void validate() {
+        Parent p = getParent();
+        if (p == null) {
+            processCSS();
+            processLayout();
+        } else {
+            while (p.getParent() != null && !p.layoutRoot) {
+                p = p.getParent();
+            }
+            p.processCSS();
+            p.processLayout();
+        }
+    }
 
 
     // Good to find a home for commonly use util. code such as EPS.
@@ -8380,6 +8398,12 @@
              styleHelper.setObservableStyleMap(styleMap);
          }
      }
+     
+    LayoutFlags layoutFlag = LayoutFlags.CLEAN;
+    
+    void processLayout() {
+        // NOP for Node, overriden in Parent
+    }
 
     /**
      * Flags used to indicate in which way this node is dirty (or whether it
@@ -8387,7 +8411,7 @@
      * scenegraph.
      */
     CssFlags cssFlag = CssFlags.CLEAN;
-
+    
     /**
      * Needed for testing.
      */
diff -r 26bf7b829a87 modules/graphics/src/main/java/javafx/scene/Parent.java
--- a/modules/graphics/src/main/java/javafx/scene/Parent.java	Mon Jul 01 13:27:40 2013 +0200
+++ b/modules/graphics/src/main/java/javafx/scene/Parent.java	Mon Jul 08 09:10:38 2013 +0200
@@ -63,6 +63,7 @@
 import com.sun.javafx.tk.Toolkit;
 
 import static com.sun.javafx.logging.PulseLogger.*;
+import com.sun.javafx.scene.LayoutFlags;
 
 /**
  * The base class for all nodes that have children in the scene graph.
@@ -636,29 +637,20 @@
             children.get(i).setScenes(newScene, newSubScene);
         }
 
-        // If this node was in the old scene's dirty layout
-        // list, then remove it from that list so that it is
-        // not processed on the next pulse
-        final boolean awaitingLayout = isNeedsLayout();
+        final boolean awaitingLayout = layoutFlag != LayoutFlags.CLEAN;
 
         sceneRoot = (newSubScene != null && newSubScene.getRoot() == this) ||
                     (newScene != null && newScene.getRoot() == this);
         layoutRoot = !isManaged() || sceneRoot;
+        
 
         if (awaitingLayout) {
-            boolean sceneChanged = oldScene != newScene;
-            if (oldScene != null && sceneChanged) {
-                oldScene.removeFromDirtyLayoutList(this);
-            }
             // If this node is dirty and the new scene or subScene is not null
             // then add this node to the new scene's dirty list
             if (newScene != null && layoutRoot) {
                 if (newSubScene != null) {
                     newSubScene.setDirtyLayout(this);
                 }
-                if (sceneChanged) {
-                    newScene.addToDirtyLayoutList(this);
-                }
             }
         }
     }
@@ -772,18 +764,33 @@
      * Indicates that this Node and its subnodes requires a layout pass on
      * the next pulse.
      */
-    private ReadOnlyBooleanWrapper needsLayout = new ReadOnlyBooleanWrapper(this, "needsLayout", true);
+    private ReadOnlyBooleanWrapper needsLayout;
 
     protected final void setNeedsLayout(boolean value) {
-        needsLayout.set(value);
+        if (value) {
+            markDirtyLayout(true);
+        } else if (layoutFlag == LayoutFlags.NEEDS_LAYOUT) {
+            boolean hasBranch = false; 
+            for (int i = 0, max = children.size(); i < max; i++) {
+                final Node child = children.get(i);
+                if (child.layoutFlag != LayoutFlags.CLEAN) {
+                    hasBranch = true;
+                    break;
+                }
+            }
+            setLayoutFlag(hasBranch ? LayoutFlags.DIRTY_BRANCH : LayoutFlags.CLEAN);
+        }
     }
 
     public final boolean isNeedsLayout() {
-        return needsLayout.get();
+        return layoutFlag == LayoutFlags.NEEDS_LAYOUT;
     }
 
     public final ReadOnlyBooleanProperty needsLayoutProperty() {
-        return needsLayout.getReadOnlyProperty();
+        if (needsLayout == null) {
+            needsLayout = new ReadOnlyBooleanWrapper(this, "needsLayout", layoutFlag == LayoutFlags.NEEDS_LAYOUT);
+        }
+        return needsLayout;
     }
 
     /**
@@ -799,6 +806,51 @@
     private double prefHeightCache = -1;
     private double minWidthCache = -1;
     private double minHeightCache = -1;
+    
+    private void setLayoutFlag(LayoutFlags flag) {
+        if (needsLayout != null) {
+            needsLayout.set(flag == LayoutFlags.NEEDS_LAYOUT);
+        }
+        layoutFlag = flag;
+    }
+    
+    private void markDirtyLayoutBranch() {
+        Parent parent = getParent();
+        while (parent != null && parent.layoutFlag == LayoutFlags.CLEAN) {
+            parent.setLayoutFlag(LayoutFlags.DIRTY_BRANCH);
+            parent = parent.getParent();
+        }
+    }
+    
+    private void markDirtyLayout(boolean local) {
+        setLayoutFlag(LayoutFlags.NEEDS_LAYOUT);
+        if (local || layoutRoot) {
+            markDirtyLayoutBranch();
+        } else {
+            requestParentLayout();
+        }
+    }
+
+    @Override
+    void processLayout() {
+        switch(layoutFlag) {
+            case CLEAN:
+                break;
+            case NEEDS_LAYOUT:
+                performingLayout = true;
+                layoutChildren();
+                // Intended fall-through
+            case DIRTY_BRANCH:
+                for (int i = 0, max = children.size(); i < max; i++) {
+                    final Node child = children.get(i);
+                    child.processLayout();
+                }
+                setLayoutFlag(LayoutFlags.CLEAN);
+                performingLayout = false;
+                break;
+        }
+    }
+    
 
     /**
      * Requests a layout pass to be performed before the next scene is
@@ -811,18 +863,12 @@
      * @since JavaFX 8.0
      */
     public void requestLayout() {
-        if (!isNeedsLayout()) {
+        if (layoutFlag != LayoutFlags.NEEDS_LAYOUT) {
             prefWidthCache = -1;
             prefHeightCache = -1;
             minWidthCache = -1;
             minHeightCache = -1;
-            PlatformLogger logger = Logging.getLayoutLogger();
-            if (logger.isLoggable(Level.FINER)) {
-                logger.finer(this.toString());
-            }
-
-            setNeedsLayout(true);
-            requestParentLayout();
+            markDirtyLayout(false);
         } else {
             clearSizeCache();
         }
@@ -838,16 +884,7 @@
      * when it's parent recomputes the layout with the new hints.
      */
     protected final void requestParentLayout() {
-        if (layoutRoot) {
-            final Scene scene = getScene();
-            final SubScene subScene = getSubScene();
-            if (subScene != null) {
-                subScene.setDirtyLayout(this);
-            }
-            if (scene != null) {
-                scene.addToDirtyLayoutList(this);
-            }
-        } else {
+        if (!layoutRoot) {
             final Parent parent = getParent();
             if (parent != null && !parent.performingLayout) {
                 parent.requestLayout();
@@ -1016,34 +1053,11 @@
 
     /**
      * Executes a top-down layout pass on the scene graph under this parent.
+     * @deprecated Use {@link Node#validate()} to get the node the valid state
      */
+    @Deprecated
     public final void layout() {
-        if (isNeedsLayout()) {
-            if (PULSE_LOGGING_ENABLED) PULSE_LOGGER.fxIncrementCounter("Parent#layout() on dirty Node");
-            performingLayout = true;
-
-            PlatformLogger logger = Logging.getLayoutLogger();
-            if (logger.isLoggable(Level.FINE)) {
-                logger.fine(this+" size: "+
-                        getLayoutBounds().getWidth()+" x "+getLayoutBounds().getHeight());
-            }
-
-            // layout the children in this parent.
-            layoutChildren();
-
-            // Perform layout on each child, hoping it has random access performance!
-            for (int i=0, max=children.size(); i<max; i++) {
-                final Node child = children.get(i);
-                if (child instanceof Parent) {
-                    ((Parent) child).layout();
-                }
-            }
-            setNeedsLayout(false);
-
-            performingLayout = false;
-        } else {
-            if (PULSE_LOGGING_ENABLED) PULSE_LOGGER.fxIncrementCounter("Parent#layout() on clean Node");
-        }
+        processLayout();
     }
 
     /**
@@ -1074,7 +1088,7 @@
      * whenever the sceneRoot field changes, or whenever the managed
      * property changes.
      */
-    private boolean layoutRoot = false;
+    boolean layoutRoot = false;
     @Override final void notifyManagedChanged() {
         layoutRoot = !isManaged() || sceneRoot;
     }
@@ -1217,6 +1231,7 @@
      * Constructs a new {@code Parent}.
      */
     protected Parent() {
+        layoutFlag = LayoutFlags.NEEDS_LAYOUT;
     }
 
     /**
diff -r 26bf7b829a87 modules/graphics/src/main/java/javafx/scene/Scene.java
--- a/modules/graphics/src/main/java/javafx/scene/Scene.java	Mon Jul 01 13:27:40 2013 +0200
+++ b/modules/graphics/src/main/java/javafx/scene/Scene.java	Mon Jul 08 09:10:38 2013 +0200
@@ -139,6 +139,7 @@
 
 import static com.sun.javafx.logging.PulseLogger.PULSE_LOGGING_ENABLED;
 import static com.sun.javafx.logging.PulseLogger.PULSE_LOGGER;
+import com.sun.javafx.scene.LayoutFlags;
 import com.sun.javafx.scene.input.KeyCodeMap;
 import com.sun.javafx.sg.PGLightBase;
 
@@ -546,79 +547,10 @@
         }
     }
 
-    /**
-     * List of dirty layout roots.
-     * When a parent is either marked as a layout root or is unmanaged and it
-     * has its needsLayout flag set to true, then that node is added to this set
-     * so that it can be laid out on the next pulse without requiring its
-     * ancestors to be laid out.
-     */
-    private Set<Parent> dirtyLayoutRootsA = new LinkedHashSet<Parent>(10);
-    private Set<Parent> dirtyLayoutRootsB = new LinkedHashSet<Parent>(10);
-    private Set<Parent> dirtyLayoutRoots = dirtyLayoutRootsA;
-
-    /**
-     * Add the specified parent to this scene's dirty layout list.
-     * @treatAsPrivate implementation detail
-     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
-     */
-    @Deprecated
-    public void addToDirtyLayoutList(Parent p) {
-        // If the current size of the list is 0 then we will need to schedule
-        // a pulse event because a layout pass is needed.
-        if (dirtyLayoutRoots.isEmpty()) {
-            Toolkit.getToolkit().requestNextPulse();
-        }
-        // Add the node.
-        dirtyLayoutRoots.add(p);
-    }
-
-    /**
-     * Remove the specified parent from this scene's dirty layout list.
-     */
-    void removeFromDirtyLayoutList(Parent p) {
-        dirtyLayoutRoots.remove(p);
-    }
-
     private void doLayoutPass() {
-        // sometimes a layout pass with cause scene-graph changes (bounds/structure)
-        // that leave some branches needing further layout, so pass through roots twice
-        layoutDirtyRoots();
-        layoutDirtyRoots();
-
-        // we don't want to spin too long in layout, so if there are still dirty
-        // roots, we'll leave those for next pulse.
-        if (dirtyLayoutRoots.size() > 0) {
-            PlatformLogger logger = Logging.getLayoutLogger();
-            if (logger.isLoggable(Level.FINER)) {
-                logger.finer("after layout pass, "+dirtyLayoutRoots.size()+" layout root nodes still dirty");
-            }
-            Toolkit.getToolkit().requestNextPulse();
-        }
-    }
-
-    private void layoutDirtyRoots() {
-        if (dirtyLayoutRoots.size() > 0) {
-            PlatformLogger logger = Logging.getLayoutLogger();
-            Set<Parent> temp = dirtyLayoutRoots;
-            if (dirtyLayoutRoots == dirtyLayoutRootsA) {
-                dirtyLayoutRoots = dirtyLayoutRootsB;
-            } else {
-                dirtyLayoutRoots = dirtyLayoutRootsA;
-            }
-
-            for (Parent parent : temp) {
-                if (parent.getScene() == this && parent.isNeedsLayout()) {
-                    if (logger.isLoggable(Level.FINE)) {
-                        logger.fine("<<< START >>> root = "+parent.toString());
-                    }
-                    parent.layout();
-                    if (logger.isLoggable(Level.FINE)) {
-                        logger.fine("<<<  END  >>> root = "+parent.toString());
-                    }
-                }
-            }
-            temp.clear();
+        final Parent r = getRoot();
+        if (r != null) {
+            r.processLayout();
         }
     }
 
@@ -1182,12 +1114,6 @@
         return root;
     }
 
-    private void doLayoutPassWithoutPulse(int maxAttempts) {
-        for (int i = 0; dirtyLayoutRoots.size() > 0 && i != maxAttempts; ++i) {
-            layoutDirtyRoots();
-        }
-    }
-
     void setNeedsRepaint() {
         if (this.impl_peer != null) {
             impl_peer.entireSceneNeedsRepaint();
@@ -1206,7 +1132,7 @@
 
         // we do not need pulse in the snapshot code
         // because this scene can be stage-less
-        doLayoutPassWithoutPulse(3);
+        doLayoutPass();
 
         if (!paused) {
             getRoot().updateBounds();
@@ -2121,9 +2047,10 @@
      * @return boolean indicating whether the scene is quiescent
      */
     boolean isQuiescent() {
+        final Parent r = getRoot();
         return !isFocusDirty()
-               && (getRoot().cssFlag == CssFlags.CLEAN)
-               && dirtyLayoutRoots.isEmpty();
+               && (r == null || (r.cssFlag == CssFlags.CLEAN &&
+                r.layoutFlag == LayoutFlags.CLEAN));
     }
 
     /**
