Esegui la migrazione da RenderScript

Le API RenderScript sono deprecate a partire da Android 12. I produttori di dispositivi e componenti hanno già smesso di fornire supporto per l'accelerazione hardware e il supporto di RenderScript verrà rimosso completamente in una versione futura.

Le prestazioni di C/C++ possono essere adeguate per molti casi d'uso e, se facevi affidamento solo su RenderScript per funzionalità intrinseche, puoi sostituire questi utilizzi con il Toolkit per la sostituzione di RenderScript Intrinsics, che è più facile da utilizzare e offre un potenziale miglioramento delle prestazioni raddoppiato.

Se hai bisogno di sfruttare appieno l'accelerazione GPU, ti consigliamo di eseguire la migrazione degli script a Vulkan. Altre opzioni accelerate includono la migrazione degli script a OpenGL, l'utilizzo di operazioni per le immagini basate su Canvas o l'utilizzo di Android Graphics Shading Language (AGSL).

In seguito al ritiro di RenderScript sulla piattaforma Android, il supporto per RenderScript verrà rimosso dal plug-in Android per Gradle. A partire dal plug-in Android per Gradle 7.2, le API RenderScript sono deprecate. Continuano a funzionare, ma richiamano avvisi. Le versioni future di AGP non includeranno più il supporto di Renderscript. Questa guida spiega come eseguire la migrazione da RenderScript.

Esegui la migrazione dalle funzionalità intrinseche

Sebbene le funzioni intrinseche di RenderScript continueranno a funzionare dopo il ritiro di RenderScript, potrebbero essere eseguite solo sulla CPU anziché sulla GPU.

Per alcune di queste operazioni, ora esistono opzioni più efficienti integrate nella piattaforma o nelle librerie Jetpack.

Operazioni integrate sulle immagini accelerate

La piattaforma Android supporta operazioni di elaborazione accelerata delle immagini che possono essere applicate alle immagini, indipendentemente dalle funzioni intrinseche di RenderScript. Tra gli esempi possibili:

  • Mix
  • Sfocatura
  • Matrice dei colori
  • Ridimensiona

Sfocatura delle immagini su Android 12 e versioni successive in una visualizzazione

RenderEffect con supporto per la sfocatura è stato aggiunto ad Android 12, livello API 31, per consentirti di sfocare un RenderNode. RenderNode è un costrutto dell'elenco di visualizzazione utilizzato da Android per accelerare la grafica della piattaforma.

Android fornisce una scorciatoia per applicare un effetto al RenderNode associato a un View. Per sfocare un View, chiama View.setRenderEffect():

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

Sfocatura immagine su Android 12 e versioni successive visualizzata in una bitmap

Se vuoi che l'immagine sfocata venga visualizzata in un elemento Bitmap, il framework supporta il rendering accelerato con un elemento HardwareRenderer supportato da HardwareBuffer. Il seguente codice crea HardwareRenderer, RenderNode e RenderEffect per la sfocatura:

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)

L'applicazione dell'effetto prevede l'utilizzo del RecordingCanvas interno per RenderNode. Il seguente codice registra il disegno, crea la richiesta di rendering e attende il completamento della richiesta:

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

L'immagine visualizzata è in una HardwareBuffer associata all'elemento ImageReader. Il seguente codice acquisisce Image e restituisce un Bitmap che esegue il wrapping del relativo HardwareBuffer.

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

Il seguente codice esegue la pulizia dopo il rendering dell'immagine. Tieni presente che ImageReader, RenderNode, RenderEffect e HardwareRenderer possono essere utilizzati per elaborare più immagini.

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

AGSL per l'elaborazione delle immagini

L'Android Graphics Shading Language (AGSL) viene utilizzato da Android 13 e versioni successive per definire il comportamento degli oggetti RuntimeShader programmabili. AGSL condivide gran parte della sua sintassi con gli ombreggiatori dei frammenti GLSL, ma funziona all'interno del sistema di rendering della grafica Android per personalizzare la pittura all'interno di Canvas e filtrare i contenuti View. Può essere utilizzata per aggiungere un'elaborazione personalizzata di immagini durante le operazioni di disegno o utilizzando RenderNode direttamente per eseguire il rendering di un'immagine in un canvas Bitmap. L'esempio seguente mostra come applicare uno scuratore personalizzato per sostituire l'effetto di sfocatura dell'immagine.

Per iniziare, crea un valore RuntimeShader, creando un'istanza con il codice mesh AGSL. Questo ombreggiatore viene utilizzato per applicare una matrice dei colori per la rotazione delle tonalità:

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

Lo Shader può essere applicato a un RenderNode, come qualsiasi altro RenderEffect. L'esempio seguente mostra come impostare le uniformi in 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)

Per ottenere Bitmap, viene utilizzata la stessa tecnica del precedente campione di sfocatura dell'immagine.

  • L'elemento RecordingCanvas interno per RenderNode applica lo meshr.
  • L'oggetto Image viene acquisito, restituendo un Bitmap che aggrega il suo HardwareBuffer.

Converti da YUV planare a RGB utilizzando CameraX

La conversione da YUV planare a RGB per l'utilizzo nell'elaborazione delle immagini è supportata nell'ambito del caso d'uso di ImageAnalysis all'interno di CameraX di Jetpack.

Sono disponibili risorse sull'utilizzo di ImageAnalysis nell'ambito del codelab Getting Started with CameraX e nel repository Esempi delle videocamere Android.

Il toolkit di sostituzione intrinseco di Renderscript

Se la tua applicazione utilizza funzionalità intrinseche, puoi utilizzare la libreria sostitutiva autonoma; i nostri test indicano che è più veloce rispetto all'utilizzo dell'implementazione esistente della CPU RenderScript.

Il toolkit include le seguenti funzioni:

  • Mix
  • Sfocatura
  • Matrice colore
  • Convolvere
  • Istogram e istogramDot
  • Tabella di ricerca (LUT) e LUT 3D
  • Ridimensiona
  • Da YUV a RGB

Per informazioni dettagliate e limitazioni, vedi README.md e Toolkit.kt del toolkit. di copertina.

Per scaricare, aggiungere e utilizzare la libreria, procedi nel seguente modo:

  1. Scarica il progetto da GitHub.

  2. Individua e crea renderscript-toolkit module.

  3. Aggiungi la libreria al progetto Android Studio modificando il file build.gradle dell'app.

  4. Richiama il metodo appropriato del toolkit.

Esempio: eseguire la migrazione dalla funzione ScriptIntrinsicBlur

Per sostituire la funzione ScriptIntrinsicBlur:

  • Per sfocare una bitmap, chiama Toolkit.blur.

    var blurredBitmap = Toolkit.blur(myBitmap, radius)
    
  • Se vuoi sfocare un'immagine rappresentata da un array di byte, specifica la larghezza, l'altezza e il numero di byte per pixel.

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

Esegui la migrazione dagli script

Se il tuo caso d'uso non può essere risolto con:

Inoltre, il tuo caso d'uso può trarre vantaggio dall'accelerazione GPU: Android supporta il calcolo GPU sulle API multipiattaforma Vulkan e OpenGL ES (GLES). Questo potrebbe non essere necessario perché sulla maggior parte dei dispositivi gli script sono già in esecuzione sulla CPU anziché sulla GPU: in alcuni casi d'uso C/C++ può essere più veloce del calcolo di RenderScript, GLES o Vulkan. (o almeno abbastanza veloce per il tuo caso d'uso)

Per comprendere meglio come eseguire la migrazione, esamina l'app di esempio. L'esempio mostra come sfocare una bitmap ed eseguire una conversione a matrice dei colori in RenderScript e ha un codice equivalente in Vulkan e OpenGL.

Se la tua applicazione deve supportare una serie di release, utilizza RenderScript per i dispositivi con Android 6 (livello API 23) e versioni precedenti, nonché Vulkan o GLES sui dispositivi supportati con Android 7 (livello API 24) e versioni successive. Se minSdkVersion è 24 o superiore, potrebbe non essere necessario utilizzare RenderScript. Vulkan o GLES 3.1 possono essere utilizzati ovunque sia necessario il supporto del calcolo GPU.

Android fornisce associazioni di SDK per le API GLES, quindi non è necessario utilizzare l'NDK quando si lavora in OpenGL ES.

Vulkan non fornisce associazioni di SDK, quindi non esiste una mappatura diretta da RenderScript a Vulkan; si scrive il codice Vulkan utilizzando l'NDK e si crea funzioni JNI per accedere a questo codice da Kotlin o Java.

Le pagine seguenti illustrano alcuni aspetti della migrazione da RenderScript. L'app di esempio implementa quasi tutte queste considerazioni. Per comprenderli meglio, confronta il codice equivalente di RenderScript e Vulkan.