在搭載 Android 10 (API 級別 29) 以上版本的裝置中,OpenGL ES (GLES) 分層 廣告。可進行偵錯的應用程式可從其 APK 載入 GLES 圖層 或從所選圖層 APK 中取得
GLES 圖層的使用方法與 Vulkan 驗證層使用方式。
需求條件
GLES 圖層僅適用於 GLES 2.0 以上版本。
圖層初始化
填入標準進入點後,EGL 載入器會將 GLES 執行個體化
LayerLoader
。如果已啟用偵錯圖層,系統會指定 LayerLoader
掃描作業
和其他圖層的目錄
Vulkan 載入器可正常運作。
如果已啟用分層,LayerLoader
會搜尋並列舉指定的
圖層清單指定方式為以冒號分隔檔案名稱。
LayerLoader
會按您指定的順序掃遍圖層,因此第一個
位於應用程式正下方對於各圖層,LayerLoader
會追蹤 AndroidGLESLayer_Initialize
和
AndroidGLESLayer_GetProcAddress
進入點。這些圖層「必須」提供這些
以及介面是否可供載入
typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*); void* AndroidGLESLayer_Initialize(void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address))
AndroidGLESLayer_Initialize()
提供要使用的圖層 ID
(layer_id
) 和可呼叫的進入點,用來查詢下列函式
資料層可以按照下列程式碼範例使用進入點:
const char* func = "eglFoo"; void* gpa = get_next_layer_proc_address(layer_id, func);
AndroidGLESLayer_GetProcAddress
會從
層在執行完畢後應呼叫的鏈結如果只有一個圖層
next
會直接指向大多數函式的驅動程式。
typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; void* AndroidGLESLayer_GetProcAddress(const char *funcName, EGLFuncPointer next)
針對找到的每一個圖層,GLES LayerLoader
都會呼叫
AndroidGLESLayer_Initialize
,步行 libEGL
的函式清單和呼叫
AndroidGLESLayer_GetProcAddress
用於所有已知函式。由資料層決定
,決定如何追蹤下一個地址。如果圖層攔截了某個函式,
它會追蹤函式的地址如果圖層未攔截函式
AndroidGLESLayer_GetProcAddress
會傳回其原定的函式位址
成功。接著,LayerLoader
會更新函式掛鉤清單,使其指向
繪製圖層的進入點
這些圖層不需要對資訊進行任何操作
「AndroidGLESLayer_Initialize
」和「get_next_layer_proc_address
」會提供
提供資料能讓現有圖層
Android GPU 檢查器和
使用 RenderDoc 提供支援
Android。圖層可以利用這些資料自行查詢函式,而非
正在等待呼叫 AndroidGLESLayer_GetProcAddress
。如果圖層選擇
在載入器查詢所有進入點之前,就先初始化這個程式碼
必須使用 get_next_layer_proc_address
。eglGetProcAddress
必須
向下傳遞到平台。
放置圖層
GLES LayerLoader
會按照順序搜尋下列位置中的圖層
優先順序:
1. 系統位置的根層級
這需要 Root 存取權
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. 應用程式的基本目錄
目標應用程式必須可進行偵錯,或者您必須具備 Root 存取權:
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. 外部 APK
決定目標應用程式的 ABI,然後安裝含有以下項目的 APK: 想要載入的圖層:
adb install --abi armeabi-v7a layers.apk
4. 在目標應用程式的 APK 中
以下範例說明瞭如何在應用程式 APK 中放置圖層:
$ 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
啟用圖層
您可以只為個別應用程式啟用 GLES 圖層,也可以全域啟用。保留個別應用程式設定 會在重新啟動時清除全域屬性。
Android 的安全性模型和政策有所不同 明顯來自其他平台如要載入外部圖層, 以下為必要項目:
目標應用程式的資訊清單檔案含有以下內容 中繼資料元素 (僅適用於 適用於指定 Android 11 (API 級別 30) 以上版本的應用程式):
<meta-data android:name="com.android.graphics.injectLayers.enable" android:value="true" />
您應該使用這個選項分析應用程式。
目標應用程式可進行偵錯。這個選項會顯示更多偵錯資訊 但可能會對應用程式效能造成負面影響
目標應用程式是在作業系統的使用者偵錯版本上執行 並授予 Root 權限
啟用個別應用程式的圖層:
# 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>
如要停用個別應用程式的圖層:
# 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
全域啟用圖層:
# This attempts to load layers for all applications, including native # executables adb shell setprop debug.gles.layers <layer1:layer2:layerN>
建立圖層
圖層必須公開下列兩種函式,如 EGL 載入器初始化:
AndroidGLESLayer_Initialize AndroidGLESLayer_GetProcAddress
被動圖層
如果圖層只會攔截少量函式,
被動初始化的圖層是最佳選擇被動初始化的圖層等待
,為 GLES LayerLoader
初始化所需的函式。
下列程式碼範例說明如何建立被動圖層。
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); } }
主動圖層
適用於需要完全初始化的更正式圖層,或圖層
需要查詢 EGL 載入器 (使用中圖層) 無法識別的擴充功能
需要初始化圖層會使用
AndroidGLESLayer_Initialize
提供給 get_next_layer_proc_address
的
查詢函式。圖層仍必須回應
載入載入器的 AndroidGLESLayer_GetProcAddress
要求,讓平台知道
轉接來電。以下程式碼範例說明如何建立
執行
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); } }