RenderScript

RenderScript to platforma do uruchamiania na Androidzie zadań wymagających dużej mocy obliczeniowej z wysoką wydajnością. Jest on przeznaczony do obliczeń równoległych na danych, ale może być też przydatny w przypadku zadań wykonywanych sekwencyjnie. Środowisko wykonawcze RenderScriptu równolegle przetwarza zadania na procesorach dostępnych na urządzeniu, takich jak wielordzeniowe procesory CPU i GPU, dzięki czemu deweloperzy mogą skupić się na wyrażaniu algorytmów, a nie na planowaniu pracy. RenderScript jest szczególnie przydatny w przypadku aplikacji, które przetwarzają obrazy, wykonują obliczenia fotograficzne lub korzystają z widzenia komputerowego.

Urządzenia z Androidem 8.0 i nowszym korzystają z tych platform RenderScript i HAL dostawców:

Rysunek 1. Kod dostawcy łączący się z bibliotekami wewnętrznymi.

Różnice w stosunku do RenderScript w Androidzie 7.x i starszych wersjach:

  • Dwie instancje wewnętrznych bibliotek RenderScript w procesie. Jeden zestaw jest przeznaczony dla ścieżki rezerwowej procesora i pochodzi bezpośrednio z /system/lib, a drugi zestaw jest przeznaczony dla ścieżki procesora graficznego i pochodzi z /system/lib/vndk-sp.
  • Wewnętrzne biblioteki RS w /system/lib są tworzone w ramach platformy i aktualizowane wraz z system.img. Biblioteki w /system/lib/vndk-sp są jednak tworzone dla dostawcy i nie są aktualizowane podczas uaktualniania system.img (mogą być aktualizowane w celu poprawienia zabezpieczeń, ale ich interfejs ABI pozostaje taki sam).
  • Kod dostawcy (RS HAL, sterownik RS i bcc plugin) jest połączony z wewnętrznymi bibliotekami RenderScript znajdującymi się w /system/lib/vndk-sp. Nie mogą one łączyć się z bibliotekami w /system/lib, ponieważ biblioteki w tym katalogu są przeznaczone na platformę i mogą nie być zgodne z kodem dostawcy (np. symbole mogą zostać usunięte). Uniemożliwiłoby to aktualizację OTA tylko z platformą.

Projektowanie

W kolejnych sekcjach znajdziesz szczegółowe informacje o architekturze RenderScript w Androidzie 8.0 i nowszych wersjach.

Biblioteki RenderScript dostępne dla dostawców

W tej sekcji znajdziesz listę bibliotek RenderScript (znanych jako Vendor NDK for Same-Process HALs lub VNDK-SP), które są dostępne dla kodu dostawcy i z którymi można się łączyć. Opisuje też dodatkowe biblioteki, które nie są związane z RenderScriptem, ale są też udostępniane w kodzie dostawcy.

Poniższa lista bibliotek może się różnić w zależności od wersji Androida, ale w przypadku konkretnej wersji Androida jest niezmienna. Aktualną listę dostępnych bibliotek znajdziesz w /system/etc/ld.config.txt.

Biblioteki RenderScript Biblioteki inne niż 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

Konfiguracja przestrzeni nazw tagu łączącego

Ograniczenie łączenia, które uniemożliwia używanie bibliotek spoza VNDK-SP przez kod dostawcy, jest egzekwowane w czasie działania za pomocą przestrzeni nazw linkera. (Więcej informacji znajdziesz w prezentacji VNDK Design).

Na urządzeniach z Androidem 8.0 i nowszym wszystkie interfejsy HAL tego samego procesu (SP-HAL) z wyjątkiem RenderScript są ładowane w przestrzeni nazw linkerasphal. RenderScript jest ładowany do przestrzeni nazw rs, która jest przeznaczona dla RenderScript i umożliwia nieco mniej rygorystyczne egzekwowanie zasad w przypadku bibliotek RenderScript. Implementacja RS musi wczytać skompilowany kod pośredni, więc do ścieżki przestrzeni nazw rs dodawany jest element /data/*/*.so (inne interfejsy SP-HAL nie mogą wczytywać bibliotek z partycji danych).

Dodatkowo przestrzeń nazw rs umożliwia korzystanie z większej liczby bibliotek niż inne przestrzenie nazw. Użytkownicy libmediandk.solibft2.so mają dostęp do przestrzeni nazw rs, ponieważ libRS_internal.so ma wewnętrzną zależność od tych bibliotek.

Rysunek 2. Konfiguracja przestrzeni nazw dla linkera.

Wczytywanie sterowników

Ścieżka rezerwowa procesora

W zależności od tego, czy podczas tworzenia kontekstu RS istnieje bit RS_CONTEXT_LOW_LATENCY, wybierana jest ścieżka procesora lub procesora graficznego. Gdy wybrana jest ścieżka procesora, libRS_internal.so (główna implementacja struktury RS) jest bezpośrednio dlopenowana z domyślnej przestrzeni nazw linkera, w której udostępniana jest wersja platformy bibliotek RS.

Implementacja RS HAL dostawcy nie jest w ogóle używana, gdy wybierana jest ścieżka rezerwowa CPU, a obiekt RsContext jest tworzony z wartością null mVendorDriverName. libRSDriver.so jest (domyślnie) dlopened, a biblioteka sterownika jest wczytywana z przestrzeni nazw default, ponieważ wywołujący (libRS_internal.so) jest również wczytywany w przestrzeni nazw default.

Rysunek 3. Ścieżka rezerwowa procesora.

Ścieżka GPU

W przypadku ścieżki GPU element libRS_internal.so jest wczytywany inaczej. Po pierwsze, libRS.so używa android.hardware.renderscript@1.0.so (i jego bazowego libhidltransport.so) do wczytywania android.hardware.renderscript@1.0-impl.so (implementacji RS HAL dostawcy) do innej przestrzeni nazw linkera o nazwie sphal. RS HAL następnie dlopens libRS_internal.so w innej przestrzeni nazw linkera o nazwie rs.

Dostawcy mogą udostępniać własny sterownik RS, ustawiając flagę czasu kompilacji OVERRIDE_RS_DRIVER, która jest osadzona w implementacji RS HAL (hardware/interfaces/renderscript/1.0/default/Context.cpp). Nazwa tego sterownika jest następnie dlopenana w kontekście RS na potrzeby ścieżki GPU.

Tworzenie obiektu RsContext jest delegowane do implementacji RS HAL. HAL wywołuje zwrotnie framework RS za pomocą funkcji rsContextCreateVendor() z nazwą sterownika, który ma być używany jako argument. Po zainicjowaniu RsContext platforma RS wczytuje określony sterownik. W tym przypadku biblioteka sterownika jest ładowana do przestrzeni nazw rs, ponieważ obiekt RsContext jest tworzony w przestrzeni nazw rs, a /vendor/lib znajduje się w ścieżce wyszukiwania przestrzeni nazw.

Rysunek 4. Ścieżka rezerwowa GPU.

Podczas przechodzenia z przestrzeni nazw default do przestrzeni nazw sphal funkcja libhidltransport.so używa funkcji android_load_sphal_library(), aby jawnie nakazać dynamicznemu linkerowi załadowanie biblioteki -impl.so z przestrzeni nazw sphal.

Podczas przechodzenia z przestrzeni nazw sphal do przestrzeni nazw rs wczytywanie odbywa się pośrednio za pomocą tego wiersza w /system/etc/ld.config.txt:

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

Ten wiersz określa, że dynamiczny linker powinien wczytać libRS_internal.so z przestrzeni nazw rs, gdy nie można znaleźć lub wczytać biblioteki z przestrzeni nazw sphal (co zawsze ma miejsce, ponieważ przestrzeń nazw sphal nie przeszukuje /system/lib/vndk-sp, w której znajduje się libRS_internal.so). W tej konfiguracji wystarczy proste wywołanie dlopen() do libRS_internal.so, aby dokonać przejścia przestrzeni nazw.

Wczytaj wtyczkę bcc

bcc plugin to biblioteka dostarczona przez dostawcę, która jest wczytywana do kompilatora bcc. Ponieważ bcc jest procesem systemowym w katalogu /system/bin, bibliotekę bcc plugin można uznać za SP-HAL (czyli HAL dostawcy, który można bezpośrednio załadować do procesu systemowego bez użycia mechanizmu Binder). Jako SP-HAL biblioteka bcc-plugin:

  • Nie można łączyć z bibliotekami zawierającymi tylko struktury, takimi jak libLLVM.so.
  • Może łączyć się tylko z bibliotekami VNDK-SP dostępnymi dla dostawcy.

To ograniczenie jest egzekwowane przez załadowanie bcc plugin do przestrzeni nazw sphal za pomocą funkcji android_sphal_load_library(). W poprzednich wersjach Androida nazwa wtyczki była określana za pomocą opcji -load, a biblioteka była wczytywana za pomocą prostego polecenia dlopen() przez libLLVM.so. W Androidzie 8.0 i nowszym jest to określone w opcji -plugin, a biblioteka jest bezpośrednio wczytywana przez bcc. Ta opcja umożliwia dostęp do projektu open source LLVM, który nie jest przeznaczony dla Androida.

Rysunek 5. Ładowanie wtyczki bcc, Android 7.x i starsze.



Rysunek 6. Wczytywanie wtyczki bcc, Android 8.0 lub nowszy.

Ścieżki wyszukiwania dla ld.mc

Podczas wykonywania ld.mc niektóre biblioteki środowiska wykonawczego RS są przekazywane do linkera jako dane wejściowe. Kod bitowy RS z aplikacji jest połączony z bibliotekami środowiska wykonawczego, a gdy przekonwertowany kod bitowy jest wczytywany do procesu aplikacji, biblioteki środowiska wykonawczego są ponownie dynamicznie łączone z przekonwertowanym kodem bitowym.

Biblioteki środowiska wykonawczego obejmują:

  • libcompiler_rt.so
  • libm.so
  • libc.so
  • Sterownik RS (libRSDriver.so lub OVERRIDE_RS_DRIVER)

Podczas wczytywania skompilowanego kodu bitowego do procesu aplikacji podaj dokładnie tę samą bibliotekę, która została użyta przez ld.mc. W przeciwnym razie skompilowany kod bitowy może nie znaleźć symbolu, który był dostępny podczas łączenia.

W tym celu platforma RS używa różnych ścieżek wyszukiwania bibliotek środowiska wykonawczego podczas wykonywania ld.mc, w zależności od tego, czy sama platforma RS jest wczytywana z /system/lib czy z /system/lib/vndk-sp. Można to ustalić, odczytując adres dowolnego symbolu biblioteki platformy RS i używając dladdr(), aby uzyskać ścieżkę pliku mapowaną na adres.

Zasady SELinux

W wyniku zmian w zasadach SELinux w Androidzie 8.0 i nowszych podczas oznaczania dodatkowych plików na partycji vendor musisz przestrzegać określonych reguł (egzekwowanych za pomocą neverallows):

  • vendor_file musi być etykietą domyślną dla wszystkich plików w vendor partycji. Zasady platformy wymagają tego, aby uzyskać dostęp do implementacji HAL typu passthrough.
  • Wszystkie nowe exec_types dodane w vendor partycji za pomocą zasad SEPolicy dostawcy muszą mieć atrybut vendor_file_type. Jest to egzekwowane za pomocą neverallows.
  • Aby uniknąć konfliktów z przyszłymi aktualizacjami platformy lub struktury, nie oznaczaj plików innych niż exec_types w partycji vendor.
  • Wszystkie zależności biblioteki w przypadku zidentyfikowanych przez AOSP interfejsów HAL działających w tym samym procesie muszą być oznaczone jako same_process_hal_file.

Więcej informacji o zasadach SELinux znajdziesz w artykule Security-Enhanced Linux w Androidzie.

Zgodność ABI w przypadku kodu bitowego

Jeśli nie zostaną dodane żadne nowe interfejsy API, co oznacza brak zmiany wersji HAL, platformy RS będą nadal korzystać z istniejącego sterownika GPU (HAL 1.0).

W przypadku drobnych zmian w HAL (HAL 1.1), które nie mają wpływu na kod pośredni, platformy powinny w przypadku tych nowo dodanych interfejsów API przełączyć się na procesor i w innych przypadkach nadal używać sterownika GPU (HAL 1.0).

W przypadku poważnych zmian w HAL (HAL 2.0) wpływających na kompilację/łączenie kodu bitowego platformy RS nie powinny wczytywać sterowników GPU dostarczonych przez dostawcę, ale zamiast tego używać ścieżki procesora lub Vulkan do akceleracji.

Korzystanie z kodu bajtowego RenderScript odbywa się w 3 etapach:

Etap Szczegóły
Kompilacja
  • Kod bitowy wejściowy (.bc) dla bcc musi mieć format kodu bitowego LLVM 3.2, a bcc musi być wstecznie zgodny z dotychczasowymi (starszymi) aplikacjami.
  • Metadane w pliku .bc mogą się jednak zmienić (mogą pojawić się nowe funkcje środowiska wykonawczego, np. funkcje ustawiające i pobierające alokację, funkcje matematyczne itp.). Część funkcji środowiska wykonawczego znajduje się w libclcore.bc, a część w bibliotece LibRSDriver lub jej odpowiedniku dostarczonym przez producenta.
  • Nowe funkcje środowiska wykonawczego lub istotne zmiany w metadanych wymagają zwiększenia poziomu interfejsu API kodu pośredniego. Sterowniki dostawcy nie będą mogły z niej korzystać, dlatego należy też zwiększyć numer wersji HAL.
  • Dostawcy mogą mieć własne kompilatory, ale wnioski i wymagania dotyczące bcc obowiązują również w przypadku tych kompilatorów.
Link
  • Skompilowany plik .o zostanie połączony ze sterownikiem dostawcy, np. libRSDriver_foo.solibcompiler_rt.so. Ścieżka CPU będzie połączona z libRSDriver.so.
  • Jeśli plik .o wymaga nowego interfejsu API środowiska wykonawczego od libRSDriver_foo, sterownik dostawcy musi zostać zaktualizowany, aby go obsługiwać.
  • Niektórzy dostawcy mogą mieć własne linkery, ale argumenty za ld.mc dotyczą też ich.
Wczytaj
  • libRSCpuRef wczytuje udostępniony obiekt. Jeśli w tym interfejsie zostaną wprowadzone zmiany, konieczna będzie aktualizacja wersji HAL.
  • Dostawcy będą korzystać z libRSCpuRef do wczytywania udostępnionego obiektu lub wdrażać własne rozwiązanie.

Oprócz HAL interfejsami są też interfejsy API środowiska wykonawczego i eksportowane symbole. Żaden z tych interfejsów nie zmienił się od Androida 7.0 (API 24) i nie planujemy wprowadzać w nich zmian w Androidzie 8.0 ani nowszych. Jeśli jednak interfejs ulegnie zmianie, wersja HAL również zostanie zwiększona.

Implementacje dostawców

Android 8.0 i nowsze wersje wymagają wprowadzenia pewnych zmian w sterowniku GPU, aby działał on prawidłowo.

Moduły sterownika

  • Moduły sterownika nie mogą zależeć od żadnych bibliotek systemowych, które nie znajdują się na liście.
  • Sterownik musi udostępniać własną android.hardware.renderscript@1.0-impl_{NAME} lub deklarować domyślną implementację android.hardware.renderscript@1.0-impl jako zależność.
  • Implementacja procesora libRSDriver.so jest dobrym przykładem tego, jak usunąć zależności inne niż VNDK-SP.

Kompilator kodu bitowego

Kod bitowy RenderScript dla sterownika dostawcy możesz skompilować na 2 sposoby:

  1. Wywołaj kompilator RenderScript specyficzny dla dostawcy w /vendor/bin/ (preferowana metoda kompilacji GPU). Podobnie jak inne moduły sterownika, binarny kompilator dostawcy nie może zależeć od żadnej biblioteki systemowej, która nie znajduje się na liście bibliotek RenderScript dostępnych dla dostawców.
  2. Wywołaj system bcc: /system/bin/bcc z dostarczonym przez dostawcę bcc plugin; ten wtyk nie może zależeć od żadnej biblioteki systemowej, która nie znajduje się na liście bibliotek RenderScript dostępnych dla dostawców.

Jeśli dostawca bcc plugin musi ingerować w kompilację procesora i nie można łatwo usunąć jego zależności od libLLVM.so, powinien skopiować bcc (i wszystkie zależności inne niż LL-NDK, w tym libLLVM.so, libbcc.so) do partycji /vendor.

Dostawcy muszą też wprowadzić te zmiany:

Rysunek 7. Zmiany w sterowniku dostawcy.

  1. Kopiowanie libclcore.bc na partycję /vendor. Dzięki temu libclcore.bc, libLLVM.so i libbcc.so są zsynchronizowane.
  2. Zmień ścieżkę do pliku wykonywalnego bcc, ustawiając RsdCpuScriptImpl::BCC_EXE_PATH w implementacji RS HAL.

Zasady SELinux

Zasady SELinux mają wpływ zarówno na sterownik, jak i na pliki wykonywalne kompilatora. Wszystkie moduły sterownika muszą być oznaczone symbolem same_process_hal_filefile_contexts urządzenia. Przykład:

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

Wykonywalny plik kompilatora musi być wywoływany przez proces aplikacji, podobnie jak kopia bcc dostawcy (/vendor/bin/bcc). Przykład:

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

Starsze urządzenia

Starsze urządzenia to te, które spełniają te warunki:

  1. Wartość PRODUCT_SHIPPING_API_LEVEL jest mniejsza niż 26.
  2. Wartość PRODUCT_FULL_TREBLE_OVERRIDE nie jest zdefiniowana.

W przypadku starszych urządzeń ograniczenia nie są wymuszane podczas aktualizacji do Androida 8.0 lub nowszego, co oznacza, że sterowniki mogą nadal łączyć się z bibliotekami w /system/lib[64]. Jednak ze względu na zmianę architektury związaną z OVERRIDE_RS_DRIVER należy zainstalować android.hardware.renderscript@1.0-impl/vendor partycji. Jeśli tego nie zrobisz, środowisko wykonawcze RenderScript będzie musiało użyć ścieżki procesora.

Informacje o przyczynach wycofania Renderscript znajdziesz na blogu dla deweloperów Androida: Android GPU Compute Going Forward (w języku angielskim). Informacje o wycofaniu tego zasobu obejmują: