خط لوله رندرینگ دوبعدی Android از شتاب سختافزاری پشتیبانی میکند، یعنی همه عملیات طراحی که روی بوم انجام میشود از واحد پردازش گرافیکی (GPU) استفاده میکند. بهدلیل افزایش منابع موردنیاز برای فعال کردن شتاب سختافزاری، برنامهتان RAM بیشتری مصرف خواهد کرد.
شتاب سختافزاری بهطور پیشفرض فعال است. اگر برنامهتان فقط از ترکیبپذیرهای استاندارد استفاده میکند، روشن کردن آن بهصورت سراسری نباید هیچ اثر نامطلوبی بر ترسیم داشته باشد. بااینحال، چون شتاب سختافزاری برای همه عملیاتهای ترسیم دوبعدی پشتیبانی نمیشود، روشن کردن آن ممکن است بر برخیاز فراخوانیهای ترسیم سفارشی شما تأثیر بگذارد. مشکلات معمولاً بهصورت عناصر نامرئی، استثناها، یا پیکسلهای اشتباه رندرشده ظاهر میشوند. برای رفع این مشکل، Android به شما این امکان را میدهد که شتاب سختافزاری را در سطوح مختلف فعال یا غیرفعال کنید. کنترل شتاب سختافزاری را ببینید.
اگر برنامهتان طراحی سفارشی انجام میدهد، برنامهتان را در دستگاههای سختافزاری واقعی درحالیکه شتاب سختافزاری روشن است آزمایش کنید تا مشکلات را پیدا کنید. بخش پشتیبانی از عملیات طراحی مشکلات شناختهشده با شتاب سختافزاری و نحوه حل آنها را شرح میدهد.
همچنین OpenGL با میاناهای برنامهسازی کاربردی چارچوب را ببینید.
کنترل شتاب سختافزاری
میتوانید شتاب سختافزاری را در سطوح زیر کنترل کنید:
- برنامه
- فعالیت
- پنجره
- تابع ترکیبپذیر
سطح برنامه
در فایل مانیفست Android، مشخصه زیر را به برچسب
<application> اضافه کنید تا شتاب سختافزاری برای کل
برنامه فعال شود:
<application android:hardwareAccelerated="true" ...>
سطح فعالیت
اگر برنامه شما با روشن بودن شتاب سختافزاری بهطور سراسری بهدرستی رفتار نمیکند، میتوانید آن را برای فعالیتهای فردی نیز کنترل کنید. برای فعال یا غیرفعال کردن شتاب سختافزاری در سطح فعالیت، میتوانید از مشخصه android:hardwareAccelerated برای عنصر <activity> استفاده کنید. مثال زیر شتاب سختافزاری را برای کل برنامه فعال میکند اما آن را برای یک فعالیت غیرفعال میکند:
<application android:hardwareAccelerated="true">
<activity ... />
<activity android:hardwareAccelerated="false" />
</application>
سطح پنجره
اگر به کنترل دقیقتری نیاز دارید، میتوانید شتاب سختافزاری را برای پنجرهای خاص با کد زیر فعال کنید:
window.setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)
سطح Composable
در Compose، هیچ کلید قابلترکیبی برای غیرفعال کردن شتاب سختافزاری وجود ندارد.
برای رندر کردن یک composable در لایه خودش، از Modifier.graphicsLayer استفاده کنید. این کار باعث میشود ویژگیهای تبدیل (مثل alpha، scaleX، scaleY، translationX،
translationY، rotationX، rotationY، rotationZ، و transformOrigin)
بدون اجرای مجدد کد ترسیم ترکیبپذیر تغییر کنند. برای بهترین
عملکرد، همیشه از شکل لامبدا اصلاحکننده برای تنظیم این ویژگیها استفاده کنید.
برای اینکه صریحاً میانگیر خارج از صفحه را برای عملیات طراحی پیشرفته، مثل ترکیب سفارشی در لایه، اجباری کنید، از CompositingStrategy.Offscreen استفاده کنید.
برای اطلاعات بیشتر، اصلاحکنندههای گرافیک را ببینید.
اگر عملیات طراحی سفارشی دارید که اکیداً به نرمافزار نیاز دارد
پرداخت، میتوانید بااستفاده از AndroidView یک «نمای» قدیمی را میزبانی کنید و
setLayerType(View.LAYER_TYPE_SOFTWARE, null) را در آن نما فراخوانی کنید.
پشتیبانی از عملیات طراحی
وقتی سختافزار شتابدهی میشود، خط لوله پردازش دوبعدی از رایجترین عملیاتهای طراحی Canvas و همچنین بسیاری از عملیاتهای کمتر استفادهشده پشتیبانی میکند. همه
عملیاتهای طراحی که برای پرداز برنامههایی که با
Android، ترکیبپذیرهای استاندارد، و جلوههای دیداری پیشرفته رایج مانند
بازتابها و بافتهای کاشیکاریشده ارائه میشوند پشتیبانی میشوند.
جدول زیر سطح پشتیبانی از عملیات مختلف را در سطوح API شرح میدهد:
| اولین سطح میانای برنامهسازی کاربردی پشتیبانیشده | ||||
| Canvas | ||||
| drawBitmapMesh() (colors array) | 18 | |||
| drawPicture() | 23 | |||
| drawPosText() | 16 | |||
| drawTextOnPath() | 16 | |||
| drawVertices() | 29 | |||
| setDrawFilter() | 16 | |||
| clipPath() | 18 | |||
| clipRegion() | 18 | |||
| clipRect(Region.Op.XOR) | 18 | |||
| clipRect(Region.Op.Difference) | 18 | |||
| clipRect(Region.Op.ReverseDifference) | 18 | |||
| clipRect() با چرخش/پرسپکتیو | 18 | |||
| رنگ | ||||
| setAntiAlias() (برای نوشتار) | 18 | |||
| setAntiAlias() (برای خطوط) | 16 | |||
| setFilterBitmap() | 17 | |||
| setLinearText() | ✗ | |||
| setMaskFilter() | ✗ | |||
| setPathEffect() (برای خطوط) | 28 | |||
| setShadowLayer() (other than text) | 28 | |||
| setStrokeCap() (برای خطوط) | 18 | |||
| setStrokeCap() (برای نقاط) | 19 | |||
| setSubpixelText() | 28 | |||
| Xfermode | ||||
| PorterDuff.Mode.DARKEN (میانگیر قاب) | 28 | |||
| PorterDuff.Mode.LIGHTEN (میانگیر قاب) | 28 | |||
| PorterDuff.Mode.OVERLAY (بافر قاب) | 28 | |||
| سایهزن | ||||
| ComposeShader inside ComposeShader | 28 | |||
| سایهزنهای همنوع در ComposeShader | 28 | |||
| ماتریس محلی در ComposeShader | 18 | |||
مقیاسبندی بوم نقاشی
خط لوله پردازش دوبعدی با شتاب سختافزاری ابتدا برای پشتیبانی از طراحی بدون مقیاس ساخته شد، اما برخی عملیات طراحی در مقادیر مقیاس بالاتر کیفیت را بهطور قابلتوجهی کاهش میدادند. این عملیات بهعنوان بافتهایی که در مقیاس ۱.۰ ترسیم شدهاند و توسط GPU تبدیل شدهاند، اجرا میشوند. از سطح API ۲۸، همه عملیاتهای طراحی میتوانند بدون مشکل مقیاسبندی شوند.
جدول زیر نشان میدهد که چه زمانی پیادهسازی برای مدیریت صحیح مقیاسهای بزرگ تغییر کرده است:
| عملیات طراحی که باید مقیاسبندی شود | اولین سطح میانای برنامهسازی کاربردی پشتیبانیشده |
| drawText() | 18 |
| drawPosText() | 28 |
| drawTextOnPath() | 28 |
| شکلهای ساده | 17 |
| شکلهای پیچیده | 28 |
| drawPath() | 28 |
| لایه سایه | 28 |
اگر عملیات ترسیمی که به آن وابسته هستید شتاب سختافزاری ندارد، ترسیم تحتتأثیر قرارگرفته را در نرمافزار خارج از صفحه Bitmap (یا ImageBitmap) رندر کنید و نتیجه را ترسیم کنید. بقیه واسط کاربر شما مسیر شتاب سختافزاری را حفظ میکند.
نکتهها و ترفندها
رفتن به گرافیک دوبعدی با شتاب سختافزاری میتواند فوراً عملکرد را افزایش دهد، اما همچنان باید برنامهتان را طوری طراحی کنید که با دنبال کردن این توصیهها بهطور مؤثر از GPU استفاده کند:
- بهحداقل رساندن پیچیدگی چیدمان و بازترکیب
- درخت چیدمان را کمعمق نگه دارید و میزان بازترکیب را محدود کنید. خواندن وضعیت را به محدودترین دامنه موکول کنید تا تغییرات کوچکترین منطقه ممکن را بازترسیم کند. برای مثال، وضعیت پویانماییشده را بهجای بدنه composable، در
Modifier.graphicsLayer { }بخوانید. برای اطلاعات بیشتر، به عملکرد Jetpack Compose مراجعه کنید. - اجتناب از برداشت اضافی
- لایههای زیادی را روی هم نکشید. هر عنصر میانای کاربری را که کاملاً توسط عناصر مات دیگر در بالای آنها پوشانده شده است بردارید. اگر لازم است چند لایه را بهصورت ترکیبشده روی هم بکشید، آنها را در یک لایه ادغام کنید. یک قانون سرانگشتی خوب با سختافزار فعلی این است که بیشاز ۲٫۵ برابر تعداد پیکسلهای روی صفحه در هر فریم ترسیم نشود (پیکسلهای شفاف در بیتمپ حساب میشوند!).
- Don't create render objects in draw methods
- یک اشتباه رایج این است که هر بار که یک روش پردازش فراخوانی میشود، یک
Paintجدید یا یکPathجدید ایجاد شود. این کار باعث میشود زبالهروب بیشتر اجرا شود و همچنین حافظههای پنهان و بهینهسازیها را در خط لوله سختافزار دور میزند. برای جلوگیری از این اتفاق، از اشیای خود مجدداً استفاده کنید و آنها را تغییر دهید:- استفاده از روشهای استاندارد: روشهای استاندارد
DrawScope(مانندdrawRectوdrawCircle) ازقبل اشیاءPaintرا بهطور داخلی و بدون نیاز به تخصیص توسعهدهنده مجدداً استفاده میکنند. - Mutate instead of re-allocating: When writing custom logic, use
path.rewindto clear an existingPathrather than instantiating a newPath. - حفظ کارآمد وضعیت: در داخل یک composable، اشیاء را یکبار بااستفاده از
remember { Path() }اختصاص دهید. اگر درحال ساختن افزونههای اصلاحکننده سفارشی با قابلیت استفاده مجدد هستید، یکModifier.Nodeسفارشی را بااستفاده ازDrawModifierNodeپیادهسازی کنید تا اشیاء را بدون ایجاد تخصیصهای تودهای جدید تخصیص دهید و مجدداً استفاده کنید.
- استفاده از روشهای استاندارد: روشهای استاندارد
- شکلها را خیلی زیاد اصلاح نکنید
- برای مثال، شکلها، مسیرها، و دایرههای پیچیده بااستفاده از ماسکهای بافت رندر میشوند. هربار که مسیری را ایجاد یا اصلاح میکنید، خط لوله سختافزار ماسک جدیدی ایجاد میکند که میتواند پرهزینه باشد.
- نقشههای بیتی را زیاد تغییر ندهید
- هر بار که محتوای یک نقشه بیتی را تغییر میدهید، دفعه بعد که آن را ترسیم میکنید، دوباره بهعنوان بافت GPU بارگذاری میشود.
- بااحتیاط از نسخه آلفا استفاده کنید
- وقتی بااستفاده از
Modifier.alphaیا Compose animation APIs یک composable را نیمهشفاف میکنید، معمولاً در میانگیر خارج از صفحه رندر میشود که نرخ پر کردن موردنیاز را دوبرابر میکند. برای جلوگیری از سربار میانگیر خارج از صفحه برای محتوای غیرهمپوشان،CompositingStrategy.ModulateAlphaرا تنظیم کنید. برای فراخوانیهای ترسیم تکی، آلفا را مستقیماً روی فرمان ترسیم (مانندcolor = Color.Red.copy(alpha = 0.5f)) بدون ایجاد لایه اعمال کنید.