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 com 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. O código do fornecedor é vinculado a libs internas.

As diferenças em relação ao 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. Não é possível vincular as bibliotecas em /system/lib porque elas 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 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 RenderScript (conhecidas como NDK do fornecedor para HALs de mesmo processo ou VNDK-SP) que estão 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 do Android. Para conferir uma lista atualizada de 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 rs específico do RenderScript, um local que permite uma aplicação um pouco mais flexível das 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 de build time 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 do HAL do 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 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 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 o plug-in Cco

bcc plugin é uma biblioteca fornecida pelo fornecedor 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 SP, ou seja, um HAL do fornecedor que pode ser carregado diretamente no processo do sistema sem ser vinculado. Como um 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(). 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 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. 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 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 pelo 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 código de bits compilado no processo do app, forneça a mesma biblioteca 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 isso, o framework RS usa caminhos de pesquisa diferentes para as bibliotecas de tempo de execução ao executar ld.mc, dependendo se o 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 acessar 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 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, não houver um aumento na versão do HAL, os frameworks do RS vão continuar usando o driver de GPU (HAL 1.0) atual.

Para mudanças menores no 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 do 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:

Estágio 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 de apps.
  • 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 execução fica no libclcore.bc, 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 compiladores próprios, mas as conclusões/requisitos para bcc também se aplicam a esses compiladores.
Link
  • O .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 .o exigir uma nova API 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.
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 mudar isso no Android 8.0 e 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 no 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

É possível compilar o 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. 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 da implementação da HAL do RS.

Política do SELinux

A política do SELinux afeta o driver e os executáveis 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 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 é menor que 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 fallback do tempo de execução do RenderScript será forçado para o caminho da CPU.

Para informações sobre a motivação para a descontinuação do Renderscript, consulte o blog Android Developers: Android GPU Compute Going Forward. As informações de recursos para essa suspensão de uso incluem o seguinte: