شتاب سخت افزاری

خط لوله رندر دوبعدی اندروید از شتاب سخت‌افزاری پشتیبانی می‌کند، به این معنی که تمام عملیات ترسیم که روی بوم انجام می‌شوند از پردازنده گرافیکی (GPU) استفاده می‌کنند. به دلیل افزایش منابع مورد نیاز برای فعال کردن شتاب سخت‌افزاری، برنامه شما رم بیشتری مصرف خواهد کرد.

شتاب سخت‌افزاری به طور پیش‌فرض فعال است. اگر برنامه شما فقط از کامپوننت‌های استاندارد استفاده می‌کند، فعال کردن آن به صورت سراسری نباید هیچ اثر نامطلوبی روی طراحی داشته باشد. با این حال، از آنجا که شتاب سخت‌افزاری برای همه عملیات طراحی دوبعدی پشتیبانی نمی‌شود، فعال کردن آن ممکن است بر برخی از فراخوانی‌های طراحی سفارشی شما تأثیر بگذارد. مشکلات معمولاً خود را به صورت عناصر نامرئی، استثنائات یا پیکسل‌های رندر شده اشتباه نشان می‌دهند. برای رفع این مشکل، اندروید به شما این امکان را می‌دهد که شتاب سخت‌افزاری را در چندین سطح فعال یا غیرفعال کنید. به بخش کنترل شتاب سخت‌افزاری مراجعه کنید.

اگر برنامه شما طراحی سفارشی انجام می‌دهد، برنامه خود را روی دستگاه‌های سخت‌افزاری واقعی با شتاب‌دهنده سخت‌افزاری فعال آزمایش کنید تا هرگونه مشکلی را پیدا کنید. بخش پشتیبانی از عملیات طراحی، مشکلات شناخته‌شده با شتاب‌دهنده سخت‌افزاری و نحوه حل آنها را شرح می‌دهد.

همچنین به OpenGL با API های چارچوب مراجعه کنید.

کنترل شتاب سخت‌افزاری

شما می‌توانید شتاب سخت‌افزاری را در سطوح زیر کنترل کنید:

  • کاربرد
  • فعالیت
  • پنجره
  • قابل ترکیب

سطح برنامه

در فایل مانیفست اندروید خود، ویژگی زیر را به تگ <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
)

سطح قابل ترکیب

در Compose، هیچ سوئیچ جداگانه‌ای برای غیرفعال کردن شتاب سخت‌افزاری وجود ندارد.

برای رندر کردن یک composable در لایه خودش، از Modifier.graphicsLayer استفاده کنید. این به شما اجازه می‌دهد تا ویژگی‌های transform (مانند alpha ، scaleX ، scaleY ، translationX ، translationY ، rotationX ، rotationY ، rotationZ و transformOrigin ) بدون اجرای مجدد کد ترسیم composable تغییر کنند. برای بهترین عملکرد، همیشه از فرم lambda اصلاح‌کننده برای تنظیم این ویژگی‌ها استفاده کنید.

برای اعمال صریح یک بافر خارج از صفحه برای عملیات طراحی پیشرفته، مانند ترکیب سفارشی درون لایه، CompositingStrategy.Offscreen استفاده کنید. برای اطلاعات بیشتر، به Graphics modifiers مراجعه کنید.

اگر یک عملیات طراحی سفارشی دارید که اکیداً به رندر نرم‌افزاری نیاز دارد، می‌توانید با استفاده از AndroidView یک نمای قدیمی (legacy View) را میزبانی کنید و setLayerType(View.LAYER_TYPE_SOFTWARE, null) را روی آن نما فراخوانی کنید.

پشتیبانی از عملیات ترسیم

وقتی سخت‌افزار شتاب‌دهی می‌شود، خط لوله رندر دوبعدی از رایج‌ترین عملیات طراحی Canvas و همچنین بسیاری از عملیات کم‌استفاده‌تر پشتیبانی می‌کند. تمام عملیات طراحی که برای رندر برنامه‌هایی که با اندروید عرضه می‌شوند، composableهای استاندارد و جلوه‌های بصری پیشرفته رایج مانند بازتاب‌ها و بافت‌های کاشی‌کاری شده استفاده می‌شوند، پشتیبانی می‌شوند.

جدول زیر سطح پشتیبانی عملیات مختلف را در سطوح مختلف API شرح می‌دهد:

اولین سطح API پشتیبانی شده
بوم نقاشی
تابع drawBitmapMesh() (آرایه رنگ‌ها) ۱۸
رسم تصویر() ۲۳
تابع ()drawPosText ۱۶
رسم متن روی مسیر () ۱۶
رسم رئوس () ۲۹
تابع ()setDrawFilter ۱۶
کلیپ‌پَچ() ۱۸
کلیپ‌ریجن() ۱۸
clipRect(Region.Op.XOR) ۱۸
clipRect(اختلاف عملیات منطقه) ۱۸
clipRect(Region.Op.ReverseDifference) ۱۸
clipRect() با چرخش/پرسپکتیو ۱۸
رنگ
setAntiAlias() (برای متن) ۱۸
تابع ()setAntiAlias ​​(برای خطوط) ۱۶
تابع setFilterBitmap() ۱۷
تابع ()setLinearText
فیلتر ماسک تنظیم‌شده()
تابع ()setPathEffect (برای خطوط) ۲۸
setShadowLayer() (غیر از متن) ۲۸
تابع ()setStrokeCap ​​(برای خطوط) ۱۸
تابع ()setStrokeCap ​​(برای امتیازها) ۱۹
تابع setSubpixelText() ۲۸
ایکس‌فرمود
PorterDuff.Mode.DARKEN (فریم بافر) ۲۸
PorterDuff.Mode.LIGHTEN (فریم بافر) ۲۸
PorterDuff.Mode.OVERLAY (بافر فریم) ۲۸
سایه‌زن
ComposeShader درون ComposeShader ۲۸
شیدرهای هم نوع درون ComposeShader ۲۸
ماتریس محلی روی ComposeShader ۱۸

مقیاس‌بندی بوم

خط لوله رندر دوبعدی با شتاب‌دهنده سخت‌افزاری ابتدا برای پشتیبانی از ترسیم بدون مقیاس ساخته شد، و برخی از عملیات ترسیم در مقادیر مقیاس بالاتر کیفیت را به طور قابل توجهی کاهش می‌دهند. این عملیات به صورت بافت‌هایی که در مقیاس ۱.۰ ترسیم شده‌اند و توسط GPU تبدیل می‌شوند، پیاده‌سازی می‌شوند. با شروع از سطح API 28، تمام عملیات ترسیم می‌توانند بدون مشکل مقیاس‌بندی شوند.

جدول زیر نشان می‌دهد که چه زمانی پیاده‌سازی برای مدیریت صحیح مقیاس‌های بزرگ تغییر داده شده است:

عملیات ترسیم که باید مقیاس‌بندی شود اولین سطح API پشتیبانی شده
رسم متن () ۱۸
تابع ()drawPosText ۲۸
رسم متن روی مسیر () ۲۸
اشکال ساده ۱۷
اشکال پیچیده ۲۸
رسم مسیر () ۲۸
لایه سایه ۲۸

اگر عملیات ترسیمی که به آن وابسته هستید، از شتاب‌دهنده سخت‌افزاری پشتیبانی نمی‌کند، ترسیم آسیب‌دیده را در یک نرم‌افزار Bitmap (یا ImageBitmap ) خارج از صفحه نمایش رندر کنید و نتیجه را ترسیم کنید. بقیه رابط کاربری شما مسیر شتاب‌دهنده سخت‌افزاری را حفظ می‌کند.

نکات و ترفندها

تغییر به گرافیک دوبعدی شتاب‌دهنده سخت‌افزاری می‌تواند فوراً عملکرد را افزایش دهد، اما شما همچنان باید برنامه خود را طوری طراحی کنید که با پیروی از این توصیه‌ها، از GPU به طور مؤثر استفاده کند:

پیچیدگی طرح‌بندی و ترکیب‌بندی مجدد را به حداقل برسانید
درخت طرح‌بندی را کم‌عمق نگه دارید و تعداد recomposeها را محدود کنید. خواندن state را به محدودترین محدوده موکول کنید، به طوری که یک تغییر، کوچکترین ناحیه ممکن را دوباره ترسیم کند. برای مثال، animated state را داخل Modifier.graphicsLayer { } بخوانید نه در بدنه یک composable. برای اطلاعات بیشتر، به عملکرد Jetpack Compose مراجعه کنید.
از برداشت بیش از حد خودداری کنید
لایه‌های زیادی را روی هم نکشید. هر عنصر رابط کاربری که کاملاً توسط عناصر مات دیگر روی آنها پنهان شده است را حذف کنید. اگر نیاز دارید چندین لایه را که روی هم ترکیب شده‌اند رسم کنید، ادغام آنها را در یک لایه واحد در نظر بگیرید. یک قانون کلی خوب با سخت‌افزار فعلی این است که بیش از ۲.۵ برابر تعداد پیکسل‌های روی صفحه در هر فریم رسم نکنید (پیکسل‌های شفاف در یک بیت‌مپ حساب می‌شوند!).
اشیاء رندر را در متدهای رسم ایجاد نکنید
یک اشتباه رایج این است که هر بار که یک متد رندر فراخوانی می‌شود، یک Paint یا Path جدید ایجاد می‌شود. این کار باعث می‌شود که garbage collector بیشتر اجرا شود و همچنین از cacheها و بهینه‌سازی‌ها در خط لوله سخت‌افزاری جلوگیری می‌کند. برای جلوگیری از این، اشیاء خود را دوباره استفاده کنید و تغییر دهید:
  • استفاده از متدهای استاندارد : متدهای استاندارد DrawScope (مانند drawRect و drawCircle ) از قبل و بدون نیاز به تخصیص منابع توسط توسعه‌دهنده، اشیاء Paint را به صورت داخلی دوباره استفاده می‌کنند.
  • تغییر به جای تخصیص مجدد : هنگام نوشتن منطق سفارشی، path.rewind برای پاک کردن یک Path موجود به جای نمونه‌سازی یک Path جدید استفاده کنید.
  • حالت را به طور موثر نگه دارید : درون یک composable، اشیاء را یک بار با استفاده از remember { Path() } اختصاص دهید. اگر در حال ساخت افزونه‌های اصلاح‌کننده سفارشی قابل استفاده مجدد هستید، یک Modifier.Node سفارشی را با استفاده از DrawModifierNode پیاده‌سازی کنید تا اشیاء را بدون ایجاد تخصیص‌های جدید هیپ، اختصاص داده و دوباره استفاده کنید.
شکل‌ها را زیاد تغییر ندهید
برای مثال، اشکال، مسیرها و دایره‌های پیچیده با استفاده از ماسک‌های بافت رندر می‌شوند. هر بار که مسیری را ایجاد یا اصلاح می‌کنید، خط لوله سخت‌افزاری یک ماسک جدید ایجاد می‌کند که می‌تواند پرهزینه باشد.
بیت‌مپ‌ها را زیاد تغییر ندهید
هر بار که محتوای یک بیت‌مپ را تغییر می‌دهید، دفعه‌ی بعد که آن را رسم می‌کنید، دوباره به عنوان یک بافت GPU آپلود می‌شود.
با احتیاط از آلفا استفاده کنید
وقتی با استفاده از APIهای انیمیشن Modifier.alpha یا Compose یک انیمیشن شفاف قابل ترکیب ایجاد می‌کنید، معمولاً در یک بافر خارج از صفحه رندر می‌شود که نرخ پر شدن مورد نیاز را دو برابر می‌کند. برای جلوگیری از سربار بافر خارج از صفحه برای محتوای غیر همپوشانی، CompositingStrategy.ModulateAlpha را تنظیم کنید. برای فراخوانی‌های ترسیم تکی، آلفا را مستقیماً به دستور ترسیم اعمال کنید (مانند color = Color.Red.copy(alpha = 0.5f) ) بدون ایجاد لایه.

منابع اضافی

محتوا را مشاهده می‌کند