Thermal API

Wydane:

Android 11 (poziom API 30) – interfejs API w wersji Thermal API

Android 12 (poziom API 31) – NDK API

(Podgląd) Android 15 (DP1) – getThermalHeadroomThresholds()

Potencjalna wydajność aplikacji jest ograniczona przez stan termiczny urządzenia, który może się zmieniać w zależności od takich czynników jak pogoda, ostatnio używane aplikacje i projekt termiczny urządzenia. Urządzenia mogą utrzymywać wysoki poziom wydajności tylko przez ograniczony czas, zanim nastąpi ograniczenie temperatury. Głównym celem wdrożenia powinno być osiągnięcie celów dotyczących wydajnoś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 zwykle mają parametry wydajności w czasie działania, które umożliwiają dostosowanie obciążenia, jakie silnik generuje na urządzeniu. Parametry te mogą na przykład określać liczbę wątków roboczych, powiązanie wątków roboczych z dużymi i małymi rdzeniami, opcje wierności GPU oraz rozdzielczości bufora ramki. W Unity Engine deweloperzy gier mogą dostosować 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ąć Throttle, należy monitorować stan termiczny urządzenia i aktywnie dostosowywać obciążenie silnika gry. Gdy urządzenie się przegrzewa, obciążenie musi spaść poniżej poziomu wydajności, aby rozproszyć ciepło. Gdy margines temperatury spadnie do bezpiecznego poziomu, gra może ponownie zwiększyć ustawienia jakości, ale pamiętaj, aby znaleźć optymalny poziom jakości dla optymalnego czasu gry.

Stan termiczny urządzenia możesz monitorować, wysyłając zapytanie do metody getThermalHeadroom. Ta metoda przewiduje, jak długo urządzenie może utrzymać obecny poziom wydajności bez przegrzania. Jeśli czas jest krótszy niż czas potrzebny do wykonania zadania, gra powinna zmniejszyć obciążenie do poziomu, który można utrzymać. Gra może na przykład przełączyć się na mniejsze rdzenie, zmniejszyć liczbę klatek na sekundę lub obniżyć jakość.

ADPF Thermal API wstępna integracja
Rysunek 1. Zasilanie bez aktywnego monitorowania getThermalHeadroom
Integracja z interfejsem ADPF Thermal API Rysunek 2.
Możliwość poprawy wyniku optymalizacji z aktywnym monitorowaniem funkcji „getThermalHeadroom”

Pozyskiwanie Menedżera termicznego

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

C++

AThermalManager* thermal_manager = AThermal_acquireManager();

Java

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

Prognozuj margines cieplny na x sekund w przód, aby uzyskać większą kontrolę

Możesz poprosić system o prognozę temperatury na x sekund z obecnym obciążeniem. Dzięki temu masz większą kontrolę i więcej czasu na reakcję, ponieważ zmniejsza się obciążenie, co zapobiega uruchamianiu ograniczania wydajności ze względu na temperaturę.

Wynik waha się od 0,0 (brak ograniczeń, THERMAL_STATUS_NONE) do 1,0 (silne ograniczenie, THERMAL_STATUS_SEVERE). Jeśli w swoich grach masz różne poziomy jakości grafiki, możesz postępować zgodnie z naszymi wskazówkami dotyczącymi marginesu mocy termicznej.

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

Możesz też sprawdzić stan termiczny.

Każdy model urządzenia może być skonstruowany inaczej. Niektóre urządzenia mogą lepiej rozprowadzać ciepło i dzięki temu wytrzymać wyższą temperaturę, zanim zostaną ograniczone. Jeśli chcesz odczytać uproszczone zgrupowanie zakresów marginesu temperatury, możesz sprawdzić stan termiczny, aby zrozumieć wartość marginesu temperatury 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ń, gdy zmienia się stan termiczny

Możesz też nie przeprowadzać zapytania thermalHeadroom, dopóki thermalStatus nie osiągnie określonego poziomu (na przykład THERMAL_STATUS_LIGHT). Aby to zrobić, możesz zarejestrować wywołanie zwrotne, aby system informował 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 usunąć słuchacza.

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

Po zakończeniu musisz wyczyścić uzyskany plik thermal_manager. Jeśli używasz języka Java, odwołanie do PowerManager może zostać automatycznie usunięte. 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 wdrażaniu interfejsu Thermal API w natywnej grze w C++ z wykorzystaniem zarówno interfejsu C++ API (NDK API), jak i interfejsu Java API (za pomocą JNI) znajdziesz w sekcji Integracja interfejsu Thermal APIAdaptability Codelab.

Wytyczne dotyczące marginesu mocy

Stan termiczny urządzenia możesz monitorować, wysyłając zapytanie do metody getThermalHeadroom. Ta metoda przewiduje, jak długo urządzenie może utrzymać obecny poziom wydajności, zanim osiągnie wartość THERMAL_STATUS_SEVERE. Jeśli na przykład getThermalHeadroom(30) zwraca wartość 0,8, oznacza to, że za 30 sekund wolna przestrzeń ma osiągnąć wartość 0,8, a w tym czasie 0,2 sekundy dzieli od poważnego ograniczenia, czyli wartości 1,0. Jeśli czas jest krótszy niż czas potrzebny do wykonania zadania, gra powinna zmniejszyć obciążenie do poziomu, który jest możliwy do wykonania. Na przykład gra może zmniejszyć częstotliwość klatek, obniżyć jakość lub ograniczyć pracę związaną z połączeniem sieciowym.

Stany termiczne i ich znaczenie

Ograniczenia interfejsu Thermal API dotyczące urządzeń

Istnieją pewne znane ograniczenia lub dodatkowe wymagania interfejsu Thermal API ze względu na implementacje interfejsu Thermal API na starszych urządzeniach. Ograniczenia i sposoby ich obejścia:

  • Nie wywołuj interfejsu API GetThermalHeadroom() zbyt często. W takim przypadku interfejs API zwraca NaN. Nie należy go wywoływać częściej niż raz na 10 sekund.
  • Unikaj wywoływania z kilku wątków, ponieważ utrudnia to zapewnienie odpowiedniej częstotliwości wywołań i może spowodować, że interfejs API zwróci wartość NaN.
  • Jeśli początkowa wartość GetThermalHeadroom() to NaN, interfejs API jest niedostę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 jest prawdopodobnie nieaktualny. Użyj heurystyki do oszacowania prawidłowego stanu 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 ona równa 0 ani NaN. Jeśli pierwsza wartość jest równa 0 lub NaN, interfejs API nie jest używany.
  2. Jeśli funkcja getCurrentThermalStatus() zwraca wartość inną niż THERMAL_STATUS_NONE, urządzenie jest ograniczane termicznie.
  3. Jeśli getCurrentThermalStatus() stale zwraca wartość THERMAL_STATUS_NONE, niekoniecznie oznacza to, że urządzenie nie jest ograniczane termicznie. Może to oznaczać, że usługa getCurrentThermalStatus() nie jest obsługiwana na urządzeniu. Sprawdź wartość zwracaną przez getThermalHeadroom(), aby mieć pewność, że urządzenie jest w dobrym stanie.
  4. Jeśli getThermalHeadroom() zwraca wartość > 1,0, stan może być faktycznie THERMAL_STATUS_SEVERE lub wyższy. Natychmiast zmniejsz obciążenie i utrzymuj je na niższym poziomie, dopóki getThermalHeadroom() nie zwróci niższej wartości.
  5. Jeśli funkcja getThermalHeadroom() zwraca wartość 0,95, stan może być faktycznie THERMAL_STATUS_MODERATE lub wyższy.Natychmiast zmniejsz obciążenie i uważnie obserwuj, aby nie dopuścić do wyższych wartości
  6. Jeśli funkcja getThermalHeadroom() zwraca wartość 0, 85, stan może być THERMAL_STATUS_LIGHT. W takim przypadku należy zachować ostrożność i w miarę możliwości zmniejszyć 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:

Przykład heurystyki ADPF
Rysunek 3. Przykład algorytmu określającego obsługę interfejsu Thermal API na starszych urządzeniach