Handling a List of a Million structs containing 2 Vectors each

Ok… So… First and foremost I want to say that I am by no means an expert on the topic of this thread; however, in the last few days, I have figured out how to use Compute Shaders and Compute Buffers to render around 2 million blades of grass on my m2 MacBook Air at around 60fps.

Here is a picture for basic reference:

As you can see, a C# script currently gathers all the vertices from a source mesh (currently a plane) and passes them to the compute shader as an array of BladeSource (a struct containing a position and normal).

This system currently needs to be used in a large world and obviously, I don’t want grass inside objects like a well, a house, an NPC, etc… Now, I could model a mesh that fits the world and has vertices where each blade of grass should be but that would be inconvenient, tedious, and hard to work with involving any change. At least from what I have tried, Unity’s serializable object severely struggles to save a List of millions of a struct, so I can’t just create a public List that is edited by a brush drawing SceneGUI. The easy part is generating the points, but what is the smartest way to generate them so that they can be saved (and not take 5 hours)?

Thanks,
Let me know if any further clarification is required.

You don’t have to use [SerializeField] to serialize data. You could use a BinaryWriter or Unity’s serialization package.

You can also use GPU Instancing to draw many copies of the same mesh.

I will look into BinaryWriters and how they might be of some use. Will I be able to massively improve save speeds with them? Even with a list with more than 1 million items?

I am currently using a mix of Compute Shaders, HLSL Shaders, and C# to use ```
Graphics.DrawProceduralIndirect(instantiatedMat, bounds, MeshTopology.Triangles, argsBuffer, 0, null, null, ShadowCastingMode.Off, true, gameObject.layer);


And since I am using Metal and it needs to be performant for mobile devices, this is an extremely efficient (the most I have found) way to draw a huge amount of grass with little to no bottleneck.

Thanks for the quick response!

Well, a million times two Vector3 equals about 23 Megabytes uncompressed. That will always take some time, and definitely not something you want to do in the middle of gameplay, especially not on mobile.

I would question why you need to save this many vectors. You could simply draw polygon areas within which grass appears, rather than trying to serialize every individual grass mesh. That is definitely overkill.

Have you looked into using volumes to indicate where the grass should and shouldn’t spawn? I’d think they would work well with compute shaders since you’re just passing in data representing the shapes of the volumes, and the shader just turns on/off the grass if it’s inside it.

Or use a texture as a density map, which opens up more styling possibilities?

@CodeSmile
Yeah, I totally agree about that being overkill. I am not needing any of this to be done in gameplay. I just want to be able to edit where the grass is in the editor and I need to save that data for initialization at the start of the game. I like the polygon idea.

@Ryiah
Is there a specific Component, Function, or Class that you are referring to which makes using volumes convenient or are you just suggesting a different route? I like the idea even though I am still not sure how I would go about implementing it… In terms of performance, I definitely would not want to be calculating any blades that are not rendered on screen, so I need to be cautious of any solution which would do that. Maybe I could do something mixed with volumes and what CodeSmile was saying about having areas in which the grass will be generated at the beginning of the game.

@AcidArrow
This was initially my favorite solution, but I dislike the idea of having to edit a texture in the editor (partially cause I know I would run into other performance issues in the editor) and being limited by resolution. Thoughts on execution?

If you edit it as a rendertexture and then have a save button so it is actually written to a file (or a CPU texture) manually every once in a while it should be pretty fast.

Although this would not be trivial to implement, especially if you want it to be user friendly.

I think I wouldn’t bother and would just replicate the significant parts of my world / scene in Modo (it’s my 3d software of choice, but it could be any), and then load the texture there and do the editing there so I don’t have to code my own custom solution inside Unity. But this quickly falls apart if you use things that aren’t exported easily from Unity (like terrain?) or if your game world is super complex and big?

If the underlying geo has enough vertices you could also just paint a vertex map and use that? Which solves the resolution problem. (Although I hate Polybrush with passion)

I don’t know, I feel there are too many unknowns and the solutions I am coming up with are mostly based on what I would do in my games, which have small maybe up to medium scenes.

I’m referring to volumes in a loose sense. Basically you would pass through data that represents spheres or cubes and the compute shader would scan through them any time it went to render grass skipping any if they fall within the boundaries of the volume.

I like this idea, but unless I’m not thinking clearly (which is highly probable), then I would have to check the distance between every blade and every volume, every frame. And that would be absolutely disgusting ??