מסגרת הסנכרון מתארת באופן מפורש את התלות בין פעולות אסינכרוניות שונות במערכת הגרפיקה של Android. המסגרת מספקת API שמאפשר לרכיבים לציין מתי המאגרים משוחררים. בנוסף, המסגרת מאפשרת להעביר פרימיטיבים של סנכרון בין מנהלי התקנים מהליבה למרחב המשתמש, ובין תהליכים במרחב המשתמש עצמו.
לדוגמה, אפליקציה יכולה להוסיף עבודה לתור לביצוע ב-GPU. ה-GPU מתחיל לצייר את התמונה. למרות שהתמונה עדיין לא נשמרה בזיכרון, מצביע המאגר מועבר למרכיב החלון יחד עם גדר שמציינת מתי פעולת ה-GPU תסתיים. הכלי להרכבת חלונות מתחיל לעבד מראש ומעביר את העבודה לבקר התצוגה. באופן דומה, עבודת המעבד מתבצעת מראש. אחרי שה-GPU מסיים, בקר התצוגה מציג את התמונה באופן מיידי.
מסגרת הסנכרון מאפשרת גם למטמיעים להשתמש במשאבי סנכרון ברכיבי החומרה שלהם. לבסוף, המסגרת מספקת תצוגה של צינור הגרפיקה כדי לעזור בניפוי באגים.
סנכרון מפורש
סנכרון מפורש מאפשר ליצרנים ולצרכנים של מאגרי גרפיקה לסמן מתי הם סיימו להשתמש במאגר. סנכרון מפורש מיושם במרחב הליבה.
היתרונות של סנכרון מפורש:
- פחות הבדלים בהתנהגות בין מכשירים
- תמיכה טובה יותר בניפוי באגים
- מדדי בדיקה משופרים
למסגרת הסנכרון יש שלושה סוגי אובייקטים:
sync_timeline
sync_pt
sync_fence
sync_timeline
sync_timeline
הוא ציר זמן שגדל באופן מונוטוני, וספקים צריכים להטמיע אותו לכל מופע של מנהל התקן, כמו הקשר GL, בקר תצוגה או blitter דו-ממדי. sync_timeline
סופר את המשימות שנשלחו לליבה עבור רכיב חומרה מסוים.
sync_timeline
מספקת ערבויות לגבי סדר הפעולות ומאפשרת הטמעות ספציפיות לחומרה.
כשמטמיעים את sync_timeline
, חשוב לפעול לפי ההנחיות הבאות:
- כדי לפשט את ניפוי הבאגים, כדאי לתת שמות שימושיים לכל הדרייברים, ציר הזמן והגדרות הגידור הגיאוגרפי.
- כדי שהפלט של ניפוי הבאגים יהיה קריא יותר, כדאי להשתמש באופרטורים
timeline_value_str
ו-pt_value_str
בציר הזמן. - אם רוצים, אפשר להטמיע את המילוי
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
הם הפרימיטיבים העיקריים שמשמשים מנהלי התקנים ומרחבי משתמשים כדי להעביר את התלות שלהם. כשמגודר אות, כל הפקודות שהונפקו לפני הגידור מובטחות להשלמה, כי מנהל ההתקן של ליבת המערכת או חסימת החומרה מבצעים את הפקודות לפי הסדר.
מסגרת הסנכרון מאפשרת לכמה צרכנים או יצרנים לסמן מתי הם סיימו להשתמש במאגר, ולתקשר את פרטי התלות באמצעות פרמטר אחד של פונקציה. הגדרות ה-Fence מגובות על ידי מתאר קובץ והן מועברות ממרחב הליבה למרחב המשתמש. לדוגמה, גדר יכולה להכיל שני ערכים של sync_pt
שמציינים מתי שני צרכני תמונה נפרדים סיימו לקרוא מאגר. כשמצוין הגדר, יוצרי התמונות יודעים ששני הצרכנים סיימו את הצריכה.
גדרות, כמו ערכי sync_pt
, מתחילות במצב פעיל ומשנות את הסטטוס שלהן בהתאם לסטטוס של הנקודות שלהן. אם כל הערכים של sync_pt
מסומנים, הערך של sync_fence
מסומן. אם אחד מהרכיבים sync_pt
עובר למצב שגיאה, כל הרכיב sync_fence
עובר למצב שגיאה.
אחרי שיוצרים את הגדר, אי אפשר לשנות את החברות ב-sync_fence
. כדי לקבל יותר מנקודה אחת בגדר, מתבצע מיזוג שבו נקודות משתי גדרות נפרדות מתווספות לגדר שלישית.
אם אחת מהנקודות האלה סומנה בגדר המקורית והשנייה לא, גם הגדר השלישית לא תהיה במצב מסומן.
כדי להטמיע סנכרון מפורש, צריך לספק את הפרטים הבאים:
- מערכת משנה במרחב הליבה שמטמיעה את מסגרת הסנכרון עבור מנהל התקן חומרה מסוים. מנהלי ההתקנים שצריכים להיות מודעים לגדר הם בדרך כלל כל דבר שיש לו גישה למחבר החומרה או שהוא מתקשר איתו.
קבצים חשובים:
- הטמעה בסיסית:
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 שתומך בפונקציית הסנכרון, צריך לפתח מנהל התקן של צג עם פונקציית מאגר תצוגה. לפני שנוצר framework הסנכרון, הפונקציה הזו קיבלה אובייקטים של 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 ועם מנהלי ההתקנים שצריכים לתקשר זה עם זה. אובייקטים במרחב הליבה מיוצגים כמתארי קבצים במרחב המשתמש.
מוסכמות שילוב
פועלים לפי המוסכמות של ממשק Android HAL:
- אם ה-API מספק מתאר קובץ שמפנה אל
sync_pt
, מנהל ההתקן של הספק או ה-HAL שמשתמשים ב-API חייבים לסגור את מתאר הקובץ. - אם מנהל ההתקן של הספק או HAL מעבירים מתאר קובץ שמכיל
sync_pt
לפונקציית API, מנהל ההתקן של הספק או HAL לא יכולים לסגור את מתאר הקובץ. - כדי להמשיך להשתמש בתיאור הקובץ של הגדר הגבול, מנהל ההתקן של הספק או ה-HAL צריכים לשכפל את התיאור.
שם האובייקט של ה-fence משתנה בכל פעם שהוא עובר דרך BufferQueue.
תמיכה ב-Kernel fence מאפשרת להוסיף מחרוזות לשמות של fences, כך שמסגרת הסנכרון משתמשת בשם החלון ובאינדקס של המאגר שנמצא בתור כדי לתת שם ל-fence, כמו 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 ולא בצד ה-CPU, כך שה-GPU ממתין ל-EGLSyncKHR
. התוסףEGL_ANDROID_wait_sync
זהה לתוסףEGL_KHR_wait_sync
.
כדי להשתמש בתוספים האלה בנפרד, צריך להטמיע את התוסף EGL_ANDROID_native_fence_sync
יחד עם תמיכת הליבה המשויכת. לאחר מכן, מפעילים את התוסף EGL_ANDROID_wait_sync
בדרייבר. התוסף EGL_ANDROID_native_fence_sync
מורכב מסוג אובייקט נפרד של גדר מקומית EGLSyncKHR
. כתוצאה מכך, תוספים שחלים על סוגי אובייקטים קיימים של EGLSyncKHR
לא חלים בהכרח על אובייקטים של EGL_ANDROID_native_fence
, וכך נמנעות אינטראקציות לא רצויות.
התוסף EGL_ANDROID_native_fence_sync
משתמש במאפיין תיאור קובץ גדר מקורי תואם שאפשר להגדיר רק בזמן היצירה, ואי אפשר לבצע שאילתה ישירות ממנו מאובייקט סנכרון קיים. אפשר להגדיר את המאפיין הזה באחד משני מצבים:
- מתאר תקין של קובץ גדר וירטואלית עוטף מתאר קיים של קובץ גדר וירטואלית מקורי של Android באובייקט
EGLSyncKHR
. - -1 יוצר מתאר קובץ של גדר מקורית ב-Android מאובייקט
EGLSyncKHR
.
משתמשים בקריאה לפונקציה DupNativeFenceFD()
כדי לחלץ את האובייקט EGLSyncKHR
ממתאר הקובץ של הגדר הגאוגרפית המקורית של Android.
התוצאה זהה לתוצאה של שאילתת המאפיין set, אבל היא תואמת למוסכמה שלפיה הנמען סוגר את הגדר (ומכאן הפעולה הכפולה). לבסוף, השמדת האובייקט EGLSyncKHR
סוגרת את מאפיין הגדר הגבולות הפנימי.
אינטגרציה של Hardware Composer
ה-Hardware Composer מטפל בשלושה סוגים של גדרות סנכרון:
- הפעולות Acquire fences מועברות יחד עם מאגרי קלט לקריאות
setLayerBuffer
ו-setClientTarget
. הם מייצגים פעולת כתיבה בהמתנה למאגר, וצריכים לסמן לפני ש-SurfaceFlinger או HWC מנסים לקרוא מהמאגר המשויך כדי לבצע קומפוזיציה. - גדרות שחרור מאוחזרות אחרי השיחה אל
presentDisplay
באמצעות השיחהgetReleaseFences
. הם מייצגים קריאה בהמתנה מהמאגר הקודם באותה שכבה. מחסום שחרור מסמן מתי בקר ה-HWC כבר לא משתמש במאגר הקודם כי המאגר הנוכחי החליף את המאגר הקודם בתצוגה. גדרות השחרור מועברות בחזרה לאפליקציה יחד עם המאגרים הקודמים שיוחלפו במהלך ההרכבה הנוכחית. האפליקציה צריכה להמתין עד שגדר השחרור תאותת לפני שהיא כותבת תוכן חדש למאגר שהוחזר אליה. - גדרות נוכחיות מוחזרות, אחת לכל פריים, כחלק מהקריאה אל
presentDisplay
. הגדרת הגדרות גידור נוכחיות מייצגת את השלמת ההרכבה של הפריים הזה, או לחלופין, את הרגע שבו כבר לא צריך את תוצאת ההרכבה של הפריים הקודם. במסכים פיזיים, הפונקציהpresentDisplay
מחזירה גדרות נוכחיות כשהפריים הנוכחי מופיע על המסך. אחרי שהגדרות ה-fence הנוכחיות מוחזרות, אפשר לכתוב שוב למאגר היעד של SurfaceFlinger, אם רלוונטי. במקרה של תצוגות וירטואליות, גדרות נוכחיות מוחזרות כשבטוח לקרוא ממאגר הפלט.