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

Author Topic: I'm new | Problem with cpu usage  (Read 3283 times)

0 Members and 1 Guest are viewing this topic.

barnack

  • Jr. Member
  • **
  • Posts: 56
    • View Profile
I'm new | Problem with cpu usage
« on: October 25, 2017, 02:15:14 pm »
Hello,
i'm new here; i started using SFML for the first time two days ago. After 2 years of active developing with both Game Maker: Studio and terminal-only C++ applications, i found myself being too much limited by GM's restriction, but sticking to GM for its magical internal resource management. Until i couldn't stand the restrictions anymore. Then i found this library, SFML, which should think about the graphics stuff while i focus on the real application (as a sidenote, i wasn't making actual games with gamemaker, but i needed it to give a nice graphical appeal to my programs).
Two days ago i started re-developing an utility i made for myself in the past with GM, which is an auto-hiding sidebar for windows 10 (i don't care about portability really) with an analog clock and a customizable array of buttons which can either open a website, run a program or execute a shell script (basically they call Window's "ExecuteShell" function), and a soft slow moving particle effect as background.
Now, while the Game Maker version also had a tetris and a minesweeper built in, the one i made with sfml only has the clock, the buttons and the particle effect.
It was all going fine, untill i said myself "let's see how much less cpu does a good old c++ equivalent to an heavily scripted gamemaker project". Had i never done that check!
My GM sidebar has a constant 0.6-1.2% cpu usage, and requires 26 MB of ram at fixed 60 steps per second.
SFML version, with less features, requires 6.0-12.0% cpu when using window.setFramerateLimit(60);. Same amount if i write a custom sleep-per-step. This happens with 8 buttons, i found out if the buttons are 2 it drops to 5.0-6.0 cpu usage (maybe rendering text is an issue?)
Without buttons at all the cpu usage drops to 0.6-1.0.

Which means, the SFML version has the same cpu usage as the GM version only if it has particles and clock going on. GM requires the same cpu to handle a bunch of buttons, tetris and minesweeper all together.

P.s. SFML version also requires almost 2 times the ram, it's reported usage is of 44.7MB, but i don't think it's an issue since i still have to optimize the texture part, and i have many small textures loaded, of which some repeated, instead of a bigger one (which in theory should perform better)

Some relevant code:
int main()
        {
        //first inits
        int WINDOW_WIDTH = 160;
        int WINDOW_HEIGHT = Screen::get_height();
        int WINDOW_MIN_X = Screen::get_width() - WINDOW_WIDTH;
        int WINDOW_SPEED = 6;
        int VERTICAL_SPACING = 8;
        int PART_AMOUNT = 8;
        int MAIN_RR = 0;
        int MAIN_GG = 240;
        int MAIN_BB = 255;
        int SEC_RR = 0;
        int SEC_GG = 0;
        int SEC_BB = 50;
        //Here i read the settings from an xml file, upper ones are defaults.

        //Window stuff
        sf::RenderWindow window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Test", sf::Style::None);
        window.setPosition({ (Screen::get_width() - WINDOW_WIDTH), 0 } );
        window.setFramerateLimit(60);
        window.setVerticalSyncEnabled(true);
        //Get windows window handle to add some options
        HWND windows_window = window.getSystemHandle();
                //always on top
                SetWindowPos(windows_window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
                //do not show in toolbar
                SetWindowLong(windows_window, GWL_EXSTYLE, WS_EX_TOOLWINDOW);

                //Transparent window
                MARGINS margins;
                margins.cxLeftWidth = -1;
                SetWindowLong(windows_window, GWL_STYLE, WS_POPUP | WS_VISIBLE);
                DwmExtendFrameIntoClientArea(window.getSystemHandle(), &margins);

        //Background as a texture fading right to left
                sf::Texture bg_texture;
                if (not bg_texture.loadFromFile("textures/t_bg.png"))
                        {/*handle error*/}
                sf::Sprite bg_sprite(bg_texture);
                bg_sprite.setScale(1, WINDOW_HEIGHT);
                bg_sprite.setColor(sf::Color(SEC_RR, SEC_GG, SEC_BB, 255));

        //Init my own particle system
        //not relevant since particles do not affect performance much, as expected from particles.
       
        //Add elements to the bar
                BarnackClock clock = BarnackClock((WINDOW_WIDTH/2), 84, MAIN_RR, MAIN_GG, MAIN_BB);
                std::vector<BarButton> buttons = std::vector<BarButton>();

                //Add buttons
                //here i read buttons text and action from an xml file.
                {
                xml::XMLDocument file;
                xml::XMLError res = file.LoadFile("files/buttons.xml");
                xml::XMLNode* root = file.FirstChild();
                xml::XMLNode* buttons_list = root->FirstChildElement("list");
                usi yy = 200;
                for (xml::XMLElement* e = buttons_list->FirstChildElement("button"); e != NULL; e = e->NextSiblingElement("button"))
                        {
                        buttons.push_back(BarButton(e->Attribute("text"), e->Attribute("action"), 96, yy));
                        yy += 32 + VERTICAL_SPACING;
                        }
                }

        //Main cycle
        while (window.isOpen())
                {
                //Window move ment
                //move the window outside of the screen if the mouse is not over it
                        {
                        int mx = sf::Mouse::getPosition().x;
                        int wx = window.getPosition().x;
                        if (mx >= wx - 1)
                                {
                                if (wx > WINDOW_MIN_X)
                                        {
                                        wx -= WINDOW_SPEED;
                                        if (wx < WINDOW_MIN_X)
                                                {
                                                wx = WINDOW_MIN_X;
                                                }
                                        }
                                }
                        else if (wx < Screen::get_width())
                                {
                                wx += WINDOW_SPEED;
                                if (wx > Screen::get_width())
                                        {
                                        wx = Screen::get_width();
                                        }
                                }
                        window.setPosition(sf::Vector2i(wx, 0));
                        }

                //EVENTS CHECKING
                sf::Event event;
                while (window.pollEvent(event))
                        {
                        // "close requested" event: we close the window
                        sf::Vector2f mouse_pos = window.mapPixelToCoords(sf::Mouse::getPosition(window));
                        if (event.type == sf::Event::Closed)
                                window.close();
                        else if (event.type == sf::Event::MouseMoved)
                                {
                                for (BarButton btn : buttons)
                                        {
                                        btn.mouse_moved(mouse_pos);
                                        }
                                }
                        else if (event.type == sf::Event::MouseButtonPressed)
                                {
                                for (BarButton btn : buttons)
                                        {
                                        btn.mouse_pressed(mouse_pos);
                                        }
                                }
                        else if (event.type == sf::Event::MouseButtonReleased)
                                {
                                for (BarButton btn : buttons)
                                        {
                                        btn.mouse_released(mouse_pos);
                                        }
                                }
                        }

                //STEP
                ps.step();
                for (size_t i = 0; i<buttons.size(); i++)
                        {
                        buttons[i].step();
                        }
                clock.step();

                //DRAW
                window.clear(sf::Color::Transparent);
                window.draw(bg_sprite);


                window.draw(ps);
                window.draw(clock);
                for (BarButton btn : buttons)
                        {
                        window.draw(btn);
                        }
                window.display();
                }
        }
 

In the main cycle i used the same order used by GM: 1st actions, 2nd step adjustments, 3rd draw.


now the button.h
class BarButton : public sf::Drawable
{
private:
        sf::Texture texture;
        std::vector<sf::IntRect> anim;
        sf::Sprite sprite;
        sf::Font font;
        sf::Text text;
        usi current;
        int changing;
        std::string action;

        virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;

public:
        BarButton(std::string text, std::string action, double x, double y);
        ~BarButton();

        void mouse_moved(sf::Vector2f mouse_pos);
        void mouse_pressed(sf::Vector2f mouse_pos);
        void mouse_released(sf::Vector2f mouse_pos);

        void step();
};
 


and the button.cc
BarButton::BarButton(std::string str, std::string action, double x, double y)
        {
        this->action = action;

        if (not texture.loadFromFile("textures/t_button.png"))
                {/*handle error*/
                }
        texture.setSmooth(true);

        if (!font.loadFromFile("fonts/times.ttf"))
                {/*handle herror*/
                }
        //SPRITES
        current = 0;
        changing = 0;

        sprite = sf::Sprite();
        sprite.setTexture(texture);
        sprite.setTextureRect(sf::IntRect(0, 0, 142, 48));
        sprite.setOrigin(142 / 2, 48 / 2);
        sprite.setPosition(x, y);
        sprite.setColor(sf::Color(000, 240, 255, 255));

        int xx = 0;
        anim = std::vector<sf::IntRect>();
        for (usi i = 0; i < 16; i++)
                {
                //std::cout << xx << std::endl;
                anim.push_back(sf::IntRect(xx, 0, 142, 48));
                xx += 142;
                }

        //TEXTS
        text.setFont(font);
        text.setColor(sf::Color(000, 0, 50, 255));
        text.setCharacterSize(12);
        text.setString(str);
        text.setOrigin(58, 7);
        text.setPosition(x, y);
        }


BarButton::~BarButton()
        {}

void BarButton::mouse_moved(sf::Vector2f mouse_pos)
        {
        if (sprite.getGlobalBounds().contains(mouse_pos))
                {
                if (current < (anim.size() - 1))
                        {
                        changing = +1;
                        }
                /*else
                        {
                        changing = 0;
                        }*/

                }
        else
                {
                if (current > 0)
                        {
                        changing = -1;
                        }
                /*else
                        {
                        changing = 0;
                        }*/

                }
        }

void BarButton::mouse_pressed(sf::Vector2f mouse_pos)
        {}

void BarButton::mouse_released(sf::Vector2f mouse_pos)
        {
        if (sprite.getGlobalBounds().contains(mouse_pos))
                {
                CA2W ca2w(action.c_str());
                //std::cout << "opening " << text.getString().toAnsiString() << std::endl;
                ShellExecute(null, L"open", ca2w, null, null, SW_SHOW);
                }
        }

void BarButton::step()
        {
        if (changing)
                {
                if (changing == 1)
                        {
                        if (current == anim.size() - 1)
                                {
                                changing = 0;
                                }
                        else
                                {
                                current++;
                                sprite.setPosition(sprite.getPosition().x - 0.75, sprite.getPosition().y);
                                text.setPosition(sprite.getPosition());
                                }
                        }
                else if (changing == -1)
                        {
                        if (current == 0)
                                {
                                changing = 0;
                                }
                        else
                                {
                                current--;
                                sprite.setPosition(sprite.getPosition().x + 0.75, sprite.getPosition().y);
                                text.setPosition(sprite.getPosition());
                                }
                        }
                }
        sprite.setTextureRect(anim[current]);
        }

void BarButton::draw(sf::RenderTarget& target, sf::RenderStates states) const
        {
        target.draw(sprite);
        target.draw(text);
        }
 
What's your OS? Graphics card? - Windows 10, AMD Radeon HD 7800
Which version of SFML are you using? lastest stable version
Are you showing all the relevant source code? particles and clock alone appear not to be an issue, whereas each button kills the cpu. Even though even without buttons cpu is slightly higher than expected and ram is still double as GM.
Have you ran the debugger? Directly from the profiler, 75% of cpu time is spent in BarButton::BarButton

Thanks,
Barnack

EDIT:
Apparently it's not the amount of textures which is causing the excessive ram usage, since even without buttons (which were guilty of reloading the same texture for each button), the ram is still at 46-47MB

Also i wonder why when testing within visual studio the application is laggy as hell, while when running the compiled on it works just fine...

EDIT EDIT:
i've also noticed a strange behavior;
Let's call this cycle "conventional"
for (size_t i = 0; i<buttons.size(); i++)
{btn = buttons[i]; ...}
 
and this one "foreach"
for (BarButton btn : buttons)
 

The btn.mouse_moved and btn.step only work when called with the "conventional" cycle (aren't called at all with the foreach cycle), whereas the btn.mouse_released and the btn.draw functions only work when called with the foreach cycle. That's really strange and makes me think i've coded something weird i don't see in button.cpp... O.o
« Last Edit: October 25, 2017, 02:45:41 pm by barnack »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: I'm new | Problem with cpu usage
« Reply #1 on: October 25, 2017, 02:37:23 pm »
Although there's no real question in your post, I'll reply something that might help you with performances.

Quote
for (BarButton btn : buttons)
Everytime you do that (at least 60 times per second for each button, plus when you press/release mouse buttons), you copy your button instances together with their font, texture, etc. This is really awful.

There are two things that you should do:

1. Don't copy when you iterate
for (auto& button : buttons)
for (const auto& button : buttons)

2. Don't store a texture and a font inside each button, unless they all have their own different font and texture ; use some sort of centralized resource management (doesn't have to be compilcated)
Laurent Gomila - SFML developer

barnack

  • Jr. Member
  • **
  • Posts: 56
    • View Profile
Re: I'm new | Problem with cpu usage
« Reply #2 on: October 25, 2017, 02:51:31 pm »
Oh thanks i forgot it gets copyed o.o
I should have sticked with the conventional "for (size_t i = 0; i<buttons.size(); i++)"...
but then why window.draw(btn) only work when i cycled the buttons with "for(type btn : buttons)"?

If i use "for (size_t i = 0; i<buttons.size(); i++){window.draw(buttons[ i ]);}" the window doesn't even appear... that's weird. and i don't see the fault
If i use "for (size_t i = 0; i<buttons.size(); i++){buttons[ i ].mouse_released())" the window doesn't even appear... that's weird. and i don't see the fault, while it works when creating new ones
If i use "for(size_t i = 0; i<buttons.size(); i++){buttons[ i ].mouse_released())" or "for (size_t i = 0; i<buttons.size(); i++){window.draw(buttons[ i ]);}" the window doesn't even appear... that's weird. and i don't see the fault, while it works when creating new ones

EDIT: about the second point i know, this is just temporary, and it should only affect ram, not cpu...
« Last Edit: October 25, 2017, 03:00:47 pm by barnack »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: I'm new | Problem with cpu usage
« Reply #3 on: October 25, 2017, 03:02:38 pm »
Quote
I should have sticked with the conventional "for (size_t i = 0; i<buttons.size(); i++)"...
No, just iterate by reference as in my reply above.

If you have other problems now, then please describe them properly and show the updated code.

PS: don't edit your initial post after people replied to it...
Laurent Gomila - SFML developer

barnack

  • Jr. Member
  • **
  • Posts: 56
    • View Profile
Re: I'm new | Problem with cpu usage
« Reply #4 on: October 25, 2017, 03:10:16 pm »
Ok, sorry for the edits;
now all the code in main is cycling by reference.
But i have weird reults when the draw is called by this way.
Main:
for (BarButton& btn : buttons)
        {
        window.draw(btn);
        }
 

Button draw:
void BarButton::draw(sf::RenderTarget& target, sf::RenderStates states) const
        {
        target.draw(sprite);
        //target.draw(text);
        }
 
(the rest of button code is unchanged, cfr first post^)
If i do it this way, buttons sprites are strangely vertically stretched (?)
Whereas, if the "target.draw(text)" is not commented, the whole window isn't shown at all. There must be something wrong in the button, but i don't manage to see what it is.

barnack

  • Jr. Member
  • **
  • Posts: 56
    • View Profile
Re: I'm new | Problem with cpu usage
« Reply #5 on: October 25, 2017, 03:12:22 pm »
PS: but yeah, changing the cycle really saved a lot of cpu (of course), i'm just sorry i did such a nooby error and didn't see it... so the main "issue" is solved, now i just have to find out what's wrong with my poor buttons

barnack

  • Jr. Member
  • **
  • Posts: 56
    • View Profile
Re: I'm new | Problem with cpu usage
« Reply #6 on: October 25, 2017, 08:59:32 pm »
Ok everything's solved, thanks a lot for the quick answers and sorry a lot for the stupid errors i made and didn't see; i'll ask about ram usage in a new topic