Sebagian besar API grafis eksplisit tidak menjalankan pemeriksaan error karena dapat menimbulkan masalah performa. Vulkan menyediakan pemeriksaan error dengan cara yang memungkinkan Anda menggunakan fitur ini saat pengembangan, tetapi tidak menyertakannya dalam build rilis aplikasi sehingga akan menghindari masalah semaksimal mungkin. Anda dapat melakukannya dengan mengaktifkan lapisan validasi Vulkan. Lapisan validasi ini mencegat atau mengaitkan titik masuk Vulkan untuk berbagai keperluan debug dan validasi.
Lapisan validasi ini mencegat titik masuk yang berisi definisi. Titik masuk yang tidak ditentukan dalam lapisan akan mencapai driver, yang merupakan level dasar, tidak divalidasi.
Contoh Android NDK dan Vulkan menyertakan lapisan validasi Vulkan untuk digunakan selama pengembangan. Anda dapat mengaitkan lapisan validasi ke tumpukan grafis untuk memungkinkannya melaporkan masalah validasi. Dengan instrumentasi ini, Anda dapat mengetahui dan memperbaiki penyalahgunaan selama pengembangan.
Lapisan validasi Khronos tunggal
Lapisan Vulkan dapat disisipkan oleh loader dalam tumpukan sehingga lapisan dengan level lebih tinggi akan memanggil lapisan di bawahnya dengan tumpukan lapisan yang pada akhirnya dihentikan di drive perangkat. Sebelumnya, ada beberapa lapisan validasi yang diaktifkan dalam urutan tertentu di Android. Namun, sekarang ada satu lapisan, VK_LAYER_KHRONOS_validation
, yang mencakup semua perilaku lapisan validasi sebelumnya. Untuk validasi Vulkan, semua aplikasi harus mengaktifkan lapisan validasi tunggal, VK_LAYER_KHRONOS_validation
.
Memaketkan lapisan validasi
NDK menyertakan biner lapisan validasi bawaan yang dapat dikirim ke perangkat pengujian dengan memaketkannya dalam APK. Anda dapat menemukan biner ini dalam direktori berikut:ndk-dir/sources/third_party/vulkan/src/build-android/jniLibs/abi/
. Saat diminta oleh aplikasi Anda, loader Vulkan akan mencari dan memuat lapisan dari APK aplikasi Anda.
Memaketkan lapisan validasi ke APK menggunakan Gradle
Anda dapat menambahkan lapisan validasi ke project menggunakan Plugin Android Gradle dan dukungan Android Studio untuk CMake dan ndk-build. Untuk menambahkan library menggunakan dukungan Android Studio untuk CMake dan ndk-build, tambahkan kode berikut ke filebuild.gradle
modul aplikasi:
sourceSets { main { jniLibs { // Gradle includes libraries in the following path as dependencies // of your CMake or ndk-build project so that they are packaged in // your app’s APK. srcDir "ndk-path/sources/third_party/vulkan/src/build-android/jniLibs" } } }Untuk mempelajari dukungan Android Studio untuk CMake dan ndk-build lebih lanjut, baca Menambahkan kode C dan C++ ke project.
Memaketkan lapisan validasi ke dalam library JNI
Anda dapat menambahkan biner lapisan validasi secara manual ke direktori library JNI project menggunakan opsi command line berikut:$ cd project-root $ mkdir -p app/src/main $ cp -fr ndk-path/sources/third_party/vulkan/src/build-android/jniLibs app/src/main/
Mem-build biner lapisan dari sumber
Jika aplikasi memerlukan lapisan validasi terbaru, Anda dapat mengambil sumber terbaru dari repositori GitHub Khronos Group dan mengikuti petunjuk proses build di sana.
Memverifikasi build lapisan
Baik Anda mem-build dengan lapisan NDK bawaan maupun dari kode sumber terbaru, proses build akan menghasilkan struktur file akhir seperti berikut:
src/main/jniLibs/ arm64-v8a/ libVkLayer_khronos_validation.so armeabi-v7a/ libVkLayer_khronos_validation.so
Contoh berikut menunjukkan cara memverifikasi bahwa APK Anda memuat lapisan validasi seperti yang diharapkan:
$ jar -xvf project.apk ... inflated: lib/arm64-v8a/libVkLayer_khronos_validation.so ...
Mengaktifkan lapisan
Vulkan API memungkinkan aplikasi mengaktifkan lapisan. Lapisan diaktifkan selama pembuatan instance. Titik masuk yang dicegat lapisan harus memiliki salah satu objek berikut sebagai parameter pertamanya:
VkInstance
VkPhysicalDevice
VkDevice
VkCommandBuffer
VkQueue
Anda dapat memanggil vkEnumerateInstanceLayerProperties()
untuk mencantumkan lapisan yang tersedia beserta propertinya. Sistem akan mengaktifkan lapisan saat vkCreateInstance()
dieksekusi.
Cuplikan kode berikut menunjukkan bagaimana aplikasi dapat menggunakan Vulkan API untuk mengaktifkan dan mengkueri lapisan secara terprogram:
// Get layer count using null pointer as last parameter uint32_t instance_layer_present_count = 0; vkEnumerateInstanceLayerProperties(&instance_layer_present_count, nullptr); // Enumerate layers with valid pointer in last parameter VkLayerProperties* layer_props = (VkLayerProperties*)malloc(instance_layer_present_count * sizeof(VkLayerProperties)); vkEnumerateInstanceLayerProperties(&instance_layer_present_count, layer_props)); // Make sure the desired validation layer is available const char *instance_layers[] = { "VK_LAYER_KHRONOS_validation" }; uint32_t instance_layer_request_count = sizeof(instance_layers) / sizeof(instance_layers[0]); for (uint32_t i = 0; i < instance_layer_request_count; i++) { bool found = false; for (uint32_t j = 0; j < instance_layer_present_count; j++) { if (strcmp(instance_layers[i], layer_props[j].layerName) == 0) { found = true; } } if (!found) { error(); } } // Pass desired layer into vkCreateInstance VkInstanceCreateInfo instance_info = {}; instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instance_info.enabledLayerCount = instance_layer_request_count; instance_info.ppEnabledLayerNames = instance_layers; ...
Output logcat default
Lapisan validasi memberikan pesan peringatan dan error di logcat dengan label tagVALIDATION
. Pesan lapisan validasi akan terlihat seperti berikut:
VALIDATION: UNASSIGNED-CoreValidation-DrawState-QueueForwardProgress(ERROR / SPEC): msgNum: 0 - VkQueue 0x7714c92dc0[] is waiting on VkSemaphore 0x192e[] that has no way to be signaled. VALIDATION: Objects: 1 VALIDATION: [0] 0x192e, type: 5, name: NULL
Mengaktifkan callback debug
Ekstensi Debug Utils VK_EXT_debug_utils
memungkinkan aplikasi Anda membuat messenger debug yang akan meneruskan pesan lapisan validasi ke callback yang disediakan aplikasi. Perlu diketahui bahwa ada juga ekstensi yang tidak berlaku lagi, yaitu VK_EXT_debug_report
, yang memberikan kemampuan serupa jika VK_EXT_debug_utils
tidak tersedia.
Sebelum menggunakan ekstensi Debug Utils, Anda harus memastikan terlebih dahulu bahwa platform mendukungnya. Contoh berikut menunjukkan cara memeriksa dukungan ekstensi debug dan mendaftarkan callback jika ekstensi didukung.
// Get the instance extension count uint32_t inst_ext_count = 0; vkEnumerateInstanceExtensionProperties(nullptr, &inst_ext_count, nullptr); // Enumerate the instance extensions VkExtensionProperties* inst_exts = (VkExtensionProperties *)malloc(inst_ext_count * sizeof(VkExtensionProperties)); vkEnumerateInstanceExtensionProperties(nullptr, &inst_ext_count, inst_exts); const char * enabled_inst_exts[16] = {}; uint32_t enabled_inst_ext_count = 0; // Make sure the debug utils extension is available for (uint32_t i = 0; i < inst_ext_count; i++) { if (strcmp(inst_exts[i].extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) { enabled_inst_exts[enabled_inst_ext_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; } } if (enabled_inst_ext_count == 0) return; // Pass the instance extensions into vkCreateInstance VkInstanceCreateInfo instance_info = {}; instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instance_info.enabledExtensionCount = enabled_inst_ext_count; instance_info.ppEnabledExtensionNames = enabled_inst_exts; PFN_vkCreateDebugUtilsMessengerEXT pfnCreateDebugUtilsMessengerEXT; PFN_vkDestroyDebugUtilsMessengerEXT pfnDestroyDebugUtilsMessengerEXT; pfnCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetDeviceProcAddr(device, "vkCreateDebugUtilsMessengerEXT"); pfnDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetDeviceProcAddr(device, "vkDestroyDebugUtilsMessengerEXT"); assert(pfnCreateDebugUtilsMessengerEXT); assert(pfnDestroyDebugUtilsMessengerEXT); // Create the debug messenger callback with desired settings VkDebugUtilsMessengerEXT debugUtilsMessenger; if (pfnCreateDebugUtilsMessengerEXT) { VkDebugUtilsMessengerCreateInfoEXT messengerInfo; constexpr VkDebugUtilsMessageSeverityFlagsEXT kSeveritiesToLog = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; constexpr VkDebugUtilsMessageTypeFlagsEXT kMessagesToLog = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; messengerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; messengerInfo.pNext = nullptr; messengerInfo.flags = 0; messengerInfo.messageSeverity = kSeveritiesToLog; messengerInfo.messageType = kMessagesToLog; messengerInfo.pfnUserCallback = &DebugUtilsMessenger; // Callback example below messengerInfo.pUserData = nullptr; // Custom user data passed to callback pfnCreateDebugUtilsMessengerEXT(instance, &messengerInfo, nullptr, &debugUtilsMessenger); } // Later, when shutting down Vulkan, call the following if (pfnDestroyDebugUtilsMessengerEXT) { pfnDestroyDebugUtilsMessengerEXT(instance, debugUtilsMessenger, nullptr); }
Setelah aplikasi Anda mendaftarkan dan mengaktifkan callback debug, sistem akan merutekan pesan debug ke callback yang Anda daftarkan. Contoh callback tersebut ditampilkan di bawah ini:
#include <android/log.h> VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsMessenger( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT *callbackData, void *userData) { const char validation[] = "Validation"; const char performance[] = "Performance"; const char error[] = "ERROR"; const char warning[] = "WARNING"; const char unknownType[] = "UNKNOWN_TYPE"; const char unknownSeverity[] = "UNKNOWN_SEVERITY"; const char* typeString = unknownType; const char* severityString = unknownSeverity; const char* messageIdName = callbackData->pMessageIdName; int32_t messageIdNumber = callbackData->messageIdNumber; const char* message = callbackData->pMessage; android_LogPriority priority = ANDROID_LOG_UNKNOWN; if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { severityString = error; priority = ANDROID_LOG_ERROR; } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { severityString = warning; priority = ANDROID_LOG_WARN; } if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) { typeString = validation; } else if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) { typeString = performance; } __android_log_print(priority, "AppName", "%s %s: [%s] Code %i : %s", typeString, severityString, messageIdName, messageIdNumber, message); // Returning false tells the layer not to stop when the event occurs, so // they see the same behavior with and without validation layers enabled. return VK_FALSE; }
Mengirim lapisan ke perangkat pengujian menggunakan ADB
Ikuti langkah-langkah di bagian ini untuk mengirim lapisan ke perangkat pengujian:
Aktifkan proses debug
Model keamanan dan kebijakan Android sangat berbeda dengan platform lainnya. Untuk memuat lapisan eksternal, salah satu kondisi berikut harus terpenuhi:
- File manifes aplikasi target menyertakan
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 memberikan lebih banyak informasi debug, tetapi mungkin akan berdampak buruk pada performa aplikasi Anda.
- Aplikasi target dijalankan pada build userdebug sistem operasi yang memberikan izin akses root.
Muat lapisan
Perangkat yang menjalankan Android 9 (API level 28) dan yang lebih baru memungkinkan Vulkan memuat lapisan dari penyimpanan lokal aplikasi Anda. Android 10 (API level 29) mendukung pemuatan lapisan dari APK terpisah.
- Biner lapisan di penyimpanan lokal perangkat
- APK yang berisi lapisan, yang diinstal secara terpisah dari aplikasi target
Biner lapisan di penyimpanan lokal perangkat
Vulkan mencari biner dalam direktori penyimpanan data sementara perangkat, sehingga Anda harus terlebih dahulu mengirimkan biner ke direktori tersebut menggunakan ADB (Android Debug Bridge), seperti berikut:
-
Gunakan perintah
adb push
untuk memuat biner lapisan yang diinginkan ke penyimpanan data aplikasi di perangkat. Contoh berikut mengirimlibVkLayer_khronos_validation.so
ke direktori/data/local/tmp
perangkat:$ adb push libVkLayer_khronos_validation.so /data/local/tmp
-
Gunakan perintah
adb shell
danrun-as
untuk memuat lapisan dalam proses aplikasi. Dengan kata lain, biner tersebut memiliki akses perangkat yang sama dengan yang dimiliki aplikasi tanpa memerlukan akses root.$ adb shell run-as com.example.myapp cp /data/local/tmp/libVkLayer_khronos_validation.so . $ adb shell run-as com.example.myapp ls libVkLayer_khronos_validation.so
- Aktifkan lapisan.
APK yang berisi lapisan
Anda dapat menggunakan adb
untuk menginstal APK,
lalu mengaktifkan lapisan.
adb install --abi abi path_to_apk
Mengaktifkan lapisan di luar aplikasi
Anda dapat mengaktifkan lapisan per aplikasi maupun secara global. Setelan per aplikasi tetap dipertahankan meskipun perangkat di-reboot, sedangkan properti global akan dihapus pada waktu reboot.
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) adb shell settings put global gpu_debug_layers <layer1:layer2:layerN> # Specify packages to search for layers adb shell settings put global gpu_debug_layer_app <package1:package2:packageN>
Jika ingin memeriksa apakah setelan sudah diaktifkan, Anda dapat melakukannya menggunakan perintah berikut:
$ adb shell settings list global | grep gpu enable_gpu_debug_layers=1 gpu_debug_app=com.example.myapp gpu_debug_layers=VK_LAYER_KHRONOS_validation
Karena setelan yang diterapkan akan dipertahankan meskipun perangkat di-reboot, Anda mungkin perlu menghapus setelan setelah lapisan dimuat:
$ adb shell settings delete global enable_gpu_debug_layers $ adb shell settings delete global gpu_debug_app $ adb shell settings delete global gpu_debug_layers $ adb shell settings delete global gpu_debug_layer_app
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 # 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.vulkan.layers <layer1:layer2:layerN>