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

Author Topic: I have a question regarding the best way to draw shapes  (Read 5990 times)

0 Members and 1 Guest are viewing this topic.

Sub

  • Full Member
  • ***
  • Posts: 159
    • View Profile
I have a question regarding the best way to draw shapes
« on: August 17, 2013, 11:00:26 pm »
I made a simple clone of Conway's Game of Life, but I'm not sure if I went about drawing the screen correctly.  When nothing is being displayed, I get 3,500 frames per second. If I were to pause the game and fill up every cell, the framerate drops to around 230.

It's basically just drawing a series of untextured rects which can be one of any arbitrary number of sf::colors. I'm thinking there has to be a way to draw the screen without such a drastic framerate drop. http://i.imgur.com/NxRJiYP.png

Right now what happens is this -- Every frame I create a sf::VertexArray, set the primitive type to sf::Quads, and then loop through every cell. If it encounters a cell that is alive, I create an sf::Vertex with the 4 points of that cell, and then append it to the vertex array.  When it's done checking every cell, the vertex array gets drawn.

That seems to be the way it's done in the tutorial, but I guess I just expected better fps than what I'm getting.

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: I have a question regarding the best way to draw shapes
« Reply #1 on: August 17, 2013, 11:41:23 pm »
There was a thread a while back where someone asked why he didn't get tens of thousands of frames when drawing nothing, and the responses from Laurent and other senior forum members were essentially:
1) Why would you ever want that many?
2) Frame counts that high (over a few hundred) are meaningless anyway.

I suspect #2 becomes obvious if you start thinking about seconds per frame instead of frames per second.

Krssst

  • Newbie
  • *
  • Posts: 11
    • View Profile
    • Email
Re: I have a question regarding the best way to draw shapes
« Reply #2 on: August 17, 2013, 11:42:52 pm »
I don't know if such FPS drop is expected (having to render a lot of rectangle should indeed make the FPS drop, but only 230fps is really surprising in my opinion). However, are you creating a new VertexArray every frame?
I think you should rather create your VertexArray once and for all before your main loop and just use VertexArray.clear() each time you want to reset it. That way, you won't reallocate memory for it each frame.

To summarize, I imagine what you are doing right now is:
Code: [Select]
while(true)
{
sf::VertexArray vertices(sf::Quads);
for(...)
vertices.append(vertex);
...
window.display();
}

... which could probably get slightly optimized if you switch to this:
Code: [Select]
sf::VertexArray vertices(sf::Quads);
while(true)
{
vertices.clear();
for(...)
vertices.append(vertex);
...
window.display();
}

AlejandroCoria

  • Jr. Member
  • **
  • Posts: 68
    • View Profile
    • alejandrocoria.games
    • Email
Re: I have a question regarding the best way to draw shapes
« Reply #3 on: August 17, 2013, 11:46:50 pm »
(google translate) A few days ago I made a clone of Conway's Game of Life. Use one vertex per cell (one pixel) I draw on a rendertexture. Then draw a sprite with that texture rescaled in the window.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
Re: I have a question regarding the best way to draw shapes
« Reply #4 on: August 17, 2013, 11:50:35 pm »
I made a simple clone of Conway's Game of Life, but I'm not sure if I went about drawing the screen correctly.
Create a minimal and complete example with the same behaviour and we will tell you, if that's the way to go.
With the description we can only guess (like Krssst did)... ;)

FPS does not decrease or increase in a linear way, so a drop from a few thousand to a few hundred is nothing strange. Beside that, as Ixrec pointed out, it really doesn't matter that much. Your display has at maximum a refresh rate of 120 Hz and those are still rather rare, most of the current displays use a refresh rate of 60 Hz. So everything above 60 FPS will be simply never been drawn on the screen.

You can take away from this: Framerate drops in the high ranges don't really say much about the performance, but it's important to understand how things work and thus if you have questions write minimal and complete example. :)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

G.

  • Hero Member
  • *****
  • Posts: 1593
    • View Profile
Re: I have a question regarding the best way to draw shapes
« Reply #5 on: August 18, 2013, 12:24:10 am »
What if you don't recreate the VertexArray every frame but just change the color of the vertices?
BTW, 230 fps isn't bad. :p

Sub

  • Full Member
  • ***
  • Posts: 159
    • View Profile
Re: I have a question regarding the best way to draw shapes
« Reply #6 on: August 18, 2013, 12:31:43 am »
230 FPS is great, but I have a very good PC.  I'm mainly wondering if I'm doing anything wrong because I'm concerned about others who might not have a PC as good as mine.

I took eXpl0it3r's advice and put together a terrible little test program.  Assuming I didn't make any mistakes, it should draw a bunch of tiny 10x10 rectangles on an 800x600 screen.  This one in release mode gives me 1,200 FPS, so I'm thinking I must have done something wrong in the original program.

#include <SFML/Graphics.hpp>
#include <sstream>

class FPS
{
private:
        unsigned int Frame;
        unsigned int Fps;
        sf::Clock FpsClock;

public:
        FPS();
        void update();
        const unsigned int getFPS();
};

//==================================

FPS::FPS()
{
        Frame = 0;
        Fps = 0;
        FpsClock.restart();
}

//==================================
 
void FPS::update()
{
        if (FpsClock.getElapsedTime().asSeconds() >= 1)
        {
                Fps = Frame;
                Frame = 0;
                FpsClock.restart();
        }
 
        Frame++;
}

//==================================
 
const unsigned int FPS::getFPS()
{
        return Fps;
}

//==================================

int main()
{
        sf::RenderWindow MainWindow;
       
        sf::ContextSettings ContextSetter(0,0,0);
       
        const int DEFAULT_SCREEN_WIDTH = 800;
        const int DEFAULT_SCREEN_HEIGHT = 600;
        MainWindow.create(sf::VideoMode(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT, 32), "Primitive Drawing Test", sf::Style::Default, ContextSetter);
       
        sf::Event Event;

        sf::VertexArray VertArray;
        VertArray.setPrimitiveType(sf::Quads);
       
        const int CELL_SIZE = 10;
        int NumOfHorizontalCells = DEFAULT_SCREEN_WIDTH / CELL_SIZE;
        int NumOfVerticalCells = DEFAULT_SCREEN_HEIGHT / CELL_SIZE;

        FPS Fps;

        bool Quit = false;
        while (Quit != true)
        {
                Fps.update();
                std::stringstream ss;
                ss << Fps.getFPS();
                MainWindow.setTitle("Primitive Drawing Test -- FPS: " + ss.str());

                while (MainWindow.pollEvent(Event))
                {              
                        if (Event.type == sf::Event::Closed)
                        {
                                Quit = true;
                        }                      
                }

                MainWindow.clear(sf::Color(60, 150, 90));

                VertArray.clear();
                for (int x = 0; x < NumOfHorizontalCells; x++)
                        for (int y = 0; y < NumOfVerticalCells; y++)
                        {
                                const float xLoc = (float)x * CELL_SIZE;
                                const float yLoc = (float)y * CELL_SIZE;
                                sf::Vertex Vertices[4] =
                                {
                                        sf::Vertex(sf::Vector2f(xLoc, yLoc), sf::Color::Black), //top left
                                        sf::Vertex(sf::Vector2f(xLoc + CELL_SIZE, yLoc), sf::Color::Black),//top right
                                        sf::Vertex(sf::Vector2f(xLoc + CELL_SIZE, yLoc + CELL_SIZE), sf::Color::Black), //bottom right
                                        sf::Vertex(sf::Vector2f(xLoc, yLoc + CELL_SIZE), sf::Color::Black) //bottom left
                                };
                                                       
                                VertArray.append(Vertices[0]);
                                VertArray.append(Vertices[1]);
                                VertArray.append(Vertices[2]);
                                VertArray.append(Vertices[3]);
                               
                        }
                               
                MainWindow.draw(VertArray);

                MainWindow.display();
        }
}
« Last Edit: August 18, 2013, 12:40:59 am by Sub »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
Re: I have a question regarding the best way to draw shapes
« Reply #7 on: August 18, 2013, 06:05:37 pm »
I took eXpl0it3r's advice and put together a terrible little test program.  Assuming I didn't make any mistakes, it should draw a bunch of tiny 10x10 rectangles on an 800x600 screen.  This one in release mode gives me 1,200 FPS, so I'm thinking I must have done something wrong in the original program.
I get 1200 FPS in debug and 1800 FPS in release mode. ;)

Anyways you should not delete and recreate the whole vertex array every frame. sf::VertexArray uses a std::vector, thus whenever you call clear() it will also call clear() on the vector. So you're basically allocating and deallocating memory in every frame iteration, which is not the fastes operation and should be reduced to a minimum as much as possible. In fact you'll most likely even run into the issue, where the vector gets moved around in memory multiple times, because it's size gets too big, to hold all the data. Such a movement takes linear time, which will add up very quickly, especially if you do it every frame iteration.

The better way to go about it, is to create the vertex array with its vertices once and then apply the changes directly to the existing vertices. If you feel like you don't have enough access through the interface of sf::VertexArray, you can also go ahead and use a std::vector<sf::Vertex> directly and use window.draw(my_vector.data(), my_vector.size()) to draw it (.data() is a C++11 feature).

To prevent moving in memory, you can either set the size from beginning with the constructor or later by calling resize() or if you're using the vector directly, you can call reserve() to allocate enough space.

After all, if you don't change the vertex array at all, then you won't have to iterate over it, every frame, constructing it once in the beginning should be enough.
« Last Edit: August 18, 2013, 06:18:03 pm by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Krssst

  • Newbie
  • *
  • Posts: 11
    • View Profile
    • Email
Re: I have a question regarding the best way to draw shapes
« Reply #8 on: August 18, 2013, 06:22:07 pm »
I took eXpl0it3r's advice and put together a terrible little test program.  Assuming I didn't make any mistakes, it should draw a bunch of tiny 10x10 rectangles on an 800x600 screen.  This one in release mode gives me 1,200 FPS, so I'm thinking I must have done something wrong in the original program.
I get 1200 FPS in debug and 1800 FPS in release mode. ;)

Anyways you should not delete and recreate the whole vertex array every frame. sf::VertexArray uses a std::vector, thus whenever you call clear() it will also call clear() on the vector. So you're basically allocating and deallocating memory in every frame iteration, which is not the fastes operation and should be reduced to a minimum as much as possible. In fact you'll most likely even run into the issue, where the vector gets moved around in memory multiple times, because it's size gets too big, to hold all the data. Such a movement takes linear time, which will add up very quickly, especially if you do it every frame iteration.
According to the documentation, it does not deallocate the memory, so I don't think this can be problematic.

Still, if the vertices' positions never change, it may indeed be a good idea to create them only once and update the color when needed.

Sub

  • Full Member
  • ***
  • Posts: 159
    • View Profile
Re: I have a question regarding the best way to draw shapes
« Reply #9 on: August 18, 2013, 07:14:29 pm »
I took eXpl0it3r's advice and put together a terrible little test program.  Assuming I didn't make any mistakes, it should draw a bunch of tiny 10x10 rectangles on an 800x600 screen.  This one in release mode gives me 1,200 FPS, so I'm thinking I must have done something wrong in the original program.
I get 1200 FPS in debug and 1800 FPS in release mode. ;)

Anyways you should not delete and recreate the whole vertex array every frame. sf::VertexArray uses a std::vector, thus whenever you call clear() it will also call clear() on the vector. So you're basically allocating and deallocating memory in every frame iteration, which is not the fastes operation and should be reduced to a minimum as much as possible. In fact you'll most likely even run into the issue, where the vector gets moved around in memory multiple times, because it's size gets too big, to hold all the data. Such a movement takes linear time, which will add up very quickly, especially if you do it every frame iteration.

The better way to go about it, is to create the vertex array with its vertices once and then apply the changes directly to the existing vertices. If you feel like you don't have enough access through the interface of sf::VertexArray, you can also go ahead and use a std::vector<sf::Vertex> directly and use window.draw(my_vector.data(), my_vector.size()) to draw it (.data() is a C++11 feature).

To prevent moving in memory, you can either set the size from beginning with the constructor or later by calling resize() or if you're using the vector directly, you can call reserve() to allocate enough space.

After all, if you don't change the vertex array at all, then you won't have to iterate over it, every frame, constructing it once in the beginning should be enough.

First of all, thanks for the response, that makes a good deal of sense.  Part of the reason I didn't keep a stored VertexArray with all the points on it is that the very array wasn't a fixed size, the program had an 'infinite grid', which was really just a series of 800x600 boards (well, I suppose 880 x 660 with the spacing) that could be allocated / deallocated anywhere needed.

http://i.imgur.com/MBmVmds.png
http://i.imgur.com/2cNJ54f.png
« Last Edit: August 18, 2013, 07:17:16 pm by Sub »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
Re: I have a question regarding the best way to draw shapes
« Reply #10 on: August 18, 2013, 07:18:07 pm »
According to the documentation, it does not deallocate the memory, so I don't think this can be problematic.
Right, I forgot that clear() on a std::vector doesn't deallocate memory.

The following code gives me ~1875 FPS in release mode and 1700 FPS in debug mode. Again if you don't change the vertices, you can construct the vertex array in the beginning.

#include <SFML/Graphics.hpp>
#include <sstream>
#include <iostream>

class FPS
{
private:
    unsigned int Frame;
    unsigned int Fps;
    sf::Clock FpsClock;

public:
    FPS();
    void update();
    const unsigned int getFPS();
};

//==================================

FPS::FPS()
{
    Frame = 0;
    Fps = 0;
    FpsClock.restart();
}

//==================================

void FPS::update()
{
    if (FpsClock.getElapsedTime().asSeconds() >= 1)
    {
        Fps = Frame;
        Frame = 0;
        FpsClock.restart();
    }

    Frame++;
}

//==================================

const unsigned int FPS::getFPS()
{
    return Fps;
}

//==================================

int main()
{
    sf::RenderWindow MainWindow;

    sf::ContextSettings ContextSetter(0,0,0);

    const int DEFAULT_SCREEN_WIDTH = 800;
    const int DEFAULT_SCREEN_HEIGHT = 600;
    MainWindow.create(sf::VideoMode(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT, 32), "Primitive Drawing Test", sf::Style::Default, ContextSetter);

    sf::Event Event;

    const int CELL_SIZE = 10;
    int NumOfHorizontalCells = DEFAULT_SCREEN_WIDTH / CELL_SIZE;
    int NumOfVerticalCells = DEFAULT_SCREEN_HEIGHT / CELL_SIZE;

    sf::VertexArray VertArray(sf::Quads, 4*NumOfHorizontalCells*NumOfVerticalCells);

    FPS Fps;

    bool Quit = false;
    while (Quit != true)
    {
        Fps.update();
        std::stringstream ss;
        ss << Fps.getFPS();
        MainWindow.setTitle("Primitive Drawing Test -- FPS: " + ss.str());

        while (MainWindow.pollEvent(Event))
        {
            if (Event.type == sf::Event::Closed)
            {
                Quit = true;
            }
        }

                for (int y = 0; y < NumOfVerticalCells; ++y)
                        for (int x = 0; x < NumOfHorizontalCells; ++x)
                        {
                                const float xLoc = static_cast<float>(x * CELL_SIZE);
                                const float yLoc = static_cast<float>(y * CELL_SIZE);

                                // top left
                                VertArray[4*((y*NumOfHorizontalCells)+x)].position = sf::Vector2f(xLoc, yLoc);
                                VertArray[4*((y*NumOfHorizontalCells)+x)].color = sf::Color::Black;

                                // top right
                                VertArray[4*((y*NumOfHorizontalCells)+x)+1].position = sf::Vector2f(xLoc + CELL_SIZE, yLoc);
                                VertArray[4*((y*NumOfHorizontalCells)+x)+1].color = sf::Color::Black;

                                // bottom right
                                VertArray[4*((y*NumOfHorizontalCells)+x)+2].position = sf::Vector2f(xLoc + CELL_SIZE, yLoc + CELL_SIZE);
                                VertArray[4*((y*NumOfHorizontalCells)+x)+2].color = sf::Color::Black;

                                // bottom left
                                VertArray[4*((y*NumOfHorizontalCells)+x)+3].position = sf::Vector2f(xLoc, yLoc + CELL_SIZE);
                                VertArray[4*((y*NumOfHorizontalCells)+x)+3].color = sf::Color::Black;
                        }



        MainWindow.clear(sf::Color(60, 150, 90));

        MainWindow.draw(VertArray);

        MainWindow.display();
    }
}
 

With the code here, you'll create the initial array with the wanted size and every frame, iterate over the grid and set the position and the color.
If you'd now want to increase the size, you could do that separately by simply calling resize and set the new values for the column/row count. Also since the position of the vertices only changes with the grid size, you don't have to reset the position every frame, but rather just on grid size change.

First of all, thanks for the response, that makes a good deal of sense.  Part of the reason I didn't keep a stored VertexArray with all the points on it is that the very array wasn't a fixed size, the program had an 'infinite grid', which was really just a series of 800x600 boards (well, I suppose 880 x 660 with the spacing) that could be allocated / deallocated anywhere needed.
You still shouldn't be recreating the whole vertex array every time, but rather use what you have and change it once the grid is supposed to change size. Constantly recreating everything, just because it may get increased is not very good for the performance.
« Last Edit: August 18, 2013, 07:25:13 pm by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

AlejandroCoria

  • Jr. Member
  • **
  • Posts: 68
    • View Profile
    • alejandrocoria.games
    • Email
Re: I have a question regarding the best way to draw shapes
« Reply #11 on: August 18, 2013, 07:52:13 pm »
Here I give you the version I made a few days ago. Includes Windows executable and the project from Visual C + + 2008.

Look at him and use it as you want, it's all yours.

http://www.mediafire.com/download/yy1kgbyd3mrikpx/GameLife.zip