RenderScript

RenderScript to framework do wykonywania zadań wymagających dużej mocy obliczeniowej na urządzeniach z Androidem. Jest on przeznaczony do korzystania z obliczeń równoległych z danymi, ale może też przynieść korzyści w przypadku zadań sekwencyjnych. Środowisko wykonawcze RenderScript równolegle wykonuje zadania na różnych procesorach dostępnych na urządzeniu, takich jak procesory wielordzeniowe i procesory graficzne, co pozwala deweloperom skupić się na wyrażaniu algorytmów zamiast na planowaniu pracy. RenderScript jest szczególnie przydatny w przypadku aplikacji wykonujących przetwarzanie obrazu, fotografię obliczeniową lub widzenie komputerowe.

Urządzenia z Androidem 8.0 lub nowszym korzystają z następujących frameworków RenderScript i interfejsów HAL:

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

Różnice między RenderScript w Androidzie 7.x i starszych wersjach:

  • 2 instancje bibliotek wewnętrznych RenderScript w procesie. Jeden zestaw jest przeznaczony do ścieżki awaryjnej procesora i jest bezpośrednio z /system/lib; drugi zestaw jest przeznaczony do ścieżki GPU i jest z /system/lib/vndk-sp.
  • Biblioteki wewnętrzne RS w /system/lib są tworzone w ramach platformy i aktualizowane wraz z uaktualnieniem system.img. Jednak biblioteki w /system/lib/vndk-sp są tworzone dla dostawcy i nie są aktualizowane, gdy system.img jest aktualizowana (chociaż można je aktualizować w celu wprowadzenia poprawki zabezpieczeń, ich ABI pozostaje taki sam).
  • Kod dostawcy (RS HAL, sterownik RS i bcc plugin) jest powiązany z bibliotekami wewnętrznymi RenderScript znajdującymi się w katalogu /system/lib/vndk-sp. Nie można ich łączyć z bibliotekami w katalogu /system/lib, ponieważ są one tworzone na potrzeby danej platformy i mogą nie być zgodne z kodem dostawcy (np. symbole mogą zostać usunięte). Uniemożliwiłoby to OTA tylko dla frameworka.

Projektowanie

W następnych sekcjach znajdziesz szczegółowe informacje o projektowaniu RenderScript w Androidzie 8.0 i nowszych wersjach.

Biblioteki RenderScript dostępne dla dostawców

Ta sekcja zawiera listę bibliotek RenderScript (znanych jako NDK dostawcy dla interfejsów HAL w tym samym procesie lub VNDK-SP), które są dostępne dla kodu dostawcy i z którymi można utworzyć link. Zawiera on też informacje o dodatkowych bibliotekach, które nie są powiązane z RenderScript, ale są również udostępniane w kodzie dostawcy.

Poniższa lista bibliotek może się różnić w zależności od wersji Androida, ale jest niezmienna w przypadku konkretnej wersji Androida. Aktualną listę dostępnych bibliotek znajdziesz na stronie /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 linkera

Ograniczenie łączenia, które uniemożliwia korzystanie z bibliotek nieznajdujących się w VNDK-SP przez kod dostawcy, jest egzekwowane w czasie wykonywania za pomocą przestrzeni nazw linkera. (szczegółowe informacje znajdziesz w prezentacji VNDK Design).

Na urządzeniu z Androidem 8.0 lub nowszym wszystkie interfejsy HAL w ramach tego samego procesu (SP-HAL) z wyjątkiem RenderScript są ładowane w przestrzeni nazw linkera sphal. RenderScript jest ładowany do specyficznej dla niego przestrzeni nazw rs, która umożliwia nieco luźniejszą kontrolę bibliotek RenderScript. Implementacja RS musi wczytać skompilowany kod bitowy, dlatego /data/*/*.so jest dodawany do ścieżki przestrzeni nazw rs (inne SP-HAL nie mogą wczytywać bibliotek z partycji danych).

Ponadto przestrzeń nazw rs umożliwia stosowanie większej liczby bibliotek niż inne przestrzenie nazw. libmediandk.solibft2.so są dostępne w przestrzeni nazw rs, ponieważ libRS_internal.so ma wewnętrzną zależność od tych bibliotek.

Rysunek 2. Konfiguracja przestrzeni nazw dla linkera.

Czynniki obciążenia

Ścieżka awaryjnego korzystania z procesora

Podczas tworzenia kontekstu RS w zależności od obecności bitu RS_CONTEXT_LOW_LATENCY wybierana jest ścieżka procesora lub karty graficznej. Gdy wybrana jest ścieżka CPU, libRS_internal.so (główna implementacja frameworku RS) jest bezpośrednio dlopenz domyślnego przedrostka linkera, w którym dostępne są biblioteki RS dla danej platformy.

Implementacja RS HAL od dostawcy nie jest w ogóle używana, gdy wybrano ścieżkę awaryjnego korzystania z procesora. Tworzony jest obiekt RsContext z wartością null mVendorDriverName. libRSDriver.so jest (domyślnie) dlopened i sterownik lib jest wczytany z default przestrzeni nazw, ponieważ wywołujący (libRS_internal.so) jest też wczytywany w przestrzeni nazw default.

Rysunek 3. Ścieżka awaryjna 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 podległych mu libhidltransport.so), aby wczytać android.hardware.renderscript@1.0-impl.so (implementację interfejsu RS HAL przez dostawcę) 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ępnić własny sterownik RS, ustawiając flagę OVERRIDE_RS_DRIVER, która jest umieszczana w implementacji interfejsu API RS (hardware/interfaces/renderscript/1.0/default/Context.cpp). Następnie nazwa tego sterownika jest dlopenw kontekście RS na potrzeby ścieżki GPU.

Tworzenie obiektu RsContext jest delegowane do implementacji RS HAL. HAL wywołuje ponownie mechanizm RS za pomocą funkcji rsContextCreateVendor() z nazwą sterownika do użycia jako argumentu. Następnie framework RS wczytuje określony sterownik po zainicjowaniu interfejsu RsContext. W tym przypadku biblioteka sterownika jest wczytana do przestrzeni nazw rs, ponieważ obiekt RsContext jest tworzony w ramach tej przestrzeni, a obiekt /vendor/lib znajduje się na ścieżce wyszukiwania tej przestrzeni.rs

Rysunek 4. Ścieżka awaryjnego korzystania z GPU.

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

Podczas przechodzenia z przestrzeni nazw sphal do rs wczytywanie odbywa się pośrednio za pomocą tego wiersza w pliku /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 przestrzeni nazw /system/lib/vndk-sp, w której znajduje się libRS_internal.so). W tej konfiguracji wystarczy wywołanie funkcji dlopen() w funkcji libRS_internal.so, aby przejść do nowej przestrzeni nazw.

Ładowanie wtyczki UDW

bcc plugin to biblioteka dostarczona przez dostawcę i wczytana do kompilatora bcc. Ponieważ bcc to proces systemowy w katalogu /system/bin, biblioteka bcc plugin może być uważana za SP-HAL (czyli interfejs HAL dostawcy, który można wczytać bezpośrednio do procesu systemowego bez korzystania z bindera). Jako SP-HAL biblioteka bcc-plugin:

  • Nie można tworzyć linków do bibliotek tylko do frameworku, takich jak libLLVM.so.
  • Może zawierać linki tylko do bibliotek VNDK-SP dostępnych dla dostawcy.

To ograniczenie jest egzekwowane przez załadowanie bcc plugin do przestrzeni nazw sphal za pomocą funkcji android_sphal_load_library(). We wcześniejszych 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 nowszych ta opcja jest określona w opcji -plugin, a biblioteka jest wczytana bezpośrednio przez bcc. Ta opcja umożliwia dostęp do projektu open source LLVM inny niż Android.

Rysunek 5. Ładowanie wtyczki bcc na Androidzie 7.x i starszych.



Rysunek 6. Ładowanie wtyczki bcc, Android 8.0 lub nowszy.

Ścieżki wyszukiwania pliku ld.mc

Podczas wykonywania ld.mc niektóre biblioteki RS są przekazywane jako dane wejściowe do linkera. Kod bitowy RS z aplikacji jest powiązany z bibliotekami środowiska wykonawczego. Gdy skompresowany kod bitowy jest ładowany do procesu aplikacji, biblioteki środowiska wykonawczego są ponownie powiązane dynamicznie z kompresowanego kodu bitowego.

Biblioteki środowiska wykonawczego:

  • 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órej używała ld.mc. W przeciwnym razie skompilowany kod bitowy może nie znaleźć symbolu, który był dostępny w momencie połączenia.

Aby to zrobić, framework RS używa różnych ścieżek wyszukiwania dla bibliotek runtime podczas wykonywania funkcji ld.mc w zależności od tego, czy sam framework RS jest wczytywany z /system/lib czy z /system/lib/vndk-sp. Można to określić, odczytując adres dowolnego symbolu biblioteki RS framework i używanie dladdr() do mapowania ścieżki pliku do adresu.

Zasady SELinux

W Androidzie 8.0 i nowszych ze względu na zmiany w zasadach SELinux musisz przestrzegać określonych zasad (wykonywanych przez neverallows) podczas etykietowania dodatkowych plików na partycji vendor:

  • vendor_file musi być domyślną etykietą dla wszystkich plików na partycji vendor. Zasady platformy wymagają tego, aby uzyskać dostęp do implementacji interfejsu HAL typu passthrough.
  • Wszystkie nowe exec_types dodane w partycji vendor za pomocą SEPolicy dostawcy muszą mieć atrybut vendor_file_type. Jest to egzekwowane za pomocą neverallows.
  • Aby uniknąć konfliktów z przyszłymi aktualizacjami platformy lub frameworka, nie etykietuj plików innymi niż exec_types w partycji vendor.
  • Wszystkie zależności bibliotek dla identyfikatorów HAL identyfikowanych przez AOSP w ramach tego samego procesu muszą być oznaczone jako same_process_hal_file.

Szczegółowe informacje o zasadach SELinux znajdziesz w artykule SELinux w Androidzie.

Zgodność kodu bitowego z interfejsem ABI

Jeśli nie zostaną dodane żadne nowe interfejsy API, co oznacza, że nie nastąpi zmiana wersji HAL, frameworki RS będą nadal używać dotychczasowego sterownika GPU (HAL 1.0).

W przypadku drobnych zmian w interfejsie HAL (HAL 1.1), które nie mają wpływu na kod bitowy, frameworki powinny stosować w przypadku nowo dodanych interfejsów API procesor CPU, a w pozostałych miejscach – sterownik GPU (HAL 1.0).

W przypadku istotnych zmian w interfejsie HAL (HAL 2.0), które mają wpływ na kompilację i linkowanie kodu bitowego, frameworki RS powinny zrezygnować z ładowania sterowników GPU dostarczanych przez dostawcę i zamiast tego używać procesora lub ścieżki Vulkan do przyspieszania.

Przetwarzanie kodu bajtowego RenderScript odbywa się w 3 etapach:

Etap Szczegóły
Kompilowanie
  • Kod bitowy wejściowy (.bc) dla bcc musi mieć format kodu bitowego LLVM 3.2, a bcc musi być zgodny z dotychczasowymi (starymi) aplikacjami.
  • Metadane w pliku .bc mogą się jednak zmienić (mogą pojawić się nowe funkcje czasu wykonywania, na przykład metody ustawiania i pobierania alokacji, funkcje matematyczne itp.). Część funkcji czasu wykonywania znajduje się w libclcore.bc, a część w LibRSDriver lub odpowiedniku dostawcy.
  • Nowe funkcje w czasie wykonywania lub zmiany w metadanych, które powodują przerwanie, wymagają zwiększenia poziomu interfejsu API kodu bajtowego. Ponieważ sterowniki dostawców nie będą mogły go używać, należy również zwiększyć wersję HAL.
  • Dostawcy mogą mieć własne kompilatory, ale wnioski/wymagania dotyczące bcc również do nich się odnoszą.
Link
  • Kompilowany plik .o zostanie połączony z driverem dostawcy, np. libRSDriver_foo.so i libcompiler_rt.so. Ścieżka procesora będzie połączona z libRSDriver.so.
  • Jeśli plik .o wymaga nowego interfejsu API w czasie wykonywania z wersji libRSDriver_foo, sterownik dostawcy musi zostać zaktualizowany, aby go obsługiwać.
  • Niektórzy dostawcy mogą mieć własne linkery, ale argument ld.mc odnosi się również do nich.
Wczytaj
  • libRSCpuRef wczytuje udostępniony obiekt. Jeśli w tym interfejsie zostaną wprowadzone zmiany, konieczne będzie uaktualnienie wersji HAL.
  • Do wczytania obiektu współdzielonego dostawcy będą używać funkcji libRSCpuRef lub będą implementować własne funkcje.

Oprócz HAL interfejsami są też interfejsy API czasu wykonywania i wyeksportowane symbole. Żaden z tych interfejsów nie zmienił się od wersji Androida 7.0 (interfejs API 24) i nie planujemy wprowadzać zmian w Androidzie 8.0 ani nowszych wersjach. Jeśli jednak interfejs ulegnie zmianie, wersja HAL również wzrośnie.

Implementacje dostawców

Aby sterownik GPU działał prawidłowo, Android 8.0 i nowsze wymaga wprowadzenia pewnych zmian.

Moduły sterujące

  • Moduł sterownika nie może być zależny od żadnych bibliotek systemowych, które nie znajdują się na liście.
  • Sterownik musi zawierać własne android.hardware.renderscript@1.0-impl_{NAME} lub zadeklarować domyślną implementację android.hardware.renderscript@1.0-impl jako swoją zależność.
  • Implementacja na procesorze CPU libRSDriver.so to dobry przykład usuwania zależności innych niż VNDK-SP.

Kompilator kodu bitowego

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

  1. Wywołaj kompilator RenderScript konkretnego dostawcy w /vendor/bin/ (preferowana metoda kompilacji na 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łanie systemu bcc: /system/bin/bcc z użyciem biblioteki dostarczonej przez dostawcę bcc plugin; ten wtyczka 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, a zależność od libLLVM.so nie może być łatwo usunięta, dostawca powinien skopiować bcc (oraz wszystkie zależności inne niż LL-NDK, w tym libLLVM.solibbcc.so) do partycji /vendor.

Dostawcy muszą też wprowadzić te zmiany:

Rysunek 7. Zmiany w komponencie dostawcy.

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

Zasady SELinux

Zasady SELinux wpływają zarówno na sterownik, jak i pliki wykonywalne kompilatora. Wszystkie moduły sterownika muszą mieć etykietę same_process_hal_file w file_contexts urządzenia. Przykład:

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

Kompilator musi być uruchamiany przez proces aplikacji, tak samo jak kopia bcc (/vendor/bin/bcc) dostawcy. Przykład:

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

Starsze urządzenia

Urządzenia starsze to te, które spełniają te warunki:

  1. Wartość parametru PRODUCT_SHIPPING_API_LEVEL jest mniejsza niż 26.
  2. Parametr PRODUCT_FULL_TREBLE_OVERRIDE nie jest zdefiniowany.

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 aplikacja android.hardware.renderscript@1.0-impl musi być zainstalowana w partycji /vendor. Jeśli tego nie zrobisz, środowisko uruchomieniowe RenderScript użyje ścieżki procesora.

Informacje o motywacjach do wycofania Renderscript znajdziesz na blogu dla deweloperów aplikacji na Androida: Proces obliczeniowy na procesorze graficznym Androida w przyszłości. Informacje o wycofanych zasobach: