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

Author Topic: Optimizing a lot of sprites  (Read 11756 times)

0 Members and 1 Guest are viewing this topic.

Foaly

  • Sr. Member
  • ****
  • Posts: 453
    • View Profile
Optimizing a lot of sprites
« on: November 30, 2012, 08:14:53 pm »
Hello everybody.
I have a question on optimization of sprite rendering. In my application I have an std::deque of sprites. Think of it as a particle system. Sprites are getting added, updated (positions) and deleted a lot. For rendering I simply iterate through the deque and draw them to the render target. Around 3000 sprites you can feel the framerate getting slower. Does anybody have an idea how I could optimize this? I'm not sure maybe I could use a vertexarray, but I have never worked with those, so I don't really know how... Does anybody have an idea?
Thanks in advance, Foaly

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10901
    • View Profile
    • development blog
    • Email
AW: Re: Optimizing a lot of sprites
« Reply #1 on: November 30, 2012, 08:28:48 pm »
Does anybody have an idea how I could optimize this? I'm not sure maybe I could use a vertexarray, but I have never worked with those, so I don't really know how... Does anybody have an idea?
There have been quite a few very similar questions lately, maybe you should go through the last few weeks of posts.
The short answer as you've already notice is to use a VertexArray to reduce the OpenGL draw calls. Also you should clearly identify what brings down your performance (profile it) and optimze there.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6286
  • Thor Developer
    • View Profile
    • Bromeon
Re: Optimizing a lot of sprites
« Reply #2 on: November 30, 2012, 09:32:34 pm »
I'm not sure maybe I could use a vertexarray, but I have never worked with those, so I don't really know how... Does anybody have an idea?
The API documentation about sf::Vertex and sf::VertexArray is quite good.

Alternatively, you can see how I implemented the particle system in Thor. Or if you don't want to reinvent the wheel, you can also directly use Thor ;)
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: Optimizing a lot of sprites
« Reply #3 on: December 01, 2012, 02:56:45 am »
It depends on what exactly you do in your game and how you handle your sprites/entities.

You need to handle the four vertexes of the sprite's quad in your drawable entity class (enemy, player, bullet or whatever), as well as the global position on the sprite. You calculate the vertexes' position according to the size of the texture you are using in it.

Then you use a class that has the VertexArray and append all the vertexes to it. However to get the convenient transformation power of a sprite you need to reimplement all transformation calculations, such as the multiplying the vectors by transformation matrixes to calculate the new positions and more.

That's the approach I recently came up with at least. I want to implement it (as it sounds very interesting), but I am not sure since I don't have a real performance problem and I have little time for working on my game. If I do actually choose to do so then I'll make a thread in the project forum. It sounds like a lot of work, but I think it gets a good payoff in the end.
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

Foaly

  • Sr. Member
  • ****
  • Posts: 453
    • View Profile
Re: Optimizing a lot of sprites
« Reply #4 on: December 03, 2012, 07:55:09 pm »
Well yeah I've already read the posts from the last couple weeks. I do understand that opengl draw calls are the thing that cost the performance. So in order to optimize the rendering I would simply use an object with four sf::Vertex (Quad) and a sf::Texture. Then i would recalculate the positions every frame (check the lifetime of the object etc.) and push it to a sf::VertexArray. Then I would simply draw that. Like some kind of batching.
But I would like to use some of the convenience that sf::Sprite provides. Mainly scaling and rotation. How would I do that? Do I have to implement that myself?

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10901
    • View Profile
    • development blog
    • Email
Re: Optimizing a lot of sprites
« Reply #5 on: December 03, 2012, 08:11:46 pm »
But I would like to use some of the convenience that sf::Sprite provides. Mainly scaling and rotation. How would I do that? Do I have to implement that myself?
Use a sf::Transform and pass it to the render state and apply that when calling draw(). ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Foaly

  • Sr. Member
  • ****
  • Posts: 453
    • View Profile
Re: Optimizing a lot of sprites
« Reply #6 on: December 03, 2012, 08:32:04 pm »
Wow awesome! That looks exactly like what I need! But after reading the documentation, I am not sure if I can use it. Because say I have an object with the four vertices and a texture reference. I can't apply the sf::Transform to the draw() call, because that call is never made. It's just pushed to the vertexarray, which then is drawn later. I there a way I could still use a sf::Transform for this?
« Last Edit: December 03, 2012, 08:46:56 pm by Foaly »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10901
    • View Profile
    • development blog
    • Email
Re: Optimizing a lot of sprites
« Reply #7 on: December 03, 2012, 09:11:26 pm »
I can't apply the sf::Transform to the draw() call, because that call is never made. It's just pushed to the vertexarray, which then is drawn later. I there a way I could still use a sf::Transform for this?
Not exactly sure what's the problem and why the draw call is never made, but if you don't want to make the transformation directly you can write a class that wraps a sf::VertexArray and inherit from sf::Transformable and sf::Drawable and do all the magic inside that class, additionally you get about the same interface for your class as a sf::Sprite has. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Optimizing a lot of sprites
« Reply #8 on: December 03, 2012, 09:17:42 pm »
He has several entities in a single vertex array, and wants to transform them individually -- I think.

This is not possible unless you do all the transformations yourself directly on the CPU, with a sf::Transform and its transformPoint function.
Laurent Gomila - SFML developer

Foaly

  • Sr. Member
  • ****
  • Posts: 453
    • View Profile
Re: Optimizing a lot of sprites
« Reply #9 on: December 03, 2012, 09:25:51 pm »
Well what I mean is that my little objects won't be drawn directly, because the whole point is that I want to decrease the draw calls. So instead of drawing the objects (containing the vertices and the texture reference) I push them all in a vertex array and then draw the whole array. This will be way faster than drawing each separately (or am I wrong? Because then this would be pointless)
So by the time the vertices are being pushed to the vertex array they already have to be at their final position. So is there a way of applying a sf::Transform to the vertices (and not to the draw call)?

edit: Exactly all entities are transformed individually. But aren't the transformations always done on the CPU?

gyscos

  • Jr. Member
  • **
  • Posts: 69
    • View Profile
Re: Optimizing a lot of sprites
« Reply #10 on: December 03, 2012, 09:31:38 pm »
OpenGL supports pushing transformation matrix ; I guess these are processed on the GPU. But for the VertexArray, you can't do that, and must just transform the positions (sf::Transform has a transformPoint method ;) ) individually before adding them.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Optimizing a lot of sprites
« Reply #11 on: December 03, 2012, 09:36:34 pm »
Yes, when you draw an entity its transformation is handled directly by OpenGL and applied on the GPU, so it's a free operation. That's why doing it on the CPU can also decrease performances significantly. But it may still be a better solution than drawing a lot of individual sprites, since transforming a point by a matrix basically just involves a few multiplies and additions.
Laurent Gomila - SFML developer

Foaly

  • Sr. Member
  • ****
  • Posts: 453
    • View Profile
Re: Optimizing a lot of sprites
« Reply #12 on: December 03, 2012, 10:06:29 pm »
OK thanks for the information! I just looked a bit more at the source and saw glLoadMatrixf(transform.getMatrix()) in RenderTarget::applyTransform that's what you meant right?
So just to make sure if I understood everything correct. For every object I have to store my four vertices and a sf::Transformable (seems to have an easier control than sf::Transform). Then I apply the transformable to each vertex, push them to the array and draw that array.
So something like this (in the example the texture is 16x16 big):
sf::Vertex vertices[4];
vertices[0].texCoords = sf::Vector2f(0, 0);
vertices[1].texCoords = sf::Vector2f(0, 16);
vertices[2].texCoords = sf::Vector2f(16, 16);
vertices[3].texCoords = sf::Vector2f(16, 0);
sf::Transformable m_transform;
//...
// update
m_transform.rotate(angle);
m_transform.move(x,y);
//...
// render
vertices[0].position = m_transform.getTransform().translatePoint(0, 0);
vertices[1].position = m_transform.getTransform().translatePoint(0, 16);
vertices[2].position = m_transform.getTransform().translatePoint(16, 16);
vertices[3].position = m_transform.getTransform().translatePoint(16, 0);

for(int i = 0; i < 4; i++)
     vertexarray.append(vertices[i]);

//...
window.draw(vertexarray);
Does this look right?
Two questions remain: Where do I specify the texture? Will calculating the transformation like this and only draw one vertex array be significantly faster then drawing lots of sf::Sprite's?
« Last Edit: December 04, 2012, 12:19:10 am by Foaly »

gyscos

  • Jr. Member
  • **
  • Posts: 69
    • View Profile
Re: Optimizing a lot of sprites
« Reply #13 on: December 04, 2012, 07:10:58 am »
The method is transformPoint, not translatePoint :p
Also, you can use a sf::Transform directly :

sf::Vertex vertices[4];
vertices[0].texCoords = sf::Vector2f(0, 0);
vertices[1].texCoords = sf::Vector2f(0, 16);
vertices[2].texCoords = sf::Vector2f(16, 16);
vertices[3].texCoords = sf::Vector2f(16, 0);

sf::Transform m_transform;
m_transform.rotate(angle);
m_transform.move(x,y);

vertices[0].position = m_transform.transformPoint(0, 0);
vertices[0].position = m_transform.transformPoint(0, 16);
vertices[0].position = m_transform.transformPoint(16, 16);
vertices[0].position = m_transform.transformPoint(16, 0);

for(int i = 0; i < 4; i++)
     vertexarray.append(vertices[i]);

window.draw(vertexarray);
 
« Last Edit: December 04, 2012, 07:13:24 am by gyscos »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Optimizing a lot of sprites
« Reply #14 on: December 04, 2012, 08:08:39 am »
sf::Transformable uses a significant amount of memory (when you have thousands of them it can make a difference), and completely recomputes the transformation matrix everytime you change one of the transformation components.

If you only have a translation and a rotation to handle, you'd better use a Vector2f and a float, and apply them directly with a simple formula.

The texture must be given as a second argument when you call the draw function.
Laurent Gomila - SFML developer