API Thermal

Date de sortie:

Android 11 (niveau d'API 30) – API Thermal

Android 12 (niveau d'API 31) : API NDK

(Preview) Android 15 (DP1) - getThermalHeadroomThresholds()

Les performances potentielles de votre application sont limitées par l'état thermique de l'appareil, qui peut varier en fonction de caractéristiques telles que la météo, son utilisation récente et sa conception thermique. Les appareils ne peuvent maintenir un niveau de performances élevé que pendant une durée limitée avant d'être soumis à une limitation thermique. L'un des buts ultimes de votre implémentation est d'atteindre les objectifs de performances sans dépasser les limites thermiques. L'API Thermal rend cela possible sans avoir besoin d'optimisations spécifiques à l'appareil. De plus, lors du débogage des problèmes de performances, il est important de savoir si l'état thermique de votre appareil limite les performances. De plus, lors du débogage des problèmes de performances, il est important de savoir si l'état thermique de votre appareil limite les performances.

Les moteurs de jeu disposent généralement de paramètres de performances d'exécution permettant d'ajuster la charge de travail qu'ils imposent à l'appareil. Par exemple, ces paramètres peuvent définir le nombre de threads de calcul, l'affinité entre les nœuds de calcul et les threads pour les cœurs de petite et de grande taille, les options de fidélité GPU et les résolutions de tampon de trame. Dans Unity Engine, les développeurs de jeux peuvent ajuster la charge de travail en modifiant les paramètres de qualité à l'aide du plug-in Adaptive Performance. Pour Unreal Engine, utilisez les paramètres d'évolutivité pour ajuster les niveaux de qualité de manière dynamique.

Lorsqu'un appareil se rapproche d'un état thermique qui n'est pas sûr, ces paramètres contribuent à réduire la charge de travail afin que votre jeu n'ait pas à en subir les conséquences. Pour éviter toute limitation, vous devez surveiller l'état thermique de l'appareil et ajuster de manière proactive la charge de travail du moteur de jeu. Lorsque l'appareil est en surchauffe, la charge de travail doit descendre en dessous des niveaux de performances durables pour dissiper la chaleur. Une fois que la plage de température thermique a atteint des niveaux plus sûrs, le jeu peut à nouveau augmenter les paramètres de qualité, mais veillez à trouver un niveau de qualité durable pour une durée de jeu optimale.

Pour surveiller l'état thermique de l'appareil, interrogez la méthode getThermalHeadroom. Cette méthode prédit la durée pendant laquelle l'appareil peut maintenir le niveau de performances actuel sans surchauffe. Si cette durée est inférieure au temps nécessaire à l'exécution de la charge de travail, le jeu doit limiter la charge de travail à un niveau durable. Par exemple, il peut basculer sur des cœurs plus petits, diminuer la fréquence de frames ou réduire la fidélité.

Pré-intégration de l'API thermique d'ADPF
Figure 1. Marge thermique sans surveillance active de getThermalHeadroom
Post-intégration de l'API Thermal d'ADPF
Figure 2. Marge thermique avec surveillance active de "getThermalHeadroom"

Acquérir un Thermal Manager

Pour utiliser l'API Thermal, vous devez d'abord vous procurer le gestionnaire de température.

C++

AThermalManager* thermal_manager = AThermal_acquireManager();

Java

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

Prévoyez la marge thermique x secondes à l'avance pour plus de contrôle

Vous pouvez demander au système de prévoir la température x secondes à l'avance avec la charge de travail actuelle. Vous bénéficiez ainsi d'un contrôle plus précis et de plus de temps pour réagir en réduisant la charge de travail afin d'empêcher toute limitation thermique.

Le résultat varie de 0.0f (pas de limitation, THERMAL_STATUS_NONE) à 1.0f (limitation élevée, THERMAL_STATUS_SEVERE). Si vous avez différents niveaux de qualité graphique dans vos jeux, vous pouvez suivre nos consignes relatives à la plage de température.

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

Vous pouvez aussi vous baser sur l'état thermique pour obtenir des précisions.

Chaque modèle d'appareil peut être conçu différemment. Certains appareils peuvent mieux distribuer la chaleur et ainsi supporter une plage de température plus élevée avant d'être limités. Si vous souhaitez lire un regroupement simplifié des plages de plage thermique, vous pouvez vérifier l'état thermique pour donner un sens à la valeur de plage thermique de l'appareil actuel.

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

Recevoir une notification en cas de changement d'état thermique

Vous pouvez également éviter d'interroger thermalHeadroom jusqu'à ce que thermalStatus atteigne un certain niveau (par exemple, THERMAL_STATUS_LIGHT). Pour ce faire, vous pouvez enregistrer un rappel afin de permettre au système de vous avertir lorsque l'état change.

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

N'oubliez pas de supprimer l'écouteur lorsque vous avez terminé.

C++

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

Java

powerManager.removeThermalStatusListener(listener);

Effectuer un nettoyage

Une fois que vous avez terminé, vous devez nettoyer le thermique_manager que vous avez acheté. Si vous utilisez Java, la référence PowerManager peut être automatiquement récupérée pour vous. Toutefois, si vous utilisez l'API Java via JNI et que vous avez conservé une référence, n'oubliez pas de la nettoyer.

C++

AThermal_releaseManager(thermal_manager);

Pour obtenir un guide complet sur l'implémentation de l'API Thermal dans un jeu C++ natif à l'aide de l'API C++ (API NDK) et de l'API Java (via JNI), consultez la section Intégrer l'API Thermal de la section Atelier de programmation sur l'adaptabilité.

Consignes concernant la plage de température

Pour surveiller l'état thermique de l'appareil, interrogez la méthode getThermalHeadroom. Cette méthode prédit la durée pendant laquelle l'appareil peut maintenir le niveau de performances actuel avant d'atteindre THERMAL_STATUS_SEVERE. Par exemple, si getThermalHeadroom(30) renvoie 0,8, cela signifie que dans 30 secondes, la marge de progression devrait atteindre 0,8, où la distance est de 0,2 autour d'un ralentissement sévère, ou de 1,0. Si le temps est inférieur au temps nécessaire à l'exécution de la charge de travail, votre jeu doit la réduire à un niveau durable. Par exemple, le jeu peut réduire la fréquence d'images, le niveau de fidélité ou le travail de connectivité réseau.

États et signification thermiques

Limites de l'API Thermal sur les appareils

L'API Thermal présente des limites ou des exigences supplémentaires connues, en raison des implémentations de cette API sur des appareils plus anciens. Les limites et la manière de les contourner sont les suivantes:

  • N'appelez pas l'API GetThermalHeadroom() trop souvent. Cela entraînerait le renvoi de la valeur "NaN" par l'API. Vous ne devez pas l'appeler au maximum une fois par seconde.
  • Si la valeur initiale de GetThermalHeadroom() est NaN, l'API n'est pas disponible sur l'appareil.
  • Si GetThermalHeadroom() renvoie une valeur élevée (par exemple, 0,85 ou plus) et que GetCurrentThermalStatus() renvoie toujours THERMAL_STATUS_NONE, l'état n'est probablement pas mis à jour. Utilisez des méthodes heuristiques pour estimer l'état correct de la limitation thermique ou utilisez simplement getThermalHeadroom() sans getCurrentThermalStatus().

Exemple d'utilisation heuristique:

  1. Vérifiez que l'API Thermal est compatible. isAPISupported() vérifie la valeur du premier appel à getThermalHeadroom pour s'assurer qu'elle n'est pas 0 ou NaN, et ignore l'utilisation de l'API si la première valeur est 0 ou NaN.
  2. Si getCurrentThermalStatus() renvoie une valeur autre que THERMAL_STATUS_NONE, l'appareil est soumis à une limitation thermique.
  3. Si getCurrentThermalStatus() continue de renvoyer THERMAL_STATUS_NONE, cela ne signifie pas nécessairement que l'appareil ne fait pas l'objet de limitation thermique. Cela peut signifier que getCurrentThermalStatus() n'est pas compatible avec l'appareil. Vérifiez la valeur renvoyée par getThermalHeadroom() pour vérifier l'état de l'appareil.
  4. Si getThermalHeadroom() renvoie une valeur supérieure à 1.0, l'état peut en réalité être THERMAL_STATUS_SEVERE ou supérieur. Réduisez immédiatement la charge de travail et maintenez-la jusqu'à ce que getThermalHeadroom() renvoie une valeur inférieure.
  5. Si getThermalHeadroom() renvoie une valeur de 0, 95, l'état peut en réalité être THERMAL_STATUS_MODERATE ou supérieur. Réduisez immédiatement la charge de travail et gardez la vigilance pour empêcher toute lecture plus importante.
  6. Si getThermalHeadroom() renvoie une valeur de 0, 85, l'état peut en réalité être THERMAL_STATUS_LIGHT. Conservez la surveillance et réduisez la charge de travail si possible.

Pseudo-code:

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

Diagramme:

Exemple heuristique ADPF
Figure 3 : Exemple d'utilisation d'une méthode heuristique pour déterminer la compatibilité de l'API Thermal sur des appareils plus anciens