Exemplo n.º 1
0
        void Constructor(IPanel panelObj, UIRenderDevice deviceObj, UIRAtlasManager atlasMan, VectorImageManager vectorImageMan)
        {
            if (disposed)
            {
                DisposeHelper.NotifyDisposedUsed(this);
            }

            // A reasonable starting depth level suggested here
            m_DirtyTracker.heads     = new List <VisualElement>(8);
            m_DirtyTracker.tails     = new List <VisualElement>(8);
            m_DirtyTracker.minDepths = new int[(int)RenderDataDirtyTypeClasses.Count];
            m_DirtyTracker.maxDepths = new int[(int)RenderDataDirtyTypeClasses.Count];
            m_DirtyTracker.Reset();

            if (m_RenderNodesData.Count < 1)
            {
                m_RenderNodesData.Add(new RenderNodeData()
                {
                    matPropBlock = new MaterialPropertyBlock()
                });
            }

            this.panel              = panelObj;
            this.device             = deviceObj;
            this.atlasManager       = atlasMan;
            this.vectorImageManager = vectorImageMan;
            this.shaderInfoAllocator.Construct();

            painter              = new Implementation.UIRStylePainter(this);
            Font.textureRebuilt += OnFontReset;
        }
        private void RefreshStats(UIRenderDevice renderDevice)
        {
            var statistics = renderDevice.GatherAllocationStatistics();

            m_GlobalStats.UpdateStats(statistics);
            m_PagesStats.UpdateStats(statistics);
        }
        static void OnGraphicsResourcesRecreate(bool recreate)
        {
            if (!recreate)
            {
                UIRenderDevice.PrepareForGfxDeviceRecreate();
            }

            var it = UIElementsUtility.GetPanelsIterator();

            while (it.MoveNext())
            {
                if (recreate)
                {
                    it.Current.Value.atlas?.Reset();
                }
                else
                {
                    (it.Current.Value.GetUpdater(VisualTreeUpdatePhase.Repaint) as UIRRepaintUpdater)?.DestroyRenderChain();
                }
            }

            if (!recreate)
            {
                UIRenderDevice.FlushAllPendingDeviceDisposes();
            }
            else
            {
                UIRenderDevice.WrapUpGfxDeviceRecreate();
            }
        }
Exemplo n.º 4
0
        internal void AfterRenderDeviceRelease()
        {
            if (disposed)
            {
                DisposeHelper.NotifyDisposedUsed(this);
            }

            Debug.Assert(device == null);
            device = new UIRenderDevice(Implementation.RenderEvents.ResolveShader((panel as BaseVisualElementPanel)?.standardShader));

            Debug.Assert(painter == null);
            painter = new Implementation.UIRStylePainter(this);

            ConstructShaderInfoAllocator();

            var ve = GetFirstElementInPanel(m_FirstCommand?.owner);

            while (ve != null)
            {
                Implementation.RenderEvents.OnRestoreTransformIDs(ve, device);
                ve.renderChainData.shaderInfoAlloc = m_FullOpacityShaderInfo; // Reset shader info allocs
                UIEOnVisualsChanged(ve, false);                               // Marking dirty will repaint and have the data regenerated
                ve = ve.renderChainData.next;
            }
            UIEOnOpacityChanged(panel.visualTree);
        }
Exemplo n.º 5
0
        void OnBeforeDrawChain(UIRenderDevice renderChain)
#endif
        {
            Material mat = renderChain.GetStandardMaterial();

            // Set global graph view shader properties (used by UIR)
            mat.SetFloat(s_EditorPixelsPerPointId, EditorGUIUtility.pixelsPerPoint);
            mat.SetFloat(s_GraphViewScaleId, scale);
            redrawn?.Invoke();
        }
        static unsafe void PrepareNudgeVertices(VisualElement ve, UIRenderDevice device, MeshHandle mesh, out IntPtr src, out IntPtr dst, out int count)
        {
            int vertCount = (int)mesh.allocVerts.size;
            NativeSlice <Vertex> oldVerts = mesh.allocPage.vertices.cpuData.Slice((int)mesh.allocVerts.start, vertCount);
            NativeSlice <Vertex> newVerts;

            device.Update(mesh, (uint)vertCount, out newVerts);

            src   = (IntPtr)oldVerts.GetUnsafePtr();
            dst   = (IntPtr)newVerts.GetUnsafePtr();
            count = vertCount;
        }
Exemplo n.º 7
0
        protected RenderChain(IPanel panel, UIRenderDevice device, UIRAtlasManager atlasManager, VectorImageManager vectorImageManager)
        {
            if (disposed)
            {
                DisposeHelper.NotifyDisposedUsed(this);
            }

            this.panel              = panel;
            this.device             = device;
            this.atlasManager       = atlasManager;
            this.vectorImageManager = vectorImageManager;
            painter              = new Implementation.UIRStylePainter(this);
            Font.textureRebuilt += OnFontReset;
        }
Exemplo n.º 8
0
        public RenderChain(IPanel panel, Shader standardShader)
        {
            if (disposed)
            {
                DisposeHelper.NotifyDisposedUsed(this);
            }

            this.panel = panel;
            device     = new UIRenderDevice(Implementation.RenderEvents.ResolveShader(standardShader));

            atlasManager       = new UIRAtlasManager();
            vectorImageManager = new VectorImageManager(atlasManager);
            painter            = new Implementation.UIRStylePainter(this);

            Font.textureRebuilt += OnFontReset;
        }
Exemplo n.º 9
0
        protected RenderChain(IPanel panel, UIRenderDevice device, UIRAtlasManager atlasManager)
        {
            if (disposed)
            {
                DisposeHelper.NotifyDisposedUsed(this);
            }

            this.panel        = panel;
            this.device       = device;
            this.atlasManager = atlasManager;
            if (atlasManager != null)
            {
                atlasManager.ResetPerformed += OnAtlasReset;
            }
            painter              = new Implementation.UIRStylePainter(this);
            Font.textureRebuilt += OnFontReset;
        }
Exemplo n.º 10
0
        void Constructor(BaseVisualElementPanel panelObj, UIRenderDevice deviceObj, AtlasBase atlas, VectorImageManager vectorImageMan)
        {
            if (disposed)
            {
                DisposeHelper.NotifyDisposedUsed(this);
            }

            // A reasonable starting depth level suggested here
            m_DirtyTracker.heads     = new List <VisualElement>(8);
            m_DirtyTracker.tails     = new List <VisualElement>(8);
            m_DirtyTracker.minDepths = new int[(int)RenderDataDirtyTypeClasses.Count];
            m_DirtyTracker.maxDepths = new int[(int)RenderDataDirtyTypeClasses.Count];
            m_DirtyTracker.Reset();

            if (m_RenderNodesData.Count < 1)
            {
                m_RenderNodesData.Add(new RenderNodeData()
                {
                    matPropBlock = new MaterialPropertyBlock()
                });
            }

            this.panel              = panelObj;
            this.device             = deviceObj;
            this.atlas              = atlas;
            this.vectorImageManager = vectorImageMan;

            // TODO: Share these across all panels
            vertsPool   = new TempAllocator <Vertex>(8192, 2048, 64 * 1024);
            indicesPool = new TempAllocator <UInt16>(8192 << 1, 2048 << 1, (64 * 1024) << 1);
            jobManager  = new JobManager();

            this.shaderInfoAllocator.Construct();
            this.opacityIdAccelerator = new OpacityIdAccelerator();

            painter = new Implementation.UIRStylePainter(this);

            var rp = panel as BaseRuntimePanel;

            if (rp != null && rp.drawToCameras)
            {
                drawInCameras = true;
                m_StaticIndex = RenderChainStaticIndexAllocator.AllocateIndex(this);
            }
        }
Exemplo n.º 11
0
        void Constructor(IPanel panelObj, UIRenderDevice deviceObj, UIRAtlasManager atlasMan, VectorImageManager vectorImageMan)
        {
            if (disposed)
            {
                DisposeHelper.NotifyDisposedUsed(this);
            }

            // A reasonable starting depth level suggested here
            m_DirtyTracker.heads = new List <VisualElement>(8);
            m_DirtyTracker.tails = new List <VisualElement>(8);
            m_DirtyTracker.Reset();

            this.panel              = panelObj;
            this.device             = deviceObj;
            this.atlasManager       = atlasMan;
            this.vectorImageManager = vectorImageMan;
            painter              = new Implementation.UIRStylePainter(this);
            Font.textureRebuilt += OnFontReset;
        }
Exemplo n.º 12
0
        internal void AfterRenderDeviceRelease()
        {
            if (disposed)
            {
                DisposeHelper.NotifyDisposedUsed(this);
            }

            Debug.Assert(device == null);

            var root              = m_RenderDeviceRestoreInfo.root;
            var panelObj          = root.panel;
            var deviceObj         = new UIRenderDevice(m_RenderDeviceRestoreInfo.standardShader);
            var atlasManObj       = m_RenderDeviceRestoreInfo.hasAtlasMan ? new UIRAtlasManager() : null;
            var vectorImageManObj = m_RenderDeviceRestoreInfo.hasVectorImageMan ? new VectorImageManager(atlasManObj) : null;

            m_RenderDeviceRestoreInfo = new RenderDeviceRestoreInfo();

            Constructor(panelObj, deviceObj, atlasManObj, vectorImageManObj);
            UIEOnChildAdded(root.parent, root, root.hierarchy.parent == null ? 0 : root.hierarchy.parent.IndexOf(panel.visualTree));
        }
Exemplo n.º 13
0
        public void Begin(VisualElement ve, UIRenderDevice device)
        {
            Debug.Assert(ve.renderChainData.usesLegacyText && ve.renderChainData.textEntries.Count > 0);
            this.m_CurrentElement = ve;
            this.m_TextEntryIndex = 0;
            Alloc allocVerts           = ve.renderChainData.data.allocVerts;
            NativeSlice <Vertex> slice = ve.renderChainData.data.allocPage.vertices.cpuData.Slice((int)allocVerts.start, (int)allocVerts.size);

            device.Update(ve.renderChainData.data, ve.renderChainData.data.allocVerts.size, out this.m_MeshDataVerts);
            RenderChainTextEntry renderChainTextEntry = ve.renderChainData.textEntries[0];
            bool flag = ve.renderChainData.textEntries.Count > 1 || renderChainTextEntry.vertexCount != this.m_MeshDataVerts.Length;

            if (flag)
            {
                this.m_MeshDataVerts.CopyFrom(slice);
            }
            int firstVertex = renderChainTextEntry.firstVertex;

            this.m_XFormClipPages            = slice[firstVertex].xformClipPages;
            this.m_IDsFlags                  = slice[firstVertex].idsFlags;
            this.m_OpacityPagesSettingsIndex = slice[firstVertex].opacityPageSVGSettingIndex;
        }
        private static void OnGraphicsResourcesRecreate(bool recreate)
        {
            bool flag = !recreate;

            if (flag)
            {
                UIRenderDevice.PrepareForGfxDeviceRecreate();
            }
            Dictionary <int, Panel> .Enumerator panelsIterator = UIElementsUtility.GetPanelsIterator();
            while (panelsIterator.MoveNext())
            {
                KeyValuePair <int, Panel> current = panelsIterator.Current;
                UIRRepaintUpdater         expr_32 = current.Value.GetUpdater(VisualTreeUpdatePhase.Repaint) as UIRRepaintUpdater;
                RenderChain renderChain           = (expr_32 != null) ? expr_32.renderChain : null;
                if (recreate)
                {
                    if (renderChain != null)
                    {
                        renderChain.AfterRenderDeviceRelease();
                    }
                }
                else if (renderChain != null)
                {
                    renderChain.BeforeRenderDeviceRelease();
                }
            }
            bool flag2 = !recreate;

            if (flag2)
            {
                UIRenderDevice.FlushAllPendingDeviceDisposes();
            }
            else
            {
                UIRenderDevice.WrapUpGfxDeviceRecreate();
            }
        }
Exemplo n.º 15
0
 protected RenderChain(BaseVisualElementPanel panel, UIRenderDevice device, AtlasBase atlas, VectorImageManager vectorImageManager)
 {
     Constructor(panel, device, atlas, vectorImageManager);
 }
Exemplo n.º 16
0
 protected RenderChain(IPanel panel, UIRenderDevice device, UIRAtlasManager atlasManager, VectorImageManager vectorImageManager)
 {
     Constructor(panel, device, atlasManager, vectorImageManager);
 }
Exemplo n.º 17
0
        static void DepthFirstOnClippingChanged(RenderChain renderChain,
                                                VisualElement parent,
                                                VisualElement ve,
                                                uint dirtyID,
                                                bool hierarchical,
                                                bool isRootOfChange,               // MUST be true  on the root call.
                                                bool isPendingHierarchicalRepaint, // MUST be false on the root call.
                                                bool inheritedClipRectIDChanged,   // MUST be false on the root call.
                                                bool inheritedMaskingChanged,      // MUST be false on the root call.
                                                UIRenderDevice device,
                                                ref ChainBuilderStats stats)
        {
            bool upToDate = dirtyID == ve.renderChainData.dirtyID;

            if (upToDate && !inheritedClipRectIDChanged && !inheritedMaskingChanged)
            {
                return;
            }

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

            if (!isRootOfChange)
            {
                stats.recursiveClipUpdatesExpanded++;
            }

            isPendingHierarchicalRepaint |= (ve.renderChainData.dirtiedValues & RenderDataDirtyTypes.VisualsHierarchy) != 0;

            // Internal operations (done in this call) to do:
            bool mustUpdateClipRectID      = hierarchical || isRootOfChange || inheritedClipRectIDChanged;
            bool mustUpdateClippingMethod  = hierarchical || isRootOfChange;
            bool mustUpdateChildrenMasking = hierarchical || isRootOfChange || inheritedMaskingChanged;

            // External operations (done by recursion or postponed) to do:
            bool mustRepaintThis       = false;
            bool mustRepaintHierarchy  = false;
            bool mustProcessSizeChange = false;
            // mustRecurse implies recursing on all children, but doesn't force anything beyond them.
            // hierarchical implies recursing on all descendants
            // As a result, hierarchical implies mustRecurse
            bool mustRecurse = hierarchical;

            ClipMethod oldClippingMethod = ve.renderChainData.clipMethod;
            ClipMethod newClippingMethod = mustUpdateClippingMethod ? DetermineSelfClipMethod(renderChain, ve) : oldClippingMethod;

            // Shader discard support
            bool clipRectIDChanged = false;

            if (mustUpdateClipRectID)
            {
                BMPAlloc newClipRectID = ve.renderChainData.clipRectID;
                if (newClippingMethod == ClipMethod.ShaderDiscard)
                {
                    if (!RenderChainVEData.AllocatesID(ve.renderChainData.clipRectID))
                    {
                        newClipRectID = renderChain.shaderInfoAllocator.AllocClipRect();
                        if (!newClipRectID.IsValid())
                        {
                            newClippingMethod = ClipMethod.Scissor; // Fallback to scissor since we couldn't allocate a clipRectID
                            // Both shader discard and scisorring work with world-clip rectangles, so no need
                            // to inherit any clipRectIDs for such elements, our own scissor rect clips up correctly
                            newClipRectID = UIRVEShaderInfoAllocator.infiniteClipRect;
                        }
                    }
                }
                else
                {
                    if (RenderChainVEData.AllocatesID(ve.renderChainData.clipRectID))
                    {
                        renderChain.shaderInfoAllocator.FreeClipRect(ve.renderChainData.clipRectID);
                    }

                    // Inherit parent's clipRectID if possible.
                    // Group transforms shouldn't inherit the clipRectID since they have a new frame of reference,
                    // they provide a new baseline with the _PixelClipRect instead.
                    if ((ve.renderHints & RenderHints.GroupTransform) == 0)
                    {
                        newClipRectID            = ((newClippingMethod != ClipMethod.Scissor) && (parent != null)) ? parent.renderChainData.clipRectID : UIRVEShaderInfoAllocator.infiniteClipRect;
                        newClipRectID.ownedState = OwnedState.Inherited;
                    }
                }

                clipRectIDChanged = !ve.renderChainData.clipRectID.Equals(newClipRectID);
                Debug.Assert((ve.renderHints & RenderHints.GroupTransform) == 0 || !clipRectIDChanged);
                ve.renderChainData.clipRectID = newClipRectID;
            }

            bool maskingChanged = false;

            if (oldClippingMethod != newClippingMethod)
            {
                ve.renderChainData.clipMethod = newClippingMethod;

                if (oldClippingMethod == ClipMethod.Stencil || newClippingMethod == ClipMethod.Stencil)
                {
                    maskingChanged            = true;
                    mustUpdateChildrenMasking = true;
                }

                if (oldClippingMethod == ClipMethod.Scissor || newClippingMethod == ClipMethod.Scissor)
                {
                    // We need to add/remove scissor push/pop commands
                    mustRepaintThis = true;
                }

                if (newClippingMethod == ClipMethod.ShaderDiscard || oldClippingMethod == ClipMethod.ShaderDiscard && RenderChainVEData.AllocatesID(ve.renderChainData.clipRectID))
                {
                    // We must update the clipping rects.
                    mustProcessSizeChange = true;
                }
            }

            if (clipRectIDChanged)
            {
                // Our children MUST update their render data clipRectIDs
                mustRecurse = true;

                // Our children MUST update their vertex clipRectIDs
                mustRepaintHierarchy = true;
            }

            if (mustUpdateChildrenMasking)
            {
                int newChildrenMaskDepth  = 0;
                int newChildrenStencilRef = 0;
                if (parent != null)
                {
                    newChildrenMaskDepth  = parent.renderChainData.childrenMaskDepth;
                    newChildrenStencilRef = parent.renderChainData.childrenStencilRef;
                    if (newClippingMethod == ClipMethod.Stencil)
                    {
                        if (newChildrenMaskDepth > newChildrenStencilRef)
                        {
                            ++newChildrenStencilRef;
                        }
                        ++newChildrenMaskDepth;
                    }

                    // When applying the MaskContainer hint, we skip because the last depth level because even though we
                    // could technically increase the reference value, it would be useless since there won't be more
                    // deeply nested masks that could benefit from it.
                    if ((ve.renderHints & RenderHints.MaskContainer) == RenderHints.MaskContainer && newChildrenMaskDepth < UIRUtility.k_MaxMaskDepth)
                    {
                        newChildrenStencilRef = newChildrenMaskDepth;
                    }
                }

                if (ve.renderChainData.childrenMaskDepth != newChildrenMaskDepth || ve.renderChainData.childrenStencilRef != newChildrenStencilRef)
                {
                    maskingChanged = true;
                }

                ve.renderChainData.childrenMaskDepth  = newChildrenMaskDepth;
                ve.renderChainData.childrenStencilRef = newChildrenStencilRef;
            }

            if (maskingChanged)
            {
                mustRecurse = true; // Our children must update their inherited state.

                // These optimizations would allow to skip repainting the hierarchy:
                // a) We could update the stencilRef in the commands without repainting
                // b) The winding order could be reversed without repainting (when required)
                // In the meantime, we have no other choice but to request a hierarchical repaint.
                mustRepaintHierarchy = true;
            }

            if ((mustRepaintThis || mustRepaintHierarchy) && !isPendingHierarchicalRepaint)
            {
                renderChain.UIEOnVisualsChanged(ve, mustRepaintHierarchy);
                isPendingHierarchicalRepaint = true;
            }

            if (mustProcessSizeChange)
            {
                renderChain.UIEOnTransformOrSizeChanged(ve, false, true);
            }

            if (mustRecurse)
            {
                int childrenCount = ve.hierarchy.childCount;
                for (int i = 0; i < childrenCount; i++)
                {
                    DepthFirstOnClippingChanged(
                        renderChain,
                        ve,
                        ve.hierarchy[i],
                        dirtyID,
                        // Having to recurse doesn't mean that we need to process ALL descendants. For example, the
                        // propagation of the transformId may stop if a group or a bone is encountered.
                        hierarchical,
                        false,
                        isPendingHierarchicalRepaint,
                        clipRectIDChanged,
                        maskingChanged,
                        device,
                        ref stats);
                }
            }
        }
Exemplo n.º 18
0
        static void DepthFirstOnTransformOrSizeChanged(RenderChain renderChain, VisualElement parent, VisualElement ve, uint dirtyID, UIRenderDevice device, bool isAncestorOfChangeSkinned, bool transformChanged, ref ChainBuilderStats stats)
        {
            if (dirtyID == ve.renderChainData.dirtyID)
            {
                return;
            }

            stats.recursiveTransformUpdatesExpanded++;

            transformChanged |= (ve.renderChainData.dirtiedValues & RenderDataDirtyTypes.Transform) != 0;

            if (RenderChainVEData.AllocatesID(ve.renderChainData.clipRectID))
            {
                renderChain.shaderInfoAllocator.SetClipRectValue(ve.renderChainData.clipRectID, GetClipRectIDClipInfo(ve));
            }

            if (transformChanged && UpdateLocalFlipsWinding(ve))
            {
                renderChain.UIEOnVisualsChanged(ve, true);
            }

            bool dirtyHasBeenResolved = true;

            if (RenderChainVEData.AllocatesID(ve.renderChainData.transformID))
            {
                renderChain.shaderInfoAllocator.SetTransformValue(ve.renderChainData.transformID, GetTransformIDTransformInfo(ve));
                isAncestorOfChangeSkinned = true;
                stats.boneTransformed++;
            }
            else if (!transformChanged)
            {
                // Only the clip info had to be updated, we can skip the other cases which are for transform changes only.
            }
            else if ((ve.renderHints & RenderHints.GroupTransform) != 0)
            {
                stats.groupTransformElementsChanged++;
            }
            else if (isAncestorOfChangeSkinned)
            {
                // Children of a bone element inherit the transform data change automatically when the root updates that data, no need to do anything for children
                Debug.Assert(RenderChainVEData.InheritsID(ve.renderChainData.transformID)); // The element MUST have a transformID that has been inherited from an ancestor
                dirtyHasBeenResolved = false;                                               // We just skipped processing, if another later transform change is queued on this element this pass then we should still process it
                stats.skipTransformed++;
            }
            else if ((ve.renderChainData.dirtiedValues & (RenderDataDirtyTypes.Visuals | RenderDataDirtyTypes.VisualsHierarchy)) == 0 && (ve.renderChainData.data != null))
            {
                // If a visual update will happen, then skip work here as the visual update will incorporate the transformed vertices
                if (!ve.renderChainData.disableNudging && CommandGenerator.NudgeVerticesToNewSpace(ve, renderChain, device))
                {
                    stats.nudgeTransformed++;
                }
                else
                {
                    renderChain.UIEOnVisualsChanged(ve, false); // Nudging not allowed, so do a full visual repaint
                    stats.visualUpdateTransformed++;
                }
            }

            if (dirtyHasBeenResolved)
            {
                ve.renderChainData.dirtyID = dirtyID; // Prevent reprocessing of the same element in the same pass
            }
            // Make sure to pre-evaluate world transform and clip now so we don't do it at render time
            if (renderChain.drawInCameras)
            {
                ve.EnsureWorldTransformAndClipUpToDate();
            }

            if ((ve.renderHints & RenderHints.GroupTransform) == 0)
            {
                // Recurse on children
                int childrenCount = ve.hierarchy.childCount;
                for (int i = 0; i < childrenCount; i++)
                {
                    DepthFirstOnTransformOrSizeChanged(renderChain, ve, ve.hierarchy[i], dirtyID, device, isAncestorOfChangeSkinned, transformChanged, ref stats);
                }
            }
        }
 static void UpdateOrAllocate(ref MeshHandle data, int vertexCount, int indexCount, UIRenderDevice device, out NativeSlice <Vertex> verts, out NativeSlice <UInt16> indices, out UInt16 indexOffset, ref ChainBuilderStats stats)
 {
     if (data != null)
     {
         // Try to fit within the existing allocation, optionally we can change the condition
         // to be an exact match of size to guarantee continuity in draw ranges
         if (data.allocVerts.size >= vertexCount && data.allocIndices.size >= indexCount)
         {
             device.Update(data, (uint)vertexCount, (uint)indexCount, out verts, out indices, out indexOffset);
             stats.updatedMeshAllocations++;
         }
         else
         {
             // Won't fit in the existing allocated region, free the current one
             device.Free(data);
             data = device.Allocate((uint)vertexCount, (uint)indexCount, out verts, out indices, out indexOffset);
             stats.newMeshAllocations++;
         }
     }
     else
     {
         data = device.Allocate((uint)vertexCount, (uint)indexCount, out verts, out indices, out indexOffset);
         stats.newMeshAllocations++;
     }
 }
        public static bool NudgeVerticesToNewSpace(VisualElement ve, RenderChain renderChain, UIRenderDevice device)
        {
            k_NudgeVerticesMarker.Begin();

            Debug.Assert(!ve.renderChainData.disableNudging);

            Matrix4x4 newTransform;

            GetVerticesTransformInfo(ve, out newTransform);
            Matrix4x4 nudgeTransform = newTransform * ve.renderChainData.verticesSpace.inverse;

            // Attempt to reconstruct the absolute transform. If the result diverges from the absolute
            // considerably, then we assume that the vertices have become degenerate beyond restoration.
            // In this case we refuse to nudge, and ask for this element to be fully repainted to regenerate
            // the vertices without error.
            const float kMaxAllowedDeviation      = 0.0001f;
            Matrix4x4   reconstructedNewTransform = nudgeTransform * ve.renderChainData.verticesSpace;
            float       error;

            error  = Mathf.Abs(newTransform.m00 - reconstructedNewTransform.m00);
            error += Mathf.Abs(newTransform.m01 - reconstructedNewTransform.m01);
            error += Mathf.Abs(newTransform.m02 - reconstructedNewTransform.m02);
            error += Mathf.Abs(newTransform.m03 - reconstructedNewTransform.m03);
            error += Mathf.Abs(newTransform.m10 - reconstructedNewTransform.m10);
            error += Mathf.Abs(newTransform.m11 - reconstructedNewTransform.m11);
            error += Mathf.Abs(newTransform.m12 - reconstructedNewTransform.m12);
            error += Mathf.Abs(newTransform.m13 - reconstructedNewTransform.m13);
            error += Mathf.Abs(newTransform.m20 - reconstructedNewTransform.m20);
            error += Mathf.Abs(newTransform.m21 - reconstructedNewTransform.m21);
            error += Mathf.Abs(newTransform.m22 - reconstructedNewTransform.m22);
            error += Mathf.Abs(newTransform.m23 - reconstructedNewTransform.m23);
            if (error > kMaxAllowedDeviation)
            {
                k_NudgeVerticesMarker.End();
                return(false);
            }

            ve.renderChainData.verticesSpace = newTransform; // This is the new space of the vertices

            var job = new NudgeJobData
            {
                vertsBeforeUVDisplacement = ve.renderChainData.displacementUVStart,
                vertsAfterUVDisplacement  = ve.renderChainData.displacementUVEnd,
                transform = nudgeTransform
            };

            PrepareNudgeVertices(ve, device, ve.renderChainData.data, out job.src, out job.dst, out job.count);
            if (ve.renderChainData.closingData != null)
            {
                PrepareNudgeVertices(ve, device, ve.renderChainData.closingData, out job.closingSrc, out job.closingDst, out job.closingCount);
            }

            renderChain.jobManager.Add(ref job);

            k_NudgeVerticesMarker.End();
            return(true);
        }