Термальный API

Выпущенный :

Android 11 (уровень API 30) – тепловой API

Android 12 (уровень API 31) — API NDK

(Предварительная версия) Android 15 (DP1) — getThermalHeadroomThresholds()

Потенциальная производительность вашего приложения ограничена тепловым состоянием устройства, которое может варьироваться в зависимости от таких характеристик, как погода, недавнее использование и тепловая конструкция устройства. Устройства могут поддерживать высокий уровень производительности только в течение ограниченного периода времени, прежде чем произойдет термическое регулирование. Ключевой целью вашей реализации должно быть достижение целевых показателей производительности без превышения температурных ограничений. Thermal API делает это возможным без необходимости оптимизации конкретного устройства. Кроме того, при устранении проблем с производительностью важно знать, ограничивает ли производительность тепловое состояние вашего устройства.

Игровые движки обычно имеют параметры производительности во время выполнения, которые могут регулировать рабочую нагрузку, которую движок возлагает на устройство. Например, эти параметры могут устанавливать количество рабочих потоков, привязку рабочих потоков для больших и малых ядер, параметры точности графического процессора и разрешения кадрового буфера. В Unity Engine разработчики игр могут регулировать рабочую нагрузку, изменяя настройки качества с помощью плагина Adaptive Performance . В Unreal Engine используйте настройки масштабируемости для динамической настройки уровней качества.

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

Вы можете отслеживать тепловое состояние устройства, опрашивая метод getThermalHeadroom . Этот метод предсказывает, как долго устройство сможет сохранять текущий уровень производительности без перегрева. Если время меньше, чем необходимое для выполнения рабочей нагрузки, ваша игра должна снизить рабочую нагрузку до устойчивого уровня. Например, игра может перейти на меньшие ядра, снизить частоту кадров или снизить точность воспроизведения.

Предварительная интеграция ADPF Thermal API
Рисунок 1. Термический запас без активного мониторинга getThermalHeadroom
ADPF Thermal API после интеграции
Рисунок 2. Термический запас при активном мониторинге getThermalHeadroom.

Приобретите Термальный менеджер

Чтобы использовать Thermal API, сначала вам необходимо приобрести Thermal Manager.

С++

AThermalManager* thermal_manager = AThermal_acquireManager();

Ява

PowerManager powerManager = (PowerManager)this.getSystemService(Context.POWER_SERVICE);

Прогнозируйте тепловой запас на x секунд вперед для большего контроля

Вы можете попросить систему спрогнозировать температуру на x секунд вперед с учетом текущей рабочей нагрузки. Это дает вам более детальный контроль и больше времени для реагирования за счет снижения рабочей нагрузки, чтобы предотвратить срабатывание теплового регулирования.

Результат варьируется от 0,0f (без регулирования, THERMAL_STATUS_NONE ) до 1,0f (сильное регулирование, THERMAL_STATUS_SEVERE ). Если в ваших играх разные уровни качества графики, вы можете следовать нашим рекомендациям по температурному запасу .

С++

float thermal_headroom = AThermal_getThermalHeadroom(10);
ALOGI("ThermalHeadroom in 10 sec: %f", thermal_headroom);

Ява

float thermalHeadroom = powerManager.getThermalHeadroom(10);
Log.d("ADPF", "ThermalHeadroom in 10 sec: " + thermalHeadroom);

Альтернативно, для уточнения можно полагаться на температурный статус.

Каждая модель устройства может быть спроектирована по-разному. Некоторые устройства могут лучше распределять тепло и, таким образом, выдерживать более высокий тепловой запас до дросселирования. Если вы хотите прочитать упрощенную группу диапазонов температурного запаса, вы можете проверить тепловой статус, чтобы понять значение температурного запаса на текущем устройстве.

С++

AThermalStatus thermal_status = AThermal_getCurrentThermalStatus(thermal_manager);
ALOGI("ThermalStatus is: %d", thermal_status);

Ява

int thermalStatus = powerManager.getCurrentThermalStatus();
Log.d("ADPF", "ThermalStatus is: " + thermalStatus);

Получайте уведомления при изменении теплового статуса

Вы также можете избежать опроса thermalHeadroom до тех пор, пока thermalStatus не достигнет определенного уровня (например: THERMAL_STATUS_LIGHT ). Для этого вы можете зарегистрировать обратный вызов, чтобы система уведомляла вас при каждом изменении статуса.

С++

int result = AThermal_registerThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
  // failed, check whether you have previously registered callback that
  // hasn’t been unregistered
}

Ява

// PowerManager.OnThermalStatusChangedListener is an interface, thus you can
// also define a class that implements the methods
PowerManager.OnThermalStatusChangedListener listener = new
  PowerManager.OnThermalStatusChangedListener() {
    @Override
    public void onThermalStatusChanged(int status) {
        Log.d("ADPF", "ThermalStatus changed: " + status);
        // check the status and flip the flag to start/stop pooling when
        // applicable
    }
};
powerManager.addThermalStatusListener(listener);

Не забудьте удалить прослушиватель, когда закончите.

С++

int result = AThermal_unregisterThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
  // failed, check whether the callback has been registered previously
}

Ява

powerManager.removeThermalStatusListener(listener);

Очистка

Когда вы закончите, вам нужно будет очистить приобретенный вами Thermal_manager. Если вы используете Java, ссылка PowerManager может быть автоматически удалена за вас. Но если вы используете Java API через JNI и сохранили ссылку, не забудьте очистить ссылку!

С++

AThermal_releaseManager(thermal_manager);

Полное руководство по реализации Thermal API в собственной игре на C++ с использованием как C++ API (NDK API), так и Java API (через JNI), см. в разделе «Интеграция Thermal API» в разделе «Кодовая лаборатория адаптивности» .

Рекомендации по температурному запасу

Вы можете отслеживать тепловое состояние устройства, опрашивая метод getThermalHeadroom . Этот метод предсказывает, как долго устройство сможет поддерживать текущий уровень производительности до достижения THERMAL_STATUS_SEVERE . Например, если getThermalHeadroom(30) возвращает 0,8, это означает, что через 30 секунд запас, как ожидается, достигнет 0,8, где расстояние до серьезного регулирования составляет 0,2, или 1,0. Если время меньше, чем необходимое для выполнения рабочей нагрузки, ваша игра должна снизить рабочую нагрузку до устойчивого уровня. Например, игра может снизить частоту кадров, снизить качество воспроизведения или уменьшить нагрузку на сетевое подключение.

Термические статусы и значение

Ограничения устройств Thermal API

Существуют некоторые известные ограничения или дополнительные требования Thermal API, связанные с реализацией Thermal API на старых устройствах. Ограничения и способы их обхода следующие:

  • Не вызывайте API GetThermalHeadroom() слишком часто. В результате API вернет NaN. Вы должны вызывать его не чаще одного раза в секунду.
  • Если начальное значение GetThermalHeadroom() равно NaN, API недоступен на устройстве.
  • Если GetThermalHeadroom() возвращает высокое значение (например: 0,85 или больше), а GetCurrentThermalStatus() по-прежнему возвращает THERMAL_STATUS_NONE , статус, скорее всего, не обновится. Используйте эвристику для оценки правильного состояния теплового регулирования или просто используйте getThermalHeadroom() без getCurrentThermalStatus() .

Пример эвристики:

  1. Убедитесь, что Thermal API поддерживается. isAPISupported() проверяет значение первого вызова getThermalHeadroom , чтобы убедиться, что оно не равно 0 или NaN, и пропускает использование API, если первое значение равно 0 или NaN.
  2. Если getCurrentThermalStatus() возвращает значение, отличное от THERMAL_STATUS_NONE , устройство термически регулируется.
  3. Если getCurrentThermalStatus() продолжает возвращать THERMAL_STATUS_NONE , это не обязательно означает, что устройство не подвергается тепловому регулированию. Это может означать, что getCurrentThermalStatus() не поддерживается на устройстве. Проверьте возвращаемое значение getThermalHeadroom() чтобы убедиться в состоянии устройства.
  4. Если getThermalHeadroom() возвращает значение > 1,0, на самом деле статус может быть THERMAL_STATUS_SEVERE или выше, немедленно уменьшите рабочую нагрузку и поддерживайте более низкую рабочую нагрузку до тех пор, пока getThermalHeadroom() не вернет более низкое значение.
  5. Если getThermalHeadroom() возвращает значение 0,95, на самом деле статус может быть THERMAL_STATUS_MODERATE или выше, немедленно уменьшите рабочую нагрузку и следите за тем, чтобы не допустить более высоких показаний.
  6. Если getThermalHeadroom() возвращает значение 0,85, статус на самом деле может быть THERMAL_STATUS_LIGHT , будьте осторожны и, если возможно, уменьшите рабочую нагрузку.

Псевдокод:

  bool isAPISupported() {
    float first_value_of_thermal_headroom = getThermalHeadroom();
    if ( first_value_of_thermal_headroom == 0 ||
      first_value_of_thermal_headroom == NaN ) {
        // Checked the thermal Headroom API's initial return value
        // it is NaN or 0,so, return false (not supported)
        return false;
    }
    return true;
  }
  
  if (!isAPISupported()) {
    // Checked the thermal Headroom API's initial return value, it is NaN or 0
    // Don’t use the API
  } else {
      // Use thermalStatus API to check if it returns valid values.
      if (getCurrentThermalStatus() > THERMAL_STATUS_NONE) {
          // The device IS being thermally throttled
      } else {
      // The device is not being thermally throttled currently. However, it
      // could also be an indicator that the ThermalStatus API may not be
      // supported in the device.
      // Currently this API uses predefined threshold values for thermal status
      // mapping. In the future  you may be able to query this directly.
      float thermal_headroom = getThermalHeadroom();
      if ( thermal_headroom > 1.0) {
            // The device COULD be severely throttled.
      } else  if ( thermal_headroom > 0.95) {
            // The device COULD be moderately throttled.
      } else if ( thermal_headroom > 0.85) {
            // The device COULD be experiencing light throttling.
      }
    }
  }

Диаграмма:

Пример эвристики ADPF
Рисунок 3. Пример эвристики для определения поддержки Thermal API на старых устройствах.