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.
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}");
}
}
}