从 RenderScript 迁移

从 Android 12 开始,RenderScript API 已弃用。设备和组件制造商已停止提供硬件加速支持,预计 RenderScript 支持将在未来版本中完全移除。

对于许多用例,C/C++ 性能可能足够,如果您仅依赖 RenderScript 用于内联函数,则可以使用 RenderScript 内联函数替换工具包 替换这些用法,该工具包更易于使用,并且可以提高 2 倍的性能!

如果您需要充分利用 GPU 加速,我们建议您 将脚本迁移到 Vulkan。其他加速选项包括 将脚本迁移到 OpenGL、使用 基于画布的图像操作 或利用 Android 图形着色语言 (AGSL)

在 Android 平台中弃用 RenderScript 后,Android Gradle 插件中将删除对 RenderScript 的支持。从 Android Gradle 插件 7.2 开始,RenderScript API 已弃用。它们继续起作用,但会发出警告。未来版本的 AGP 将不再包含 Renderscript 支持。本指南介绍如何从 RenderScript 迁移。

从内联函数迁移

尽管在 RenderScript 弃用后,RenderScript 内联函数仍继续起作用,但它们可能仅在 CPU 而不是 GPU 上执行。

对于其中一些操作,平台或 Jetpack 库中现在内置了更高效的选项。

内置加速图像操作

Android 平台支持可以应用于图像的加速图像处理操作,独立于 RenderScript 内联函数。例如:

  • 混合
  • 模糊
  • 颜色矩阵
  • 调整大小

Android 12+ 中的图像模糊到 View 中

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 的图像模糊

如果您需要将模糊图像渲染到 Bitmap 中,框架支持使用由 HardwareBuffer 支持的 HardwareRenderer 进行加速渲染。以下代码创建 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 片段着色器有很多共同之处,但在 Android 图形渲染系统中工作,既可以自定义 Canvas 中的绘制,也可以过滤 View 内容。这可以用于在绘图操作期间添加自定义图像处理,或者通过直接使用 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 中设置 uniforms。

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

平面 YUV 转换为 RGB 以用于图像处理,作为 Jetpack 的 CameraX 中 ImageAnalysis 用例的一部分得到支持。

在 Jetpack 的 CameraX CameraX 入门 代码实验室以及 Android 相机 示例 存储库中,提供了有关使用 ImageAnalysis 的资源。

Renderscript 内在函数替换工具包

如果您的应用程序使用内在函数,则可以使用独立的替换库;我们的测试表明它比使用现有的 RenderScript CPU 实现更快。

工具包包含以下函数

  • 混合
  • 模糊
  • 颜色矩阵
  • 卷积
  • 直方图和直方图点积
  • 查找表 (LUT) 和 3D LUT
  • 调整大小
  • YUV 到 RGB

有关完整详细信息和限制,请参阅工具包的 README.mdToolkit.kt 文件。

执行以下步骤下载、添加和使用库

  1. 从 GitHub 下载 项目

  2. 找到并构建 renderscript-toolkit 模块

  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;Vulkan 或 GLES 3.1 可用于任何需要 GPU 计算支持的地方。

Android 为 GLES API 提供 SDK 绑定,因此在使用 OpenGL ES 时无需使用 NDK。

Vulkan 不提供 SDK 绑定,因此 RenderScript 与 Vulkan 之间没有直接映射;您使用 NDK 编写 Vulkan 代码,并创建 JNI 函数以从 Kotlin 或 Java 访问此代码。

以下页面涵盖了从 RenderScript 迁移的各个方面。示例应用程序实现了几乎所有这些考虑因素。要更好地理解它们,请比较 RenderScript 和 Vulkan 的等效代码。