"אתה מתחיל הכי מהר שלך, ולאט לאט אתה מגביר" – הכל על hardware acceleration.


פורסם ב 08/05/2014 ע"י Royi Benyossef

שלום לכולם,

לפני בערך שנתיים נוסף למילון של מפתחי ה-Android מונח חדש שהוא האצה חומרתית (או hardware acceleration), זה קרה למעשה בגרסת Honeycomb 3.x אבל לא ממש השפיע על רוב מפתחי האפליקציות עד יציאת ICS 4.x ומאז רוב המפתחים מתחזקים באדיקות יחסי שנאה-התעלמות עם המושג הזה למרות שבבסיסו הוא מבשר חיים טובים יותר עם אפליקציות יפות בעלות אנימציות זורמות יותר ולכן אני החלטתי להקדיש את הפוסט הבא לנושא הזה כדי להסביר ולהנגיש את השימוש הנכון בכלי הזה כי בניגוד למקרים אחרים, התעלמות ממנו תזיק לכם.

This post is also available in English on my personal blog here: http://royiby.blogspot.com

מוטיבציה – למה זה מעניין אותי?

כמפתחים אפליקציות (או כל קוד צד לקוח) אנחנו בדרך כלל שואפים תמיד לשיפור בכל מה שקשור לחווית וממשק משתמש:

  • אנימצות חלקות יותר.
  • תגובתיות מהירה יותר.

אנחנו גם בדרך כלל נעדיף יעילות מימושית בה נוכל להתגאות כמו:

  • תקורת זכרון קטנה יותר.
  • עומס קטן יותר על ה-CPU.

האצה חומרתית נוצרה ונועדה ע"מ לעזור למפתחים בדברים אלה בדיוק.

איך בדיוק זה יכול לעזור לי (מה זו האצה חומרתית)?

האצה חומרתית באופן כללי אומרת שטוב יותר ליצור ולהשתמש ברכיב חומרה ייעודי ע"מ לבצע משימה ספציפית או במקרה שלנו, להשתמש ב-GPU בשביל גרפיקה.

למה?

  • תכנון יעודי יבצע משימה ספציפית טוב ומהר יותר מאשר רכיב גנרי שמבצע עוד הרבה משימות.
  • רכיב ייעודי יחזיק משאבים בנפרד שלא יכולים להלקח ע"י המערכת.
  • דרך גישה ייעודית תמומש מהר יותר מאשר צורה גנרית ותחסוך זמן ריצה.

ב-Android, בו כל View מרונדר ע"ג Canvas המקושר אליו, המעבר להאצה חומרתית אומר כי אותו Canvas יצויר ע"י ה-GPU בעזרת מודל הציור החומרתי במקום להיות מצויר ע"י ה-CPU המודל הציור התכנתי, מה אלה המודלים הללו (והאם אכפת לי) אתם שואלים?

מודל הציור התכנתי:

  1. Invalidate – על ה-View שקרא למתודה ועל (כמעט) כל View שקשור אליו.
  2. ציור מחדש של כל ה-Views עליהם נקרא Invalidate (ולמעשה על כל ההיררכיה) מיידית.

מודל הציור החומרתי:

  1. Invalidate – על ה-View הקורא בלבד.
  2. ציור מחדש של ה-View הקורא.
  3. עדכון ה-Display lists עם השינוי של ה-View.
  4. ציור החלקים שהשתנו ב-Display lists במהלך ה-G-sync הבא.

מסקנות:

  • ציור במהלך ה-G-sync (במודל החומרתי) יגרום לשינויים להיות תואמים לזמני ציור המסך כך שהציור והאנימציות יהיו תואמות לשאר המרכת ולכן יראו חלקות וזורמות יותר.
  • במודל החומרתי רק מה שהשתנה צויר מחדש (יעיל וחסכוני הרבה יותר).
  • בזכות ה-Displaylists יש מעקב על מה שהשתנה ורק מה ששונה רונדר מחדש.

זה נשמע מגניב, אבל אז למה יש אפשרות לא לאפשר האצה חומרתית, האם לא תמיד טוב להשתמש בה?

התשובה הקצרה היא לא!

אמנם החל מ-ICS 4.x ברירת המחדל של שימו בהאצה חומרתית היא חיובית אבל העובדה שיש אפשרות לכבות אותה מרמזת כי יש מקרים בהם היא לא רק לא תורמת אלא אפילו מזיקה ולכך יש 2 סיבות:

האצה חומרתית גוררת שימוש יתר בזכרון RAM של המכשיר.

זה די מדבר עבור עצמו למה זה לא טוב :)

לא כל הפעולות נתמכות:

האצה חומרתית למעשה מתרגמת פעולות ציור של Views ו-Canvas לפעולות ציור של OpenGL אבל אלה לא נתמכות כולן עד היום (API 19) ויותר מכך ישנן פעולות ציור שלא תורגמו עדיין, בדרך כלל בגלל שאלה פעולות שלא משתמשים בהם ב-Standard Views המגיעים עם Android ולכן זה יוצר מצב בו חלק מהפעולות הן חומרתיות וחלק תוכנתיות מה שיכול לגרום לבאגים ויזואליים מוזרים וקשים מאוד לדיבוג וכמו-כן לבעיות פונקציונליות קשות כמו:

  • רכיבי ממשק שנעלמים ללא סיבה הגיונית.
  • Errors ו-Exceptions מוזרים, בעיקר שקשורות בזכירון.
  • פיקסלים שמרונדרים לא נכון וללא סיבה הגיונית.

עכשיו אני מבולבל, מה אני אמור לעשות ואיך אני יודע אם יש משהו לא בסדר?

לפני שנמצאו באגים:

נתחיל במתי אתם יכולים להיות רגועים:

  • אתם אינכם משתמשים ב-Custom Views או Canvas בכלל.
  • רמת API מינימלית היא 17.

נמשיך בלהצביע על איך אפשר לבדוק אם הפעולות בהן אתם משתמשים הינן נתמכות כאן: http://developer.android.com/guide/topics/graphics/hardware-accel.html#drawing-support.

גם אם גיליתם סיבה לדאגה עדיין יש מה לבדוק:

  1. השתמשו בכלי הבדיקה שיש ל-Android להציע כגון; DDMS, HierarcyViewer, TraceView, GLTracer ו – Systrace.
  2. בידקו על מכשירים ממשיים וכמה שיותר (התנהגות האצת החומרה משתנה בין GPU ל-GPU ובין רמות API שונות), הרגישו חופשיים לבקש שימוש ב-Campus device library כאן: http://www.campustelaviv.com/hackspace/
ונניח שמצאתם באגים (מה לעשות?):
1. הקטינו את מספר ה-Views בכלל וה-Custom Views בפרט

היררכית Views גדולה ועמוקה יוצרת עומס על המערכת, זה נכון שבעתיים כאשר אנחנו מדברים על מודל הציור החומרתי שכולל את ה-DisplayLists ולכן הקטנת העומס ע"י הורדת מספר ה-Views בכללי יכול לפתור הרבה בעיות performance בכללי וגם כאלה של האצה חומרתית.

היוצאים מן הכלל של המשפט האחרון הינם ה-Custom Views משום ש-Standard Views חסינים לבעיות הנובעות משימוש לא נכון של האצה חומרתית אז החלפת Custom Views בכאלה סטנדרטיים תקל על המערכת גם עם יהיו יותר Views אבל רק עד גבול מסוים לפני שבעית performance כללית תגבר על התועלת של שימוש ב-Views סטנדרטיים.

2. ציירו רק מה שנראה (המנעו מ-Overdraw).

גם אם יש לכם צורך מאיזשהי סיבה לצייר על פיקסל שכבר צויר בעבר למטרות גרפיקה של עומק או משהו דומה, לעולם אל תעברו את הרף של x2.5 (אל תציירו על אותו פיקסל 3 פעמים) משום שה-GPU חייב לעבוד הרבה יותר קשה ע"מ ליצור את ה-Texture הזו ויכולות להיות התנגשויות ולכן לבעיות וכיוצא מזה, אם יש לכם מצב של יותר מ-x2.5 עליכם למחוק או למזג שכבות (layers) ע"מ להבטיח פעולה תקינה.

* כדאי לשים לב כי פיקסלים "שקופים" של bitmaps נכנסים לחשבון הזה ואסור להתעלם מהם.

3. New is bad (נסו אתם לתרגם את זה לעברית P:)

קטעי הקוד של הציור (onDraw, onMeasure וכו') נקראים הרבה מאוד פעמים ולכן אם תיצרו שם מופעים של משתנים אתם תעמיסו מאוד על ה-garbage collector ומקבילו החומרתי ותגרמו להם לרוץ הרבה פעמים, זוהי פעולה יקרה מאוד שתיקח לכם performace אבל גם עלולה ליצור התנגשויות גישה ב-GPU שיצרו באגים לא מוסברים וקשים לדיבוג.

פשוט השתמשו ב-design pattern דומה ל-ViewHolder וצרו את ה-Path, Paint וכו' שלכם מראש במשתנה גלובלי פרטי ושנו את המידע בהם כשצריך.

4. שנו רק את מה שדורש שינוי.

אובייקטים כמו Shape, Circle, Path, Bitmap יוצרים Texture חדש כל פעם שהם משתנים שזה פחות אבל עדיין מכביד בדומה ליצירתם מחדש עם new ולכן שנו אותם רק מתי שיש בכך צורך ורק אז.

5. נהלו שקיפות בנפרד.

בניגוד לצורות ופעולות גרפיות אחרות, שקיפות (alpha) ניתנת לשינוי ב-buffer בלי לשנות את ה-Texture ב-GPU כאשר הן נעשות בהאצת חומרה ולכן נהלו אותן תמיד תחת האצה חומרתית גם אם זה אומר לנהל אותן בנפרד.

6. העלו Textures ל-GPU רק פעם אחת.

נניח שיש לכם כפתור שמסיבות מסוימות הוא Custom View ולכפתור זה תמיד אתם מעלים את אותו רקע ומשתמשים בכפתור הזה במספר מקומות במערכת.

מה שיקרה אם לא תטפלו בכך זה שכל פעם שהכפתור מופיע אותו רקע יועלה ל-GPU עוד פעם רק שה-GPU מכיר את ה-Textures שלו וינסה למזג ביניהם לבדו… בדרך כלל ללא הצלחה ועם באגים מאוד מיוחדים.

הפיתרון:



private class PieView extends View {

       public PieView(Context context) {
           super(context);
           if (!isInEditMode()) {
               setLayerType(View.LAYER_TYPE_HARDWARE, null);
           }
       }
...
}
7. נהלו את הרזולוציה של ההאצה החומרתית רק ללאן שאתם זקוקים לה.

ב-Android אתם יכולים לכבות או להדליק את האצת החומרה ע"פ רזולוציות שונות השתמשו בזאת בתבונה ותמיד נסו לשנות.

Application:

<application android:hardwareAccelerated="true">

Activity:

<application android:hardwareAccelerated="true">
    <activity ... />
    <activity android:hardwareAccelerated="false" />
</application>

Window:

getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

View & layer:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

* שימו לב  למספר דברים הקשורים ל-Layers:

1. יש 3 סוגי Layers ושיש להם משמעות ברורה:

  • LAYER_TYPE_NONE – לא נשמרת או מגובה בשום צורה, מצוירת במודל התכנתי.
  • LAYER_TYPE_HARDWARE – אם האצה חומרתית מאופשרת, ה-Layer תגובה ע"י Texture buffer חומרתי ותצויר במודל החומרתי אחרת היא תתנהג כמו ה-LAYER_TYPE_SOFTWARE. בדיוק.
  • LAYER_TYPE_SOFTWARE – מגובה ע"י Bitmap ומצוירת במודל התכנתי.

2. העדיפו את ה-LAYER_TYPE_SOFTWARE כאשר אתם תומכים ברמת API קטנה מ-11 ובאופו כללי לתאימות אחורה. או כאשר אתם נתקלים במכשיר שיש לו GPU שמתנהג שונה ממש ויוצר לכם בעיות בפעולות ספציפיות (כמו שאמרתי בהתחלה יתכנו שינויים בין GPU אחד לשני).

3. העדיפו את ה- LAYER_TYPE_HARDWARE כאשר אתם צריכים ויכולים לדרוש performance גרפי ברמה גבוהה ובייחוד כאשר אתם צריכים שקיפויות (alpha).

4. השתמשו ב-getters ע"מ לדעת את מצב ה-Layer שאתם עובדים עליה.

ישנן 2 מתודות אפשריות:

העדיפו את הראשונה כשאפשר משום שהשניה תתן לכם תשובה לא נכונה (false positive) במקרים בהם ה-Window מקושר ל-Hardware Layer אבל מגובה ב-Bitmap לצרכי caching ולכן יצויר במודל התכנתי בניגוד למה שתחזיר המתודה השניה.

Royi is a Google Developer Expert for Android in 2013, a mentor at Google's CampusTLV for Android and (last but not least) the set top box team leader at Vidmind, an OTT TV and Video Platform Provider. www.vidmind.com

This post is also available in English on my personal blog here: http://royiby.blogspot.com

פוסטים קשורים:

FacebookTwitterGoogle+EmailPinterestWhatsAppLinkedInשתפו אותי

Royi Benyossef

I have been an Android developer since before the first Android phone existed and proceeded to work on development for Android based tablets and TVs long before they were launched by Google so you might say i'm somewhat Android pioneer whose expertise is in non-standard Android platforms and products which involve Android internals as well as application development. As an avid Open source enthusiast i have been Organizing events as well as blogging, speaking and mentoring on Android related topics since 2010 and it is due to the above that i am a Google developer expert since the program's inception which means that i'm not a Google employee but that Google believes that i know what i'm talking about as well as how to say it. Outside of Android i have been a full stack developer at a projects company where i was developing mobile applications in Android as well as iOS (Objective-c) and also dabbled in server side development in PHP, Jquery and more including a few projects of mobile applications using cross platform push technologies using socket.io, XMPP and other similar protocols as a server-side, i also had other wonderful projects which granted me experience in widgets, live wallpapers, network and battery efficiency and many more exciting topics.

1 Comment

  1. 1_aviv
    08/05/2014 בשעה 23:30

    יש לי משחק שלם שכתוב על canvas.
    אין לי שום בעיית ביצועים גם לא על מכשירים חלשים.אבל עדין נשמע שווה בדיקה.

להגיב