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 działa równolegle na różnych procesorach dostępnych w urządzeniu, takich jak procesory wielordzeniowe i GPU, dzięki czemu deweloperzy mogą skupić się na prezentowaniu algorytmów, a nie na planowaniu pracy. Skrypt RenderScript jest szczególnie przydatny w aplikacjach służących do przetwarzania obrazu, fotografii obliczeniowej lub rozpoznawania obrazów.

Urządzenia z Androidem 8.0 lub nowszym korzystają z następującego frameworku RenderScript i interfejsów HAL dostawców:

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 jako część platformy i aktualizowane wraz z uaktualnianiem system.img. Biblioteki w /system/lib/vndk-sp są jednak tworzone dla dostawcy i nie są aktualizowane po uaktualnieniu system.img (chociaż można je zaktualizować pod kątem poprawki zabezpieczeń, interfejs ABI pozostaje bez zmian).
  • Kod dostawcy (RS HAL, sterownik RS i bcc plugin) jest połączony z wewnętrznymi bibliotekami RenderScript na stronie /system/lib/vndk-sp. Nie można ich łączyć z bibliotekami w katalogu /system/lib, ponieważ są one tworzone dla danej platformy i mogą nie być zgodne z kodem dostawcy (np. symbole mogą zostać usunięte). W przeciwnym razie byłoby to niemożliwe.

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

W tej sekcji wymieniono biblioteki RenderScript (znane jako NDK dostawcy dla interfejsów HAL w ramach tego samego procesu lub VNDK-SP), które są dostępne dla kodu dostawcy i z którymi można utworzyć link. Znajdziesz w nim też informacje o dodatkowych bibliotekach, które nie są związane z RenderScriptem, ale które zostały przekazane do kodu 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.

RenderScript Libs 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 wczytywać skompilowany kod bitowy, dlatego do ścieżki przestrzeni nazw rs jest dodawany ciąg /data/*/*.so (inne identyfikatory SP-HAL nie mogą ładować libs z partycji danych).

Dodatkowo 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 libRS_internal.so jest wczytywana inaczej. Najpierw usługa libRS.so używa polecenia android.hardware.renderscript@1.0.so (i bazowego libhidltransport.so), aby wczytać android.hardware.renderscript@1.0-impl.so (implementację dostawcy RS HAL) do innej przestrzeni nazw tagu łączącego o nazwie sphal. RS HAL następnie dlopens libRS_internal.so w innej przestrzeni nazw linkera o nazwie rs.

Dostawcy mogą dostarczyć własnego sterownika 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). Ta nazwa sterownika jest następnie dlopenużywana w kontekście RS w ścieżce 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 podczas inicjowania interfejsu RsContext. W tym przypadku biblioteka sterownika jest wczytywana 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 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 przestrzeni nazw 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 w bibliotece bcc-plugin:

  • Nie można połączyć z bibliotekami działającymi tylko na platformie, takimi 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 jest to określone w opcji -plugin, a biblioteka jest ładowana 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 dla 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 połączony z bibliotekami środowiska wykonawczego, a gdy przekonwertowany kod bitowy jest wczytywany do procesu aplikacji, pakiety te są ponownie dynamicznie łączone z przekonwertowanego kodu bitowego.

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órej używała ld.mc. W przeciwnym razie skompilowany kod binarny 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 framework RS jest wczytywany z poziomu /system/lib czy /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 platformy, unikaj oznaczania na partycji vendor plików innych niż exec_types.
  • 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ść interfejsu ABI z kodem bitowym

Jeśli nie zostaną dodane żadne nowe interfejsy API, co oznacza brak aktualizacji 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 dostarczonych przez dostawcę i zamiast tego używać procesora lub ścieżki Vulkan do przyspieszania.

Przetwarzanie kodu bitowego 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 aplikacjami.
  • Metadane w pliku .bc mogą się jednak zmienić (w czasie działania mogą pojawić się nowe funkcje, np. metody ustawiające i pobierające alokację, 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. Sterowniki dostawców nie mogą ich przetworzyć, dlatego wersja HAL również musi być wyższa.
  • 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 poziomu 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 wprowadzono zmiany, konieczne jest uaktualnienie wersji HAL.
  • Dostawcy mogą korzystać z libRSCpuRef do wczytywania udostępnionego obiektu lub wdrożyć własny.

Oprócz HAL interfejsami są też interfejsy API czasu wykonywania i wyeksportowane symbole. Od Androida 7.0 (API 24) nie zmienił się żaden z interfejsów. Poza tym na Androidzie 8.0 i nowszych nie planujemy wprowadzać żadnych zmian. Jeśli jednak interfejs się zmieni, wersja HAL również wzrośnie.

Implementacje dostawców

Aby sterownik GPU działał prawidłowo, w Androidzie 8.0 i nowszych wymagane są pewne zmiany.

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 podać własną właściwość android.hardware.renderscript@1.0-impl_{NAME} lub zadeklarować domyślną implementację android.hardware.renderscript@1.0-impl jako zależność.
  • Implementacja procesora 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 sterownika 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.

Zasada 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 sekcji file_contexts urządzenia. Na przykład:

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

Proces aplikacji musi móc wywołać plik wykonywalny kompilatora oraz kopię pola UDW (/vendor/bin/bcc) od dostawcy. Na 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. PRODUCT_SHIPPING_API_LEVEL ma wartość mniejszą 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]. Ze względu na zmianę architektury związaną z OVERRIDE_RS_DRIVER musisz jednak zainstalować aplikację android.hardware.renderscript@1.0-impl na /vendor partycji. Jeśli tego nie zrobisz, wymusza to przejście środowiska wykonawczego RenderScript na ścieżkę procesora.

Informacje o powodach wycofania technologii Renderscript znajdziesz na blogu dla deweloperów aplikacji na Androida: Android GPU Compute Going Forward (w języku angielskim). Informacje o wycofanych zasobach: