Pada perangkat yang menjalankan Android 10 (level API 29) dan yang lebih tinggi, pelapisan OpenGL ES (GLES) tersedia. Aplikasi yang dapat di-debug bisa memuat lapisan GLES dari APK-nya, direktori dasarnya, atau APK lapisan yang dipilih.
Penggunaan lapisan GLES mirip dengan penggunaan lapisan validasi Vulkan.
Persyaratan
Lapisan GLES hanya didukung pada GLES versi 2.0 ke atas.
Inisialisasi lapisan
Setelah mengisi titik masuk standar, Loader EGL akan membuat instance LayerLoader
GLES. Jika lapisan debug diaktifkan, LayerLoader
akan memindai direktori yang ditentukan untuk lapisan, seperti yang dilakukan oleh loader Vulkan.
Jika pelapisan diaktifkan, LayerLoader
akan menelusuri dan mengenumerasi daftar lapisan yang ditentukan. Daftar lapisan ditentukan oleh nama file yang dipisahkan titik dua.
LayerLoader
akan melewati lapisan dalam urutan yang Anda tentukan sehingga lapisan pertama akan berada langsung di bawah aplikasi. Untuk setiap lapisan, LayerLoader
akan melacak titik masuk AndroidGLESLayer_Initialize
dan AndroidGLESLayer_GetProcAddress
. Lapisan tersebut harus menyediakan antarmuka berikut ini agar dapat dimuat.
typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*); void* AndroidGLESLayer_Initialize(void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address))
AndroidGLESLayer_Initialize()
menyediakan ID untuk lapisan yang akan digunakan (layer_id
) dan titik masuk yang dapat dipanggil untuk mencari fungsi di bawah lapisan tersebut. Titik masuk dapat digunakan seperti yang ditunjukkan dalam contoh kode berikut:
const char* func = "eglFoo"; void* gpa = get_next_layer_proc_address(layer_id, func);
AndroidGLESLayer_GetProcAddress
mengambil alamat panggilan berikutnya dalam rangkaian yang harus dipanggil oleh lapisan tersebut setelah selesai. Jika hanya ada satu lapisan, next
akan mengarah langsung ke driver untuk sebagian besar fungsi.
typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; void* AndroidGLESLayer_GetProcAddress(const char *funcName, EGLFuncPointer next)
Untuk setiap lapisan yang ditemukan, LayerLoader
GLES akan memanggil AndroidGLESLayer_Initialize
, menelusuri daftar fungsi libEGL
, dan memanggil AndroidGLESLayer_GetProcAddress
untuk semua fungsi yang dikenal. Penentuan cara untuk melacak alamat berikutnya bergantung pada lapisan tersebut. Jika mengintersep fungsi, lapisan akan melacak alamat fungsi tersebut. Jika lapisan tidak mengintersep fungsi, AndroidGLESLayer_GetProcAddress
akan menampilkan alamat fungsi yang sama dengan yang semula dilewatkan. Selanjutnya, LayerLoader
akan memperbarui daftar hook fungsi agar mengarah ke
titik entri lapisan.
Lapisan tidak perlu memproses informasi
yang diberikan oleh AndroidGLESLayer_Initialize
dan get_next_layer_proc_address
, tetapi
penyediaan data ini akan memudahkan lapisan yang ada, seperti
GAPID dan
RenderDoc,
mendukung Android. Dengan data tersebut, lapisan dapat mencari fungsi secara mandiri,
tanpa perlu menunggu panggilan ke AndroidGLESLayer_GetProcAddress
. Jika lapisan memilih untuk menginisialisasi sendiri sebelum loader mengkueri semua titik masuk, lapisan harus menggunakan get_next_layer_proc_address
. eglGetProcAddress
harus diturunkan dari rantai ke platform.
Menempatkan lapisan
LayerLoader
GLES menelusuri lapisan di lokasi berikut sesuai urutan prioritas:
1. Lokasi sistem untuk root
Akses root diperlukan
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. Direktori dasar aplikasi
Aplikasi target harus dapat di-debug, atau Anda harus memiliki akses 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 eksternal
Tentukan ABI aplikasi target Anda, lalu instal APK yang berisi lapisan yang ingin Anda muat:
adb install --abi armeabi-v7a layers.apk
4. Dalam APK aplikasi target
Contoh berikut menunjukkan cara menempatkan lapisan dalam APK aplikasi:
$ 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
Mengaktifkan lapisan
Anda dapat mengaktifkan lapisan GLES per aplikasi maupun secara global. Setelan per aplikasi tetap dipertahankan meskipun perangkat di-reboot, sedangkan properti global akan dihapus saat reboot.
Model keamanan dan kebijakan Android berbeda signifikan dengan platform lain. Untuk memuat lapisan eksternal, salah satu hal berikut harus terpenuhi:
File manifes aplikasi target mencakup elemen meta-data berikut (hanya berlaku untuk aplikasi yang menargetkan Android 11 (API level 30) atau yang lebih baru):
<meta-data android:name="com.android.graphics.injectLayers.enable" android:value="true" />
Anda harus menggunakan opsi ini untuk membuat profil aplikasi.
Aplikasi target dapat di-debug. Opsi ini memberi Anda informasi debug lainnya, tetapi mungkin akan berdampak buruk terhadap performa aplikasi Anda.
Aplikasi target dijalankan pada build userdebug sistem operasi yang memberikan izin akses root.
Untuk mengaktifkan lapisan per aplikasi:
# 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>
Untuk menonaktifkan lapisan per aplikasi:
# 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
Untuk mengaktifkan lapisan secara global:
# This attempts to load layers for all applications, including native # executables adb shell setprop debug.gles.layers <layer1:layer2:layerN>
Membuat lapisan
Lapisan harus menampilkan dua fungsi berikut yang dijelaskan dalam inisialisasi Loader EGL:
AndroidGLESLayer_Initialize AndroidGLESLayer_GetProcAddress
Lapisan pasif
Untuk lapisan yang hanya mengintersep beberapa fungsi, lapisan yang diinisialisasi secara pasif telah optimal. Lapisan yang diinisialisasi secara pasif akan menunggu LayerLoader
GLES menginisialisasi fungsi yang diperlukan.
Contoh kode berikut menunjukkan cara membuat lapisan pasif.
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); } }
Lapisan aktif
Untuk lapisan yang lebih formal dan perlu diinisialisasi sepenuhnya di awal, atau lapisan yang perlu mencari ekstensi yang tidak diketahui oleh Loader EGL, inisialisasi lapisan aktif wajib dilakukan. Lapisan tersebut menggunakan get_next_layer_proc_address
yang disediakan oleh AndroidGLESLayer_Initialize
untuk mencari fungsi. Lapisan masih harus merespons permintaan AndroidGLESLayer_GetProcAddress
dari loader agar platform mengetahui tujuan perutean panggilan. Contoh kode berikut ini menunjukkan cara membuat lapisan aktif.
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); } }