SFML community forums
Help => Graphics => Topic started by: SuperV1234 on March 04, 2011, 10:16:38 pm
-
I'm using SFML.Net (latest version from SVN), but I think it's not a problem related to .Net, so I'm posting it here.
Anyway, I have to draw a 64x64 tile-based map, where every Tile has a List<Entity> (linked list, or std::vector for C++ users), and every Entity has a Sprite object.
I loop through every Tile, and in every Tile I loop through every Entity, and then draw the sprite on screen.
Sprites reach 5000, 6000, 7000, even 10000, and the FPS go down dramatically.
What can I do to avoid FPS getting below 60? Is SFML not capable of drawing thousands of 16x16 sprites? Is there a solution, or should I use some "dirty trick" to fix the problem?
Thanks. :)
More info:
*My Entities/Tiles are dynamic, they can change every turn, also they can be scrolled by drag'n'dropping with the mouse.
*I'm already culling Tiles outside of the window.
*I'm drawing every sprite, every frame. No RenderImages or anything else. Just plain drawing.
-
Hello,
Without your code it's a bit hard to solve your problem.
Btw, you should draw on your screen only tiles that you can see.
To my mind it's a conception problem. I mean that I have a similar map engine (using tiles) and I can show over than 30 000 sprites without any FPS decrease.
-
I think it's not a problem related to .Net
Not for sure. In fact .Net may slowdown the loops due to intensive boxing/unboxing that may implicitly occur in the code. Did you look through MSIL code?
-
Anyway, I have to draw a 64x64 tile-based map, where every Tile has a List<Entity> (linked list, or std::vector for C++ users), and every Entity has a Sprite object.
std::vector is a dynamic array.
std::list is a doubly linked list.
Somebody correct me if I'm wrong.
-
You can speed things up by putting all of your tiles into sprite sheets with one sprite for each sheet and another data structure to hold the subrect coordinates for each tile. Then to draw you just load the subrect for that tile into the sprite for its sheet and draw it to the screen. That should cut back some on the opengl call bottleneck that you are seeing.
I'm actually working on a system to dynamically create sprite sheets based on the max texture size supported by a users video card for my own tile engine for this very same reason.
-
There's probably nothing you can do, SFML 1 is bad at drawing too many things (it generates too many driver calls).
If you're not afraid you can switch to SFML 2. If all your tiles are inside a single image, you should see a big speed improvement.
-
There's probably nothing you can do, SFML 1 is bad at drawing too many things (it generates too many driver calls).
If you're not afraid you can switch to SFML 2. If all your tiles are inside a single image, you should see a big speed improvement.
So, a question on that topic: How does SFML2 draw things anyways?
I know SFML1 simply does a draw call for every sprite, and therefor it's pretty bad since even modern high end graphic cards can't handle more than maybe 10000 draw calls per frame and run at 60fps or more.
But how does SFML do it? If it used vertex arrays (does it?) you could basically draw a 100.000 (well maybe not that many, but a LOT ) sprites at a very high framerate, since the actual amount of vertices doesn't even scratch modern graphic cards.
How does rendering to a render image (etc.) work? What happens when I call sf.:Sprite::Draw() or RenderImage::draw()?
-
So, a question on that topic: How does SFML2 draw things anyways?
The same, but with less OpenGL calls. However I'd like to change this, I hope that I can make it for SFML 2.0.
But how does SFML do it? If it used vertex arrays (does it?) you could basically draw a 100.000 (well maybe not that many, but a LOT ) sprites at a very high framerate, since the actual amount of vertices doesn't even scratch modern graphic cards.
That's true as long as you can batch big amounts of vertices together. Due to the highly dynamic nature of SFML (there's no static geometry, nor even batching), I can't get the most out of VBOs; in fact immediate mode still gives the best results.
So what I want to do in SFML 2 is to provide a way for the user to build and draw static geometry (like tiled backgrounds), or large batches of geometry that share the same states (like particle systems) -- and there I'll be able to use VBOs and rise the performance limit to something acceptable.
How does rendering to a render image (etc.) work?
It uses OpenGL FBOs or P-Buffers as render targets, instead of windows. That's the only difference, the rendering process is the same.
What happens when I call sf.:Sprite::Draw() or RenderImage::draw()?
A lot of things! Look at the source code if you want to know more, I don't know what kind of details you want.
(sf::Sprite::Draw doesn't exist, by the way).
-
There's probably nothing you can do, SFML 1 is bad at drawing too many things (it generates too many driver calls).
If you're not afraid you can switch to SFML 2. If all your tiles are inside a single image, you should see a big speed improvement.
I'm using SFML 2. Should put everything in a RenderImage and draw that every frame?
Hello,
Without your code it's a bit hard to solve your problem.
Btw, you should draw on your screen only tiles that you can see.
To my mind it's a conception problem. I mean that I have a similar map engine (using tiles) and I can show over than 30 000 sprites without any FPS decrease.
I'm only drawing the sprites I see. Also, could you post the code for your 30000 sprites game? That's very impressive and I would love to see how it's done.
I think it's not a problem related to .Net
Not for sure. In fact .Net may slowdown the loops due to intensive boxing/unboxing that may implicitly occur in the code. Did you look through MSIL code?
How do I look at the MSIL code? And what should I look for?
-
On the previous note, I did a little test on ruby where I first created 1000 images and set 1000 sprites to that image. I got a FPS around 35-40 on my computer. Doing the same test but with 1 image for all of the 1000 sprites didn't change the FPS at all.
If I understand correctly, SFML will be faster if we only use one image for several sprites since only one image needs to be transferred to the GPU right? Then this test shows that it's the languages that are the bottlenecks, most probably the loops.
Here's my test code:
require 'sfml/all'
window = SFML::RenderWindow.new( [800, 600], "Performance Test" )
window.vertical_sync = true
fps_text = SFML::Text.new
fps_text.position = [700, 550]
image = SFML::Image.new( "hal-9000-eye.jpg" )
#images = Array.new( 1000 ) { SFML::Image.new( "hal-9000-eye.jpg" ) }
sprites = Array.new( 1000 ) { SFML::Sprite.new( image ) }
#sprites = Array.new( 1000 ) { | index | SFML::Sprite.new( images[ index ] ) }
frame = 0
elapsed_time = 0.0
fps = 0
while true
while event = window.get_event
if event.type == SFML::Event::Closed
exit
end
end
window.clear()
for sprite in sprites
window.draw( sprite )
end
window.draw( fps_text )
window.display()
if elapsed_time >= 1.0
fps = frame
frame = 0
elapsed_time = 0
end
elapsed_time += window.frame_time
frame += 1
fps_text.string = fps.to_s + " FPS"
end
Disabling vertical sync had no effect. I'm wondering is there anything I can do to optimize this more to get a higher FPS? I might have done something stupid in the test, I usually don't do benchmarks. The biggest lies of all time :P
Note: Ruby is much slower than C# or whatever your writing in with SFML.NET so don't worry about the low sprite count. Though I will have to worry about that :/
-
I tried drawing everything to a RenderImage, then setting a Sprite's Image to RenderImage.Image, and drawing the Sprite. No FPS gain.
-
I tried drawing everything to a RenderImage, then setting a Sprite's Image to RenderImage.Image, and drawing the Sprite. No FPS gain.
Erhm why should you? That's not what he meant. What he meant was that if you have everything you want to draw on one and single image( a spritesheet ) then there's less texture traffic that needs to be done and the draw calls will be faster.
Now you just add another indirection to the draw.
Do a similar test like me but in C#(I guess that's the language that you use) and see if you have ANY FPS gain from changing from 1000 sprites with 1000 image instances to one specific image. If you get a FPS gain then GOODIE! You got your solution. IF you don't, then it's the language that is the bottleneck.
Edit: Laurent, I'm wondering, your changes that is coming, is something like a render-list included there? Or something like that which would remove looping yourself. Just thinking in languages like this where it's expensive. If not I'll create a class and just post it here on the wiki for Ruby.
-
Laurent, I'm wondering, your changes that is coming, is something like a render-list included there? Or something like that which would remove looping yourself. Just thinking in languages like this where it's expensive. If not I'll create a class and just post it here on the wiki for Ruby.
I don't have anything coming :lol:
For now it's just an idea, but it's hard to translate to a clean API. But yes, it will probably let you build the whole background in one drawable, and render it in a single call.
-
Laurent, I'm wondering, your changes that is coming, is something like a render-list included there? Or something like that which would remove looping yourself. Just thinking in languages like this where it's expensive. If not I'll create a class and just post it here on the wiki for Ruby.
I don't have anything coming :lol:
For now it's just an idea, but it's hard to translate to a clean API. But yes, it will probably let you build the whole background in one drawable, and render it in a single call.
Aight since it's just an idea I'll just create sfml/ext page under projects in the wiki which will eventually contain some neat tricks for use in rbSFML.
-
That was surprising, I created a RenderList class coded in C to avoid any potential overhead by ruby but I didn't get ANY difference in FPS when using it.
You others who could render over 10 000, how did you do? I must missing something in the bigger picture.
Here's the render method:
static VALUE RenderList_Render( VALUE self, VALUE aTarget, VALUE aRenderer )
{
VALUE array = rb_iv_get( self, "@array" );
long length = RARRAY_LEN( array );
ID symDraw = rb_intern( "draw" );
for( long index = 0; index < length; index++ )
{
VALUE entry = rb_ary_entry( array, index );
rb_funcall( aTarget, symDraw, 1, entry );
}
return Qnil;
}
I'm starting to think that I've done something really bad in the Draw method for RenderTarget.
-
You should test in C or C++ the same program that you wrote in Ruby, just to make sure that the problem is not your hardware/drivers configuration.
Honestly, I don't think that loops in Ruby (or anything else of the language) can be the bottleneck in a graphical application.
-
Pheeeew that scared the crap out of me.
I got the same frame rate in C++ with equivalent code.
Though it's kind of weird that I only have ~40 FPS with 1 000 sprites. Must have an old driver or something.
You guys who get a decent frame rate for 10 000+ sprites, do you do anything special to get that or you just loop trough them and render?
-
There's nothing special to do in SFML, really. Looping and rendering each sprite one by one is the only way to go.
Do you have bad performances on other (non-SFML) graphics applications?
-
There's nothing special to do in SFML, really. Looping and rendering each sprite one by one is the only way to go.
Do you have bad performances on other (non-SFML) graphics applications?
Nope, I play most games on a +40 frame rate I think. I'll have a look after I've updated my drivers.
Update: I've updated my drivers and still low frame-rate on 1000 sprites. To have something to reference with I'll check how it looks in Oblivion. Though if it lags there I'll create a new topic since it feels this has strayed away from the original.
Might be my computer. Oblivion worked fine but the FPS wasn't what I hoped for. I'll test at the university too the same code, same game and see what I get. It's a similar card just not the mobile version.
GPU: GeForce 9700M GT 32 @ 625MHz
CPU: Intel Core 2 Duo CPU @ ~2.2Ghz
Could just be me having a crappy card.
-
You could analyze the application with a profiler and see what consumes most of the time.
Are the applications that run well based on DirectX? Do you have OpenGL code which behaves fast?
-
Well my profiler don't seem to function that well.. It tells me I'm spending 100% of my time in std::vector operator[].
Anyway I couldn't see Oblivions frame-rate directly. Though it felt okay at normal detail. If I guessed I would say it is the same. League Of Legends sit around 30-40 FPS actually now when I looked while I was playing. I think both of them are DirectX apps.
Though I think it's weird that my card would be able to display a 3D world but have a hard time displaying 1000+ sprites.
Is there something on my end I might have missed that I could do to improve it?
Sorry Original Poster, this might not even have anything to do with your problem. But it might just be that you can't show more than 10k because of the card. If you move the application to another computer with a better card/drivers then you should see improvement if this is in fact the problem.(I'll do it myself when I get to the university.)
Aight sorry again for hijacking.
-
Are you sure that you compile in release mode with enabled optimizations and any security features (such as checked iterators/indices) as well as the debug runtime environment are disabled?
What if you use a raw array instead of std::vector?
-
@ SuperV1234:
How do I look at the MSIL code? And what should I look for?
To get MSIL code out of a built managed assembly you may use MS utility IL DASM (IL disassembler).
An example of what ILDASM gives:
.....
IL_000c: ldloc.0
IL_000d: box [mscorlib]System.Int32
IL_0012: ldstr ", "
IL_0017: ldloc.1
IL_0018: unbox.any [mscorlib]System.In32
.....
You can see box and unbox operations. They are performed when value types are converted to reference types (to place objects in the managed heap) and vice versa. Depending on your code there may be lots of these operations. They may be rather time-consuming and make your loops heavy. I'm afraid I can't give you detailed explanation as English is not my native language. You may use googling to find relevant info on this topic. :wink:
-
See this thread:
http://www.sfml-dev.org/forum/viewtopic.php?t=3173
My answer at the bottom is the way to draw thousands of tiles at high frame rates.
-
Hopefully this won't be necessary anymore in SFML 2.
-
Just render the tiles that you are watching on screen. No try render the whole map.
With this the performance will increase.