Unity thinks this node is managed. What am I doing wrong?

public unsafe struct UnManagedNode
{
    // A* ready values
    public bool visited;
    public float finalCost;
    public float fromEndCost;
    public float fromStartCost;
    public int parent;

    public UnManagedBounds thisBounds;
    public NativeArray<int> connections;

    public int id;

    public UnManagedNode(UnManagedBounds thisBounds, NativeArray<int> connections, int id)
    {
        this.visited = false;
        this.fromStartCost = float.PositiveInfinity;
        this.fromEndCost = float.PositiveInfinity;
        this.finalCost = float.PositiveInfinity;
        this.id = id;
        this.parent = -1;

        this.thisBounds = thisBounds;
        this.connections = connections;
    }

    public void Dispose()
    {
        connections.Dispose();
    }
}

then there’s the UnManagedBounds:

using Unity.Burst;
using Unity.Mathematics;

[BurstCompile]
public unsafe struct UnManagedBounds
{
    public float3 center;
    public float3 extents;

    public UnManagedBounds(float3 center, float3 extents)
    {
        this.center = center;
        this.extents = extents;
    }

    public bool Contains(float3 point)
    {
        float3 min = center - extents;
        float3 max = center + extents;
        return point.x >= min.x && point.y >= min.y && point.z >= min.z &&
               point.x <= max.x && point.y <= max.y && point.z <= max.z;
    }

    public float3 ClosestPoint(float3 point)
    {
        float3 min = center - extents;
        float3 max = center + extents;
        float3 closestPoint = point;
        closestPoint.x = math.clamp(closestPoint.x, min.x, max.x);
        closestPoint.y = math.clamp(closestPoint.y, min.y, max.y);
        closestPoint.z = math.clamp(closestPoint.z, min.z, max.z);
        return closestPoint;
    }
}

Unity throws the following error:

InvalidOperationException: UnManagedNode used in NativeArray<UnManagedNode> must be unmanaged (contain no managed types).
Unity.Collections.NativeArray`1[T].IsUnmanagedAndThrow () (at <6f7018b8b8c149e68c4a65a05ac289be>:0)
Unity.Collections.NativeArray`1[T].Allocate (System.Int32 length, Unity.Collections.Allocator allocator, Unity.Collections.NativeArray`1[T]& array) (at <6f7018b8b8c149e68c4a65a05ac289be>:0)
Unity.Collections.NativeArray`1[T]..ctor (UnManagedNode[] array, Unity.Collections.Allocator allocator) (at <6f7018b8b8c149e68c4a65a05ac289be>:0)
OctTree._SetupAndCreateEdges () (at Assets/Game/Scripts/OctTree/OctTree.cs:235)
OctTree.GenerateOctTreeTraversble () (at Assets/Game/Scripts/OctTree/OctTree.cs:149)
OctTreeEditor.OnInspectorGUI () (at Assets/Game/Editor/OctTree/OctTreeEditor.cs:112)
UnityEditor.UIElements.InspectorElement+<>c__DisplayClass72_0.<CreateInspectorElementUsingIMGUI>b__0 () (at <5d5ebefe97114215928ac1d9cd096522>:0)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&)

also, this I believe causes the issue:

NativeArray<UnManagedNode> nativeAllNodes = new NativeArray<UnManagedNode>(allNodes.ToArray(), Allocator.Persistent);

Okay so I actually fixed that by doing this, but got this instead:

NativeArray<UnManagedNode> nativeAllNodes = allNodes.ToNativeArray(Allocator.Persistent);
InvalidOperationException: The Unity.Collections.NativeArray`1[UnManagedNode] PathFindingJob.allNodes can not be accessed. Nested native containers are illegal in jobs.
Unity.Jobs.LowLevel.Unsafe.JobsUtility.Schedule (Unity.Jobs.LowLevel.Unsafe.JobsUtility+JobScheduleParameters& parameters) (at <6f7018b8b8c149e68c4a65a05ac289be>:0)
Unity.Jobs.IJobExtensions.Schedule[T] (T jobData, Unity.Jobs.JobHandle dependsOn) (at <6f7018b8b8c149e68c4a65a05ac289be>:0)

Yes, ToArray() will create a managed array.
The nesting not allowed is a well known limitation but it‘s easily overcome by replacing the nested native collection with its UnsafeNative counterpart. For NativeArray this would be UnsafeNativeList. The unsafe collections are „unsafe“ in the sense that no checks are performed such as bounds and I believe also aliasing and others that can cause issues in multithreaded code.

UnsafeList. It’s called UnsafeList, man. Bounds / key checks are included as with native containers when collection checks are on. It’s basically just excluding usage of AtomicSafetyHandle for read/write safety.
https://docs.unity3d.com/Packages/com.unity.collections@2.4/manual/collections-overview.html

For what it’s worth, this restriction is listed on some random page about custom containers.

You might alternatively (preferably) create a separate data representation of UnManagedNode purely for passing into jobs which swaps out the NativeArray for a pointer and length, since nested safety is out of the question anyway.

1 Like