Частота кадров и время кадра
Плавность анимации измеряют не «красивостью», а числом кадров в секунду и временем на один кадр.
FPS (frames per second) — сколько готовых кадров система успевает показать за секунду; frame time — сколько миллисекунд ушло на один кадр.
Зачем это знать
Графика реального времени — это гонка со временем. Монитор обновляется, например, 60 раз в секунду, и у вас есть жёсткий бюджет на кадр. Если не уложиться, кадр «опоздает», и пользователь увидит рывок. Оптимизация шейдеров и сцены — это, по сути, борьба за миллисекунды.
Связь FPS и времени кадра
Время одного кадра — это просто единица, делённая на FPS. При 60 кадрах в секунду на кадр приходится около 16.7 мс. Это и есть ваш бюджет: всё — обновление логики, расчёт вершин, растеризация, фрагментные шейдеры, постобработка — должно уместиться сюда.
for fps in [30, 60, 120, 144]:
ms = 1000.0 / fps
print(f"{fps} FPS -> {ms:.2f} мс на кадр")
Вывод:
30 FPS -> 33.33 мс на кадр 60 FPS -> 16.67 мс на кадр 120 FPS -> 8.33 мс на кадр 144 FPS -> 6.94 мс на кадр
Почему средний FPS обманывает
FPS — усреднённая величина. Если 59 кадров отрисовались мгновенно, а один завис на 100 мс, средний FPS будет высоким, но пользователь почувствует именно тот один рывок. Поэтому профессионалы смотрят на распределение времени кадров, особенно на «1% low» — худшие кадры.
frame_times = [16.0, 16.5, 16.2, 50.0, 16.1, 16.3, 16.4, 16.0] # мс
n = len(frame_times)
avg = sum(frame_times) / n
avg_fps = 1000.0 / avg
worst = max(frame_times)
print("Средний frame time:", round(avg, 2), "мс")
print("Средний FPS:", round(avg_fps, 1))
print("Худший кадр:", worst, "мс ->", round(1000.0 / worst, 1), "FPS в этот момент")
Вывод:
Средний frame time: 20.44 мс Средний FPS: 48.9 Худший кадр: 50.0 мс -> 20.0 FPS в этот момент
Средние 49 FPS выглядят терпимо, но просадка до 20 FPS на один кадр — это видимый рывок (stutter).
Как работает под капотом
Кадр проходит конвейер: процессор готовит команды отрисовки (draw calls) и шлёт их видеокарте, та исполняет вершинные и фрагментные шейдеры, заполняет фреймбуфер. Узким местом может быть и CPU (слишком много команд), и GPU (тяжёлые шейдеры, много пикселей). Если включён VSync, готовый раньше времени кадр ждёт следующего обновления монитора — поэтому при 60 Гц мониторе FPS «залипает» на 60, 30 или 20, а не плавно меняется.
Частые ошибки
- Гнаться за высоким средним FPS, игнорируя редкие, но заметные просадки.
- Двигать объекты на фиксированную величину за кадр без учёта delta time — тогда на быстром компьютере всё летает, а на медленном ползёт.
- Путать частоту обновления монитора (Гц) и FPS приложения: монитор 60 Гц не покажет больше 60 разных кадров в секунду.
Итоги
- Время кадра = 1000 / FPS; при 60 FPS бюджет ~16.7 мс.
- Средний FPS скрывает рывки — смотрите на худшие кадры (frame time).
- Движение привязывайте к delta time, чтобы скорость не зависела от железа.
- VSync устраняет разрывы, но «квантует» FPS под частоту монитора.