Entities RenderMeshUtility.AddComponents with MaterialMeshInfo.FromMaterialMeshIndexRange bug

The Issue

I think I found a bug in the Entities Graphics package 1.3.2 RenderMeshUtility.AddComponents function.

I wanted to use the following code to add rendering to my entity, the mesh that I have has submeshes with one material per submesh.

void AddRenderingComponents(Entity entity, Mesh mesh, Material[] materials)
{
    //   Create a mapping between each material and submesh, all using a single mesh
    MaterialMeshIndex[] indices = new MaterialMeshIndex[materials.Length];
    for (int i = 0; i < indices.Length; i++)
    {
        indices[i].MaterialIndex = i;
        indices[i].MeshIndex = 0;
        indices[i].SubMeshIndex = i;
    }
    
    // Set up RenderMeshArray
    RenderMeshArray renderMeshArray = new(
        materials,
        new Mesh[] { mesh },
        indices);
    
    // Set up RenderMeshDescription
    RenderMeshDescription renderMeshDescription = new(
        shadowCastingMode: UnityEngine.Rendering.ShadowCastingMode.On,
        receiveShadows: true
    );
    
    // Create MaterialMeshInfo
    MaterialMeshInfo materialMeshInfo = MaterialMeshInfo.FromMaterialMeshIndexRange(0, indices.Length);
    
    // Add the necessary components for rendering
    RenderMeshUtility.AddComponents(entity, entityManager, renderMeshDescription, renderMeshArray, materialMeshInfo);
}

However, this code will throw an error: materialMeshInfo does not point to a valid mesh.

I’m not sure exactly what part of the chain of execution I would define as a bug, but what happens is the following. RenderMeshUtility.AddComponents does an check to make sure the mesh is valid:

var mesh = renderMeshArray.GetMesh(materialMeshInfo);

if (mesh == null)
{
    Debug.LogError(("materialMeshInfo does not point to a valid mesh"));
    return;
}

This uses RenderMeshArray.GetMesh which has the following check at the start of it’s function:

public Mesh GetMesh(MaterialMeshInfo materialMeshInfo)
{
    if (materialMeshInfo.IsRuntimeMesh)
        return null;
    
    ... rest of function
}

The MaterialMeshInfo.IsRuntimeMesh property is defined as the following:

bool IsRuntimeMesh => Mesh >= 0;

This uses MaterialMeshInfo.Mesh which does not have a good documentation. MaterialMeshInfo.Material, for which this bug is exactly the same has good documentation however:

The material ID can be one of the following:

  • A literal Material ID received from the RegisterMaterial API, encoded as a positive integer.
  • An array index to the RenderMeshArray shared component of the entity, encoded as a negative integer.

It basically checks if it is using a globally registered material (positive), or one from the RenderMeshArray (negative)

Because I’m using sub meshes MaterialMeshInfo.Mesh and MaterialMeshInfo.Material are not used. The MaterialMeshInfo.FromMaterialMeshIndexRange specifically sets MaterialMeshInfo.Mesh and MaterialMeshInfo.Material to 0:

public static MaterialMeshInfo FromMaterialMeshIndexRange(int rangeStart, int rangeLength)
    => new(0, 0, new SubMeshIndexInfo32((ushort)rangeStart, (byte)rangeLength));

Because of this the MaterialMeshInfo.Mesh and MaterialMeshInfo.Material are presumed to be globally registered meshes, materials. Now when it comes time for MaterialMeshInfo.IsRuntimeMesh to run this will return true as MaterialMeshInfo.Mesh and MaterialMeshInfo.Material are 0. Even though these properties are unused they will cause the RenderMeshArray.GetMesh to return null ignoring it’s code designed specifically for when MaterialMeshInfo.HasMaterialMeshIndexRange is true.

Solution

In my opinion the issue is with the MaterialMeshInfo.IsRuntimeMesh property which should account for MaterialMeshInfo.HasMaterialMeshIndexRange rather than assume the MaterialMeshInfo.Mesh and MaterialMeshInfo.Material are used.

Workaround

In the meantime to work around this issue I changed my code to create my MaterialMeshInfo the following way:

MaterialMeshInfo materialMeshInfo = MaterialMeshInfo.FromMaterialMeshIndexRange(0, indices.Length);
materialMeshInfo.Mesh = -1;
materialMeshInfo.Material = -1;

Using this the RenderMeshUtility.AddComponents will not return an error as the MaterialMeshInfo.IsRuntimeMesh property now returns false.

This has already been reported

Ahh, missed that, thanks for pointing it out. Should I keep this post or delete it?