מסגרת הסנכרון מתארת באופן מפורש יחסי תלות בין פעולות אסינכרוניות שונות במערכת הגרפיקה של Android. המסגרת מספקת ממשק API שמאפשר לרכיבים לציין מתי מאגרים מתפנים. המסגרת מאפשרת גם להעביר פרימיטיבים של סנכרון בין מנהלי התקנים מהליבה למרחב המשתמש, ובין תהליכים במרחב המשתמש עצמם.
לדוגמה, אפליקציה עשויה להוסיף משימות לתור לביצוע ב-GPU. המעבד הגרפי מתחיל לצייר את התמונה הזו. התמונה עדיין לא נוצרה בזיכרון, אבל מצביע המאגר מועבר למרכיב המרכזי של החלון יחד עם פס שמציין מתי העבודה של ה-GPU תסתיים. רכיב ה-window compositor מתחיל את העיבוד מראש ומעביר את העבודה לבקר המסך. באופן דומה, העבודה של המעבד מתבצעת מראש. אחרי שה-GPU מסיים, בקר התצוגה מציג את התמונה באופן מיידי.
מסגרת הסנכרון מאפשרת למטמיעים גם לנצל משאבי סנכרון ברכיבי החומרה שלהם. לבסוף, המסגרת מספקת שקיפות בצינור עיבוד הנתונים של הגרפיקה כדי לעזור בניפוי באגים.
סנכרון מפורש
סנכרון מפורש מאפשר ליוצרים ולצרכנים של מאגרי גרפיקה לסמן מתי הם סיימו להשתמש במאגר. הסנכרון המפורש מיושם במרחב הליבה.
היתרונות של סנכרון מפורש כוללים:
- פחות תנודות בהתנהגות בין מכשירים
- תמיכה טובה יותר בניפוי באגים
- מדדי בדיקה משופרים
יש שלושה סוגים של אובייקטים במסגרת הסנכרון:
sync_timeline
sync_pt
sync_fence
sync_timeline
sync_timeline
הוא ציר זמן שגדל באופן מונוטוני, שספקים צריכים להטמיע בכל מופע של הנהג, כמו הקשר GL, בקר תצוגה או 2D blitter. sync_timeline
מונה את המשימות שנשלחו לליבה עבור חומרה מסוימת.
sync_timeline
מספק ערבויות לגבי סדר הפעולות ומאפשר הטמעות ספציפיות לחומרה.
כשמטמיעים את sync_timeline
, צריך לפעול לפי ההנחיות הבאות:
- כדי לפשט את ניפוי הבאגים, כדאי לתת שמות שימושיים לכל הגורמים, לוחות הזמנים והגדרות הגדרת הגבולות.
- מטמיעים את האופרטורים
timeline_value_str
ו-pt_value_str
בזמני פעילות כדי להקל על הקריאה של הפלט של ניפוי הבאגים. - אם רוצים, אפשר להטמיע את הפונקציה fill
driver_data
כדי לתת לספריות במרחב המשתמש, כמו ספריית GL, גישה לנתוני ציר הזמן הפרטי.data_driver
מאפשרת לספקים להעביר מידע עלsync_fence
ו-sync_pts
שלא משתנה, כדי ליצור שורות פקודה על סמך המידע הזה. - אסור לאפשר למרחב המשתמש ליצור גדר או לסמן אותה באופן מפורש. יצירת אותות או גדרות באופן מפורש גורמת להתקפת מניעת שירות (DoS) שגורמת להפסקה של הפונקציונליות של צינור עיבוד הנתונים.
- אין לגשת לרכיבים
sync_timeline
,sync_pt
אוsync_fence
באופן מפורש. ה-API מספק את כל הפונקציות הנדרשות.
sync_pt
sync_pt
הוא ערך יחיד או נקודה יחידה ב-sync_timeline
. לנקודה יש שלושה מצבים: פעיל, אות ושיגיאה. הנקודות מתחילות במצב פעיל ומעברות למצבים 'איתור' או 'שגיאה'. לדוגמה, כשצרכן תמונה כבר לא זקוק למאגר, הוא שולח אות sync_pt
כדי ליידע את יוצר התמונה שאפשר לכתוב שוב במאגר.
sync_fence
sync_fence
הוא אוסף של ערכים של sync_pt
, שלרוב יש להם הורים שונים של sync_timeline
(למשל, לצורכי בקרת המסך ולצורכי GPU). sync_fence
, sync_pt
ו-sync_timeline
הם הפרימיטיבים העיקריים שבהם משתמשים מנהלי ההתקנים ומרחב המשתמש כדי להעביר את יחסי התלות שלהם. כשמקבלים אות לגדר, מובטח שכל הפקודות שהופקו לפני הגדר מושלמות, כי מנהל ההתקן של הליבה או בלוק החומרה מבצעים את הפקודות לפי הסדר.
מסגרת הסנכרון מאפשרת למספר צרכנים או יצרנים לסמן שהם סיימו באמצעות מאגר, ולשלוח את פרטי התלות באמצעות פרמטר פונקציה אחד. מחסומי הגנה נתמכים על ידי מתאר קובץ ומועברים ממרחב הליבה למרחב המשתמש. לדוגמה, גדר יכולה להכיל שני ערכים של sync_pt
שמציינים מתי שני צרכני תמונות נפרדים סיימו לקרוא מאגר. כשהמערכת מקבלת אות מהגדר הגבול, יוצרי התמונות יודעים ששני הצרכנים סיימו לצרוך את התמונה.
גדרות, כמו ערכי sync_pt
, מתחילות להיות פעילות ומחליפות את המצב שלהן בהתאם למצב של הנקודות שלהן. אם כל הערכים של sync_pt
יתקבלו אות, sync_fence
יקבל אות. אם אחד מ-sync_pt
נכנס למצב שגיאה, כל ה-sync_fence
נכנס למצב שגיאה.
אי אפשר לשנות את החברות בקבוצה sync_fence
אחרי שיוצרים את גדר. כדי לקבל יותר מנקודה אחת בגדר, מתבצע מיזוג שבו נקודות משתי גדרות נפרדות מתווספות לגדר שלישית.
אם אחת מהנקודות האלה קיבלה אות בגדר המקורית והשנייה לא, גם הגדר השלישית לא תהיה במצב איתות.
כדי להטמיע סנכרון מפורש, צריך לספק את הפרטים הבאים:
- מערכת משנה במרחב הליבה שמטמיעה את מסגרת הסנכרון למנהל חומרה מסוים. מנהלי התקנים שצריכים להיות מודעים למחסום הם בדרך כלל כל מה שיש לו גישה ל-Hardware Composer או מתקשר איתו.
קבצים חשובים כוללים:
- הטמעת הליבה:
kernel/common/include/linux/sync.h
kernel/common/drivers/base/sync.c
- מסמכי העזרה ב-
kernel/common/Documentation/sync.txt
- ספרייה לתקשורת עם מרחב הליבה ב-
platform/system/core/libsync
- הטמעת הליבה:
- הספק צריך לספק את גדרות הסנכרון המתאימות כפרמטרים לפונקציות
validateDisplay()
ו-presentDisplay()
ב-HAL. - שני תוספים של GL שקשורים למחסום (
EGL_ANDROID_native_fence_sync
ו-EGL_ANDROID_wait_sync
) ותמיכה במחסום במנהל הגרפיקה.
מקרה לדוגמה: הטמעת מנהל תצוגה
כדי להשתמש ב-API שתומך בפונקציית הסנכרון, צריך לפתח מנהל תצוגה עם פונקציית מאגר תצוגה. לפני שהמסגרת של הסנכרון הייתה קיימת, הפונקציה הזו הייתה מקבלת אובייקטים מסוג dma-buf
, מעבירה את המאגרים האלה לתצוגה וחוסמת בזמן שהמאגר גלוי. לדוגמה:
/* * assumes buffer is ready to be displayed. returns when buffer is no longer on * screen. */ void display_buffer(struct dma_buf *buffer);
כשמשתמשים במסגרת הסנכרון, הפונקציה display_buffer
מורכבת יותר. כשמציגים מאגר, הוא משויך למחסום שמציין מתי המאגר יהיה מוכן. תוכלו להוסיף את העבודה לתור ולהתחיל אותה אחרי שהיא תהיה זמינה.
הוספה של משימות לתור והפעלת משימות אחרי שהמגבלה מתבטלת לא גורמות לחסימה של שום דבר. אתם מחזירים מיד את הגדרת הגדרת הגבול שלכם, וכך מוודאים מתי המאגר יוצא מהתצוגה. כשמאגרי הנתונים הזמני נכנסים לתור, הליבה מציגה את יחסי התלות במסגרת הסנכרון:
/* * displays buffer when fence is signaled. returns immediately with a fence * that signals when buffer is no longer displayed. */ struct sync_fence* display_buffer(struct dma_buf *buffer, struct sync_fence *fence);
שילוב סנכרון
בקטע הזה מוסבר איך לשלב את מסגרת הסנכרון במרחב הליבה עם החלקים במרחב המשתמש של מסגרת Android והדרייברים שצריכים לתקשר ביניהם. אובייקטים במרחב הליבה מיוצגים כמתארי קבצים במרחב המשתמש.
מוסכמות שילוב
פועלים לפי המוסכמות של ממשק HAL ב-Android:
- אם ה-API מספק מתאר קובץ שמתייחס ל-
sync_pt
, חייבים לסגור את מתאר הקובץ על ידי הנהג של הספק או ה-HAL שמשתמש ב-API. - אם מנהל ההתקן של הספק או ה-HAL מעבירים תיאור קובץ שמכיל
sync_pt
לפונקציית API, מנהל ההתקן של הספק או ה-HAL לא יכולים לסגור את תיאור הקובץ. - כדי להמשיך להשתמש בתיאור הקובץ של המחסום, מנהל ההתקן של הספק או ה-HAL צריכים להעתיק את התיאורים.
שם של אובייקט גדר משתנה בכל פעם שהוא עובר דרך BufferQueue.
התמיכה במחסום הליבה מאפשרת למחסומים לכלול מחרוזות לשמות, כך שמסגרת הסנכרון משתמשת בשם החלון ובאינדקס המאגר שנמצא בתור כדי לתת שם למחסום, למשל SurfaceView:0
. זה עוזר לנפות באגים ולזהות את המקור של נעילה מרומזת, כי השמות מופיעים בפלט של /d/sync
ובדוחות באגים.
שילוב עם ANativeWindow
ANativeWindow מודע למחיצות. ל-dequeueBuffer
, queueBuffer
ו-cancelBuffer
יש פרמטרים של גדרות.
שילוב עם OpenGL ES
השילוב של OpenGL ES לסנכרון מסתמך על שני תוספי EGL:
EGL_ANDROID_native_fence_sync
מספק דרך לעטוף או ליצור מתארי קבצים של גדרות מקומיות של Android באובייקטים שלEGLSyncKHR
.EGL_ANDROID_wait_sync
מאפשרת עיכובים בצד ה-GPU במקום בצד המעבד, כך שה-GPU ממתין ל-EGLSyncKHR
. התוסףEGL_ANDROID_wait_sync
זהה לתוסףEGL_KHR_wait_sync
.
כדי להשתמש בתוספים האלה בנפרד, צריך להטמיע את התוסף EGL_ANDROID_native_fence_sync
יחד עם תמיכת הליבה המשויכת. בשלב הבא, מפעילים את התוסף EGL_ANDROID_wait_sync
ב-driver. התוסף EGL_ANDROID_native_fence_sync
מורכב מסוג אובייקט EGLSyncKHR
נפרד של גדר מקומית. כתוצאה מכך, תוספים שחלים על סוגי אובייקטים קיימים מסוג EGLSyncKHR
לא חלים בהכרח על אובייקטים מסוג EGL_ANDROID_native_fence
, וכך נמנעות אינטראקציות לא רצויות.
סיומת EGL_ANDROID_native_fence_sync
משתמשת במאפיין מתאים של מתאר קובץ גדר מקומי, שאפשר להגדיר רק בזמן היצירה, ולא ניתן לשלוח שאילתות ישירות ממנו לאובייקט סנכרון קיים. אפשר להגדיר את המאפיין הזה לאחד משני המצבים הבאים:
- מתאר קובץ חומה תקין עוטף מתאר קובץ חומה מקורי של Android באובייקט
EGLSyncKHR
. - הערך -1 יוצר מתאר קובץ של גדר מקורית ב-Android מאובייקט
EGLSyncKHR
.
משתמשים בקריאה לפונקציה DupNativeFenceFD()
כדי לחלץ את האובייקט EGLSyncKHR
מתיאורי הקובץ של הגדרת הגדרת הגבול המובנית של Android.
התוצאה זהה לשאילתה על המאפיין שהוגדר, אבל היא תואמת למוסכמה שלפיה הנמען סוגר את הגדרת ההרשאות (לכן הפעולה כפולה). לבסוף, השמדת האובייקט EGLSyncKHR
סוגרת את מאפיין הגדרת הגבולות הפנימי.
שילוב של Hardware Composer
ה-Hardware Composer מטפל בשלושה סוגים של גדרות סנכרון:
- Acquire fences מועברים יחד עם מאגרי הקלט לשיחות
setLayerBuffer
ו-setClientTarget
. הם מייצגים כתיבה בהמתנה למאגר, וצריך לסמן אותם לפני ש-SurfaceFlinger או ה-HWC מנסים לקרוא מהמאגר המשויך כדי לבצע קומפוזיציה. - גדרות שחרור מאוחזרות אחרי הקריאה ל-
presentDisplay
באמצעות הקריאהgetReleaseFences
. אלה מייצגים קריאה בהמתנה מהמאגר הקודם באותה שכבה. release fence מאותת כאשר ה-HWC כבר לא משתמש במאגר הקודם כי המאגר הנוכחי החליף את המאגר הקודם בתצוגה. גדרות השחרור מועברות חזרה לאפליקציה יחד עם מאגרי הנתונים הקודמים, שיוחלפו במהלך היצירה הנוכחית. האפליקציה צריכה להמתין עד שתקבל אות מ-release fence כדי לכתוב תוכן חדש במאגר שקיבלתם בחזרה. - Present fences מוחזרים, אחד לכל פריים, כחלק מהקריאה ל-
presentDisplay
. גדרות נוכחיות מייצגות את הזמן שבו ההרכבה של הפריים הזה הושלמה, או את הזמן שבו לא נדרשת יותר התוצאה של ההרכבה של הפריים הקודם. במסכים פיזיים, הפונקציהpresentDisplay
מחזירה את המחיצות הנוכחיות כשהפריים הנוכחי מופיע במסך. אחרי שהגדרות הגדרת המחסום הקיימות מוחזרות, אפשר לכתוב שוב במאגר היעד של SurfaceFlinger, אם הדבר רלוונטי. במסכים וירטואליים, גדרות נוכחיות מוחזרות כשבטוח לקרוא מאגר הפלט.