Термальный API

Выпущенный :

Android 11 (уровень API 30) - Thermal API

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

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

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

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

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

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

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

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

Приобретите терморегулятор.

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

C++

AThermalManager* thermal_manager = AThermal_acquireManager();

Java

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

Для более точного контроля прогнозируйте запас тепловой мощности на x секунд вперед.

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

Результат находится в диапазоне от 0,0f (без регулирования, THERMAL_STATUS_NONE ).

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

C++

float thermal_headroom = AThermal_getThermalHeadroom(0);
ALOGI("ThermalHeadroom: %f", thermal_headroom);

Java

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

В качестве альтернативы, для уточнения можно использовать данные о тепловом состоянии.

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

C++

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

Java

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

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

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

C++

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

Java

// 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);

Не забудьте удалить слушатель после завершения работы.

C++

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

Java

powerManager.removeThermalStatusListener(listener);

Уборка

После завершения вам потребуется удалить полученный объект thermal_manager. Если вы используете Java, ссылка на PowerManager может быть автоматически удалена сборщиком мусора. Но если вы используете Java API через JNI и сохранили ссылку, не забудьте удалить её!

C++

AThermal_releaseManager(thermal_manager);

Полное руководство по реализации Thermal API в нативной игре на C++ с использованием как API C++ (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. Эти ограничения и способы их преодоления описаны ниже:

  • Не вызывайте функцию GetThermalHeadroom() слишком часто. В противном случае функция вернет NaN . Не следует вызывать ее чаще, чем раз в 10 секунд.
  • Избегайте вызовов из нескольких потоков, так сложнее обеспечить частоту вызовов, и это может привести к тому, что 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 на старых устройствах.