ממשק ה-API של קצב הפריימים מאפשר לאפליקציות לעדכן את פלטפורמת Android לגבי הפריים הרצוי וזמין באפליקציות שמטרגטות את Android 11 (רמת API 30) ואילך. בעבר, רוב המכשירים תמכו רק בקצב רענון מסך אחד, בדרך כלל 60Hz, אבל המצב הזה השתנה. במכשירים רבים יש עכשיו תמיכה בתכונות נוספות קצבי רענון כמו 90Hz או 120Hz. חלק מהמכשירים תומכים בקצב רענון חלק בזמן שאחרים מציגים לרגע מסך שחור, שלרוב נמשך שנייה.
המטרה העיקרית של ה-API היא לאפשר לאפליקציות לנצל טוב יותר את כל היתרונות של כל
את קצב הרענון הנתמכים של המסך. לדוגמה, אפליקציה שמפעילה וידאו של 24Hz שמפעילה את setFrameRate()
עשויה לגרום למכשיר לשנות את קצב הרענון של המסך מ-60Hz ל-120Hz. קצב הרענון החדש מאפשר הפעלה חלקה של סרטונים באיכות 24Hz ללא רעידות, ללא צורך ב-3:2 pulldown, כפי שנדרש להפעלת אותו סרטון במסך 60Hz. כך המשתמש יהיה טוב יותר
חוויה אישית.
שימוש בסיסי
ב-Android יש כמה דרכים לגשת למשטחים ולשלוט בהם, ולכן יש כמה גרסאות של ה-API setFrameRate()
. כל גרסה של ה-API לוקחת את
אותם פרמטרים ופועלת בדיוק כמו האחרים:
Surface.setFrameRate()
SurfaceControl.Transaction.setFrameRate()
ANativeWindow_setFrameRate()
ASurfaceTransaction_setFrameRate()
האפליקציה לא צריכה לקחת בחשבון את קצב הרענון הנתמך בפועל של התצוגה,
שאפשר לקבל באמצעות קריאה
Display.getSupportedModes()
כדי להתקשר אל setFrameRate()
בבטחה. לדוגמה, גם אם המכשיר בלבד
יש תמיכה ב-60Hz, או לבצע קריאה אל setFrameRate()
עם קצב הפריימים המועדף לאפליקציה.
במכשירים שלא מתאים להם קצב רענון טוב יותר של המסך לקצב הפריימים של האפליקציה, קצב הרענון של המסך לא ישתנה.
כדי לבדוק אם קריאה ל-setFrameRate()
גורמת לשינוי בקצב הרענון של המסך, צריך להירשם לקבלת התראות על שינויים במסך באמצעות קריאה למספר DisplayManager.registerDisplayListener()
או AChoreographer_registerRefreshRateCallback()
.
כשקוראים ל-setFrameRate()
, עדיף להעביר את קצב הפריימים המדויק במקום לעגל למספר שלם. לדוגמה, בעת רינדור סרטון שהוקלט
29.97Hz, מעבירים ב-29.97 במקום לעגל ל-30.
באפליקציות וידאו, יש להגדיר את פרמטר התאימות שמועבר אל setFrameRate()
אל Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
כדי לתת רמז נוסף
פלטפורמת Android שהאפליקציה תשתמש בה בתפריט הנפתח כדי להתאים את עצמה לפלטפורמת Android לא תואמת
את קצב הרענון של המסך (שיגרמו לרעידות).
בתרחישים מסוימים, תכונת הווידאו תפסיק לשלוח פריימים, אבל היא תישאר
גלוי במסך לזמן מה. התרחישים הנפוצים: בזמן ההפעלה
מגיע לסוף הסרטון או כשהמשתמש משהה את ההפעלה. במקרים כאלה, צריך להפעיל את setFrameRate()
עם הפרמטר של קצב הפריימים מוגדר ל-0 כדי למחוק את ההגדרה של קצב הפריימים של פני השטח ולהחזיר אותה לערך ברירת המחדל. ניקוי ההגדרה של קצב הפריימים
גם אין צורך בכך כשהרוסים את המשטח, או כשהפני השטח
מוסתרת כי המשתמש עובר לאפליקציה אחרת. ניקוי של קצב הפריימים
רק כאשר המשטח נשאר גלוי וללא שימוש.
מתג לקצב פריימים בצורה לא חלקה
במכשירים מסוימים, המעבר בין שיעורי הרענון עשוי לגרום להפרעות חזותיות, כמו מסך שחור למשך שנייה או שתיים. פעולה זו מתרחשת בדרך כלל בממירים, בלוחות טלוויזיה,
ומכשירים דומים. כברירת מחדל, ה-framework של Android לא מחליף מצבים
כאשר Surface.setFrameRate()
מתבצעת קריאה ל-API, כדי למנוע הפרעות חזותיות כאלה.
חלק מהמשתמשים מעדיפים הפרעה חזותית בתחילת הסרטון ובסופו, במיוחד בסרטונים ארוכים. כך קצב הרענון של המסך יכול להתאים את קצב הפריימים של הסרטון, ונמנעים מיצירת ארטיפקטים של המרה לקצב פריימים כמו 3:2 למשוך למטה להפעלת סרט.
מהסיבה הזו, ניתן להפעיל מתגים לא יעילים של קצב רענון אם הבעת הסכמה מצד משתמשים ואפליקציות:
- משתמשים: כדי להביע הסכמה, המשתמשים יכולים להפעיל את ההגדרה התאמת קצב הפריימים לתוכן.
- אפליקציות: כדי להביע הסכמה, אפליקציות יכולות להעביר
CHANGE_FRAME_RATE_ALWAYS
אלsetFrameRate()
.
מומלץ תמיד להשתמש ב-CHANGE_FRAME_RATE_ALWAYS
בסרטונים ארוכים כמו סרטים. הסיבה לכך היא שהיתרון של התאמת קצב הפריימים של הסרטון עולה על החיסרון של ההפרעה שמתרחשת כשמשנים את קצב הרענון.
המלצות נוספות
מומלץ לפעול לפי ההמלצות הבאות בתרחישים נפוצים.
מספר משטחים
פלטפורמת Android תוכננה לטפל בצורה נכונה בתרחישים שבהם יש כמה משטחים עם הגדרות שונות של קצב פריימים. אם באפליקציה יש כמה משטחים עם שיעורי פריימים שונים, צריך לבצע קריאה ל-setFrameRate()
עם שיעור הפריימים הנכון לכל משטח. גם אם פועלות במכשיר מספר אפליקציות ב-
פעם אחת, באמצעות מסך מפוצל או מצב 'תמונה בתוך תמונה', כל אפליקציה יכולה להתקשר בבטחה
setFrameRate()
לפלטפורמות השונות.
הפלטפורמה לא משתנה לקצב הפריימים של האפליקציה
גם אם המכשיר תומך בקצב הפריימים שהאפליקציה מציינת בקריאה ל-setFrameRate()
, יש מקרים שבהם המכשיר לא יעביר את המסך לקצב הרענון הזה. לדוגמה, יכול להיות שלפלטפורמה בעדיפות גבוהה יותר
הגדרת קצב הפריימים, או שהמכשיר נמצא במצב חיסכון בסוללה (הגדרה של
הגבלה על קצב הרענון של המסך כדי לחסוך בצריכת הסוללה). האפליקציה צריכה להמשיך
פועלות בצורה תקינה כשהמכשיר לא משנה את קצב הרענון של המסך
הגדרת קצב הפריימים של האפליקציה, גם אם המכשיר משתנה במצב רגיל
בנסיבות השונות.
האפליקציה מחליטה איך להגיב כשקצב הרענון של המסך מוצג
לא תואם לקצב הפריימים באפליקציה. בסרטונים, קצב הפריימים קבוע לקצב של
את סרטון המקור, ויהיה צורך למשוך למטה כדי להציג את תוכן הסרטון. במקום זאת, המשחק עשוי לנסות לפעול בקצב הרענון של המסך במקום להישאר בקצב הפריימים המועדף עליו. האפליקציה לא צריכה לשנות את הערך שהיא מעבירה אל setFrameRate()
על סמך הפעולות של הפלטפורמה. היא צריכה להישאר מוגדרת
לקצב הפריימים המועדף של האפליקציה, בלי קשר לאופן שבו האפליקציה מטפלת במקרים שבהם
הפלטפורמה לא מתאימה את עצמה לבקשה של האפליקציה. כך, אם התנאים במכשיר ישתנו ויאפשרו להשתמש בקצבי רענון נוספים של המסך, בפלטפורמה יהיה המידע הנכון כדי לעבור לקצב הפריימים המועדף של האפליקציה.
במקרים שבהם האפליקציה לא פועלת או לא יכולה לפעול בקצב הרענון של המסך, האפליקציה לציין חותמות זמן של המצגת עבור כל פריים, באמצעות אחת על המנגנונים של הפלטפורמה להגדרת חותמות זמן של המצגת:
שימוש בחותמות הזמן האלה מונע מהפלטפורמה להציג גם מסגרת של אפליקציה מוקדם, דבר שיוביל לערעור מיותר. שימוש נכון במסגרת חותמות הזמן של המצגת קצת מסובכות. כדי למצוא משחקים, אפשר לעיין מדריך לקצב הפריימים לקבלת מידע נוסף על הימנעות ממשחקים, כדאי לשקול להשתמש הספרייה של Android בתחום קצב הפריימים.
במקרים מסוימים, הפלטפורמה עשויה לעבור למכפיל של קצב הפריימים שהאפליקציה ציינה ב-setFrameRate()
. לדוגמה, אפליקציה עשויה לקרוא ל-setFrameRate()
עם 60Hz, והמכשיר עשוי לשנות את המסך ל-120Hz. אחת מהסיבות לכך היא שאפליקציה אחרת כוללת משטח עם הגדרת קצב פריימים של 24Hz. במקרה כזה, הפעלת המסך ב-120Hz תאפשר להפעיל גם את פני השטח של 60Hz וגם את פני השטח של 24Hz בלי צורך ב-pulldown.
כשהתצוגה פועלת במכפיל של קצב הפריימים של האפליקציה, האפליקציה צריכה לציין חותמות זמן של הצגה לכל פריים כדי למנוע רעידות מיותרות. למשחקים, הספרייה של Android Frame Pacing מועילה בצורה נכונה הגדרת חותמות זמן להצגת הפריימים.
setFrameRate() לעומת preferredDisplayModeId
WindowManager.LayoutParams.preferredDisplayModeId
היא דרך נוספת שבה אפליקציות יכולות לציין את קצב הפריימים שלהן לפלטפורמה. במידה מסוימת
אפליקציות רוצים לשנות רק את קצב הרענון של המסך במקום לשנות
ההגדרות של מצב התצוגה, כמו רזולוציית המסך. באופן כללי, מומלץ
setFrameRate()
במקום preferredDisplayModeId
. setFrameRate()
שקל יותר להשתמש בה, כי האפליקציה לא צריכה לחפש
רשימה של מצבי תצוגה כדי למצוא מצב עם קצב פריימים ספציפי.
setFrameRate()
מאפשר לפלטפורמה לבחור בקצב פריימים תואם בתרחישים שבהם יש כמה משטחים שפועלים בקצב פריימים שונה. לדוגמה, נבחן תרחיש שבו שתי אפליקציות
פועל במצב מסך מפוצל ב-Pixel 4, כשאפליקציה אחת מפעילה וידאו של 24Hz
והשני מציג למשתמש רשימה שניתן לגלול. Pixel 4 תומך בשני קצב רענון של התצוגה: 60Hz ו-90Hz. באמצעות ה-API של preferredDisplayModeId
,
פלטפורמת הווידאו נאלצת לבחור ב-60Hz או 90Hz. בהתקשרות
setFrameRate()
עם 24Hz, משטח הווידאו מספק לפלטפורמה יותר
מידע על קצב הפריימים של סרטון המקור, מה שמאפשר לפלטפורמה
לבחור 90Hz לקצב הרענון של המסך, שזה טוב יותר מ-60Hz
במקרה הזה.
עם זאת, יש תרחישים שבהם כדאי להשתמש במאפיין preferredDisplayModeId
.
במקום setFrameRate()
, למשל:
- אם האפליקציה רוצה לשנות את הרזולוציה או הגדרות אחרות של מצב התצוגה, צריך להשתמש ב-
preferredDisplayModeId
. - הפלטפורמה תשנה את מצבי התצוגה רק בתגובה לקריאה ל-
setFrameRate()
אם שינוי המצב קל ולא צפוי להיות מורגש למשתמש. אם האפליקציה מעדיפה להחליף את הרענון של המסך גם אם נדרש מעבר למצב גדול (למשל, ב-Android TV) במכשיר), יש להשתמש ב-preferredDisplayModeId
. - אפליקציות שלא יכולות להתמודד עם ההצגה של כמה מסגרות של האפליקציה
שדורש הגדרה של חותמות זמן להצגה בכל פריים,
להשתמש ב-
preferredDisplayModeId
.
setFrameRate() לעומת preferredRefreshRate
WindowManager.LayoutParams#preferredRefreshRate
מגדירה קצב פריימים מועדף בחלון של האפליקציה, והקצב הרלוונטי
לכל הפלטפורמות בחלון. האפליקציה צריכה לציין את קצב הפריימים המועדף עליה, ללא קשר לקצבי הרענון הנתמכים במכשיר, בדומה ל-setFrameRate()
, כדי לתת לתזמון רמז טוב יותר לגבי קצב הפריימים המיועד של האפליקציה.
המערכת מתעלמת מ-preferredRefreshRate
ב-Surfaces שמשתמשים ב-setFrameRate()
. לחשבון
יש להשתמש ב-setFrameRate()
באופן כללי, אם אפשר.
preferencesרענןRate לעומת גורם מועדף ל-DisplayModeId
אם האפליקציות רוצות לשנות רק את קצב הרענון המועדף, עדיף להשתמש בהן
preferredRefreshRate
במקום preferredDisplayModeId
.
הימנעות מקריאה תכופה מדי ל-setFrameRate()
השיחה עם setFrameRate()
לא יקרה מאוד מבחינת הביצועים,
אפליקציות צריכות להימנע מקריאה ל-setFrameRate()
בכל פריים או כמה פעמים
שנייה. סביר להניח שקריאות אל setFrameRate()
יובילו לשינוי של
של קצב הרענון של המסך, מה שעלול לגרום לירידה בפריים במהלך המעבר.
כדאי לחשב מראש את קצב הפריימים הנכון ולקרוא ל-setFrameRate()
פעם אחת.
שימוש במשחקים או באפליקציות אחרות שאינן וידאו
למרות שסרטון הוא התרחיש לדוגמה העיקרי של setFrameRate()
API, הוא יכול להיות
שמשמש לאפליקציות אחרות. לדוגמה, משחק שהמטרה שלו היא שלא להציג מודעות למשך זמן ארוך מ-
60Hz (כדי להפחית את צריכת החשמל ולהשיג סשנים ארוכים יותר של הפעלה) יכולים להפעיל
Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
כאן
מכשיר שפועל ב-90Hz כברירת מחדל יפעל ב-60Hz
פעיל, כדי למנוע את
המשחק פעל ב-60Hz והתצוגה פעלה ב-90Hz.
שימוש ב-FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
מיועד רק לאפליקציות וידאו. עבור
שימוש שאינו וידאו, יש להשתמש ב-FRAME_RATE_COMPATIBILITY_DEFAULT
.
בחירת אסטרטגיה לשינוי של קצב הפריימים
- מומלץ מאוד שהאפליקציות שיוצגו יהיו סרטונים ארוכים כמו
סרטים, שיחה
setFrameRate(
fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)
כאשר FPS הוא קצב הפריימים של הסרטון. - מומלץ מאוד לא להשתמש באפליקציות שמתקשרות אל
setFrameRate()
באמצעותCHANGE_FRAME_RATE_ALWAYS
כאשר אתם מצפים שמשך הפעלת הסרטון יימשך כמה דקות או פחות.
שילוב לדוגמה של אפליקציות להפעלת סרטונים
מומלץ לפעול לפי השלבים הבאים לשילוב מתגי קצב רענון באפליקציות להפעלת סרטונים:
- קובעים את
changeFrameRateStrategy
:- אם מפעילים סרטון ארוך, כמו סרט, משתמשים ב-
MATCH_CONTENT_FRAMERATE_ALWAYS
- אם מפעילים סרטון קצר, כמו טריילר, צריך להשתמש בסמל
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
- אם מפעילים סרטון ארוך, כמו סרט, משתמשים ב-
- אם הערך של
changeFrameRateStrategy
הואCHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
, מעבר לשלב 4. - כדי לזהות אם עומדת להתרחש החלפה לא חלקה של קצב הרענון, צריך לוודא ששני העובדות הבאות מתקיימות:
- אי אפשר לעבור בצורה חלקה ממצב של קצב רענון נוכחי (נניח C) לקצב הפריימים של הסרטון (נניח V). המצב הזה יקרה אם C ו-V שונים ו-
Display.getMode().getAlternativeRefreshRates
לא מכיל מכפלה של V. - המשתמש הביע הסכמה לשינויים לא פשוטים בקצב הרענון. תוכלו לזהות
כדי לבדוק אם
DisplayManager.getMatchContentFrameRateUserPreference
החזרותMATCH_CONTENT_FRAMERATE_ALWAYS
- אי אפשר לעבור בצורה חלקה ממצב של קצב רענון נוכחי (נניח C) לקצב הפריימים של הסרטון (נניח V). המצב הזה יקרה אם C ו-V שונים ו-
- אם המעבר יתבצע בצורה חלקה, מבצעים את הפעולות הבאות:
- חיוג אל
setFrameRate
ולהעביר אותהfps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
, ו-changeFrameRateStrategy
, כאשרfps
הוא קצב הפריימים של הסרטון. - התחלת הפעלת הסרטון
- חיוג אל
- אם עומד להתבצע שינוי מצב לא חלק, צריך לבצע את הפעולות הבאות:
- הצגת חוויית משתמש כדי להודיע למשתמש. חשוב לזכור: מומלץ להטמיע דרך שבה המשתמש יוכל לסגור את ממשק המשתמש הזה ולדלג על העיכוב הנוסף בשלב 5.ד. הסיבה לכך היא שההשהיה המומלצת שלנו גדולה מהנדרש במסכים עם זמני מעבר מהירים יותר.
- קוראים ל-
setFrameRate
ומעבירים אליו את הערכיםfps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
ו-CHANGE_FRAME_RATE_ALWAYS
, כאשרfps
הוא קצב הפריימים של הסרטון. - יש להמתין ל-
onDisplayChanged
קריאה חוזרת. - ממתינים 2 שניות עד ששינוי המצב יסתיים.
- הפעלת הסרטון
הקוד המדומה בלבד שתומך במעבר חלק הוא:
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();
הקוד המדומה שתומך במעבר חלק ובמעבר לא חלק כפי שמתואר למעלה הוא:
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
if (isSeamlessSwitch(contentFrameRate)) {
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();
} else if (displayManager.getMatchContentFrameRateUserPreference()
== MATCH_CONTENT_FRAMERATE_ALWAYS) {
showRefreshRateSwitchUI();
sleep(shortDelaySoUserSeesUi);
displayManager.registerDisplayListener(…);
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ALWAYS);
transaction.apply();
waitForOnDisplayChanged();
sleep(twoSeconds);
hideRefreshRateSwitchUI();
beginPlayback();
}