Search Unity

Question Most optimal way to set vertex colors via jobs?

Discussion in 'Entity Component System' started by xVergilx, Nov 11, 2022.

  1. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    The goal is to process large amount of meshes and set the vertex color to specific value (one per mesh);

    I was thinking something like:
    - Grab writable MeshData from the Mesh;
    - Grab vertex color NativeArray;
    - Memset? value for all vertices inside the job;
    - Somehow prevent stalling of scheduling / apply results back to the mesh.

    Has anyone done anything similar? Or is there a more performant way to do this?
     
    bb8_1 likes this.
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    6,005
    Not memset, just make sure assignment is vectorized by burst.

    I‘ve done runtime mesh create with jobs here: https://github.com/CodeSmile-0000011110110111/GMesh/blob/main/Runtime/GMesh.Combine.cs

    Not the most optimized code but this is essentially what turned out to be the fastest separation of the task into jobs. Note that it‘s often more beneficial to have multiple jobs, each focusing on a single aspect, and sequence that rather than trying to make just one job that does it all because Burst will trip over many things (ie branches) so brute-force doing one thing can be alot faster than non-bursted code.
     
    xVergilx and bb8_1 like this.
  3. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    I'm more interested in editing existing meshes in runtime.

    My main concern is that MeshData[Array] has to be allocated for a lot of meshes simultaneously.
    Which may not be feasible for the Android / mobile platform. Since it might stall main thread for a long period of time and cost a lot of memory. At the same time, it probably will be hidden under loading screen, and I'm overthinking it.

    Also, I've though about plain writing vertex color to the array.
    But if color is the same for all verts, it feels like a waste of a lot of ops.
    E.g. mesh color of byte[1, 0, 0] which should be set to all vertex.

    If Burst is smart enough to fill it automatically fast enough (akin MemCpy), that would be great. Less unsafe code.
     
  4. scottjdaley

    scottjdaley

    Joined:
    Aug 1, 2013
    Posts:
    163
  5. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Yeah, I'd like to abuse dynamic batching extensively.

    MPB's would be simpler, yet not as performant for mobile devices of middle and low-end segments.
     
  6. scottjdaley

    scottjdaley

    Joined:
    Aug 1, 2013
    Posts:
    163
    I haven't used Material Property Blocks before, but from what I understand, setting them breaks batching (correct me if I'm wrong). Material Overrides do not have the same problem. Things can be batched into a single draw call even when different instances have different values.

    Here is an example of when I was testing this over a year ago: https://twitter.com/GravitonPunch/status/1429099724133437443
     
  7. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,271
    MeshData is probably the way to go on Android. For iOS, you could just dump it into a graphics buffer or texture, since Apple GPUs have a proper arbitrary data cache similar to desktop GPUs.
     
    xVergilx likes this.
  8. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Material Overrides are the same thing pretty much as MPB's, except for Entities / Entities Graphics.
    They do not break instancing. However, its not possible not to break batching if meshes are different.

    Plus, there's an instancing limit on the mobile, which is way lower than on other platform. Plus, instanced rendering performance REALLY differ on different devices. Even those devices that should support instancing sometimes does not, or do it very poorly.

    Which is why dynamic batching is [still] the fastest solution for those platforms. That is including SRP batching or DOTS instancing.

    This technique we've used is really good for 2d, since you could just encode "instance" id into vertex color and pass it simply calling SpriteRenderer.color = value. Decode it inside shader, and voila. You've got yourself an access to the global arrays, which can be accessed / filled from CPU blazing fast. And, everything is batched together, since there's no mesh restriction and material is the same. There are some other restrictions, such as global array size, but its not that big of deal for some cases.

    Pretty much how ParticleSystem works + global arrays.

    However, for 3D its a bit more trickier to do, since no proper [fast] vertex color API, and larger amount of vertices. MeshData is probably the closest thing to it. But I guess only tests can show.
     
    Last edited: Nov 11, 2022
    scottjdaley likes this.
  9. scottjdaley

    scottjdaley

    Joined:
    Aug 1, 2013
    Posts:
    163
    Got it. Yeah, I'm not at all familiar with the various platform differences when it comes to instancing.

    I'm able to draw different meshes in a single draw call so long as they are using the same material (using Entities Graphics).
     
    xVergilx likes this.
  10. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,271
    Have you tested with the new GL ES mode of Entities Graphics? That version using constant buffers which run a lot faster on most Android devices.
     
    xVergilx likes this.
  11. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Nope, since Entity Graphics does not support built-in. Probably worth looking into for future projects though.
     
  12. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    So, I've tested a couple of things:
    - MeshRenderer.additionalVertexStreams -
    Serialized data from editor, zero overhead from creating meshes in runtime. Perfect for my usecase.
    But... it breaks dynamic batching.

    Ideally custom data should be uploaded as an override of initial mesh, but something internal probably does not allow that. Such potential wasted.

    - [Writable] MeshData is a mess.
    Definitely built with an idea of creating meshes from scratch by person who likes torturing other people.
    But overall, pretty bad when it comes to editing the existing mesh.
    (Everything has to be copied to the new mesh. Access_denied.wav)

    What I haven't tested:
    - Access actual shared mesh;
    - Grab Read Only data
    - Copy everything to the new mesh + apply edits.
    Probably would work. But at this point I'm wondering if its even worth it.

    Like, Instantiating (or accessing MeshFilter.mesh) new mesh + applying colors might be faster than doing deep copy. Might be wrong though.