[ECSB-1294] "MaterialMeshInfo does not point to a valid mesh" when using mesh/material indices

Issue ECSB-1294 was reported & confirmed about a month ago.

Here is the information that was provided in the bug report, QA replaced the issue details with repro steps. I would appreciate this defect being addressed, it was encountered in the Unity Discord by another user.

ECSB-1294

Entities Graphics provides an API to create rendered entities at runtime, through calling RenderMeshUtility.AddComponents with appropriate arguments.

Users are able to create a RenderMeshArray to pass to AddComponents, with the ability to specify an array of MaterialMeshIndex that references indices in the arrays of meshes and materials that are also stored in the RenderMeshArray. Users can then create a MaterialMeshInfo (specifying what material(s) and mesh(s) an entity is to be rendered with), and specify a range in this materialMeshIndices array with MaterialMeshInfo.FromMaterialMeshIndexRange. This allows the user to give a specific entity multiple meshes to render and with different materials, as opposed to one mesh with one material through MaterialMeshInfo.FromRenderMeshArrayIndices.

What happened

Attempting to call RenderMeshArray.AddComponents (with MaterialMeshInfo configured with MaterialMeshInfo.FromMaterialMeshIndexRange) fails with an error message in the Console:
“materialMeshInfo does not point to a valid mesh”

I looked through the code, and I believe that the issue is that the class RenderMeshArray uses faulty checks for the GetMaterial, GetMaterials, and GetMesh methods. Here, the check for “MaterialMeshInfo.IsRuntimeMesh” / “MaterialMeshInfo.IsRuntimeMaterial” are done before checking “MaterialMeshInfo.HasMaterialMeshIndexRange” - this is counter to other similar checks which perform correctly / do not cause errors. Additionally, it seems that a call in PushMeshDataSystem is incorrect in that it does not check HasMaterialMeshIndexRange at all.

These checks are what I believe to be correct, and examples of what should be done:

  • EntitiesGraphicsSystem.cs:243
    Checks HasMaterialMeshIndexRange first, only checks IsRuntimeMesh if false.

  • DrawCommandGeneration.cs:1143
    Checks HasMaterialMeshIndexRange first, only checks IsRuntimeMesh if false.

These checks are what I believe to be incorrect, and should be of particular interest in fixing this bug:

  • RenderMeshArray.cs:645/670/702
    Checks IsRuntimeMesh first, which is always true when MaterialMeshInfo.FromMaterialMeshIndexRange is used, so these always return null and cause RenderMeshArray.AddComponents to fail.
    I tried embedding the package and moving these if-return-nulls to inside the else clause of the HasMaterialMeshIndexRange check (the diff for the code changes I used is in Assets/test.diff), and this change seems to fix things and allow rendering.

  • PushMeshDataSystem.cs:331/628
    These only check IsRuntimeMesh without checking HasMaterialMeshIndexRange at all, and MaterialMeshInfo.FromMaterialMeshIndexRange does not provide a usable MeshID value directly (it needs to go through the RenderMeshArray), so the wrong mesh would be used in the case of MaterialMeshInfo.FromMaterialMeshIndexRange.

How can we reproduce it using the example you attached

  • Open Scenes/TestScene1
  • Enter Play Mode
  • Observe error “materialMeshInfo does not point to a valid mesh” and bluish-green objects on the right do not appear

This can be corrected by applying the code changes described in the diff file Assets/test.diff to RenderMeshArray.cs BUT this does not account for the PushMeshDataSystem.cs code that also looks incorrect, only the RenderMeshArray code

Repro Project

test_urp_6000_0_14_rma_repro.zip (1.7 MB)

Diff for convenience

--- a/com.unity.entities.graphics/Unity.Entities.Graphics/RenderMeshArray.cs
+++ b/com.unity.entities.graphics/Unity.Entities.Graphics/RenderMeshArray.cs
@@ -642,9 +642,6 @@ namespace Unity.Rendering
         /// <returns>Returns the associated material instance, or null if the material is runtime.</returns>
         public Material GetMaterial(MaterialMeshInfo materialMeshInfo)
         {
-            if (materialMeshInfo.IsRuntimeMaterial)
-                return null;
-
             // When using an index range, just return the first material of the range
             if (materialMeshInfo.HasMaterialMeshIndexRange)
             {
@@ -656,6 +653,9 @@ namespace Unity.Rendering
             }
             else
             {
+                if (materialMeshInfo.IsRuntimeMaterial)
+                    return null;
+
                 return MaterialReferences[materialMeshInfo.MaterialArrayIndex];
             }
         }
@@ -667,9 +667,6 @@ namespace Unity.Rendering
         /// <returns>Returns the associated material instances, or null if the material is runtime.</returns>
         public List<Material> GetMaterials(MaterialMeshInfo materialMeshInfo)
         {
-            if (materialMeshInfo.IsRuntimeMaterial)
-                return null;
-
             if (materialMeshInfo.HasMaterialMeshIndexRange)
             {
                 RangeInt range = materialMeshInfo.MaterialMeshIndexRange;
@@ -687,6 +684,9 @@ namespace Unity.Rendering
             }
             else
             {
+                if (materialMeshInfo.IsRuntimeMaterial)
+                    return null;
+
                 var material = MaterialReferences[materialMeshInfo.MaterialArrayIndex];
                 return new List<Material> { material };
             }
@@ -699,9 +699,6 @@ namespace Unity.Rendering
         /// <returns>Returns the associated Mesh instance or null if the mesh is runtime.</returns>
         public Mesh GetMesh(MaterialMeshInfo materialMeshInfo)
         {
-            if (materialMeshInfo.IsRuntimeMesh)
-                return null;
-
             // When using an index range, just return the first mesh of the range
             if (materialMeshInfo.HasMaterialMeshIndexRange)
             {
@@ -713,6 +710,9 @@ namespace Unity.Rendering
             }
             else
             {
+                if (materialMeshInfo.IsRuntimeMesh)
+                    return null;
+
                 return MeshReferences[materialMeshInfo.MeshArrayIndex];
             }
         }

2 Likes

I’m also looking forward to a resolution for this error, it’s still present in Entities Graphics 1.4.3.

Hope we can get a patch before the end of the year.

This issue is marked as resolved in entities 1.3.2, but it is still present in all packages and changelog doesn’t mention that it was fixed at all.

1 Like

Still actual in Entities Graphics 1.4.12.

 public Mesh GetMesh(MaterialMeshInfo materialMeshInfo)
 {
     // This code not allowing set mesh
     //===============================
     if (materialMeshInfo.IsRuntimeMesh)
         return null;
     //==================================

     // When using an index range, just return the first mesh of the range
     if (materialMeshInfo.HasMaterialMeshIndexRange)
     {
         RangeInt range = materialMeshInfo.MaterialMeshIndexRange;
         Assert.IsTrue(range.length > 0);

         int firstMeshIndex = MaterialMeshIndices[range.start].MeshIndex;
         return MeshReferences[firstMeshIndex];
     }
     else
     {
         return MeshReferences[materialMeshInfo.MeshArrayIndex];
     }
 }