internal static uint DepthFirstOnChildAdded(RenderChain renderChain, VisualElement parent, VisualElement ve, int index, bool resetState)
        {
            Debug.Assert(ve.panel != null);

            if (ve.renderChainData.isInChain)
            {
                return(0); // Already added, redundant call
            }
            if (resetState)
            {
                ve.renderChainData = new RenderChainVEData();
            }

            ve.renderChainData.isInChain           = true;
            ve.renderChainData.verticesSpace       = Matrix4x4.identity;
            ve.renderChainData.transformID         = UIRVEShaderInfoAllocator.identityTransform;
            ve.renderChainData.clipRectID          = UIRVEShaderInfoAllocator.infiniteClipRect;
            ve.renderChainData.opacityID           = UIRVEShaderInfoAllocator.fullOpacity;
            ve.renderChainData.colorID             = BMPAlloc.Invalid; // Rely on vertex tint color by default
            ve.renderChainData.backgroundColorID   = BMPAlloc.Invalid;
            ve.renderChainData.borderLeftColorID   = BMPAlloc.Invalid;
            ve.renderChainData.borderTopColorID    = BMPAlloc.Invalid;
            ve.renderChainData.borderRightColorID  = BMPAlloc.Invalid;
            ve.renderChainData.borderBottomColorID = BMPAlloc.Invalid;
            ve.renderChainData.tintColorID         = BMPAlloc.Invalid;
            ve.renderChainData.textCoreSettingsID  = UIRVEShaderInfoAllocator.defaultTextCoreSettings;
            ve.renderChainData.compositeOpacity    = float.MaxValue; // Any unreasonable value will do to trip the opacity composer to work
            UpdateLocalFlipsWinding(ve);

            if (parent != null)
            {
                if ((parent.renderHints & (RenderHints.GroupTransform)) != 0)
                {
                    ve.renderChainData.groupTransformAncestor = parent;
                }
                else
                {
                    ve.renderChainData.groupTransformAncestor = parent.renderChainData.groupTransformAncestor;
                }
                ve.renderChainData.hierarchyDepth = parent.renderChainData.hierarchyDepth + 1;
            }
            else
            {
                ve.renderChainData.groupTransformAncestor = null;
                ve.renderChainData.hierarchyDepth         = 0;
            }

            renderChain.EnsureFitsDepth(ve.renderChainData.hierarchyDepth);

            if (index > 0)
            {
                Debug.Assert(parent != null);
                ve.renderChainData.prev = GetLastDeepestChild(parent.hierarchy[index - 1]);
            }
            else
            {
                ve.renderChainData.prev = parent;
            }
            ve.renderChainData.next = ve.renderChainData.prev != null ? ve.renderChainData.prev.renderChainData.next : null;

            if (ve.renderChainData.prev != null)
            {
                ve.renderChainData.prev.renderChainData.next = ve;
            }
            if (ve.renderChainData.next != null)
            {
                ve.renderChainData.next.renderChainData.prev = ve;
            }

            // TransformID
            // Since transform type is controlled by render hints which are locked on the VE by now, we can
            // go ahead and prep transform data now and never check on it again under regular circumstances
            Debug.Assert(!RenderChainVEData.AllocatesID(ve.renderChainData.transformID));
            if (NeedsTransformID(ve))
            {
                ve.renderChainData.transformID = renderChain.shaderInfoAllocator.AllocTransform(); // May fail, that's ok
            }
            else
            {
                ve.renderChainData.transformID = BMPAlloc.Invalid;
            }
            ve.renderChainData.boneTransformAncestor = null;

            if (NeedsColorID(ve))
            {
                InitColorIDs(renderChain, ve);
                SetColorValues(renderChain, ve);
            }

            if (!RenderChainVEData.AllocatesID(ve.renderChainData.transformID))
            {
                if (parent != null && (ve.renderHints & RenderHints.GroupTransform) == 0)
                {
                    if (RenderChainVEData.AllocatesID(parent.renderChainData.transformID))
                    {
                        ve.renderChainData.boneTransformAncestor = parent;
                    }
                    else
                    {
                        ve.renderChainData.boneTransformAncestor = parent.renderChainData.boneTransformAncestor;
                    }

                    ve.renderChainData.transformID            = parent.renderChainData.transformID;
                    ve.renderChainData.transformID.ownedState = OwnedState.Inherited; // Mark this allocation as not owned by us (inherited)
                }
                else
                {
                    ve.renderChainData.transformID = UIRVEShaderInfoAllocator.identityTransform;
                }
            }
            else
            {
                renderChain.shaderInfoAllocator.SetTransformValue(ve.renderChainData.transformID, GetTransformIDTransformInfo(ve));
            }

            // Recurse on children
            int  childrenCount = ve.hierarchy.childCount;
            uint deepCount     = 0;

            for (int i = 0; i < childrenCount; i++)
            {
                deepCount += DepthFirstOnChildAdded(renderChain, ve, ve.hierarchy[i], i, resetState);
            }
            return(1 + deepCount);
        }