מסגרת הסנכרון מתארת באופן מפורש את התלות בין פעולות אסינכרוניות שונות במערכת הגרפיקה של Android. המסגרת מספקת API שמאפשר לרכיבים לציין מתי המאגרים משוחררים. בנוסף, המסגרת מאפשרת להעביר פרימיטיבים של סנכרון בין מנהלי התקנים מהליבה למרחב המשתמש, ובין תהליכים במרחב המשתמש עצמו.
לדוגמה, אפליקציה יכולה להוסיף עבודה לתור לביצוע ב-GPU. ה-GPU מתחיל לצייר את התמונה. למרות שהתמונה עדיין לא נשמרה בזיכרון, מצביע המאגר מועבר למרכיב של חלון המערכת יחד עם גדר שמציינת מתי פעולת ה-GPU תסתיים. ה-compositor של החלון מתחיל לעבד מראש ומעביר את העבודה לבקר התצוגה. באופן דומה, עבודת המעבד מתבצעת מראש. אחרי שה-GPU מסיים, בקר התצוגה מציג את התמונה באופן מיידי.
בנוסף, ה-framework של הסנכרון מאפשר למיישמים להשתמש במשאבי סנכרון ברכיבי החומרה שלהם. לבסוף, המסגרת מספקת תצוגה של צינור הגרפיקה כדי לעזור בניפוי באגים.
סנכרון מפורש
סנכרון מפורש מאפשר ליצרנים ולצרכנים של מאגרי גרפיקה לאותת כשהם מסיימים להשתמש במאגר. סנכרון מפורש מיושם במרחב הליבה.
היתרונות של סנכרון מפורש כוללים:
- פחות שינויים בהתנהגות בין מכשירים
- תמיכה טובה יותר בניפוי באגים
- מדדי בדיקה משופרים
למסגרת הסנכרון יש שלושה סוגי אובייקטים:
sync_timelinesync_ptsync_fence
sync_timeline
sync_timeline הוא ציר זמן שגדל באופן מונוטוני וספקים צריכים להטמיע אותו עבור כל מופע של מנהל התקן, כמו הקשר GL, בקר תצוגה או blitter דו-ממדי. sync_timeline counts
jobs submitted to the kernel for a particular piece of hardware.
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. הם מייצגים קריאה בהמתנה מהמאגר הקודם באותה שכבה. מחסום שחרור (release fence) מסמן מתי ה-HWC כבר לא משתמש במאגר הקודם כי המאגר הנוכחי החליף את המאגר הקודם בתצוגה. גדרות השחרור מועברות בחזרה לאפליקציה יחד עם המאגרים הקודמים שיוחלפו במהלך ההרכבה הנוכחית. האפליקציה צריכה להמתין עד שגדר השחרור מסמנת לפני שהיא כותבת תוכן חדש לתוך המאגר שהוחזר אליה. - גדרות נוכחיות מוחזרות, אחת לכל פריים, כחלק מהקריאה ל-
presentDisplay. הגדרת גדרות מייצגת את השלמת ההרכבה של הפריים הזה, או לחלופין, את הרגע שבו כבר לא צריך את תוצאת ההרכבה של הפריים הקודם. במסכים פיזיים,presentDisplayמחזירה גדרות נוכחיות כשהפריים הנוכחי מופיע על המסך. אחרי שהגדרות ה-fence הנוכחיות מוחזרות, אפשר לכתוב שוב למאגר היעד של SurfaceFlinger, אם רלוונטי. במקרה של תצוגות וירטואליות, גדרות נוכחיות מוחזרות כשבטוח לקרוא מהמאגר של הפלט.