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];
}
}