מסגרת סנכרון

מסגרת הסנכרון מתארת באופן מפורש את התלות בין פעולות אסינכרוניות שונות במערכת הגרפיקה של 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, אם רלוונטי. במקרה של תצוגות וירטואליות, גדרות נוכחיות מוחזרות כשבטוח לקרוא ממאגר הפלט.