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.