Search Unity

SetSubMesh Draw order

Discussion in 'General Graphics' started by GilCat, Oct 14, 2019.

  1. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    676
    I've built a mesh with 2 submeshes (2 quads) and they seem to don't respect the order that i call Mesh.SetSubMesh.
    I'm a bit confused here because if i have a mesh with a single submesh the draw order is the one from the IndexBufferData array.

    I can affect the order of the submesh if i add change the supplied matrix on the next stage (Graphics.DrawMesh).

    Is this a bug or am i missing something here?

    Thanks

    EDIT: Attached a sample project with two scenes. One where i draw 2 quads with only one submesh and another with one submesh per quad. In the second scene you can see my render order problem.
     

    Attached Files:

    Last edited: Oct 14, 2019
  2. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    Unity decides the draw order depending on whether the meshes are opaque or transparent. Some of the rules:

    * Grouping identical materials together to help reduce state change
    * Front to back draw order for opaque objects, to reduce overdraw
    * Back to front draw order for transparent objects, to ensure correct visual results

    (This list is not exhaustive - it's just a few examples)

    There are ways to apply rules on top of this to control the draw order, see:
    https://docs.unity3d.com/Manual/class-TagManager.html
    https://docs.unity3d.com/ScriptReference/GameObject-layer.html
    https://docs.unity3d.com/ScriptReference/Renderer-sortingOrder.html
     
    GilCat likes this.
  3. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    676
    Thanks but i think my issue is something else.
    Note that in my case i'm batching all the vertices into a single mesh using the new mesh API.
    An in this case what i see is that the order of rendering is dependent on the order that i assign the vertex indices (which is fine for me). The problem arises when i have more that one submesh, this is due to requiring different materials for each submesh.

    I update my mesh like this:
    Code (CSharp):
    1.     var vertexCount = m_vertices.Length;
    2.     m_mesh.SetVertexBufferParams(
    3.       vertexCount,
    4.       m_meshDescriptors[0],
    5.       m_meshDescriptors[1],
    6.       m_meshDescriptors[2]);
    7.     m_mesh.SetVertexBufferData(m_vertices, 0, 0, vertexCount, 0);
    8.     m_mesh.SetIndexBufferParams(m_indices.Length, IndexFormat.UInt32);
    9.     m_mesh.SetIndexBufferData(m_indices, 0, 0, m_indices.Length);
    10.     m_mesh.subMeshCount = m_subMeshes.Count;
    11.     for (var i = 0; i < m_subMeshes.Count; i++) {
    12.       var subMesh = m_subMeshes[i];
    13.       var descr = new SubMeshDescriptor() {
    14.         baseVertex = 0,
    15.         bounds = default,
    16.         indexCount = i < m_subMeshes.Count - 1
    17.               ? m_subMeshes[i + 1].Offset - subMesh.Offset
    18.               : m_indices.Length - subMesh.Offset,
    19.         indexStart = subMesh.Offset,
    20.         topology = MeshTopology.Triangles
    21.       };
    22.       m_mesh.SetSubMesh(i, descr);
    23.     }
    24.     m_mesh.UploadMeshData(false);
    My Draw code:
    Code (CSharp):
    1.     for (var i = 0; i < m_subMeshes.Count; ++i) {
    2.       var material = m_subMeshes[i].Material;
    3.       Graphics.DrawMesh(
    4.         mesh: m_mesh,
    5.         matrix: Matrix4x4.identity,
    6.         material: material,
    7.         submeshIndex: i,
    8.         layer: 0,
    9.         camera: Camera.main);
    10.     }
    My real use case for this is to draw massive amount of Text in DOTS having a single batched mesh which i already do but now i'm facing this problem sorting that text only when i have different fonts (different material).

    Thanks again for your time
     
  4. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    The draw order rules are applied to each submesh, not each mesh. (because each submesh can have its own material)
    within each draw call, i agree that the expected draw order is the order of the indices, but Unity is deciding which order to submit the draw call for each submesh, based on its own internal rules.

    If it's for text, maybe you can use Graphics.DrawMeshNow() .. the submesh parameter is named materialIndex (sorry for the inconsistent naming!). This method bypasses the sorting rules and immediately draws your submesh once to the active render target. (vs DrawMesh, which inserts a command into the scene to draw the submesh into each required pass (color, shadows, reflections, etc) If trying this, you may need to be careful about where you call DrawMeshNow from, i.e. the script docs call it from OnPostRender: https://docs.unity3d.com/ScriptReference/Graphics.DrawMeshNow.html)

    (EDIT: cool use of DOTS btw!) ;)
     
    GilCat likes this.
  5. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    676
    Thanks for the tip. Yeah i've looked into that before but materialIndex really is a bad naming :). I was drawn away form it because i thought it could not handle submeshes. Tried it and it works fine in my sample but with DOTS i was avoiding the use of Monobehaviours at all and like that i must attach one to the camera in order to call DrawMeshNow.
    Is there a place to call this using DOTS? My RenderSystem updates in PresentationSystemGroup but it wont render there.
     
  6. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    Ahhh, yes this is awkward. I don't think we have a good answer for this right now - I think it's something we can't handle without using a MonoBehaviour yet. I would suggest trying to limit it to only one MonoBehaviour and piping all these draw calls into that, so the single MonoBehaviour does a bunch of Graphics.DrawMeshNow calls (vs. having one call in many MonoBehaviours)

    On the plus side, Graphics.DrawMesh triggers quite a lot of work inside Unity, so, the performance difference of this vs. using a MonoBehaviour probably (hopefully) isn't going to be significant.

    "Proper" rendering within the DOTS ecosystem is something we are working on at the moment :)
     
    GilCat likes this.
  7. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    676
    Yeah ended up piping it to a monobehaviour attached to the camera that is instantiated when the Render System starts running. I had to be careful to avoid accessing dealloacted data in that monobehaviour since OnPostRender happens later in the frame and the supplied that can be invalid at that stage.

    Before that tried DrawMeshInstanceIndirect (with no compute buffers other than the arguments) but it follows the same rules of rendering as DrawMesh.

    It's working now and will update it as the DOTS rendering evolves. So thank you for the help!
     
    richardkettlewell likes this.