Renderscript

RenderScript is a framework for running computationally intensive tasks at high performance on Android. It is designed for use with data-parallel computation, although serial workloads can benefit as well. The RenderScript runtime parallelizes work across processors available on a device, such as multi-core CPUs and GPUs, enabling developers to focus on expressing algorithms rather than scheduling work. RenderScript is especially useful for applications performing image processing, computational photography, or computer vision.

Android O devices use the following RenderScript framework and vendor HALs:

Figure 1. Vendor code linking to internal libs.

Differences from RenderScript in Android 7.x and earlier include:

  • Two instances of RenderScript internal libs in a process. One set is for CPU fallback path and is from directly at /system/lib; the other set is for GPU path and is from /system/lib/vndk-sp.
  • RS internal libs in /system/lib are built as part of the platform and are updated as system.img is upgraded. However, libs in /system/lib/vndk-sp are built for the vendor and are not updated when system.img is upgraded (while they can be updated for a security fix, their ABI remains the same).
  • Vendor code (RS HAL, RS driver, and the bcc plugin) are linked against the RenderScript internal libs located at /system/lib/vndk-sp. They cannot link against libs in /system/lib because libs in that directory are built for the platform and thus may not be compatible with the vendor code (i.e., symbols may be removed). Doing so would make a framework-only OTA impossible.

For more details, see Renderscript on developer.android.com.

Design

The following sections detail RenderScript design in Android O.

RenderScript libs available to vendors

This section lists the RenderScript libs (known as Vendor NDK for Same-Process HALs or VNDK-SP) that are available to vendor code and which can be linked against. It also details additional libraries that are unrelated to RenderScript but which are also provided to vendor code.

While the following list of libraries might differ between Android release, it is immutable for a specific Android release; for an up-to-date list of available libraries, refer to /system/etc/ld.config.txt.

RenderScript Libs Non-RenderScript Libs
  • 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

Linker namespace configuration

The linking restriction that prevents libs not in VNDK-SP from being used by vendor code is enforced at runtime using the linker namespace. (For details, refer to the VNDK Design in Android O presentation.)

On a device running Android O, all Same-Process HALs (SP-HALs) except RenderScript are loaded inside the linker namespace sphal. RenderScript is loaded into the RenderScript-specific namespace rs, a location that enables a slightly looser enforcement for RenderScript libs. Because the RS implementation needs to load the compiled bitcode, /data/*/*.so is added to the path of the rs namespace (other SP-HALs are not allowed to load libs from the data partition).

In addition, the rs namespace allows more libs than provided for by other namespaces. libmediandk.so and libft2.so are exposed to the rs namespace because libRS_internal.so has an internal dependency to these libraries.

Figure 2. Namespace configuration for linker.

Loading drivers

CPU fallback path

Depending on the existence of the RS_CONTEXT_LOW_LATENCY bit when creating an RS context, either the CPU or GPU path is selected. When the CPU path is selected, libRS_internal.so (the main implementation of the RS framework) is directly dlopened from the default linker namespace where the platform version of RS libs are provided.

The RS HAL implementation from the vendor is not used at all when the CPU fallback path is taken, and an RsContext object is created with null mVendorDriverName. libRSDriver.so is (by default) dlopened and the driver lib is loaded from the default namespace because the caller (libRS_internal.so) is also loaded in the default namespace.

Figure 4. CPU fallback path.

GPU path

For the GPU path, the libRS_internal.so is loaded differently. First, libRS.so uses android.hardware.renderscript@1.0.so (and its underlying libhidltransport.so) to load android.hardware.renderscript@1.0-impl.so (a vendor implementation of RS HAL) into a different linker namespace called sphal. The RS HAL then dlopens libRS_internal.so in a another linker namespace called rs.

Vendors can provide their own RS driver by setting the build-time flag OVERRIDE_RS_DRIVER, which is embedded into the RS HAL implementation (hardware/interfaces/renderscript/1.0/default/Context.cpp). This driver name is then dlopened for the RS context for the GPU path.

The creation of the RsContext object is delegated to the RS HAL implementation. The HAL calls back to the RS framework using rsContextCreateVendor() function with the name of the driver to use as an argument. The RS framework then loads the specified driver when the RsContext is initialized. In this case, the driver library is loaded into the rs namespace because the RsContext object is created inside the rs namespace and /vendor/lib is in the search path of the namespace.

Figure 5. GPU fallback path.

When transitioning from the default namespace to the sphal namespace, libhidltransport.so uses the android_load_sphal_library() function to explicitly order the dynamic linker to load the -impl.so library from the sphal namespace.

When transitioning from the sphal namespace to the rs namespace, loading is done indirectly by the following line in /system/etc/ld.config.txt:

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

This line specifies the dynamic linker should load libRS_internal.so from the rs namespace when the lib can't be found/loaded from the sphal namespace (which is always the case because sphal namespace does not search /system/lib/vndk-sp where libRS_internal.so resides). With this configuration, a simple dlopen() call to libRS_internal.so is enough to make the namespace transition.

Loading bcc plugin

bcc plugin is a vendor-provided library loaded into the bcc compiler. Because bcc is a system process in the /system/bin directory, the bcc plugin library can be considered an SP-HAL (i.e., a vendor HAL that can be directly loaded into the system process without being binderized). As an SP-HAL, the bcc-plugin library:

  • Cannot link against framework-only libraries such as libLLVM.so.
  • Can link against only the VNDK-SP libraries available to the vendor.

This restriction is enforced by loading the bcc plugin into the sphal namespace using the android_sphal_load_library() function. In previous versions of Android, the plugin name was specified using the -load option and the lib was loaded using the simple dlopen() by libLLVM.so. In Android O, this is specified in -plugin option and the lib is directly loaded by the bcc itself. This option enables a non-Android-specific path to the open source LLVM project.

Figure 6. Loading bcc plugin, Android 7.x and earlier.


Figure 7. Loading bcc plugin, Android O.

Search paths for ld.mc

When executing ld.mc, some RS runtime libs are given as inputs to the linker. The RS bitcode from the app is linked against the runtime libs and when the converted bitcode is loaded into an app process, the runtime libs are again dynamically linked from the converted bitcode.

Runtime libs include:

  • libcompiler_rt.so
  • libm.so
  • libc.so
  • RS driver (either libRSDriver.so or OVERRIDE_RS_DRIVER)

When loading the compiled bitcode into the app process, we must provide the exact same library that was used by ld.mc. Otherwise, the compiled bitcode may not find a symbol which was available when it was linked.

To do so, RS framework uses different search paths for the runtime libs when executing ld.mc, depending on whether the RS framework itself is loaded from /system/lib or from /system/lib/vndk-sp. This can be determined by reading the address of an arbitrary symbol of a RS framework lib and using dladdr() to get the file path mapped to the address.

SELinux policy

As a result of the SELinux policy changes in Android O, you must follow specific rules (enforced through neverallows) when labelling additional files in vendor partition:

  • vendor_file must be the default label in for all files in vendor partition. The platform policy requires this to access passthrough HAL implementations.
  • All new exec_types added in vendor partition through vendor SEPolicy must have vendor_file_type attribute. This is enforced through neverallows.
  • To avoid conflicts with future platform/framework updates, avoid labelling files other than exec_types in vendor partition.
  • All library dependencies for AOSP-identified same process HALs must be labelled as same_process_hal_file.

ABI compatibility for bitcode

If no new APIs are added, which means no HAL version bump, the RS frameworks will keep using the existing GPU (HAL 1.0) driver.

For minor HAL changes (HAL 1.1) not affecting bitcode, the frameworks should fallback to CPU for these newly added APIs and keep using GPU (HAL 1.0) driver elsewhere.

For major HAL changes (HAL 2.0) affecting bitcode compilation/linking, RS frameworks should choose not to load vendor-provided GPU drivers and instead use the CPU or Vulkan path for acceleration.

Consuming RenderScript bitcode occurs in three stages:

Stage Details
Compile
  • The input bitcode (.bc) for bcc must be in LLVM 3.2 bitcode format and bcc must be backward compatible with existing (legacy) apps.
  • However, the meta-data in .bc could change (there could new runtime functions, e.g, Allocation setters ∓ getters, math functions, etc.). Part of the runtime functions lives in libclcore.bc, part of them lives in LibRSDriver or vendor equivalent.
  • New runtime functions or breaking meta-data changes require incrementing the bitcode API level. Because vendor drivers won't be able to consume it, the HAL version must also be incremented.
  • Vendors may have their own compilers, but the conclusions/requirements for bcc also apply to those compilers.
Link
  • The compiled .o will be linked with vendor driver, e.g, libRSDriver_foo.so, and libcompiler_rt.so. The CPU path will link with libRSDriver.so.
  • If the .o requires a new runtime API from libRSDriver_foo, the vendor driver has to be updated to support it.
  • Certain vendors may have their own linkers, but the argument for ld.mc also apply to them.
Load
  • libRSCpuRef loads the the shared object. If there are changes to this interface, a HAL version bump is needed.
  • Vendors would either rely on libRSCpuRef to load the shared object, or implement their own.

In addition to the HAL, runtime APIs and the exported symbols are also interfaces. Neither interface has changed since Android 7.0 (API 24) and there are no immediate plans to change it in Android O and beyond. However, if the interface does change, the HAL version will also increment.

Vendor implementations

Android O requires some GPU driver changes for the GPU driver to work correctly.

Driver modules

  • Driver modules must not depend on any system libraries that are not in the list.
  • Driver must provide its own android.hardware.renderscript@1.0-impl_{NAME}, or declare the default implementation android.hardware.renderscript@1.0-impl as its dependency.
  • CPU implementation libRSDriver.so is a good example of how to remove non-VNDK-SP dependencies.

Bitcode compiler

You can compile RenderScript bitcode for the vendor driver in two ways:

  1. Invoke vendor-specific RenderScript compiler in /vendor/bin/ (preferred method of GPU compilation). Similar to other driver modules, the vendor compiler binary cannot depend on any system library that is not in the list of RenderScript libs available to vendors.
  2. Invoke system bcc: /system/bin/bcc with a vendor-provided bcc plugin. The bcc plugin cannot depend on any system library that is not in the list of RenderScript libs available to vendors.

If the vendor bcc plugin needs to interfere with the CPU compilation and its dependency on libLLVM.so cannot be easily removed, the vendor should copy bcc (and all the non-LL-NDK dependencies, including libLLVM.so, libbcc.so) into /vendor partition.

In addition, vendors need to make the following changes:

Figure 8. Changes to vendor driver.
  1. Copy libclcore.bc to /vendor partition. This ensures libclcore.bc, libLLVM.so, and libbcc.so are in sync.
  2. Change the path to the bcc executable by setting RsdCpuScriptImpl::BCC_EXE_PATH from the RS HAL implementation.

SELinux policy

SELinux policy affects both the driver and the compiler executables. All driver modules must be labeled same_process_hal_file in the device's file_contexts. For example:

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

The compiler executable must be able to be invoked by an app process, as does the vendor copy of bcc (/vendor/bin/bcc). For example:

device/vendor_foo/device_bar/sepolicy/file.te:
type renderscript_exec, exec_type, file_type;

device/vendor_foo/device_bar/sepolicy/app.te:
allow appdomain renderscript_exec:file { read open getattr execute execute_no_trans };

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

Legacy devices

Legacy devices are those that satisfy the following conditions:

  1. PRODUCT_SHIPPING_API_LEVEL is lower than 26.
  2. PRODUCT_FULL_TREBLE_OVERRIDE is not defined.

For legacy devices, the restrictions detailed throughout this document are not enforced when upgrading to Android O, meaning the drivers can continue to link to libraries in /system/lib[64]. However, because of the architecture change related with OVERRIDE_RS_DRIVER, android.hardware.renderscript@1.0-impl must be installed to /vendor partition; failing to do so forces RenderScript runtime fallback to CPU path.