Simple mesh creation fails while setting a submesh

Hi,
i am working on a mesh generation algorithm using the advanced mesh API. When setting the submesh, I either get an error like
Index buffer element #0 (value 816551072) is out of bounds; mesh only has 4 vertices.
This makes sense so far, since the index buffer has just been allocated and still contains arbitrary data. I am actually using the flag MeshUpdateFlags.DontValidateIndices when setting the SubmeshDescriptor. However when I do this, Unity will crash completely. I hope you can give me advice, I just cannot find the reason for this!

Here is a simplified version of my code, with which it is possible to reproduce this error:

using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class TestSubmeshCreation : MonoBehaviour
{

    public void Generate()
    {
        int vertexCount = 4;
        int indexCount = 6;
       
        Mesh.MeshDataArray meshDataArray = Mesh.AllocateWritableMeshData(1);
        Mesh.MeshData meshData = meshDataArray[0];
        NativeArray<VertexAttributeDescriptor> descriptor = new(
                    4, Allocator.Temp, NativeArrayOptions.UninitializedMemory
                );
        try
        {
            descriptor[0] = new VertexAttributeDescriptor(dimension: 3);
            descriptor[1] = new VertexAttributeDescriptor(
                VertexAttribute.Normal, dimension: 3
            );
            descriptor[2] = new VertexAttributeDescriptor(
                VertexAttribute.Tangent, dimension: 4
            );
            descriptor[3] = new VertexAttributeDescriptor(
                VertexAttribute.TexCoord0, dimension: 2
            );
            meshData.SetVertexBufferParams(vertexCount, descriptor);
       
            meshData.SetIndexBufferParams(indexCount, IndexFormat.UInt32);

            meshData.subMeshCount = 1;
            int startIndex = 0;

            var subMeshDescriptor = new SubMeshDescriptor(startIndex, indexCount);
            meshData.SetSubMesh(
                0, subMeshDescriptor
                //MeshUpdateFlags.DontValidateIndices
            );
            var mesh = GetComponent<MeshFilter>().mesh;
            Mesh.ApplyAndDisposeWritableMeshData(meshDataArray, mesh);
        }
        finally
        {
            meshDataArray.Dispose();
            descriptor.Dispose();
        }

    }

}

Here is the stack trace from the crash log:

Assertion failed on expression: ‘(int)lastVertex < data.GetVertexCount()’
UnityEngine.StackTraceUtility:ExtractStackTrace ()
UnityEngine.Mesh/MeshData:SetSubMeshImpl (intptr,int,UnityEngine.Rendering.SubMeshDescriptor,UnityEngine.Rendering.MeshUpdateFlags)
UnityEngine.Mesh/MeshData:SetSubMesh (int,UnityEngine.Rendering.SubMeshDescriptor,UnityEngine.Rendering.MeshUpdateFlags)
TestSubmeshCreation:Generate () (at Assets/Scripts/TestSubmeshCreation.cs:49)
LiquidPlanet.EditorUI.TestSubmeshCreationInspector:OnInspectorGUI () (at Assets/Scripts/EditorUI/TestSubmeshCreationInspector.cs:35)
UnityEditor.UIElements.InspectorElement/<>c__DisplayClass59_0:b__0 ()
UnityEngine.UIElements.IMGUIContainer:smile:oOnGUI (UnityEngine.Event,UnityEngine.Matrix4x4,UnityEngine.Rect,bool,UnityEngine.Rect,System.Action,bool)
UnityEngine.UIElements.IMGUIContainer:HandleIMGUIEvent (UnityEngine.Event,UnityEngine.Matrix4x4,UnityEngine.Rect,System.Action,bool)
UnityEngine.UIElements.IMGUIContainer:HandleIMGUIEvent (UnityEngine.Event,System.Action,bool)
UnityEngine.UIElements.IMGUIContainer:HandleIMGUIEvent (UnityEngine.Event,bool)
UnityEngine.UIElements.IMGUIContainer:SendEventToIMGUIRaw (UnityEngine.UIElements.EventBase,bool,bool)
UnityEngine.UIElements.IMGUIContainer:SendEventToIMGUI (UnityEngine.UIElements.EventBase,bool,bool)
UnityEngine.UIElements.IMGUIContainer:HandleEvent (UnityEngine.UIElements.EventBase)
UnityEngine.UIElements.CallbackEventHandler:HandleEventAtTargetPhase (UnityEngine.UIElements.EventBase)
UnityEngine.UIElements.MouseCaptureDispatchingStrategy:smile:ispatchEvent (UnityEngine.UIElements.EventBase,UnityEngine.UIElements.IPanel)
UnityEngine.UIElements.EventDispatcher:ApplyDispatchingStrategies (UnityEngine.UIElements.EventBase,UnityEngine.UIElements.IPanel,bool)
UnityEngine.UIElements.EventDispatcher:ProcessEvent (UnityEngine.UIElements.EventBase,UnityEngine.UIElements.IPanel)
UnityEngine.UIElements.EventDispatcher:ProcessEventQueue ()
UnityEngine.UIElements.EventDispatcher:OpenGate ()
UnityEngine.UIElements.EventDispatcherGate:smile:ispose ()
UnityEngine.UIElements.EventDispatcher:ProcessEvent (UnityEngine.UIElements.EventBase,UnityEngine.UIElements.IPanel)
UnityEngine.UIElements.EventDispatcher:smile:ispatch (UnityEngine.UIElements.EventBase,UnityEngine.UIElements.IPanel,UnityEngine.UIElements.DispatchMode)
UnityEngine.UIElements.BaseVisualElementPanel:SendEvent (UnityEngine.UIElements.EventBase,UnityEngine.UIElements.DispatchMode)
UnityEngine.UIElements.UIElementsUtility:smile:oDispatch (UnityEngine.UIElements.BaseVisualElementPanel)
UnityEngine.UIElements.UIElementsUtility:UnityEngine.UIElements.IUIElementsUtility.ProcessEvent (int,intptr,bool&)
UnityEngine.UIElements.UIEventRegistration:ProcessEvent (int,intptr)
UnityEngine.UIElements.UIEventRegistration/<>c:<.cctor>b__1_2 (int,intptr)
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

Well, that’s the reason right there as you said yourself. You can also read about that in the documentation:

So why are you setting the submesh when you don’t have set any valid data yet? The example over here clearly shows that you should fill your index buffer before you call SetSubMesh.

The index buffer is used by the GPU to index into the vertex buffer. What do you expect when you have an index around
“816551072”? You said you know the buffer contains bogus data, so what did you expect what happens when you throw that data at an advanced low level API that doesn’t do any checks? Of course it could result in a catastrophic failure. Pretty much the same happens when you pick a random number and interpret it as a pointer and start writing to that address. In most cases this won’t end well :wink:

1 Like

“Hello, Doctor? Today I set my foot on fire, and now … well, it seems my foot is on fire. Can you help me?” :slight_smile:

1 Like

Let me explain: I was assuming that Unity will have no problems with arbitrary numbers in the index buffer until Mesh.ApplyAndDisposeWritableMeshData() will be called. So my plan was to fill the index buffer with valid data after setting the submesh, but before calling Mesh.ApplyAndDisposeWritableMeshData() .
I assumed that this will work, since I learned it from an tutorial from catlikecoding.com.
Here, it is done in the same order, and the submesh is set before the index buffer is filled. You can see this if you look into the corresponding source files:

  1. Bitbucket
  2. Bitbucket
  3. Bitbucket

This project works without any crashes. So there must be something I am missing. Anyway, if it’s the easiest solution, I will change my larger project and put the submesh configuration after filling the index buffer .

I had same problem,I know why it went wrong now, this is problem of Brust which named " Memory aliasing",and this the official document about it
https://docs.unity3d.com/Packages/com.unity.burst@1.3/manual/index.html#synchronous-compilation
You can solve it based on the following code,add [NoAlias] before the Native arr

        [NativeDisableContainerSafetyRestriction]
        [NoAlias]
        NativeArray<Stream0> stream0;

        [NativeDisableContainerSafetyRestriction]
        [NoAlias]
        NativeArray<TriangleUInt16> triangles;