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