ज़्यादातर ग्राफ़िक्स एपीआई, गड़बड़ी की जांच नहीं करते. ऐसा इसलिए, क्योंकि इससे परफ़ॉर्मेंस पर असर पड़ सकता है. Vulkan में मान्य लेयर होती हैं. ये डेवलपमेंट के दौरान गड़बड़ियों की जांच करती हैं. इससे आपके ऐप्लिकेशन के रिलीज़ बिल्ड में परफ़ॉर्मेंस पर असर नहीं पड़ता. मान्य लेयर, सामान्य मकसद वाली लेयरिंग के तरीके पर निर्भर करती हैं. यह एपीआई एंट्री पॉइंट को इंटरसेप्ट करती है.
Khronos की पुष्टि करने वाली एक लेयर
पहले, Vulkan में पुष्टि करने वाली कई लेयर उपलब्ध थीं. इन्हें एक खास क्रम में चालू करना पड़ता था. Vulkan SDK के 1.1.106.0 वर्शन के रिलीज़ होने के बाद, आपके ऐप्लिकेशन को एक ही पुष्टि करने वाली लेयर चालू करनी होगी, VK_LAYER_KHRONOS_validation
, ताकि पिछली पुष्टि करने वाली लेयर की सभी सुविधाएं मिल सकें.
अपने APK में पैकेज की गई पुष्टि करने वाली लेयर का इस्तेमाल करना
आपके APK में पैकेजिंग की पुष्टि करने वाली लेयर, यह पक्का करती हैं कि APK सबसे सही तरीके से काम करे. पुष्टि करने वाली लेयर, पहले से बनी बाइनरी के तौर पर उपलब्ध होती हैं. इन्हें सोर्स कोड से भी बनाया जा सकता है.
पहले से बने बाइनरी का इस्तेमाल करना
GitHub के रिलीज़ पेज से, Android Vulkan Validation layer के नए बाइनरी डाउनलोड करें.
अपने APK में लेयर जोड़ने का सबसे आसान तरीका यह है कि पहले से बनी लेयर के बाइनरी फ़ाइल को अपने मॉड्यूल की src/main/jniLibs/
डायरेक्ट्री में एक्सट्रैक्ट करें. साथ ही, ABI डायरेक्ट्री (जैसे कि arm64-v8a
या x86-64
) को इस तरह से इंटैक्ट रखें:
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
सोर्स कोड से पुष्टि करने वाली लेयर बनाना
पुष्टि करने वाली लेयर के सोर्स कोड में डीबग करने के लिए, Khronos Group की GitHub रिपॉज़िटरी से नया सोर्स पुल करें. इसके बाद, वहां दिए गए निर्देशों का पालन करके इसे बनाएं.
पुष्टि करें कि पुष्टि करने वाली लेयर को सही तरीके से पैकेज किया गया है
Khronos की प्रीबिल्ट लेयर या सोर्स से बनाई गई लेयर का इस्तेमाल करके, बिल्ड प्रोसेस से आपके APK में फ़ाइनल फ़ाइल स्ट्रक्चर बनता है. यह स्ट्रक्चर इस तरह का होता है:
lib/ arm64-v8a/ libVkLayer_khronos_validation.so armeabi-v7a/ libVkLayer_khronos_validation.so x86/ libVkLayer_khronos_validation.so x86-64/ libVkLayer_khronos_validation.so
यहां दिए गए कमांड से, यह पुष्टि करने का तरीका बताया गया है कि आपके APK में, पुष्टि करने वाली लेयर मौजूद है या नहीं:
$ 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
इंस्टेंस बनाते समय, पुष्टि करने वाली लेयर चालू करना
Vulkan API की मदद से, ऐप्लिकेशन को इंस्टेंस बनाने के दौरान लेयर चालू करने की अनुमति मिलती है. जिन एंट्री पॉइंट को लेयर इंटरसेप्ट करती है उनके पहले पैरामीटर के तौर पर, इनमें से कोई एक ऑब्जेक्ट होना चाहिए:
VkInstance
VkPhysicalDevice
VkDevice
VkCommandBuffer
VkQueue
उपलब्ध लेयर और उनकी प्रॉपर्टी की सूची बनाने के लिए, vkEnumerateInstanceLayerProperties()
को कॉल करें. Vulkan, लेयर तब चालू करता है, जब vkCreateInstance()
एक्ज़ीक्यूट होता है.
नीचे दिए गए कोड स्निपेट में बताया गया है कि कोई ऐप्लिकेशन, प्रोग्राम के हिसाब से लेयर के बारे में क्वेरी करने और उन्हें चालू करने के लिए, Vulkan API का इस्तेमाल कैसे कर सकता है:
// 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,
logcat का डिफ़ॉल्ट आउटपुट
पुष्टि करने वाली लेयर, logcat में चेतावनी और गड़बड़ी के मैसेज दिखाती है. इन मैसेज को VALIDATION
टैग से लेबल किया जाता है. पुष्टि करने वाली लेयर का मैसेज इस तरह दिखता है (यहां लाइन ब्रेक इसलिए जोड़े गए हैं, ताकि स्क्रोल करना आसान हो):
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)
डीबग कॉलबैक चालू करना
Debug Utils एक्सटेंशन VK_EXT_debug_utils
की मदद से, आपका ऐप्लिकेशन एक डिबग मैसेंजर बना सकता है. यह मैसेंजर, पुष्टि करने वाली लेयर के मैसेज को ऐप्लिकेशन के दिए गए कॉलबैक पर भेजता है. ऐसा हो सकता है कि आपका डिवाइस इस एक्सटेंशन को लागू न करे. हालांकि, इसे पुष्टि करने वाली सबसे नई लेयर में लागू किया गया है. एक ऐसा एक्सटेंशन भी है जिसका इस्तेमाल अब नहीं किया जाता. इसका नाम VK_EXT_debug_report
है. अगर VK_EXT_debug_utils
उपलब्ध नहीं है, तो यह एक्सटेंशन इसी तरह की सुविधाएं देता है.
Debug Utils एक्सटेंशन का इस्तेमाल करने से पहले, आपको यह पक्का करना होगा कि आपका डिवाइस या लोड की गई पुष्टि करने वाली लेयर, इसे सपोर्ट करती हो. इस उदाहरण में यह जांच करने का तरीका दिखाया गया है कि क्या डीबग यूटिलिटी एक्सटेंशन काम करता है. साथ ही, अगर एक्सटेंशन डिवाइस या पुष्टि करने वाली लेयर के साथ काम करता है, तो कॉलबैक रजिस्टर करने का तरीका भी दिखाया गया है.
// 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); }
जब आपका ऐप्लिकेशन, कॉलबैक को रजिस्टर और चालू कर देता है, तब सिस्टम डीबग करने से जुड़े मैसेज को उस पर भेजता है.
#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; }
पुष्टि करने वाली बाहरी लेयर का इस्तेमाल करना
आपको अपने APK में पुष्टि करने वाली लेयर को पैकेज करने की ज़रूरत नहीं है. Android 9 (एपीआई लेवल 28) और इसके बाद के वर्शन पर काम करने वाले डिवाइस, आपके बाइनरी के बाहर की पुष्टि करने वाली लेयर का इस्तेमाल कर सकते हैं. साथ ही, उन्हें डाइनैमिक तरीके से बंद और चालू कर सकते हैं. अपने टेस्ट डिवाइस पर पुष्टि करने वाली लेयर भेजने के लिए, इस सेक्शन में दिया गया तरीका अपनाएं:
अपने ऐप्लिकेशन को बाहरी पुष्टि करने वाली लेयर इस्तेमाल करने की अनुमति देना
Android का सुरक्षा मॉडल और नीतियां, अन्य प्लैटफ़ॉर्म से काफ़ी अलग हैं. बाहरी पुष्टि करने वाली लेयर लोड करने के लिए, इनमें से कोई एक शर्त पूरी होनी चाहिए:
टारगेट ऐप्लिकेशन को डीबग किया जा सकता है. इस विकल्प से डीबग करने से जुड़ी ज़्यादा जानकारी मिलती है. हालांकि, इससे आपके ऐप्लिकेशन की परफ़ॉर्मेंस पर बुरा असर पड़ सकता है.
टारगेट ऐप्लिकेशन, ऑपरेटिंग सिस्टम के userdebug बिल्ड पर चलता है. इससे रूट ऐक्सेस मिलता है.
सिर्फ़ Android 11 (एपीआई लेवल 30) या उसके बाद के वर्शन को टारगेट करने वाले ऐप्लिकेशन के लिए: आपकी टारगेट Android मेनिफ़ेस्ट फ़ाइल में यह
meta-data
एलिमेंट शामिल है:<meta-data android:name="com.android.graphics.injectLayers.enable" android:value="true"/>
पुष्टि करने वाली बाहरी लेयर लोड करना
Android 9 (एपीआई लेवल 28) और इसके बाद के वर्शन वाले डिवाइसों पर, Vulkan को आपके ऐप्लिकेशन के लोकल स्टोरेज से पुष्टि करने वाली लेयर लोड करने की अनुमति मिलती है. Android 10 (एपीआई लेवल 29) से, Vulkan किसी दूसरे APK से पुष्टि करने वाली लेयर भी लोड कर सकता है. आपके पास अपनी पसंद का कोई भी तरीका चुनने का विकल्प होता है. हालांकि, यह ज़रूरी है कि आपके Android वर्शन पर वह तरीका काम करता हो.
अपने डिवाइस के लोकल स्टोरेज से, पुष्टि करने वाली लेयर का बाइनरी लोड करना
Vulkan आपके डिवाइस के अस्थायी डेटा स्टोरेज डायरेक्ट्री में बाइनरी ढूंढता है. इसलिए, आपको Android Debug Bridge (adb) का इस्तेमाल करके, बाइनरी को उस डायरेक्ट्री में पुश करना होगा. इसके लिए, यह तरीका अपनाएं:
डिवाइस पर अपने ऐप्लिकेशन के डेटा स्टोरेज में लेयर बाइनरी लोड करने के लिए,
adb push
कमांड का इस्तेमाल करें:$ adb push libVkLayer_khronos_validation.so /data/local/tmp
अपने ऐप्लिकेशन प्रोसेस के ज़रिए लेयर लोड करने के लिए,
adb shell
औरrun-as
कमांड का इस्तेमाल करें. इसका मतलब है कि बाइनरी के पास डिवाइस का वही ऐक्सेस है जो ऐप्लिकेशन के पास है. इसके लिए, रूट ऐक्सेस की ज़रूरत नहीं होती.$ 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
किसी दूसरे APK से पुष्टि करने वाली लेयर का बाइनरी लोड करना
लेयर वाला APK इंस्टॉल करने के लिए, adb
का इस्तेमाल किया जा सकता है. इसके बाद, लेयर चालू करें.
adb install --abi abi path_to_apk
ऐप्लिकेशन से बाहर लेयर चालू करना
Vulkan लेयर को हर ऐप्लिकेशन के हिसाब से या ग्लोबल तौर पर चालू किया जा सकता है. हर ऐप्लिकेशन के लिए की गई सेटिंग, रीबूट करने पर भी बनी रहती हैं. वहीं, ग्लोबल प्रॉपर्टी रीबूट करने पर हट जाती हैं.
हर ऐप्लिकेशन के हिसाब से लेयर चालू करना
यहां दिए गए तरीके से, हर ऐप्लिकेशन के हिसाब से लेयर चालू की जा सकती हैं:
लेयर चालू करने के लिए, adb shell settings का इस्तेमाल करें:
$ adb shell settings put global enable_gpu_debug_layers 1
उस टारगेट ऐप्लिकेशन के बारे में बताएं जिस पर लेयर चालू करनी हैं:
$ adb shell settings put global gpu_debug_app <package_name>
चालू की जाने वाली लेयर की सूची (ऊपर से नीचे की ओर) तय करें. हर लेयर को कोलन लगाकर अलग करें:
$ adb shell settings put global gpu_debug_layers <layer1:layer2:layerN>
हमारे पास एक ही Khronos पुष्टि करने वाली लेयर है. इसलिए, कमांड इस तरह दिखेगी:
$ adb shell settings put global gpu_debug_layers VK_LAYER_KHRONOS_validation
लेयर खोजने के लिए, एक या उससे ज़्यादा पैकेज तय करें:
$ adb shell settings put global gpu_debug_layer_app <package1:package2:packageN>
इन कमांड का इस्तेमाल करके, यह देखा जा सकता है कि सेटिंग चालू हैं या नहीं:
$ 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
लागू की गई सेटिंग, डिवाइस को रीबूट करने के बाद भी बनी रहती हैं. इसलिए, लेयर लोड होने के बाद, आपको सेटिंग मिटानी पड़ सकती हैं:
$ 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
लेयर को ग्लोबल लेवल पर चालू करना
अगले रीबूट तक, एक या उससे ज़्यादा लेयर को ग्लोबल तौर पर चालू किया जा सकता है. यह सभी ऐप्लिकेशन के लिए लेयर लोड करने की कोशिश करता है. इनमें नेटिव एक्ज़ीक्यूटेबल भी शामिल हैं.
$ adb shell setprop debug.vulkan.layers <layer1:layer2:layerN>