acombine.ru
← все заметки

60 fps на бюджетном Android: где я терял кадры

На моём телефоне игра шла гладко. На дешёвом телефоне тестировщика — дёргалась каждые пару секунд. Классика: разработчик видит игру только на своём хорошем железе. Разбор занял вечер и свёлся к трём находкам.

1. Микрозадержки от сборщика мусора

Дёрганье было не постоянным, а рывками — верный признак сборщика мусора. Он просыпается, когда программа насоздавала много временных объектов, замирает на несколько миллисекунд и чистит память. На кадре в 16 мс даже пауза в 8 мс — это уже пропущенный кадр.

Виноват был игровой цикл. В нём на каждом кадре создавались временные объекты: новые векторы координат, промежуточные массивы. Шестьдесят раз в секунду — гора мусора.

// было: новый объект каждый кадр
function update() {
  const dir = { x: target.x - me.x, y: target.y - me.y };
  ...
}

// стало: переиспользуем один буфер
const _dir = { x: 0, y: 0 };
function update() {
  _dir.x = target.x - me.x;
  _dir.y = target.y - me.y;
  ...
}

Правило, которое я теперь держу в голове: в игровом цикле не создавать объекты. Всё, что нужно каждый кадр, готовится заранее и переиспользуется.

2. Полупрозрачный слой во весь экран

Вторая половина потерь пришлась на одну красивую мелочь: затемняющий полупрозрачный слой поверх всей сцены. Прозрачность на мобильном GPU дорогая — устройство обязано для каждого пикселя смешать новый цвет со старым, и делает это по всему экрану каждый кадр, даже когда затемнение почти незаметно.

Один полноэкранный полупрозрачный прямоугольник на слабом телефоне стоил мне примерно 6 fps.

Я оставил его только там, где он реально нужен (пауза, конец уровня), и убрал из основного геймплея.

3. Отрисовка того, чего не видно

Третья мелочь: я честно рисовал все объекты уровня, включая уехавшие далеко за край экрана. Добавил простую проверку — если прямоугольник спрайта не пересекается с экраном, не рисуем (это называется culling). На больших уровнях это вернуло ещё несколько кадров бесплатно.

Как я теперь мерю

Главный вывод не про конкретные баги, а про привычку: тестировать на самом слабом телефоне, до которого могу дотянуться, и смотреть не на среднюю частоту кадров, а на просадки. Средние 58 fps с рывками до 30 играются хуже, чем ровные 50. Игрок чувствует именно рывки.

← все заметки