Invalid mesh conversion

Certain submesh constructions can cause PolySpatial to throw errors in editor, and generate invalid index buffers in simulator/device, resulting in a hard reset on the device.

The error thrown is

ArgumentOutOfRangeException: sub array range 108-161 is outside the range of the native array 0-155
Parameter name: length
Unity.Collections.NativeArray`1[T].CheckGetSubArrayArguments (System.Int32 start, System.Int32 length) (at /Users/bokken/build/output/unity/unity/Runtime/Export/NativeArray/NativeArray.cs:752)
Unity.Collections.NativeArray`1[T].GetSubArray (System.Int32 start, System.Int32 length) (at /Users/bokken/build/output/unity/unity/Runtime/Export/NativeArray/NativeArray.cs:763)
Unity.PolySpatial.Internals.MeshConversionHelpers.ConvertMeshAssetToPolySpatialMesh (UnityEngine.Mesh uMesh, System.Action`1[T] postConversionCallback) (at /Users/bokken/build/output/unity/quantum/Packages/com.unity.polyspatial/Runtime/Rendering/MeshConversionHelpers.cs:171)
Unity.PolySpatial.Internals.LocalAssetManager.ProcessChangedAsset (Unity.PolySpatial.Internals.AssetRepresentation representation) (at /Users/bokken/build/output/unity/quantum/Packages/com.unity.polyspatial/Runtime/AssetManagement/LocalAssetManager.cs:930)

This occurs for meshes that have submeshes that do not entirely cover the range of the index buffer. For example (contrived example):

// .. set up any vertex data here
var indices = new ushort[30]; // 30 indices
mesh.SetIndexBufferParams(indices.Length, IndexFormat.UInt16);
mesh.SetIndexBufferData(indices, 0, 0, indices.Length);
mesh.subMeshCount = 1;
mesh.SetSubMesh(0, new SubMeshDescriptor(21, 9), MeshUpdateFlags.Default); // Add a submesh referencing indices 21-30

The error occurs because MeshConversionHelpers.ConvertMeshAssetToPolySpatialMesh allocates the index array to be the size of total number of indices referenced by the submeshes, and then attempts to write into it using the index addressing of the submeshes:

(Decompiled):

			for (int subMeshIndex = 0; subMeshIndex < ((MeshData)(ref meshData)).get_subMeshCount(); subMeshIndex++)
			{
				SubMeshDescriptor subMeshDescriptor2 = ((MeshData)(ref meshData)).GetSubMesh(subMeshIndex);
				PolySpatialSubMesh polySpatialSubMesh = default(PolySpatialSubMesh);
				polySpatialSubMesh.indexStart = ((SubMeshDescriptor)(ref subMeshDescriptor2)).get_indexStart();
				polySpatialSubMesh.indexCount = ((SubMeshDescriptor)(ref subMeshDescriptor2)).get_indexCount();
				polySpatialSubMesh.vertexBaseIndex = ((SubMeshDescriptor)(ref subMeshDescriptor2)).get_baseVertex();
				polySpatialSubMesh.topology = ((SubMeshDescriptor)(ref subMeshDescriptor2)).get_topology().ToPlatform();
				PolySpatialSubMesh subMesh = polySpatialSubMesh;
				subMeshes.set_Item(subMeshIndex, subMesh);
				totalIndices += ((SubMeshDescriptor)(ref subMeshDescriptor2)).get_indexCount();
			}
            if ((int)((MeshData)(ref meshData)).get_indexFormat() == 0)
			{
				convertedMesh.indices16 = new Nullable<NativeArray<ushort>>(new NativeArray<ushort>(totalIndices, (Allocator)2, (NativeArrayOptions)1));
				for (int subMeshIndex2 = 0; subMeshIndex2 < ((MeshData)(ref meshData)).get_subMeshCount(); subMeshIndex2++)
				{
					PolySpatialSubMesh subMeshDescriptor3 = subMeshes.get_Item(subMeshIndex2);
					NativeArray<ushort> subArray2 = convertedMesh.indices16.get_Value().GetSubArray(subMeshDescriptor3.indexStart, subMeshDescriptor3.indexCount);
					((MeshData)(ref meshData)).GetIndices(subArray2, subMeshIndex2, true);
				}
			}

The simplest fix is to set totalIndices to the max of indexStart + indexCount instead of the sum of indexCount.

Wow, great sleuthing; thanks! We will add this fix ASAP.

Cheers. I actually continued to encounter errors with submeshes even after modifying my code to only generate submeshes with adjacent usage in the index buffer – using more than one submesh would reliably crash the simulator. Rather than investigate further I’ve simply stopped using submeshes altogether for my procedural meshes, and split them into separate meshes (it looks like this is what RealityKit requires anyway?).

We convert the submeshes into different MeshResource.Parts that share the same buffers (at least, we assign the same buffers to positions, normals, etc.–it’s not clear if this means they’re actually shared on the GPU) and use triangleIndices extracted from the Unity index buffer ranges for each submesh.

If you could submit a bug report with an example of a submesh that’s crashing (and let me know the incident number), that would help greatly. I’ll try a basic submesh setup just to see if that reproduces the issue.

I was able to get a basic submesh with two materials working just as a sanity check:

using UnityEngine;

public class SubMeshCreator : MonoBehaviour
{
    void Start()
    {
        var mesh = new Mesh();
        mesh.vertices = new[]
        {
            new Vector3(0, 0, 0),
            new Vector3(0, 1, 0),
            new Vector3(1, 1, 0),
            new Vector3(1, 0, 0),
            new Vector3(0, 0, 1),
            new Vector3(0, 1, 1),
            new Vector3(1, 1, 1),
            new Vector3(1, 0, 1),
        };
        mesh.subMeshCount = 2;
        mesh.SetTriangles(new[] { 0, 1, 2, 2, 3, 0 }, 0);
        mesh.SetTriangles(new[] { 4, 5, 6, 6, 7, 4 }, 1);

        GetComponent<MeshFilter>().sharedMesh = mesh;
    }
}

Definitely submit a bug report if you have a submesh that crashes consistently.

Sorry I don’t currently have time to put together a repro case. It’s possible that the issue relates to updating meshes in-place (spitballing: perhaps changing the number of submeshes on the mesh causes a mismatch between the materials array and the parts array which is desynchronized).

Looks like that was the issue – easy to reproduce using your example with the MeshRenderer only having 1 material instead of 2. I’ve submitted IN-58968 which reproduces this.

OK; thanks for the bug report!

Thanks to your bug report, I was able to reproduce this easily. We’ll get in a fix that reproduces Unity’s behavior (submeshes beyond the material count will be invisible).

1 Like