4
\$\begingroup\$

I'm using a custom particle system for my LibGDX / Java based game project (because I used Slick2D earlier on, need more parameters so I made my own and then ported). The system is fairly standard as far as I'm concerned, uses particle pooling (each emitter has its own fixed size particle pool) and renders each particle in a batch and uses one single (2048x1028) packed texture with all particle textures on it. Here is how I render the particles in semi-pseudo-code (because the entire code isn't relevant):

//ParticleSystem class
//Note: ExtendedBatch is my custom sprite batch implementation, largely just the normal SpriteBatch with two additional vertices for grayscale factor and additive tinting 
void renderEmitter(ParticleEmitter emitter, ExtendedBatch batch)
{
    ParticlePool pool = particlePools.get(emitter);

    if (emitter.shouldScissor)
        Renderer.pushScissor(emitter.scissor);

    for (Particle particle : pool.particles)
    {      
        if (particle.inUse)
              particle.render(batch);
    }

    if (emitter.shouldScissor)
        Renderer.popScissor();
}

//Particle class
void render(ExtendedBatch batch)
{
      relativeX = getRelativeX();
      relativeY = getRelativeY();

      if (isInScreenBounds(relativeX, relativeY))
      {
            batch.setColor(myColor);
            batch.draw(myTexture, position, origin, size, scale, rotation);
      }
}

Now for some reason with only around 300 particles (split up into 10 emitters with varying sizes) the performance drops to awful ~30FPS on my notebook's integrated GPU (Intel HD 4400) when I need / want 60FPS at all times. I know iGPUs aren't great, but that one is one of the better ones out there and games like Ori or Braid which have thousands of similar particles run without any problems at 60FPS on that very chip. I also doubt (and hope) that it's not just Java vs C++ which is causing this huge performance drop here.

Looking at in-game profiling data:

In-game profiling

The in-game profiling data however shows a few things: Particles aren't really taking that long to render and there is a lot of idle time. To me, that doesn't really make much sense. It looks like there would be enough resources to easily render everything at 200FPS or more, but it is stuck at a horrible 30FPS.

There are a lot of things I already tried that didn't help:

  • Packing all particle textures into one (which is 2048x1024)
  • Batching all particle draw calls
  • Profiling to find out the cause (see above)
  • VisualVM to find potential memory issues, didn't help
  • Disabling vSync and FPS locks doesn't help

For the record, here's a VisualVM CPU sample over a timespan of 2 minutes:

VisualVM CPU sample

There must be something I'm doing wrong and I also want / need more particles than just 300 so I definitely have to fix this - but I don't know how.

Update: Using the default SpriteBatch implementation and default shaders doesn't improve performance either.

Update - Solution:

I forgot to turn off MSAA. I had it running at 2x sampling rate for smoother antialising, I completely forgot to check for that. In the meantime I improved performance in a lot of other parts, but that's what finally did it.

\$\endgroup\$
  • \$\begingroup\$ Could it be that you are removing/adding a lot of particles every second? Or does your ParticlePool re-use particles? If removing is the case you could try to simply re-use particles instead of removing them (i.e. resetting the position and other variables) and see if that helps performance. \$\endgroup\$ – Charanor May 14 '16 at 21:28
  • \$\begingroup\$ @Charanor Thanks for the reply. The ParticlePool of course reuses the particles, that's the point of having it ;) It's not an allocation problem, the memory usage isn't an issue (as seen in the CPU sample and profiing data). Unfortunately it's not that easy :/ \$\endgroup\$ – flotothemoon May 14 '16 at 21:45
  • \$\begingroup\$ Your application doesn't seem CPU bound, so I would look at debugging what's happening on the GPU. The first thing that jumps out is, do you have mip maps enabled? Heavily down scaled textures can be really expensive to render without mip maps. Debugging-wise, try swapping out the texture of your particles with a 1x1 blank texture and see what happens. \$\endgroup\$ – Alex Sherman May 14 '16 at 23:00
  • \$\begingroup\$ @Vector57 Thanks for the reply. Tried that, still at measily 30FPS. \$\endgroup\$ – flotothemoon May 15 '16 at 9:34
  • \$\begingroup\$ My guess is that the problem isn't in any of the code that you've shown here. I have been able to get pretty good performance with this many particles using Libgdx's ParticleEffect class. Is there a reason you couldn't use that? \$\endgroup\$ – spectacularbob May 16 '16 at 16:15
2
+50
\$\begingroup\$

I would take a look at glDraw*Instances (Instancing) Also, reducing vertex count for a large amount of small particles is extremely important. I don't know if your using a model or a single 2D texture or what, but some general tips:

Use as few verticies as possible.

If using transparency in your textures, use glAlphaFunc to reduce blending needed.

If using textures, keep batching them, and definitely use VAO's if your not already.

A nice approach I've seen is to have a single 2D texture for particles, and have it face the camera. Minecraft uses this for some particles.

I could say more, but I don't see your rendering code above.

\$\endgroup\$
  • 1
    \$\begingroup\$ Note that glAlphaFunc is deprecated. According to this it's deprecated in OpenGL 3.1 \$\endgroup\$ – Greffin28 May 15 '16 at 16:28
  • \$\begingroup\$ Thanks a lot for the reply. I will look into instancing, but the main problem is that I'm doing virtually the same thing as the original LibGDX implementation and mine just runs much slower. That has a few sub-problems, where each of them could be the cause; like why is it only utilising 15% of the available time and spending all the other time in glSwapBuffers. \$\endgroup\$ – flotothemoon May 15 '16 at 16:47
  • \$\begingroup\$ @1337 You don't seem to be profiling GPU, look into APITrace or other software to profile OpenGL. Greffin28 Yes, ideally one would use shaders for this, but I've not played in GLSL yet. \$\endgroup\$ – JavaProphet May 15 '16 at 17:16
  • \$\begingroup\$ Turns out I left MSAA on at 2x sampling rate... yeah. That absolutely kills performance. In the meantime I improved performance in a lot of other parts so that's good anyway. But thanks a lot for your help, I awarded you the bounty. \$\endgroup\$ – flotothemoon May 18 '16 at 18:44
1
\$\begingroup\$

Dont know how your particle system is written but it is very possible that your bottle neck is the communication time between CPU and GPU. Not going to go into too much detail but just know that this operation can be very slow because it causes stalling. Perhaps the idle time you see in profiling is due to that.

What a good particle system do is to move all the particle movement calculation to the GPU so that CPU only need to communicate with GPU when a particle is spawned and when a particle needs to be destroyed.

This can be accomplished by passing some additional attribute to the GPU, such as the spawntime, start_position, velocity and acceleration of each particle. Then on the GPU, calculate the position of each particle as start_position + vel * dt + accel * dt * dt; (dt = current_time-spawntime;) in the vertex shader.

\$\endgroup\$
  • \$\begingroup\$ That's a very good idea, I'll do that if none of this works out. What could I do to reduce the communication time / stalling? It could very well be that. Maybe it's good to note that when using the dedicated graphics card instead of the integrated graphics chip it runs completely fine (60FPS). That is of course due to it being much faster, but I don't really know if that matters in terms of stalling anyway. \$\endgroup\$ – flotothemoon May 17 '16 at 8:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.