קצב מסגרות

ממשק ה-API של קצב הפריימים מאפשר לאפליקציות לעדכן את פלטפורמת Android לגבי הפריים הרצוי וזמין באפליקציות שמטרגטות את Android 11 (רמת API 30) ואילך. באופן מסורתי, רוב המכשירים תומכים רק בקצב רענון אחד של המסך, בדרך כלל 60Hz, אבל זה השתנה. במכשירים רבים יש עכשיו תמיכה בתכונות נוספות קצבי רענון כמו 90Hz או 120Hz. חלק מהמכשירים תומכים בקצב רענון חלק בזמן שאחרים מציגים לרגע מסך שחור, שלרוב נמשך שנייה.

המטרה העיקרית של ה-API היא לאפשר לאפליקציות לנצל טוב יותר את כל היתרונות של כל את קצב הרענון הנתמכים של המסך. לדוגמה, אפליקציה שמפעילה סרטון 24Hz קריאה אל setFrameRate() עשויה לגרום למכשיר לשנות את המסך קצב הרענון מ-60Hz עד 120Hz. קצב הרענון החדש הזה מאפשר פעולה חלקה, הפעלה ללא רעשי רקע של וידאו 24Hz, ללא צורך בתפריט נפתח של 3:2 כפי שיהיה נדרש כדי להפעיל את אותו סרטון במסך 60Hz. כך המשתמש יהיה טוב יותר חוויה אישית.

שימוש בסיסי

ב-Android יש כמה דרכים לגשת לפלטפורמות ולשלוט בהן, לכן כמה גרסאות של setFrameRate() API. כל גרסה של ה-API לוקחת את אותם פרמטרים ופועלת בדיוק כמו האחרים:

האפליקציה לא צריכה לקחת בחשבון את קצב הרענון הנתמך בפועל של התצוגה, שאפשר לקבל באמצעות קריאה 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 עבור סרטונים ארוכים, כמו סרטים. הסיבה לכך היא שהיתרון של ההתאמה קצב הפריימים של הסרטון גובר על ההפרעה שמתרחשת בעת שינוי קצב הרענון.

המלצות נוספות

מומלץ ליישם את ההמלצות הבאות בתרחישים נפוצים.

מספר פלטפורמות

פלטפורמת Android מתוכננת לטפל כראוי בתרחישים שבהם יש בכמה פלטפורמות עם הגדרות שונות של קצב פריימים. כאשר באפליקציה יש כמה בפלטפורמות עם קצב פריימים שונה, יש לקרוא לפונקציה setFrameRate() קצב הפריימים של כל פלטפורמה. גם אם פועלות במכשיר מספר אפליקציות ב- פעם אחת, באמצעות מסך מפוצל או מצב 'תמונה בתוך תמונה', כל אפליקציה יכולה להתקשר בבטחה setFrameRate() לפלטפורמות השונות.

קצב הפריימים של הפלטפורמה לא משתנה

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

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

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

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

במקרים מסוימים, הפלטפורמה עשויה לעבור למכפלה של קצב הפריימים באפליקציה צוין ב-setFrameRate(). לדוגמה, אפליקציה עשויה לקרוא ל-setFrameRate() עם 60Hz, והמכשיר עשוי לשנות את המסך ל-120Hz. אחת הסיבות לכך הוא יכול לקרות אם לאפליקציה אחרת יש פלטפורמה עם קצב פריימים של 24Hz. לחשבון במקרה כזה, הפעלת הצג ב-120Hz תאפשר גם את השטח של 60Hz משטח של 24Hz להפעלה בלי צורך בתפריט נפתח.

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

setFrameRate() לעומת מועדףDisplayModeId

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() לעומת preferences RefreshRate

WindowManager.LayoutParams#preferredRefreshRate מגדירה קצב פריימים מועדף בחלון של האפליקציה, והקצב הרלוונטי לכל הפלטפורמות בחלון. האפליקציה צריכה לציין את הגרסה המועדפת קצב הפריימים, ללא קשר לקצב הרענון שנתמך במכשיר, בדומה ל- setFrameRate(), כדי לתת לכלי התזמון רמז טוב יותר לגבי מטרת האפליקציה קצב הפריימים

המערכת מתעלמת מ-preferredRefreshRate בפלטפורמות שנעשה בהן שימוש ב-setFrameRate(). לחשבון יש להשתמש ב-setFrameRate() באופן כללי, אם אפשר.

preferences RefreshRate לעומת מועדף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 כאשר אתם מצפים שמשך ההפעלה של הסרטון יימשך כמה דקות או פחות.

שילוב לדוגמה של אפליקציות להפעלת סרטונים

מומלץ לפעול לפי השלבים הבאים לשילוב מתגי קצב רענון באפליקציות להפעלת סרטונים:

  1. בוחרים את changeFrameRateStrategy:
    1. אם אתם מפעילים סרטון שנמשך זמן רב, כמו סרט, יש להשתמש בסמל MATCH_CONTENT_FRAMERATE_ALWAYS
    2. אם מפעילים סרטון קצר, כמו טריילר, צריך להשתמש בסמל CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
  2. אם הערך של changeFrameRateStrategy הוא CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS , מעבר לשלב 4.
  3. למדו אם מתבצע מעבר חלק של קצב הרענון על ידי בדיקה ששתי העובדות האלה נכונות:
    1. לא ניתן להחליף מצב בצורה חלקה מקצב הרענון הנוכחי (בואו קוראים לזה C) לקצב הפריימים של הסרטון (נקרא לו V). הפעולה הזו תגרור אם C ו-V שונים Display.getMode().getAlternativeRefreshRates לא מכיל כפולה של V.
    2. המשתמש הביע הסכמה לשינויים לא פשוטים בקצב הרענון. תוכלו לזהות כדי לבדוק אם DisplayManager.getMatchContentFrameRateUserPreference החזרות MATCH_CONTENT_FRAMERATE_ALWAYS
  4. אם המעבר יתבצע בצורה חלקה, מבצעים את הפעולות הבאות:
    1. התקשרות אל setFrameRate ולהעביר אותה fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, ו-changeFrameRateStrategy, כאשר fps הוא קצב הפריימים של הסרטון.
    2. התחלת הפעלת הסרטון
  5. אם עומד להתרחש שינוי לא חלק במצב, יש לבצע את הפעולות הבאות:
    1. הצגת UX כדי לשלוח הודעה למשתמש. לתשומת ליבכם: אנחנו ממליצים להטמיע דרך המשתמש יכול לסגור את חוויית המשתמש ולדלג על העיכוב הנוסף בשלב 5. הדבר כיוון שהעיכוב המומלץ שלנו גדול מהנדרש בתצוגות מציינים זמני החלפה מהירים יותר.
    2. התקשרות אל setFrameRate ולהעביר אותה fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, וגם CHANGE_FRAME_RATE_ALWAYS, כאשר fps הוא קצב הפריימים של הסרטון.
    3. יש להמתין ל-onDisplayChanged קריאה חוזרת.
    4. ממתינים 2 שניות עד ששינוי המצב יסתיים.
    5. התחלת הפעלת הסרטון

הקוד המדומה שתומך רק במעבר חלק הוא:

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