Thermal API

Veröffentlicht:

Android 11 (API-Level 30) – Thermal API

Android 12 (API-Level 31) – NDK API

(Vorschau) Android 15 (DP1) – getThermalHeadroomThresholds()

Die potenzielle Leistung Ihrer App wird durch den thermischen Zustand des Geräts begrenzt. Dieser kann je nach Faktoren wie Wetter, aktueller Nutzung und thermischem Design des Geräts variieren. Geräte können nur für eine begrenzte Zeit eine hohe Leistung erbringen, bevor sie thermisch gedrosselt werden. Ein wichtiges Ziel Ihrer Implementierung sollte darin bestehen, Leistungsziele zu erreichen, ohne die thermischen Einschränkungen zu überschreiten. Mit der Thermal API ist das ohne gerätespezifische Optimierungen möglich. Außerdem ist es wichtig zu wissen, ob der thermische Zustand Ihres Geräts die Leistung einschränkt, wenn Sie Leistungsprobleme beheben.

Game-Engines haben in der Regel Laufzeit-Leistungsparameter, mit denen die Arbeitslast angepasst werden kann, die die Engine auf das Gerät legt. Mit diesen Parametern können beispielsweise die Anzahl der Arbeitsthreads, die Arbeitsthread-Affinität für große und kleine Kerne, die GPU-Qualitätsoptionen und die Framebuffer-Auflösungen festgelegt werden. In der Unity Engine können Spiele entwickler die Arbeitslast anpassen, indem sie die Qualitäts einstellungen mit dem Adaptive Performance-Plug-in ändern. In der Unreal Engine können Sie die Skalierbarkeitseinstellungen verwenden, um die Qualitätsstufen dynamisch anzupassen.

Wenn sich ein Gerät einem unsicheren thermischen Zustand nähert, kann Ihr Spiel durch Verringern der Arbeitslast mithilfe dieser Parameter gedrosselt werden. Um eine Drosselung zu vermeiden, sollten Sie den thermischen Zustand des Geräts im Blick behalten und die Arbeitslast der Game-Engine proaktiv anpassen.

Sobald das Gerät überhitzt, muss die Arbeitslast unter das nachhaltige Leistungsniveau sinken, damit die Wärme abgeleitet werden kann. Nachdem der thermische Spielraum auf ein sichereres Niveau gesunken ist, kann das Spiel die Qualitätseinstellungen wieder erhöhen. Sie sollten jedoch eine nachhaltige Qualitätsstufe für eine optimale Spielzeit finden.

Sie können den thermischen Zustand des Geräts überwachen, indem Sie die getThermalHeadroom Methode abfragen. Diese Methode sagt voraus, wie lange das Gerät das aktuelle Leistungsniveau aufrechterhalten kann, ohne zu überhitzen. Wenn die Zeit kürzer ist als die Zeit, die zum Ausführen der Arbeitslast erforderlich ist, sollte Ihr Spiel die Arbeitslast auf ein nachhaltiges Niveau reduzieren. Das Spiel kann beispielsweise auf kleinere Kerne umgestellt, die Framerate reduziert oder die Qualität verringert werden.

ADPF Thermal API – Vor der Integration
Abbildung 1. Thermischer Spielraum ohne aktive Überwachung von „getThermalHeadroom“
ADPF Thermal API nach der Integration
Abbildung 2. Thermischer Spielraum mit aktivem Monitoring von `getThermalHeadroom`

Thermal Manager abrufen

Um die Thermal API zu verwenden, müssen Sie zuerst den Thermal Manager abrufen.

C++

AThermalManager* thermal_manager = AThermal_acquireManager();

Java

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

Thermischen Spielraum abfragen

Sie können das System nach dem aktuellen thermischen Spielraum fragen. So erhalten Sie einen Hinweis darauf, wie nah Ihre Arbeitslast an der thermischen Drosselung liegt. Mit der API können Sie auch die Temperatur in x Sekunden mit der aktuellen Arbeitslast vorhersagen. So hat Ihre Anwendung mehr Zeit zu reagieren, ist aber weniger genau als die Verwendung des aktuellen thermischen Status.

Das Ergebnis liegt zwischen 0,0f (keine Drosselung, THERMAL_STATUS_NONE)

und 1,0f (starke Drosselung, THERMAL_STATUS_SEVERE). Wenn Ihre Spiele unterschiedliche Grafikqualitätsstufen haben, können Sie unsere Richtlinien für den thermischen Spielraumbefolgen.

C++

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

Java

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

Alternativ den thermischen Status zur Klärung verwenden

Jedes Gerätemodell kann anders gestaltet sein. Einige Geräte können Wärme besser ableiten und daher einen höheren thermischen Spielraum aushalten, bevor sie gedrosselt werden. Wenn Sie eine vereinfachte Gruppierung von Bereichen des thermischen Spielraums lesen möchten, können Sie den thermischen Status prüfen, um den Wert des thermischen Spielraums auf dem aktuellen Gerät zu verstehen.

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

Benachrichtigungen bei Änderungen des thermischen Status erhalten

Sie können auch vermeiden, thermalHeadroom abzufragen, bis thermalStatus ein bestimmtes Niveau erreicht (z. B. THERMAL_STATUS_LIGHT). Dazu können Sie einen Callback registrieren, damit Sie vom System benachrichtigt werden, wenn sich der Status geändert hat.

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

Denken Sie daran, den Listener zu entfernen, wenn Sie fertig sind.

C++

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

Java

powerManager.removeThermalStatusListener(listener);

Bereinigen

Wenn Sie fertig sind, müssen Sie den abgerufenen „thermal_manager“ bereinigen. Wenn Sie Java verwenden, kann die PowerManager-Referenz automatisch per Garbage Collection entfernt werden. Wenn Sie die Java API jedoch über JNI verwenden und eine Referenz beibehalten haben, denken Sie daran, die Referenz zu bereinigen.

C++

AThermal_releaseManager(thermal_manager);

Eine vollständige Anleitung zur Implementierung der Thermal API in einem nativen C++-Spiel mit der C++ API (NDK API) und der Java API (über JNI) finden Sie im Codelab zur Anpassungsfähigkeit im Abschnitt Thermal API einbinden.

Richtlinien für den thermischen Spielraum

Sie können den thermischen Zustand des Geräts überwachen, indem Sie die getThermalHeadroom Methode abfragen. Diese Methode sagt voraus, wie lange das Gerät das aktuelle Leistungsniveau aufrechterhalten kann, bevor THERMAL_STATUS_SEVERE erreicht wird. Wenn getThermalHeadroom(30) beispielsweise 0,8 zurückgibt, bedeutet das, dass der Spielraum in 30 Sekunden voraussichtlich 0,8 erreichen wird. Das ist 0,2 von der starken Drosselung (1,0) entfernt. Wenn die Zeit kürzer ist als die Zeit, die zum Ausführen der Arbeitslast erforderlich ist, sollte Ihr Spiel die Arbeitslast auf ein nachhaltiges Niveau reduzieren. Das Spiel kann beispielsweise die Framerate reduzieren, die Qualität verringern oder die Arbeitslast für die Netzwerkverbindung reduzieren.

Thermische Status und ihre Bedeutung

Geräteeinschränkungen der Thermal API

Aufgrund der Implementierungen der Thermal API auf älteren Geräten gibt es einige bekannte Einschränkungen oder zusätzliche Anforderungen an die Thermal API. Die Einschränkungen und Möglichkeiten, sie zu umgehen, sind wie folgt:

  • Rufen Sie die GetThermalHeadroom() API nicht zu häufig auf. Andernfalls gibt die API NaN zurück. Sie sollten sie nicht mehr als einmal alle 10 Sekunden aufrufen.
  • Vermeiden Sie Aufrufe von mehreren Threads aus. Es ist schwieriger, die Aufrufhäufigkeit zu gewährleisten, und die API gibt möglicherweise NaN zurück.
  • Wenn der Anfangswert von GetThermalHeadroom() NaN ist, ist die API auf dem Gerät nicht verfügbar.
  • Wenn GetThermalHeadroom() einen hohen Wert zurückgibt (z.B.0,85 oder mehr) und GetCurrentThermalStatus() weiterhin THERMAL_STATUS_NONE zurückgibt, wird der Status wahrscheinlich nicht aktualisiert. Verwenden Sie Heuristiken, um den korrekten Status der thermischen Drosselung zu schätzen, oder verwenden Sie einfach getThermalHeadroom() ohne getCurrentThermalStatus().

Beispiel für Heuristik:

  1. Prüfen Sie, ob die Thermal API unterstützt wird. isAPISupported() prüft den Wert des ersten Aufrufs von getThermalHeadroom, um sicherzustellen, dass er nicht 0 oder NaN ist. Wenn der erste Wert 0 oder NaN ist, wird die API nicht verwendet.
  2. Wenn getCurrentThermalStatus() einen anderen Wert als THERMAL_STATUS_NONE zurückgibt, wird das Gerät thermisch gedrosselt.
  3. Wenn getCurrentThermalStatus() weiterhin THERMAL_STATUS_NONE zurückgibt, bedeutet das nicht unbedingt, dass das Gerät nicht thermisch gedrosselt wird. Es kann bedeuten, dass getCurrentThermalStatus() auf dem Gerät nicht unterstützt wird. Prüfen Sie den Rückgabewert von getThermalHeadroom(), um den Zustand des Geräts zu ermitteln.
  4. Wenn getThermalHeadroom() einen Wert von > 1,0 zurückgibt, kann der Status tatsächlich THERMAL_STATUS_SEVERE oder höher sein. Reduzieren Sie die Arbeitslast sofort und halten Sie sie so lange niedrig, bis getThermalHeadroom() einen niedrigeren Wert zurückgibt.
  5. Wenn getThermalHeadroom() einen Wert von 0,95 zurückgibt, kann der Status tatsächlich THERMAL_STATUS_MODERATE oder höher sein. Reduzieren Sie die Arbeitslast sofort und behalten Sie die Überwachung bei, um höhere Werte zu vermeiden.
  6. Wenn getThermalHeadroom() einen Wert von 0,85 zurückgibt, kann der Status tatsächlich THERMAL_STATUS_LIGHT sein. Behalten Sie die Überwachung bei und reduzieren Sie die Arbeitslast, wenn möglich.

Pseudocode:

  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.
      }
    }
  }

Diagramm:

ADPF-Heuristik
Beispiel
Abbildung 3: Beispiel für eine Heuristik zur Bestimmung der Unterstützung der Thermal API auf älteren Geräten