API กราฟิกที่โจ่งแจ้งส่วนใหญ่จะไม่ทำการตรวจสอบข้อผิดพลาดเนื่องจาก ส่งผลให้มีบทลงโทษด้านประสิทธิภาพ Vulkan มีเลเยอร์การตรวจสอบที่ให้ ในระหว่างการพัฒนา หลีกเลี่ยงบทลงโทษด้านประสิทธิภาพใน เปิดตัวบิลด์ของแอปคุณ เลเยอร์การตรวจสอบความถูกต้องจะขึ้นอยู่กับจุดประสงค์ทั่วไป เลเยอร์กลไกที่ดักจับจุดเข้าถึง API
ชั้นการตรวจสอบ Khronos เดียว
ก่อนหน้านี้ Vulkan มีเลเยอร์การตรวจสอบความถูกต้องหลายชั้นที่จำเป็นต้องเปิดใช้
ตามลำดับที่กำหนด เริ่มตั้งแต่รุ่น 1.1.106.0 Vulkan SDK แอปของคุณ
ต้องเปิดใช้การตรวจสอบความถูกต้องเพียงครั้งเดียว
เลเยอร์
VK_LAYER_KHRONOS_validation
หากต้องการใช้ฟีเจอร์ทั้งหมดจาก
เลเยอร์การตรวจสอบความถูกต้อง
ใช้เลเยอร์การตรวจสอบความถูกต้องแพ็กเกจใน APK
เลเยอร์การตรวจสอบแพ็กเกจภายใน APK ช่วยให้มั่นใจได้ว่ามีความเข้ากันได้อย่างเหมาะสม เลเยอร์การตรวจสอบความถูกต้องพร้อมใช้งานเป็นไบนารีที่สร้างไว้ล่วงหน้าหรือสร้างได้จาก ซอร์สโค้ด
ใช้ไบนารีที่สร้างไว้ล่วงหน้า
ดาวน์โหลดไบนารีของเลเยอร์การตรวจสอบความถูกต้องของ Android Vulkan ล่าสุดจาก GitHub หน้าเผยแพร่
วิธีที่ง่ายที่สุดในการเพิ่มเลเยอร์ลงใน 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)
เปิดใช้ Callback ของการแก้ไขข้อบกพร่อง
ส่วนขยาย Debug Utils VK_EXT_debug_utils
ช่วยให้แอปพลิเคชันสร้าง
โปรแกรมรับส่งข้อความแก้ไขข้อบกพร่องที่ส่งข้อความเลเยอร์การตรวจสอบความถูกต้องไปยังแอปพลิเคชัน
Callback อุปกรณ์อาจไม่ติดตั้งส่วนขยายนี้ แต่มีการติดตั้งใช้งานใน
เลเยอร์การตรวจสอบความถูกต้องล่าสุด นอกจากนี้ยังมีส่วนขยายที่เลิกใช้งานแล้วชื่อ
VK_EXT_debug_report
ซึ่งมีความสามารถที่คล้ายกัน
VK_EXT_debug_utils
ไม่พร้อมใช้งาน
ก่อนที่จะใช้ส่วนขยายแก้ไขข้อบกพร่อง คุณควรตรวจสอบว่าอุปกรณ์ของคุณ หรือเลเยอร์การตรวจสอบความถูกต้องที่โหลดไว้รองรับ ตัวอย่างต่อไปนี้จะแสดงวิธีการ ตรวจสอบว่าระบบรองรับส่วนขยายการแก้ไขข้อบกพร่องหรือไม่ และลงทะเบียน Callback อุปกรณ์หรือชั้นการตรวจสอบความถูกต้องรองรับส่วนขยายดังกล่าว
// 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); }
หลังจากที่แอปลงทะเบียนและเปิดใช้ Callback แล้ว ระบบจะกำหนดเส้นทางการแก้ไขข้อบกพร่อง ข้อความไปที่ข้อความนี้
#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 (API ระดับ 28) ขึ้นไปสามารถใช้เลเยอร์การตรวจสอบภายนอกกับไบนารีของคุณ แล้วปิดและเปิดแบบไดนามิก ทำตามขั้นตอนในส่วนนี้เพื่อพุช เลเยอร์การตรวจสอบความถูกต้องลงในอุปกรณ์ทดสอบ
เปิดให้แอปใช้เลเยอร์การตรวจสอบภายนอก
รูปแบบและนโยบายของ Android แตกต่างจาก ใหม่ หากต้องการโหลดเลเยอร์การตรวจสอบภายนอก เงื่อนไขใดเงื่อนไขหนึ่งต่อไปนี้ ต้องเป็นจริง
แอปเป้าหมายแก้ไขข้อบกพร่องได้ ตัวเลือกนี้จะทำให้แก้ไขข้อบกพร่องได้มากขึ้น แต่อาจส่งผลกระทบทางลบต่อประสิทธิภาพของแอป
แอปเป้าหมายเรียกใช้ในบิลด์ userdebug ของระบบปฏิบัติการที่ ให้สิทธิ์การเข้าถึงราก
แอปที่กำหนดเป้าหมายเป็น Android 11 (API ระดับ 30) ขึ้นไปเท่านั้น: Android เป้าหมายของคุณ ไฟล์ Manifest ประกอบด้วยข้อมูลต่อไปนี้ องค์ประกอบ
meta-data
:<meta-data android:name="com.android.graphics.injectLayers.enable" android:value="true"/>
โหลดเลเยอร์การตรวจสอบภายนอก
อุปกรณ์ที่ใช้ Android 9 (API ระดับ 28) ขึ้นไปอนุญาตให้ Vulkan โหลด เลเยอร์การตรวจสอบความถูกต้องจากพื้นที่เก็บข้อมูลในเครื่องของแอป เริ่มต้น ใน Android 10 (API ระดับ 29) Vulkan ยังสามารถโหลดเลเยอร์การตรวจสอบจาก APK แยกต่างหาก คุณเลือกวิธีการใดก็ได้ที่ต้องการ ตราบใดที่เวอร์ชัน Android ของคุณรองรับ
โหลดไบนารีของเลเยอร์การตรวจสอบจากพื้นที่เก็บข้อมูลในเครื่องของอุปกรณ์
เนื่องจาก Vulkan จะมองหาไบนารีในพื้นที่เก็บข้อมูลชั่วคราวของอุปกรณ์ คุณต้องพุชไบนารีไปยังไดเรกทอรีนั้นโดยใช้การแก้ไขข้อบกพร่องของ Android ก่อน ตัวเชื่อม (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 อื่น
คุณสามารถใช้ adb
เพื่อติดตั้ง APK ที่
มีเลเยอร์นั้นอยู่ แล้วเปิดใช้เลเยอร์
adb install --abi abi path_to_apk
เปิดใช้เลเยอร์นอกแอปพลิเคชัน
คุณสามารถเปิดใช้เลเยอร์ Vulkan ต่อแอปหรือทั่วโลกได้ การตั้งค่าของแต่ละแอป คงอยู่ในการรีบูต ขณะที่พร็อพเพอร์ตี้ส่วนกลางมีการล้าง รีบูต
เปิดใช้เลเยอร์ในแอปแต่ละรายการ
ขั้นตอนต่อไปนี้อธิบายวิธีเปิดใช้เลเยอร์ในแต่ละแอป
ใช้การตั้งค่าเชลล์ adb เพื่อเปิดใช้เลเยอร์:
$ 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
เปิดใช้เลเยอร์ทั่วโลก
คุณสามารถเปิดใช้เลเยอร์อย่างน้อย 1 เลเยอร์ได้ทั่วโลกจนกว่าจะมีการรีบูตครั้งถัดไป การดำเนินการนี้เป็นการพยายามโหลดเลเยอร์สำหรับแอปพลิเคชันทั้งหมดรวมถึงแอปที่มาพร้อมเครื่อง ไฟล์ปฏิบัติการ
$ adb shell setprop debug.vulkan.layers <layer1:layer2:layerN>