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 trabalha em paralelo em todos os processadores disponíveis em um dispositivo, como CPUs e GPUs de vários núcleos, permitindo que os desenvolvedores se concentrem em expressar algoritmos em vez de programar o 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 o seguinte framework RenderScript e HALs do fornecedor:

Figura 1. Link do código do fornecedor para bibliotecas internas.

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

  • Duas instâncias de libs internas do RenderScript em um processo. Um conjunto é para o caminho de fallback da CPU e é diretamente de /system/lib. O outro conjunto é para o caminho da GPU e é de /system/lib/vndk-sp.
  • As libs internas do RS no /system/lib são criadas como parte da plataforma e são atualizadas conforme 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 elas possam ser atualizadas para uma correção de segurança, a ABI delas permanece a mesma.
  • O código do fornecedor (HAL do RS, driver do RS e bcc plugin) são vinculados às libs internas do RenderScript localizadas em /system/lib/vndk-sp. Elas não podem se 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 tornaria impossível uma OTA somente de framework.

Design

As seções abaixo 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 do mesmo processo ou VNDK-SP) que estão disponíveis para o código do fornecedor e que podem ser vinculadas. 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 do Android. Para conferir uma lista atualizada das bibliotecas disponíveis, consulte /system/etc/ld.config.txt.

Bibliotecas do RenderScript Bibliotecas que não são do 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 de namespace do vinculador

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

Em um dispositivo com o Android 8.0 e versões mais recentes, todas as HALs do mesmo processo (SP-HALs, na sigla em inglês) exceto RenderScript são carregadas dentro do 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 de RS precisa carregar o código de bits compilado, /data/*/*.so é adicionado ao caminho do namespace rs. Outros SP-HALs não podem carregar libs da partição de dados.

Além disso, o namespace rs permite mais bibliotecas do que as fornecidas por 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 fallback da CPU

Dependendo da existência do bit RS_CONTEXT_LOW_LATENCY ao criar um contexto de RS, o caminho da CPU ou da GPU é selecionado. Quando o caminho da CPU é selecionado, libRS_internal.so (a implementação principal do framework RS) é diretamente dlopened do namespace de vinculador padrão 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 é usado e um objeto RsContext é criado com mVendorDriverName nulo. libRSDriver.so é dlopenado (por padrão) e a biblioteca do driver é carregada do namespace default porque o autor da chamada (libRS_internal.so) também é carregado no namespace default.

Figura 3. Caminho de fallback da CPU.

Caminho da GPU

Para o 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) para carregar android.hardware.renderscript@1.0-impl.so (uma implementação do fornecedor do RS HAL) em um namespace de linker diferente chamado sphal. O HAL RS dlopens libRS_internal.so em outro namespace de vinculação chamado rs.

Os fornecedores podem fornecer o próprio driver RS definindo a flag OVERRIDE_RS_DRIVER, que é incorporada à implementação HAL do 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 de RS. O HAL chama de volta para a estrutura RS usando a função rsContextCreateVendor() com o nome do driver para usar como um argumento. O framework RS carrega o driver especificado quando o RsContext é inicializado. Nesse caso, a biblioteca de drivers é 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 alternativo da GPU.

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

Ao fazer a transição do namespace sphal para o rs, o carregamento é feito indiretamente pela linha a seguir 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 em que libRS_internal.so reside). Com essa configuração, uma chamada dlopen() simples para libRS_internal.so é suficiente para fazer a transição de namespace.

Carregar plug-in 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 um HAL do fornecedor, que pode ser carregado diretamente no processo do sistema sem ser vinculado. Como uma SP-HAL, a biblioteca bcc-plugin:

  • Não é possível vincular bibliotecas exclusivas do framework, como libLLVM.so.
  • Pode ser vinculado apenas às bibliotecas do VNDK-SP disponíveis para o fornecedor.

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

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



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

Pesquisar caminhos para ld.mc

Ao executar ld.mc, algumas bibliotecas de execução do RS são fornecidas como entradas para o vinculador. O bitcode RS do app é vinculado às bibliotecas de execução e, quando o bitcode convertido é carregado em um processo do app, as bibliotecas de execução são 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 a mesma biblioteca que foi usada por ld.mc. Caso contrário, o bitcode compilado pode não encontrar um símbolo que estava disponível quando foi vinculado.

Para fazer isso, o framework de RS usa diferentes caminhos de pesquisa para as bibliotecas de ambiente de execução ao executar ld.mc, dependendo se o próprio framework do RS é carregado de /system/lib ou /system/lib/vndk-sp. Isso pode ser determinado lendo o endereço de um símbolo arbitrário de uma lib de framework de RS e usando dladdr() para ter o caminho de 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 pelo neverallows) ao rotular outros arquivos na partição vendor:

  • vendor_file precisa ser o rótulo 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 usando a SEPolicy do fornecedor precisam ter o atributo vendor_file_type. Isso é aplicado por neverallows.
  • Para evitar conflitos com futuras atualizações de plataforma/framework, evite rotular arquivos diferentes de exec_types na partição vendor.
  • Todas as dependências de biblioteca para HALs de mesmo processo identificados pelo AOSP precisam ser marcadas 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, ou seja, sem promoção de versão da HAL, os frameworks de RS continuarão usando o driver da GPU (HAL 1.0) existente.

Para mudanças menores do HAL (HAL 1.1) que não afetam o bitcode, os frameworks precisam usar a CPU para essas APIs recém-adicionadas e continuar usando o driver de GPU (HAL 1.0) em outro lugar.

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

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

Etapa Detalhes
Compilar
  • O bitcode de entrada (.bc) para bcc precisa estar no formato de bitcode LLVM 3.2 e bcc precisa ser compatível com versões anteriores dos apps existentes (legados).
  • No entanto, os metadados no .bc podem mudar (pode haver novas funções de tempo de execução, por exemplo, Setters e getters de alocação, funções matemáticas etc. Parte das funções de ambiente de execução fica em libclcore.bc e parte delas fica no LibRSDriver ou no equivalente do fornecedor.
  • Novas funções de execução ou mudanças de metadados interruptivas exigem o incremento do nível da API do bitcode. Como os drivers do fornecedor não podem consumi-lo, a versão do HAL também precisa ser incrementada.
  • Os fornecedores podem ter seus próprios compiladores, mas as conclusões/requisitos de bcc também se aplicam a eles.
Link
  • O arquivo .o compilado será vinculado ao driver do fornecedor, por exemplo, libRSDriver_foo.so e libcompiler_rt.so. O caminho da CPU vai ser vinculado a libRSDriver.so.
  • Se o arquivo .o exigir uma nova API de ambiente de execução de libRSDriver_foo, o driver do fornecedor precisará ser atualizado para ser compatível.
  • Alguns fornecedores podem ter os próprios linkers, mas o argumento para ld.mc também se aplica a eles.
Carregamento
  • libRSCpuRef carrega o objeto compartilhado. Se houver mudanças nessa interface, será necessário um aumento de versão da HAL.
  • Os fornecedores dependem de libRSCpuRef para carregar o objeto compartilhado ou implementar o próprio.

Além do HAL, as APIs 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 mudá-la no Android 8.0 e em versões mais recentes. No entanto, se a interface mudar, a versão do HAL também vai aumentar.

Implementações do fornecedor

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

Módulos de driver

  • Os módulos de driver não podem depender de nenhuma biblioteca do sistema que não esteja 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 de CPU libRSDriver.so é um bom exemplo de como remover dependências que não são do VNDK-SP.

Compilador de bitcode

Você pode compilar bitcode do RenderScript para o driver do fornecedor de duas maneiras:

  1. Invocar o compilador do RenderScript específico do fornecedor em /vendor/bin/ (método preferido 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. Chamar 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 bcc plugin do fornecedor precisar interferir na compilação da CPU e a dependência de libLLVM.so não puder ser removida facilmente, o fornecedor precisará copiar bcc (e todas as dependências que não sejam 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. Alterações 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 da 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 do 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 ser invocado por um processo do 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 é inferior a 26.
  2. PRODUCT_FULL_TREBLE_OVERRIDE não está definido.

Para dispositivos legados, as restrições não são aplicadas ao fazer upgrade para o Android 8.0 e versões mais recentes, o que significa que os drivers podem continuar vinculando às 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. Se isso não for feito, o tempo de execução do RenderScript será substituído pelo caminho da CPU.

Para informações sobre o motivo da descontinuação do Renderscript, consulte o Blog de desenvolvedores Android: Android GPU Compute Going Forward (link em inglês). As informações de recursos para essa descontinuação incluem o seguinte: