API Thermal

Uscita:

Android 11 (livello API 30) - API Thermal

Android 12 (livello API 31) - API NDK

(Anteprima) Android 15 (DP1) - getThermalHeadroomThresholds()

Il rendimento potenziale della tua app è limitato dallo stato termico del dispositivo, che può variare in base a caratteristiche come meteo, utilizzo recente e design termico del dispositivo. I dispositivi possono mantenere un livello elevato di prestazioni solo per un periodo di tempo limitato prima di essere sottoposti a throttling termico. Uno scopo fondamentale della tua implementazione dovrebbe essere raggiungere gli obiettivi di rendimento senza superare le limitazioni termiche. L'API Thermal lo rende possibile senza dover eseguire ottimizzazioni specifiche per il dispositivo. Inoltre, durante il debug dei problemi di rendimento, è importante sapere se lo stato termico del dispositivo sta limitando il rendimento.

I motori di gioco in genere hanno parametri di prestazioni di runtime che possono regolare il carico di lavoro imposto dal motore sul dispositivo. Ad esempio, questi parametri possono impostare il numero di thread worker, l'affinità dei thread worker per i core grandi e piccoli, le opzioni di fedeltà della GPU e le risoluzioni del framebuffer. In Unity Engine, gli sviluppatori di giochi possono regolare il carico di lavoro modificando le Impostazioni di qualità utilizzando il plug-in Adaptive Performance. Per Unreal Engine, utilizza le Impostazioni di scalabilità per regolare dinamicamente i livelli di qualità.

Quando un dispositivo si avvicina a uno stato termico non sicuro, il gioco può evitare di essere limitato diminuendo il carico di lavoro tramite questi parametri. Per evitare il throttling, devi monitorare lo stato termico del dispositivo e regolare in modo proattivo il carico di lavoro del motore di gioco. Quando il dispositivo si surriscalda, il carico di lavoro deve diminuire al di sotto del livello di prestazioni sostenibile per dissipare il calore. Dopo che il margine termico è diminuito a livelli più sicuri, il gioco può aumentare di nuovo le impostazioni di qualità, ma assicurati di trovare un livello di qualità sostenibile per un tempo di gioco ottimale.

Puoi monitorare lo stato termico del dispositivo eseguendo il polling del metodo getThermalHeadroom. Questo metodo prevede per quanto tempo il dispositivo può mantenere il livello di prestazioni corrente senza surriscaldarsi. Se il tempo è inferiore al tempo necessario per eseguire il carico di lavoro, il gioco dovrebbe ridurlo a un livello sostenibile. Ad esempio, il gioco può passare a core più piccoli, ridurre la frequenza frame o la fedeltà.

Pre-integrazione dell'API ADPF Thermal
Figura 1. Headroom termico senza monitorare attivamente getThermalHeadroom
Post-integrazione dell'API ADPF Thermal
Figura 2. Headroom termico con monitoraggio attivo di "getThermalHeadroom"

Acquista Thermal Manager

Per utilizzare l'API Thermal, devi prima acquisire Thermal Manager

C++

AThermalManager* thermal_manager = AThermal_acquireManager();

Java

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

Prevedi il margine termico con x secondi di anticipo per un maggiore controllo

Puoi chiedere al sistema di prevedere la temperatura con x secondi di anticipo con il carico di lavoro corrente. In questo modo hai un controllo più granulare e più tempo per reagire riducendo il carico di lavoro per evitare l'attivazione del throttling termico.

Il risultato va da 0,0 f (nessun throttling, THERMAL_STATUS_NONE) a 1,0 f (throttling elevato, THERMAL_STATUS_SEVERE). Se i tuoi giochi hanno diversi livelli di qualità grafica, puoi seguire le nostre linee guida sul margine termico.

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

In alternativa, puoi fare affidamento sullo stato termico per avere chiarimenti

Ogni modello di dispositivo potrebbe essere progettato in modo diverso. Alcuni dispositivi potrebbero essere in grado di distribuire meglio il calore e quindi di resistere a un margine termico più elevato prima di essere limitati. Se vuoi leggere un raggruppamento semplificato di intervalli di margine termico, puoi controllare lo stato termico per comprendere il valore del margine termico sul dispositivo attuale.

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

Ricevere notifiche quando cambia lo stato termico

Puoi anche evitare di eseguire il polling di thermalHeadroom finché thermalStatus non raggiunge un determinato livello (ad esempio THERMAL_STATUS_LIGHT). Per farlo, puoi registrare un callback per consentire al sistema di inviarti una notifica ogni volta che lo stato cambia.

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

Ricordati di rimuovere l'ascoltatore al termine

C++

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

Java

powerManager.removeThermalStatusListener(listener);

Pulizia

Al termine, dovrai ripulire il file thermal_manager che hai acquisito. Se utilizzi Java, il riferimento a PowerManager può essere gestito automaticamente tramite il garbage collection. Tuttavia, se utilizzi l'API Java tramite JNI e hai conservato un riferimento, ricordati di ripulirlo.

C++

AThermal_releaseManager(thermal_manager);

Per una guida completa su come implementare l'API Thermal in un gioco nativo C++ utilizzando sia l'API C++ (API NDK) sia l'API Java (tramite JNI), consulta la sezione Integrare l'API Thermal nella sezione Codelab sull'adattabilità.

Linee guida per il margine termico

Puoi monitorare lo stato termico del dispositivo eseguendo il polling del metodo getThermalHeadroom. Questo metodo prevede per quanto tempo il dispositivo può mantenere il livello di rendimento corrente prima di raggiungere THERMAL_STATUS_SEVERE. Ad esempio, se getThermalHeadroom(30) restituisce 0,8, indica che in 30 secondi il margine di manovra dovrebbe raggiungere 0,8, dove manca 0,2 per il throttling severo o 1,0. Se il tempo è inferiore a quello necessario per eseguire il carico di lavoro, il gioco dovrebbe ridurlo a un livello sostenibile. Ad esempio, il gioco può ridurre la frequenza dei fotogrammi, la fedeltà o il lavoro di connettività di rete.

Stati termici e relativo significato

Limitazioni dei dispositivi dell'API Thermal

Esistono alcune limitazioni o requisiti aggiuntivi noti dell'API Thermal, a causa delle implementazioni dell'API Thermal sui dispositivi meno recenti. Di seguito sono riportate le limitazioni e le modalità per ovviarle:

  • Non chiamare l'API GetThermalHeadroom() troppo spesso. In questo caso, l'API restituisce NaN. Non dovresti chiamarlo più di una volta ogni 10 secondi.
  • Evita di effettuare chiamate da più thread, perché è più difficile garantire la frequenza di chiamata e l'API potrebbe restituire NaN.
  • Se il valore iniziale di GetThermalHeadroom() è NaN, l'API non è disponibile sul dispositivo
  • Se GetThermalHeadroom() restituisce un valore elevato (ad es. 0,85 o più) e GetCurrentThermalStatus() restituisce ancora THERMAL_STATUS_NONE, è probabile che lo stato non sia aggiornato. Utilizza le strategie di ricerca heuristica per stimare lo stato corretto della limitazione termica o utilizza semplicemente getThermalHeadroom() senza getCurrentThermalStatus().

Esempio di euristiche:

  1. Verifica che l'API Thermal sia supportata. isAPISupported() controlla il valore della prima chiamata a getThermalHeadroom per assicurarsi che non sia 0 o NaN e salta l'utilizzo dell'API se il primo valore è 0 o NaN.
  2. Se getCurrentThermalStatus() restituisce un valore diverso da THERMAL_STATUS_NONE, il dispositivo è sottoposto a throttling termico.
  3. Se getCurrentThermalStatus() continua a restituire THERMAL_STATUS_NONE, non significa necessariamente che il dispositivo non è soggetto a throttling termico. Ciò potrebbe indicare che getCurrentThermalStatus() non è supportato sul dispositivo. Controlla il valore restituito di getThermalHeadroom() per verificare le condizioni del dispositivo.
  4. Se getThermalHeadroom() restituisce un valore maggiore di 1, 0, lo stato potrebbe essere effettivamente THERMAL_STATUS_SEVERE o superiore. Riduci immediatamente il carico di lavoro e mantienilo basso finché getThermalHeadroom() non restituisce un valore inferiore.
  5. Se getThermalHeadroom() restituisce un valore pari a 0,95, lo stato potrebbe essere effettivamente THERMAL_STATUS_MODERATE o superiore, riduci immediatamente il carico di lavoro e mantieni l'attenzione per evitare una lettura più alta
  6. Se getThermalHeadroom() restituisce un valore di 0,85, lo stato potrebbe essere effettivamente THERMAL_STATUS_LIGHT, quindi fai attenzione e riduci il carico di lavoro se possibile

Pseudocodice:

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

Diagramma:

Esempio di euristica ADPF
Figura 3. Esempio di euristica per determinare il supporto dell'API Thermal sui dispositivi meno recenti