ה-HAL של Hardware Composer (HWC) מרכיב שכבות שהתקבלו מ-SurfaceFlinger, ומפחית את כמות ההרכבה ש-OpenGL ES (GLES) וה-GPU מבצעים.
ה-HWC מבצע הפשטה של אובייקטים, כמו שכבות-על ו-2D blitters, כדי ליצור משטחים מורכבים, ומתקשר עם חומרה מיוחדת להרכבת חלונות כדי ליצור חלונות מורכבים. משתמשים ב-HWC כדי ליצור קומפוזיציה של חלונות במקום ליצור קומפוזיציה של SurfaceFlinger באמצעות ה-GPU. רוב יחידות ה-GPU לא מותאמות להרכבה, וכשיחידת ה-GPU מרכיבה שכבות מ-SurfaceFlinger, אפליקציות לא יכולות להשתמש ב-GPU לעיבוד משלהן.
הטמעות של HWC צריכות לתמוך ב:
- לפחות ארבע שכבות-על:
- שורת סטטוס
- סרגל המידע
- אפליקציה
- טפט/רקע
- שכבות שגדולות יותר מהתצוגה (לדוגמה, טפט)
- מיזוג אלפא בו-זמני של כל פיקסל בנפרד ומיזוג אלפא של כל מישור בנפרד
- נתיב חומרה להפעלת סרטונים מוגנים
- סדר האריזה של RGBA, פורמטים של YUV ומאפיינים של tiling, swizzling ו-stride
כדי להטמיע את HWC:
- הטמעה של HWC לא פעיל ושליחת כל עבודת הקומפוזיציה ל-GLES.
- מטמיעים אלגוריתם להעברת ההרכבה ל-HWC באופן מצטבר. לדוגמה, להקצות רק את שלושת או ארבעת המשטחים הראשונים לחומרה של שכבת העל של ה-HWC.
- אופטימיזציה של ה-HWC. למשל:
- בחירת פלטפורמות שממזערות את העומס על ה-GPU ושליחתן ל-HWC.
- זיהוי אם המסך מתעדכן. אם לא, צריך להעביר את ההרכבה ל-GLES במקום ל-HWC כדי לחסוך בחשמל. כשהמסך מתעדכן שוב, ממשיכים להעביר את ההרכבה ל-HWC.
- הכנה לתרחישים נפוצים כמו:
- מסך הבית, שכולל את שורת הסטטוס, סרגל המערכת, חלון האפליקציה וטפטים דינמיים
- משחקים במסך מלא במצב לאורך ולרוחב
- סרטון במסך מלא עם כתוביות ושליטה בהפעלה
- הפעלת סרטון מוגן
- ריבוי חלונות במסך מפוצל
פרימיטיבים של HWC
ה-HWC מספק שני פרימיטיבים, שכבות ומסכים, כדי לייצג עבודת קומפוזיציה ואת האינטראקציה שלה עם חומרת המסך. HWC גם מספק שליטה ב-VSync וקריאה חוזרת ל-SurfaceFlinger כדי להודיע לו כשמתרחש אירוע VSync.
ממשק HIDL
ב-Android מגרסה 8.0 ואילך נעשה שימוש בממשק HIDL שנקרא Composer HAL לצורכי IPC עם Binder בין HWC ל-SurfaceFlinger. ה-HAL של Composer מחליף את הממשק hwcomposer2.h מדור קודם. אם ספקים מספקים הטמעה של Composer HAL של HWC, Composer HAL מקבל ישירות קריאות HIDL מ-SurfaceFlinger. אם הספקים מספקים הטמעה מדור קודם של HWC, Composer HAL טוען מצביעים לפונקציות מ-hwcomposer2.h, ומעביר קדימה קריאות HIDL לקריאות מצביעים לפונקציות.
ה-HWC מספק פונקציות לקביעת המאפיינים של מסך נתון, למעבר בין תצורות מסך שונות (כמו רזולוציית 4K או 1080p) ומצבי צבע (כמו צבע מקורי או sRGB אמיתי), ולהפעלה, להשבתה או להעברה של המסך למצב צריכת חשמל נמוכה אם הוא תומך בכך.
מצביעים לפונקציות
אם ספקים מטמיעים את Composer HAL ישירות, SurfaceFlinger קורא לפונקציות שלו דרך HIDL IPC. לדוגמה, כדי ליצור שכבה, SurfaceFlinger קורא ל-createLayer() ב-Composer HAL.
אם הספקים מטמיעים את הממשק hwcomposer2.h, Composer HAL קורא למצביעי הפונקציה hwcomposer2.h. בהערות hwcomposer2.h, הפונקציות של ממשק HWC נקראות בשמות בפורמט lowerCamelCase שלא קיימים בממשק כשדות בעלי שם. כמעט כל פונקציה נטענת על ידי בקשת מצביע פונקציה באמצעות getFunction שסופק על ידי hwc2_device_t. לדוגמה, הפונקציה createLayer
היא מצביע פונקציה מסוג HWC2_PFN_CREATE_LAYER, שמוחזר כשמעבירים את הערך המנומר HWC2_FUNCTION_CREATE_LAYER אל getFunction.
למידע מפורט על פונקציות HAL של Composer ופונקציות HWC של העברת נתונים, אפשר לעיין במאמר composer. לתיעוד מפורט על מצביעים לפונקציות של HWC, אפשר לעיין בhwcomposer2.h.
ידיות של שכבות ותצוגות
השכבות והתצוגות מופעלות על ידי נקודות אחיזה שנוצרות על ידי ה-HWC. הכינויים אטומים ל-SurfaceFlinger.
כש-SurfaceFlinger יוצר שכבה חדשה, הוא קורא ל-createLayer, שמוחזרת מהסוג Layer להטמעות ישירות או hwc2_layer_t להטמעות של העברת נתונים. כש-SurfaceFlinger משנה מאפיין של השכבה הזו, הוא מעביר את הערך hwc2_layer_t לפונקציית השינוי המתאימה, יחד עם כל מידע אחר שנדרש לביצוע השינוי. הסוג hwc2_layer_t גדול מספיק כדי להכיל מצביע או אינדקס.
מסכים פיזיים נוצרים על ידי חיבור מהיר (hotplug). כשמחברים מסך פיזי, ה-HWC יוצר ידית ומעביר אותה ל-SurfaceFlinger דרך הקריאה החוזרת של hotplug. תצוגות וירטואליות נוצרות על ידי SurfaceFlinger
שקורא ל-createVirtualDisplay() כדי לבקש תצוגה. אם HWC
תומך בהרכבת תצוגה וירטואלית, הוא מחזיר נקודת אחיזה. לאחר מכן, SurfaceFlinger מעביר את הקומפוזיציה של התצוגות ל-HWC. אם ה-HWC לא תומך בהרכבת מסך וירטואלי, SurfaceFlinger יוצר את ה-handle ומרכיב את המסך.
פעולות של הרכב התצוגה
פעם אחת בכל VSync, SurfaceFlinger מתעורר אם יש לו תוכן חדש להרכבה. התוכן החדש יכול להיות מאגרי תמונות חדשים מאפליקציות או שינוי במאפיינים של שכבה אחת או יותר. כאשר SurfaceFlinger מעיר אותו:
- מטפל בעסקאות, אם יש כאלה.
- נועל את מאגרי הגרפיקה החדשים, אם קיימים.
- מבצעת קומפוזיציה חדשה, אם שלב 1 או 2 הביאו לשינוי בתוכן התצוגה.
כדי לבצע קומפוזיציה חדשה, SurfaceFlinger יוצר ומסיר שכבות או משנה את מצבי השכבות, בהתאם לצורך. הוא גם מעדכן את השכבות עם התוכן הנוכחי שלהן, באמצעות קריאות כמו setLayerBuffer או setLayerColor. אחרי שכל השכבות מתעדכנות, SurfaceFlinger קורא ל-validateDisplay, שמורה ל-HWC לבדוק את מצב השכבות ולקבוע איך יתבצע השילוב. כברירת מחדל, SurfaceFlinger מנסה להגדיר כל שכבה כך שהשכבה תורכב על ידי HWC. עם זאת, בנסיבות מסוימות, SurfaceFlinger מרכיב שכבות באמצעות GPU fallback.
אחרי הקריאה ל-validateDisplay, SurfaceFlinger קורא ל-getChangedCompositionTypes כדי לבדוק אם HWC רוצה לשנות את אחד מסוגי השכבות לפני ביצוע ההרכבה. כדי לאשר את השינויים, SurfaceFlinger קורא ל-acceptDisplayChanges.
אם יש שכבות שמסומנות להרכבה ב-SurfaceFlinger, SurfaceFlinger מרכיב אותן לתוך מאגר היעד. לאחר מכן, SurfaceFlinger קורא ל-setClientTarget כדי להעביר את המאגר לתצוגה, כך שהמאגר יוצג במסך או יורכב עם שכבות שלא סומנו להרכבה ב-SurfaceFlinger. אם לא מסומנות שכבות להרכבה ב-SurfaceFlinger, המערכת מדלגת על שלב ההרכבה ב-SurfaceFlinger.
לבסוף, SurfaceFlinger קורא ל-presentDisplay כדי להודיע ל-HWC להשלים את תהליך ההרכבה ולהציג את התוצאה הסופית.
מודעות לרשת המדיה בכמה גדלים
Android 10 תומך במספר מסכים פיזיים. כשמתכננים הטמעה של HWC שמיועדת לשימוש ב-Android 7.0 ומעלה, יש כמה הגבלות שלא מופיעות בהגדרה של HWC:
- ההנחה היא שיש בדיוק מסך פנימי אחד. המסך הפנימי הוא המסך שעליו מדווחים על חיבור וניתוק מהיר במהלך האתחול. אחרי שמחברים את המסך הפנימי בחיבור חם, אי אפשר לנתק אותו.
- בנוסף למסך הפנימי, אפשר לחבר מספר כלשהו של מסכים חיצוניים בזמן הפעולה הרגילה של המכשיר. המסגרת מניחה שכל החיבורים והניתוקים של מכשירים בזמן שהמחשב פועל אחרי המסך הפנימי הראשון הם מסכים חיצוניים, ולכן אם מוסיפים עוד מסכים פנימיים, הם מסווגים בצורה שגויה כ-
Display.TYPE_HDMIבמקום כ-Display.TYPE_BUILT_IN.
הפעולות של SurfaceFlinger שמתוארות למעלה מתבצעות לכל מסך, אבל הן מתבצעות ברצף לכל המסכים הפעילים, גם אם התוכן של מסך אחד בלבד מתעדכן.
לדוגמה, אם המסך החיצוני מתעדכן, הרצף הוא:
// In Android 9 and lower: // Update state for internal display // Update state for external display validateDisplay(<internal display>) validateDisplay(<external display>) presentDisplay(<internal display>) presentDisplay(<external display>) // In Android 10 and higher: // Update state for internal display // Update state for external display validateInternal(<internal display>) presentInternal(<internal display>) validateExternal(<external display>) presentExternal(<external display>)
הרכב של מסך וירטואלי
ההרכב של מסך וירטואלי דומה להרכב של מסך חיצוני. ההבדל בין קומפוזיציה של תצוגה וירטואלית לבין קומפוזיציה של תצוגה פיזית הוא שתצוגות וירטואליות שולחות פלט למאגר Gralloc במקום למסך. ה-Hardware Composer (HWC) כותב את הפלט למאגר, מספק את גדר ההשלמה ושולח את המאגר לצרכן (כמו מקודד הווידאו, ה-GPU, ה-CPU וכו'). בתצוגות וירטואליות אפשר להשתמש ב-2D/blitter או בשכבות-על אם צינור התצוגה כותב לזיכרון.
מצבים
כל פריים נמצא באחד משלושת המצבים הבאים אחרי ש-SurfaceFlinger קורא לשיטה validateDisplay() HWC:
- GLES – ה-GPU מרכיב את כל השכבות וכותב ישירות למאגר הפלט. ה-HWC לא מעורב ביצירה.
- MIXED – ה-GPU מרכיב כמה שכבות לתוך מאגר הפריים, וה-HWC מרכיב את מאגר הפריים ואת השכבות שנותרו, וכותב ישירות למאגר הפלט.
- HWC – HWC יוצר קומפוזיציה של כל השכבות וכותב ישירות אל מאגר הפלט.
פורמט פלט
פורמטים של פלט של מאגר וירטואלי לתצוגה תלויים במצב שלו:
- מצב GLES – מנהל ההתקן של EGL מגדיר את פורמט מאגר הפלט ב-
dequeueBuffer(), בדרך כללRGBA_8888. הצרכן צריך להיות מסוגל לקבל את פורמט הפלט שהוגדר על ידי מנהל ההתקן, אחרת לא ניתן לקרוא את המאגר. - מצבי MIXED ו-HWC – אם צרכן צריך גישה למעבד, הוא מגדיר את הפורמט. אחרת, הפורמט הוא
IMPLEMENTATION_DEFINED, ו-Gralloc מגדיר את הפורמט הכי טוב על סמך דגלי השימוש. לדוגמה, Gralloc מגדיר פורמט YCbCr אם הצרכן הוא מקודד וידאו, ו-HWC יכול לכתוב את הפורמט ביעילות.
גדרות סנכרון
גדרות סנכרון הן היבט חשוב במערכת הגרפיקה של Android. הגדרת גדרות מאפשרת לעיבוד במעבד להמשיך באופן עצמאי מעיבוד מקביל במעבד הגרפי, והחסימה מתרחשת רק כשיש תלות אמיתית.
לדוגמה, כששולחים מאגר נתונים זמני שנוצר ב-GPU, שולחים גם אובייקט של גדר סנכרון. הגדרת הגדר הזה מאותתת מתי ה-GPU סיים לכתוב לתוך המאגר.
ה-HWC דורש שה-GPU יסיים לכתוב את המאגרים לפני שהמאגרים יוצגו. גדרות סנכרון מועברות דרך צינור הגרפיקה עם מאגרי נתונים (buffers) ומאותתות כשמאגרי הנתונים נכתבים. לפני שמוצג מאגר, ה-HWC בודק אם גדר הסנכרון אותתה, ואם כן, הוא מציג את המאגר.
מידע נוסף על גדרות סנכרון זמין במאמר שילוב של Hardware Composer.