Von RenderScript migrieren

RenderScript APIs werden ab Android 12 eingestellt. Die Geräte- und Komponentenhersteller unterstützen die Hardwarebeschleunigung bereits nicht mehr und die RenderScript-Unterstützung wird in einer zukünftigen Version voraussichtlich vollständig entfernt.

Die C-/C++-Leistung kann für viele Anwendungsfälle angemessen sein. Wenn Sie RenderScript nur für die Intrinsik genutzt haben, können Sie diese Anwendungen durch das RenderScript Intrinsics Replacement Toolkit ersetzen. Dieses Tool ist einfacher zu verwenden und mit einer potenziellen Leistungsverbesserung um die doppelte Leistung verbunden.

Wenn Sie die GPU-Beschleunigung optimal nutzen möchten, empfehlen wir, Ihre Skripts zu Vulkan zu migrieren, Ihre Skripts zu OpenGL zu migrieren, Canvas-basierte Bildvorgänge zu nutzen oder Android Graphics Shading Language (AGSL) zu nutzen.

Nach der Einstellung von RenderScript auf der Android-Plattform wird die Unterstützung für RenderScript im Android-Gradle-Plug-in entfernt. Ab dem Android-Gradle-Plug-in 7.2 werden die RenderScript APIs eingestellt. Sie funktionieren weiterhin, lösen aber Warnungen aus. Künftige Versionen von AGP werden RenderScript nicht mehr unterstützen. In diesem Leitfaden wird die Migration von RenderScript erläutert.

Von systeminternen Daten migrieren

Obwohl die intrinsischen RenderScript-Funktionen nach der Einstellung von RenderScript weiterhin funktionieren, werden sie möglicherweise nur auf der CPU statt auf der GPU ausgeführt.

Für einige dieser Vorgänge gibt es effizientere Optionen, die jetzt in die Plattform oder in Jetpack-Bibliotheken integriert sind.

Integrierte, beschleunigte Bildvorgänge

Die Android-Plattform unterstützt beschleunigte Bildverarbeitungsvorgänge, die unabhängig von den intrinsischen RenderScript-Objekten auf Bilder angewendet werden können. Beispiele:

  • Mischen
  • Unkenntlich machen
  • Farbmatrix
  • Größe ändern

Bildverpixelung auf Android 12 und höher in einer Ansicht

RenderEffect mit Unterstützung für Unkenntlichmachung wurde in Android 12, API-Level 31, hinzugefügt, sodass du RenderNode unkenntlich machen kannst. RenderNode ist ein Konstrukt der Anzeigeliste, die Android zur Beschleunigung von Plattformgrafiken verwendet.

Android bietet eine Verknüpfung zum Anwenden eines Effekts auf die RenderNode, die mit einer View verknüpft ist. Rufen Sie View.setRenderEffect() auf, um ein View unkenntlich zu machen:

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

Unkenntlichmachung des Bildes unter Android 12 und höher, gerendert in einer Bitmap

Wenn das unkenntlich gemachte Bild in einem Bitmap gerendert werden muss, unterstützt das Framework ein beschleunigtes Rendering mit einem HardwareRenderer, unterstützt von einem HardwareBuffer. Mit dem folgenden Code werden HardwareRenderer, RenderNode und RenderEffect für die Unkenntlichmachung erstellt:

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)

Zum Anwenden des Effekts wird der interne RecordingCanvas für die RenderNode verwendet. Mit dem folgenden Code wird die Zeichnung aufgezeichnet, die Renderinganfrage erstellt und dann auf den Abschluss der Anfrage gewartet:

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

Das gerenderte Bild befindet sich in einem HardwareBuffer, das dem ImageReader zugeordnet ist. Der folgende Code ruft die Image ab und gibt ein Bitmap zurück, das ihr HardwareBuffer umschließt.

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

Mit dem folgenden Code wird nach dem Rendern des Bildes eine Bereinigung durchgeführt. ImageReader, RenderNode, RenderEffect und HardwareRenderer können zum Verarbeiten mehrerer Bilder verwendet werden.

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

AGSL für Bildverarbeitung

Die Android Graphics Shading Language (AGSL) wird von Android 13 und höher verwendet, um das Verhalten programmierbarer RuntimeShader-Objekte zu definieren. AGSL verwendet einen Großteil seiner Syntax mit GLSL-Fragment-Shadern, funktioniert jedoch im Grafik-Rendering-System von Android, um sowohl das Painting innerhalb von Canvas anzupassen als auch View-Inhalte zu filtern. So können Sie während Zeichenvorgängen eine benutzerdefinierte Bildverarbeitung hinzufügen oder ein Bild direkt mit einem RenderNode in einen Bitmap-Canvas rendern. Das folgende Beispiel zeigt, wie ein benutzerdefinierter Shader angewendet wird, um den Weichzeichnereffekt des Bildes zu ersetzen.

Erstellen Sie zuerst einen RuntimeShader und instanziieren Sie ihn mit dem AGSL-Shader-Code. Mit diesem Shader wird eine Farbmatrix für die Farbtonrotation angewendet:

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

Der Shader kann wie jede andere RenderEffect auf ein RenderNode angewendet werden. Das folgende Beispiel zeigt, wie die Uniformen in hueShader festgelegt werden:

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)

Zum Abrufen des Bitmap wird dieselbe Technik wie im vorherigen Beispiel zum Unkenntlichmachen von Bildern verwendet.

  • Der interne RecordingCanvas für die RenderNode wendet den Shader an.
  • Das Image wird abgerufen und gibt ein Bitmap zurück, das seine HardwareBuffer umschließt.

Mit CameraX von planarem YUV in RGB konvertieren

Die Konvertierung von planarem YUV in RGB zur Verwendung in der Bildverarbeitung wird im Rahmen des Anwendungsfalls ImageAnalysis in CameraX von Jetpack unterstützt.

Informationen zur Verwendung von ImageAnalysis finden Sie im Codelab Erste Schritte mit CameraX und im Beispiel-Repository für Android-Kameras.

Toolkit zum Ersetzen von Renderscript-Funktionen

Wenn Ihre Anwendung intrinsische Elemente verwendet, können Sie die eigenständige Ersatzbibliothek verwenden. Unsere Tests zeigen, dass dies schneller ist als die vorhandene RenderScript-CPU-Implementierung.

Das Toolkit umfasst die folgenden Funktionen:

  • Mischen
  • Unkenntlich machen
  • Farbmatrix
  • Falten
  • Histogramm und HistogrammPunkt
  • Suchtabelle (LUT) und LUT 3D
  • Größe ändern
  • YUV zu RGB

Vollständige Informationen und Einschränkungen finden Sie in den README.md und Toolkit.kt des Toolkits. -Dateien.

So können Sie die Bibliothek herunterladen, hinzufügen und verwenden:

  1. Laden Sie das Projekt von GitHub herunter.

  2. Suchen und erstellen Sie die renderscript-toolkit module.

  3. Fügen Sie die Bibliothek Ihrem Android Studio-Projekt hinzu, indem Sie die Datei build.gradle der App ändern.

  4. Rufen Sie die entsprechende Methode des Toolkits auf.

Beispiel: Migration von der Funktion ScriptIntrinsicBlur

So ersetzen Sie die Funktion ScriptIntrinsicBlur:

  • Rufen Sie Toolkit.blur auf, um eine Bitmap unkenntlich zu machen.

    var blurredBitmap = Toolkit.blur(myBitmap, radius)
    
  • Wenn Sie ein durch ein Bytearray dargestelltes Bild unkenntlich machen möchten, geben Sie die Breite, Höhe und die Anzahl der Byte pro Pixel an.

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

Von Skripts migrieren

Wenn Ihr Anwendungsfall nicht durch eine der folgenden Maßnahmen gelöst werden kann:

Ihr Anwendungsfall profitiert außerdem von der GPU-Beschleunigung. Android unterstützt GPU-Berechnungen mit plattformübergreifenden Vulkan- und OpenGL ES (GLES) APIs. Dies kann unnötig sein, da Ihre Skripts auf den meisten Geräten bereits auf der CPU und nicht auf der GPU ausgeführt werden: C/C++ kann in einigen Anwendungsfällen schneller als RenderScript, GLES oder Vulkan sein. (oder zumindest für Ihren Anwendungsfall schnell genug)

Weitere Informationen zur Migration finden Sie in der Beispiel-App. Darin wird gezeigt, wie Sie in RenderScript eine Bitmap unkenntlich machen und eine Farbmatrix konvertieren können. Außerdem gibt es einen entsprechenden Code in Vulkan und OpenGL.

Wenn Ihre App eine Reihe von Releases unterstützen muss, verwenden Sie RenderScript für Geräte mit Android 6 (API-Level 23) und niedriger und Vulkan oder GLES auf unterstützten Geräten mit Android 7 (API-Level 24) und höher. Wenn minSdkVersion 24 oder höher ist, müssen Sie RenderScript möglicherweise nicht verwenden. Vulkan oder GLES 3.1 können Sie überall dort einsetzen, wo Sie GPU-Computing-Unterstützung benötigen.

Android bietet SDK-Bindungen für GLES APIs, sodass die Verwendung des NDK bei der Arbeit in OpenGL ES nicht erforderlich ist.

Vulkan stellt keine SDK-Bindungen bereit, sodass es keine direkte Zuordnung von RenderScript zu Vulkan gibt. Sie schreiben den Vulkan-Code mit dem NDK und erstellen JNI-Funktionen, um über Kotlin oder Java auf diesen Code zuzugreifen.

Auf den folgenden Seiten werden Aspekte der Migration von RenderScript behandelt. In der Beispielanwendung werden fast alle diese Überlegungen implementiert. Vergleichen Sie zum besseren Verständnis den entsprechenden Code von RenderScript und Vulkan.