Exemple #1
0
            void sort(NativeSlice<uint> array , NativeSlice<uint> tempArray , NativeSlice<int> Histogram, int digitIndex)
            {
                if (digitIndex < 0 || array.Length <= 1) return;

                int exp = (int)math.pow((double)multi, digitIndex);

                for (int i = 0; i < multi; i++)
                    Histogram[i] = 0;

                for (int i = 0; i < array.Length; i++)
                    Histogram[(int)((array[i] / exp) % multi)]++;

                for (int i = 1; i < multi; i++)
                    Histogram[i] += Histogram[i - 1];

                for (int i = 0; i < array.Length; i++)
                    tempArray[--Histogram[(int)((array[i] / exp) % multi)]] = array[i];

                for (int i = 0; i < array.Length; i++)
                    array[i] = tempArray[i];

                digitIndex--;
                var newHistogram = globalHistogram.Slice(multi * digitIndex, multi);
                int preHead = 0; int length = 0;
                for (int i = 1; i < multi; i++)
                {
                    preHead = Histogram[i - 1];
                    length = Histogram[i] - preHead;
                    sort(array.Slice(preHead, length), tempArray.Slice(preHead, length) , newHistogram, digitIndex);
                }
                preHead = Histogram[multi - 1];
                length = array.Length - preHead;
                sort(array.Slice(preHead, length), tempArray.Slice(preHead, length), newHistogram, digitIndex);
            }
    /// <summary>
    /// Recursively applies Haar transform up to given depth of iterations.
    /// </summary>
    /// <param name="a">Buffer containing input data</param>
    /// <param name="b">Additional buffer used in computation</param>
    /// <param name="depth">Pointer to buffer containing output data, which is either a or b.</param>
    /// <returns></returns>
    private static NativeSlice <float> Transform(NativeSlice <float> a, NativeSlice <float> b, int depth)
    {
        if (depth > MaxTransformDepth(a.Length))
        {
            throw new System.ArgumentException(string.Format("Depth {0} cannot exceed maximum depth of {1} for input with length {2}", depth, MaxTransformDepth(a.Length), a.Length));
        }

        for (int i = 0; i < depth; i++)
        {
            // transform on slices recursively, pingponging between the arrays
            // each level transforms the previous trend signal, which is the
            // left half of the input buffer
            int extents = a.Length / ((int)Mathf.Pow(2, i));
            Debug.Log("Extents: " + extents);
            Transform(a.Slice(0, extents), b.Slice(0, extents));
            Copy(a.Slice(extents), b.Slice(extents));

            var stringBuilder = new StringBuilder(1024);
            stringBuilder.Append("level " + i + " [");
            for (int j = 0; j < b.Length; j++)
            {
                stringBuilder.Append(math.round((b[j] / sqrt2)) + ", ");
            }
            stringBuilder.Append("]");
            Debug.Log(stringBuilder.ToString());

            // swap the buffers
            var temp = a;
            a = b;
            b = temp;
        }

        return(a);
    }
        public int Read(NativeSlice <float> buffer, NativeSlice <float> buffer2)
        {
            if (stopped)
            {
                return(0);
            }

            var read = 0;

            while (true)
            {
                // we need to remember how much was available this round
                var available  = samples.Length / channels - offset;
                var bufferSize = buffer.Length;

                // handle the easier scenario of the sample being longer than the buffer
                if (bufferSize < available)
                {
                    //if (buffer.Length != buffer2.Length)
                    //    throw new System.Exception($"{buffer.Length}!={buffer2.Length}");

                    // copy over the next block of data
                    buffer.CopyFrom(samples.Slice(offset, bufferSize));
                    buffer2.CopyFrom(samples.Slice(offset + samples.Length / channels, buffer2.Length));

                    // advance the sample forward
                    offset += bufferSize;

                    // the buffer is full
                    return((read + bufferSize) / channels);
                }

                // copy over the remaining sample data
                buffer.Slice(0, available)
                .CopyFrom(samples.Slice(offset, available));
                buffer2.Slice(0, available)
                .CopyFrom(samples.Slice(offset + samples.Length / channels, available));
                read += available;

                // reset the sample
                offset = 0;

                // stop if we are not set to loop
                if (!loop)
                {
                    stopped = true;
                    return(read / channels);
                }

                if (buffer.Length == available)
                {
                    return(read / channels);
                }

                // advance the buffer forward
                buffer = buffer.Slice(available);
            }
        }
    /// <summary>
    /// Applies one level of the Haar transform
    /// </summary>
    /// <param name="input">Input buffer</param>
    /// <param name="output">Output buffer containing [trend,fluctuations]</param>
    private static void Transform(NativeSlice <float> input, NativeSlice <float> output)
    {
        var trend = output.Slice(0, input.Length / 2);
        var fluct = output.Slice(input.Length / 2, input.Length / 2);

        for (int i = 0; i < trend.Length; i++)
        {
            trend[i] = (input[i * 2] + input[i * 2 + 1]) / sqrt2;
            fluct[i] = (input[i * 2] - input[i * 2 + 1]) / sqrt2;
        }
    }
        // read either mono or stereo, always convert to stereo interleaved
        static unsafe bool ReadSamples(SampleProvider provider, NativeSlice <float> destination)
        {
            if (!provider.Valid)
            {
                return(true);
            }

            var finished = false;

            // Read from SampleProvider and convert to stereo if needed
            var destinationFrames = destination.Length / 2;

            if (provider.ChannelCount == 2)
            {
                var read = provider.Read(destination.Slice(0, destination.Length));
                if (read < destinationFrames)
                {
                    for (var i = read; i < destinationFrames; i++)
                    {
                        destination[i] = 0;
                        destination[i + destinationFrames] = 0;
                    }

                    return(true);
                }
            }
            else
            {
                var buffer = destination.Slice(0, destinationFrames);
                var read   = provider.Read(buffer);

                if (read < destinationFrames)
                {
                    for (var i = read; i < destinationFrames; i++)
                    {
                        destination[i] = 0;
                    }

                    finished = true;
                }

                var left  = (float *)destination.GetUnsafePtr();
                var right = left + read;
                UnsafeUtility.MemCpy(right, left, read * UnsafeUtility.SizeOf <float>());
            }

            return(finished);
        }
Exemple #6
0
        public void FragmentPacket(NativeSlice <byte> packet, int sequence)
        {
            int packetSize = (int)packet.Length;                                  // should assert against too big for int16

            int numFragments = (int)((packetSize + (FRAG_SIZE - 1)) / FRAG_SIZE); // should assert against too many fragments here
            int cFragment    = 0;
            int skipAmount   = 0;

            while (packetSize != 0)
            {
                int currentFragSize = Math.Min(packetSize, FRAG_SIZE);

                FragmentedPacket tFrag = new FragmentedPacket();

                tFrag.ID          = sequence;
                tFrag.SequenceCnt = numFragments;
                tFrag.SequenceNum = ((cFragment + 1) == numFragments) ? (int)(-currentFragSize) : cFragment++;

                tFrag.packetData = packet.Slice(skipAmount, currentFragSize);

                fragmentedOutgoing.Enqueue(tFrag);

                skipAmount += currentFragSize;
                packetSize -= currentFragSize;
            }
            // globalPacketID++;
        }
        // read either mono or stereo, always convert to stereo interleaved
        static bool ReadSamples(SampleProvider provider, NativeSlice <float> destination)
        {
            if (!provider.Valid)
            {
                return(true);
            }

            var finished = false;

            // Read from SampleProvider and convert to interleaved stereo if needed
            if (provider.ChannelCount == 2)
            {
                var read = provider.Read(destination.Slice(0, destination.Length));
                if (read < destination.Length / 2)
                {
                    for (var i = read * 2; i < destination.Length; i++)
                    {
                        destination[i] = 0;
                    }
                    return(true);
                }
            }
            else
            {
                var n      = destination.Length / 2;
                var buffer = destination.Slice(0, n);
                var read   = provider.Read(buffer);

                if (read < n)
                {
                    for (var i = read; i < n; i++)
                    {
                        destination[i] = 0;
                    }

                    finished = true;
                }

                for (var i = n - 1; i >= 0; i--)
                {
                    destination[i * 2 + 0] = destination[i];
                    destination[i * 2 + 1] = destination[i];
                }
            }

            return(finished);
        }
Exemple #8
0
 public void Execute(int startIndex, int count)
 {
     // Write results to proper slice!
     for (int index = startIndex; index < startIndex + count; ++index)
     {
         NativeSlice <int> resultsSlice = m_results.Slice(index * m_k, m_k);
         m_container.QueryKNearest(m_queryPositions[index], resultsSlice);
     }
 }
        public void CopyTo(NativeSlice <Entity> dst, int startIndex = 0)
        {
            NativeArray <Entity> chunkArray;

            for (int i = 0; i < dst.Length; i += chunkArray.Length)
            {
                chunkArray = this.GetChunkArray(startIndex + i, dst.Length - i);
                dst.Slice <Entity>(i, chunkArray.Length).CopyFrom(chunkArray);
            }
        }
        private NativeSlice <byte> ReadNext(int length)
        {
            if (_offset + length > _buffer.Length)
            {
                throw new TinyMsgPackDeserializeException("MsgPackReader - out of buffer range");
            }
            var slice = _buffer.Slice(_offset, length);

            _offset += length;
            return(slice);
        }
Exemple #11
0
        public void Execute(int startIndex, int count)
        {
            var temp = KnnContainer.KnnQueryTemp.Create(m_k);

            // Write results to proper slice!
            for (int index = startIndex; index < startIndex + count; ++index)
            {
                var resultsSlice = m_results.Slice(index * m_k, m_k);
                m_container.KNearest(m_queryPositions[index], resultsSlice, ref temp);
            }
        }
Exemple #12
0
        public void CopyTo(NativeSlice <Entity> dst, int startIndex = 0)
        {
            var copiedCount = 0;

            while (copiedCount < dst.Length)
            {
                var chunkArray = GetChunkArray(startIndex + copiedCount, dst.Length - copiedCount);
                dst.Slice(copiedCount, chunkArray.Length).CopyFrom(chunkArray);

                copiedCount += chunkArray.Length;
            }
        }
Exemple #13
0
        public NativeSlice <byte> Receive(NetworkPipelineContext ctx, NativeSlice <byte> inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate)
        {
            needsResume = false;
            var reader  = new DataStreamReader(inboundBuffer);
            var context = default(DataStreamReader.Context);

            unsafe
            {
                var    oldSequenceId = (int *)ctx.internalProcessBuffer.GetUnsafePtr();
                ushort sequenceId    = reader.ReadUShort(ref context);

                if (SequenceHelpers.GreaterThan16(sequenceId, (ushort)*oldSequenceId))
                {
                    *oldSequenceId = sequenceId;
                    // Skip over the part of the buffer which contains the header
                    return(inboundBuffer.Slice(sizeof(ushort)));
                }
            }
            return(default(NativeSlice <byte>));
        }
Exemple #14
0
        // Push audio data to the FIFO buffer.
        public void Push(NativeSlice <float> data)
        {
            var length = data.Length;

            if (length == 0)
            {
                return;
            }

            if (length < _N)
            {
                // The data is smaller than the buffer: Dequeue and copy
                var part = _N - length;
                NativeArray <float> .Copy(_I, _N - part, _I, 0, part);

                data.CopyTo(_I.GetSubArray(part, length));
            }
            else
            {
                // The data is larger than the buffer: Simple fill
                data.Slice(length - _N).CopyTo(_I);
            }
        }
Exemple #15
0
        public unsafe void Update(float dt, int nodeIndex, TreeDef treeDef, NativeSlice <NodeState> state, NativeSlice <byte> data)
        {
            Profiler.BeginSample("TNode::Update");
            var arr = data.SliceConvert <T>();

            for (int i = 0; i < arr.Length; ++i)
            {
                var h = new NodeStateHandle(nodeIndex, state.Slice(i * treeDef.Nodes.Length, treeDef.Nodes.Length), treeDef);
                var v = arr[i];

                if (h.State == NodeState.Activating)
                {
                    h.State = Activate(h, ref v);
                }

                if (h.State == NodeState.Running)
                {
                    h.State = Update(dt, h, ref v);
                    arr[i]  = v;
                }
            }
            Profiler.EndSample();
        }
Exemple #16
0
        public void Update(float dt, int nodeIndex, TreeDef treeDef, NativeSlice <NodeState> state, NativeSlice <byte> data)
        {
            int nStates = state.Length / treeDef.Nodes.Length;

            for (int i = 0; i < nStates; ++i)
            {
                var h = new NodeStateHandle(nodeIndex, state.Slice(i * treeDef.Nodes.Length, treeDef.Nodes.Length), treeDef);

                if (!h.State.Active())
                {
                    continue;
                }

                if (h.State == NodeState.Activating)
                {
                    h.State = Activate(h);
                }

                if (h.State == NodeState.Running)
                {
                    h.State = Update(dt, h);
                }
            }
        }
 internal static void Copy <T>(this NativeArray <T> dest, int destIdx, NativeSlice <T> src, int srcIdx, int count) where T : struct
 {
     dest.Slice(destIdx, count).CopyFrom(src.Slice(srcIdx, count));
 }
        public unsafe static UIRStylePainter.ClosingInfo PaintElement(RenderChain renderChain, VisualElement ve, ref ChainBuilderStats stats)
        {
            var device = renderChain.device;

            var isClippingWithStencil  = ve.renderChainData.clipMethod == ClipMethod.Stencil;
            var isClippingWithScissors = ve.renderChainData.clipMethod == ClipMethod.Scissor;
            var isGroup = (ve.renderHints & RenderHints.GroupTransform) != 0; // Groups need to push view and scissors

            if ((UIRUtility.IsElementSelfHidden(ve) && !isClippingWithStencil && !isClippingWithScissors && !isGroup) || ve.renderChainData.isHierarchyHidden)
            {
                if (ve.renderChainData.data != null)
                {
                    device.Free(ve.renderChainData.data);
                    ve.renderChainData.data = null;
                }
                if (ve.renderChainData.firstCommand != null)
                {
                    ResetCommands(renderChain, ve);
                }

                renderChain.ResetTextures(ve);

                return(new UIRStylePainter.ClosingInfo());
            }

            // Retain our command insertion points if possible, to avoid paying the cost of finding them again
            RenderChainCommand oldCmdPrev = ve.renderChainData.firstCommand?.prev;
            RenderChainCommand oldCmdNext = ve.renderChainData.lastCommand?.next;
            RenderChainCommand oldClosingCmdPrev, oldClosingCmdNext;
            bool commandsAndClosingCommandsWereConsecutive = (ve.renderChainData.firstClosingCommand != null) && (oldCmdNext == ve.renderChainData.firstClosingCommand);

            if (commandsAndClosingCommandsWereConsecutive)
            {
                oldCmdNext        = ve.renderChainData.lastClosingCommand.next;
                oldClosingCmdPrev = oldClosingCmdNext = null;
            }
            else
            {
                oldClosingCmdPrev = ve.renderChainData.firstClosingCommand?.prev;
                oldClosingCmdNext = ve.renderChainData.lastClosingCommand?.next;
            }
            Debug.Assert(oldCmdPrev?.owner != ve);
            Debug.Assert(oldCmdNext?.owner != ve);
            Debug.Assert(oldClosingCmdPrev?.owner != ve);
            Debug.Assert(oldClosingCmdNext?.owner != ve);

            ResetCommands(renderChain, ve);
            renderChain.ResetTextures(ve);

            k_GenerateEntries.Begin();
            var painter = renderChain.painter;

            painter.Begin(ve);

            if (ve.visible)
            {
                painter.DrawVisualElementBackground();
                painter.DrawVisualElementBorder();
                painter.ApplyVisualElementClipping();

                InvokeGenerateVisualContent(ve, painter.meshGenerationContext);
            }
            else
            {
                // Even though the element hidden, we still have to push the stencil shape or setup the scissors in case any children are visible.
                if (isClippingWithScissors || isClippingWithStencil)
                {
                    painter.ApplyVisualElementClipping();
                }
            }
            k_GenerateEntries.End();

            MeshHandle data = ve.renderChainData.data;

            if (painter.totalVertices > device.maxVerticesPerPage)
            {
                Debug.LogError($"A {nameof(VisualElement)} must not allocate more than {device.maxVerticesPerPage } vertices.");

                if (data != null)
                {
                    device.Free(data);
                    data = null;
                }

                renderChain.ResetTextures(ve);

                // Restart without drawing anything.
                painter.Reset();
                painter.Begin(ve);
            }

            // Convert entries to commands.
            var entries = painter.entries;

            if (entries.Count > 0)
            {
                NativeSlice <Vertex> verts   = new NativeSlice <Vertex>();
                NativeSlice <UInt16> indices = new NativeSlice <UInt16>();
                UInt16 indexOffset           = 0;

                if (painter.totalVertices > 0)
                {
                    UpdateOrAllocate(ref data, painter.totalVertices, painter.totalIndices, device, out verts, out indices, out indexOffset, ref stats);
                }

                int vertsFilled = 0, indicesFilled = 0;

                RenderChainCommand cmdPrev = oldCmdPrev, cmdNext = oldCmdNext;
                if (oldCmdPrev == null && oldCmdNext == null)
                {
                    FindCommandInsertionPoint(ve, out cmdPrev, out cmdNext);
                }

                // Vertex data, lazily computed
                bool      vertexDataComputed = false;
                Matrix4x4 transform          = Matrix4x4.identity;
                Color32   xformClipPages     = new Color32(0, 0, 0, 0);
                Color32   ids                  = new Color32(0, 0, 0, 0);
                Color32   addFlags             = new Color32(0, 0, 0, 0);
                Color32   opacityPage          = new Color32(0, 0, 0, 0);
                Color32   textCoreSettingsPage = new Color32(0, 0, 0, 0);

                k_ConvertEntriesToCommandsMarker.Begin();
                int firstDisplacementUV = -1, lastDisplacementUVPlus1 = -1;
                foreach (var entry in painter.entries)
                {
                    if (entry.vertices.Length > 0 && entry.indices.Length > 0)
                    {
                        if (!vertexDataComputed)
                        {
                            vertexDataComputed = true;
                            GetVerticesTransformInfo(ve, out transform);
                            ve.renderChainData.verticesSpace = transform; // This is the space for the generated vertices below
                        }

                        Color32 transformData        = renderChain.shaderInfoAllocator.TransformAllocToVertexData(ve.renderChainData.transformID);
                        Color32 opacityData          = renderChain.shaderInfoAllocator.OpacityAllocToVertexData(ve.renderChainData.opacityID);
                        Color32 textCoreSettingsData = renderChain.shaderInfoAllocator.TextCoreSettingsToVertexData(ve.renderChainData.textCoreSettingsID);
                        xformClipPages.r = transformData.r;
                        xformClipPages.g = transformData.g;
                        ids.r            = transformData.b;
                        opacityPage.r    = opacityData.r;
                        opacityPage.g    = opacityData.g;
                        ids.b            = opacityData.b;
                        if (entry.isTextEntry)
                        {
                            // It's important to avoid writing these values when the vertices aren't for text,
                            // as these settings are shared with the vector graphics gradients.
                            // The same applies to the CopyTransformVertsPos* methods below.
                            textCoreSettingsPage.r = textCoreSettingsData.r;
                            textCoreSettingsPage.g = textCoreSettingsData.g;
                            ids.a = textCoreSettingsData.b;
                        }

                        Color32 clipRectData = renderChain.shaderInfoAllocator.ClipRectAllocToVertexData(entry.clipRectID);
                        xformClipPages.b = clipRectData.r;
                        xformClipPages.a = clipRectData.g;
                        ids.g            = clipRectData.b;
                        addFlags.r       = (byte)entry.addFlags;

                        float textureId = entry.texture.ConvertToGpu();

                        // Copy vertices, transforming them as necessary
                        var targetVerticesSlice = verts.Slice(vertsFilled, entry.vertices.Length);

                        if (entry.uvIsDisplacement)
                        {
                            if (firstDisplacementUV < 0)
                            {
                                firstDisplacementUV     = vertsFilled;
                                lastDisplacementUVPlus1 = vertsFilled + entry.vertices.Length;
                            }
                            else if (lastDisplacementUVPlus1 == vertsFilled)
                            {
                                lastDisplacementUVPlus1 += entry.vertices.Length;
                            }
                            else
                            {
                                ve.renderChainData.disableNudging = true;  // Disjoint displacement UV entries, we can't keep track of them, so disable nudging optimization altogether
                            }
                        }

                        int  entryIndexCount         = entry.indices.Length;
                        int  entryIndexOffset        = vertsFilled + indexOffset;
                        var  targetIndicesSlice      = indices.Slice(indicesFilled, entryIndexCount);
                        bool shapeWindingIsClockwise = UIRUtility.ShapeWindingIsClockwise(entry.maskDepth, entry.stencilRef);
                        bool transformFlipsWinding   = ve.renderChainData.worldFlipsWinding;

                        var job = new ConvertMeshJobData
                        {
                            vertSrc              = (IntPtr)entry.vertices.GetUnsafePtr(),
                            vertDst              = (IntPtr)targetVerticesSlice.GetUnsafePtr(),
                            vertCount            = targetVerticesSlice.Length,
                            transform            = transform,
                            transformUVs         = entry.uvIsDisplacement ? 1 : 0,
                            xformClipPages       = xformClipPages,
                            ids                  = ids,
                            addFlags             = addFlags,
                            opacityPage          = opacityPage,
                            textCoreSettingsPage = textCoreSettingsPage,
                            isText               = entry.isTextEntry ? 1 : 0,
                            textureId            = textureId,

                            indexSrc    = (IntPtr)entry.indices.GetUnsafePtr(),
                            indexDst    = (IntPtr)targetIndicesSlice.GetUnsafePtr(),
                            indexCount  = targetIndicesSlice.Length,
                            indexOffset = entryIndexOffset,
                            flipIndices = shapeWindingIsClockwise == transformFlipsWinding ? 1 : 0
                        };
                        renderChain.jobManager.Add(ref job);

                        if (entry.isClipRegisterEntry)
                        {
                            painter.LandClipRegisterMesh(targetVerticesSlice, targetIndicesSlice, entryIndexOffset);
                        }

                        var cmd = InjectMeshDrawCommand(renderChain, ve, ref cmdPrev, ref cmdNext, data, entryIndexCount, indicesFilled, entry.material, entry.texture, entry.stencilRef);
                        if (entry.isTextEntry)
                        {
                            // Set font atlas texture gradient scale
                            cmd.state.sdfScale = entry.fontTexSDFScale;
                        }

                        vertsFilled   += entry.vertices.Length;
                        indicesFilled += entryIndexCount;
                    }
                    else if (entry.customCommand != null)
                    {
                        InjectCommandInBetween(renderChain, entry.customCommand, ref cmdPrev, ref cmdNext);
                    }
                    else
                    {
                        Debug.Assert(false); // Unable to determine what kind of command to generate here
                    }
                }

                if (!ve.renderChainData.disableNudging && (firstDisplacementUV >= 0))
                {
                    ve.renderChainData.displacementUVStart = firstDisplacementUV;
                    ve.renderChainData.displacementUVEnd   = lastDisplacementUVPlus1;
                }

                k_ConvertEntriesToCommandsMarker.End();
            }
            else if (data != null)
            {
                device.Free(data);
                data = null;
            }
            ve.renderChainData.data = data;

            if (painter.closingInfo.clipperRegisterIndices.Length == 0 && ve.renderChainData.closingData != null)
            {
                // No more closing data needed, so free it now
                device.Free(ve.renderChainData.closingData);
                ve.renderChainData.closingData = null;
            }

            if (painter.closingInfo.needsClosing)
            {
                k_GenerateClosingCommandsMarker.Begin();
                RenderChainCommand cmdPrev = oldClosingCmdPrev, cmdNext = oldClosingCmdNext;
                if (commandsAndClosingCommandsWereConsecutive)
                {
                    cmdPrev = ve.renderChainData.lastCommand;
                    cmdNext = cmdPrev.next;
                }
                else if (cmdPrev == null && cmdNext == null)
                {
                    FindClosingCommandInsertionPoint(ve, out cmdPrev, out cmdNext);
                }

                if (painter.closingInfo.PopDefaultMaterial)
                {
                    var cmd = renderChain.AllocCommand();
                    cmd.type    = CommandType.PopDefaultMaterial;
                    cmd.closing = true;
                    cmd.owner   = ve;
                    InjectClosingCommandInBetween(renderChain, cmd, ref cmdPrev, ref cmdNext);
                }

                if (painter.closingInfo.blitAndPopRenderTexture)
                {
                    {
                        var cmd = renderChain.AllocCommand();
                        cmd.type           = CommandType.BlitToPreviousRT;
                        cmd.closing        = true;
                        cmd.owner          = ve;
                        cmd.state.material = GetBlitMaterial(ve.subRenderTargetMode);
                        Debug.Assert(cmd.state.material != null);
                        InjectClosingCommandInBetween(renderChain, cmd, ref cmdPrev, ref cmdNext);
                    }

                    {
                        var cmd = renderChain.AllocCommand();
                        cmd.type    = CommandType.PopRenderTexture;
                        cmd.closing = true;
                        cmd.owner   = ve;
                        InjectClosingCommandInBetween(renderChain, cmd, ref cmdPrev, ref cmdNext);
                    }
                }

                if (painter.closingInfo.clipperRegisterIndices.Length > 0)
                {
                    var cmd = InjectClosingMeshDrawCommand(renderChain, ve, ref cmdPrev, ref cmdNext, null, 0, 0, null, TextureId.invalid, painter.closingInfo.maskStencilRef);
                    painter.LandClipUnregisterMeshDrawCommand(cmd); // Placeholder command that will be filled actually later
                }
                if (painter.closingInfo.popViewMatrix)
                {
                    var cmd = renderChain.AllocCommand();
                    cmd.type    = CommandType.PopView;
                    cmd.closing = true;
                    cmd.owner   = ve;
                    InjectClosingCommandInBetween(renderChain, cmd, ref cmdPrev, ref cmdNext);
                }
                if (painter.closingInfo.popScissorClip)
                {
                    var cmd = renderChain.AllocCommand();
                    cmd.type    = CommandType.PopScissor;
                    cmd.closing = true;
                    cmd.owner   = ve;
                    InjectClosingCommandInBetween(renderChain, cmd, ref cmdPrev, ref cmdNext);
                }
                k_GenerateClosingCommandsMarker.End();
            }

            // When we have a closing mesh, we must have an opening mesh. At least we assumed where we decide
            // whether we must nudge or not: we only test whether the opening mesh is non-null.
            Debug.Assert(ve.renderChainData.closingData == null || ve.renderChainData.data != null);

            var closingInfo = painter.closingInfo;

            painter.Reset();
            return(closingInfo);
        }