ספרייה של קצב פריימים חלק מ-Android Game Development Kit.
הספרייה של Android Frame Pacing, שנקראת גם Swappy, היא חלק מספריות AGDK. זה עוזר לאפשר עיבוד חלק במשחקי OpenGL ו-Vulkan קצב הפריימים הנכון ב-Android. המסמך הזה מגדיר את קצב הפריימים, מתאר במצבים שבהם יש צורך בקצב הפריימים, ומראה איך הספרייה במצבים האלה. אם רוצים לעבור ישירות להטמעת קצב הפריימים במשחק שלכם, ראו השלב הבא.
רקע
קצב פריימים הוא סנכרון הלוגיקה ולולאת הרינדור של המשחק מערכת משנה לתצוגה של מערכת הפעלה וחומרת המסך הבסיסית. מכשיר Android מערכת המשנה לתצוגה תוכננה למנוע פגמים חזותיים (שנקראים קריעה) שעלולות להתרחש כאשר חומרת המסך עוברת למסגרת חדשה באופן חלקי באמצעות עדכון. כדי להימנע מפריטי מידע שנוצרו בתהליך הפיתוח (Artifact) כאלה, מערכת המשנה של התצוגה הבאים:
- אחסון זמני בפריימים קודמים באופן פנימי
- זיהוי סרטונים שנשלחו באיחור
- חזרה על התצוגה של פריימים קודמים כאשר מזוהים פריימים מאוחרים
משחק מודיעין
SurfaceFlinger,
המרכיב במערכת המשנה של התצוגה, שהוא שלח את כל
נדרשות שיחות למסגרת (באמצעות קריאה ל-eglSwapBuffers
או vkQueuePresentKHR
).
SurfaceFlinger מאותת את הזמינות של מסגרת לחומרת המסך באמצעות
בריח. לאחר מכן חומרת המסך מציגה את המסגרת הנתונה. חומרת המסך
קצב קבוע, לדוגמה 60Hz, ואם אין פריים חדש
בחומרה יש צורך בחומרה, היא תציג שוב את הפריים הקודם.
זמני רינדור לא עקביים מתרחשים בדרך כלל כשמתבצע רינדור של לולאת רינדור במשחק קצב שונה מזה של חומרת המסך המקורית. אם משחק פועל בקצב של 30FPS שמנסה לעבד רינדור במכשיר שתומך במקור של 60 FPS, תהליך העיבוד של המשחק לולאה לא מבינה שמסגרת חוזרת נשארת במסך למשך 16 תווים נוספים אלפיות שנייה. הניתוק הזה יוצר בדרך כלל חוסר עקביות משמעותי במסגרת למשל: 49 אלפיות שנייה, 16 אלפיות שנייה, 33 אלפיות שנייה. יותר מדי סצנות מורכבות יותר מורכבות מבעיה זו, מכיוון שהן גורמות להחמצת פריימים יתרחשו.
פתרונות לא אופטימליים
הפתרונות הבאים לקצב פריימים שימשו בעבר במשחקים ובדרך כלל גורמים לזמני רינדור לא עקביים ולזמן אחזור ארוך יותר של הקלט.
שליחת פריימים במהירות שבה מאפשר ה-API לרינדור
הגישה הזו קושרת בין משחק לפעילות משתנה של SurfaceFlinger, ומאפשרת מסגרת נוספת של זמן אחזור. צינור עיבוד הנתונים לתצוגה מכיל תור של מסגרות, בדרך כלל בגודל 2, שמתמלא אם המשחק מנסה גם להציג פריימים במהירות. כשאין יותר מקום בתור, לולאת המשחק (או לפחות שרשור רינדור) חסום על ידי קריאת OpenGL או Vulkan. לאחר מכן המשחק נאלץ לחכות עד שחומרת המסך תציג פריים, מסנכרנת בין שני הרכיבים. המצב הזה נקרא דחיסת נתונים במאגר הנתונים הזמני, מילוי תור. תהליך כלי הרינדור לא מבין מה קורה, ולכן חוסר העקביות של קצב הפריימים גובר. אם של דגימות משחק לפני הפריים, זמן האחזור של הקלט מצטמצם יותר.
שימוש בכוריאוגרף של Android בלבד
המשחקים משתמשים גם בכוריאוגרף של Android לסנכרון. ברכיב הזה, זמין ב-Java מ-API 16 וב-C++ מ-API 24, מספק סימונים קבועים אותה תדירות כמו במערכת המשנה של התצוגה. עדיין יש דקויות כשהסימון הזה נשלח ביחס לחומרה של VSYNC בפועל, ההיסטים משתנים בהתאם למכשיר. דחיסת נתונים במאגר הנתונים הזמני עשויה עדיין להתבצע בפריימים ארוכים.
היתרונות של ספריית קצב הפריימים
הספרייה של קצב הפריימים משתמשת בכוריאוגרף של Android לסנכרון מתייחסת לשונות באספקת הסימונים עבורך. היא משתמשת במצגת חותמות זמן כדי לוודא שהפריימים מוצגים בזמן הנכון וסנכרון גדרות כדי למנוע דחיסת נתונים במאגר הנתונים הזמני. הספרייה משתמשת כוריאוגרף של NDK, אם הוא זמין חוזר אל הכוריאוגרף של Java אם אבל היא לא.
הספרייה מטפלת במספר קצבי רענון אם המכשיר תומך בהם, שמעניק למשחק יותר גמישות בהצגת פריים. לדוגמה, עבור מכשיר שתומך בקצב רענון של 60Hz וגם ב-90Hz, משחק שלא יכול רזולוציה של 60 פריימים לשנייה עשויה לרדת ל-45 FPS במקום ל-30 FPS כדי להישאר חלק. הספרייה מזהה את קצב הפריימים הצפוי של המשחק ומתאימה אוטומטית את הפריים בהתאם לזמני ההצגה. הספרייה של קצב הפריימים גם משפרת את הסוללה חיים מפני שהיא מונעת עדכוני תצוגה מיותרים. לדוגמה, אם משחק רינדור ב-60 FPS אבל המסך מתעדכן ב-120Hz, עודכנו פעמיים לכל פריים. כדי למנוע זאת, הספרייה 'קצב פריימים' יכולה למנוע זאת על ידי הגדרה קצב הרענון לערך שנתמך על ידי המכשיר שהכי קרוב ליעד קצב הפריימים
איך זה עובד
הקטעים הבאים מראים איך הספרייה 'קצב פריימים' מתמודדת עם פריימים של משחק קצר כדי להגיע לקצב הפריימים הנכון.
קצב פריימים נכון של 30 Hz
ברינדור של 30Hz במכשיר של 60Hz, המצב האידיאלי ב-Android הוא שמוצגת באיור 1. SurfaceFlinger מחבר מאגרי נתונים גרפיים חדשים, אם יש כאלה (NB ב- בתרשים מצוין 'ללא מאגר נתונים זמני' הנוכחית וקודמת).
איור 1. קצב פריימים אידיאלי של 30Hz במכשיר של 60Hz
פריימים קצרים במשחק מובילים לגמגם
ברוב המכשירים המודרניים, מנועי המשחקים מסתמכים על הכוריאוגרף של הפלטפורמה באמצעות סימונים שיעודדו את שליחת הפריימים. עם זאת, עדיין יש האפשרות לקצב פריימים נמוך בגלל פריימים קצרים, כפי שניתן לראות באיור 2. פריימים קצרים ולאחר מכן פריימים ארוכים נתפסים על ידי הנגן כגמגם.
איור 2. מסגרת המשחק הקצרה C גורמת למסגרת B להציג רק פריים אחד, ולאחר מכן כמה מסגרות C
הספרייה 'קצב פריימים' פותרת את הבעיה באמצעות חותמות זמן של מצגות.
משתמשת בתוספי חותמת הזמן של המצגת
EGL_ANDROID_presentation_time
וגם
VK_GOOGLE_display_timing
כך שהפריימים לא יוצגו מוקדם, כפי שמתואר באיור 3.
איור 3. מסגרת B של משחק מוצגת פעמיים לתצוגה חלקה יותר
פריימים ארוכים גורמים לגירוי ולזמן אחזור
כשעומס העבודה ברשת המדיה נמשך יותר זמן מעומס העבודה של האפליקציה, המסגרות נוספות לתור. הדבר מוביל שוב לסימפטומים, להוביל למסגרת נוספת של זמן אחזור כתוצאה מדחיסת מאגר הנתונים הזמני (ראו איור 4). הן מספקות גם את הגמגם וגם את הפריים הנוסף של זמן האחזור.
איור 4. מסגרת ארוכה B מעידה על קצב שגוי ל-2 פריימים – A ו-B
הספרייה פותרת את הבעיה באמצעות גדרות סנכרון
(EGL_KHR_fence_sync
וגם
VkFence
)
להחדיר דרכי המתנה לאפליקציה שמאפשרת לצינור עיבוד הנתונים לתצוגה לקלוט
במקום לאפשר לחץ להיווצר. מסגרת A עדיין מציגה
מסגרת נוספת, אך מסגרת B מוצגת כעת בצורה נכונה, כפי שנראה באיור 5.
איור 5. המתנה להצגה במסגרות C ו-D
מצבי הפעלה נתמכים
ניתן להגדיר את הספרייה 'קצב פריימים' לפעול באחת משלושת המצבים הבאים:
- מצב אוטומטי מושבת + צינור עיבוד נתונים
- מצב אוטומטי מופעל + צינור עיבוד נתונים
- מצב אוטומטי מופעל + מצב צינור עיבוד נתונים אוטומטי (צינור עיבוד נתונים/לא צינור עיבוד נתונים)
מצב מומלץ
תוכלו להתנסות במצב אוטומטי ובמצב צינור עיבוד נתונים, אבל קודם כל צריך להשבית אותם ולכלול את הרכיבים הבאים לאחר אתחול ההחלפה:
swappyAutoSwapInterval(false);
swappyAutoPipelineMode(false);
swappyEnableStats(false);
swappySwapIntervalNS(1000000000L/yourPreferredFrameRateInHz);
מצב צינור עיבוד נתונים
כדי לתאם את עומסי העבודה של המנועים, הספרייה בדרך כלל משתמשת במודל צינור עיבוד נתונים שמפריד בין עומסי העבודה של המעבד (CPU) וה-GPU בין גבולות VSYNC.
איור 6. מצב צינור עיבוד נתונים
מצב ללא צינור עיבוד נתונים
באופן כללי, הגישה הזו מובילה למסך קלט נמוך וצפוי יותר זמן אחזור. במקרים שבהם זמן רינדור הפריים של המשחק קצר מאוד, גם המעבד (CPU) וגם ה-GPU עומסי עבודה יכולים להתאים למרווח החלפה אחד. במקרה הזה, תהיה למעשה זמן אחזור קצר יותר של מסך הקלט.
איור 7. מצב ללא צינור עיבוד נתונים
מצב אוטומטי
ברוב המשחקים אין אפשרות לבחור את מרווח ההחלפה, שהוא משך הזמן של שכל פריים מוצג (לדוגמה, 33.3 אלפיות השנייה ל-30Hz). במכשירים מסוימים, רינדור של משחק יכול להיות במהירות של 60 FPS, ואילו במשחק אחר הוא יצטרך לרדת לרמה נמוכה יותר עם ערך מסוים. במצב אוטומטי מודדים את זמני המעבד (CPU) וה-GPU כדי לבצע את הפעולות הבאות:
- בחירה אוטומטית של מרווחי החלפה: משחקים עם מהירות של 30Hz סצנות ו- 60 Hz אחרים יכולים לאפשר לספרייה לשנות את המרווח הזה באופן דינמי.
- השבתת צינור עיבוד נתונים לפריימים מהירים במיוחד: הצגה אופטימלית זמן אחזור של מסך הקלט בכל המקרים.
קצבי רענון מרובים
מכשירים שתומכים במספר קצבי רענון מספקים יותר גמישות כדי לבחור מרווח החלפה שנראה חלק:
- במכשירי 60 FPS: 60 FPS / 30 FPS / 20 FPS
- במכשירים בקצב 60 FPS + 90 FPS: 90 FPS / 60 FPS / 45 FPS / 30 FPS
- במכשירים שתומכים ב- 60 Hz + 90 Hz + 120 Hz: 120FPS / 90 FPS / 60 FPS / 45 FPS / 40 FPS / 30 FPS
הספרייה בוחרת את קצב הרענון שהכי מתאים לעיבוד בפועל את משך הפריימים של המשחק, וכך מעניקה חוויה חזותית טובה יותר.
למידע נוסף על קצב רענון מרובים של פריימים, אפשר לעיין ב רינדור קצב רענון גבוה ב-Android לפוסט בבלוג.
נתונים סטטיסטיים של מסגרת
הספרייה 'קצב פריימים' מציעה את הנתונים הסטטיסטיים הבאים לניפוי באגים וגם למטרות הפרופיילינג:
- היסטוגרמה של מספר המסך מרעננת מסגרת שהמתנתה אחרי שהעיבוד הושלם.
- היסטוגרמה של מספר רענון המסך שהועברו בין את השעה שבה צריך להציג את המצגת ואת הזמן הנוכחי בפועל.
- היסטוגרמה של מספר רענון המסך שהועברה בין שני רענון פריימים.
- היסטוגרמה של מספר רענון המסך שהועברו בין תחילת המעבד (CPU) במסגרת הזו והשעה הנוכחית בפועל.
השלב הבא
אפשר לעיין באחד מהמדריכים הבאים לשילוב הספרייה של Android Frame Pacing למשחק שלך: