Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple C++ SFML program high CPU usage

I'm currently working on a platformer and trying to implement a timestep, but for framerate limits greater than 60 the CPU usage goes up from 1% to 25% and more.

I made this minimal program to demonstrate the issue. There are two comments (lines 10-13, lines 26-30) in the code that describe the problem and what I have tested.

Note that the FPS stuff is not relevant to the problem (I think).

I tried to keep the code short and simple:

#include <memory>
#include <sstream>
#include <iomanip>
#include <SFML\Graphics.hpp>

int main() {
  // Window
  std::shared_ptr<sf::RenderWindow> window;
  window = std::make_shared<sf::RenderWindow>(sf::VideoMode(640, 480, 32), "Test", sf::Style::Close);
  /*
  When I use the setFramerateLimit() function below, the CPU usage is only 1% instead of 25%+
  (And only if I set the limit to 60 or less. For example 120 increases CPU usage to 25%+ again.)
  */
  //window->setFramerateLimit(60);

  // FPS text
  sf::Font font;
  font.loadFromFile("font.ttf");
  sf::Text fpsText("", font, 30);
  fpsText.setColor(sf::Color(0, 0, 0));

  // FPS
  float fps;
  sf::Clock fpsTimer;
  sf::Time fpsElapsedTime;
  /*
  When I set framerateLimit to 60 (or anything less than 60) 
  instead of 120, CPU usage goes down to 1%.
  When the limit is greater, in this case 120, CPU usage is 25%+
  */
  unsigned int framerateLimit = 120;
  sf::Time fpsStep = sf::milliseconds(1000 / framerateLimit);
  sf::Time fpsSleep;
  fpsTimer.restart();

  while (window->isOpen()) {
    // Update timer
    fpsElapsedTime = fpsTimer.restart();
    fps = 1000.0f / fpsElapsedTime.asMilliseconds();

    // Update FPS text
    std::stringstream ss;
    ss << "FPS: " << std::fixed << std::setprecision(0) << fps;
    fpsText.setString(ss.str());

    // Get events
    sf::Event evt;
    while (window->pollEvent(evt)) {
      switch (evt.type) {
      case sf::Event::Closed:
        window->close();
        break;
      default:
        break;
      }
    }

    // Draw
    window->clear(sf::Color(255, 255, 255));
    window->draw(fpsText);
    window->display();

    // Sleep
    fpsSleep = fpsStep - fpsTimer.getElapsedTime();
    if (fpsSleep.asMilliseconds() > 0) {
      sf::sleep(fpsSleep);
    }

  }

  return 0;
}

I don't want to use SFML's setFramerateLimit(), but my own implementation with the sleep because I will use the fps data to update my physics and stuff.

Is there a logic error in my code? I fail to see it, given it works with a framerate limit of for example 60 (or less). Is it because I have a 60 Hz monitor?

PS: Using SFML's window->setVerticalSync() doesn't change the results

like image 890
A. D. Avatar asked Mar 02 '14 14:03

A. D.


2 Answers

I answered another similar question with this answer.

The thing is, it's not exactly helping you with CPU usage, but I tried your code and it is working fine under 1% cpu usage at 120 FPS (and much more). When you make a game or an interactive media with a "game-loop", you don't want to lose performance by sleeping, you want to use as much cpu time as the computer can give you. Instead of sleeping, you can process other data, like loading stuff, pathfinding algorithm, etc., or just don't put limits on rendering.

I provide some useful links and code, here it is:


Similar question: Movement Without Framerate Limit C++ SFML.

What you really need is fixed time step. Take a look at the SFML Game development book source code. Here's the interesting snippet from Application.cpp:

const sf::Time Game::TimePerFrame = sf::seconds(1.f/60.f);

// ...

sf::Clock clock;
sf::Time timeSinceLastUpdate = sf::Time::Zero;
while (mWindow.isOpen())
{
    sf::Time elapsedTime = clock.restart();
    timeSinceLastUpdate += elapsedTime;
    while (timeSinceLastUpdate > TimePerFrame)
    {
        timeSinceLastUpdate -= TimePerFrame;

        processEvents();
        update(TimePerFrame);

    }

    updateStatistics(elapsedTime);
    render();
}

If this is not really what you want, see "Fix your timestep!" which Laurent Gomila himself linked in the SFML forum.

like image 102
Emile Bergeron Avatar answered Nov 06 '22 20:11

Emile Bergeron


I suggest to use the setFrameRate limit, because it's natively implemented in SFML and will work a lot better. For getting the elapsed time you must do :

fpsElapsedTime = fpsTimer.getElapsedTime();

If I had to implement something similar, I would do:

/* in the main loop */
fpsElapsedTime = fpsTimer.getElapsedTime();
if(fpsElapsedTime.asMillisecond() >= (1000/framerateLimit))
{
   fpsTimer.restart();
  // All your content
}

Other thing, use sf::Color::White or sf::Color::Black instead of (sf::Color(255,255,255))

Hope this help :)

like image 44
user3337178 Avatar answered Nov 06 '22 20:11

user3337178