Пару вопросов по SFML
Я решил познакомиться с библиотекой SFML. Написал простую программу окна. Код:
#include <SFML\Graphics.hpp>
#include <sstream>
using namespace sf;
using namespace std;
void Update(RenderWindow& MainWindow); // Обновление
void Render(RenderWindow& MainWindow); // Рендеринг
int main()
{
// Создать главное окно
RenderWindow MainWindow(VideoMode(1024, 768), "Engine", Style::Close);
// Ограничить FPS до 60
MainWindow.setFramerateLimit(60);
// Часы
Clock clock;
// Основной цикл
while (MainWindow.isOpen())
{
float DeltaTime = clock.restart().asSeconds();
Update(MainWindow); // Обновление
Render(MainWindow); // Рендеринг
// Вывод FPS на заголовок окна
stringstream TitleString;
TitleString << "FPS: " << 1.f / DeltaTime;
MainWindow.setTitle(TitleString.str());
}
return 0;
}
// Обновление
void Update(RenderWindow& MainWindow)
{
// События
Event event;
// Обработка событий (нажатие кнопок, закрытие окна и т.д.)
while (MainWindow.pollEvent(event))
{
// Закрыть окно если событие "Закрыть окно"
if (event.type == Event::Closed)
MainWindow.close();
}
// ...
}
// Рендеринг
void Render(RenderWindow& MainWindow)
{
MainWindow.clear(); // Очистить
// ...
MainWindow.display(); // Отобразить
}
После запуска программа значительно нагружает CPU и GPU (соответственно ~10% и ~50%). Возможным решением этой проблемы является ограничение FPS через setFramerateLimit (поставил 60). Это действительно помогает снизить нагрузку на CPU и GPU. Но нагрузка на GPU всё равно остается достаточно высоким (~10%). Дополнение к этому FPS иногда перескакивает за 60 (до 61-62), хотя стоит ограничение.
Как решить эти проблемы?
Ответы (1 шт):
Ваш счётчик FPS по сути измеряет время одного текущего кадра и пытается экстраполировать результат на всю секунду. Лучше считать количество отрисованных по факту FPS в течении одной секунды и выводить это значение.
Например:
int main() {
// Создать главное окно
RenderWindow MainWindow(VideoMode(1024, 768), "Engine", Style::Close);
// Ограничить FPS до 60
MainWindow.setFramerateLimit(60);
Clock clock; // Часы
unsigned int fps = 0; // Счётчик кадров в секунду
// Основной цикл
while (MainWindow.isOpen()) {
Update(MainWindow); // Обновление
Render(MainWindow); // Рендеринг
++fps; // Прибавляем один отрисованный кадр
// Если прошла одна секунда или больше ...
if (sf::seconds(1) <= clock.getElapsedTime()) {
// Выводим значение FPS в шапку окна
stringstream TitleString;
TitleString << "FPS: " << fps;
MainWindow.setTitle(TitleString.str());
clock.restart(); // Перезапускаем часы
fps = 0; // Обнуляем счётчик
}
}
return 0;
}
Отклонение на +/- 1 FPS это нормально, это связано с округлением целочисленного значения счётчика в ту или иную сторону.
Нужно отметить что функция sf::Window::setFramerateLimit основана на sf::sleep, точность которой зависит от таймера и планировщика операционной системы и может давать неточные результаты.
setFramerateLimit можно заменить на sf::Window::setVerticalSyncEnabled чтобы обновление изображения соответствовало частоте развёртки экрана. Однако задача вертикальной синхронизации убирать tearing (разрыв изображения), а не ограничивать FPS, поэтому его значение не обязано быть фиксированным и зависит от настроек видеодрайвера и оборудования.
Одновременно применять setVerticalSyncEnabled(true) и setFramerateLimit документацией не рекомендуется.
Стоит добавить что применение std::chrono может измерять время точнее. Тогда управлять потоком можно с помощью std::this_thread::sleep_for или std::this_thread::sleep_until.
Я выставил ограничение 10 FPS и у меня потребление GPU сократилось до 1%. Можно попробовать снизить разрешение окна или рисовать меньше контента. На SFML по другому влиять на производительность сложно. Нужны более низкоуровневые GAPI, например OpenGL или Vulkan.