Simple C++ SFML program high CPU usage

Refresh

November 2018

Views

2.8k time

6

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

2 answers

6

Я ответил другой подобный вопрос с этим ответом .

Дело в том, что это точно не помогает вам с использованием центрального процессора, но я попробовал ваш код и он работает отлично под 1% использование центрального процессора на 120 кадров в секунду (и многое другое). Когда вы делаете игру или интерактивные медиа с «игровой петлей», вы не хотите терять производительность сна, вы хотите использовать столько время процессора, как компьютер может дать вам. Вместо того, чтобы спать, вы можете обрабатывать другие данные, например, загрузки материала, алгоритм поиска пути и т.д., или просто не поставить ограничения на визуализацию.

Я обеспечиваю некоторые полезные ссылки и код, вот это:


Похожий вопрос: Движение без Framerate Limit C ++ SFML .

Что вам действительно нужно , это фиксированный интервал времени. Посмотрите на развитие игры книги SFML исходного кода . Вот интересный отрывок из 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();
}

Если это не совсем то , что вы хотите, видеть « Исправьте шаг время! » , Который сама Лоран Гомила связана в форуме SFML.

1

Я предлагаю использовать предел setFrameRate, поскольку он изначально реализован в SFML и будет работать гораздо лучше. Для получения истекшего времени вы должны сделать:

fpsElapsedTime = fpsTimer.getElapsedTime();

Если бы я должен был сделать что-то подобное, я хотел бы сделать:

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

Другое дело, использовать НФ :: Цвет :: белый или научная фантастика :: Цвет :: Черный вместо (SF :: Color (255255255))

Надеюсь, что это поможет :)