What's the best way to optimize a moving pattern like this ?

Hi !
I can’t manage to correctly optimize this :

As you can see, the FPS drop frop 90fps to 70fps once the hexagons get rendered, which is pretty terrible :frowning:
The video is running on a Quest 2, so it’s an android-based project.

I’m animating the hexagons with perlin noise, executing the following script on every single hexagon :

    [HideInInspector] public float x, y;
    private Renderer mesh;
    private MaterialPropertyBlock _propBlock;

    // Start is called before the first frame update
    void Start()
    {
        x = transform.position.x;
        y = transform.position.z;
        mesh = GetComponent<Renderer>();
        _propBlock = new MaterialPropertyBlock();
    }

    // Update is called once per frame
    void Update()
    {
        if (mesh.isVisible)
        {
            float level = Mathf.PerlinNoise(Time.timeSinceLevelLoad / 4 + x / 10, Time.timeSinceLevelLoad / 4 + y / 10);
            transform.position = new Vector3(transform.position.x, -2f + level * 0.75f, transform.position.z);

            //mesh.materials[1].SetColor("_EmissionColor", Color.white * level * 1.5f);

            mesh.GetPropertyBlock(_propBlock);
            _propBlock.SetColor("_EmissionColor", Color.white * level * 1.5f);
            mesh.SetPropertyBlock(_propBlock);
        }
    }

Do anyone know what I could do to further optimize it ?

Hi. Are you using a gpu instanced shader for the hexagons presumably? Maybe have a look with the profiler to see if you are gpu or cpu limited?

I had GPU Instancing disabled, but once enabled, nothing changed (stuck at 250 draw calls)
Then I disabled SRP Batcher from the URP settings and it dropped to 160 draw calls !
It was now batching the concrete material from the hexagons but not the neon part on the sides.
There’s definitely something wrong with the SRP Batcher…

In addition to running all the animation on a single script instead of on each hexagon, I’m now at 80fps in worst case ! Which is way better than the previous 70 but still 10fps too low :frowning: (targeting 90fps)

Any idea how I could batch the neon material while still keeping the color animations going ?
(edit: definitely GPU limited)

Looks like even without the neon animation and having only 80 draw calls, those hexagons are still draining a lot of performance… :S

To be honest i am still using built in renderer for my project so i can’t really comment on srp batcher. From what i have read online gpu instancing support is not great but don’t take my word for it.

The hexagons look like they are using a fairly simple shader. It might be worth trying the following:

  1. disable all animations, check fps. The animation of position and or colour might be breaking batching

  2. make a single large mesh of all hexagons baked together.

If (2) achieves the speed you need then maybe look to a pure shader implementation of the animation? For example use a perlin texture lookup to animate vertex position

Good luck with your project!

1 Like

@Wasplay
What is the polycount of 1 of those hexagons?

About 30, but this doesn’t seem to affect the performance.

Updates on material properties require a unique runtime material to store/track unique values per use —that will break batching. But I think updating all materials (get/set) every frame might be the bigger deal.

if you disable all the material read/write instructions is it a significant improvement?

(in terms of division of labor; Color operation will be handled by shader)

  • single static material : the Shader reads objects (center point) world location (pos.z) to factor on vertex color based on height (material Color / Tint / Emissive causes runtime material creation and breaks batching)
  • While leaving motion in your posted script operating on object level (not on the shader and vertices)

you can use vertex position in shader, might be a savings as it means object level doesn’t move

Im not sure about this, but does calling a perlin function on each object cause a tiny issue? rather than having 1 function globally outside and the objects only offset time by their XorY location

also when you say ‘neon’, you mean setting “_EmissionColor” to 1.5f right? (that can be in the shader but needs to stay static, you just fine tune the vertex i/o based on position z height)