Akselerasi hardware (Penayangan)

Konsep dan penerapan Jetpack Compose

Mulai dari Android 3.0 (level API 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 level API Target Anda adalah >=14, tetapi juga dapat diaktifkan secara eksplisit. Jika aplikasi Anda hanya menggunakan tampilan standar dan Drawable, mengaktifkannya secara global tidak akan menyebabkan efek gambar yang merugikan. Namun, karena akselerasi hardware tidak didukung untuk semua operasi gambar 2D, mengaktifkannya mungkin akan memengaruhi beberapa tampilan kustom atau panggilan gambar Anda. Masalah 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 terkait 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);

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);

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 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, ada dua kelemahan pada model gambar ini:

  • 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. Oleh karena itu, Anda harus selalu memanggil invalidate di tampilan kustom setiap kali Anda memodifikasi data atau status yang memengaruhi kode gambar tampilan tersebut.

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. Bukanya menjalankan perintah gambar 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 tampilan untuk tampilan yang ditandai sebagai area kotor oleh panggilan invalidate. Tampilan yang belum dibatalkan dapat digambar ulang 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 bahwa sistem Android mencatat daftar tampilan suatu tampilan, Anda harus memanggil invalidate. Jika Anda lupa melakukannya, tampilan akan terlihat sama meskipun telah diubah.

Penggunaan daftar tampilan 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 untuk tampilan dengan daftar tampilan (tampilan ketika aplikasi Anda diakselerasi dengan hardware.) Misalnya, anggaplah ada LinearLayout yang berisi ListView di atas Button. Daftar tampilan 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 level API 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

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 mengetahui 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 pudar menggunakan Canvas.saveLayer untuk merender tampilan menjadi lapisan untuk sementara, lalu menyusunnya kembali di layar dengan faktor opasitas.

Mulai dari Android 3.0 (level API 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 buffer luar-layar. 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 hingga 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 properti tampilan, yang akan 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 tampilan teranimasi.

Jika tampilan ditunjang oleh lapisan hardware, beberapa propertinya akan ditangani saat lapisan disusun di layar. Menetapkan properti ini juga merupakan langkah yang efisien karena dengan demikian tampilan tidak perlu dibatalkan dan digambar ulang. Berikut adalah daftar properti yang memengaruhi cara lapisan disusun. 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.