Thermal API

リリース日:

Android 11(API レベル 30)- Thermal API

Android 12(API レベル 31)- NDK API

(プレビュー)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 Manager を取得する

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

熱ステータスが変化したときに通知を受け取る

また、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 を実装する方法の完全なガイドについては、適応性に関する CodelabThermal API を統合するセクションをご覧ください。

サーマル ヘッドルームのガイドライン

デバイスの温度状態をモニタリングするには、getThermalHeadroom メソッドをポーリングします。このメソッドは、デバイスが THERMAL_STATUS_SEVERE に達するまで現在のパフォーマンス レベルを維持できる期間を予測します。たとえば、getThermalHeadroom(30) が 0.8 を返した場合、30 秒後にヘッドルームが 0.8 に達すると予想され、厳重なスロットリング(1.0)まで 0.2 の距離があることを示します。維持できる期間がワークロードの実行に必要な期間よりも短い場合、ゲームはワークロードを持続可能なレベルまで下げる必要があります。たとえば、ゲームはフレームレートを下げたり、忠実度を下げたり、ネットワーク接続の作業を減らしたりする可能性があります。

温度ステータスとその意味

Thermal API のデバイスに関する制限事項

古いデバイスでの Thermal API の実装により、Thermal API には既知の制限事項や追加の要件がいくつかあります。制限事項とその回避策は次のとおりです。

  • GetThermalHeadroom() API を頻繁に呼び出さないでください。この場合、API は NaN を返します。10 秒に 1 回以上呼び出すことはできません。
  • 複数のスレッドから呼び出すことは避けてください。呼び出し頻度を確保することが難しくなり、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 のサポートを判断するヒューリスティックの例