static void DepthFirstOnVisualsChanged(RenderChain renderChain, VisualElement ve, uint dirtyID, bool parentHierarchyHidden, bool hierarchical, ref ChainBuilderStats stats)
        {
            if (dirtyID == ve.renderChainData.dirtyID)
            {
                return;
            }
            ve.renderChainData.dirtyID = dirtyID; // Prevent reprocessing of the same element in the same pass

            if (hierarchical)
            {
                stats.recursiveVisualUpdatesExpanded++;
            }

            bool wasHierarchyHidden = ve.renderChainData.isHierarchyHidden;

            ve.renderChainData.isHierarchyHidden = parentHierarchyHidden || IsElementHierarchyHidden(ve);
            if (wasHierarchyHidden != ve.renderChainData.isHierarchyHidden)
            {
                hierarchical = true;
            }

            if (!hierarchical && (ve.renderChainData.dirtiedValues & RenderDataDirtyTypes.AllVisuals) == RenderDataDirtyTypes.VisualsOpacityId)
            {
                stats.opacityIdUpdates++;
                CommandGenerator.UpdateOpacityId(ve, renderChain);
                return;
            }

            UpdateWorldFlipsWinding(ve);

            Debug.Assert(ve.renderChainData.clipMethod != ClipMethod.Undetermined);
            Debug.Assert(RenderChainVEData.AllocatesID(ve.renderChainData.transformID) || ve.hierarchy.parent == null || ve.renderChainData.transformID.Equals(ve.hierarchy.parent.renderChainData.transformID) || (ve.renderHints & RenderHints.GroupTransform) != 0);

            if (ve is TextElement)
            {
                RenderEvents.UpdateTextCoreSettings(renderChain, ve);
            }

            UIRStylePainter.ClosingInfo closingInfo = CommandGenerator.PaintElement(renderChain, ve, ref stats);

            if (hierarchical)
            {
                // Recurse on children
                int childrenCount = ve.hierarchy.childCount;
                for (int i = 0; i < childrenCount; i++)
                {
                    DepthFirstOnVisualsChanged(renderChain, ve.hierarchy[i], dirtyID, ve.renderChainData.isHierarchyHidden, true, ref stats);
                }
            }

            // By closing the element after its children, we can ensure closing data is allocated
            // at a time that would maintain continuity in the index buffer
            if (closingInfo.needsClosing)
            {
                CommandGenerator.ClosePaintElement(ve, closingInfo, renderChain, ref stats);
            }
        }
        static void OnColorChanged(RenderChain renderChain, VisualElement ve, uint dirtyID, ref ChainBuilderStats stats)
        {
            if (dirtyID == ve.renderChainData.dirtyID)
            {
                return;
            }

            ve.renderChainData.dirtyID = dirtyID; // Prevent reprocessing of the same element in the same pass
            stats.colorUpdatesExpanded++;

            var newColor = ve.resolvedStyle.backgroundColor;

            ve.renderChainData.backgroundColor = newColor;

            bool shouldUpdateVisuals = false;

            if ((ve.renderHints & RenderHints.DynamicColor) == RenderHints.DynamicColor)
            {
                if (InitColorIDs(renderChain, ve))
                {
                    // New colors were allocated, we need to update the visuals
                    shouldUpdateVisuals = true;
                }

                SetColorValues(renderChain, ve);

                if (ve is TextElement && !RenderEvents.UpdateTextCoreSettings(renderChain, ve))
                {
                    shouldUpdateVisuals = true;
                }
            }
            else
            {
                shouldUpdateVisuals = true;
            }

            if (shouldUpdateVisuals)
            {
                renderChain.UIEOnVisualsChanged(ve, false);
            }
        }