private void UpdateSubTree(VisualElement ve, int currentLayoutPass, bool isDisplayed = true)
        {
            Rect yogaLayoutRect = new Rect(ve.yogaNode.LayoutX, ve.yogaNode.LayoutY, ve.yogaNode.LayoutWidth, ve.yogaNode.LayoutHeight);
            // This is not the "real" padding rect: it may differ in size and position because the borders are ignored.
            // Alone, it cannot be used to identify padding size changes, because the bottom and right values depend on the
            // layout rect width/height. A change in layout rect width/height with a corresponding variation in
            // right/bottom values may yield the same pseudoPaddingRect. Fortunately, the layout rect size and padding rect
            // size change trigger the same code path which explains why we haven't seen any issue with this so far.
            Rect yogaPseudoPaddingRect = new Rect(
                ve.yogaNode.LayoutPaddingLeft,
                ve.yogaNode.LayoutPaddingTop,
                ve.yogaNode.LayoutWidth - (ve.yogaNode.LayoutPaddingLeft + ve.yogaNode.LayoutPaddingRight),
                ve.yogaNode.LayoutHeight - (ve.yogaNode.LayoutPaddingTop + ve.yogaNode.LayoutPaddingBottom));
            Rect lastLayoutRect        = ve.lastLayout;
            Rect lastPseudoPaddingRect = ve.lastPseudoPadding;
            bool wasHierarchyDisplayed = ve.isHierarchyDisplayed;

            VersionChangeType changeType = 0;
            // Changing the layout/padding size should trigger the following version changes:
            // - Size:    to update the clipping rect, when required
            // - Repaint: to update the geometry inside the new rect
            bool layoutSizeChanged        = lastLayoutRect.size != yogaLayoutRect.size;
            bool pseudoPaddingSizeChanged = lastPseudoPaddingRect.size != yogaPseudoPaddingRect.size;

            if (layoutSizeChanged || pseudoPaddingSizeChanged)
            {
                changeType |= VersionChangeType.Size | VersionChangeType.Repaint;
            }

            // Changing the layout/padding position should trigger the following version change:
            // - Transform: to draw the element and content at the right position
            bool layoutPositionChanged  = yogaLayoutRect.position != lastLayoutRect.position;
            bool paddingPositionChanged = yogaPseudoPaddingRect.position != lastPseudoPaddingRect.position;

            if (layoutPositionChanged || paddingPositionChanged)
            {
                changeType |= VersionChangeType.Transform;
            }

            isDisplayed            &= ve.resolvedStyle.display != DisplayStyle.None;
            ve.isHierarchyDisplayed = isDisplayed;

            if (changeType != 0)
            {
                ve.IncrementVersion(changeType);
            }

            ve.lastLayout        = yogaLayoutRect;
            ve.lastPseudoPadding = yogaPseudoPaddingRect;

            // ignore clean sub trees
            bool hasNewLayout = ve.yogaNode.HasNewLayout;

            if (hasNewLayout)
            {
                var childCount = ve.hierarchy.childCount;
                for (int i = 0; i < childCount; ++i)
                {
                    UpdateSubTree(ve.hierarchy[i], currentLayoutPass, isDisplayed);
                }
            }

            // Only send GeometryChanged events when the layout changes
            // (padding changes don't affect the element's geometry).
            if ((layoutSizeChanged || layoutPositionChanged) && ve.HasEventCallbacksOrDefaultActions(GeometryChangedEvent.EventCategory))
            {
                using (var evt = GeometryChangedEvent.GetPooled(lastLayoutRect, yogaLayoutRect))
                {
                    evt.layoutPass = currentLayoutPass;
                    evt.target     = ve;
                    ve.HandleEventAtTargetAndDefaultPhase(evt);
                }
            }

            if (hasNewLayout)
            {
                ve.yogaNode.MarkLayoutSeen();
            }
        }