SFML community forums
Help => Graphics => Topic started by: TechnoCore on January 06, 2009, 05:50:34 pm
-
When i draw more than around 1000 sprites each frame, the app really slows down. Why? Is there no batching of the draw calls?
I should be able to draw *a lot* more than that, should i not? Maybe I am doing something wrong.
My app is in C++, on an Intel quad 9550, with the radieon 4870 gfx card.
Thanks for any help!
/ TC :)
-
There's no batching, because SFML is too low level (i.e. it has no global knowledge about what you're drawing). How would you batch with the current interface? After you call window.Draw(sprite) you expect your sprite to be drawn on screen, not to be put in a list-to-be-rendered-some-time-later. Moreover, the order of call defines the order of drawing, so the triangles can't be reordered to allow batching.
A class will probably added to handle explicit batching, for systems such as tiles or particles. By the way, it's already in the roadmap ;)
-
Hmm.. is this the same as I'm experiencing in my game ( http://www.sfml-dev.org/forum/viewtopic.php?t=830 )???
I call the draw-method at least around 140 times per frame. And it goes really slow on my laptop.
-
Probably, even if 140 rendering calls is not really a lot to handle for the GPU.
-
Thanks for your quick answer :)
I see the problem with the ordering. Nice to hear about the batching class.
So I could then maybe create a batch and add sprites to it, like:
my_batch.AddSprite( sprite )
...and when i want to draw all sprites in the batch i just call like Window.DrawBatch( my_batch )
Or ?
/TC :)
-
Hmm.. is this the same as I'm experiencing in my game ( http://www.sfml-dev.org/forum/viewtopic.php?t=830 )???
I call the draw-method at least around 140 times per frame. And it goes really slow on my laptop.
Yeah, its because there is one draw-call per sprite. The graphics card can only handle around 1000 draw-calls per frame (or something like that) after that it will slow down alot.
In order to optimize things you can send a chunk (a batch) consisting of many sprites at the same time in a single draw-call. This is called batching.
Optimal size of a batch is something like 1000 vertices, and a sprite would consist of 4 vertices. (4 corners ), so maybe 250 sprites per draw-call could be rendered without any penalties in speed. 250 sprites * 1000 draw calls = 250.000 sprites / frame on a modern gfx card.
And just like Laurent says it's in the to-do list :)
-
So, I have a corollary to this. I'm building an asteroids-esque game, and I want to have stars flying by in the background with parallax, hue, etc.
Currently, I just a 1x1px png for my star, and an array of sprites to accomplish what I want. So my Background.Draw() method looks something like:
for (int i = 0; i < MAX_STARS; i++)
{
renderWindow.Draw(_stars[i]);
}
I've looked at my code using two different profilers, and this is where I am spending the majority of each frame. Setting MAX_STARS above 100 or so really impacts the framerate. Is there a better way to do this?
Eventually, batching will solve this problem, right?
-
Absolutely.
-
For now, the workaround I've found is simply to have my background class contain 1 sf::Image that I change the pixels of. Every Update() I write all the star pixels black, change all my structs, and write their new locations white.
It's resulted in a 10x improvement in framerate, and it allows me to draw many more stars.
-
What about shapes? Are they subject to the same problem, too?
-
What about shapes? Are they subject to the same problem, too?
What makes rendering several drawables slow, is that each object has its own set of render states, and reseting OpenGL states is what eats most of the performances. So, same problem for shapes. They might be slightly faster because they don't use textures.
For now, the workaround I've found is simply to have my background class contain 1 sf::Image that I change the pixels of. Every Update() I write all the star pixels black, change all my structs, and write their new locations white.
It's resulted in a 10x improvement in framerate, and it allows me to draw many more stars.
Even better optimization: create a Background class which inherits from sf::Drawable, and draw your stars using OpenGL commands in its Render function. You'll be able to draw millions of them ;)
-
The same goes for tilemaps. Maybe it'd be a good idea to create a new class from sf::Drawable that constructs a connected mesh (flat terrain) and applies the proper textures, thus rendering all the stuff in a row.
I'm not an OpenGL expert and I've only used it rarely directly, but what about display lists? Can't they just be created around Draw() calls for compiling them?
-
I'm not an OpenGL expert and I've only used it rarely directly, but what about display lists? Can't they just be created around Draw() calls for compiling them?
It doesn't improve performances.
I've basically tested all optimizations that don't involve major code changes (using display lists, using a state manager to avoid setting multiple times the same state, precomputing as much as possible), but nothing is enough. That's why I have to introduce a new concept in SFML, which is batching.
-
What makes rendering several drawables slow, is that each object has its own set of render states, and reseting OpenGL states is what eats most of the performances.
Can't you keep the same render states as much as possible ?
This means testing the state with glGet*/glIs* and enabling/disabling only when needed.
Edit: testing is not the point. I just mean not enabling and disabling everything for each Draw() call.
After you call window.Draw(sprite) you expect your sprite to be drawn on screen, not to be put in a list-to-be-rendered-some-time-later.
Nah, I expect the sprite to be drawn when calling Display(), so maybe you could put everything that needs to be drawn in a list and draw all that only when calling Display().
-
When batching is available will you gain anything from it even if you don't draw a lot of sprites? Will batching improve performance on all drawables (including strings)? How many sprites can you draw before you notice a performance decrease? I don't use a lot of sprites, I do draw a lot of text how ever.
-
Edit: testing is not the point. I just mean not enabling and disabling everything for each Draw() call.
That's what I do when PreserveOpenGLStates is not set. But there are still many states to set for each drawable.
Nah, I expect the sprite to be drawn when calling Display(), so maybe you could put everything that needs to be drawn in a list and draw all that only when calling Display().
What about screenshots? PostFx? They both depend on what's currently drawn in the window.
When batching is available will you gain anything from it even if you don't draw a lot of sprites? Will batching improve performance on all drawables (including strings)? How many sprites can you draw before you notice a performance decrease? I don't use a lot of sprites, I do draw a lot of text how ever.
I don't think batching will be automatic, you will have to do it explicitely if you want to use it.
-
Nah, I expect the sprite to be drawn when calling Display(), so maybe you could put everything that needs to be drawn in a list and draw all that only when calling Display().
I don't think this would increase performance. If you're drawing instantly or "just later" shouldn't increase any speed.
-
Edit: testing is not the point. I just mean not enabling and disabling everything for each Draw() call.
That's what I do when PreserveOpenGLStates is not set. But there are still many states to set for each drawable.
For example ?
Nah, I expect the sprite to be drawn when calling Display(), so maybe you could put everything that needs to be drawn in a list and draw all that only when calling Display().
What about screenshots? PostFx? They both depend on what's currently drawn in the window.
Are screenshots supposed to capture what has not been flushed ?
And couldn't post-fx effects be added in a what-is-to-be-done-when-displaying list ?
I'm interested in batching as I've begun a small 2D strategy game with three friends of mine and I noticed about 20% CPU usage (for one core) when drawing about 200 sprites. I expect to have much more to do with all the tiles of the map (about 20x20 grounds + some units => more than 400 tiles). And 40% CPU usage is really much for so few things..
-
Nah, I expect the sprite to be drawn when calling Display(), so maybe you could put everything that needs to be drawn in a list and draw all that only when calling Display().
I don't think this would increase performance. If you're drawing instantly or "just later" shouldn't increase any speed.
But you could know what are the render states required next, and skip the useless changes.
-
For example ?
Just a few actually: disabling alpha test, depth buffer and lighting. All others are not constant during the execution.
Are screenshots supposed to capture what has not been flushed ?
Screenshots are supposed to capture what has been drawn so far.
And couldn't post-fx effects be added in a what-is-to-be-done-when-displaying list ?
PostFx are supposed to be applied to what has been drawn so far.
For the two last things, I could end up writing a rendering commands queue, but I don't really want to do that ;)
I don't think this would increase performance. If you're drawing instantly or "just later" shouldn't increase any speed.
Drawing all at once enables to sort items to minimize rendering states switches. That's batching actually.
-
Yeah, by grouping the batch jobs, it is.