מסגרת הסנכרון מתארת באופן מפורש את התלות בין פעולות אסינכרוניות שונות במערכת הגרפיקה של Android. המסגרת מספקת API שמאפשר לרכיבים לציין מתי המאגרים משוחררים. בנוסף, המסגרת מאפשרת להעביר פרימיטיבים של סנכרון בין מנהלי התקנים מהליבה למרחב המשתמש, ובין תהליכים במרחב המשתמש עצמו.
לדוגמה, אפליקציה יכולה להוסיף עבודה לתור לביצוע ב-GPU. ה-GPU מתחיל לצייר את התמונה. למרות שהתמונה עדיין לא נשמרה בזיכרון, מצביע המאגר מועבר למרכיב החלון יחד עם גדר שמציינת מתי פעולת ה-GPU תסתיים. הכלי להרכבת חלונות מתחיל לעבד מראש ומעביר את העבודה לבקר התצוגה. באופן דומה, עבודת המעבד מתבצעת מראש. אחרי שה-GPU מסיים, בקר התצוגה מציג את התמונה באופן מיידי.
בנוסף, מסגרת הסנכרון מאפשרת למיישמים להשתמש במשאבי סנכרון ברכיבי החומרה שלהם. לבסוף, המסגרת מספקת תצוגה של צינור הגרפיקה כדי לעזור בניפוי באגים.
סנכרון מפורש
סנכרון מפורש מאפשר ליצרנים ולצרכנים של מאגרי גרפיקה לסמן מתי הם סיימו להשתמש במאגר. סנכרון מפורש מיושם במרחב הליבה.
היתרונות של סנכרון מפורש:
- פחות הבדלים בהתנהגות בין מכשירים
- תמיכה טובה יותר בניפוי באגים
- מדדי בדיקה משופרים
למסגרת הסנכרון יש שלושה סוגי אובייקטים:
sync_timelinesync_ptsync_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. כדי לקבל יותר מנקודה אחת בגדר, מתבצע מיזוג שבו נקודות משתי גדרות נפרדות מתווספות לגדר שלישית.
אם אחת מהנקודות האלה סומנה בגדר המקורית והשנייה לא, גם הגדר השלישית לא תהיה במצב מסומן.
כדי להטמיע סנכרון מפורש, צריך לספק את הפרטים הבאים:
- מערכת משנה במרחב הליבה שמטמיעה את מסגרת הסנכרון עבור מנהל התקן ספציפי של חומרה. בדרך כלל, דרייברים שצריכים להיות מודעים לגדר הם כל דרייבר שניגש ל-Hardware Composer (HWC) או מתקשר איתו.
קבצים חשובים:
- הטמעה בסיסית:
kernel/common/include/linux/sync.hkernel/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
ה-HWC מטפל בשלושה סוגים של גדרות סנכרון:
- הפעולות Acquire fences מועברות יחד עם מאגרי קלט לקריאות
setLayerBufferו-setClientTarget. הם מייצגים פעולת כתיבה בהמתנה למאגר, וצריכים לסמן לפני ש-SurfaceFlinger או HWC מנסים לקרוא מהמאגר המשויך כדי לבצע קומפוזיציה. - גדרות שחרור מאוחזרות אחרי השיחה אל
presentDisplayבאמצעות השיחהgetReleaseFences. הם מייצגים קריאה בהמתנה מהמאגר הקודם באותה שכבה. מחסום שחרור מסמן מתי בקר ה-HWC כבר לא משתמש במאגר הקודם כי המאגר הנוכחי החליף את המאגר הקודם בתצוגה. גדרות השחרור מועברות בחזרה לאפליקציה יחד עם המאגרים הקודמים שיוחלפו במהלך ההרכבה הנוכחית. האפליקציה צריכה להמתין עד שגדר השחרור תאותת לפני שהיא כותבת תוכן חדש למאגר שהוחזר אליה. - גדרות נוכחיות מוחזרות, אחת לכל פריים, כחלק מהקריאה ל-
presentDisplay. הגדרת הגדרות גידור נוכחיות מייצגת את השלמת ההרכבה של הפריים הזה, או לחלופין, את הרגע שבו כבר לא צריך את תוצאת ההרכבה של הפריים הקודם. במסכים פיזיים, הפונקציהpresentDisplayמחזירה גדרות נוכחיות כשהפריים הנוכחי מופיע על המסך. אחרי שהגדרות הגידור הנוכחיות מוחזרות, אפשר לכתוב שוב למאגר היעד של SurfaceFlinger, אם רלוונטי. במקרה של תצוגות וירטואליות, המערכת מחזירה גדרות נוכחיות כשאפשר לקרוא מהמאגר של הפלט.