Most efficient way to render thousands of small textures

Hi all,

I’m making a fluid simulator and I need to draw thousands of moving particles in real-time to a Render Texture. This Render Texture is then passed to a shader for visual effects.

My current solution is to Instantiate each particle as a textured quad to take advantage of dynamic batching. It is ok, but I can only reach a maximum of 5000 particles before frame rate drops.

Here are the other things I tried:

  • Use Graphics.DrawMesh to bypass the overhead of managing GameObjects. This is much faster for the first thousand particles, but it also disables dynamic batching so I can’t go very far with this method.
  • Use a particle system. This approach is not flexible enough though, as I need the particles in a List to rapidly add and remove them. Unless there’s a way to dynamically add and remove specific particles in an efficient way, this is a no go.
  • Use Graphics.DrawTexture. Same problem as DrawMesh. Draw calls get ugly really fast.

Here is a demo: https://dl.dropboxusercontent.com/u/9289442/Fluid/Fluid.html

What would be the most efficient way to draw all these moving particles to a Render Texture?

Thanks!

My first question would be whether it’s actually getting batched… and how many draw calls you have. I’m guessing that your solution is probably GPU bound. I ran your simulation above on a machine with the following specs:

CPU: Xeon Ivy Bridge (4 cores, 8 with HyperThreading)
16 GB Ram

Even approaching 5k particles I didn’t see a huge uptick in CPU or memory usage but the performance degradation was still there. Above approximately 2,500 particles it seemed to degrade fairly linearly down to about 15fps @ 5k particles.

I get 5 draw calls using the current solution. Could the performance hit be caused by rendering a lot of transparent materials even if they’re batched?

So to answer your question, yes, the particles are definitely batched since I have 5 draw calls and thousands saved by batching as shown in the editor stats.

Not sure if it would be any faster than using Graphics.DrawMesh, but have you tried using the drawing functions in the GL class?

link: http://docs.unity3d.com/Documentation/ScriptReference/GL.html

The docs say you should use it in OnPostRender, but I think calling it during OnGUI should work as well (just make sure Event.current’s type is EventType.repaint). Though if you’re drawing to a render texture I imagine the former will work too.

If I’m understanding your situation correctly, you should be able to use this to set the material to whatever you were using for the quads, and then use a series of GL.Vertex calls to draw them all at once. It should stay within one draw call as long as the material doesn’t change.

there is a package named tc particles in the assetstore which simulates “millions” of particles on the gpu (dx11). if i’m not mistaken you can even have forces on them and collisions.

do you put these quads all in one mesh? the problem is that you need to move and update them regulary what means the mesh data need to be send to the gpu every frame. but this is a constant cost.
so my suggestion would be to have an array of the vertices, an array of the centers to calculate movement, then update the vertices with the centers postion after fluid simulation. then update the mesh.

my test:
win 7 64 bit
radeon 9600 with 1 gb gpuram
i7 860 (2.8 ghz)
8 gb ram

0 53 fps
3000-6000 30 fps quite constant
6000-10000 20fps quite constant

btw jesta in your turbulence library thread are some unanswerd questions, just incase you got no notification ;).

Giometric: That’s a good idea. I tested with the GL class and it is much faster (I can reach 10000 particles at 60 fps). However, I couldn’t manage to draw textured particles. I tried manually assigning a texture before the SetPass(0) but it didn’t work.

exiguous: I swear I tried to subscribe to my own thread a dozen times but it never worked… Really sorry about that. I’ll answer everyone today with substantial support.

I’ll try your approach and compare it to the GL drawing. Thanks for the suggestion!

Ok so I got the textures working… simply forgot to put texture coordinates in the GL calls. However, is there a way to draw only in a specific layer when using the GL class?