Thermal API

출시일:

Android 11 (API 수준 30) - Thermal API

Android 12 (API 수준 31) - NDK API

(미리보기) Android 15 (DP1) - getThermalHeadroomThresholds()

앱은 기기의 열 상태에 따라 잠재적으로 성능이 제한되며, 기기의 열 상태는 날씨, 최근 사용량, 기기의 열 관련 설계와 같은 특성에 따라 달라질 수 있습니다. 기기는 열 제한 전에 제한된 시간 동안만 높은 수준의 성능을 유지할 수 있습니다. 구현의 주요 목표는 열 제한을 초과하지 않고 성능 목표를 달성하는 것입니다. Thermal API를 사용하면 기기별 최적화 없이도 이를 수행할 수 있습니다. 또한 성능 문제를 디버깅할 때는 기기의 열 상태가 성능을 제한하는지 아는 것이 중요합니다. 또한 성능 문제를 디버깅할 때는 기기의 열 상태가 성능을 제한하는지 아는 것이 중요합니다.

게임 엔진에는 일반적으로 엔진이 기기에 부여하는 워크로드를 조정할 수 있는 런타임 성능 매개변수가 있습니다. 예를 들어, 이러한 매개변수는 작업자 스레드 수, 큰 코어와 작은 코어의 작업자 스레드 어피니티, GPU 충실도 옵션, 프레임 버퍼 해상도를 설정할 수 있습니다. Unity Engine에서 게임 개발자는 적응형 성능 플러그인으로 품질 설정을 변경하여 워크로드를 조정할 수 있습니다. Unreal Engine의 경우 Scalability Settings를 사용하여 품질 수준을 동적으로 조정합니다.

기기가 안전하지 않은 열 상태에 도달하면 게임은 이러한 매개변수를 통해 워크로드를 줄여 성능이 제한되는 상황을 피할 수 있습니다. 제한 상황을 피하려면 기기의 열 상태를 모니터링하고 게임 엔진 워크로드를 미리 조정해야 합니다. 기기가 일단 과열되면 열을 방출하기 위해 워크로드가 지속 가능한 성능 수준 아래로 떨어져야 합니다. 열 헤드룸이 더 안전한 수준으로 감소하면 게임 품질 설정을 다시 높일 수 있지만 최적의 플레이 시간을 위해 지속 가능한 품질 수준을 찾아야 합니다.

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(10);
ALOGI("ThermalHeadroom in 10 sec: %f", thermal_headroom);

Java

float thermalHeadroom = powerManager.getThermalHeadroom(10);
Log.d("ADPF", "ThermalHeadroom in 10 sec: " + 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);

열 상태가 변경되면 알림 받기

또한 thermalStatus가 특정 수준 (예: THERMAL_STATUS_LIGHT)에 도달할 때까지 thermalHeadroom 폴링을 방지할 수 있습니다. 이렇게 하려면 상태가 변경될 때마다 시스템에서 알릴 수 있도록 콜백을 등록하면 됩니다.

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 참조가 자동으로 가비지 컬렉션될 수 있습니다. 그러나 JNI를 통해 Java API를 사용하고 참조를 유지한 경우 참조를 정리해야 합니다.

C++

AThermal_releaseManager(thermal_manager);

C++ API (NDK API)와 Java API (JNI를 통해)를 모두 사용하여 네이티브 C++ 게임에 Thermal API를 구현하는 방법에 관한 전체 가이드는 적응성 Codelab 섹션의 Thermal API 통합 섹션을 참고하세요.

열 헤드룸 가이드라인

getThermalHeadroom 메서드를 폴링하여 기기의 열 상태를 모니터링할 수 있습니다. 이 메서드는 기기가 THERMAL_STATUS_SEVERE에 도달하기 전까지 얼마나 오랫동안 현재 성능 수준을 유지할 수 있는지 예측합니다. 예를 들어 getThermalHeadroom(30)이 0.8을 반환하면 헤드룸이 30초 후에 0.8에 도달할 것으로 예상되며 심각한 제한에서 0.2 거리가 떨어져 있는 경우 1.0입니다. 시간이 워크로드를 실행하는 데 필요한 시간보다 짧은 경우 게임은 워크로드를 지속 가능한 수준으로 줄여야 합니다. 예를 들어 게임은 프레임 속도를 낮추거나 충실도를 낮추거나 네트워크 연결 작업을 줄일 수 있습니다.

열 상태 및 의미

Thermal API의 기기 제한사항

이전 기기에 Thermal API를 구현했기 때문에 Thermal API에 몇 가지 알려진 제한사항이나 추가 요구사항이 있습니다. 제한사항과 해결 방법은 다음과 같습니다.

  • GetThermalHeadroom() API를 너무 자주 호출하지 마세요. 이렇게 하면 API가 NaN을 반환합니다. 초당 최대 한 번 호출해야 합니다.
  • GetThermalHeadroom()의 초깃값이 NaN이면 기기에서 API를 사용할 수 없습니다.
  • GetThermalHeadroom()가 높은 값 (예: 0.85 이상)을 반환하고 GetCurrentThermalStatus()가 여전히 THERMAL_STATUS_NONE을 반환하면 상태가 업데이트되지 않을 가능성이 높습니다. 휴리스틱을 사용하여 올바른 열 제한 상태를 추정하거나 getCurrentThermalStatus() 없이 getThermalHeadroom()를 사용하세요.

휴리스틱 예시:

  1. Thermal API가 지원되는지 확인합니다. isAPISupported()는 첫 번째 getThermalHeadroom 호출의 값을 확인하여 0 또는 NaN이 아닌지 확인하고 첫 번째 값이 0 또는 NaN이면 API 사용을 건너뜁니다.
  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 지원을 판단하는 휴리스틱의 예