RenderScript

O RenderScript é um framework para executar tarefas intensivas em termos de computação com alto desempenho no Android. Ele foi projetado para uso com computação paralela de dados, embora cargas de trabalho seriais também possam se beneficiar. O tempo de execução do RenderScript carrega em paralelo todos os processadores disponíveis em um dispositivo, como CPUs e GPUs com vários núcleos, permitindo que os desenvolvedores se concentrem em expressar algoritmos em vez de programar trabalho. O RenderScript é especialmente útil para apps que realizam processamento de imagens, fotografia computacional ou visão computacional.

Dispositivos com o Android 8.0 e versões mais recentes usam as seguintes estruturas do RenderScript e HALs do fornecedor:

Figura 1. Vinculação de código do fornecedor a bibliotecas internas.

As diferenças do RenderScript no Android 7.x e em versões anteriores incluem:

  • Duas instâncias de bibliotecas internas do RenderScript em um processo. Um conjunto é para o caminho de substituição da CPU e vem diretamente de /system/lib. O outro conjunto é para o caminho da GPU e vem de /system/lib/vndk-sp.
  • As bibliotecas internas do RS em /system/lib são criadas como parte da plataforma e atualizadas quando o system.img é atualizado. No entanto, as bibliotecas em /system/lib/vndk-sp são criadas para o fornecedor e não são atualizadas quando o system.img é atualizado (embora possam ser atualizadas para uma correção de segurança, a ABI permanece a mesma).
  • O código do fornecedor (RS HAL, driver RS e bcc plugin) é vinculado às bibliotecas internas do RenderScript localizadas em /system/lib/vndk-sp. Não é possível vincular a bibliotecas em /system/lib porque as bibliotecas nesse diretório são criadas para a plataforma e, portanto, podem não ser compatíveis com o código do fornecedor (ou seja, os símbolos podem ser removidos). Isso impossibilitaria uma OTA somente de framework.

Design

As seções a seguir detalham o design do RenderScript no Android 8.0 e versões mais recentes.

Bibliotecas do RenderScript disponíveis para fornecedores

Esta seção lista as bibliotecas do RenderScript (conhecidas como NDK do fornecedor para HALs de mesmo processo ou VNDK-SP) disponíveis para o código do fornecedor e que podem ser vinculadas a ele. Ele também detalha outras bibliotecas que não estão relacionadas ao RenderScript, mas que também são fornecidas ao código do fornecedor.

Embora a lista de bibliotecas a seguir possa variar entre as versões do Android, ela é imutável para uma versão específica. Para conferir uma lista atualizada de bibliotecas disponíveis, consulte /system/etc/ld.config.txt.

Bibliotecas do RenderScript Bibliotecas não RenderScript
  • android.hardware.graphics.renderscript@1.0.so
  • libRS_internal.so
  • libRSCpuRef.so
  • libblas.so
  • libbcinfo.so
  • libcompiler_rt.so
  • libRSDriver.so
  • libc.so
  • libm.so
  • libdl.so
  • libstdc++.so
  • liblog.so
  • libnativewindow.so
  • libsync.so
  • libvndksupport.so
  • libbase.so
  • libc++.so
  • libcutils.so
  • libutils.so
  • libhardware.so
  • libhidlbase.so
  • libhidltransport.so
  • libhwbinder.so
  • liblzma.so
  • libz.so
  • libEGL.so
  • libGLESv1_CM.so
  • libGLESv2.so

Configuração do namespace do vinculador

A restrição de vinculação que impede que bibliotecas que não estão no VNDK-SP sejam usadas pelo código do fornecedor é aplicada em tempo de execução usando o namespace do vinculador. Para mais detalhes, consulte a apresentação Design do VNDK.

Em um dispositivo com Android 8.0 e versões mais recentes, todas as HALs do mesmo processo (SP-HALs), exceto o RenderScript, são carregadas no namespace do vinculador sphal. O RenderScript é carregado no namespace específico do RenderScript rs, um local que permite uma aplicação um pouco mais flexível para bibliotecas do RenderScript. Como a implementação do RS precisa carregar o bitcode compilado, /data/*/*.so é adicionado ao caminho do namespace rs. Outras SP-HALs não podem carregar bibliotecas da partição de dados.

Além disso, o namespace rs permite mais bibliotecas do que outros namespaces. libmediandk.so e libft2.so são expostos ao namespace rs porque libRS_internal.so tem uma dependência interna dessas bibliotecas.

Figura 2. Configuração de namespace para o vinculador.

Carregar drivers

Caminho de substituição da CPU

Dependendo da existência do bit RS_CONTEXT_LOW_LATENCY ao criar um contexto do RS, o caminho da CPU ou da GPU é selecionado. Quando o caminho da CPU é selecionado, libRS_internal.so (a principal implementação do framework RS) é diretamente dlopened do namespace padrão do vinculador, em que a versão da plataforma das bibliotecas RS é fornecida.

A implementação do HAL do RS do fornecedor não é usada quando o caminho de fallback da CPU é adotado, e um objeto RsContext é criado com mVendorDriverName nulo. libRSDriver.so é (por padrão) dlopened, e a biblioteca de driver é carregada do namespace default porque o caller (libRS_internal.so) também é carregado no namespace default.

Figura 3. Caminho de substituição da CPU.

Caminho da GPU

No caminho da GPU, o libRS_internal.so é carregado de maneira diferente. Primeiro, libRS.so usa android.hardware.renderscript@1.0.so (e o libhidltransport.so subjacente) para carregar android.hardware.renderscript@1.0-impl.so (uma implementação do fornecedor do RS HAL) em um namespace de vinculador diferente chamado sphal. O HAL do RS dlopen libRS_internal.so em outro namespace do vinculador chamado rs.

Os fornecedores podem fornecer o próprio driver RS definindo a flag de tempo de build OVERRIDE_RS_DRIVER, que é incorporada à implementação da HAL RS (hardware/interfaces/renderscript/1.0/default/Context.cpp). Esse nome de driver é dlopened para o contexto RS do caminho da GPU.

A criação do objeto RsContext é delegada à implementação da HAL do RS. A HAL faz uma chamada de volta para o framework RS usando a função rsContextCreateVendor() com o nome do driver a ser usado como argumento. Em seguida, o framework RS carrega o driver especificado quando o RsContext é inicializado. Nesse caso, a biblioteca de driver é carregada no namespace rs porque o objeto RsContext é criado dentro do namespace rs e /vendor/lib está no caminho de pesquisa do namespace.

Figura 4. Caminho de substituição da GPU.

Ao fazer a transição do namespace default para o sphal, o libhidltransport.so usa a função android_load_sphal_library() para ordenar explicitamente o vinculador dinâmico a carregar a biblioteca -impl.so do namespace sphal.

Ao fazer a transição do namespace sphal para o rs, o carregamento é feito indiretamente pela seguinte linha em /system/etc/ld.config.txt:

namespace.sphal.link.rs.shared_libs = libRS_internal.so

Essa linha especifica que o vinculador dinâmico precisa carregar libRS_internal.so do namespace rs quando a biblioteca não pode ser encontrada/carregada do namespace sphal (o que sempre acontece porque o namespace sphal não pesquisa /system/lib/vndk-sp onde libRS_internal.so está). Com essa configuração, uma simples chamada dlopen() para libRS_internal.so é suficiente para fazer a transição do namespace.

Carregar plug-in de Cco

bcc plugin é uma biblioteca fornecida pelo fornecedor e carregada no compilador bcc. Como bcc é um processo do sistema no diretório /system/bin, a biblioteca bcc plugin pode ser considerada uma SP-HAL (ou seja, uma HAL do fornecedor que pode ser carregada diretamente no processo do sistema sem ser vinculada). Como uma SP-HAL, a biblioteca bcc-plugin:

  • Não é possível vincular a bibliotecas somente de framework, como libLLVM.so.
  • Só pode ser vinculado às bibliotecas VNDK-SP disponíveis para o fornecedor.

Essa restrição é aplicada carregando o bcc plugin no namespace sphal usando a função android_sphal_load_library(). Em versões anteriores do Android, o nome do plug-in era especificado usando a opção -load e a biblioteca era carregada usando o simples dlopen() por libLLVM.so. No Android 8.0 e versões mais recentes, isso é especificado na opção -plugin, e a biblioteca é carregada diretamente pelo próprio bcc. Essa opção permite um caminho não específico do Android para o projeto de código aberto LLVM.

Figura 5. Carregando o plug-in bcc, Android 7.x e versões anteriores.



Figura 6. Carregando o plug-in bcc, Android 8.0 e versões mais recentes.

Caminhos de pesquisa para ld.mc

Ao executar ld.mc, algumas bibliotecas de tempo de execução do RS são fornecidas como entradas para o vinculador. O bitcode do RS do app é vinculado às bibliotecas de tempo de execução e, quando o bitcode convertido é carregado em um processo de app, as bibliotecas de tempo de execução são novamente vinculadas dinamicamente do bitcode convertido.

As bibliotecas de execução incluem:

  • libcompiler_rt.so
  • libm.so
  • libc.so
  • Driver RS (libRSDriver.so ou OVERRIDE_RS_DRIVER)

Ao carregar o bitcode compilado no processo do app, forneça exatamente a mesma biblioteca usada pelo ld.mc. Caso contrário, o bitcode compilado pode não encontrar um símbolo que estava disponível quando foi vinculado.

Para isso, o framework RS usa diferentes caminhos de pesquisa para as bibliotecas de tempo de execução ao executar ld.mc, dependendo se o próprio framework RS é carregado de /system/lib ou de /system/lib/vndk-sp. Isso pode ser determinado lendo o endereço de um símbolo arbitrário de uma biblioteca de framework RS e usando dladdr() para receber o caminho do arquivo mapeado para o endereço.

Política do SELinux

Como resultado das mudanças na política do SELinux no Android 8.0 e versões mais recentes, é necessário seguir regras específicas (aplicadas por neverallows) ao rotular arquivos adicionais na partição vendor:

  • vendor_file precisa ser o marcador padrão para todos os arquivos na partição vendor. A política da plataforma exige isso para acessar implementações de HAL de passagem.
  • Todos os novos exec_types adicionados na partição vendor pela SEPolicy do fornecedor precisam ter o atributo vendor_file_type. Isso é aplicado por neverallows.
  • Para evitar conflitos com atualizações futuras de plataforma/framework, evite rotular arquivos que não sejam exec_types na partição vendor.
  • Todas as dependências de biblioteca para HALs de mesmo processo identificados pelo AOSP precisam ser rotuladas como same_process_hal_file.

Para mais detalhes sobre a política do SELinux, consulte Security-Enhanced Linux no Android.

Compatibilidade com ABI para bitcode

Se nenhuma API nova for adicionada, o que significa que não haverá aumento na versão da HAL, os frameworks do RS vão continuar usando o driver da GPU atual (HAL 1.0).

Para pequenas mudanças na HAL (HAL 1.1) que não afetam o bitcode, os frameworks precisam recorrer à CPU para essas APIs recém-adicionadas e continuar usando o driver da GPU (HAL 1.0) em outros lugares.

Para mudanças importantes na HAL (HAL 2.0) que afetam a compilação/vinculação de bitcode, as estruturas do RS não devem carregar drivers de GPU fornecidos pelo fornecedor e, em vez disso, usar o caminho da CPU ou do Vulkan para aceleração.

O consumo de bitcode do RenderScript ocorre em três etapas:

Estágio Detalhes
Compilar
  • O bitcode de entrada (.bc) para bcc precisa estar no formato de bitcode LLVM 3.2, e bcc precisa ter compatibilidade com versões anteriores de apps legados.
  • No entanto, os metadados em .bc podem mudar (pode haver novas funções de runtime, por exemplo, Setters e getters de alocação, funções matemáticas etc. Parte das funções de tempo de execução fica em libclcore.bc, e outra parte em LibRSDriver ou equivalente do fornecedor.
  • Novas funções de tempo de execução ou mudanças interruptivas de metadados exigem o incremento do nível da API do bitcode. Como os drivers do fornecedor não poderão consumir isso, a versão da HAL também precisará ser incrementada.
  • Os fornecedores podem ter os próprios compiladores, mas as conclusões/requisitos para bcc também se aplicam a eles.
Link
  • O .o compilado será vinculado ao driver do fornecedor, por exemplo, libRSDriver_foo.so e libcompiler_rt.so. O caminho da CPU será vinculado a libRSDriver.so.
  • Se o .o exigir uma nova API de tempo de execução do libRSDriver_foo, o driver do fornecedor precisará ser atualizado para oferecer suporte a ela.
  • Alguns fornecedores podem ter os próprios vinculadores, mas o argumento para ld.mc também se aplica a eles.
Carregar
  • libRSCpuRef carrega o objeto compartilhado. Se houver mudanças nessa interface, será necessário aumentar a versão da HAL.
  • Os fornecedores usariam libRSCpuRef para carregar o objeto compartilhado ou implementariam o próprio.

Além da HAL, as APIs de tempo de execução e os símbolos exportados também são interfaces. Nenhuma das interfaces mudou desde o Android 7.0 (API 24), e não há planos imediatos para mudar no Android 8.0 e versões mais recentes. No entanto, se a interface mudar, a versão da HAL também será incrementada.

Implementações de fornecedores

O Android 8.0 e versões mais recentes exigem algumas mudanças no driver da GPU para que ele funcione corretamente.

Módulos de driver

  • Os módulos de driver não podem depender de bibliotecas do sistema que não estejam na lista.
  • O driver precisa fornecer o próprio android.hardware.renderscript@1.0-impl_{NAME} ou declarar a implementação padrão android.hardware.renderscript@1.0-impl como dependência.
  • A implementação da CPU libRSDriver.so é um bom exemplo de como remover dependências não VNDK-SP.

Compilador de bitcode

É possível compilar o bitcode do RenderScript para o driver do fornecedor de duas maneiras:

  1. Invocar o compilador RenderScript específico do fornecedor em /vendor/bin/ (método preferencial de compilação de GPU). Assim como outros módulos de driver, o binário do compilador do fornecedor não pode depender de nenhuma biblioteca do sistema que não esteja na lista de bibliotecas do RenderScript disponíveis para fornecedores.
  2. Invoque o bcc do sistema: /system/bin/bcc com um bcc plugin fornecido pelo fornecedor. Esse plug-in não pode depender de nenhuma biblioteca do sistema que não esteja na lista de bibliotecas do RenderScript disponíveis para fornecedores.

Se o fornecedor bcc plugin precisar interferir na compilação da CPU e a dependência dela em libLLVM.so não puder ser removida facilmente, o fornecedor deverá copiar bcc (e todas as dependências não LL-NDK, incluindo libLLVM.so, libbcc.so) para a partição /vendor.

Além disso, os fornecedores precisam fazer as seguintes mudanças:

Figura 7. Mudanças no driver do fornecedor.

  1. Copie libclcore.bc para a partição /vendor. Isso garante que libclcore.bc, libLLVM.so e libbcc.so estejam sincronizados.
  2. Mude o caminho para o executável bcc definindo RsdCpuScriptImpl::BCC_EXE_PATH na implementação da HAL do RS.

Política do SELinux

A política do SELinux afeta os executáveis do driver e do compilador. Todos os módulos de driver precisam ser rotulados como same_process_hal_file no file_contexts do dispositivo. Exemplo:

/vendor/lib(64)?/libRSDriver_EXAMPLE\.so     u:object_r:same_process_hal_file:s0

O executável do compilador precisa poder ser invocado por um processo de app, assim como a cópia do fornecedor de bcc (/vendor/bin/bcc). Por exemplo:

device/vendor_foo/device_bar/sepolicy/file_contexts:
/vendor/bin/bcc                    u:object_r:same_process_hal_file:s0

Dispositivos legados

Os dispositivos legados são aqueles que atendem às seguintes condições:

  1. PRODUCT_SHIPPING_API_LEVEL é menor que 26.
  2. PRODUCT_FULL_TREBLE_OVERRIDE não está definido.

Em dispositivos legados, as restrições não são aplicadas ao fazer upgrade para o Android 8.0 ou versões mais recentes. Isso significa que os drivers podem continuar vinculando bibliotecas em /system/lib[64]. No entanto, devido à mudança de arquitetura relacionada a OVERRIDE_RS_DRIVER, android.hardware.renderscript@1.0-impl precisa ser instalado na partição /vendor. Caso contrário, o tempo de execução do RenderScript vai usar o caminho da CPU.

Para informações sobre a motivação da descontinuação do RenderScript, consulte o blog de desenvolvedores do Android: Android GPU Compute Going Forward (em inglês). As informações de recursos para essa suspensão de uso incluem o seguinte: