RenderScript

RenderScript היא מסגרת להרצת משימות עתירות חישוב בביצועים גבוהים ב-Android. הוא מיועד לשימוש עם חישוב מקביל לנתונים, אבל יכול להועיל גם לעומסי עבודה סדרתיים. סביבת זמן הריצה של RenderScript מבצעת עבודה במקביל בין המעבדים שזמינים במכשיר, כמו מעבדי CPU מרובי ליבות ומעבדי GPU, וכך מאפשרת למפתחים להתמקד בביטוי אלגוריתמים במקום בתזמון עבודה. ‫RenderScript שימושי במיוחד לאפליקציות שמבצעות עיבוד תמונה, צילום חישובי או ראייה ממוחשבת.

במכשירים עם Android 8.0 ואילך נעשה שימוש במסגרת RenderScript הבאה וב-HALs של הספקים:

איור 1. קוד ספק שמקשר לספריות פנימיות.

ההבדלים מ-RenderScript ב-Android 7.x ומטה כוללים:

  • שני מופעים של ספריות פנימיות של RenderScript בתהליך. קבוצה אחת היא לנתיב חלופי של CPU ומגיעה ישירות מ-/system/lib, והקבוצה השנייה היא לנתיב GPU ומגיעה מ-/system/lib/vndk-sp.
  • ספריות פנימיות של RS ב-/system/lib נוצרות כחלק מהפלטפורמה ומתעדכנות כשמשדרגים את system.img. עם זאת, ספריות libs ב-/system/lib/vndk-sp מיועדות לספק ולא מתעדכנות כשמשדרגים את system.img (אפשר לעדכן אותן לצורך תיקון אבטחה, אבל ה-ABI שלהן נשאר זהה).
  • קוד הספק (RS HAL,‏ RS driver ו-bcc plugin) מקושר לספריות הפנימיות של RenderScript שנמצאות ב-/system/lib/vndk-sp. הם לא יכולים לקשר מול ספריות ב-/system/lib כי ספריות בספרייה הזו מיועדות לפלטפורמה, ולכן יכול להיות שהן לא תואמות לקוד של הספק (כלומר, יכול להיות שסמלים יוסרו). אם תעשו את זה, לא תוכלו לבצע עדכון OTA של המסגרת בלבד.

עיצוב

בקטעים הבאים מפורט העיצוב של RenderScript ב-Android מגרסה 8.0 ואילך.

ספריות RenderScript שזמינות לספקים

בקטע הזה מפורטות ספריות RenderScript (שנקראות Vendor NDK עבור HALs באותו תהליך או VNDK-SP) שזמינות לקוד של ספקים שאפשר לקשר אליהן. בנוסף, מפורטות בו ספריות נוספות שלא קשורות ל-RenderScript, אבל מסופקות גם לקוד הספק.

הרשימה הבאה של ספריות עשויה להיות שונה בין גרסאות Android, אבל היא קבועה לגרסת Android ספציפית. כדי לראות רשימה עדכנית של הספריות הזמינות, אפשר לעיין ב/system/etc/ld.config.txt.

ספריות RenderScript ספריות שאינן RenderScript
  • android.hardware.graphics.renderscript@1.0.so
  • libRS_internal.so
  • libRSCpuRef.so
  • libblas.so
  • libbcinfo.so
  • libcompiler_rt.so
  • libRSDriver.so
  • libc.so
  • libm.so
  • libdl.so
  • libstdc++.so
  • liblog.so
  • libnativewindow.so
  • libsync.so
  • libvndksupport.so
  • libbase.so
  • libc++.so
  • libcutils.so
  • libutils.so
  • libhardware.so
  • libhidlbase.so
  • libhidltransport.so
  • libhwbinder.so
  • liblzma.so
  • libz.so
  • libEGL.so
  • libGLESv1_CM.so
  • libGLESv2.so

הגדרת מרחב שמות של Linker

ההגבלה על הקישור שמונעת שימוש בספריות שלא נמצאות ב-VNDK-SP על ידי קוד הספק נאכפת בזמן הריצה באמצעות מרחב השמות של המקשר. (פרטים נוספים זמינים במצגת VNDK Design).

במכשיר עם Android מגרסה 8.0 ואילך, כל ה-HALs של אותו תהליך (SP-HALs) למעט RenderScript נטענים בתוך מרחב השמות של ה-linker sphal. ‫RenderScript נטען למרחב השמות הספציפי ל-RenderScript‏ rs, מיקום שמאפשר אכיפה קצת פחות מחמירה של ספריות RenderScript. הטמעת ה-RS צריכה לטעון את הביטקוד המהודר, ולכן /data/*/*.so נוסף לנתיב של מרחב השמות rs (אסור ל-SP-HAL אחרים לטעון ספריות ממחיצת הנתונים).

בנוסף, מרחב השמות rs מאפשר יותר ספריות מאשר מרחבי שמות אחרים. ‫libmediandk.so ו-libft2.so חשופים למרחב השמות rs כי ל-libRS_internal.so יש תלות פנימית בספריות האלה.

איור 2. הגדרת מרחב שמות עבור הכלי לקישור.

טעינת נהגים

נתיב חלופי של המעבד (CPU)

בהתאם לקיום של RS_CONTEXT_LOW_LATENCY ביט כשיוצרים הקשר RS, נבחר נתיב CPU או GPU. כשבוחרים בנתיב של המעבד, libRS_internal.so (ההטמעה הראשית של מסגרת ה-RS) מdlopenתקשרת ישירות ממרחב השמות של מקשר ברירת המחדל, שבו מסופקות ספריות ה-RS של גרסת הפלטפורמה.

היישום של RS HAL מהספק לא נמצא בשימוש בכלל כשמשתמשים בנתיב חלופי של CPU, ואובייקט RsContext נוצר עם mVendorDriverName null. הערך של libRSDriver.so הוא (כברירת מחדל) dlopen, וספריית מנהל ההתקן נטענת ממרחב השמות default כי גם המתקשר (libRS_internal.so) נטען במרחב השמות default.

איור 3. נתיב חלופי של המעבד (CPU).

נתיב ה-GPU

בנתיב ה-GPU, הטעינה של libRS_internal.so שונה. קודם כול, libRS.so משתמש ב-android.hardware.renderscript@1.0.so (וב-libhidltransport.so הבסיסי שלו) כדי לטעון את android.hardware.renderscript@1.0-impl.so (יישום של ספק של RS HAL) למרחב שמות שונה של מקשר שנקרא sphal. רכיב ה-HAL של RS אז dlopens libRS_internal.so במרחב שמות אחר של linker שנקרא rs.

ספקים יכולים לספק מנהל התקן משלהם של RS על ידי הגדרת הדגל של זמן הבנייה OVERRIDE_RS_DRIVER, שמוטמע ביישום של RS HAL (hardware/interfaces/renderscript/1.0/default/Context.cpp). שם מנהל ההתקן הזה dlopened עבור הקשר של RS לנתיב של ה-GPU.

היצירה של אובייקט RsContext מוקצית להטמעה של RS HAL. רכיב ה-HAL מבצע קריאה חוזרת למסגרת ה-RS באמצעות הפונקציה rsContextCreateVendor() עם שם הדרייבר שבו רוצים להשתמש כארגומנט. לאחר מכן, מסגרת ה-RS טוענת את מנהל ההתקן שצוין כשמאותחלת הפונקציה RsContext. במקרה הזה, ספריית מנהלי ההתקנים נטענת למרחב השמות rs כי האובייקט RsContext נוצר בתוך מרחב השמות rs ו-/vendor/lib נמצא בנתיב החיפוש של מרחב השמות.

איור 4. נתיב חלופי של GPU.

במעבר ממרחב השמות default למרחב השמות sphal, נעשה שימוש בפונקציה android_load_sphal_library() כדי להורות ל-linker הדינמי לטעון את הספרייה -impl.so ממרחב השמות sphal.libhidltransport.so

כשעוברים ממרחב השמות sphal למרחב השמות rs, הטעינה מתבצעת באופן עקיף על ידי השורה הבאה בקובץ /system/etc/ld.config.txt:

namespace.sphal.link.rs.shared_libs = libRS_internal.so

בשורה הזו מצוין שהמקשר הדינמי צריך לטעון את libRS_internal.so ממרחב השמות rs אם אי אפשר למצוא או לטעון את lib ממרחב השמות sphal (וזה תמיד המצב כי מרחב השמות sphal לא מחפש את /system/lib/vndk-sp שבו נמצא libRS_internal.so). במקרה כזה, מספיק לבצע קריאה פשוטה של dlopen() אל libRS_internal.so כדי לבצע את המעבר למרחב השמות.

טעינת הפלאגין של העותק המוסתר

bcc plugin היא ספרייה שסופקה על ידי ספק ונטענה למהדר bcc. מכיוון ש-bcc הוא תהליך מערכת בספריית /system/bin, אפשר להתייחס לספריית bcc plugin כאל SP-HAL (כלומר, HAL של ספק שאפשר לטעון ישירות לתהליך המערכת בלי להשתמש ב-binder). בתור SP-HAL, ספריית bcc-plugin:

  • אי אפשר לקשר לספריות שמבוססות על מסגרות בלבד, כמו libLLVM.so.
  • אפשר לקשר רק לספריות VNDK-SP שזמינות לספק.

ההגבלה הזו נאכפת על ידי טעינת bcc plugin למרחב השמות sphal באמצעות הפונקציה android_sphal_load_library(). בגרסאות קודמות של Android, שם הפלאגין צוין באמצעות האפשרות -load והספרייה נטענה באמצעות הפקודה הפשוטה dlopen() על ידי libLLVM.so. ב-Android מגרסה 8.0 ואילך, האפשרות הזו מצוינת ב--plugin והספרייה נטענת ישירות על ידי bcc עצמו. האפשרות הזו מאפשרת להגדיר נתיב לא ספציפי ל-Android לפרויקט הקוד הפתוח LLVM.

איור 5. טעינת הפלאגין של bcc, ‏ Android מגרסה 7.x ומטה.



איור 6. טעינת התוסף bcc, ‏ Android 8.0 ומעלה.

נתיבי חיפוש של ld.mc

כשמבצעים את ld.mc, חלק מספריות זמן הריצה של RS ניתנות כקלט למקשר. הביטקוד של RS מהאפליקציה מקושר לספריות של זמן הריצה וכשהביטקוד שהומר נטען לתהליך של אפליקציה, הספריות של זמן הריצה מקשרות שוב באופן דינמי מהביטקוד שהומר.

ספריות זמן הריצה כוללות:

  • libcompiler_rt.so
  • libm.so
  • libc.so
  • נהג RS (libRSDriver.so או OVERRIDE_RS_DRIVER)

כשמעלים את קוד הביטקוד המהודר לתהליך האפליקציה, צריך לספק את אותה ספרייה שבה נעשה שימוש ב-ld.mc. אחרת, יכול להיות שקוד הביטקוד המהודר לא ימצא סמל שהיה זמין כשהוא קושר.

לשם כך, מסגרת RS משתמשת בנתיבי חיפוש שונים לספריות זמן הריצה כשמבצעים ld.mc, בהתאם לשאלה אם מסגרת RS עצמה נטענת מ-/system/lib או מ-/system/lib/vndk-sp. אפשר לקבוע את זה על ידי קריאת הכתובת של סמל שרירותי של ספריית מסגרת RS ושימוש ב-dladdr() כדי לקבל את נתיב הקובץ שממופה לכתובת.

מדיניות SELinux

בעקבות השינויים במדיניות SELinux ב-Android מגרסה 8.0 ואילך, צריך לפעול לפי כללים ספציפיים (שנאכפים באמצעות neverallows) כשמסמנים קבצים נוספים במחיצה vendor:

  • vendor_file חייבת להיות התווית שמוגדרת כברירת מחדל לכל הקבצים במחיצה vendor. מדיניות הפלטפורמה מחייבת זאת כדי לגשת להטמעות של HAL passthrough.
  • לכל exec_types חדש שנוסף במחיצה vendor דרך ספק SEPolicy צריך להיות מאפיין vendor_file_type. האכיפה מתבצעת באמצעות neverallows.
  • כדי למנוע התנגשויות עם עדכונים עתידיים של פלטפורמות או מסגרות, אל תתנו תוויות לקבצים אחרים מלבד exec_types במחיצה vendor.
  • כל יחסי התלות של הספרייה עבור HALs באותו תהליך שזוהו ב-AOSP צריכים להיות מסומנים בתווית same_process_hal_file.

פרטים על מדיניות SELinux זמינים במאמר בנושא Security-Enhanced Linux ב-Android.

תאימות ABI לביטקוד

אם לא נוספים ממשקי API חדשים, כלומר לא מתבצע עדכון של גרסת HAL, מסגרות ה-RS ימשיכו להשתמש במנהל ההתקן הקיים של ה-GPU (HAL 1.0).

במקרה של שינויים קלים ב-HAL‏ (HAL 1.1) שלא משפיעים על bitcode, המסגרות צריכות לחזור לשימוש ב-CPU עבור ממשקי ה-API החדשים האלה ולהמשיך להשתמש במנהל ההתקן של GPU‏ (HAL 1.0) במקומות אחרים.

במקרה של שינויים משמעותיים ב-HAL (גרסה HAL 2.0) שמשפיעים על הידור או קישור של bitcode, כדאי שמסגרות RS לא יטענו מנהלי התקנים של GPU שסופקו על ידי הספק, ובמקום זאת ישתמשו בנתיב CPU או Vulkan להאצה.

השימוש בביטקוד של RenderScript מתבצע בשלושה שלבים:

שלב פרטים
הידור
  • קוד הביטים (‎.bc) של bcc צריך להיות בפורמט קוד ביטים LLVM 3.2, וbcc צריך להיות תואם לאחור לאפליקציות קיימות (מדגם ישן).
  • עם זאת, המטא-נתונים ב-‎.bc יכולים להשתנות (יכולות להיות פונקציות חדשות של זמן ריצה, למשל, פונקציות להגדרת הקצאה ולקבלת הקצאה, פונקציות מתמטיות וכו'). חלק מהפונקציות של זמן הריצה נמצא ב-libclcore.bc, וחלק מהן נמצא ב-LibRSDriver או במקבילה של הספק.
  • פונקציות חדשות של זמן ריצה או שינויים משמעותיים במטא-נתונים מחייבים הגדלה של רמת ה-API של bitcode. מכיוון שמנהלי התקנים של ספקים לא יוכלו להשתמש בו, צריך להגדיל גם את גרסת ה-HAL.
  • יכול להיות שלספקים יהיו קומפיילרים משלהם, אבל המסקנות או הדרישות לגבי bcc חלות גם על הקומפיילרים האלה.
קישור
  • קובץ ה-‎.o המהודר יקושר לדרייבר של הספק, למשל: libRSDriver_foo.so ו-libcompiler_rt.so. הנתיב של יחידת העיבוד המרכזית (CPU) יקושר ל-libRSDriver.so.
  • אם קובץ ה-‎ .o דורש API חדש של זמן ריצה מ-libRSDriver_foo, צריך לעדכן את מנהל ההתקן של הספק כדי לתמוך בו.
  • יכול להיות שלספקים מסוימים יש כלי קישור משלהם, אבל הטיעון לגבי ld.mc תקף גם לגביהם.
טעינה
  • libRSCpuRef טוען את האובייקט המשותף. אם יש שינויים בממשק הזה, צריך לעדכן את גרסת ה-HAL.
  • ספקים יכולים להסתמך על libRSCpuRef כדי לטעון את האובייקט המשותף, או להטמיע אובייקט משלהם.

בנוסף ל-HAL, ממשקי API של זמן ריצה והסמלים המיוצאים הם גם ממשקים. הממשקים לא השתנו מאז Android 7.0 ‏ (API 24) ואין תוכניות מיידיות לשנות אותם ב-Android 8.0 ואילך. עם זאת, אם הממשק משתנה, גם גרסת ה-HAL תגדל.

הטמעות של ספקים

ב-Android מגרסה 8.0 ואילך, כדי שמנהל ההתקן של ה-GPU יפעל בצורה תקינה, צריך לבצע כמה שינויים במנהל ההתקן של ה-GPU.

מודולים של דרייברים

  • מודולים של מנהלי התקנים לא יכולים להיות תלויים בספריות מערכת שלא מופיעות ברשימה.
  • הדרייבר חייב לספק android.hardware.renderscript@1.0-impl_{NAME} משלו, או להצהיר על הטמעה של ברירת המחדל android.hardware.renderscript@1.0-impl כתלות שלו.
  • ההטמעה של CPU libRSDriver.so היא דוגמה טובה להסרת תלות שאינה VNDK-SP.

קומפיילר של ביטקוד

יש שתי דרכים לקומפילציה של קוד ביניים של RenderScript עבור מנהל ההתקן של הספק:

  1. הפעלת מהדר RenderScript ספציפי לספק ב-/vendor/bin/ (השיטה המועדפת למהדר GPU). בדומה למודולים אחרים של מנהלי התקנים, קובץ הבינארי של מהדר הספק לא יכול להיות תלוי בספריית מערכת שלא מופיעה ברשימה של ספריות RenderScript שזמינות לספקים.
  2. הפעלת מערכת ה-BCC: /system/bin/bcc עם bcc plugin שסופק על ידי הספק; הפלאגין הזה לא יכול להיות תלוי בספריית מערכת שלא נמצאת ברשימה של ספריות RenderScript שזמינות לספקים.

אם הספק bcc plugin צריך להתערב בהידור של המעבד והתלות שלו ב-libLLVM.so לא ניתנת להסרה בקלות, הספק צריך להעתיק את bcc (ואת כל התלויות שאינן LL-NDK, כולל libLLVM.so, libbcc.so) למחיצה /vendor.

בנוסף, ספקים צריכים לבצע את השינויים הבאים:

איור 7. שינויים במנהל ההתקן של הספק.

  1. העתקת libclcore.bc למחיצה /vendor. כך מוודאים ש-libclcore.bc, libLLVM.so ו-libbcc.so מסונכרנים.
  2. כדי לשנות את הנתיב לקובץ ההרצה bcc, צריך להגדיר את RsdCpuScriptImpl::BCC_EXE_PATH מההטמעה של RS HAL.

מדיניות SELinux

מדיניות SELinux משפיעה גם על מנהל ההתקן וגם על קובצי ההפעלה של הקומפיילר. כל מודולי הדרייבר צריכים להיות מסומנים בתווית same_process_hal_file ב-file_contexts של המכשיר. לדוגמה:

/vendor/lib(64)?/libRSDriver_EXAMPLE\.so     u:object_r:same_process_hal_file:s0

תהליך האפליקציה צריך להיות מסוגל להפעיל את קובץ ההפעלה של הקומפיילר, כמו גם את העותק של bcc של הספק (/vendor/bin/bcc). לדוגמה:

device/vendor_foo/device_bar/sepolicy/file_contexts:
/vendor/bin/bcc                    u:object_r:same_process_hal_file:s0

מכשירים מדור קודם

מכשירים מדור קודם הם מכשירים שעומדים בתנאים הבאים:

  1. הערך של PRODUCT_SHIPPING_API_LEVEL נמוך מ-26.
  2. הערך PRODUCT_FULL_TREBLE_OVERRIDE לא מוגדר.

במכשירים מדור קודם, ההגבלות לא נאכפות כשמשדרגים ל-Android מגרסה 8.0 ואילך, כלומר מנהלי ההתקנים יכולים להמשיך לקשר לספריות ב-/system/lib[64]. עם זאת, בגלל השינוי בארכיטקטורה שקשור ל-OVERRIDE_RS_DRIVER, צריך להתקין את android.hardware.renderscript@1.0-impl במחיצה /vendor. אם לא עושים את זה, זמן הריצה של RenderScript יחזור לנתיב של יחידת העיבוד המרכזית (CPU).

מידע על הסיבות להוצאה משימוש של Renderscript זמין בבלוג של Android Developers: Android GPU Compute Going Forward. פרטי המשאב לגבי הוצאה משימוש כוללים את הפרטים הבאים: