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

Author Topic: Drawing thousands of sprites  (Read 20082 times)

0 Members and 1 Guest are viewing this topic.

SuperV1234

  • SFML Team
  • Full Member
  • *****
  • Posts: 190
    • View Profile
Drawing thousands of sprites
« 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.

Hubert

  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Drawing thousands of sprites
« Reply #1 on: March 05, 2011, 12:39:11 am »
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.

Relic

  • Newbie
  • *
  • Posts: 43
    • View Profile
Drawing thousands of sprites
« Reply #2 on: March 05, 2011, 05:47:48 am »
Quote
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?

XDest

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Drawing thousands of sprites
« Reply #3 on: March 05, 2011, 06:37:52 am »
Quote from: "SuperV1234"
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.

Breakman79

  • Jr. Member
  • **
  • Posts: 58
    • View Profile
Drawing thousands of sprites
« Reply #4 on: March 05, 2011, 07:07:54 am »
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.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Drawing thousands of sprites
« Reply #5 on: March 05, 2011, 09:17:09 am »
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.
Laurent Gomila - SFML developer

heishe

  • Full Member
  • ***
  • Posts: 121
    • View Profile
Drawing thousands of sprites
« Reply #6 on: March 05, 2011, 11:44:20 am »
Quote from: "Laurent"
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()?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Drawing thousands of sprites
« Reply #7 on: March 05, 2011, 01:11:41 pm »
Quote
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.

Quote
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.

Quote
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.

Quote
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).
Laurent Gomila - SFML developer

SuperV1234

  • SFML Team
  • Full Member
  • *****
  • Posts: 190
    • View Profile
Drawing thousands of sprites
« Reply #8 on: March 05, 2011, 02:01:59 pm »
Quote from: "Laurent"
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?

Quote from: "Hubert"
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.

Quote from: "Relic"
Quote
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?

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Drawing thousands of sprites
« Reply #9 on: March 05, 2011, 02:24:44 pm »
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:
Code: [Select]
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 :/
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

SuperV1234

  • SFML Team
  • Full Member
  • *****
  • Posts: 190
    • View Profile
Drawing thousands of sprites
« Reply #10 on: March 05, 2011, 02:49:36 pm »
I tried drawing everything to a RenderImage, then setting a Sprite's Image to RenderImage.Image, and drawing the Sprite. No FPS gain.

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Drawing thousands of sprites
« Reply #11 on: March 05, 2011, 02:57:00 pm »
Quote from: "SuperV1234"
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.
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Drawing thousands of sprites
« Reply #12 on: March 05, 2011, 04:24:59 pm »
Quote
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 Gomila - SFML developer

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Drawing thousands of sprites
« Reply #13 on: March 05, 2011, 04:30:04 pm »
Quote from: "Laurent"
Quote
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.
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Drawing thousands of sprites
« Reply #14 on: March 06, 2011, 06:36:46 pm »
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:
Code: [Select]
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.
Developer and Maker of rbSFML and Programmer at Paradox Development Studio