Bermigrasi dari RenderScript

RenderScript API tidak digunakan lagi mulai Android 12. Produsen perangkat dan komponen telah berhenti menyediakan dukungan akselerasi hardware, dan dukungan RenderScript diperkirakan akan dihapus sepenuhnya dalam rilis mendatang.

Performa C/C++ mungkin cukup untuk banyak kasus penggunaan, dan jika Anda hanya mengandalkan RenderScript untuk intrinsik, Anda dapat mengganti penggunaan tersebut dengan Toolkit Penggantian Intrinsik RenderScript yang lebih mudah digunakan dan berpotensi meningkatkan performa 2x lipat.

Jika Anda perlu memanfaatkan akselerasi GPU secara optimal, sebaiknya migrasikan skrip Anda ke Vulkan. Opsi akselerasi lainnya mencakup memigrasikan skrip ke OpenGL, menggunakan Operasi gambar berbasis kanvas, atau memanfaatkan Android Graphics Shading Language (AGSL).

Setelah RenderScript tidak digunakan lagi di platform Android, dukungan untuk RenderScript akan dihapus dalam plugin Android Gradle. Mulai plugin Android Gradle 7.2, API RenderScript tidak digunakan lagi. API tersebut tetap berfungsi, tetapi menimbulkan peringatan. Versi AGP mendatang tidak akan lagi menyertakan dukungan Renderscript. Panduan ini menjelaskan cara melakukan migrasi dari RenderScript.

Bermigrasi dari intrinsik

Meskipun fungsi intrinsik RenderScript akan terus berfungsi setelah penghentian RenderScript, fungsi tersebut hanya dapat dijalankan pada CPU, bukan GPU.

Untuk beberapa operasi ini, ada opsi yang lebih efisien yang kini terintegrasi ke dalam platform atau di library Jetpack.

Operasi gambar bawaan yang dipercepat

Platform Android mendukung operasi pemrosesan gambar yang dipercepat dan dapat diterapkan ke gambar, terlepas dari intrinsik RenderScript. Contohnya mencakup:

  • Penggabungan
  • Pemburaman
  • Matriks warna
  • Perubahan ukuran

Pemburaman gambar di Android 12+ ke Tampilan

RenderEffect dengan dukungan untuk pemburaman ditambahkan ke Android 12, level API 31, yang memungkinkan Anda memburamkan RenderNode. RenderNode adalah konstruksi daftar tampilan yang digunakan Android untuk membantu mempercepat grafis platform.

Android menyediakan pintasan untuk menerapkan efek ke RenderNode yang terkait dengan View. Untuk memburamkan View, panggil View.setRenderEffect():

val blurRenderEffect = RenderEffect.createBlurEffect(radius, radius,
        Shader.TileMode.MIRROR
    )
view.setRenderEffect(blurRenderEffect)

Pemburaman gambar di Android 12+ yang dirender menjadi Bitmap

Jika Anda ingin gambar yang diburamkan dirender menjadi Bitmap, framework mendukung rendering yang dipercepat dengan HardwareRenderer yang didukung oleh HardwareBuffer. Kode berikut membuat HardwareRenderer, RenderNode, dan RenderEffect untuk pemburaman:

val imageReader = ImageReader.newInstance(
    bitmap.width, bitmap.height,
    PixelFormat.RGBA_8888, numberOfOutputImages,
    HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT
)
val renderNode = RenderNode("BlurEffect")
val hardwareRenderer = HardwareRenderer()

hardwareRenderer.setSurface(imageReader.surface)
hardwareRenderer.setContentRoot(renderNode)
renderNode.setPosition(0, 0, imageReader.width, imageReader.height)
val blurRenderEffect = RenderEffect.createBlurEffect(
    radius, radius,
    Shader.TileMode.MIRROR
)
renderNode.setRenderEffect(blurRenderEffect)

Untuk menerapkan efek, Anda harus menggunakan RecordingCanvas internal untuk RenderNode. Kode berikut merekam gambar, membuat permintaan render, lalu menunggu permintaan selesai:

val renderCanvas = it.renderNode.beginRecording()
renderCanvas.drawBitmap(it.bitmap, 0f, 0f, null)
renderNode.endRecording()
hardwareRenderer.createRenderRequest()
    .setWaitForPresent(true)
    .syncAndDraw()

Gambar yang dirender berada di HardwareBuffer yang terkait dengan ImageReader. Kode berikut memperoleh Image dan menampilkan Bitmap yang menggabungkan HardwareBuffer-nya.

val image = imageReader.acquireNextImage() ?: throw RuntimeException("No Image")
val hardwareBuffer = image.hardwareBuffer ?: throw RuntimeException("No HardwareBuffer")
val bitmap = Bitmap.wrapHardwareBuffer(hardwareBuffer, null)
    ?: throw RuntimeException("Create Bitmap Failed")

Kode berikut akan melakukan pembersihan setelah merender gambar. Perhatikan bahwa ImageReader, RenderNode, RenderEffect, dan HardwareRenderer dapat digunakan untuk memproses beberapa gambar.

hardwareBuffer.close()
image.close()
imageReader.close()
renderNode.discardDisplayList()
hardwareRenderer.destroy()

AGSL untuk pemrosesan gambar

Android Graphics Shading Language (AGSL) digunakan oleh Android 13+ untuk menentukan perilaku objek RuntimeShader yang dapat diprogram. AGSL berbagi banyak sintaksisnya dengan shader fragmen GLSL, tetapi berfungsi dalam sistem rendering grafis Android untuk menyesuaikan proses menggambar dalam Canvas sekaligus memfilter konten View. Hal ini dapat digunakan untuk menambahkan pemrosesan gambar kustom selama operasi gambar, atau dengan menggunakan RenderNode secara langsung untuk merender gambar menjadi kanvas Bitmap. Contoh berikut menunjukkan cara menerapkan shader kustom untuk mengganti efek pemburaman gambar.

Mulai dengan membuat RuntimeShader, membuat instance-nya dengan kode shader AGSL. Shader ini digunakan untuk menerapkan matriks warna untuk rotasi hue:

val hueShader = RuntimeShader("""
    uniform float2 iResolution;       // Viewport resolution (pixels)
    uniform float2 iImageResolution;  // iImage1 resolution (pixels)
    uniform float iRadian;            // radian to rotate things around
    uniform shader iImage1;           // An input image
    half4 main(float2 fragCoord) {
    float cosR = cos(iRadian);
    float sinR = sin(iRadian);
        mat4 hueRotation =
        mat4 (
                0.299 + 0.701 * cosR + 0.168 * sinR, //0
                0.587 - 0.587 * cosR + 0.330 * sinR, //1
                0.114 - 0.114 * cosR - 0.497 * sinR, //2
                0.0,                                 //3
                0.299 - 0.299 * cosR - 0.328 * sinR, //4
                0.587 + 0.413 * cosR + 0.035 * sinR, //5
                0.114 - 0.114 * cosR + 0.292 * sinR, //6
                0.0,                                 //7
                0.299 - 0.300 * cosR + 1.25 * sinR,  //8
                0.587 - 0.588 * cosR - 1.05 * sinR,  //9
                0.114 + 0.886 * cosR - 0.203 * sinR, //10
                0.0,                                 //11
                0.0, 0.0, 0.0, 1.0 );                //12,13,14,15
        float2 scale = iImageResolution.xy / iResolution.xy;
        return iImage1.eval(fragCoord * scale)*hueRotation;
    }
""")

Shader dapat diterapkan ke RenderNode, seperti RenderEffect lainnya. Contoh berikut menunjukkan cara menyetel seragam di hueShader:

hueShader.setFloatUniform("iImageResolution", bitmap.width.toFloat(),
    bitmap.height.toFloat())
hueShader.setFloatUniform("iResolution", bitmap.width.toFloat(),
    bitmap.height.toFloat())
hueShader.setFloatUniform("iRadian", radian)
hueShader.setInputShader( "iImage1", BitmapShader(bitmap, Shader.TileMode.MIRROR,
    Shader.TileMode.MIRROR))
val colorFilterEffect = RenderEffect.createShaderEffect(it.hueShader)
renderNode.setRenderEffect(colorFilterEffect)

Untuk mendapatkan Bitmap, teknik yang sama digunakan seperti pada contoh pemburaman gambar sebelumnya.

  • RecordingCanvas internal untuk RenderNode menerapkan shader.
  • Image diperoleh, menampilkan Bitmap yang menggabungkan HardwareBuffer.

Mengonversi dari YUV planar ke RGB menggunakan CameraX

Konversi dari YUV planar ke RGB untuk digunakan dalam pemrosesan gambar didukung sebagai bagian dari kasus penggunaan ImageAnalysis dalam CameraX Jetpack.

Ada resource tentang penggunaan ImageAnalysis sebagai bagian dari codelab Mulai Menggunakan CameraX dan di repositori contoh kamera Android.

Toolkit pengganti intrinsik Renderscript

Jika aplikasi Anda menggunakan intrinsik, Anda dapat menggunakan library pengganti mandiri; pengujian kami menunjukkan bahwa penggunaan library ini lebih cepat daripada implementasi CPU RenderScript yang sudah ada.

Toolkit ini mencakup fungsi berikut:

  • Penggabungan
  • Pemburaman
  • Matriks warna
  • Konvolusi
  • Histogram dan histogramDot
  • Tabel pemeta (LUT) dan LUT 3D
  • Perubahan ukuran
  • YUV ke RGB

Untuk detail dan batasan selengkapnya, lihat file README.md dan Toolkit.kt .

Lakukan langkah-langkah berikut untuk mendownload, menambahkan, dan menggunakan library:

  1. Download project dari GitHub.

  2. Temukan dan buat renderscript-toolkit module.

  3. Tambahkan library ke project Android Studio dengan memodifikasi file build.gradle aplikasi.

  4. Panggil metode yang sesuai dari toolkit.

Contoh: bermigrasi dari fungsi ScriptIntrinsicBlur

Untuk mengganti fungsi ScriptIntrinsicBlur:

  • Untuk memburamkan bitmap, panggil Toolkit.blur.

    var blurredBitmap = Toolkit.blur(myBitmap, radius)
    
  • Jika Anda ingin memburamkan gambar yang diwakili oleh sederet byte, tentukan lebar, tinggi, dan jumlah byte per piksel.

    val outArray = Toolkit.blur(inputArray, bytesPerPixel, width, height, radius)
    

Bermigrasi dari skrip

Jika kasus penggunaan Anda tidak dapat diselesaikan dengan:

Selain itu, kasus penggunaan Anda dapat memanfaatkan akselerasi GPU. Android mendukung komputasi GPU pada API Vulkan dan OpenGL ES (GLES) lintas platform. Tindakan ini mungkin tidak diperlukan karena di sebagian besar perangkat, skrip Anda sudah berjalan di CPU, bukan GPU: C/C++ mungkin lebih cepat daripada komputasi RenderScript, GLES, atau Vulkan untuk beberapa kasus penggunaan. (atau setidaknya cukup cepat untuk kasus penggunaan Anda)

Untuk lebih memahami cara bermigrasi, tinjau aplikasi contoh. Contoh ini menunjukkan cara memburamkan bitmap dan mengonversi matriks warna di RenderScript, serta memiliki kode yang setara di Vulkan dan OpenGL.

Jika aplikasi Anda perlu mendukung berbagai rilis, gunakan RenderScript untuk perangkat yang menjalankan Android 6 (level API 23) dan yang lebih rendah, serta Vulkan atau GLES pada perangkat yang didukung dengan Android 7 (level API 24) dan yang lebih tinggi. Jika minSdkVersion adalah 24 atau lebih tinggi, Anda mungkin tidak perlu menggunakan RenderScript. Vulkan atau GLES 3.1 dapat digunakan di mana pun Anda memerlukan dukungan komputasi GPU.

Android menyediakan binding SDK untuk API GLES, sehingga tidak perlu menggunakan NDK saat menggunakan OpenGL ES.

Vulkan tidak menyediakan binding SDK, sehingga tidak ada pemetaan langsung dari RenderScript ke Vulkan. Anda harus menulis kode Vulkan menggunakan NDK dan membuat fungsi JNI untuk mengakses kode ini dari Kotlin atau Java.

Halaman berikut ini membahas aspek proses migrasi dari RenderScript. Aplikasi contoh mengimplementasikan hampir semua pertimbangan ini. Untuk lebih memahaminya, bandingkan kode RenderScript dan Vulkan yang setara.