SFML community forums

Help => Graphics => Topic started by: Solluxx on February 04, 2013, 06:44:53 am

Title: Sprite Efficiency
Post by: Solluxx on February 04, 2013, 06:44:53 am
Hola, I was just wondering what, if any, implications creating sprites on the fly has. I would think creating a texture and sprite in the header and then just calling the sprite would be more efficient, but is it ok if I just make the texture in the header and then create sprites and set their parameters on the fly, letting the scope clean them up after every draw? Will this hit the performance enough where I should be worried about it? I prefer making them on the fly for header cleanliness but meh. Any thoughts you may have would be great.

-Sam
Title: Re: Sprite Efficiency
Post by: Laurent on February 04, 2013, 07:58:22 am
Sprites are lightweight objects, the difference should not be noticeable.
Title: Re: Sprite Efficiency
Post by: Solluxx on February 05, 2013, 02:18:01 am
ok awesome ty
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 05, 2013, 08:52:19 am
I have a similar question. What if I have hundreds of objects(in tile engine, for example)? Stroring every sprite in Tile class will need more memory, but on the other hand creating them on the fly may create some perfomance issues. What is the best way to do it?
Title: Re: Sprite Efficiency
Post by: Laurent on February 05, 2013, 10:36:07 am
There's no best way. Measure the memory taken, the CPU consumption, and decide which compromise is best for your particular application.

Note that for a tile map you probably want a single vertex array instead of many sprites.
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 05, 2013, 11:27:27 am
Thanks, Laurent!

And another question: what are the main differences between using sf::Sprite and sf::VertexArray for tiles?
Title: Re: Sprite Efficiency
Post by: Laurent on February 05, 2013, 11:40:00 am
With a vertex array you can put all your tiles into a single entity, which makes a big difference when you draw it, since performances directly depend on the number of calls to the draw function.
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 05, 2013, 11:46:29 am
Thanks again, I'll try to do it that way. :)
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 05, 2013, 01:03:39 pm
Some things are not pretty clear though...
Right now I use this method. I have a tileset map, with char keys(chars are needed to read information from level file)

std::map<char, Tile*> tilesetMap;
Where Tile is just a simple struct(which will then contain collision information, animation etc.) with a texture created in a Tileset class(using the tileset sf::Image) and a sprite which uses this texture

struct Tile {
        sf::Texture texture;
        sf::Sprite sprite;
};
 

The level is stored like this:
std::vector<Tile*> map;

So when I draw a tile I just do:
int id  = x + y * width;
sf::Sprite tempSprite = map[id]->sprite;
tempSprite.setPosition(x * TILE_SIZE, y * TILE_SIZE);
mainWindow->draw(tempSprite);

What changes do I have to make to use vertex arrays?

(my method works pretty good, though
18068 kb in RAM for 4x4 map and 18088 kb for 276*15 map)
Title: Re: Sprite Efficiency
Post by: krzat on February 05, 2013, 01:32:28 pm
You can use my tilemap renderer: http://pastebin.com/SrBE69EC

You need to write custom TileProvider function to wire it with your code.
Title: Re: Sprite Efficiency
Post by: Laurent on February 05, 2013, 02:02:54 pm
First, having one texture per tile is definitely overkill. At least, use the same sf::Texture instance for tiles that are the same. And to get the best performances, store your whole tileset into a single sf::Texture (if possible).

Then why do you set the sprites' position everytime? It's a fixed value, just assign it upon loading.

But back to the point. Instead of having a vector of N sprites, requiring N draw calls, you could just gather all these static sprites (they never change, right?) into a single entity composed of many quads, that you can draw in a single call. Trust me, the difference is huge, both in terms of performances and memory consumption.
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 05, 2013, 02:16:45 pm
krzat, thanks, I don't think I will use the entire code, but some parts will be very helpful

Laurent, so do I have to keep one texture in a tileset instance(for example) and keep pointers to this texture in the tiles? If so, how do I use a part of a texture?(I'm new to SFML haven't figured this yet)

About sprite position... yeah, it was pretty stupid I guess. :) EDIT: I have to do this, because I have only n instances of Tile, where n - number of tiles in the tileset, because creating different instance for each tile in map will be overkill, so I have similar tiles in a tileset, I just draw them in a different places, therefore I have to assign sprite position for a tempSprite
 I don't have to make N calls, because I draw just those tiles which are seen on the screen at the moment.

Quote
you could just gather all these static sprites (they never change, right?) into a single entity composed of many quads
Is there any examples? I've never done such things before, so example would be very helpful and I believe it will really boost the perfomance.
Title: Re: Sprite Efficiency
Post by: Laurent on February 05, 2013, 02:46:12 pm
Quote
Laurent, so do I have to keep one texture in a tileset instance(for example) and keep pointers to this texture in the tiles?
No need to store references to the texture, sf::Sprite already does that. All you need to store for a tile (with your current design) is a sf::Sprite, ready to be drawn.

Quote
If so, how do I use a part of a texture?
sprite.setTextureRect(sf::IntRect(left, top, width, height));

Quote
Is there any examples? I've never done such things before, so example would be very helpful and I believe it will really boost the perfomance.
The corresponding tutorial is not online yet, and there's no official example that uses a vertex array yet. But if you search on this forum, and probably on the wiki, you should find interesting stuff.

Because it's not very hard, let's try a brief explanation.

If you have a sprite:
sf::Sprite tile;
tile.setTexture(&tileset);
tile.setPosition(x, y);
tile.setTextureRect(sf::IntRect(tx, ty, w, h));

window.draw(tile);

Then you can easily create a similar entity with a vertex array:
sf::VertexArray tile(4, sf::Quads);

// first Vector2f is the position, second Vector2f is the texture coordinates
tile[0] = sf::Vertex(sf::Vector2f(x, y), sf::Vector2f(tx, ty));
tile[1] = sf::Vertex(sf::Vector2f(x + w, y), sf::Vector2f(tx + w, ty));
tile[2] = sf::Vertex(sf::Vector2f(x + w, y + h), sf::Vector2f(tx + w, ty + h));
tile[3] = sf::Vertex(sf::Vector2f(x, y + h), sf::Vector2f(tx, ty + h));

window.draw(tile, &tileset);

FYI, that's more or less how sf::Sprite is implemented internally.

Now put more than 1 quad into your vertex array:
sf::VertexArray map(sf::Quads, 4 * nbTilesX * nbTilesY);
for (int i = 0; i < nbTilesX; ++i)
    for (int j = 0; j < nbTilesY; ++j)
    {
        float tx = ... // from your loaded map info
        float ty = ... // from your loaded map info

        map[(i + j * nbTilesX) * 4 + 0] = sf::Vertex(sf::Vector2f(i * w, j * h), sf::Vector2f(tx, ty));
        map[(i + j * nbTilesX) * 4 + 1] = sf::Vertex(sf::Vector2f((i + 1) * w, j * h), sf::Vector2f(tx + w, ty));
        map[(i + j * nbTilesX) * 4 + 2] = sf::Vertex(sf::Vector2f((i + 1) * w, (j + 1) * h), sf::Vector2f(tx + w, ty + h));
        map[(i + j * nbTilesX) * 4 + 3] = sf::Vertex(sf::Vector2f(i * w, (j + 1) * h), sf::Vector2f(tx, ty + h));
}

window.draw(map, &tileset);

That's it, a super optimized static tile map :)
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 05, 2013, 06:13:55 pm
There're some problems with your code, Laurent. First of all, there're some problems with your loop and second, sf::VertexArray has its arguments switched, and third, window.draw doesn't work this way, so this code doesn't work for me. :(
Title: Re: Sprite Efficiency
Post by: Nexus on February 05, 2013, 06:23:26 pm
so this code doesn't work for me. :(
If you already recognize what's wrong, you will also be able to fix it.

In the forum, the codes that people give are often meant as inspiration, to show an idea or concept. They are not intended to be copied 1:1 and to work out of the box.
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 05, 2013, 06:46:56 pm
Nexus, I fixed everything I could in this code, however I don't really know how to fix "window.draw" error
Title: Re: Sprite Efficiency
Post by: Nexus on February 05, 2013, 07:51:48 pm
Consult the API documentation (http://www.sfml-dev.org/documentation/2.0/classsf_1_1RenderWindow.php). There are two overloads for draw(), one for a sf::Drawable and one for a sf::Vertex*. Since sf::VertexArray is derived from sf::Drawable, you take the first overload.

And in the future, please post more meaningful error descriptions than "window.draw doesn't work".
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 05, 2013, 08:10:08 pm
Well, I looked through API over and over again and didn't find answer to my question, sorry.

The problem is with this line:

window.draw(sprite, &tileset);

It references pointer to tileset texture somehow, but I cannot find a way to assign a texture to a sf::VertexArray, because these examples are incomplete. I managed to draw one sprite, but I don't really get how to draw the whole map using sf::VertexArray
Title: Re: Sprite Efficiency
Post by: victorlevasseur on February 05, 2013, 08:18:02 pm
When you use a VertexArray, you pass the texture in the draw function, you don't affect the texture to the VertexArray.
Title: Re: Sprite Efficiency
Post by: masskiller on February 05, 2013, 08:18:46 pm
The RenderStates have a texture* alongside a blendmode and shader if any. Once again a look at the doc or the sources could have solved it without asking.

It would be better to construct the RenderStates with the texture (to be more explicit), a missing include can also be the cause of some compiling errors in the case you are not including the whole graphics module.
Title: Re: Sprite Efficiency
Post by: Laurent on February 05, 2013, 08:23:32 pm
Sorry. It's now fixed.
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 06, 2013, 09:19:55 am
Thanks, everyone! I've got it working now. So my game used ~19132 kb or RAM, and now it uses ~18672kb. Considering a level is not very big it's pretty good result. However I'm wondering if I'm doing it right.
So, my Tile struct looks like this now:
struct Tile {
        //collision information, animation information will be there soon
        int tx, ty; // position of tile in a tileset texture
};

Tileset is defined like this:
std::map<char, Tile*> tilesetMap;

And map is just:
std::vector<char> map

So for example my level is

SSSSS
SSSSS
SSSSS
GGGGG

And it's very easy to get corresponding tile from a tileset:
Tile currentTile = &tilesetMap[map[x + y * width]];

So, back to rendering. Here's how I set up VertexArray:

//these are not changing so it's outside drawing function
sf::RenderStates states;
states.texture = &tileset.getTilesetTexture();
sf::VertexArray map(sf::Quads, 4 * levelWidth * levelHeight);
int w, h;
w = h = 16;

...

//left, right, top, down - these are the number of tiles currently seen on screen
        for (int i = left; i < right; ++i)
    for (int j = top; j < down; ++j)
    {
        id = i + j * levelWidth;
                float tx = tileset.getTile(tiles[id])->tx;
        float ty = tileset.getTile(tiles[id])->ty;

        map[(id) * 4 + 0] = sf::Vertex(sf::Vector2f(i * w, j * h), sf::Vector2f(tx, ty));
        map[(id) * 4 + 1] = sf::Vertex(sf::Vector2f((i + 1) * w, j * h), sf::Vector2f(tx + w, ty));
        map[(id) * 4 + 2] = sf::Vertex(sf::Vector2f((i + 1) * w, (j + 1) * h), sf::Vector2f(tx + w, ty + h));
        map[(id) * 4 + 3] = sf::Vertex(sf::Vector2f(i * w, (j + 1) * h), sf::Vector2f(tx, ty + h));
        }

//So let's draw it.
        mainWindow->draw(map, states);

 

Am I drawing my map efficiently now?
Title: Re: Sprite Efficiency
Post by: Laurent on February 06, 2013, 09:37:27 am
Looks good.

Since everything is static, you build the vertex array in the init function, not everytime you draw it, right? Then why do you keep a tile structure in parallel? Once the vertex array is built, you don't need to store a separate structure of tile objects just to keep the texture coordinates.
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 06, 2013, 09:54:41 am
It's pretty useless now, yeah. But it will be very useful in future when I'll implement collision system, so I can assign different properties to each tile. And I need to rebuilt VertexArray each time I draw my level(well, because I don't need to draw each tile, I just need to draw tiles which are on the screen at the moment).

Or maybe there's no need to do that?
Title: Re: Sprite Efficiency
Post by: FRex on February 06, 2013, 10:06:20 am
There are three very different examples on the wiki, one of them is almost the same as yours but rebuilds only part of array at a time.
Title: Re: Sprite Efficiency
Post by: Laurent on February 06, 2013, 10:20:35 am
There's also the strategy to have a small number of smaller chunks, organized in a partitionning system (like a quad-tree). It's a good compromise because you don't need to rebuild anything, yet you can draw a good approximation of what's visible on screen and with very few draw calls.

Partitionning structures also help to speed up collision tests.
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 06, 2013, 10:28:46 am
FRex, I can't find those examples. Can you post links, please?
Well, my method rebuilds only part of array too. For example if I want to draw sprites idX:[2-4], idY[6-7], for example, I set:
 
left = 2; right = 5; top = 6; down = 8;
And only 24 members of VertexArray are rebuilt. Actually this is what I use(viewRect is what is seen on the screen at the moment)
int left = 0;
        if(viewRect.left > 0) left = viewRect.left / tileWidth;
        int top = 0;
        if(viewRect.top > 0) top = viewRect.top / tileHeight;

        int right = (viewRect.left + viewRect.width) / tileWidth + 1;
        int down = (viewRect.top + viewRect.height) / tileHeight + 1; // '+1', because some tiles are chopped off
        if(right > width) right = levelWidth;
        if(down > height) down = levelHeight;

Laurent, this sounds interesting. But I can't understand how can I do that. I only draw 20 * 15 tiles, so how will quad-tree look like?
Title: Re: Sprite Efficiency
Post by: Laurent on February 06, 2013, 10:48:53 am
20x15? How can it be bigger than the window? What is the size of your tiles?
Title: Re: Sprite Efficiency
Post by: FRex on February 06, 2013, 10:50:50 am
https://github.com/SFML/SFML/wiki/Sources

Quote
20x15? How can it be bigger than the window? What is the size of your tiles?
32x32 on a 640x480 window? ;)
Title: Re: Sprite Efficiency
Post by: krzat on February 06, 2013, 10:54:08 am
I can't find those examples. Can you post links, please?
Well, I gave you one before. I wonder what was wrong with it. I tried to make it as universal as possible,
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 06, 2013, 10:59:55 am
FRex, I've seen those before, I was just wondering if what tutorials from this list you were reffering to
It's 16x16 on 240x320 than scaled to 2x. I'm making NES-like game, so it's okay for now ;)

krzat, there's no trouble with your code, I was just interested in exploring more methods so I can come up with mine.
Title: Re: Sprite Efficiency
Post by: FRex on February 06, 2013, 11:02:09 am
Shader one and krzat's, scrap the 'basic' one, it's too 'basic'.
Yeah, the basic one and shader one are mine(+idea and glsl code to start off of from dbug).
I'm so promoting myself right now. :P
https://github.com/SFML/SFML/wiki/Source%3A-ShaderTileMap
That's for 32x32 but it can be changed easily to whatever sizes, unlike the example glsl from dbug, because I've written it from scratch.
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 06, 2013, 11:08:22 am
Thanks again, I'll try to come up with something later. :)
Title: Re: Sprite Efficiency
Post by: Laurent on February 06, 2013, 11:29:01 am
If your number of tiles exactly fill the window, why do you need to check which tiles are visible at all (they are all always visible)?
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 06, 2013, 11:38:19 am
Laurent, it's because of scrolling. Level is much bigger that what's on screen
Title: Re: Sprite Efficiency
Post by: Laurent on February 06, 2013, 11:42:21 am
Yeah... what I wanted to know is the total size of the map, of course ;)
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 06, 2013, 11:50:16 am
Well, I'm not really sure about that. Right now I just use 276 * 15 tiles map. This is the size of first Super Mario Bros 3. level(excluding vectical scrolling). I think my maps will be even bigger
Title: Re: Sprite Efficiency
Post by: Laurent on February 06, 2013, 12:04:53 pm
So, what's the problem with the quadtree?
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 06, 2013, 12:14:28 pm
I don't see a problem, I just don't really know how to use them in my situation :)
Title: Re: Sprite Efficiency
Post by: Laurent on February 06, 2013, 12:16:42 pm
Well... then read some tutorials ;)

And don't hesitate to come back if you have more specific problems with it.
Title: Re: Sprite Efficiency
Post by: Elias Daler on February 06, 2013, 12:31:47 pm
Well, okay.  ;D

It's nice to see such a nice community on forums. Thanks, Laurent.  ;)
Title: Re: Sprite Efficiency
Post by: albert_lazaro on May 03, 2021, 07:44:10 pm
Hi,

it must be strange that an old post receives a new comment. I'm designing the tilemap for the game and I wanted to know what did you implement at the end: Did you used a tileset based on an array of sprites or did you build it with a VertexArray?