Home > Back-end >  SFML slow when drawing more than 500 shapes
SFML slow when drawing more than 500 shapes

Time:11-28

I am new to SFML and I would like to implement a fluid boids simulation. However, I realized that when more than 500 shapes are drawn at the same time, the fps drop quickly.

How can I improve the code below to make it much faster to run?

#include <SFML/Graphics.hpp>
#include <vector>
#include <iostream>
sf::ConvexShape newShape() {
    sf::ConvexShape shape(3);
    shape.setPoint(0, sf::Vector2f(0, 0));
    shape.setPoint(1, sf::Vector2f(-7, 20));
    shape.setPoint(2, sf::Vector2f(7, 20));
    shape.setOrigin(0, 10);
    shape.setFillColor(sf::Color(49, 102, 156, 150));
    shape.setOutlineColor(sf::Color(125, 164, 202, 150));
    shape.setOutlineThickness(1);
    shape.setPosition(rand() % 800, rand() % 600);
    shape.setRotation(rand() % 360);
    return shape;
}

int main() { 
    sf::Clock dtClock, fpsTimer;
    sf::RenderWindow window(sf::VideoMode(800, 600), "Too Slow");
    std::vector<sf::ConvexShape> shapes;
    for (int i = 0; i < 1000; i  ) shapes.push_back(newShape());
    while (window.isOpen()) {
        window.clear(sf::Color(50, 50, 50));
        for (auto &shape : shapes) { shape.rotate(0.5); window.draw(shape); }
        window.display();
        float dt = dtClock.restart().asSeconds();
        if (fpsTimer.getElapsedTime().asSeconds() > 1) {
            fpsTimer.restart();
            std::cout << ((1.0 / dt > 60) ? 60 : (1.0 / dt)) << std::endl;
        }
    }
}

I have the following performance:

Shapes   FPS
10       60
100      60
500      60
600      60
700      55
800      50
900      45
1000     21

My goal is to have about 5k boids on the screen.

EDIT

I am building the project on Windows 11 under WSL2 with vGPU enabled. When testing natively on Windows 11 with Visual Studio I get much better performance (I can run 5k boids at 60 FPS)

CodePudding user response:

The problems are a lot of draw calls. That is slow part of this program. In order to fix this, we can put all triangles into single vertex array and call draw upon only that array. That way we will speed up program. Problem with it is that you must implement your own rotate method. I implemented the method below and edited so the function returns triangles in single vertex array.

#include <SFML/Graphics.hpp>
#include <iostream>
//This function returns vertex array by given number of triangles
sf::VertexArray newShape(int numberOfTriangles) {
    sf::VertexArray shape(sf::Triangles);
    //we are going trough every point in each triangle
    for (int i=0;i<3*numberOfTriangles;i  ){
        //creating points of triangles as vertexes 1, 2 and 3
        sf::Vertex v1(sf::Vector2f(rand() % 800, rand() % 600));
        sf::Vertex v2(sf::Vector2f(v1.position.x - 7, v1.position.y - 20));
        sf::Vertex v3(sf::Vector2f(v1.position.x   7, v1.position.y - 20));
        //setting color
        v1.color = v2.color = v3.color = sf::Color(49, 102, 156, 150);
        //rotating for random angle
        sf::Transform transform;
        transform.rotate(rand()%90, (v2.position.x   v3.position.x) / 2,v1.position.y - 10);
        v1.position = transform.transformPoint(v1.position);
        v2.position = transform.transformPoint(v2.position);
        v3.position = transform.transformPoint(v3.position);
        //appending them into vertex array
        shape.append(v1);
        shape.append(v2);
        shape.append(v3);

    }
    return shape;   
}
//rotate function finds the middle of 3 vertexes and rotates them
void rotate(sf::VertexArray& array, double angle){
    for (int i=0;i<array.getVertexCount();i =3){
        sf::Transform transform;
        transform.rotate(angle, (array[i 1].position.x   array[i 2].position.x) / 2, (array[i].position.y   array[i 1].position.y)/2);
        array[i].position = transform.transformPoint(array[i].position);
        array[i 1].position = transform.transformPoint(array[i 1].position);
        array[i 2].position = transform.transformPoint(array[i 2].position);
    }

}
int main() { 
    sf::Clock dtClock, fpsTimer;
    sf::RenderWindow window(sf::VideoMode(800, 600), "Too Slow");
    //creating new array with 30000 triangles
    sf::VertexArray shapes = newShape(30000);
    window.setFramerateLimit(60);
    while (window.isOpen()) {
        //event to close window on close button
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        window.clear(sf::Color(50, 50, 50));
        //no need for for now, as you can rotate them all in function and draw them together
        rotate(shapes,5); 
        window.draw(shapes); 
        window.display();
        float dt = dtClock.restart().asSeconds();
        if (fpsTimer.getElapsedTime().asSeconds() > 1) {
            fpsTimer.restart();
            std::cout << ((1.0 / dt > 60) ? 60 : (1.0 / dt)) << std::endl;
        }
    }
}

The bottleneck now is not drawing but rotating, i tested this code with 100000 triangles and im getting around 45 fps. With 1000000 i am getting bad framerate because of rotation.

  • Related