API térmica

Lanzamiento:

Android 11 (nivel de API 30): API de Thermal

Android 12 (nivel de API 31): API de NDK

Android 15 (versión preliminar) (DP1): getThermalHeadroomThresholds()

El rendimiento potencial de tu app está limitado por el estado térmico del dispositivo, que puede variar según las características, como el clima, el uso reciente y el diseño térmico del dispositivo. Los dispositivos solo pueden mantener un nivel alto de rendimiento durante un tiempo limitado antes de que se los limite térmicamente. Un objetivo clave de la implementación debe ser alcanzar los objetivos de rendimiento sin exceder las limitaciones térmicas. La API de Thermal lo hace posible sin necesidad de optimizaciones específicas del dispositivo. Además, cuando depuras problemas de rendimiento, es importante saber si el estado térmico del dispositivo limita el rendimiento.

Por lo general, los motores de juegos tienen parámetros de rendimiento del entorno de ejecución que pueden ajustar la carga de trabajo que el motor coloca en el dispositivo. Por ejemplo, estos parámetros pueden establecer la cantidad de subprocesos de trabajo, afinidad de subprocesos de trabajo para núcleos grandes y pequeños, opciones de fidelidad de GPU y resoluciones de búfer de fotogramas. En Unity Engine, los desarrolladores de juegos pueden ajustar la carga de trabajo cambiando la configuración de calidad con el complemento de rendimiento adaptable. En Unreal Engine, usa la Configuración de escalabilidad para ajustar los niveles de calidad de forma dinámica.

Cuando un dispositivo se acerca a un estado térmico inseguro, el juego puede evitar ser limitado al disminuir la carga de trabajo a través de estos parámetros. Para evitar la limitación, se debe supervisar el estado térmico del dispositivo y ajustar la carga de trabajo del motor de juego de forma proactiva. Una vez que el dispositivo se sobrecalienta, la carga de trabajo debe caer por debajo del nivel de rendimiento sostenible para disipar el calor. Después de que el margen térmico disminuya a niveles más seguros, el juego puede volver a aumentar la configuración de calidad, pero asegúrate de encontrar un nivel de calidad sostenible para obtener un tiempo de juego óptimo.

Puedes supervisar el estado térmico del dispositivo mediante un sondeo del método getThermalHeadroom. Este método predice cuánto tiempo el dispositivo puede mantener el nivel de rendimiento actual sin sobrecalentarse. Si el tiempo es menor que la cantidad necesaria para ejecutar la carga de trabajo, el juego debe disminuirla a un nivel sostenible. Por ejemplo, el juego puede cambiar a núcleos más pequeños, reducir la velocidad de fotogramas o reducir la fidelidad.

Integración previa de la API de Thermal de ADPF
Figura 1: Margen térmico sin supervisar de forma activa getThermalHeadroom
Integración posterior de la API de Thermal de ADPF
Figura 2. Margen térmico con supervisión activa de "getThermalHeadroom"

Adquiere Thermal Manager

Para usar la API de Thermal, primero debes adquirir Thermal Manager.

C++

AThermalManager* thermal_manager = AThermal_acquireManager();

Java

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

Prevé el margen térmico con x segundos de anticipación para tener más control

Puedes pedirle al sistema que pronostique la temperatura con x segundos de anticipación con la carga de trabajo actual. Esto te brinda un control más detallado y más tiempo para reaccionar, ya que reduce la carga de trabajo para evitar que se active el estrangulamiento térmico.

El resultado varía de 0.0 f (sin limitación, THERMAL_STATUS_NONE) a 1.0 f (limitación alta, THERMAL_STATUS_SEVERE). Si tienes diferentes niveles de calidad de gráficos en tus juegos, puedes seguir nuestros Lineamientos de margen térmico.

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

Como alternativa, puedes usar el estado térmico para obtener más información.

Cada modelo de dispositivo puede tener un diseño diferente. Es posible que algunos dispositivos puedan distribuir mejor el calor y, por lo tanto, soportar un margen térmico más alto antes de que se los limite. Si deseas leer una agrupación simplificada de rangos de margen térmico, puedes verificar el estado térmico para comprender el valor del margen térmico en el dispositivo actual.

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

Cómo recibir notificaciones cuando cambie el estado térmico

También puedes evitar sondear thermalHeadroom hasta que thermalStatus alcance un nivel determinado (por ejemplo, THERMAL_STATUS_LIGHT). Para ello, puedes registrar una devolución de llamada para permitir que el sistema te notifique cada vez que cambie el estado.

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

Recuerda quitar el objeto de escucha cuando termines.

C++

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

Java

powerManager.removeThermalStatusListener(listener);

Limpieza

Cuando termines, deberás limpiar el thermal_manager que adquiriste. Si usas Java, la referencia de PowerManager se puede recoger automáticamente. Sin embargo, si usas la API de Java a través de JNI y retuviste una referencia, recuerda limpiarla.

C++

AThermal_releaseManager(thermal_manager);

Si deseas obtener una guía completa para implementar la API de Thermal en un juego nativo de C++ con la API de C++ (API de NDK) y la API de Java (a través de JNI), consulta la sección Cómo integrar la API de Thermal en el codelab de adaptabilidad.

Lineamientos de margen térmico

Puedes supervisar el estado térmico del dispositivo mediante un sondeo del método getThermalHeadroom. Este método predice cuánto tiempo el dispositivo puede mantener el nivel de rendimiento actual antes de alcanzar THERMAL_STATUS_SEVERE. Por ejemplo, si getThermalHeadroom(30) muestra 0.8, indica que, en 30 segundos, se espera que el margen de seguridad alcance 0.8, donde hay 0.2 de distancia de la limitación severa, o 1.0. Si el tiempo es menor que la cantidad necesaria para ejecutar la carga de trabajo, el juego debe disminuirla a un nivel sostenible. Por ejemplo, el juego puede reducir la velocidad de fotogramas, reducir la fidelidad o reducir el trabajo de conectividad de red.

Estados térmicos y su significado

Limitaciones de dispositivos de la API de Thermal

Existen algunas limitaciones o requisitos adicionales conocidos de la API de Thermal debido a las implementaciones de la API de Thermal en dispositivos más antiguos. Las limitaciones y cómo solucionarlas son las siguientes:

  • No llames a la API de GetThermalHeadroom() con demasiada frecuencia. Si lo haces, la API muestra NaN. No debes llamarlo más de una vez cada 10 segundos.
  • Evita realizar llamadas desde varios subprocesos, ya que es más difícil garantizar la frecuencia de llamadas y puede hacer que la API devuelva NaN.
  • Si el valor inicial de GetThermalHeadroom() es NaN, la API no está disponible en el dispositivo.
  • Si GetThermalHeadroom() muestra un valor alto (p. ej., 0.85 o más) y GetCurrentThermalStatus() aún muestra THERMAL_STATUS_NONE, es probable que el estado no se haya actualizado. Usa heurísticas para estimar el estado correcto de limitación térmica o simplemente usa getThermalHeadroom() sin getCurrentThermalStatus().

Ejemplo de heurísticas:

  1. Comprueba que la API de Thermal sea compatible. isAPISupported() verifica el valor de la primera llamada a getThermalHeadroom para asegurarse de que no sea 0 o NaN, y omite el uso de la API si el primer valor es 0 o NaN.
  2. Si getCurrentThermalStatus() muestra un valor que no es THERMAL_STATUS_NONE, el dispositivo está limitado térmicamente.
  3. Si getCurrentThermalStatus() sigue mostrando THERMAL_STATUS_NONE, no significa necesariamente que el dispositivo no esté en modo de limitación térmica. Esto podría significar que getCurrentThermalStatus() no es compatible con el dispositivo. Verifica el valor que muestra getThermalHeadroom() para asegurarte de que el dispositivo esté en buen estado.
  4. Si getThermalHeadroom() muestra un valor superior a 1.0, el estado podría ser THERMAL_STATUS_SEVERE o superior. Reduce la carga de trabajo de inmediato y mantén una carga de trabajo más baja hasta que getThermalHeadroom() muestre un valor más bajo.
  5. Si getThermalHeadroom() muestra un valor de 0.95, el estado podría ser THERMAL_STATUS_MODERATE o superior. Reduce la carga de trabajo de inmediato y mantente atento para evitar lecturas más altas.
  6. Si getThermalHeadroom() muestra un valor de 0.85, el estado podría ser THERMAL_STATUS_LIGHT. Mantente atento y reduce la carga de trabajo si es posible.

Pseudocódigo:

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

Diagrama:

Ejemplo de heurística de ADPF
Figura 3: Ejemplo de heurística para determinar la compatibilidad con la API de Thermal en dispositivos más antiguos