Sebagian besar API grafis eksplisit tidak melakukan pemeriksaan error karena dapat menyebabkan masalah performa. Vulkan memiliki lapisan validasi yang menyediakan pemeriksaan error selama pengembangan, sehingga menghindari masalah performa dalam build rilis aplikasi Anda. Lapisan validasi mengandalkan mekanisme lapisan tujuan umum yang mencegat titik entri API.
Lapisan validasi Khronos tunggal
Sebelumnya, Vulkan menyediakan beberapa lapisan validasi yang harus diaktifkan
dalam urutan tertentu. Mulai dari rilis Vulkan SDK 1.1.106.0, aplikasi Anda
hanya perlu mengaktifkan satu lapisan
validasi,
VK_LAYER_KHRONOS_validation
untuk mendapatkan semua fitur dari lapisan
validasi sebelumnya.
Menggunakan lapisan validasi yang dipaketkan dalam APK
Memaketkan lapisan validasi dalam APK akan memastikan kompatibilitas yang optimal. Lapisan validasi ini tersedia sebagai biner bawaan atau dapat dibangun dari kode sumber.
Menggunakan biner bawaan
Download biner Lapisan validasi Android Vulkan dari halaman rilis GitHub.
Cara termudah untuk menambahkan lapisan ke APK adalah dengan mengekstrak biner lapisan
bawaan ke direktori src/main/jniLibs/
modul Anda, dengan direktori
ABI (seperti arm64-v8a
atau x86-64
) lengkap, seperti ini:
src/main/jniLibs/ arm64-v8a/ libVkLayer_khronos_validation.so armeabi-v7a/ libVkLayer_khronos_validation.so x86/ libVkLayer_khronos_validation.so x86-64/ libVkLayer_khronos_validation.so
Mem-build lapisan validasi dari kode sumber
Untuk men-debug kode sumber lapisan validasi, gunakan sumber terbaru dari repositori GitHub Khronos Group dan ikuti petunjuk proses build di sana.
Memverifikasi bahwa lapisan validasi sudah dipaketkan dengan benar
Terlepas dari apakah Anda mem-build dengan lapisan Khronos bawaan atau lapisan yang di-build dari sumber, proses build akan menghasilkan struktur file akhir dalam APK seperti berikut:
lib/ arm64-v8a/ libVkLayer_khronos_validation.so armeabi-v7a/ libVkLayer_khronos_validation.so x86/ libVkLayer_khronos_validation.so x86-64/ libVkLayer_khronos_validation.so
Perintah berikut menunjukkan cara memverifikasi bahwa APK Anda berisi lapisan validasi seperti yang diharapkan:
$ jar -tf project.apk | grep libVkLayer lib/x86_64/libVkLayer_khronos_validation.so lib/armeabi-v7a/libVkLayer_khronos_validation.so lib/arm64-v8a/libVkLayer_khronos_validation.so lib/x86/libVkLayer_khronos_validation.so
Mengaktifkan lapisan validasi selama pembuatan instance
Vulkan API memungkinkan aplikasi mengaktifkan lapisan selama pembuatan instance. Titik entri yang diintersepsi lapisan harus memiliki salah satu objek berikut sebagai parameter pertama:
VkInstance
VkPhysicalDevice
VkDevice
VkCommandBuffer
VkQueue
Panggil vkEnumerateInstanceLayerProperties()
untuk mencantumkan lapisan yang tersedia dan propertinya. Vulkan mengaktifkan lapisan saat
vkCreateInstance()
dieksekusi.
Cuplikan kode berikut menunjukkan cara aplikasi menggunakan Vulkan API untuk mengkueri dan mengaktifkan lapisan secara terprogram:
// Enable just the Khronos validation layer. static const char *layers[] = {"VK_LAYER_KHRONOS_validation"}; // Get the layer count using a null pointer as the last parameter. uint32_t instance_layer_present_count = 0; vkEnumerateInstanceLayerProperties(&instance_layer_present_count, nullptr); // Enumerate layers with a valid pointer in the last parameter. VkLayerProperties layer_props[instance_layer_present_count]; vkEnumerateInstanceLayerProperties(&instance_layer_present_count, layer_props); // Make sure selected validation layers are available. VkLayerProperties *layer_props_end = layer_props + instance_layer_present_count; for (const char* layer:layers) { assert(layer_props_end != std::find_if(layer_props, layer_props_end, [layer](VkLayerProperties layerProperties) { return strcmp(layerProperties.layerName, layer) == 0; })); } // Create a Vulkan instance, requesting all enabled layers or extensions // available on the system VkInstanceCreateInfo instanceCreateInfo{ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pNext = nullptr, .pApplicationInfo = &appInfo, .enabledLayerCount = sizeof(layers) / sizeof(layers[0]), .ppEnabledLayerNames = layers,
Output logcat default
Lapisan validasi memberikan pesan peringatan dan error di logcat yang diberi label
tag VALIDATION
. Pesan lapisan validasi terlihat seperti berikut (dengan
baris baru ditambahkan di sini agar lebih mudah di-scroll):
Validation -- Validation Error: [ VUID-VkDeviceQueueCreateInfo-pQueuePriorities-parameter ] Object 0: VK_NULL_HANDLE, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0xd6d720c6 | vkCreateDevice: required parameter pCreateInfo->pQueueCreateInfos[0].pQueuePriorities specified as NULL. The Vulkan spec states: pQueuePriorities must be a valid pointer to an array of queueCount float values (https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html #VUID-VkDeviceQueueCreateInfo-pQueuePriorities-parameter)
Mengaktifkan callback debug
Ekstensi Debug Utils VK_EXT_debug_utils
memungkinkan aplikasi Anda membuat
pembawa pesan debug yang meneruskan pesan lapisan validasi ke callback yang disediakan
aplikasi. Perangkat Anda mungkin tidak menerapkannya, tetapi ekstensi ini diterapkan di
lapisan validasi terbaru. Ada juga ekstensi yang disebut VK_EXT_debug_report
, yang tidak
digunakan lagi dan memberikan kemampuan serupa jika
VK_EXT_debug_utils
tidak tersedia.
Sebelum menggunakan ekstensi Debug Utils, Anda harus memastikan bahwa perangkat atau lapisan validasi yang dimuat mendukungnya. Contoh berikut menunjukkan cara memeriksa apakah ekstensi debug utils didukung dan mendaftarkan callback jika ekstensi didukung oleh perangkat atau lapisan validasi.
// Get the instance extension count. uint32_t inst_ext_count = 0; vkEnumerateInstanceExtensionProperties(nullptr, &inst_ext_count, nullptr); // Enumerate the instance extensions. VkExtensionProperties inst_exts[inst_ext_count]; vkEnumerateInstanceExtensionProperties(nullptr, &inst_ext_count, inst_exts); // Check for debug utils extension within the system driver or loader. // Check if the debug utils extension is available (in the driver). VkExtensionProperties *inst_exts_end = inst_exts + inst_ext_count; bool debugUtilsExtAvailable = inst_exts_end != std::find_if(inst_exts, inst_exts_end, [](VkExtensionProperties extensionProperties) { return strcmp(extensionProperties.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0; }); if ( !debugUtilsExtAvailable ) { // Also check the layers for the debug utils extension. for (auto layer: layer_props) { uint32_t layer_ext_count; vkEnumerateInstanceExtensionProperties(layer.layerName, &layer_ext_count, nullptr); if (layer_ext_count == 0) continue; VkExtensionProperties layer_exts[layer_ext_count]; vkEnumerateInstanceExtensionProperties(layer.layerName, &layer_ext_count, layer_exts); VkExtensionProperties * layer_exts_end = layer_exts + layer_ext_count; debugUtilsExtAvailable = layer_exts != std::find_if( layer_exts, layer_exts_end,[](VkExtensionProperties extensionProperties) { return strcmp(extensionProperties.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0; }); if (debugUtilsExtAvailable) { // Add the including layer into the layer request list if necessary. break; } } } if (!debugUtilsExtAvailable) return; // since this snippet depends on debugUtils const char * enabled_inst_exts[] = { ..., VK_EXT_DEBUG_UTILS_EXTENSION_NAME }; uint32_t enabled_extension_count = sizeof(enabled_inst_exts)/sizeof(enabled_inst_exts[0]); // Pass the instance extensions into vkCreateInstance. VkInstanceCreateInfo instance_info = {}; instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instance_info.enabledExtensionCount = enabled_extension_count; instance_info.ppEnabledExtensionNames = enabled_inst_exts; // NOTE: Can still return VK_ERROR_EXTENSION_NOT_PRESENT if validation layer // isn't loaded. vkCreateInstance(&instance_info, nullptr, &instance); auto pfnCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( tutorialInstance, "vkCreateDebugUtilsMessengerEXT"); auto pfnDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( tutorialInstance, "vkDestroyDebugUtilsMessengerEXT"); // Create the debug messenger callback with your the settings you want. 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; // The DebugUtilsMessenger callback is explained in the following section. messengerInfo.pfnUserCallback = &DebugUtilsMessenger; 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, sistem akan merutekan pesan debug kepadanya.
#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; }
Menggunakan lapisan validasi eksternal
Anda tidak perlu memaketkan lapisan validasi dalam APK; perangkat yang menjalankan Android 9 (API level 28) dan yang lebih tinggi dapat menggunakan lapisan validasi di luar biner Anda serta menonaktifkan dan mengaktifkannya secara dinamis. Ikuti langkah-langkah di bagian ini untuk mengirim lapisan validasi ke perangkat pengujian Anda:
Mengaktifkan aplikasi untuk menggunakan lapisan validasi eksternal
Model keamanan dan kebijakan Android sangat berbeda dari platform lainnya. Untuk memuat lapisan validasi eksternal, salah satu kondisi berikut harus terpenuhi:
Aplikasi target dapat di-debug. Opsi ini menghasilkan lebih banyak informasi debug, tetapi dapat berpengaruh negatif terhadap performa aplikasi Anda.
Aplikasi target dijalankan pada build userdebug sistem operasi yang memberikan izin akses root.
Khusus aplikasi yang menargetkan Android 11 (API level 30) atau yang lebih tinggi: File manifes Android target Anda menyertakan elemen
meta-data
berikut:<meta-data android:name="com.android.graphics.injectLayers.enable" android:value="true"/>
Memuat lapisan validasi eksternal
Perangkat yang menjalankan Android 9 (API level 28) dan yang lebih tinggi memungkinkan Vulkan memuat lapisan validasi dari penyimpanan lokal aplikasi. Mulai dari Android 10 (API level 29), Vulkan juga dapat memuat lapisan validasi dari APK terpisah. Anda dapat memilih metode mana pun yang disukai selama versi Android Anda mendukungnya.
Memuat biner lapisan validasi dari penyimpanan lokal perangkat
Karena Vulkan mencari biner dalam direktori penyimpanan data sementara perangkat, Anda harus terlebih dahulu mengirimkan biner ke direktori tersebut menggunakan Android Debug Bridge (adb), seperti berikut:
Gunakan perintah
adb push
untuk memuat biner lapisan ke penyimpanan data aplikasi di perangkat:$ adb push libVkLayer_khronos_validation.so /data/local/tmp
Gunakan perintah
adb shell
danrun-as
untuk memuat lapisan selama proses aplikasi Anda. Dengan kata lain, biner tersebut memiliki akses perangkat yang sama seperti 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
Memuat biner lapisan validasi dari APK lain
Anda dapat menggunakan adb
untuk menginstal APK yang
berisi lapisan, lalu mengaktifkan lapisan.
adb install --abi abi path_to_apk
Mengaktifkan lapisan di luar aplikasi
Anda dapat mengaktifkan lapisan Vulkan, baik per aplikasi maupun secara global. Setelan per aplikasi tetap dipertahankan meskipun perangkat dimulai ulang, sedangkan properti global akan dihapus saat perangkat dimulai ulang.
Mengaktifkan lapisan per aplikasi
Langkah-langkah berikut menjelaskan cara mengaktifkan lapisan per aplikasi:
Gunakan setelan shell adb untuk mengaktifkan lapisan:
$ adb shell settings put global enable_gpu_debug_layers 1
Tentukan aplikasi target tempat lapisan akan diaktifkan:
$ adb shell settings put global gpu_debug_app <package_name>
Tentukan daftar lapisan yang akan diaktifkan (dari atas ke bawah), dengan memisahkan setiap lapisan menggunakan titik dua:
$ adb shell settings put global gpu_debug_layers <layer1:layer2:layerN>
Karena kita memiliki lapisan validasi Khronos tunggal, perintahnya mungkin akan terlihat seperti:
$ adb shell settings put global gpu_debug_layers VK_LAYER_KHRONOS_validation
Tentukan satu atau beberapa paket untuk menelusuri lapisan dalam:
$ adb shell settings put global gpu_debug_layer_app <package1:package2:packageN>
Anda dapat memeriksa apakah setelan diaktifkan 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 dimulai ulang, sebaiknya hapus 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
Mengaktifkan lapisan secara global
Anda dapat mengaktifkan satu atau beberapa lapisan secara global hingga perangkat dimulai ulang. Tindakan ini berupaya memuat lapisan untuk semua aplikasi, termasuk file native yang dapat dieksekusi.
$ adb shell setprop debug.vulkan.layers <layer1:layer2:layerN>