Feature proposal - Make NativeList.ParallelWriter.Add(Range)NoResize return new length of the list

Hey,
recently I’ve been generating meshes with use of Burst jobs. One of the algorithm steps is generating vertices + triangles buffers. I found out that I could make whole thing parallel if only I had knowledge about index of recently added vertex position into NativeList. After some digging, I’ve modified sources of NativeList.ParallelWriter.Add(Range)NoResize and made it return new length of list. Given that I can easily have code like:

var index0 = vertices.AddNoResize(v0) - 1;
var index1 = vertices.AddNoResize(v1) - 1;
var index2 = vertices.AddNoResize(v2) - 1;

triangles.AddNoResize(math.int3(index0, index1, index2));

From the Collections perspective, there is no big change, since AddNoResize/AddRangeNoResize internally uses new length of the array to calculate idx local variable [var idx = Interlocked.Increment(ref ListData->Length) - 1;].

Obviously, the very same pattern could be applied to other collection’s ParallelWriters.

3 Likes

Hi! This is good idea. I’ll add it to our task list.

3 Likes

Would love this feature too!
Or is there any way to determine how full the list currently is, aka how much capacity there is left in the ParallelWriter?

@DragonCoder Information much space is left is volatile, so worthless in parallel job.

But you can “inject” needed extension method which returns new length/index of added element yourself by using of other trick I’ve described here:

Here is the code and simple package:

using System;
using System.Diagnostics;
using System.Threading;
using Unity.Collections.LowLevel.Unsafe;

namespace Unity.Collections
{
    public static class NativeListExtensions
    {
        /// <summary>
        /// Appends an element to the end of this list and returns index on which element was added.
        /// </summary>
        /// <param name="value">The value to add to the end of this list.</param>
        /// <remarks>
        /// Increments the length by 1 unless doing so would exceed the current capacity.
        /// </remarks>
        /// <returns>Index at which element was added</returns>
        /// <exception cref="Exception">Thrown if adding an element would exceed the capacity.</exception>
        public unsafe static int AddNoResizeEx<T>(this NativeList<T>.ParallelWriter writer, T value)
            where T : unmanaged
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.CheckWriteAndThrow(writer.m_Safety);
#endif
            var idx = Interlocked.Increment(ref writer.ListData->m_length) - 1;
            CheckSufficientCapacity(writer.ListData->Capacity, idx + 1);

            UnsafeUtility.WriteArrayElement(writer.ListData->Ptr, idx, value);
            return idx;
        }

        [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
        static void CheckSufficientCapacity(int capacity, int length)
        {
            if (capacity < length)
                throw new Exception($"Length {length} exceeds capacity Capacity {capacity}");
        }
    }
}

8149628–1058546–com.hacks.collections.zip (2.62 KB)

2 Likes