從 RenderScript 遷移

自 Android 12 起,RenderScript API 已淘汰。裝置和元件製造商已停止提供硬體加速支援,後續版本也將完全移除 RenderScript 支援功能。

C/C++ 的效能或許就能滿足許多用途。如果您只仰賴 RenderScript 執行內建函式,可以使用 RenderScript 內建函式替換工具包取代這些用途,這樣不僅更易於使用,效能也可能翻倍!

如果需要完全利用 GPU 加速功能,建議您將指令碼遷移至 Vulkan。其他加速選項包括將指令碼遷移至 OpenGL、使用畫布型圖片作業,或利用 Android 圖形著色語言 (AGSL)

在 Android 平台淘汰 RenderScript 後,Android Gradle 外掛程式也會停止支援 RenderScript。自 Android Gradle 外掛程式 7.2 版開始,RenderScript API 已淘汰。這些函式會繼續運作,但會叫用警告。Android Gradle 外掛程式後續版本將不再提供 Renderscript 支援功能。本指南將說明如何從 RenderScript 遷移。

從內建函式遷移

雖然 RenderScript 內建函式會在 RenderScript 淘汰後繼續運作,但可能只能在 CPU 上執行,無法用於 GPU。

針對部分作業,平台或 Jetpack 程式庫現已內建更有效率的方式。

內建圖片加速處理作業

Android 平台支援可套用至圖片的圖片加速處理作業,不需要使用 RenderScript 內建函式。例如:

  • 混合
  • 模糊處理
  • 色彩矩陣
  • 調整大小

在 Android 12 以上版本將圖片模糊處理效果算繪為檢視區塊

支援模糊處理效果的 RenderEffect 已新增至 Android 12 (API 級別 31),可用來模糊處理 RenderNodeRenderNode 是顯示清單的建構項目,供 Android 用來協助加快算繪平台圖形。

Android 提供捷徑,可將效果套用至與 View 相關聯的 RenderNode。如要模糊處理 View,請呼叫 View.setRenderEffect()

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

在 Android 12 以上版本將圖片模糊處理效果算繪為點陣圖

如果需要將經過模糊處理的圖片算繪為 Bitmap,此架構支援透過採用 HardwareBufferHardwareRenderer 加快算繪作業。下列程式碼會建立 HardwareRendererRenderNode,以及模糊處理效果的 RenderEffect

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)

套用效果時,需要針對 RenderNode 使用內部 RecordingCanvas。下列程式碼會記錄繪圖作業、建立算繪要求,然後等待要求完成:

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

算繪的圖片位於與 ImageReader 相關聯的 HardwareBuffer 中。下列程式碼會取得 Image,並傳回包裝其 HardwareBufferBitmap

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

算繪圖片後,下列程式碼會執行清除作業。請注意,ImageReaderRenderNodeRenderEffectHardwareRenderer 可用來處理多張圖片。

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

使用 AGSL 處理圖片

Android 13 以上版本會使用 Android 圖形著色語言 (AGSL),為可程式化的 RuntimeShader 物件定義行為。AGSL 的語法大部分與 GLSL 片段著色器相同,但 AGSL 是在 Android 圖形算繪系統中運作,可篩選 View 內容及在 Canvas 內自訂繪製方式。這可用於在繪圖作業期間新增自訂圖片處理作業,或直接使用 RenderNode 將圖片算繪到 Bitmap 畫布。以下範例說明如何套用自訂著色器,取代圖片模糊處理效果。

請先建立 RuntimeShader,並使用 AGSL 著色器程式碼將此著色器例項化。此著色器可套用色彩矩陣,用來旋轉色相:

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

著色器可套用至 RenderNode,就像任何其他 RenderEffect 一樣。以下範例說明如何在 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)

如要取得 Bitmap,請運用與上述圖片模糊處理範例相同的技巧。

  • RenderNode 的內部 RecordingCanvas 會套用著色器。
  • 系統會取得 Image,並傳回包裝其 HardwareBufferBitmap

使用 CameraX 從平面 YUV 轉換至 RGB

Jetpack CameraX 的 ImageAnalysis 用途支援從平面 YUV 轉換至 RGB,以便用於圖片處理作業。

如要瞭解如何使用 ImageAnalysis,可以參考「開始使用 CameraX」程式碼研究室,以及 Android 相機範例存放區等資源。

Renderscript 內建函式替換工具包

如果應用程式使用內建函式,您可以使用獨立的替代程式庫;我們的測試結果顯示,比起現有的 RenderScript CPU 實作項目,獨立替代程式庫的速度更快。

工具包中包含下列函式:

  • 混合
  • 模糊處理
  • 色彩矩陣
  • 卷積
  • 直方圖和 histogramDot
  • 對照表 (LUT) 函數和 LUT 3D
  • 調整大小
  • 從 YUV 轉換為 RGB

如需完整的詳細資料和限制條件,請參閱工具包的 README.mdToolkit.kt. 檔案。

請按照下列步驟下載、新增及使用資料庫:

  1. 從 GitHub 下載專案

  2. 找出並建構 renderscript-toolkit module

  3. 修改應用程式的 build.gradle 檔案,將程式庫新增至 Android Studio 專案。

  4. 叫用適當的工具包方法。

範例:從 ScriptIntrinsicBlur 函式遷移

如要取代 ScriptIntrinsicBlur 函式:

  • 如要模糊處理點陣圖,請呼叫 Toolkit.blur

    var blurredBitmap = Toolkit.blur(myBitmap, radius)
    
  • 如要模糊處理由位元組陣列呈現的圖片,請指定每個像素的寬度、高度和位元組數。

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

從指令碼遷移

如果您的用途無法使用以下項目解決問題:

而且用途可受益於 GPU 加速功能 (Android 在跨平台 Vulkan 和 OpenGL ES (GLES) API 上支援 GPU 運算功能),那麼您可能會發現不必採取這項措施,因為在大部分裝置上,指令碼都已在 CPU (而非 GPU) 上執行:對於某些用途,C/C++ 的運算速度可能比 RenderScript、GLES 或 Vulkan 更快 (或至少夠快)。

如要進一步瞭解如何遷移,請參考範例應用程式。此範例示範如何在 RenderScript 中模糊處理點陣圖並執行色彩矩陣轉換,並提供 Vulkan 和 OpenGL 中的對等程式碼。

如果應用程式需要支援多種版本,請針對搭載 Android 6 (API 級別 23) 以下版本的裝置使用 RenderScript;若是搭載 Android 7 (API 級別 24) 以上版本的支援裝置,則請使用 Vulkan 或 GLES。如果 minSdkVersion 為 24 以上,可能不需要使用 RenderScript;針對任何需要 GPU 運算支援功能的用途,只需使用 Vulkan 或 GLES 3.1 即可。

Android 可為 GLES API 提供 SDK 繫結,因此在 OpenGL ES 中運作時不需要使用 NDK。

Vulkan 不提供 SDK 繫結,因此無法將 RenderScript 直接對應至 Vulkan;您會使用 NDK 編寫 Vulkan 程式碼,並建立 JNI 函式,以便從 Kotlin 或 Java 存取此程式碼。

以下各節介紹從 RenderScript 遷移的各個面向。範例應用程式幾乎涵蓋了上述所有考量因素。如要進一步瞭解這些因素,請比較 RenderScript 和 Vulkan 的對等程式碼。