Na urządzeniach z Androidem 10 (poziom interfejsu API 29) lub nowszym nakładanie warstw OpenGL ES (GLES) jest i dostępności informacji. Aplikacja z możliwością debugowania może wczytywać warstwy GLES z pliku APK lub z pliku APK wybranej warstwy.
Użycie warstwy GLES jest podobne do użycie warstwy weryfikacji Vulkan.
Wymagania
Warstwy GLES są obsługiwane tylko w GLES w wersji 2.0 lub nowszej.
Inicjowanie warstwy
Po wypełnieniu standardowych punktów wejścia moduł ładowania EGL tworzy instancję GLES
LayerLoader
Jeśli włączone są warstwy debugowania, LayerLoader
określonego skanowania
katalogi warstw, takie jak
Tak działa program ładujący Vulkan.
Jeśli jest włączone tworzenie warstw, LayerLoader
wyszukuje i wylicza określony element
warstw. Lista warstw jest określana przez nazwy plików rozdzielone dwukropkiem.
LayerLoader
przemierza warstwy w określonej przez Ciebie kolejności, więc pierwszy z nich
warstwa znajduje się bezpośrednio pod aplikacją. W przypadku każdej warstwy parametr LayerLoader
śledzi AndroidGLESLayer_Initialize
oraz
AndroidGLESLayer_GetProcAddress
punktów wejścia. Warstwy muszą zawierać te
które są łatwe do załadowania.
typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*); void* AndroidGLESLayer_Initialize(void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address))
AndroidGLESLayer_Initialize()
udostępnia identyfikator warstwy, której ma użyć
(layer_id
) i punktu wejścia, który można wywołać, aby wyszukać funkcje poniżej
w pobliżu. Punktu wejścia można użyć w sposób pokazany w tym przykładowym kodzie:
const char* func = "eglFoo"; void* gpa = get_next_layer_proc_address(layer_id, func);
AndroidGLESLayer_GetProcAddress
przejmie adres następnego połączenia w
łańcuch, który ma wywołać warstwa po zakończeniu. Jeśli istnieje tylko jedna warstwa,
W przypadku większości funkcji next
wskazuje bezpośrednio sterownik.
typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; void* AndroidGLESLayer_GetProcAddress(const char *funcName, EGLFuncPointer next)
W przypadku każdej warstwy, którą znajdzie LayerLoader
GLES, wywołuje
AndroidGLESLayer_Initialize
, przedstawia listy funkcji i wywołania funkcji libEGL
AndroidGLESLayer_GetProcAddress
w przypadku wszystkich znanych funkcji. Decyzja należy do warstwy
aby określić, jak śledzić następny adres. Jeśli warstwa przechwytuje funkcję,
śledzi adres funkcji. Jeśli warstwa nie przechwytuje funkcji,
Funkcja AndroidGLESLayer_GetProcAddress
zwraca ten sam adres funkcji
zaliczono. Następnie LayerLoader
aktualizuje listę punktów zaczepienia funkcji, tak aby wskazywała
do punktu wejścia warstwy.
Warstwy nie muszą wykonywać żadnych działań z informacjami
AndroidGLESLayer_Initialize
i get_next_layer_proc_address
udostępniają, ale
udostępnianie danych ułatwia tworzenie warstw,
Android GPU Inspector oraz
RenderDoc do obsługi
na urządzeniu z Androidem. Dzięki tym danym warstwa może wyszukiwać funkcje niezależnie,
oczekiwanie na połączenie z numerem AndroidGLESLayer_GetProcAddress
. Jeśli warstwy
są zainicjowane przed wykonaniem zapytania o wszystkie punkty wejścia,
musi używać parametru get_next_layer_proc_address
. eglGetProcAddress
musi
i przekazywać je na platformę.
Umieść warstwy
GLES LayerLoader
wyszukuje warstwy w następujących lokalizacjach w kolejności
priorytetu:
1. Lokalizacja systemowa katalogu głównego
Wymaga dostępu na poziomie roota
adb root adb disable-verity adb reboot adb root adb shell setenforce 0 adb shell mkdir -p /data/local/debug/gles adb push <layer>.so /data/local/debug/gles/
2. Katalog podstawowy aplikacji
Aplikacja docelowa musi umożliwiać debugowanie lub musisz mieć dostęp na poziomie roota:
adb push libGLTrace.so /data/local/tmp adb shell run-as com.android.gl2jni cp /data/local/tmp/libGLTrace.so . adb shell run-as com.android.gl2jni ls | grep libGLTrace libGLTrace.so
3. Zewnętrzny plik APK
Określ interfejs ABI aplikacji docelowej, a następnie zainstaluj plik APK zawierający warstwy, które chcesz wczytać:
adb install --abi armeabi-v7a layers.apk
4. W pliku APK aplikacji docelowej
Poniższy przykład pokazuje, jak umieścić warstwy w pliku APK aplikacji:
$ jar tf GLES_layers.apk lib/arm64-v8a/libGLES_glesLayer1.so lib/arm64-v8a/libGLES_glesLayer2.so lib/arm64-v8a/libGLES_glesLayer3.so lib/armeabi-v7a/libGLES_glesLayer1.so lib/armeabi-v7a/libGLES_glesLayer2.so lib/armeabi-v7a/libGLES_glesLayer3.so resources.arsc AndroidManifest.xml META-INF/CERT.SF META-INF/CERT.RSA META-INF/MANIFEST.MF
Włącz warstwy
Warstwy GLES możesz włączyć dla poszczególnych aplikacji lub globalnie. Zachowywanie ustawień dla poszczególnych aplikacji podczas ponownego uruchamiania urządzenia, a właściwości globalne są czyszczone przy ponownym uruchomieniu.
Model zabezpieczeń na Androidzie i zasady różnią się znacznie różnią się od innych platform. Żeby wczytać warstwy zewnętrzne, musi być spełniony ten warunek:
Plik manifestu aplikacji docelowej zawiera te elementy element meta-data (ma zastosowanie tylko do aplikacji kierowanych na Androida 11 (poziom interfejsu API 30) lub nowszego):
<meta-data android:name="com.android.graphics.injectLayers.enable" android:value="true" />
Ta opcja służy do profilowania aplikacji.
Aplikację docelową można debugować. Ta opcja zapewnia więcej danych debugowania, ale mogą negatywnie wpływać na wydajność aplikacji.
Aplikacja docelowa jest uruchamiana w kompilacji userdebug systemu operacyjnego, która przyznaje dostęp na poziomie roota.
Aby włączyć warstwy dla poszczególnych aplikacji:
# Enable layers adb shell settings put global enable_gpu_debug_layers 1 # Specify target application adb shell settings put global gpu_debug_app <package_name> # Specify layer list (from top to bottom) # Layers are identified by their filenames, such as "libGLLayer.so" adb shell settings put global gpu_debug_layers_gles <layer1:layer2:layerN> # Specify packages to search for layers adb shell settings put global gpu_debug_layer_app <package1:package2:packageN>
Aby wyłączyć warstwy dla poszczególnych aplikacji:
# Delete the global setting that enables layers adb shell settings delete global enable_gpu_debug_layers # Delete the global setting that selects target application adb shell settings delete global gpu_debug_app # Delete the global setting that specifies layer list adb shell settings delete global gpu_debug_layers_gles # Delete the global setting that specifies layer packages adb shell settings delete global gpu_debug_layer_app
Aby włączyć warstwy globalnie:
# This attempts to load layers for all applications, including native # executables adb shell setprop debug.gles.layers <layer1:layer2:layerN>
Tworzenie warstwy
Warstwy muszą mieć widoczne dwie następujące funkcje opisane w Inicjowanie polecenia EGL Loader:
AndroidGLESLayer_Initialize AndroidGLESLayer_GetProcAddress
Warstwy pasywne
W przypadku warstwy, która przechwytuje tylko kilka funkcji,
optymalna jest warstwa zainicjowana pasywnie. Pasywna warstwa oczekuje
w GLES LayerLoader
, aby zainicjować potrzebną funkcję.
Poniższy przykładowy kod pokazuje, jak utworzyć warstwę pasywną.
namespace { std::unordered_map<std::string, EGLFuncPointer> funcMap; EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig ( EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config) { EGLFuncPointer entry = funcMap["eglChooseConfig"]; typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)( EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*); PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry); return next(dpy, attrib_list, configs, config_size, num_config); } EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) { #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \ return (EGLFuncPointer)glesLayer_##func; } GETPROCADDR(eglChooseConfig); // Don't return anything for unrecognized functions return nullptr; } EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer( void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { // This function is purposefully empty, since this layer does not proactively // look up any entrypoints } EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress( const char* funcName, EGLFuncPointer next) { EGLFuncPointer entry = eglGPA(funcName); if (entry != nullptr) { funcMap[std::string(funcName)] = next; return entry; } return next; } } // namespace extern "C" { __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize( void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address); } __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddress( const char *funcName, EGLFuncPointer next) { return (void*)glesLayer_GetLayerProcAddress(funcName, next); } }
Aktywne warstwy
Do bardziej sformalizowanych warstw, które muszą w pełni zainicjować inicjalizację na początku, lub warstw
które muszą wyszukiwać rozszerzenia nieznane procesowi wczytywania EGL, aktywna warstwa
zainicjowanie jest wymagane. Warstwa używa funkcji
get_next_layer_proc_address
, z którymi usługa AndroidGLESLayer_Initialize
korzysta
wyszukaj funkcję. Warstwa musi w dalszym ciągu odpowiadać na
AndroidGLESLayer_GetProcAddress
żądań od wczytującego, dzięki którym platforma wie
dokąd kierować połączenia. Poniższy przykładowy kod pokazuje, jak utworzyć aktywną
warstwę.
namespace { std::unordered_map<std::string, EGLFuncPointer> funcMap; EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig ( EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config) { EGLFuncPointer entry = funcMap["eglChooseConfig"]; typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)( EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*); PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry); return next(dpy, attrib_list, configs, config_size, num_config); } EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) { #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \ return (EGLFuncPointer)glesLayer_##func; } GETPROCADDR(eglChooseConfig); // Don't return anything for unrecognized functions return nullptr; } EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer( void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { // Note: This is where the layer would populate its function map with all the // functions it cares about const char* func = “eglChooseConfig”; funcMap[func] = get_next_layer_proc_address(layer_id, func); } EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress( const char* funcName, EGLFuncPointer next) { EGLFuncPointer entry = eglGPA(funcName); if (entry != nullptr) { return entry; } return next; } } // namespace extern "C" { __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize( void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address); } __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddress( const char *funcName, EGLFuncPointer next) { return (void*)glesLayer_GetLayerProcAddress(funcName, next); } }