Akselerasi hardware

Mulai dari Android 3.0 (API level 11), pipeline rendering 2D Android mendukung akselerasi hardware, yang berarti semua operasi gambar yang dilakukan di kanvas View akan menggunakan GPU. Karena perlu resource yang lebih tinggi untuk mengaktifkan akselerasi hardware, aplikasi Anda akan mengonsumsi lebih banyak RAM.

Akselerasi hardware diaktifkan secara default jika API level target Anda adalah >=14, tetapi juga dapat diaktifkan secara eksplisit. Jika aplikasi Anda hanya menggunakan tampilan standar dan Drawable, mengaktifkan akselerasi hardware secara global seharusnya tidak akan menimbulkan efek gambar yang merugikan. Namun, karena tidak semua operasi gambar 2D mendukung akselerasi hardware, mengaktifkan akselerasi hardware mungkin akan memengaruhi beberapa tampilan kustom atau panggilan gambar Anda. Masalah ini biasanya bermanifestasi sebagai elemen tak terlihat, pengecualian, atau piksel yang salah dirender. Untuk mengatasinya, Android menyediakan opsi untuk mengaktifkan atau menonaktifkan akselerasi hardware di beberapa tingkat. Lihat Mengontrol akselerasi hardware.

Jika aplikasi Anda menjalankan gambar kustom, uji aplikasi Anda pada hardware sebenarnya dengan akselerasi hardware diaktifkan untuk menemukan masalah. Bagian Dukungan untuk operasi gambar menjelaskan masalah umum akselerasi hardware dan cara mengatasinya.

Lihat juga OpenGL dengan Framework API dan Renderscript

Mengontrol akselerasi hardware

Anda dapat mengontrol akselerasi hardware di tingkat berikut:

  • Aplikasi
  • Aktivitas
  • Jendela
  • Lihat

Tingkat aplikasi

Dalam file manifes Android Anda, tambahkan atribut berikut ke tag <application> untuk mengaktifkan akselerasi hardware di seluruh aplikasi Anda:

<application android:hardwareAccelerated="true" ...>

Tingkat aktivitas

Jika aplikasi Anda tidak berperilaku sebagaimana mestinya saat akselerasi hardware diaktifkan secara global, Anda juga dapat mengontrolnya untuk aktivitas tertentu. Untuk mengaktifkan atau menonaktifkan akselerasi hardware di tingkat aktivitas, Anda dapat menggunakan atribut android:hardwareAccelerated untuk elemen <activity>. Contoh berikut mengaktifkan akselerasi hardware untuk keseluruhan aplikasi, tetapi menonaktifkannya untuk satu aktivitas:

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

Tingkat jendela

Jika memerlukan kontrol yang lebih terperinci, Anda dapat mengaktifkan akselerasi hardware untuk jendela tertentu dengan kode berikut:

Kotlin

window.setFlags(
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)

Java

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

Catatan: Saat ini Anda tidak dapat menonaktifkan akselerasi hardware di tingkat jendela.

Tingkat tampilan

Anda dapat menonaktifkan akselerasi hardware untuk setiap tampilan pada waktu proses dengan kode berikut:

Kotlin

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)

Java

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

Catatan: Saat ini Anda tidak dapat mengaktifkan akselerasi hardware di tingkat tampilan. Lapisan tampilan memiliki fungsi lain selain menonaktifkan akselerasi hardware. Lihat Lapisan tampilan untuk informasi lebih lanjut mengenai penggunaannya.

Menentukan apakah tampilan mengaktifkan akselerasi hardware

Terkadang, aplikasi perlu mengetahui apakah saat ini akselerasi hardware diaktifkan, terutama untuk hal-hal seperti tampilan kustom. Hal ini sangat berguna jika aplikasi Anda melakukan banyak operasi gambar kustom dan tidak semua operasi didukung dengan baik oleh pipeline rendering yang baru.

Ada dua cara untuk memeriksa apakah akselerasi hardware diaktifkan pada aplikasi:

Jika Anda harus menjalankan pemeriksaan ini dalam kode gambar, gunakan Canvas.isHardwareAccelerated(), bukan View.isHardwareAccelerated(), jika memungkinkan. Saat tampilan dihubungkan ke jendela dengan akselerasi hardware, tampilan tersebut masih dapat digambar menggunakan Canvas tanpa akselerasi hardware. Hal ini terjadi, misalnya, saat menggambar tampilan ke dalam bitmap untuk keperluan caching.

Model gambar Android

Jika akselerasi hardware diaktifkan, framework Android akan menggunakan model gambar baru yang memanfaatkan daftar tampilan untuk merender aplikasi Anda ke layar. Untuk memahami daftar tampilan dan pengaruhnya terhadap aplikasi, sebaiknya pahami juga cara Android menggambar tampilan tanpa akselerasi hardware. Bagian berikut menjelaskan model gambar berbasis software dan model gambar dengan akselerasi hardware.

Model gambar berbasis software

Dalam model gambar berbasis software, tampilan digambar dalam dua langkah berikut:

  1. Membatalkan hierarki
  2. Menggambar hierarki

Setiap kali perlu mengubah bagian UI-nya, aplikasi akan memanggil invalidate() (atau salah satu variannya) di tampilan mana saja yang kontennya telah berubah. Pesan pembatalan akan disebarkan ke seluruh hierarki tampilan untuk menghitung area layar yang perlu digambar ulang (area kotor). Selanjutnya, sistem Android akan menggambar tampilan di hierarki tersebut yang berpotongan dengan area kotor. Sayangnya, model gambar ini memiliki dua kelemahan:

  • Pertama, model ini memerlukan eksekusi banyak kode di setiap operasi gambar. Misalnya, jika aplikasi Anda memanggil invalidate() di sebuah tombol, dan tombol tersebut berada di atas tampilan lain, maka sistem Android perlu menggambar ulang tampilan meskipun tampilan itu sendiri belum berubah.
  • Masalah kedua adalah model gambar ini dapat menyembunyikan bug dalam aplikasi Anda. Karena sistem Android menggambar ulang tampilan saat berpotongan dengan area kotor, tampilan yang kontennya sudah Anda ubah mungkin akan digambar ulang sekalipun invalidate() tidak dipanggil di tampilan itu. Jika itu terjadi, Anda mengandalkan pembatalan tampilan lain untuk mendapatkan perilaku yang tepat. Perilaku ini dapat berubah setiap kali Anda memodifikasi aplikasi. Karena itu, Anda harus selalu memanggil invalidate() di tampilan kustom setiap kali Anda memodifikasi data atau status yang memengaruhi kode gambar tampilan tersebut.

Catatan: Tampilan Android otomatis memanggil invalidate() ketika propertinya berubah, seperti warna latar belakang atau teks dalam TextView.

Model gambar dengan akselerasi hardware

Sistem Android tetap menggunakan invalidate() dan draw() untuk meminta update layar dan merender tampilan, tetapi menangani operasi gambar yang sebenarnya dengan cara berbeda. Bukannya menjalankan perintah menggambar dengan seketika, sistem Android mencatatnya di dalam daftar tampilan, yang berisi output kode gambar hierarki tampilan tersebut. Pengoptimalan lainnya adalah sistem Android hanya perlu merekam dan memperbarui daftar display untuk tampilan yang ditandai sebagai area kotor oleh panggilan invalidate(). Tampilan yang belum dibatalkan dapat digambar ulang cukup dengan menerbitkan kembali daftar tampilan yang telah direkam sebelumnya. Model gambar baru ini terdiri dari tiga tahap:

  1. Membatalkan hierarki
  2. Merekam dan memperbarui daftar display
  3. Menggambar daftar tampilan

Dengan model ini, Anda tidak dapat mengandalkan tampilan yang berpotongan dengan area kotor untuk menjalankan metode draw()-nya. Untuk memastikan sistem Android mencatat daftar tampilan suatu tampilan, Anda harus memanggil invalidate(). Jika Anda tidak melakukannya, tampilan akan terlihat sama meskipun telah diubah.

Penggunaan daftar display juga menunjang performa animasi karena penetapan properti tertentu, seperti alfa atau rotasi, tidak memerlukan pembatalan tampilan yang ditargetkan (hal tersebut dilakukan otomatis). Pengoptimalan ini juga berlaku pada tampilan dengan daftar tampilan (tampilan ketika aplikasi mengaktifkan akselerasi hardware.) Misalnya, anggaplah terdapat sebuah LinearLayout yang berisi ListView di atas sebuah Button. Daftar display untuk LinearLayout akan terlihat seperti ini:

  • DrawDisplayList(ListView)
  • DrawDisplayList(Button)

Sekarang asumsikan Anda ingin mengubah opasitas ListView. Setelah memanggil setAlpha(0.5f) di ListView, daftar display sekarang berisi:

  • SaveLayerAlpha(0.5)
  • DrawDisplayList(ListView)
  • Restore
  • DrawDisplayList(Button)

Kode gambar kompleks di ListView tidak dieksekusi. Sebagai gantinya, sistem hanya memperbarui daftar tampilan LinearLayout yang jauh lebih sederhana. Dalam aplikasi yang tidak mengaktifkan akselerasi hardware, kode gambar baik untuk daftar maupun induknya akan dieksekusi lagi.

Dukungan untuk operasi gambar

Jika akselerasi hardware diaktifkan, pipeline rendering 2D mendukung operasi gambar Canvas yang paling umum digunakan, serta banyak operasi lain yang jarang digunakan. Semua operasi gambar yang digunakan untuk merender aplikasi yang disertakan dengan Android, widget dan tata letak default, serta efek visual lanjutan yang umum seperti pantulan dan tekstur berubin, juga didukung.

Tabel berikut menjelaskan tingkat dukungan berbagai operasi di tingkat API:

Level API pertama yang didukung
Canvas
drawBitmapMesh() (array warna) 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() dengan rotasi/perspektif 18
Paint
setAntiAlias() (untuk teks) 18
setAntiAlias() (untuk garis) 16
setFilterBitmap() 17
setLinearText()
setMaskFilter()
setPathEffect() (untuk garis) 28
setShadowLayer() (selain teks) 28
setStrokeCap() (untuk garis) 18
setStrokeCap() (untuk titik) 19
setSubpixelText() 28
Xfermode
PorterDuff.Mode.DARKEN (framebuffer) 28
PorterDuff.Mode.LIGHTEN (framebuffer) 28
PorterDuff.Mode.OVERLAY (framebuffer) 28
Shader
ComposeShader di dalam ComposeShader 28
Shader jenis yang sama di dalam ComposeShader 28
Matriks lokal di ComposeShader 18

Penskalaan kanvas

Pipeline rendering 2D dengan akselerasi hardware mula-mula dibuat untuk mendukung gambar yang tidak diskalakan, karena beberapa operasi gambar mengalami penurunan kualitas secara signifikan pada nilai skala lebih tinggi. Operasi ini diimplementasikan saat tekstur yang digambar pada skala 1.0 diubah oleh GPU. Mulai API level 28, semua operasi gambar dapat diskalakan tanpa masalah.

Tabel berikut menunjukkan kapan implementasi diubah untuk menangani skala besar dengan benar:
Operasi gambar yang akan diskalakan Level API pertama yang didukung
drawText() 18
drawPosText() 28
drawTextOnPath() 28
Bentuk Sederhana* 17
Bentuk Kompleks* 28
drawPath() 28
Lapisan bayangan 28

Catatan: Bentuk 'Sederhana' adalah perintah drawRect(), drawCircle(), drawOval(), drawRoundRect(), dan drawArc() (dengan useCenter=false) yang diterbitkan dengan Paint yang tidak memiliki PathEffect, dan tidak berisi gabungan non-default (via setStrokeJoin()/ setStrokeMiter()). Contoh lain dari perintah gambar ini termasuk dalam 'Kompleks', dalam diagram di atas.

Jika aplikasi Anda terpengaruh oleh salah satu fitur atau batasan yang hilang ini, Anda dapat menonaktifkan akselerasi hardware hanya untuk bagian aplikasi yang terpengaruh dengan memanggil setLayerType(View.LAYER_TYPE_SOFTWARE, null). Dengan begitu, Anda masih dapat memanfaatkan akselerasi hardware di tempat lain. Lihat Mengontrol akselerasi hardware untuk informasi selengkapnya tentang cara mengaktifkan dan menonaktifkan akselerasi hardware di berbagai tingkat dalam aplikasi Anda.

Lapisan tampilan

Di semua versi Android, tampilan dapat dirender menjadi buffer luar-layar, baik menggunakan cache gambar tampilan, atau menggunakan Canvas.saveLayer(). Buffer luar-layar, atau lapisan, memiliki beberapa kegunaan. Anda dapat menggunakannya untuk memperoleh performa yang lebih baik saat menganimasikan tampilan yang kompleks atau untuk menerapkan efek komposisi. Misalnya, Anda dapat mengimplementasikan efek fade menggunakan Canvas.saveLayer() untuk merender tampilan menjadi lapisan untuk sementara, lalu menyusunnya kembali di layar dengan faktor opasitas.

Mulai dari Android 3.0 (API level 11), Anda dapat menggunakan metode View.setLayerType() untuk mengontrol bagaimana dan kapan lapisan digunakan. API ini memerlukan dua parameter: jenis lapisan yang ingin digunakan dan objek Paint opsional yang menjelaskan bagaimana lapisan harus disusun. Anda dapat menggunakan parameter Paint untuk menerapkan filter warna, mode penggabungan khusus, atau opasitas ke sebuah lapisan. Sebuah tampilan dapat menggunakan salah satu dari tiga jenis lapisan:

  • LAYER_TYPE_NONE: Tampilan dirender secara normal dan tidak ditunjang oleh buffering off-screen. Ini merupakan perilaku default.
  • LAYER_TYPE_HARDWARE: Tampilan dirender di hardware menjadi tekstur hardware, jika aplikasi mengaktifkan akselerasi hardware. Jika aplikasi tidak mengaktifkan akselerasi hardware, jenis lapisan ini berperilaku sama seperti LAYER_TYPE_SOFTWARE.
  • LAYER_TYPE_SOFTWARE: Tampilan dirender di software menjadi bitmap.

Jenis lapisan yang Anda gunakan bergantung pada sasaran Anda:

  • Performa: Gunakan jenis lapisan hardware untuk merender tampilan menjadi tekstur hardware. Setelah tampilan dirender menjadi lapisan, kode gambarnya tidak perlu dijalankan sampai tampilan tersebut memanggil invalidate(). Beberapa animasi, seperti animasi alfa, dapat langsung diterapkan ke lapisan, yang sangat efisien jika ditangani oleh GPU.
  • Efek visual: Gunakan jenis lapisan hardware atau software dan Paint untuk menerapkan perlakuan visual khusus ke tampilan. Misalnya, Anda dapat menggambar tampilan secara hitam putih menggunakan ColorMatrixColorFilter.
  • Kompatibilitas: Gunakan jenis lapisan software untuk memaksa tampilan dirender di software. Jika tampilan yang diakselerasi dengan hardware (misalnya, jika seluruh aplikasi Anda diakselerasi dengan hardware) mengalami masalah rendering, ini merupakan cara mudah untuk mengatasi keterbatasan pipeline rendering hardware.

Lapisan tampilan dan animasi

Lapisan hardware dapat menghasilkan animasi yang lebih cepat dan lebih halus jika aplikasi Anda menggunakan akselerasi hardware. Menjalankan animasi pada kecepatan 60 frame per detik tidak selalu dimungkinkan saat menganimasikan tampilan kompleks yang menghasilkan banyak operasi gambar. Hal ini dapat diatasi dengan menggunakan lapisan hardware untuk merender tampilan menjadi tekstur hardware. Selanjutnya, tekstur hardware dapat digunakan untuk menganimasikan tampilan, sehingga tampilan tidak perlu terus-menerus menggambar ulang dirinya sendiri saat sedang dianimasikan. Tampilan tidak digambar ulang kecuali jika Anda mengubah yang memanggil invalidate(), atau jika Anda memanggil invalidate() secara manual. Jika Anda menjalankan animasi dalam aplikasi dan hasilnya tidak sehalus yang diinginkan, cobalah mengaktifkan lapisan hardware di animasi tampilan.

Jika tampilan ditunjang oleh lapisan hardware, beberapa propertinya akan ditangani saat lapisan disusun di layar. Menyetel properti ini juga merupakan langkah yang efisien karena dengan demikian tampilan tidak perlu dibatalkan dan digambar ulang. Daftar properti berikut memengaruhi penyusunan lapisan. Memanggil setter untuk salah satu properti ini akan menghasilkan pembatalan yang optimal dan meniadakan keharusan untuk menggambar ulang tampilan yang ditargetkan:

  • alpha: Mengubah opasitas lapisan
  • x, y, translationX, translationY: Mengubah posisi lapisan
  • scaleX, scaleY: Mengubah ukuran lapisan
  • rotation, rotationX, rotationY: Mengubah orientasi lapisan dalam ruang 3D
  • pivotX, pivotY: Mengubah asal transformasi lapisan

Properti ini adalah nama yang digunakan saat menganimasikan tampilan dengan ObjectAnimator. Jika Anda ingin mengakses properti ini, panggil setter atau getter yang sesuai. Misalnya, untuk mengubah properti alfa, panggil setAlpha(). Cuplikan kode berikut menunjukkan cara paling efisien untuk memutar tampilan secara 3D di seputar sumbu Y:

Kotlin

view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
ObjectAnimator.ofFloat(view, "rotationY", 180f).start()

Java

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator.ofFloat(view, "rotationY", 180).start();

Karena lapisan hardware menggunakan memori video, sebaiknya Anda mengaktifkannya hanya selama durasi animasi, lalu menonaktifkannya setelah animasi selesai. Anda dapat melakukannya melalui pemroses animasi:

Kotlin

view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
ObjectAnimator.ofFloat(view, "rotationY", 180f).apply {
    addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator) {
            view.setLayerType(View.LAYER_TYPE_NONE, null)
        }
    })
    start()
}

Java

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});
animator.start();

Untuk informasi selengkapnya tentang animasi properti, lihat Animasi properti.

Tips dan trik

Peralihan ke grafis 2D dengan akselerasi hardware dapat meningkatkan performa dengan seketika, tetapi Anda tetap harus mendesain aplikasi agar menggunakan GPU secara efektif dengan mengikuti rekomendasi berikut:

Kurangi jumlah tampilan di aplikasi Anda
Semakin banyak tampilan yang harus digambar oleh sistem, semakin lambat pemuatannya. Hal ini juga berlaku untuk pipeline rendering software. Mengurangi jumlah tampilan adalah salah satu cara termudah untuk mengoptimalkan UI Anda.
Hindari overdraw
Jangan menggambar terlalu banyak lapisan yang bertumpuk satu di atas yang lain. Hapus tampilan yang sepenuhnya terhalang oleh tampilan buram lainnya di atasnya. Jika Anda perlu menggambar beberapa lapisan yang dipadukan satu di atas yang lain, pertimbangkan untuk menggabungkannya menjadi satu lapisan. Salah satu aturan praktis terkait hardware saat ini adalah jangan menggambar lebih dari 2,5 kali lipat jumlah piksel di layar per frame (piksel transparan dalam jumlah bitmap!).
Jangan membuat objek render dalam metode gambar
Kesalahan yang umum adalah membuat Paint baru atau Path baru setiap kali metode rendering dipanggil. Hal ini memaksa pembersih sampah memori untuk berjalan lebih sering dan juga mengabaikan cache dan pengoptimalan di pipeline hardware.
Jangan terlalu sering mengubah bentuk
Bentuk, jalur, dan lingkaran yang kompleks, misalnya, dirender menggunakan mask tekstur. Setiap kali Anda membuat atau mengubah jalur, pipeline hardware akan membuat mask baru, dan ini dapat memerlukan banyak biaya.
Jangan terlalu sering mengubah bitmap
Setiap kali Anda mengubah konten bitmap, bitmap akan diupload lagi sebagai tekstur GPU saat Anda menggambarnya lagi.
Gunakan alfa dengan hati-hati
Saat Anda membuat tampilan transparan menggunakan setAlpha(), AlphaAnimation, atau ObjectAnimator, tampilan akan dirender di buffer luar-layar yang menggandakan rasio pengisian yang diperlukan. Saat menerapkan alfa di tampilan yang sangat besar, sebaiknya setel jenis lapisan tampilan ke LAYER_TYPE_HARDWARE.