SFML community forums

Help => General => Topic started by: cooldog99 on June 07, 2011, 10:12:37 pm

Title: Question about optimizing in SFML
Post by: cooldog99 on June 07, 2011, 10:12:37 pm
Hey guys. It's been awhile, and if you know us, "XSG Team" you know we have a game out called "Paradox".
unfortunately this is cancelled until further notice due to an increase of lag.

My questions are why is it that our game drops 30 fps when we spawn 5 "zombies".

Zombies are basically a small simple class, with a few int variables and a sprite, NOT IMAGE.

The game DOES do frustrum culling, and currently runs at 40-50+ fps with over 256x256 tiled (64x64 pixel) tiles.

I've tried optimizing like crazy, but to no avail.
We are no C++/SFML masters, so that's why we're reaching out to this awesome community.

We've tried to make a "Stamp" sprite instead of a bunch of sprites, and just have it change image/location each call in a for loop, but that made it WORSE.

I don't get it, the game does NOT draw outside the view window, yet it lags with 5 entities that aren't drawn? The updating is only a for loop that iterates for each entity.

it's not just the entities that cause lag, unfortunately.

Is there any further optimization tricks for SFML you guys can share? (v1.6 currently, 2.0 is too buggy for our taste).
Title: Question about optimizing in SFML
Post by: Nexus on June 07, 2011, 10:26:52 pm
Can you show a minimal code example of your rendering? What graphics card do you have? Do the SFML samples work well?
Title: Question about optimizing in SFML
Post by: Laurent on June 07, 2011, 10:30:09 pm
There's definitely something wrong in your code if you can draw hundreds of tiles but not 5 zombies.

Quote
2.0 is too buggy for our taste

May I ask what bugs are blocking you? I think 2.0 is already more stable than 1.6.
Title: Question about optimizing in SFML
Post by: Hiura on June 07, 2011, 10:32:20 pm
There are too many factor involved here to tell you what make your app going so slow. Did you try using some tool to find out where the bottleneck is ? (Like profiling your app.)

Quote
2.0 is too buggy for our taste
What do you mean ? Please help us make it better.  :wink:
Title: Question about optimizing in SFML
Post by: cooldog99 on June 07, 2011, 10:35:41 pm
Quote from: "Laurent"
There's definitely something wrong in your code if you can draw hundreds of tiles but not 5 zombies.

Quote
2.0 is too buggy for our taste

May I ask what bugs are blocking you? I think 2.0 is already more stable than 1.6.


Well, me and my other dev-team partner tend to transfer our projects over to each other. and Even though we have the SAME lib, project settings/properties and files, it wont render on his but it does on mine?

As for drawing the tiles, it lags with those as well. Without them it's 150fps, with it's 50.
it only draws the ones I see too. and there is little updating on those, so what's going on?

I can't exactly show the updating code, as it's pretty messy atm.
but I can show you the drawing loops basically.

Code: [Select]

            int ZombieAmountSize = Zombies.size();
            for (int i = 0; i < ZombieAmountSize; ++i)
{
                if (Zombies[i].Alive)
{
float X1 = Zombies[i].Sprite.GetPosition().x;
float Y1 = Zombies[i].Sprite.GetPosition().y;
sf::FloatRect tempRect;
tempRect.Left = X1;
tempRect.Top = Y1;
tempRect.Right = X1 + 64;
tempRect.Bottom = Y1 + 64;

sf::FloatRect tempRect2;

tempRect2 = View.GetRect();
float R = Zombies[i].Sprite.GetColor().r;
float G = Zombies[i].Sprite.GetColor().g;
float B = Zombies[i].Sprite.GetColor().b;
Zombies[i].Sprite.SetColor(sf::Color(R, G, B, Zombies[i].Alpha));
if (tempRect.Intersects(tempRect2))
{
Window->Draw(Zombies[i].Sprite);
}
}
}


Keep in mind, this code is similar to the other drawing ones.
And the updating is really simple as well..

And yes I tried profiling, but I'm not a pro at that either =/
Title: Question about optimizing in SFML
Post by: Laurent on June 07, 2011, 10:40:18 pm
Quote
and Even though we have the SAME lib, project settings/properties and files, it wont render on his but it does on mine?

So please make a detailed bug report :)

It's hard to tell what slows down a complete application. If you get better performances by simply commenting the Window->Draw(Zombie) line, then there's nothing you can do. If it doesn't help, there's definitely something to optimize in your own code.
Title: Question about optimizing in SFML
Post by: cooldog99 on June 07, 2011, 11:01:17 pm
Quote from: "Laurent"
Quote
and Even though we have the SAME lib, project settings/properties and files, it wont render on his but it does on mine?

So please make a detailed bug report :)

It's hard to tell what slows down a complete application. If you get better performances by simply commenting the Window->Draw(Zombie) line, then there's nothing you can do. If it doesn't help, there's definitely something to optimize in your own code.


How would I make a detailed bug report? o.O
Yeah, I know there's no real way of determining the problem. Maybe I can give more insight on our program.

the thing is, it only runs the window->Draw(zombies) code when the zombie is in view of player...

here's our basic structure of program.. VERY BASIC VIEW

main loop
App clear
game state switch loop
case game { game.sendvars(&App); game.update(); game.draw() }
App display

Game Update
for (zombies) //for each zombie in zombies (Not real code, lul)
check if collision with player/world objects/ world items.
move/hurt zombie accordingly.

Game Draw
for (zombies)
if zombies are in bounds of view rect, draw them
Title: Question about optimizing in SFML
Post by: Laurent on June 07, 2011, 11:13:16 pm
Quote
check if collision with player/world objects/ world items

What about this one? It looks quite heavy in terms of computation.

Have you tried to comment the line that draws zombies and see if it makes your app run faster?

More generally, you should gradually disable parts of your code to see which one is eating all the performances -- instead of trying to find the guilty line directly, just by looking at the code.
Title: Question about optimizing in SFML
Post by: cooldog99 on June 07, 2011, 11:25:27 pm
Quote from: "Laurent"
Quote
check if collision with player/world objects/ world items

What about this one? It looks quite heavy in terms of computation.

Have you tried to comment the line that draws zombies and see if it makes your app run faster?

More generally, you should gradually disable parts of your code to see which one is eating all the performances -- instead of trying to find the guilty line directly, just by looking at the code.


Here it is, with minor lines taken out (minimal)
remember we're not C++ masters, so if we made a "no-no" please tell us o.o


Code: [Select]

void Zombie::Update()
{
    int BulletSizeMax2 = mainGame.bullets.size();
    for (int j = 0; j < BulletSizeMax2; ++j)
                {
                    if (mainGame.bullets[j].Alive)
                        if (PixelCollision(Sprite,mainGame.bullets[j].BulletSprite,200))
                            {
                                if (Health > 0)
                                    {
                                        //hurt/move zombie.
                                        Sprite.SetPosition(ZomX, ZomY);
                                       //add new blood effect.
                                        mainGame.BloodSplatter.push_back(blood);
                                        break;
                                    }

                            }
                }

    if (Health <= 0 && Alive)
    {
        CanHurtPlayer = false;
        if (Alpha > 0 && Alive)
            Alpha -= 10;

        if (Alpha <= 0 && Alive)
        {
            Alive = false;
        }

    }

    if (Alive)
    {

            for (std::list<Item>::iterator lolk = mainGame.WorldItems.begin(); lolk != mainGame.WorldItems.end(); ++lolk)
            {

                if ((*lolk).UniqueID == 18)
                {
                        //check if item can interact with zombie
                        if (PixelCollision(Sprite, (*lolk).Sprite, 50) && CanHurtPlayer)
                        {
                            //do stuff, then delete item.
                            mainGame.WorldItems.erase(lolk);
                            break;
                        }
                }
            }
            //move zombie

            //check for collision with player
            if (PixelCollision(mainGame.Sprite, Sprite, 50) && CanHurtPlayer)
                {
                    //hurt player
                }
            int WorldObjectsSizeMax = mainGame.WorldObjects.size();
            for (int l = 0; l < WorldObjectsSizeMax; ++l)
            {
                  //loop through world objects, and have zombie interact with them.

            }
 //move zombie finally
}
}


all the above code is called within zombie class
and the code that calls it

Code: [Select]

int ZombieSizeMax = Zombies.size();
for (int i = 0; i < ZombieSizeMax; ++i)
{
                    Zombies[i].Update();
                }



Does this help?
Title: Question about optimizing in SFML
Post by: Disch on June 07, 2011, 11:44:22 pm
Code: [Select]
PixelCollision(Sprite,mainGame.bullets[j].BulletSprite,200))

PixelCollision?

You're not really checking every single pixel, are you?

If you are, that has to be the problem.
Title: Question about optimizing in SFML
Post by: Laurent on June 07, 2011, 11:44:23 pm
I'm not sure you understand what I mean.

My point is that you should do tests, which consist of disabling parts of the code (drawing, collision detection, etc.) then run the game -- in order to identify the part of your code that eats all the performances. There's no point looking at your whole code if only a very small part of it is relevant -- and you can find it easily with tests.

By the way,
Quote
How would I make a detailed bug report? o.O

Open a new topic and describe the bug, with as many useful information as you can about it.
Title: Question about optimizing in SFML
Post by: cooldog99 on June 07, 2011, 11:46:44 pm
Quote from: "Disch"
Code: [Select]
PixelCollision(Sprite,mainGame.bullets[j].BulletSprite,200))

PixelCollision?

You're not really checking every single pixel, are you?

If you are, that has to be the problem.


Is it really that CPU consuming??

Quote from: "Laurent"
I'm not sure you understand what I mean.

My point is that you should do tests, which consist of disabling parts of the code (drawing, collision detection, etc.) then run the game -- in order to identify the part of your code that eats all the performances. There's no point looking at your whole code if only a very small part of it is relevant -- and you can find it easily with tests.

By the way,
Quote
How would I make a detailed bug report? o.O

Open a new topic and describe the bug, with as many useful information as you can about it.


I understand, but I was trying to see if we screwed up, and someone with more C++ knowledge could help, we'll definitely do tests.

Btw, just a quick thanks to all of you who help us ;)
Title: Question about optimizing in SFML
Post by: Laurent on June 07, 2011, 11:49:46 pm
Quote
Is it really that CPU consuming??

Depends on how you implement it ;)

Quote
I understand, but I was trying to see if we screwed up, and someone with more C++ knowledge could help, we'll definitely do tests.

We're not gods or compilers. It's better for everyone if you can reduce the amount of code to examine before posting it ;)
Title: Question about optimizing in SFML
Post by: Disch on June 08, 2011, 12:00:55 am
Quote
Is it really that CPU consuming??


It can be very consuming.  Espeically if you're getting the pixel data from the video card for every check.

Every time you get pixels from the video card it has to move from VRAM to CPU RAM.  In addition to being a copy of the whole image (which is a big copy), it has to move along the bus which is also very slow.  What's more, while the video card is spending it's time getting pixel data, it can't do things it would otherwise be doing, like rendering stuff.


But all that aside, doing pixel-by-pixel collision is a rather absurd and overly complicated way to do this anyway.  Why not a simple bounding box?
Title: Question about optimizing in SFML
Post by: cooldog99 on June 08, 2011, 12:03:44 am
Quote from: "Disch"
Quote
Is it really that CPU consuming??


It can be very consuming.  Espeically if you're getting the pixel data from the video card for every check.

Every time you get pixels from the video card it has to move from VRAM to CPU RAM.  In addition to being a copy of the whole image (which is a big copy), it has to move along the bus which is also very slow.  What's more, while the video card is spending it's time getting pixel data, it can't do things it would otherwise be doing, like rendering stuff.


But all that aside, doing pixel-by-pixel collision is a rather absurd and overly complicated way to do this anyway.  Why not a simple bounding box?


Erm, To be honest, I'm not even sure why most of them are pixel collision...I've reverted most back to rectangular and it helps alot.

Thanks for pointing that out ^_^
Title: Question about optimizing in SFML
Post by: Groogy on June 08, 2011, 12:10:06 am
Quote from: "Laurent"
We're not gods
I am! :D j/k

Anyway what changes have you done since last? Since it wasn't that bad when I tried it for you? You could have several zombies on without any lag(staying around 60FPS).

Also the drawing is not the culprit here. (It might be a bit on tiles but not when it come to entities) so I would guess somewhere in your logic.

A very simple and probably one of the best debugging tests is what Laurent suggests. IF you think drawing is the culprit, just comment out THAT line and nothing more and see what happens. Does the FPS go up significantly? No then the problem is somewhere else and I think it's in your logic, especially since you got pixel collision. It is generally slow, there are tricks to speed it up but compared to other tests it is really slow. (Imagine a 16x16 image using a AABB test versus pixel test. Either we have 4 comparisons or we have 256 if it's a primitive version)
Title: Question about optimizing in SFML
Post by: cooldog99 on June 08, 2011, 12:17:15 am
Quote from: "Groogy"
Quote from: "Laurent"
We're not gods
I am! :D j/k

Anyway what changes have you done since last? Since it wasn't that bad when I tried it for you? You could have several zombies on without any lag(staying around 60FPS).

Also the drawing is not the culprit here. (It might be a bit on tiles but not when it come to entities) so I would guess somewhere in your logic.

A very simple and probably one of the best debugging tests is what Laurent suggests. IF you think drawing is the culprit, just comment out THAT line and nothing more and see what happens. Does the FPS go up significantly? No then the problem is somewhere else and I think it's in your logic, especially since you got pixel collision. It is generally slow, there are tricks to speed it up but compared to other tests it is really slow. (Imagine a 16x16 image using a AABB test versus pixel test. Either we have 4 comparisons or we have 256 if it's a primitive version)



Well the problem seems to come within the for loops, which is to be expected.

once we removed PixelCollision and replaced with rectangular collision, it was a bit faster.

but we're not sure how to optimize for loops exactly...
Title: Question about optimizing in SFML
Post by: Groogy on June 08, 2011, 01:46:33 am
It's kinda hard to help you over the forum like this since you have problem giving us more precise information. Also you seem to be in a different timezone. I should be sleeping now ^^

Did you gain anything major by commenting out the Draw call?

How large is the world? How do you cull the tiles from being updated/tested against/rendered?

If you do a test against each tile in the world if it is shown that might be your villain.
Title: Question about optimizing in SFML
Post by: cooldog99 on June 08, 2011, 02:16:51 am
Quote from: "Groogy"
It's kinda hard to help you over the forum like this since you have problem giving us more precise information. Also you seem to be in a different timezone. I should be sleeping now ^^

Did you gain anything major by commenting out the Draw call?

How large is the world? How do you cull the tiles from being updated/tested against/rendered?

If you do a test against each tile in the world if it is shown that might be your villain.


If the tile intersects the view (window) it's drawn.
however if I comment our draw line for TILES, I get 130 fps vs, 50.

Zombies are now fine. But Tiles are my issue...

World is (192x192)x64 now. (64 being width/height of tile)
Title: Question about optimizing in SFML
Post by: Groogy on June 08, 2011, 12:45:45 pm
That means that you do test against each tile in the world? Try with this, implement a grid where each cell is as big as one screen. And then before checking against the tile you first check "What cell's are currently in view(A maximum of 4 can be seen in worst case scenario) and then you go into these cells and test against the tiles inside the cell.

Pseudo code:
Code: [Select]

for each cell in grid
{
        if( view.rect.intersects( cell.rect ) == true )
        {
                for each tile in cell
                {
                        if( view.rect.intersects( tile.rect ) == true )
                        {
                                window.draw( tile )
                        }
                }
        }
}


With this you will cut away a lot of tiles by effectively ignoring cells that is not inside the view.
This can also be used for cutting away impossible collision tests as you only need to test against the objects placed in the same cell or the neighbor cells.

Just gonna try some example math here. Let's say we have what you said, 192 x 192 tiles of the size 64. That means we got a total of 36864 tests to see if it is shown or not. Let's assume we have a 800x600 screen then only about 10x10 tiles are visible at the same time. But we still do 36864 tests to see if we can see them. With a grid we will arrange so we have 10x10 tiles in each grid cell. So that means we have 369 cells over our map. So now instead to find the tiles that can be shown we do 369 tests for see what cell is shown and 100x(the amount of cells shown,1-4) tests against the tiles to see what to draw. So instead of doing 36864 tests we have reduced it to 369 + 100 x (1-4). So worst case scenario is 769 tests.

NOTE: I might have done some numbers wrong but the fact stands that you do cut away a lot just by adding a higher hierarchy grid to test against. Depending on how your logic is you might need to tweak some things(like size of cells and so on) in order to get most optimal performance gain from it.
Title: Question about optimizing in SFML
Post by: Svenstaro on June 08, 2011, 05:43:53 pm
Currently you only seem to be poking around. Why not actually profile your code?

Get valgrind and kcachegrind. Assuming your binary is called "zombies", do this:
Code: [Select]
valgrind --tool=callgrind --callgrind-out-file=zombies.callgrind ./zombies
This will run your game in a virtual CPU and record all function calls and see how long they will take. It also records API calls and everything. It will be very slow but don't worry, it will not produce false results.
Then use kcachegrind to visualize all this in an easy and colorful browser:
Code: [Select]
kcachegrind zombies.callgrind

This should get you a whole lot farther than any amount of guessing will.

EDIT: It will look something like this (http://i.imgur.com/MTfYp.png). Pay attention to the "self" bar which show you how long a function will take by itself.
Title: Question about optimizing in SFML
Post by: cooldog99 on June 08, 2011, 07:09:34 pm
Quote from: "Groogy"
That means that you do test against each tile in the world? Try with this, implement a grid where each cell is as big as one screen. And then before checking against the tile you first check "What cell's are currently in view(A maximum of 4 can be seen in worst case scenario) and then you go into these cells and test against the tiles inside the cell.

Pseudo code:
Code: [Select]

for each cell in grid
{
        if( view.rect.intersects( cell.rect ) == true )
        {
                for each tile in cell
                {
                        if( view.rect.intersects( tile.rect ) == true )
                        {
                                window.draw( tile )
                        }
                }
        }
}


With this you will cut away a lot of tiles by effectively ignoring cells that is not inside the view.
This can also be used for cutting away impossible collision tests as you only need to test against the objects placed in the same cell or the neighbor cells.

Just gonna try some example math here. Let's say we have what you said, 192 x 192 tiles of the size 64. That means we got a total of 36864 tests to see if it is shown or not. Let's assume we have a 800x600 screen then only about 10x10 tiles are visible at the same time. But we still do 36864 tests to see if we can see them. With a grid we will arrange so we have 10x10 tiles in each grid cell. So that means we have 369 cells over our map. So now instead to find the tiles that can be shown we do 369 tests for see what cell is shown and 100x(the amount of cells shown,1-4) tests against the tiles to see what to draw. So instead of doing 36864 tests we have reduced it to 369 + 100 x (1-4). So worst case scenario is 769 tests.

NOTE: I might have done some numbers wrong but the fact stands that you do cut away a lot just by adding a higher hierarchy grid to test against. Depending on how your logic is you might need to tweak some things(like size of cells and so on) in order to get most optimal performance gain from it.


Very nice idea, we'll definitely look into it and incorporate it in the future!

Quote from: "Svenstaro"
Currently you only seem to be poking around. Why not actually profile your code?

Get valgrind and kcachegrind. Assuming your binary is called "zombies", do this:
Code: [Select]
valgrind --tool=callgrind --callgrind-out-file=zombies.callgrind ./zombies
This will run your game in a virtual CPU and record all function calls and see how long they will take. It also records API calls and everything. It will be very slow but don't worry, it will not produce false results.
Then use kcachegrind to visualize all this in an easy and colorful browser:
Code: [Select]
kcachegrind zombies.callgrind

This should get you a whole lot farther than any amount of guessing will.

EDIT: It will look something like this (http://i.imgur.com/MTfYp.png). Pay attention to the "self" bar which show you how long a function will take by itself.


I've been profiling our code alot actually, it's just so vague because it spends it's time in update/drawing, and I don't have enough specific functions just yet but thanks for the help.

P.S. you guys are awesome as always ^^