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

Author Topic: sf::VertexArray with multiple sf::TriangeFans  (Read 2162 times)

0 Members and 1 Guest are viewing this topic.

HUM4N

  • Newbie
  • *
  • Posts: 5
    • View Profile
    • Email
sf::VertexArray with multiple sf::TriangeFans
« on: April 12, 2023, 02:33:11 am »
Hi!
I want to draw a VertexArray of TriangleFan(s), I kinda got it to work but it does not behave as I would want it to. It connects every TriangleFan with another. First picture shows how it's done with 1000 draw calls.  Second shows how it behaves using VertexArray. Is it possible to do so that they are not connected?
Thank you for help.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: sf::VertexArray with multiple sf::TriangeFans
« Reply #1 on: April 12, 2023, 07:33:57 am »
The connectedness is part of what makes up the TriangleFan primitive:

Quote
A set of triangles connected to a central point. The first vertex is the center, then each new vertex defines a new triangle, using the center and the previous vertex.

If you don't want that, then you need to use Triangles directly, which however also means, you need to duplicate some vertices.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

HUM4N

  • Newbie
  • *
  • Posts: 5
    • View Profile
    • Email
Re: sf::VertexArray with multiple sf::TriangeFans
« Reply #2 on: April 12, 2023, 11:53:58 am »
I did just that, performance got even worse. Why? I thought draw calls are expensive?

Garwin

  • Jr. Member
  • **
  • Posts: 59
    • View Profile
Re: sf::VertexArray with multiple sf::TriangeFans
« Reply #3 on: April 12, 2023, 01:18:50 pm »
How you did it? Do you use single container for that - sf:VertexArray or similar container?

Are each frame just updating positions or recreating whole vector?

HUM4N

  • Newbie
  • *
  • Posts: 5
    • View Profile
    • Email
Re: sf::VertexArray with multiple sf::TriangeFans
« Reply #4 on: April 12, 2023, 01:47:59 pm »
class EnemyVertexArray : public sf::Drawable, sf::Transformable
{
public:
        EnemyVertexArray(const std::vector<Enemy>& enemies)
        {
                vertices.setPrimitiveType(sf::Triangles);
                vertices.resize(enemies.size() * 30 * 3);

                while(endIndex < enemies.size() * 30 * 3)
                {
                        for (size_t i = startIndex; i < endIndex; i += 3)
                        {
                                vertices[i].position = enemies[enemy].getPosition();
                                vertices[i + 1].position = vertices[i].position + sf::Vector2f{ cosf(angle) * 10.f, sinf(angle) * 10.f };
                                angle += ANGLE_INCREASE_RADIAN;
                                vertices[i + 2].position = vertices[i].position + sf::Vector2f{ cosf(angle) * 10.f, sinf(angle) * 10.f };
                        }

                        startIndex = endIndex;
                        endIndex = startIndex + 90;
                        enemy++;
                }
        }
private:
        virtual void draw(sf::RenderTarget& rt, sf::RenderStates states) const
        {
                states.transform *= getTransform();
                rt.draw(vertices, states);
        }

        size_t startIndex = 0;
        size_t endIndex = startIndex + 90;
        const float ANGLE_INCREASE_RADIAN = 0.22439947525510914f;
        float angle = 0.f;
        size_t enemy = 0;
        sf::VertexArray vertices;
};
 

It's constructed every frame because I need to check for collisions with bullets. I know it probably can be done better but I wanted something that works for now, but the performance is horrible.

Garwin

  • Jr. Member
  • **
  • Posts: 59
    • View Profile
Re: sf::VertexArray with multiple sf::TriangeFans
« Reply #5 on: April 12, 2023, 08:22:00 pm »
Is your code even working, you should not have const parameter in your construction.

I quickly measured your code and for 1000 entities it takes about 2.7 ms just to construct your VertexArray and about 0.3 ms to draw it.

You construction is quite expensive as the sin function is not a cheap function. You should create one entity that will have calculated all points and then use it for each enemy, so you only need to move positions which is cheap addition. Another thing is that you use 30 points for the circle, I would suggest using fewer points for the circle of such a radius.


HUM4N

  • Newbie
  • *
  • Posts: 5
    • View Profile
    • Email
Re: sf::VertexArray with multiple sf::TriangeFans
« Reply #6 on: April 12, 2023, 08:56:35 pm »
Yes! Your suggestion definitely has given performance increase. I rewrote entire class so now it looks like this(can adjust radius and number of sides):
class EnemyVertexArrayOptimized : public sf::Drawable, public sf::Transformable
{
public:
        explicit EnemyVertexArrayOptimized(float radius, int sides) : RADIUS{ radius }, SIDES{ sides }, ANGLE_INCREASE_RADIAN{ 360.f / sides * PI / 180.f }
        {
                vertices.setPrimitiveType(sf::Triangles);
                for (size_t i = 0; i < SIDES+1; i++)
                {
                        points.push_back({ cosf(ANGLE_INCREASE_RADIAN * i) * RADIUS, sinf(ANGLE_INCREASE_RADIAN * i) * RADIUS });
                }
        }

        void update(const std::vector<Enemy>& enemies)
        {
                vertices.clear();
                for (const Enemy& enemy : enemies)
                {
                        const sf::Vector2f enemyPos = enemy.getPosition();
                        for (int i = 0; i < SIDES; i++)
                        {
                                sf::Vertex v1(enemyPos, sf::Color::Green);
                                sf::Vertex v2(enemyPos + points[i], sf::Color::Green);
                                sf::Vertex v3(enemyPos + points[i+1], sf::Color::Green);
                                vertices.append(v1);
                                vertices.append(v2);
                                vertices.append(v3);
                        }
                }
        }
private:
        virtual void draw(sf::RenderTarget& rt, sf::RenderStates states) const
        {
                states.transform *= getTransform();
                rt.draw(vertices, states);
        }

        std::vector<sf::Vector2f> points;
        const float ANGLE_INCREASE_RADIAN;
        const float RADIUS;
        const int SIDES;
        sf::VertexArray vertices;
};
 

Is there anything else I can do to squeeze more performance out of it?

Garwin

  • Jr. Member
  • **
  • Posts: 59
    • View Profile
Re: sf::VertexArray with multiple sf::TriangeFans
« Reply #7 on: April 13, 2023, 01:07:16 am »
I have tried how many entities can SFML handle w/o antialiasing.

And I got 35 FPS with each frame creating a completely new VertexArray of 100,000 entities, each entity have 4 triangles, radius 2, so total 1,200,000 points were created each frame including each frame updating position of each point, so 100,000 entities have random generated movement in range of int (-1,1) for x and y.

Switching on antialiasing drops FPS to 25.

HUM4N

  • Newbie
  • *
  • Posts: 5
    • View Profile
    • Email
Re: sf::VertexArray with multiple sf::TriangeFans
« Reply #8 on: April 13, 2023, 06:28:45 pm »
Alright I gotcha. So what should my takeaway from this be? I'm not quite sure.

Garwin

  • Jr. Member
  • **
  • Posts: 59
    • View Profile
Re: sf::VertexArray with multiple sf::TriangeFans
« Reply #9 on: April 14, 2023, 08:13:28 am »
You have not tell much information than it is difficult to give an advice.
But if the performance is the issue, you should profile your code and see the bottleneck.
If you have a really a lot of entities and your enemy class is large (storing a lot of member variables) than probably ECS can be a better solution or at least separate position, movement and anything relating to graphics to separate containers that they can be accessed sequentially quickly without cache misses.

But again you should start to see what is your targetted fps, from that you know how much time you have for each of your time in ms and then you can see what in your code takes this time and what can be improved.