RenderScript

RenderScript es un marco de trabajo para ejecutar tareas de procesamiento intensivo con alto rendimiento en Android. Está diseñado para usarse con procesamiento paralelo de datos, aunque las cargas de trabajo en serie también pueden beneficiarse. El entorno de ejecución de RenderScript permite trabajar en paralelo con todos los procesadores disponibles en un dispositivo, como las GPU y CPU de varios núcleos, lo que permite a los desarrolladores enfocarse en expresar algoritmos en lugar de programar el trabajo. RenderScript es especialmente útil para apps que ejecutan procesamiento de imágenes, fotografía computacional o visión artificial.

Los dispositivos que ejecutan Android 8.0 y versiones posteriores usan las siguientes HAL del framework y del proveedor de RenderScript:

Figura 1: Código del proveedor que vincula a bibliotecas internas

Entre las diferencias con RenderScript en Android 7.x y versiones anteriores, se incluyen las siguientes:

  • Dos instancias de bibliotecas internas de RenderScript en un proceso Un conjunto es para la ruta de resguardo de la CPU y proviene directamente de /system/lib. El otro conjunto es para la ruta de la GPU y proviene de /system/lib/vndk-sp.
  • Las bibliotecas internas de RS en /system/lib se compilan como parte de la plataforma y se actualizan a medida que se actualiza system.img. Sin embargo, las bibliotecas de /system/lib/vndk-sp se compilan para el proveedor y no se actualizan cuando se actualiza system.img (si bien se pueden actualizar para una corrección de seguridad, su ABI sigue siendo la misma).
  • El código de proveedor (HAL de RS, controlador de RS y bcc plugin) está vinculado con las bibliotecas internas de RenderScript ubicadas en /system/lib/vndk-sp. No pueden vincularse con las bibliotecas en /system/lib porque las bibliotecas de ese directorio se compilan para la plataforma y, por lo tanto, es posible que no sean compatibles con el código del proveedor (es decir, es posible que se quiten los símbolos). De lo contrario, sería imposible realizar una actualización OTA solo de framework.

Diseño

En las siguientes secciones, se detalla el diseño de RenderScript en Android 8.0 y versiones posteriores.

Bibliotecas de RenderScript disponibles para los proveedores

En esta sección, se enumeran las bibliotecas de RenderScript (conocidas como NDK del proveedor para HALs de mismo proceso o VNDK-SP) que están disponibles para el código del proveedor y con las que se pueden vincular. También detalla las bibliotecas adicionales que no están relacionadas con RenderScript, pero que también se proporcionan al código del proveedor.

Si bien la siguiente lista de bibliotecas puede diferir entre las versiones de Android, es inmutable para una versión específica de Android. Para obtener una lista actualizada de las bibliotecas disponibles, consulta /system/etc/ld.config.txt.

Bibliotecas de RenderScript Bibliotecas que no son de 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

Configuración del espacio de nombres del vinculador

La restricción de vinculación que evita que el código del proveedor use las bibliotecas que no están en VNDK-SP se aplica durante el tiempo de ejecución con el espacio de nombres del vinculador. (Para obtener más información, consulta la presentación de Diseño de VNDK).

En un dispositivo que ejecuta Android 8.0 y versiones posteriores, todas las HAL de mismo proceso (SP-HAL), excepto RenderScript, se cargan dentro del espacio de nombres del vinculador sphal. RenderScript se carga en el espacio de nombres rs específico de RenderScript, una ubicación que habilita una aplicación forzosa un poco más flexible para las bibliotecas de RenderScript. Debido a que la implementación de RS necesita cargar el código de bytes compilado, se agrega /data/*/*.so a la ruta de acceso del espacio de nombres rs (otros SP-HAL no pueden cargar bibliotecas desde la partición de datos).

Además, el espacio de nombres rs permite más bibliotecas que las que proporcionan otros espacios de nombres. libmediandk.so y libft2.so están expuestos al espacio de nombres rs porque libRS_internal.so tiene una dependencia interna a estas bibliotecas.

Figura 2: Configuración del espacio de nombres para el vinculador.

Controladores de carga

Ruta de resguardo de la CPU

Según la existencia del bit RS_CONTEXT_LOW_LATENCY cuando se crea un contexto RS, se selecciona la ruta de la CPU o la GPU. Cuando se selecciona la ruta de acceso a la CPU, libRS_internal.so (la implementación principal del framework de RS) se dlopense conecta directamente desde el espacio de nombres del vinculador predeterminado donde se proporciona la versión de la plataforma de las bibliotecas RS.

La implementación de RS HAL del proveedor no se usa en absoluto cuando se toma la ruta de resguardo de la CPU y se crea un objeto RsContext con un mVendorDriverName nulo. libRSDriver.so se dlopen (de forma predeterminada) y la biblioteca del controlador se carga desde el espacio de nombres default porque el llamador (libRS_internal.so) también se carga en el espacio de nombres default.

Figura 3: Ruta de resguardo de la CPU.

Ruta de acceso de la GPU

En el caso de la ruta de acceso de la GPU, libRS_internal.so se carga de manera diferente. Primero, libRS.so usa android.hardware.renderscript@1.0.so (y su libhidltransport.so subyacente) para cargar android.hardware.renderscript@1.0-impl.so (una implementación de proveedor de la HAL de RS) en un espacio de nombres de vinculador diferente llamado sphal. Luego, el HAL de RS dlopen libRS_internal.so en otro espacio de nombres del vinculador llamado rs.

Los proveedores pueden proporcionar su propio controlador de RS configurando la marca de tiempo de compilación OVERRIDE_RS_DRIVER, que está incorporada en la implementación de HAL de RS (hardware/interfaces/renderscript/1.0/default/Context.cpp). Luego, este nombre de controlador se dlopen para el contexto de RS de la ruta de acceso de la GPU.

La creación del objeto RsContext se delega a la implementación de HAL de RS. La HAL vuelve a llamar al framework de RS usando la función rsContextCreateVendor() con el nombre del controlador que se usará como argumento. Luego, el framework de RS carga el controlador especificado cuando se inicializa RsContext. En este caso, la biblioteca del controlador se carga en el espacio de nombres rs porque el objeto RsContext se crea dentro del espacio de nombres rs y /vendor/lib está en la ruta de búsqueda del espacio de nombres.

Figura 4: Ruta de resguardo de GPU.

Cuando se realiza la transición del espacio de nombres default al espacio de nombres sphal, libhidltransport.so usa la función android_load_sphal_library() para ordenar de forma explícita al vinculador dinámico que cargue la biblioteca -impl.so desde el espacio de nombres sphal.

Cuando se realiza la transición del espacio de nombres sphal al espacio de nombres rs, la carga se realiza de forma indirecta con la siguiente línea en /system/etc/ld.config.txt:

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

Esta línea especifica que el vinculador dinámico debe cargar libRS_internal.so desde el espacio de nombres rs cuando no se pueda encontrar ni cargar la biblioteca desde el espacio de nombres sphal (como sucede siempre porque el espacio de nombres sphal no busca /system/lib/vndk-sp donde reside libRS_internal.so). Con esta configuración, una simple llamada dlopen() a libRS_internal.so es suficiente para realizar la transición del espacio de nombres.

Carga el complemento de Cco

bcc plugin es una biblioteca proporcionada por el proveedor que se carga en el compilador bcc. Debido a que bcc es un proceso del sistema en el directorio /system/bin, la biblioteca bcc plugin se puede considerar un SP-HAL (es decir, un HAL del proveedor que se puede cargar directamente en el proceso del sistema sin estar vinculado). Como un SP-HAL, la biblioteca bcc-plugin hace lo siguiente:

  • No se puede vincular con bibliotecas exclusivas del framework, como libLLVM.so.
  • Se puede vincular solo con las bibliotecas de VNDK-SP disponibles para el proveedor.

Esta restricción se aplica mediante la carga de bcc plugin en el espacio de nombres sphal con la función android_sphal_load_library(). En versiones anteriores de Android, el nombre del complemento se especificaba con la opción -load y la biblioteca se cargaba con el dlopen() simple de libLLVM.so. En Android 8.0 y versiones posteriores, esto se especifica en la opción -plugin, y bcc carga directamente la biblioteca. Esta opción habilita una ruta de acceso no específica de Android al proyecto de LLVM de código abierto.

Figura 5: Carga del complemento bcc, Android 7.x y versiones anteriores.



Figura 6: Carga del complemento bcc, Android 8.0 y versiones posteriores

Rutas de búsqueda para ld.mc

Cuando se ejecuta ld.mc, algunas bibliotecas del entorno de ejecución de RS se proporcionan como entradas al vinculador. El código de bytes de RS de la app está vinculado con las bibliotecas del entorno de ejecución y, cuando el código de bytes convertido se carga en un proceso de la app, las bibliotecas del entorno de ejecución se vuelven a vincular de forma dinámica desde el código de bytes convertido.

Las bibliotecas de entorno de ejecución incluyen lo siguiente:

  • libcompiler_rt.so
  • libm.so
  • libc.so
  • Controlador de RS (libRSDriver.so o OVERRIDE_RS_DRIVER)

Cuando cargues el código de bytes compilado en el proceso de la app, proporciona la misma biblioteca que usó ld.mc. De lo contrario, es posible que el código de bytes compilado no encuentre un símbolo que estaba disponible cuando se vinculó.

Para ello, el framework de RS usa diferentes rutas de búsqueda para las bibliotecas del entorno de ejecución cuando se ejecuta ld.mc, según si el framework de RS se carga desde /system/lib o desde /system/lib/vndk-sp. Para determinar esto, se puede leer la dirección de un símbolo arbitrario de una biblioteca del framework de RS y usar dladdr() para obtener la ruta de acceso del archivo asignada a la dirección.

Política de SELinux

Como resultado de los cambios en la política de SELinux en Android 8.0 y versiones posteriores, debes seguir reglas específicas (aplicadas a través de neverallows) cuando etiquetas archivos adicionales en la partición vendor:

  • vendor_file debe ser la etiqueta predeterminada para todos los archivos de la partición vendor. La política de la plataforma requiere esto para acceder a las implementaciones de HAL de transferencia.
  • Todos los exec_types nuevos agregados en la partición vendor a través de SEPolicy del proveedor deben tener el atributo vendor_file_type. Esto se aplica a través de neverallows.
  • Para evitar conflictos con futuras actualizaciones de la plataforma o el framework, evita etiquetar archivos que no sean exec_types en la partición vendor.
  • Todas las dependencias de bibliotecas para los HAL de procesos idénticos identificados por AOSP deben etiquetarse como same_process_hal_file.

Para obtener más información sobre la política de SELinux, consulta Security-Enhanced Linux en Android.

Compatibilidad de ABI para código de bits

Si no se agregan APIs nuevas, lo que significa que no hay un aumento de versión de HAL, los frameworks de RS seguirán usando el controlador de GPU existente (HAL 1.0).

En el caso de los cambios menores de HAL (HAL 1.1) que no afectan el código de bytes, los frameworks deben recurrir a la CPU para estas APIs recién agregadas y seguir usando el controlador de GPU (HAL 1.0) en otro lugar.

En el caso de los cambios importantes en HAL (HAL 2.0) que afectan la compilación o vinculación de código de bytes, los frameworks de RS deben elegir no cargar los controladores de GPU proporcionados por el proveedor y, en su lugar, usar la ruta de acceso de la CPU o Vulkan para la aceleración.

El consumo de código de bits de RenderScript se produce en tres etapas:

Etapa Detalles
Compilar
  • El código de bytes de entrada (.bc) para bcc debe estar en formato de código de bytes LLVM 3.2 y bcc debe ser retrocompatible con las apps existentes (heredadas).
  • Sin embargo, los metadatos en .bc podrían cambiar (podría haber funciones de entorno de ejecución nuevas, p.ej., Setters y getters de asignación, funciones matemáticas, etcétera). Parte de las funciones del entorno de ejecución reside en libclcore.bc, y parte de ellas reside en LibRSDriver o en el equivalente del proveedor.
  • Las nuevas funciones del entorno de ejecución o los cambios rotundos en los metadatos requieren aumentar el nivel de API de código de bits. Debido a que los controladores de proveedores no podrán consumirlo, la versión de HAL también debe aumentarse.
  • Los proveedores pueden tener sus propios compiladores, pero las conclusiones o requisitos para bcc también se aplican a esos compiladores.
Vínculo
  • El archivo .o compilado se vinculará con el controlador del proveedor, p.ej., libRSDriver_foo.so y libcompiler_rt.so. La ruta de acceso de la CPU se vinculará con libRSDriver.so.
  • Si el .o requiere una nueva API de tiempo de ejecución de libRSDriver_foo, se debe actualizar el controlador del proveedor para admitirlo.
  • Ciertos proveedores pueden tener sus propios vinculadores, pero el argumento de ld.mc también se aplica a ellos.
Carga
  • libRSCpuRef carga el objeto compartido. Si se realizan cambios en esta interfaz, se necesitará un cambio de versión de HAL.
  • Los proveedores usarían libRSCpuRef para cargar el objeto compartido o implementarían el suyo propio.

Además del HAL, las APIs del entorno de ejecución y los símbolos exportados también son interfaces. Ninguna interfaz cambió desde Android 7.0 (nivel de API 24) y no hay planes inmediatos para cambiarla en Android 8.0 y versiones posteriores. Sin embargo, si la interfaz cambia, la versión de HAL también aumentará.

Implementaciones de proveedores

Android 8.0 y las versiones posteriores requieren algunos cambios en el controlador de GPU para que este funcione correctamente.

Módulos de controladores

  • Los módulos de controladores no deben depender de ninguna biblioteca del sistema que no esté en la lista.
  • El controlador debe proporcionar su propio android.hardware.renderscript@1.0-impl_{NAME} o declarar la implementación predeterminada android.hardware.renderscript@1.0-impl como su dependencia.
  • La implementación de CPU libRSDriver.so es un buen ejemplo de cómo quitar dependencias que no pertenecen al VNDK-SP.

Compilador de código de bits

Puedes compilar el código de bits de RenderScript para el controlador del proveedor de dos maneras:

  1. Invoca el compilador RenderScript específico del proveedor en /vendor/bin/ (método preferido de compilación de GPU). Al igual que otros módulos de controlador, el objeto binario del compilador del proveedor no puede depender de ninguna biblioteca del sistema que no esté en la lista de bibliotecas de RenderScript disponibles para los proveedores.
  2. Invoca el sistema bcc: /system/bin/bcc con un bcc plugin proporcionado por el proveedor. Este complemento no puede depender de ninguna biblioteca del sistema que no esté en la lista de bibliotecas de RenderScript disponibles para los proveedores.

Si el proveedor bcc plugin necesita interferir en la compilación de la CPU y su dependencia de libLLVM.so no se puede quitar fácilmente, el proveedor debe copiar bcc (y todas las dependencias que no sean de LL-NDK, incluidas libLLVM.so y libbcc.so) en la partición /vendor.

Además, los proveedores deben realizar los siguientes cambios:

Figura 7: Cambios en el controlador de proveedor.

  1. Copia libclcore.bc en la partición /vendor. Esto garantiza que libclcore.bc, libLLVM.so y libbcc.so estén sincronizados.
  2. Para cambiar la ruta de acceso al ejecutable bcc, configura RsdCpuScriptImpl::BCC_EXE_PATH desde la implementación de HAL de RS.

Política de SELinux

La política de SELinux afecta al controlador y a los ejecutables del compilador. Todos los módulos de controladores deben etiquetarse como same_process_hal_file en el file_contexts del dispositivo. Por ejemplo:

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

El proceso de la app debe poder invocar el ejecutable del compilador, al igual que la copia del proveedor de bcc (/vendor/bin/bcc). Por ejemplo:

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

Dispositivos heredados

Los dispositivos heredados son aquellos que cumplen con las siguientes condiciones:

  1. PRODUCT_SHIPPING_API_LEVEL es inferior a 26.
  2. No se definió PRODUCT_FULL_TREBLE_OVERRIDE.

En el caso de los dispositivos heredados, las restricciones no se aplican cuando se actualiza a Android 8.0 y versiones posteriores, lo que significa que los controladores pueden seguir vinculándose a bibliotecas en /system/lib[64]. Sin embargo, debido al cambio de arquitectura relacionado con OVERRIDE_RS_DRIVER, android.hardware.renderscript@1.0-impl debe instalarse en la partición /vendor. De lo contrario, se forzará el resguardo del entorno de ejecución de RenderScript en la ruta de acceso de la CPU.

Para obtener más información sobre los motivos por los que Renderscript dejó de estar disponible, consulta el Blog para desarrolladores de Android: Android GPU Compute Going Forward. La información de recursos para esta baja incluye lo siguiente: