2
\$\begingroup\$

We have a problem. Our game steadily slows down as we increase the number of models we draw. When the number reaches 100 - FPS is dead. Our humble tests showed that the reason is not GPU. This is what we did:

  • Rendered models with the most basic shader code - nothing changed;
  • Checked the NVidia NSigt metrics - GPU was less and less loaded per second as we increased number of models;

At the same time, a simple Windows Task manager showed that, as the number of models rise, the load increases (up to a 100%) on the single CPU core that our game uses. So we are pretty sure our game is running slowly because of the CPU.

We then moved on and tried to identify which line exactly causes the problem. We used c# TimeSpan metrics and timers for that. Our research showed that the biggest cause of the slow performance is... mesh.Draw() function used by every model that we draw.

We tried commenting this very line and things improved instantly (ofc. nothing was drawn on the screen).

So the question is, what are we to conclude from this? mesh.Draw is a closed function and we cannot optimize it. Trying to bruteforce parallel it is impossible (i guess it was natural). But something tells me that we need to look elswhere if this function is slowing the CPU, the question is where?

Update: here is the code I use to draw the model: public void Draw(...) { int index = 0;

            int i = 0;

            foreach (ModelMesh mesh in Actor.Model.Meshes)
            {

                bool isDrawable = false;
                #region Check if this ModelMesh needs to be drawn according to PartsToDraw (string) list.
                foreach (string s in PartsToDraw)
                    if (s == mesh.Name)
                        isDrawable = true;
                #endregion

                if (isDrawable)
                {
                    int effectStartIndex = index;
                    foreach (Effect effect in mesh.Effects)
                    {
                        if (Actor.MatrixPaletteParams[index] != null)
                        {
                            Actor.WorldParams[index].SetValue(World);
                            Actor.MatrixPaletteParams[index].SetValue(Actor.Palette[i]);
                        }
                        else
                            Actor.WorldParams[index].SetValue(Actor.Pose[mesh.ParentBone.Index] * World);

                        if (drawType != null && drawType.DrawShadow)
                            SetShadowEffectParameters(effect, renderer, drawType.DrawShadowMort);
                        else
                        {
                            SetCommonEffectParameters(effect, renderer);
                            SetLitEffectParameters(effect, renderer, drawType);
                        }

                        index++;
                    }
                }
                else
                    index++;

                if (isDrawable)
                    mesh.Draw();

                i++;
            }
        }

"Set...EfectParameters" methods mostly consist of effect.Parameters["SomeParameter"].SetValue() calls. I pass around 30 parameters to the GPU there, mostly floats, matrices and bools. Moreover each ModelMesh has three textures that it passes into the GPU. I have no idea how fast this works and if it can be optimized. It should be fast.

You should also note that I draw a skeletaly animated model using Dastle's XNA Animation Component library. Once again, I understand that this might be the reason behind lags, and we did find the weak spot of the animation code, but why on Earth does the metrics show that mesh.Draw is the slowest spot? What is happening there?

\$\endgroup\$
9
  • \$\begingroup\$ Is this in a release build? I kinda remember that function being quite slow in debug mode. \$\endgroup\$
    – Roy T.
    Dec 4, 2013 at 10:21
  • \$\begingroup\$ Wow, we will check the release build now, hang on... \$\endgroup\$
    – cubrman
    Dec 4, 2013 at 10:23
  • \$\begingroup\$ Draw commands are bound to be slow, thats why you try to pack and minimize statechanges, drawcalls as much as you can. \$\endgroup\$
    – Tordin
    Dec 4, 2013 at 10:27
  • \$\begingroup\$ Ok, ok but WHAT should I concentrate on? Should I try to reduce effect.Parameters calls? I know that instancing is the way to go, but I debugged Sait's Row 4 with NVidia NSight and saw that they use NO instancing whatsoever, and it runs at INSANE speed with over 4000 draw calls (can't remember acurately but the timeline was COMPLETELY blue while in our game it has a number of thin stripes across it's white background). \$\endgroup\$
    – cubrman
    Dec 4, 2013 at 10:33
  • \$\begingroup\$ Yes, thats common. A modern engine can easily draw ~10k drawcalls just fine. It´s hard to say with outseeing any code. if you could update with your draw code and how you traverse on your sceen it would be greate. \$\endgroup\$
    – Tordin
    Dec 4, 2013 at 11:09

1 Answer 1

-1
\$\begingroup\$

It's really weird that no one answered my question, but the truth is I simply hit a batch limit. Indeed Draw takes the longest time on the CPU and that is why you need to limit the number of draw calls per frame. Andrew Russell has a brilliant series of answers here about this problem: https://gamedev.stackexchange.com/a/37794/31853

\$\endgroup\$
8
  • \$\begingroup\$ So how did you resolve your problem, knowing this? \$\endgroup\$
    – ashes999
    Dec 6, 2013 at 12:20
  • \$\begingroup\$ I am working towards using instancing, just like Andrew suggests, and I am asking this question: gamedev.stackexchange.com/questions/66932/… \$\endgroup\$
    – cubrman
    Dec 6, 2013 at 18:28
  • \$\begingroup\$ -1, this answer doesn't actually address the problem described in the question. You should re-word one or both to match. \$\endgroup\$
    – user1430
    Dec 6, 2013 at 18:43
  • \$\begingroup\$ My question has already been answered before, I did not know that like many others that come here. I have posted a link to the actual answer and that was clear enough from my point of view. But you are an admin, so I have added even more explanations. If this is not enough - feel free to edit my answer. \$\endgroup\$
    – cubrman
    Dec 6, 2013 at 19:26
  • \$\begingroup\$ I seriously doubt draw calls are so limited that you cant display 100 objects at a decent framerate. Try a fifty thousand objects, that might be limiting without instancing on any CPU that came out in the last 5 years. \$\endgroup\$
    – TravisG
    Dec 6, 2013 at 20:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .