תאריך הפרסום:
Android 11 (רמת API 30) – Thermal API
Android 12 (רמת API 31) – NDK API
(גרסת טרום-השקה) Android 15 (DP1) – getThermalHeadroomThresholds()
הביצועים הפוטנציאליים של האפליקציה מוגבלים על ידי המצב התרמי של המכשיר, שיכול להשתנות בהתאם למאפיינים כמו מזג האוויר, השימוש האחרון והעיצוב התרמי של המכשיר. מכשירים יכולים לשמור על רמת ביצועים גבוהה רק למשך זמן מוגבל לפני שהם מוגבלים בגלל חום. אחד היעדים העיקריים של ההטמעה צריך להיות השגת יעדי הביצועים בלי לחרוג ממגבלות הטמפרטורה. ה-API של Thermal מאפשר זאת בלי צורך באופטימיזציות ספציפיות למכשיר. בנוסף, כשמנסים לפתור בעיות בביצועים, חשוב לדעת אם המצב התרמי של המכשיר מגביל את הביצועים.
למנועי משחקים יש בדרך כלל פרמטרים של ביצועים בזמן ריצה, שאפשר לשנות כדי להתאים את עומס העבודה שהמנוע מטיל על המכשיר. לדוגמה, הפרמטרים האלה יכולים להגדיר את מספר ה-worker threads, את הקשר בין ה-worker threads לליבות גדולות וקטנות, את אפשרויות הדיוק של ה-GPU ואת הרזולוציות של ה-framebuffer. ב-Unity Engine, מפתחי משחקים יכולים לשנות את עומס העבודה על ידי שינוי הגדרות האיכות באמצעות הפלאגין Adaptive Performance. ב-Unreal Engine, משתמשים בהגדרות ההתאמה כדי לשנות את רמות האיכות באופן דינמי.
כשהמכשיר מתקרב למצב תרמי לא בטוח, המשחק יכול להימנע מהגבלת מהירות העיבוד על ידי הקטנת עומס העבודה באמצעות הפרמטרים האלה. כדי להימנע מהגבלת מהירות, כדאי לעקוב אחרי המצב התרמי של המכשיר ולהתאים באופן יזום את עומס העבודה של מנוע המשחק.
אם המכשיר מתחמם יתר על המידה, עומס העבודה חייב לרדת מתחת לרמת הביצועים הקבועה כדי שהחום יתפזר. אחרי שהמרווח התרמי יורד לרמות בטוחות יותר, המשחק יכול להעלות שוב את הגדרות האיכות, אבל חשוב למצוא רמת איכות ברת קיימא לזמן משחק אופטימלי.
אפשר לעקוב אחרי מצב הטמפרטורה של המכשיר באמצעות שיטת הסקר 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(0);
ALOGI("ThermalHeadroom: %f", thermal_headroom);
Java
float thermalHeadroom = powerManager.getThermalHeadroom(0);
Log.d("ADPF", "ThermalHeadroom: " + 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, כלומר יש מרחק של 0.2 מהגבלת קצב העברת נתונים חמורה, או 1.0. אם הזמן קצר מהזמן שנדרש להפעלת עומס העבודה, המשחק צריך להפחית את עומס העבודה לרמה שניתן לשמור עליה. לדוגמה, המשחק יכול להפחית את קצב הפריימים, להפחית את רמת הדיוק או להפחית את העומס על קישוריות הרשת.
המשמעות של סטטוסי הטמפרטורה
- אם לא מתבצעת במכשיר הגבלת מהירות בגלל התחממות יתר:
- הגבלת קצב נתונים מסוימת, אבל ללא השפעה משמעותית על הביצועים:
- הגבלת קצב משמעותית שמשפיעה על הביצועים:
מגבלות של 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().
דוגמה להיוריסטיקה:
- בודקים אם יש תמיכה ב-Thermal API.
isAPISupported()בודקת את הערך של הקריאה הראשונה אלgetThermalHeadroomכדי לוודא שהוא לא 0 או NaN, ומדלגת על השימוש בממשק ה-API אם הערך הראשון הוא 0 או NaN. - אם
getCurrentThermalStatus()מחזיר ערך שונה מ-THERMAL_STATUS_NONE, המכשיר עובר ויסות תרמי. - אם הפקודה
getCurrentThermalStatus()מחזירה שוב ושוב את הערךTHERMAL_STATUS_NONE, זה לא אומר בהכרח שהמכשיר לא עובר ויסות תרמי. יכול להיות שהמכשיר לא תומך ב-getCurrentThermalStatus(). בודקים את ערך ההחזרה שלgetThermalHeadroom()כדי לוודא את מצב המכשיר. - אם הפונקציה
getThermalHeadroom()מחזירה ערך גדול מ-1.0, יכול להיות שהסטטוס הואTHERMAL_STATUS_SEVEREאו גבוה יותר. במקרה כזה, צריך להפחית את עומס העבודה באופן מיידי ולשמור על עומס עבודה נמוך יותר עד שהפונקציהgetThermalHeadroom()תחזיר ערך נמוך יותר. - אם הפונקציה
getThermalHeadroom()מחזירה ערך של 0.95, יכול להיות שהסטטוס הואTHERMAL_STATUS_MODERATEאו גבוה יותר. במקרה כזה, צריך לצמצם את עומס העבודה באופן מיידי ולהמשיך לעקוב כדי למנוע קריאה גבוהה יותר. - אם הפונקציה
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.
}
}
}
דיאגרמה: