DrawMeshNow broken in 2021

Hi there!

I’m using the built-in render pipeline. Recently found that pretty much all of my custom editor tools are visually broken in Unity 2021 and up. After a few days spent debugging this, the culprit seems to be Graphics.DrawMeshNow. Conceptually, this is the issue:

mesh.colors = redColors;
Graphics.DrawMeshNow(mesh);
mesh.colors = greenColors;
Graphics.DrawMeshNow(mesh);
//Mesh is drawn twice, using greenColors for both.

Expected result: the mesh is rendered twice, once red and once green (Unity 2019.4 and older):

Result: the mesh is rendered twice, both times green (same code, Unity 2021.3.0f1 and up, haven’t tested versions in-between):

What-the-heck. Seems like DrawMeshNow is somehow caching the mesh, instead of rendering it immediately. This used to work fine in all previous Unity versions. I’m starting to doubt the reliability of very basic stuff in Unity, which is kinda frustrating.

Here’s the actual code I’m using to test this:

using UnityEngine;

public class DrawMeshNowBug : MonoBehaviour
{

    Material paintMaterial;
    public Mesh mesh;

    Mesh copy;

    // Start is called before the first frame update
    void Start()
    {
        paintMaterial = Resources.Load<Material>("VertexColorMaterial");
        copy = GameObject.Instantiate(mesh);
    }

    private void OnDestroy()
    {
        Destroy(copy);
    }

    // Update is called once per frame
    public void OnPostRender()
    {
 
        if (paintMaterial.SetPass(0))
        {
            Color[] colors = new Color[copy.vertexCount];
            for (int i = 0; i < colors.Length; i++)
                colors[i] = Color.red;

            copy.colors = colors;
            Graphics.DrawMeshNow(copy, Matrix4x4.identity);

            for (int i = 0; i < colors.Length; i++)
                colors[i] = Color.green;

            copy.colors = colors;
            Graphics.DrawMeshNow(copy, Matrix4x4.Translate(Vector3.right));

        }
    }
}

This is the shader used for flat vertex color rendering (built-in pipeline), which imho is as simple as it gets:

Pass {
 
            Cull Back
            Fog { Mode Off }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct vin{
                float4 vertex   : POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
            };

            struct v2f {
                float4 pos: POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
            };

            sampler2D _MainTex;

            v2f vert(vin v) {
                v2f o;
                o.pos = UnityObjectToClipPos (v.vertex);
                o.texcoord = v.texcoord;
                o.color = v.color;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                return i.color;
            }

            ENDCG
        }

Just a sanity check before I submit a proper bug report: am I doing something wrong here, or misinterpreted the way DrawMeshNow is supposed to work?

Edit: generalized this to multiple DrawMeshNow calls in a row, modifying the mesh in-between calls: mesh data is indeed cached, only the very last form of the mesh is rendered multiple times. :hushed::sweat_smile:

The only workaround I found is to create a separate mesh for each DrawMeshNow call. Reporting this as a bug.

It seems odd to me that it would work the other way around tbh.

Why? That’s the documented behavior:

“This function will draw a given mesh immediately. Currently set shader and material (see Material.SetPass) will be used”

So after the method has been called, modifying the mesh can’t possibly have any effect on what has been already drawn. Otherwise it means the mesh has in fact not been drawn immediately.

Moreover, if behavior has changed with respect to older versions (since Unity 5) it should have been properly documented since it breaks a lot of existing projects.

No the odd part to me isn’t about drawing the mesh immediately, the odd part is about sending mesh data to the GPU right then. But hey if it’s broken/against spec, makes sense it should be fixed.

I’d also assume the actual drawcall isn’t issued right then and there, but mesh data copied into some sort of command and rendered later. But, surprisingly:

https://discussions.unity.com/t/613281

When using multi-threaded rendering I’d expect mesh data (along with rendering state) to be internally copied, not referenced - otherwise behavior of multi-threaded and single-threaded paths would be completely different which is not a good thing. Will investigate further.

I reported this as a bug, and it has been confirmed as such:

2 Likes