Migrar do RenderScript

As APIs do RenderScript foram descontinuadas desde o Android 12. Os fabricantes de dispositivos e componentes já pararam de oferecer suporte à aceleração de hardware, e o suporte para o RenderScript será totalmente removido em uma versão futura.

O desempenho do código C/C++ pode ser adequado para muitos casos de uso. Se você dependia apenas do RenderScript para intrínsecos, poderá substituir esses casos usando o Kit de ferramentas de substituição de intrínsecos do RenderScript, que é mais fácil de usar e pode dobrar o desempenho.

Se você precisa aproveitar ao máximo a aceleração de GPU, recomendamos migrar seus scripts para o Vulkan. Outras opções de aceleração incluem migrar seus scripts para o OpenGL, usando Operações de imagem baseadas em tela ou usando a Linguagem de sombreamento gráfico do Android (AGSL, na sigla em inglês).

Após a descontinuação do RenderScript na plataforma Android, o suporte ao RenderScript foi removido do Plug-in do Android para Gradle. Na versão 7.2 do Plug-in do Android para Gradle e mais recentes, as APIs do RenderScript foram descontinuadas. Elas continuam funcionando, mas invocam avisos. As versões futuras do AGP não vão mais incluir suporte ao Renderscript. Este guia explica como migrar do RenderScript.

Migrar dos intrínsecos

As funções intrínsecas do RenderScript vão continuar funcionando após a descontinuação do RenderScript, contudo, elas poderão ser executadas apenas na CPU e não na GPU.

Para algumas dessas operações, há opções mais eficientes integradas à plataforma ou às bibliotecas do Jetpack.

Operações de aceleração de imagem integradas

A plataforma Android oferece suporte a operações de processamento de aceleração de imagem que podem ser aplicadas a imagens, independente dos intrínsecos do RenderScript. Por exemplo:

  • Mesclar
  • Desfocar
  • Matriz de cores
  • Redimensionar

Desfoque de imagem no Android 12 e versões mais recentes em uma visualização

O RenderEffect com suporte ao desfoque foi adicionado no Android 12, nível 31 da API, permitindo que você desfoque um RenderNode. O RenderNode é um elemento da lista de exibição que o Android usa para ajudar a acelerar gráficos da plataforma.

O Android oferece um atalho para aplicar um efeito ao RenderNode associado a uma View. Para desfocar uma View, chame View.setRenderEffect():

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

Desfoque de imagem no Android 12 e versões mais recentes renderizado em um bitmap

Caso você precise que a imagem desfocada seja renderizada em um Bitmap, o framework oferece suporte à renderização acelerada com um HardwareRenderer com um HardwareBuffer de apoio. O código abaixo cria HardwareRenderer, RenderNode e RenderEffect para o desfoque:

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)

A aplicação do efeito envolve o uso de uma RecordingCanvas interna para o RenderNode. O código abaixo grava a imagem, cria a solicitação de renderização e espera que a solicitação seja concluída:

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

A imagem renderizada fica em um HardwareBuffer associado ao ImageReader. O código abaixo extrai a Image e retorna um Bitmap unido ao 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")

O código abaixo é limpo depois de renderizar a imagem. Observe que ImageReader, RenderNode, RenderEffect e HardwareRenderer podem ser usados para processar várias imagens.

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

AGSL para processamento de imagens

A Linguagem de sombreamento gráfico do Android (AGSL, na sigla em inglês) é usada pelo Android 13 e versões mais recentes para definir o comportamento de objetos RuntimeShader programáveis. A AGSL compartilha grande parte da sintaxe com sombreadores de fragmentos GLSL, mas funciona no sistema de renderização de gráficos do Android para personalizar a pintura na Canvas e filtrar o conteúdo da View. Isso pode ser usado para adicionar processamento personalizado de imagem durante operações de renderizaçãoou usando um RenderNode diretamente para renderizar uma imagem em uma tela Bitmap. O exemplo abaixo demonstra como aplicar um sombreador personalizado para substituir o efeito de desfoque da imagem.

Comece criando um RuntimeShader, instanciando-o com o código de sombreador da AGSL. Esse sombreador é usado para aplicar uma matriz de cores para rotação de matiz:

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

O sombreador pode ser aplicado a um RenderNode, assim como qualquer outro RenderEffect. O exemplo abaixo demonstra como definir os uniformes no 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)

Para gerar o Bitmap, a mesma técnica do exemplo anterior de desfoque de imagem é usada.

  • A RecordingCanvas internA do RenderNode aplica o sombreador.
  • A Image é extraída, retornando um Bitmap unido ao HardwareBuffer.

Converter de planos YUV para RGB usando o CameraX

A conversão de planos YUV em RGB para uso no processamento de imagens tem suporte como parte do caso de uso da ImageAnalysis no CameraX do Jetpack.

Existem recursos sobre o uso da ImageAnalysis como parte do codelab Começar a usar o CameraX e no repositório de exemplos (link em inglês) da câmera do Android.

Kit de ferramentas de substituição de intrínsecos do Renderscript

Caso seu aplicativo use intrínsecos, você poderá usar a biblioteca de substituição independente. Nossos testes indicam que isso é mais rápido do que usar a implementação de CPU atual do RenderScript.

O kit de ferramentas inclui as seguintes funções:

  • Mesclar
  • Desfocar
  • Matriz de cores
  • Convolução
  • Histograma e histogramDot
  • Tabela de consulta (LUT) e LUT 3D
  • Redimensionar
  • YUV para RGB

Para conferir todos os detalhes e as limitações, consulte README.md e Toolkit.kt do kit de ferramentas. e arquivos de imagem da capa.

Siga estas etapas para fazer o download, adicionar e usar a biblioteca:

  1. Faça o download do projeto no GitHub.

  2. Localize e crie o renderscript-toolkit module.

  3. Adicione a biblioteca ao projeto do Android Studio modificando o arquivo build.gradle do app.

  4. Invoque o método adequado do kit de ferramentas.

Exemplo: migrar da função ScriptIntrinsicBlur

Para substituir a função ScriptIntrinsicBlur:

  • Para desfocar um bitmap, chame Toolkit.blur.

    var blurredBitmap = Toolkit.blur(myBitmap, radius)
    
  • Se você quiser desfocar uma imagem representada por uma matriz de bytes, especifique a largura, a altura e o número de bytes por pixel.

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

Migrar dos scripts

Se o caso de uso não puder ser resolvido com:

Além disso, seu caso de uso pode se beneficiar da aceleração de GPU. O Android oferece suporte à computação de GPU em APIs multiplataforma Vulkan e OpenGL ES (GLES). Isso pode ser desnecessário porque, na maioria dos dispositivos, seus scripts já estão sendo executados na CPU em vez da GPU. O C/C++ pode ser mais rápido que a computação do RenderScript, do GLES ou do Vulkan em alguns casos de uso. (ou pelo menos rápido o suficiente para seu caso de uso)

Para entender melhor como migrar as funções, consulte o app de exemplo (link em inglês). O exemplo mostra como desfocar um bitmap e fazer a conversão de uma matriz de cores no RenderScript e tem o código equivalente do Vulkan.

Se o app precisa oferecer suporte a várias versões, use o RenderScript para dispositivos com o Android 6 (nível 23 da API) ou versões anteriores e o Vulkan ou o GLES em dispositivos com suporte ao Android 7 (nível 24 da API) ou mais recente. Se a minSdkVersion for 24 ou mais recente, talvez não seja necessário usar o RenderScript. É possível usar Vulkan ou o GLES 3.1 em qualquer lugar em que você precise de suporte à computação da GPU.

O Android fornece vinculações de SDK para APIs GLES. Não é necessário usar o NDK ao trabalhar com OpenGL ES.

O Vulkan não fornece vinculações de SDK. Não há mapeamento direto do RenderScript para o Vulkan. Programe o código Vulkan usando o NDK e crie funções JNI para acessar esse código em Kotlin ou Java.

As páginas abaixo abordam aspectos da migração do RenderScript. O app de exemplo implementa quase todas essas considerações. Para entender melhor, compare o RenderScript e o código equivalente do Vulkan.