Thermal API

תאריך הפרסום:

Android 11‏ (רמת API 30) – Thermal API

Android 12‏ (רמת API 31) – NDK API

(תצוגה מקדימה) Android 15‏ (DP1) – getThermalHeadroomThresholds()

הביצועים הפוטנציאליים של האפליקציה מוגבלים על ידי המצב התרמי של המכשיר, שעשוי להשתנות בהתאם למאפיינים כמו מזג האוויר, השימוש האחרון והעיצוב התרמי של המכשיר. המכשירים יכולים לשמור על רמת ביצועים גבוהה רק למשך זמן מוגבל, לפני שהם מוגבלים תרמית. אחד מהיעדים העיקריים של ההטמעה צריך להיות להשיג את יעדי הביצועים בלי לחרוג מהמגבלות התרמו-מכניות. Thermal API מאפשר לעשות זאת בלי צורך באופטימיזציות ספציפיות למכשיר. בנוסף, כשמנסים לנפות באגים שקשורים לביצועים, חשוב לדעת אם המצב התרמי של המכשיר מגביל את הביצועים.

בדרך כלל למנועי משחקים יש פרמטרים של ביצועים בסביבת זמן ריצה, שיכולים לשנות את עומס העבודה שהמנוע מטיל על המכשיר. לדוגמה, אפשר להשתמש בפרמטרים האלה כדי להגדיר את מספר חוטי העבודה, את ההתאמה של חוטי העבודה ליבות גדולות וקטנות, את אפשרויות הדיוק של ה-GPU ואת רזולוציות של framebuffer. במנוע Unity, מפתחי משחקים יכולים לשנות את עומס העבודה על ידי שינוי הגדרות האיכות באמצעות הפלאגין Adaptive Performance. ב-Unreal Engine, משתמשים בהגדרות הגמישות כדי לשנות את רמות האיכות באופן דינמי.

כשמכשיר מתקרב למצב תרמי לא בטוח, אפשר להפחית את עומס העבודה באמצעות הפרמטרים האלה כדי למנוע הגבלת המשחק. כדי למנוע הגבלת קצב התעבורה, כדאי לעקוב אחרי המצב התרמי של המכשיר ולשנות באופן יזום את עומס העבודה של מנוע המשחק. כשהמכשיר מתחמם יתר על המידה, עומס העבודה צריך לרדת מתחת לרמת הביצועים האפשרית כדי לפזר את החום. אחרי שהמרחב התרמי העודף יירד לרמות בטוחות יותר, המשחק יוכל להגדיל שוב את הגדרות האיכות, אבל חשוב למצוא רמת איכות יציבה כדי ליהנות מזמן משחק אופטימלי.

כדי לעקוב אחרי המצב התרמי של המכשיר, אפשר לבצע סקרים של השיטה getThermalHeadroom. השיטה הזו מאפשרת לחזות כמה זמן המכשיר יכול לשמור על רמת הביצועים הנוכחית בלי להתחמם יתר על המידה. אם הזמן קצר מהזמן הנדרש להפעלת עומס העבודה, המשחק צריך להפחית את עומס העבודה לרמה סבירה. לדוגמה, המשחק יכול לעבור ליבות קטנות יותר, להפחית את קצב הפריימים או להוריד את רמת הנאמנות.

שילוב מקדים של ADPF Thermal API
איור 1. מרווח תרמי ללא מעקב פעיל אחר getThermalHeadroom
ADPF Thermal API לאחר השילוב
איור 2. מרווח תרמי עם מעקב פעיל אחרי 'getThermalHeadroom'

רכישת Thermal Manager

כדי להשתמש ב-Thermal API, קודם צריך לרכוש את Thermal Manager

C++‎

AThermalManager* thermal_manager = AThermal_acquireManager();

Java

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

חיזוי של מרווח האנרגיה התרמית x שניות מראש כדי לקבל יותר שליטה

אפשר לבקש מהמערכת לחזות את הטמפרטורה x שניות קדימה לפי עומס העבודה הנוכחי. כך תוכלו לצמצם את עומס העבודה כדי למנוע הפעלה של הגבלת הביצועים כתוצאה מהתחממות, וכך לקבל שליטה מפורטת יותר זמן רב יותר להגיב.

התוצאה נעה בין 0.0f (ללא הגבלת קצב, THERMAL_STATUS_NONE) ל-1.0f (הגבלת קצב משמעותית, THERMAL_STATUS_SEVERE). אם יש לכם רמות שונות של איכות גרפיקה במשחקים, תוכלו לפעול לפי ההנחיות שלנו בנושא מרווח תרמי.

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

לחלופין, אפשר להסתמך על סטטוס התרמוסטט לקבלת הבהרה

העיצוב של כל דגם מכשיר עשוי להיות שונה. יכול להיות שחלק מהמכשירים יוכלו לחלק את החום בצורה טובה יותר, ולכן יוכלו לעמוד בלחץ תרמי גבוה יותר לפני שהם יאטו. אם אתם רוצים לקרוא קיבוץ פשוט יותר של טווחים של מרווח תרמי, תוכלו לבדוק את הסטטוס התרמי כדי להבין את הערך של מרווח תרמי במכשיר הנוכחי.

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

קבלת התראות כשהסטטוס התרמי משתנה

אפשר גם להימנע מבדיקת thermalHeadroom עד ש-thermalStatus מגיע לרמה מסוימת (לדוגמה: THERMAL_STATUS_LIGHT). כדי לעשות זאת, אפשר לרשום קריאה חוזרת (callback) כדי לאפשר למערכת להודיע לכם בכל פעם שהסטטוס השתנה.

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

חשוב לזכור להסיר את המאזין בסיום

C++‎

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

Java

powerManager.removeThermalStatusListener(listener);

ניקוי

בסיום, תצטרכו לנקות את ה-thermal_manager שקיבלתם. אם אתם משתמשים ב-Java, המערכת יכולה לאסוף את ההפניה ל-PowerManager באופן אוטומטי. אבל אם אתם משתמשים ב-Java API דרך JNI ושומרים על הפניה, חשוב לזכור לנקות את הפניה!

C++‎

AThermal_releaseManager(thermal_manager);

במדריך המלא בנושא הטמעת Thermal API במשחק מקורי ב-C++‎, שמשתמש גם ב-C++ API‏ (NDK API) וגם ב-Java API (דרך JNI), תוכלו לקרוא את הקטע Integrate Thermal API בקטע Adaptability codelab.

הנחיות לגבי מרווח תרמי

כדי לעקוב אחרי המצב התרמי של המכשיר, אפשר לבצע סקרים של השיטה getThermalHeadroom. השיטה הזו מנבאת כמה זמן המכשיר יכול לשמור על רמת הביצועים הנוכחית לפני שהוא יגיע ל-THERMAL_STATUS_SEVERE. לדוגמה, אם הערך המוחזר של getThermalHeadroom(30) הוא 0.8, המשמעות היא שבתוך 30 שניות, נפח העבודה הנוסף צפוי להגיע ל-0.8, כאשר המרחק מ-1.0 הוא 0.2. אם הזמן קצר מהזמן הנדרש להרצת עומס העבודה, המשחק צריך להפחית את עומס העבודה לרמה סבירה. לדוגמה, המשחק יכול להפחית את קצב הפריימים, את רמת הנאמנות או את העבודה של קישוריות הרשת.

הסטטוסים של הטמפרטורה והמשמעות שלהם

מגבלות על מכשירים ב-Thermal API

יש כמה מגבלות ידועות או דרישות נוספות של Thermal API, בגלל הטמעות של Thermal API במכשירים ישנים יותר. אלה המגבלות והדרכים לעקוף אותן:

  • אל תפעילו את ה-API של GetThermalHeadroom() בתדירות גבוהה מדי. אם תעשו זאת, ה-API יחזיר את הערך NaN. אין להפעיל אותו יותר מפעם אחת בכל 10 שניות.
  • מומלץ להימנע מקריאה מכמה שרשורים, כי קשה יותר לוודא את תדירות הקריאה ויכול להיות שה-API יחזיר את הערך NaN.
  • אם הערך הראשוני של GetThermalHeadroom() הוא NaN, ה-API לא זמין במכשיר
  • אם GetThermalHeadroom() מחזירה ערך גבוה (למשל: 0.85 או יותר) ו-GetCurrentThermalStatus() עדיין מחזירה את הערך THERMAL_STATUS_NONE, סביר להניח שהסטטוס לא עודכן. משתמשים בהיגוריסטיקה כדי להעריך את סטטוס הצמצום התרמי הנכון, או פשוט משתמשים ב-getThermalHeadroom() בלי getCurrentThermalStatus().

דוגמה להיוריסטיקה:

  1. בודקים אם יש תמיכה ב-Thermal API. isAPISupported() בודק את הערך של הקריאה הראשונה ל-getThermalHeadroom כדי לוודא שהוא לא 0 או NaN, ומדלג על השימוש ב-API אם הערך הראשון הוא 0 או NaN.
  2. אם getCurrentThermalStatus() מחזיר ערך שאינו THERMAL_STATUS_NONE, המכשיר עובר הגבלת מהירות תרמית.
  3. אם הערך שמוחזר על ידי getCurrentThermalStatus() הוא THERMAL_STATUS_NONE, זה לא בהכרח אומר שהמכשיר לא מושפע מהגבלת הביצועים התרמית. יכול להיות שאין תמיכה ב-getCurrentThermalStatus() במכשיר. בודקים את הערך המוחזר של getThermalHeadroom() כדי לוודא את מצב המכשיר.
  4. אם getThermalHeadroom() מחזיר ערך גדול מ-1.0, יכול להיות שהסטטוס הוא למעשה THERMAL_STATUS_SEVERE או גבוה יותר. במקרה כזה, צריך להפחית את עומס העבודה באופן מיידי ולשמור על עומס עבודה נמוך יותר עד ש-getThermalHeadroom() יחזיר ערך נמוך יותר.
  5. אם getThermalHeadroom() מחזירה ערך של 0.95, יכול להיות שהסטטוס הוא למעשה THERMAL_STATUS_MODERATE או גבוה יותר. במקרה כזה, צריך להפחית את עומס העבודה באופן מיידי ולהמשיך לעקוב כדי למנוע קריאה גבוהה יותר.
  6. אם getThermalHeadroom() מחזיר ערך של 0.85, יכול להיות שהסטטוס הוא למעשה THERMAL_STATUS_LIGHT. כדאי להמשיך לעקוב ולצמצם את עומס העבודה אם אפשר

קוד מדומה:

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

דיאגרמה:

דוגמה להיגיון של ADPF
איור 3.דוגמה להיררכיה לזיהוי תמיכה ב-Thermal API במכשירים ישנים יותר