Thermal API

Data premiery:

Android 11 (poziom API 30) – interfejs Thermal API

Android 12 (poziom 31 interfejsu API) – interfejs NDK API

(Wersja zapoznawcza) Android 15 (DP1) – getThermalHeadroomThresholds()

Potencjalna wydajność aplikacji jest ograniczona przez stan termiczny urządzenia, który może się różnić w zależności od takich czynników jak pogoda, ostatnie użycie i konstrukcja termiczna urządzenia. Urządzenia mogą utrzymywać wysoki poziom wydajności tylko przez ograniczony czas, zanim nastąpi ograniczenie wydajności z powodu temperatury. Głównym celem wdrożenia powinno być osiągnięcie celów związanych ze skutecznością bez przekraczania ograniczeń termicznych. Interfejs Thermal API umożliwia to bez konieczności optymalizacji pod kątem konkretnego urządzenia. Ponadto podczas debugowania problemów z wydajnością ważne jest, aby wiedzieć, czy stan termiczny urządzenia ogranicza wydajność.

Silniki gier mają zwykle parametry wydajności środowiska wykonawczego, które mogą dostosowywać obciążenie urządzenia. Za pomocą tych parametrów można na przykład ustawić liczbę wątków instancji roboczych, powiązanie wątków instancji roboczych z dużymi i małymi rdzeniami, opcje wierności GPU oraz rozdzielczość bufora ramki. W silniku Unity deweloperzy gier mogą dostosowywać obciążenie, zmieniając ustawienia jakości za pomocą wtyczki Adaptive Performance. W przypadku Unreal Engine użyj ustawień skalowalności, aby dynamicznie dostosowywać poziomy jakości.

Gdy urządzenie zbliża się do niebezpiecznego stanu termicznego, gra może uniknąć ograniczenia wydajności, zmniejszając obciążenie za pomocą tych parametrów. Aby uniknąć ograniczenia przepustowości, monitoruj stan termiczny urządzenia i proaktywnie dostosowuj obciążenie silnika gry.

Gdy urządzenie się przegrzeje, obciążenie musi spaść poniżej poziomu wydajności, który można utrzymać, aby rozproszyć ciepło. Gdy zapas mocy cieplnej zmniejszy się do bezpiecznego poziomu, gra może ponownie zwiększyć ustawienia jakości, ale musi znaleźć zrównoważony poziom jakości, aby zapewnić optymalny czas gry.

Stan termiczny urządzenia możesz monitorować, wywołując metodę getThermalHeadroom. Ta metoda przewiduje, jak długo urządzenie może utrzymać obecny poziom wydajności bez przegrzewania się. Jeśli czas jest krótszy niż czas potrzebny do uruchomienia zadania, gra powinna zmniejszyć obciążenie do poziomu, który można utrzymać. Na przykład gra może przejść na mniejsze rdzenie, zmniejszyć liczbę klatek na sekundę lub obniżyć jakość.

Wstępna integracja interfejsu ADPF Thermal API
Rysunek 1. Thermal Headroom bez aktywnego monitorowania getThermalHeadroom
ADPF Thermal API po integracji
Rysunek 2. Thermal Headroom z aktywnym monitorowaniem funkcji `getThermalHeadroom`

Pobieranie Menedżera termicznego

Aby korzystać z interfejsu Thermal API, musisz najpierw uzyskać dostęp do Menedżera termicznego.

C++

AThermalManager* thermal_manager = AThermal_acquireManager();

Java

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

Prognozowanie rezerwy termicznej z wyprzedzeniem o x sekund w celu zwiększenia kontroli

Możesz poprosić system o prognozowanie temperatury za x sekund przy bieżącym obciążeniu. Dzięki temu zyskujesz większą kontrolę i więcej czasu na reakcję, ponieważ zmniejsza to obciążenie, aby zapobiec włączeniu się ograniczenia termicznego.

Wynik mieści się w zakresie od 0,0f (brak ograniczania przepustowości,THERMAL_STATUS_NONE)

do 1,0f (duże ograniczenie,THERMAL_STATUS_SEVERE). Jeśli w swoich grach masz różne poziomy jakości grafiki, możesz skorzystać z naszych wskazówek dotyczących zapasu mocy cieplnej.

C++

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

Java

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

Możesz też sprawdzić stan termiczny, aby uzyskać więcej informacji.

Każdy model urządzenia może być zaprojektowany inaczej. Niektóre urządzenia mogą lepiej rozprowadzać ciepło, dzięki czemu mogą wytrzymać wyższy margines termiczny, zanim dojdzie do ograniczenia wydajności. Jeśli chcesz zapoznać się z uproszczonym podziałem zakresów rezerwy termicznej, możesz sprawdzić stan termiczny, aby zrozumieć wartość rezerwy termicznej na bieżącym urządzeniu.

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

Otrzymywanie powiadomień o zmianach stanu termicznego

Możesz też uniknąć sondowania thermalHeadroom, dopóki thermalStatus nie osiągnie określonego poziomu (np. THERMAL_STATUS_LIGHT). W tym celu możesz zarejestrować wywołanie zwrotne, aby system powiadamiał Cię o każdej zmianie stanu.

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

Pamiętaj, aby po zakończeniu pracy usunąć detektor.

C++

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

Java

powerManager.removeThermalStatusListener(listener);

Uporządkuj

Gdy skończysz, musisz wyczyścić uzyskany obiekt thermal_manager. Jeśli używasz języka Java, odwołanie do klasy PowerManager może zostać automatycznie usunięte przez mechanizm odśmiecania pamięci. Jeśli jednak używasz interfejsu Java API za pomocą JNI i zachowujesz odwołanie, pamiętaj, aby je usunąć.

C++

AThermal_releaseManager(thermal_manager);

Pełny przewodnik po implementacji interfejsu Thermal API w natywnej grze w C++ z użyciem interfejsu C++ API (NDK API) i interfejsu Java API (przez JNI) znajdziesz w sekcji Integracja interfejsu Thermal API w sekcji Adaptability codelab.

Wytyczne dotyczące zapasu mocy cieplnej

Stan termiczny urządzenia możesz monitorować, odpytując metodę getThermalHeadroom. Ta metoda przewiduje, jak długo urządzenie może utrzymać obecny poziom wydajności, zanim osiągnie THERMAL_STATUS_SEVERE. Jeśli na przykład funkcja getThermalHeadroom(30) zwraca wartość 0,8, oznacza to, że w ciągu 30 sekund wartość headroom osiągnie 0,8, czyli będzie o 0,2 jednostki oddalona od wartości 1,0, która oznacza poważne ograniczanie przepustowości. Jeśli czas jest krótszy niż czas potrzebny do uruchomienia zadania, gra powinna zmniejszyć obciążenie do poziomu, który można utrzymać. Może na przykład zmniejszyć liczbę klatek na sekundę, obniżyć jakość lub ograniczyć pracę związaną z łącznością sieciową.

Stany termiczne i ich znaczenie

Ograniczenia interfejsu Thermal API dotyczące urządzeń

Istnieją pewne znane ograniczenia lub dodatkowe wymagania dotyczące interfejsu Thermal API, które wynikają z jego implementacji na starszych urządzeniach. Ograniczenia i sposoby ich obejścia są następujące:

  • Nie wywołuj interfejsu GetThermalHeadroom() API zbyt często. W takim przypadku interfejs API zwraca NaN. Nie należy wywoływać tej funkcji częściej niż raz na 10 sekund.
  • Unikaj wywoływania z wielu wątków, ponieważ trudniej jest zapewnić częstotliwość wywoływania i może to spowodować, że interfejs API zwróci wartość NaN.
  • Jeśli początkowa wartość GetThermalHeadroom() to NaN, interfejs API nie jest dostępny na urządzeniu.
  • Jeśli funkcja GetThermalHeadroom() zwraca wysoką wartość (np. 0,85 lub więcej), a funkcja GetCurrentThermalStatus() nadal zwraca wartość THERMAL_STATUS_NONE, stan prawdopodobnie nie jest aktualizowany. Użyj heurystyki, aby oszacować prawidłowy stan ograniczenia termicznego, lub po prostu użyj getThermalHeadroom() bez getCurrentThermalStatus().

Przykład heurystyki:

  1. Sprawdź, czy interfejs Thermal API jest obsługiwany. isAPISupported() sprawdza wartość pierwszego wywołania funkcji getThermalHeadroom, aby upewnić się, że nie jest to 0 ani NaN, i pomija użycie interfejsu API, jeśli pierwsza wartość to 0 lub NaN.
  2. Jeśli getCurrentThermalStatus() zwraca wartość inną niż THERMAL_STATUS_NONE, oznacza to, że urządzenie jest poddawane ograniczeniu termicznemu.
  3. Jeśli getCurrentThermalStatus() stale zwraca wartość THERMAL_STATUS_NONE, nie musi to oznaczać, że urządzenie nie jest poddawane ograniczeniu termicznemu. Może to oznaczać, że usługa getCurrentThermalStatus() nie jest obsługiwana na tym urządzeniu. Sprawdź wartość zwracaną przez getThermalHeadroom(), aby upewnić się, że urządzenie jest w odpowiednim stanie.
  4. Jeśli getThermalHeadroom() zwraca wartość > 1,0, stan może być w rzeczywistości równy THERMAL_STATUS_SEVERE lub wyższy. Należy natychmiast zmniejszyć obciążenie i utrzymywać je na niższym poziomie, dopóki getThermalHeadroom() nie zwróci niższej wartości.
  5. Jeśli funkcja getThermalHeadroom() zwróci wartość 0,95, stan może być w rzeczywistości równy THERMAL_STATUS_MODERATE lub wyższy. W takim przypadku należy natychmiast zmniejszyć obciążenie i zachować czujność, aby zapobiec wyższym odczytom.
  6. Jeśli funkcja getThermalHeadroom() zwróci wartość 0, 85, stan może być w rzeczywistości THERMAL_STATUS_LIGHT. Zachowaj ostrożność i w miarę możliwości zmniejsz obciążenie.

Pseudokod:

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

Diagram:

Heurystyka ADPF
Przykład
Ilustracja 3. Przykład heurystyki do określania obsługi interfejsu Thermal API na starszych urządzeniach