Çoğu açık grafik API'si, performans kaybına neden olabileceği için hata kontrolü gerçekleştirmez. Vulkan, geliştirme sırasında hata kontrolü sağlayan ve uygulamanızın yayın derlemesinde performans kaybını önleyen doğrulama katmanlarına sahiptir. Doğrulama katmanları, API giriş noktalarını kesen genel amaçlı bir katmanlama mekanizmasına dayanır.
Tek Khronos doğrulama katmanı
Daha önce Vulkan, belirli bir sırayla etkinleştirilmesi gereken birden fazla doğrulama katmanı sağlıyordu. 1.1.106.0 Vulkan SDK sürümünden itibaren, uygulamanızın önceki doğrulama katmanlarındaki tüm özellikleri kullanabilmesi için yalnızca VK_LAYER_KHRONOS_validation
adlı tek bir doğrulama katmanını etkinleştirmesi yeterlidir.
APK'nıza paketlenmiş doğrulama katmanlarını kullanma
APK'nızdaki paket doğrulama katmanları, optimum uyumluluk sağlar. Doğrulama katmanları, önceden oluşturulmuş ikili dosyalar olarak kullanılabilir veya kaynak kodundan derlenebilir.
Önceden derlenmiş ikili dosyaları kullanma
En son Android Vulkan Validation katmanı ikililerini GitHub sürüm sayfasından indirin.
Katmanları APK'nıza eklemenin en kolay yolu, önceden oluşturulmuş katman ikililerini ABI dizinleri (arm64-v8a
veya x86-64
gibi) bozulmadan modülünüzün src/main/jniLibs/
dizine ayıklamaktır. Örneğin:
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
Doğrulama katmanını kaynak kodundan oluşturma
Doğrulama katmanı kaynak kodunda hata ayıklamak için Khronos Group GitHub deposundan en son kaynağı çekip buradaki derleme talimatlarını uygulayın.
Doğrulama katmanının doğru şekilde paketlendiğini doğrulama
Khronos önceden oluşturulmuş katmanlarıyla veya kaynaktan oluşturulmuş katmanlarla derleme yapıp yapmadığınızdan bağımsız olarak derleme işlemi, APK'nızda aşağıdaki gibi bir nihai dosya yapısı oluşturur:
lib/ arm64-v8a/ libVkLayer_khronos_validation.so armeabi-v7a/ libVkLayer_khronos_validation.so x86/ libVkLayer_khronos_validation.so x86-64/ libVkLayer_khronos_validation.so
Aşağıdaki komutta, APK'nızın doğrulama katmanını beklendiği gibi içerdiğini nasıl doğrulayacağınız gösterilmektedir:
$ 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
Örnek oluşturma sırasında doğrulama katmanını etkinleştirme
Vulkan API, bir uygulamanın örnek oluşturma sırasında katmanları etkinleştirmesine olanak tanır. Bir katmanın kestiği giriş noktalarında, ilk parametre olarak aşağıdaki nesnelerden biri bulunmalıdır:
VkInstance
VkPhysicalDevice
VkDevice
VkCommandBuffer
VkQueue
Kullanılabilir katmanları ve özelliklerini listelemek için vkEnumerateInstanceLayerProperties()
işlevini çağırın. Vulkan, vkCreateInstance()
yürütüldüğünde katmanları etkinleştirir.
Aşağıdaki kod snippet'i, bir uygulamanın katmanları programatik olarak sorgulamak ve etkinleştirmek için Vulkan API'yi nasıl kullanabileceğini gösterir:
// 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,
Varsayılan logcat çıkışı
Doğrulama katmanı, logcat'te VALIDATION
etiketiyle etiketlenmiş uyarı ve hata mesajları yayınlar. Doğrulama katmanı mesajı aşağıdaki gibi görünür (daha kolay kaydırma için buraya satır sonları eklenir):
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)
Hata ayıklama geri çağırma işlevini etkinleştirme
Debug Utils uzantısı VK_EXT_debug_utils
, uygulamanızın doğrulama katmanı mesajlarını uygulama tarafından sağlanan bir geri çağırma işlevine ileten bir hata ayıklama mesajcısı oluşturmasına olanak tanır. Cihazınız bu uzantıyı uygulayamayabilir ancak en son doğrulama
katmanlarında uygulanmıştır. Ayrıca, VK_EXT_debug_utils
kullanılamıyorsa benzer özellikler sunan ve desteği sonlandırılmış VK_EXT_debug_report
adlı bir uzantı da vardır.
Debug Utils uzantısını kullanmadan önce cihazınızın veya yüklenen bir doğrulama katmanının uzantıyı desteklediğinden emin olmalısınız. Aşağıdaki örnekte, hata ayıklama yardımcı programları uzantısının desteklenip desteklenmediğinin nasıl kontrol edileceği ve uzantı cihaz veya doğrulama katmanı tarafından destekliyorsa geri çağırma işlevinin nasıl kaydedileceği gösterilmektedir.
// 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); }
Uygulamanız geri aramayı kaydettikten ve etkinleştirdikten sonra sistem, hata ayıklama mesajlarını ona yönlendirir.
#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; }
Harici doğrulama katmanlarını kullanma
Doğrulama katmanlarını APK'nıza paketlemeniz gerekmez. Android 9 (API düzeyi 28) ve sonraki sürümleri çalıştıran cihazlar, ikili dosyanızın dışındaki doğrulama katmanlarını kullanabilir ve bunları dinamik olarak devre dışı bırakabilir ya da etkinleştirebilir. Doğrulama katmanlarını test cihazınıza göndermek için bu bölümdeki adımları uygulayın:
Uygulamanızın harici doğrulama katmanlarını kullanmasına izin verin
Android'in güvenlik modeli ve politikaları diğer platformlardan önemli ölçüde farklıdır. Harici doğrulama katmanlarını yüklemek için aşağıdaki koşullardan biri doğru olmalıdır:
Hedef uygulama hata ayıklama özelliğine sahip olmalıdır. Bu seçenek, daha fazla hata ayıklama bilgisi sunar ancak uygulamanızın performansını olumsuz yönde etkileyebilir.
Hedef uygulama, işletim sisteminin kök erişimi veren bir userdebug derlemesi üzerinde çalıştırılır.
Yalnızca Android 11 (API düzeyi 30) veya sonraki sürümleri hedefleyen uygulamalar: Hedef Android manifest dosyanız aşağıdaki
meta-data
öğesini içeriyor:<meta-data android:name="com.android.graphics.injectLayers.enable" android:value="true"/>
Harici bir doğrulama katmanı yükleme
Android 9 (API düzeyi 28) ve sonraki sürümleri çalıştıran cihazlar, Vulkan'ın uygulamanızın yerel depolama alanından doğrulama katmanını yüklemesine izin verir. Android 10'dan (API düzeyi 29) itibaren Vulkan, doğrulama katmanını ayrı bir APK'dan da yükleyebilir. Android sürümünüz destekliyorsa istediğiniz yöntemi seçebilirsiniz.
Cihazınızın yerel depolama alanından bir doğrulama katmanı ikili programı yükleyin
Vulkan, cihazınızın geçici veri depolama dizinindeki ikili dosyayı aradığı için öncelikle aşağıdaki gibi Android DebugBridge'i (adb) kullanarak ikili programı bu dizine aktarmanız gerekir:
Katman ikilisini uygulamanızın cihazdaki veri depolama alanına yüklemek için
adb push
komutunu kullanın:$ adb push libVkLayer_khronos_validation.so /data/local/tmp
Katmanı uygulama işleminiz aracılığıyla yüklemek için
adb shell
verun-as
komutlarını kullanın. Yani ikili dosya, root erişimi gerektirmeden uygulamanın sahip olduğu cihaz erişimine sahiptir.$ 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
Başka bir APK'dan doğrulama katmanı ikili programı yükle
Katmanı içeren bir APK yüklemek ve ardından katmanı etkinleştirmek için adb
'ü kullanabilirsiniz.
adb install --abi abi path_to_apk
Katmanları uygulamanın dışında etkinleştirme
Vulkan katmanlarını uygulama bazında veya genel olarak etkinleştirebilirsiniz. Uygulama başına ayarlar yeniden başlatmalarda korunur, genel özellikler ise yeniden başlatma sırasında silinir.
Katmanları uygulama bazında etkinleştirme
Aşağıdaki adımlarda, katmanların uygulama bazında nasıl etkinleştirileceği açıklanmaktadır:
Katmanları etkinleştirmek için adb kabuk ayarlarını kullanın:
$ adb shell settings put global enable_gpu_debug_layers 1
Katmanları etkinleştireceğiniz hedef uygulamayı belirtin:
$ adb shell settings put global gpu_debug_app <package_name>
Her katmanı iki nokta üst üste ile ayırarak, etkinleştirilecek katmanların listesini belirtin (yukarıdan aşağıya):
$ adb shell settings put global gpu_debug_layers <layer1:layer2:layerN>
Tek bir Khronos doğrulama katmanımız olduğundan komut büyük olasılıkla şöyle görünür:
$ adb shell settings put global gpu_debug_layers VK_LAYER_KHRONOS_validation
Aşağıdakiler içindeki katmanların aranacağı bir veya daha fazla paket belirtin:
$ adb shell settings put global gpu_debug_layer_app <package1:package2:packageN>
Aşağıdaki komutları kullanarak ayarların etkin olup olmadığını kontrol edebilirsiniz:
$ 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
Uyguladığınız ayarlar cihaz yeniden başlatıldığında da geçerli olmaya devam ettiğinden, katmanlar yüklendikten sonra ayarları temizlemek isteyebilirsiniz:
$ 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
Katmanları dünya genelinde etkinleştirme
Bir veya daha fazla katmanı bir sonraki yeniden başlatmaya kadar genel olarak etkinleştirebilirsiniz. Bu işlem, yerel yürütülebilir dosyalar dahil tüm uygulamaların katmanlarını yüklemeye çalışır.
$ adb shell setprop debug.vulkan.layers <layer1:layer2:layerN>