Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Best practices for sprites?  (Read 2335 times)

0 Members and 1 Guest are viewing this topic.

smurf

  • Newbie
  • *
  • Posts: 36
    • View Profile
Best practices for sprites?
« on: November 16, 2023, 07:04:29 am »
Heres the scenario:

I have an image that is composed of many 32x32 squares of sub-images. It is my spritesheet/texture atlass whatever you want to call it. Many of the squares are part of an animation. For example, a bad guy character may have 10 square images which make up a walking animation. I create a Texture of this image, and plan to create Sprites for each 32x32 square in the texture.

The question is how many sprites should I actually create. So far I have exactly 1 sprite per 32x32 square. If there are many objects on screen with the same appearance (for eg 200 instances of the same type of bad guy), then all of these instances share the same sprite reference. When I'm looping through the bad guys to update/render them, for each of them I update the sprite properties, then draw it. For example I might change the position, rotation, color, scaling of the sprite, then render it. On the next iteration, I change the sprite properties all again for the next bad guy then draw it.

Is this an inefficient way to do things? Constantly changing the properties of a single sprite back and forth for each bad guy?

The other option I was considering was to have completely separate spite instances for each bad guy. This way, maybe I won't have to update properties as much. For example a blue bad guy can stay blue and I don't have to update the sprite's color on every iteration through the bad guy update loop. Downside I guess would be more memory. But maybe extra strain on the GPU somehow? I don't know.

The extra wrinkle is each bad guy needs 10 sprites because I change the rendered sprite every second as his walking animation needs to change.

So what is the correct way to do things? Should I try to share and re-use sprites and just change their properties as needed? Or is it better to create as many Sprites as needed and avoid changing their properties ?

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: Best practices for sprites?
« Reply #1 on: November 16, 2023, 08:52:09 pm »
No there isn't one correct way to do things, that's the fun, but also not so fun thing about programming. ;D

You can use a single sprite for all you bad guys, that's totally fine. You can also use a vector of all the bad guy sprites and render that, that's also fine. One might use a little bit more CPU, one might use a little bit more memory.
I think on the scale you're operating right now, neither the CPU nor the memory usage will be an issue, so you're essentially free to choose the option you like better.

Looking ahead, if you do eventually run into CPU or memory constraints, the alternative solution might actually be to use a vertex array instead and reduce the draw calls overall, but at the cost of having to manage vertices instead of sprites.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

smurf

  • Newbie
  • *
  • Posts: 36
    • View Profile
Re: Best practices for sprites?
« Reply #2 on: November 18, 2023, 06:56:02 pm »
To clarify, by "correct", I meant "most efficient".

I know the answer to this is usually "measure both ways", but I figured there was already a definitive answer to this by now.

Anyway, I measured both ways  :P

To my pretty big surprise, its significantly worse to have individual sprites per bad guy instance. Its better to have a single sprite per image in the texture atlas, and just change its properties (position, scale, color, rotation) for each badguy before drawing it.

This was surprising because if each badguy has its own sprite, then the sprite properties need to change way less often. I expected this approach to take a bit more memory (and it did - 90MB vs 85MB or similar, so totally not a factor), and I expected it to use less CPU resulting in better FPS.

In fact the FPS reduced from 120 to 75 by using this approach!

I think the conclusion is that somehow sprites are not as lightweight as I originally thought, and its better to re-use them than to create them freely for different purposes.



eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: Best practices for sprites?
« Reply #3 on: November 19, 2023, 07:48:33 pm »
Are you making sure you're not allocating the whole selection of sprites every frame?
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

smurf

  • Newbie
  • *
  • Posts: 36
    • View Profile
Re: Best practices for sprites?
« Reply #4 on: November 20, 2023, 02:22:55 am »
Yep

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Best practices for sprites?
« Reply #5 on: November 20, 2023, 03:45:10 pm »
To clarify, by "correct", I meant "most efficient".
I figured there was already a definitive answer to this by now.
I think it's likely that when multiple sprites (or re-used sprites) are used, people are not too worried about speed issues because when they are, they stop using both methods and use vertex arrays (or similar) so this would be a possible reason exact timings of those two methods have not been so deeply investigated.

somehow sprites are not as lightweight as I originally thought
Sprites are lightweight. The operations you can cause them to perform, however, can not be so much. Mainly draw calls of course but others could be changing textures or major maths (e.g. incredibly severe number of transformations/projections).

CONCLUSION:
If you're struggling to keep 75+ FPS just from how you're using your sprites, it's likely you need to not make so many draw calls i.e. use vertex arrays instead ("batching sprites").
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

smurf

  • Newbie
  • *
  • Posts: 36
    • View Profile
Re: Best practices for sprites?
« Reply #6 on: November 20, 2023, 10:34:13 pm »
Yea, I'm hearing you guys that vertex arrays are the way to go. I think I tried them long ago, and I forget what but something about them didnt quite work for my scenario. I'll look again.

But as a programmer, I still want to understand whats going on with the sprites.

with pseudocode, here are the 2 scenarios boiled down:

#1:
foreach (var baddie in baddies) {
        theOnlySprite.x = baddie.x;
        theOnlySprite.y = baddie.y;
        theOnlySprite.color = baddie.color;
        theOnlySprite.rotation = baddie.rotation;
        theOnlySprite.scale = baddie.scale;
       
        window.Draw(theOnlySprite);
}
 

#2:
foreach (var baddie in baddies)
{      
        window.Draw(baddie.mySprite);
}
 


#2 has much less setting of variables and creation of unnecessary new objects. The only downside I can see for #2 is more memory usage (and its hardly any). Its the same number of draw calls either way. But #2 is way slower.

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Best practices for sprites?
« Reply #7 on: November 20, 2023, 11:43:50 pm »
I wouldn't expect #2 to be an issue. With that said, I don't think I noticed you were not using C++ previously so I don't exactly what those lines do.
For example, in a "foreach loop", I would expect to be using a reference to each sprite instead of copying it each time. (e.g. for (auto& sprite : sprites))

So, "baddies" is a vector of sprites and #1 goes through each one, copies its stuff to another sprite and then draws that "only sprite"?

I wonder if there is some texture switching happening somewhere because that can impact performance.
Maybe drawing multiple sprites is causing the texture to be set each time whereas drawing the same sprite each time is not changing its texture (and you aren't copying which texture to the "only sprite"). Maybe some optimisation is happening there.

How many sprites are there and why is it only 120 FPS?

Also, are you trying these in debug mode or release mode?
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

smurf

  • Newbie
  • *
  • Posts: 36
    • View Profile
Re: Best practices for sprites?
« Reply #8 on: November 21, 2023, 06:04:43 pm »
Your understanding of the code is correct. There is no behind the scenes copying of objects or anything that would be hiding a big performance impact.

I also wonder if there is any texture switching going on. But there absolutely shouldn't be because there is only 1 texture in the entire app, and every sprite references it when it is created, and it never changes.

I'm drawing 20,000 sprites. Release mode, but Debug mode is the same (slightly slower but still a big relative difference between the 2 approaches)