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

Author Topic: 'Slow' performance - what am I doing wrong?  (Read 5180 times)

0 Members and 1 Guest are viewing this topic.

hayer

  • Jr. Member
  • **
  • Posts: 58
    • View Profile
'Slow' performance - what am I doing wrong?
« on: May 28, 2014, 07:17:13 pm »
So I am trying to make SFML integrate nicely with Artemis-C++ port(Entity component system).
At the moment it is using about 0.39-0.5sec to draw a frame when drawing 713 "meshes" as a tiled background.

This is obviously something I'm doing wrong so if anyone can point me in the right direction that would be great!

Edit
Meant to press preview, not save.

I know that I am calculating the transform every frame.
/Edit

Here is my current code;

Renderer
Quote
class Renderer
{
public:
   void addToQueue(MeshCollection* mc, Transform* t) {
      m_meshCollections.push_back(mc);
      m_transforms.push_back(t);
      m_count++;
   }

   void render(sf::RenderTarget* target) {
      sf::RenderStates rs = sf::RenderStates::Default;
      sf::Transform original = rs.transform;
      clock.restart();
      for (int i = 0; i < m_count; i++) {
         rs.transform = original;
         rs.transform *= Renderer::getTransform(m_transforms);;
         target->draw(*m_meshCollections, rs);
      }
      std::cout << m_count << " mesh collections drawn in " << clock.getElapsedTime().asSeconds() << "sec" << std::endl;
         
      m_meshCollections.clear();
      m_transforms.clear();

      m_count = 0;
   }

private:
   static inline sf::Transform getTransform(Transform* t) {
      float angle = -t->rotation * 3.141592654f / 180.f;
      float cosine = static_cast<float>(std::cos(angle));
      float sine = static_cast<float>(std::sin(angle));
      float sxc = cosine;
      float syc = cosine;
      float sxs = sine;
      float sys = sine;
      float tx = -0 * sxc - 0 * sys + t->position.x;
      float ty = 0 * sxs - 0 * syc + t->position.y;

      return sf::Transform(sxc, sys, tx, -sxs, syc, ty, 0.f, 0.f, 1.f);
   }

private:
   std::vector<MeshCollection*> m_meshCollections;
   std::vector<Transform*> m_transforms;
   int m_count;
   sf::Clock clock;
};


Mesh
Quote
class Mesh
{
   friend class MeshCollection;
public:
   sf::Vector2f offset;
   sf::Texture texture;
   sf::Vector2f size;
   float rotation;

private:
   sf::Vertex m_vertices[4];
   sf::Vector2f m_origin = sf::Vector2f(0, 0);

   void updateVertices() {
      sf::Vector2u size = texture.getSize();
      m_vertices[0].position = sf::Vector2f(0.0f, 0.0f);
      m_vertices[1].position = sf::Vector2f(0, size.y);
      m_vertices[2].position = sf::Vector2f(size.x, size.y);
      m_vertices[3].position = sf::Vector2f(size.x, 0);
   }

   sf::Transform getTransform() {
      float angle = -rotation * 3.141592654f / 180.f;
      float cosine = static_cast<float>(std::cos(angle));
      float sine = static_cast<float>(std::sin(angle));
      float sxc = size.x * cosine;
      float syc = size.y * cosine;
      float sxs = size.x * sine;
      float sys = size.y * sine;
      float tx = -m_origin.x * sxc - m_origin.y * sys + offset.x;
      float ty = m_origin.x * sxs - m_origin.y * syc + offset.y;

      return sf::Transform(sxc, sys, tx, -sxs, syc, ty, 0.f, 0.f, 1.f);
   }
};

MeshCollection
Quote
class MeshCollection : public sf::Drawable
{
public:
   void add(sf::Vector2f offset, sf::Texture texture, float rotation = 0.0f, sf::Vector2f size = sf::Vector2f(1.0f, 1.0f)) {
      Mesh m;
      m.offset = offset;
      m.texture = texture;
      m.rotation = rotation;
      m.size = size;
      m.updateVertices();
      m_meshes.push_back(m);
   }

   virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const {
      // sf::Transform originalTransform = states.transform;
      for (auto m : m_meshes) {
         // states.transform = states.transform.scale(m.size).rotate(m.rotation).translate(m.offset);
         states.transform *= m.getTransform();
         states.texture = &m.texture;
         target.draw((sf::Vertex*)&m.m_vertices, 4, sf::PrimitiveType::Quads, states);
         // states.transform = originalTransform;
      }
   }

private:
   std::vector<Mesh> m_meshes;
};

Transform
Quote
class Transform : public artemis::Component
   {
   public:
      sf::Vector2f position;
      float rotation;
      // size ?

      Transform() {

      }
   };

And here is my test.
Quote
int main() {

   sf::RenderWindow window(sf::VideoMode(1024, 768), "");
   Renderer r;

   sf::Texture texture;
   texture.loadFromFile("Assets\\32x32_red.png");

   const int debugsize = 50;
   MeshCollection mcArray[debugsize * debugsize];
   Transform tArray[debugsize * debugsize];
   int c = 0;
   for (int x = 0; x < debugsize; x++)
   {
      for (int y = 0; y < debugsize; y++)
      {
         MeshCollection m;
         m.add(sf::Vector2f(0, 0), texture);
         mcArray[c] = m;
         tArray[c].position = sf::Vector2f(x * 32, y * 32);
         tArray[c].rotation = 0.0f;
         c++;
      }
   }


   while (window.isOpen())
   {
      sf::Event event;
      while (window.pollEvent(event))
      {
         if (event.type == sf::Event::Closed || sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
            window.close();
      }

      window.clear(sf::Color::Black);
      
      int c = 0;
      for (int x = 0; x < debugsize; x++)
      {
         for (int y = 0; y < debugsize; y++)
         {
            if (tArray[c].position.x > 0 && tArray[c].position.x < 1024 &&
               tArray[c].position.y > 0 && tArray[c].position.y < 768)
               r.addToQueue(&mcArray[c], &tArray[c]);

            c++;
         }
      }
      /*
      for (int i = 0; i < debugsize * debugsize; i++)
      {
         
         r.addToQueue(&mcArray, &tArray);
      }
      */
      r.render(&window);

      window.display();
   }

   return 0;
}
« Last Edit: May 28, 2014, 07:19:00 pm by hayer »

kingcools

  • Jr. Member
  • **
  • Posts: 57
    • View Profile
Re: 'Slow' performance - what am I doing wrong?
« Reply #1 on: May 28, 2014, 08:35:27 pm »
have you used valgrind to profile your code?
did you use releasemode?
have you turned your debugger off?
are all optimizations turned on in your compiler?

what is this loop doing:
for (int x = 0; x < debugsize; x++)
   {
      for (int y = 0; y < debugsize; y++)
      {
         MeshCollection m;
         m.add(sf::Vector2f(0, 0), texture);
         mcArray[c] = m;
         tArray[c].position = sf::Vector2f(x * 32, y * 32);
         tArray[c].rotation = 0.0f;
         c++;
      }
   }

the add() method probably forces the internal vector to reallocate memory every single time you are using the function which is rather slow.
your code looks rather akward, why do you add new elements on each run that on first sight never seem to change? lots and lots of wasted performance.

hayer

  • Jr. Member
  • **
  • Posts: 58
    • View Profile
Re: 'Slow' performance - what am I doing wrong?
« Reply #2 on: May 28, 2014, 09:19:21 pm »
have you used valgrind to profile your code?
did you use releasemode?
have you turned your debugger off?
are all optimizations turned on in your compiler?

what is this loop doing:
for (int x = 0; x < debugsize; x++)
   {
      for (int y = 0; y < debugsize; y++)
      {
         MeshCollection m;
         m.add(sf::Vector2f(0, 0), texture);
         mcArray[c] = m;
         tArray[c].position = sf::Vector2f(x * 32, y * 32);
         tArray[c].rotation = 0.0f;
         c++;
      }
   }

the add() method probably forces the internal vector to reallocate memory every single time you are using the function which is rather slow.
your code looks rather akward, why do you add new elements on each run that on first sight never seem to change? lots and lots of wasted performance.
ยจ

The code is ran once for creating a array of objects to draw. Can see how that will impact performance anything more than long load time?

No profiling yet.

Release mode, no debuggers attached. Yes, artemis and SFML was compiled in release mode with /O2.



Edit:
After a quick session with the VS2013 performance analyzer it seems that Renderer::render is the snail.. Pointing to this line; "target->draw(*m_meshCollections, rs);" -- any tips on how to optimize that?


Edit2:
@kingcools: My respons to you might seem a bit offsense. That was not intended, sorry. Thanks for a fast reply :)
« Last Edit: May 28, 2014, 09:25:53 pm by hayer »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: 'Slow' performance - what am I doing wrong?
« Reply #3 on: May 28, 2014, 09:24:42 pm »
MeshCollection.add copies the texture argument. Then you copy it again when you store it inside the Mesh. That's a terrible idea, especially since it's all the same texture data in the end. And it would not only help to improve the loading time, but also the drawing performances because you would not make OpenGL switch to a different texture at every draw call.
Laurent Gomila - SFML developer

hayer

  • Jr. Member
  • **
  • Posts: 58
    • View Profile
Re: 'Slow' performance - what am I doing wrong?
« Reply #4 on: May 28, 2014, 09:28:24 pm »
MeshCollection.add copies the texture argument. Then you copy it again when you store it inside the Mesh. That's a terrible idea, especially since it's all the same texture data in the end. And it would not only help to improve the loading time, but also the drawing performances because you would not make OpenGL switch to a different texture at every draw call.

So I should implement some sort of resources loader  that will;

1. User askes for "Assets\32x32_red.png"
2a. If not loaded, load it
2b. Its loaded, do nothing
3. Return a shared pointer to it

?

kingcools

  • Jr. Member
  • **
  • Posts: 57
    • View Profile
Re: 'Slow' performance - what am I doing wrong?
« Reply #5 on: May 28, 2014, 09:31:09 pm »


The code is ran once for creating a array of objects to draw. Can see how that will impact performance anything more than long load time?


my bad, i thought it was the loop inside your renderloop because those two look similiar due to the "< debugsize" condition^^

well, i second laurent

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: 'Slow' performance - what am I doing wrong?
« Reply #6 on: May 28, 2014, 09:32:03 pm »
@hayer: Or simply pass a reference to the texture and make sure that it remains alive while it's used.

The other use case you're talking about can also be meaningful, but you should have a good reasons, because things are a bit more complicated. I've written a resource module in Thor for exactly such situations, so you don't have to write it yourself. thor::ResourceCache allows you to load resources on-demand, automatically recognizing if a resource has already been loaded and if it's not used anymore, everything through shared ownership.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

hayer

  • Jr. Member
  • **
  • Posts: 58
    • View Profile
Re: 'Slow' performance - what am I doing wrong?
« Reply #7 on: May 28, 2014, 09:35:32 pm »
Jeez, Nexus.. I was looking at Thor and was about to edit my post with; "Or I could grab Thor and use that. Well, guess I'll try out Thor then.

The main reason I asked was because I don't know how well the transformations should be handled. Like, I can figure it out, but I'm not always sure I get it a 100 per-cent correct.

Well, I guess I'll try looking at resource caching and see if that improves performance.


Thank you all for fast and very useful replies  :)




Edit: Just for testing; changed the sf::Texture to sf::Texture* in the Mesh class.. Now it uses 0.0002 sec per frame, in debug. Btw; how is SFML for drawing on a terminal server to clients? Like Windows Server 2008? I suddenly got the idea of implementing a renderer for work that uses SFML and the other one uses.. GDI+    :-\
« Last Edit: May 28, 2014, 09:40:04 pm by hayer »

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: 'Slow' performance - what am I doing wrong?
« Reply #8 on: May 28, 2014, 09:47:08 pm »
As Laurent mentioned, the reason for your bad performance are the texture copies. You can easily solve that by thinking about ownership and passing references or pointers instead of copies.

Resource management systems like the one in Thor have a different purpose: to have a centralized place where resources are stored and accessed, and to do the boilerplate work (checking whether resource is already loaded, handling and reporting errors appropriately, cleaning up automatically, etc.). thor::ResourceCache comes with some complexity and requires you to use shared pointers, so it may not be ideal for every situation.

Sometimes, something more lightweight like the ResourceHolder class we made for the SFML book is more appropriate. By the way, this class is likely to be part of a future version of Thor.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

 

anything