Search Unity

Can MeshInstanceRenderer do per-instance data and custom draw order?

Discussion in 'Graphics for ECS' started by 5argon, Jun 5, 2018.

  1. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    I wish to move my game's drawing code to ECS. However I found out that MeshInstanceRenderer requires GPU instancing on the material.

    Currently my game uses dynamic batching only, and I have multiple colors of the same mesh by means of using a texture with lookup color, and I made a different duplicate of meshes to have a different UV coordinate to get different color. This way it can dynamic batch since the criteria is only same material.

    However to use GPU instancing and therefore MeshInstanceRenderer, I need to :

    1. Use the same mesh, because that's GPU instancing's criteria. Therefore I could not use a different mesh with different UV trick anymore. For this, I would use "per-instance data" for the different color instead since now it can be efficiently draw considering it is the same material even with different colors. (https://docs.unity3d.com/Manual/GPUInstancing.html)
    2. When I tried GPU instancing it does not group the draw in a good way. (It just goes from back to front, and it breaks the instancing very often when encountering different meshes that was sandwiched between them) The official solution from GPU instancing manual page is to use Graphics.DrawMeshInstanced so I can choose to draw the same mesh together in an order that I want.

    So currently does MeshInstanceRenderer has per-instance data and an equivalent of Graphics.DrawMeshInstanced? Will the system just pick whatever data with that component and draw them with Unity's default ordering? (The system seems to be in Unity.Rendering which we could not check the source code?)
     
    Last edited: Jun 5, 2018
  2. Afonso-Lage

    Afonso-Lage

    Joined:
    Jul 8, 2012
    Posts:
    70
    I'm not able to answer your question, but I'm able to provide you this.They are located on com.unity.entities@0.0.12-preview.5\Unity.Rendering.Hybrid path.
     

    Attached Files:

    5argon likes this.
  3. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Ah I see, now I also found this file in my editor as well.

    So it does use Graphics.DrawMeshInstanced internally, and it explicitly says
    Graphics.DrawMeshInstanced(renderer.mesh, renderer.subMesh, renderer.material, m_MatricesArray, length, null, renderer.castShadows, renderer.receiveShadows);
    where the null is per-instance data. As for drawing order it loops through
    EntityManager.GetAllUniqueSharedComponentDatas
    and so the order is kind of uncontrollable?

    I guess I will wait a bit until it is more stabilized seeing comments in that code.
     
  4. Afonso-Lage

    Afonso-Lage

    Joined:
    Jul 8, 2012
    Posts:
    70
    Since i'm not gonna use GPU Instancing, I'll write my own MeshRendererSystem, based on that system of course. But it is simpler than I thought, which just shows how powerfull ECS is.
     
  5. Afonso-Lage

    Afonso-Lage

    Joined:
    Jul 8, 2012
    Posts:
    70
    Just to add some info, MeshRendererSystem was running in 4,7ms here. I made my own simplier and specific render system and now I spend only 0,6ms to render my objects. Also, I saved 0,1 from TransformBarrier and 0,3 from TransformSystem, which is a gain of 4,5ms per frame!

    But this is because on my game, I just need the position to render fix objects, they don't rotate and don't scale, and have a unique mesh per entity. So this system was enough to me:

    Code (CSharp):
    1.     public class RenderSystem : ComponentSystem
    2.     {
    3.         ComponentGroup m_Group;
    4.  
    5.         protected override void OnCreateManager(int capacity)
    6.         {
    7.             m_Group = GetComponentGroup(typeof(ChunkRenderData), typeof(Position));
    8.         }
    9.  
    10.         protected override void OnUpdate()
    11.         {
    12.             var renderData = m_Group.GetSharedComponentDataArray<ChunkRenderData>();
    13.             var positionData = m_Group.GetComponentDataArray<Position>();
    14.  
    15.             for (var i = 0; i < renderData.Length; i++)
    16.             {
    17.                 var position = math.translate(positionData[i].Value);
    18.                 var data = renderData[i];
    19.  
    20.                 Graphics.DrawMesh(data.mesh, new Matrix4x4(position.m0, position.m1, position.m2, position.m3), data.material, 0);
    21.             }
    22.         }
    23.     }
     
    Arathorn_J and avvie like this.
  6. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Interesting. So this way ECS can actually render anything. Is it much better than non-ECS (non-instanced) rendering that you have before? (Is a bunch of Graphics.DrawMesh will be dynamic/static batched by Unity like usual, for example)

    But having Unity draw from ECS data rather than a data on GameObject sounds nicer overall. Other than iteration speed it is easy to do selective drawing based on components/subtractives.
     
  7. Afonso-Lage

    Afonso-Lage

    Joined:
    Jul 8, 2012
    Posts:
    70
    I can't compare with a non-ECS version of my game, since I got many optimizations on my logic, by using ECS, so it would be a apple vs orange comparison. But what surprises me was how easy it is to render on ECS. I tough there would be some magic in order to render meshes, but it was a way easier.
     
  8. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Ok, that's alright. I also have done ECSifying all the logics and already at the final point of optimization that is drawing things.

    Today I spent time committed to the ECS drawing already about half way through and found one limitation I could not see before I begin : TransformSystem does not care about scaling just yet.

    There are local position/rotation + parenting and the system makes a correct world matrix ready to use with Graphics.DrawMesh but it is only missing scale information. I can imagine it involves all sorts of messy thing if we have to account scaling with parent-child hierarchy so I don't even think of rolling my own.

    For now, the best for me seems to be using TransformAccess to change transform from threads and let MeshRenderer draw like usual.
     
  9. avvie

    avvie

    Joined:
    Jan 26, 2014
    Posts:
    74
    Well i was doing an experiment where i would transform 200K cubes on a scene. Turns out if you do the transformations yourself (be wary that was a constrained problem, where very few things can happen) and managed to do the same task at half the time. By constructing the transformation matrix from scratch and voiding the TransformSystem. It should be very easy to add scaling if you approach it like that.
     
    5argon likes this.
  10. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    I still don't get how VoidSystem<System> works in TransformSystem.cs. Is it only usable with SubtractiveComponent? It contains no code and no other place in the source has any definition of VoidSystem. What would happen if we remove
    [ReadOnly] public SubtractiveComponent<VoidSystem<TransformSystem>> transfromExternal;
    from the inject groups?
     
  11. avvie

    avvie

    Joined:
    Jan 26, 2014
    Posts:
    74
    Look here and especially Bootstrap.cs for the archetype definitions and at RotationSpeedSystem.cs for the calculation, sorry but i havent cleaned it up yet nor have i comment it. :S
    but between the two and the math that you need to know to scale something it should be relatively straight far ward