La maggior parte delle API grafiche esplicite non esegue il controllo degli errori perché influiranno sul rendimento. Vulkan ha strati di convalida che forniscono il controllo degli errori durante lo sviluppo, evitando una penalizzazione nel rendimento di release della tua app. I livelli di convalida si basano meccanismo di stratificazione che intercetta i punti di ingresso delle API.
Singolo livello di convalida Khronos
In precedenza, Vulkan forniva più livelli di convalida che dovevano essere abilitati
in un ordine specifico. A partire dalla release 1.1.106.0 dell'SDK Vulkan, la tua app
deve attivare una singola convalida
,
VK_LAYER_KHRONOS_validation
, per ottenere tutte le funzionalità della versione precedente
e gli strati di convalida.
Usa i livelli di convalida pacchettizzati nell'APK
La pacchettizzazione dei livelli di convalida all'interno dell'APK garantisce una compatibilità ottimale. I livelli di convalida sono disponibili come file binari predefiniti o possono essere generati codice sorgente.
Usa programmi binari predefiniti
Scarica i file binari del livello Android Vulkan Validation più recenti da GitHub. pagina di rilascio.
Il modo più semplice per aggiungere livelli all'APK è estrarre il livello predefinito
file binari alla directory src/main/jniLibs/
del tuo modulo, con ABI
(ad esempio arm64-v8a
o x86-64
) inalterate, come segue:
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
Crea il livello di convalida dal codice sorgente
Per eseguire il debug del codice sorgente del livello di convalida, esegui il pull della sorgente più recente dal Gruppo Khronos GitHub repository e segui le istruzioni per la build.
Verificare che il livello di convalida sia pacchettizzato correttamente
Che tu crei con gli strati predefiniti di Khronos o con i livelli creati dall'origine, il processo di compilazione produce una struttura di file finale nell'APK come le seguenti:
lib/ arm64-v8a/ libVkLayer_khronos_validation.so armeabi-v7a/ libVkLayer_khronos_validation.so x86/ libVkLayer_khronos_validation.so x86-64/ libVkLayer_khronos_validation.so
Il seguente comando mostra come verificare che l'APK contenga la convalida livello come previsto:
$ 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
Abilita un livello di convalida durante la creazione dell'istanza
L'API Vulkan consente a un'app di abilitare i livelli durante la creazione dell'istanza. Da principiante i punti intercettati da uno strato devono avere uno dei seguenti oggetti come primo parametro:
VkInstance
VkPhysicalDevice
VkDevice
VkCommandBuffer
VkQueue
Chiama il numero vkEnumerateInstanceLayerProperties()
per elencare i livelli disponibili e le loro proprietà. Vulkan abilita gli strati
vkCreateInstance()
.
Il seguente snippet di codice mostra come un'app può utilizzare l'API Vulkan per esegui query e abilita i livelli in modo programmatico:
// 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 predefinito
Il livello di convalida emette messaggi di avviso ed errore in logcat etichettati con un
Tag VALIDATION
. Un messaggio del livello di convalida ha il seguente aspetto (con
interruzioni di riga aggiunte qui per facilitare lo scorrimento):
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)
Attiva il callback di debug
L'estensione Debug Utils VK_EXT_debug_utils
consente alla tua applicazione di creare un
un messaggero di debug che passa i messaggi del livello di convalida a un'applicazione
di Google. Il tuo dispositivo potrebbe non implementare questa estensione, ma è implementata in
gli strati di convalida più recenti. Esiste anche un'estensione obsoleta chiamata
VK_EXT_debug_report
, che offre funzionalità simili se
VK_EXT_debug_utils
non è disponibile.
Prima di utilizzare l'estensione Debug Utils, assicurati che il tuo dispositivo o un livello di convalida caricato. L'esempio seguente mostra come verifica se l'estensione utils di debug è supportata e registra un callback se l'estensione sia supportata dal livello dispositivo o dal livello di convalida.
// 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); }
Dopo che l'app si è registrata e attiva il callback, il sistema instrada il debug messaggi inviati e ricevuti.
#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; }
Utilizzare livelli di convalida esterni
Non è necessario pacchettizzare i livelli di convalida nell'APK; dispositivi con sistema operativo Android 9 (livello API 28) e versioni successive possono usare livelli di convalida esterni al file binario. e disattivarli e attivarli in modo dinamico. Segui i passaggi di questa sezione per eseguire il push di convalida al dispositivo di test:
Consenti alla tua app di utilizzare livelli di convalida esterni
Il modello e le norme di sicurezza di Android differiscono significativamente da altri piattaforme di terze parti. Per caricare livelli di convalida esterni, viene eseguita una delle seguenti condizioni deve essere vero:
È possibile eseguire il debugg dell'app di destinazione. Questa opzione comporta un maggiore debug informazioni, ma ciò potrebbe influire negativamente sulle prestazioni dell'app.
L'app di destinazione viene eseguita su una build userdebug del sistema operativo che concede l'accesso root.
Solo app che hanno come target Android 11 (livello API 30) o versioni successive: il tuo Android target file manifest include quanto segue: Elemento
meta-data
:<meta-data android:name="com.android.graphics.injectLayers.enable" android:value="true"/>
Carica un livello di convalida esterno
I dispositivi con Android 9 (livello API 28) e versioni successive consentono a Vulkan di caricare dallo spazio di archiviazione locale dell'app. In fase di avvio in Android 10 (livello API 29), Vulkan può anche caricare il livello di convalida da un APK separato. Puoi scegliere il metodo che preferisci a condizione che la tua versione di Android lo supporti.
Carica un file binario del livello di convalida dallo spazio di archiviazione locale del dispositivo
Perché Vulkan cerca il file binario nello spazio di archiviazione temporaneo dei dati del dispositivo. devi prima eseguire il push del file binario in quella directory utilizzando Debug di Android Bridge (adb), come segue:
Utilizza il comando
adb push
per caricare Integra il file binario nello spazio di archiviazione dei dati dell'app sul dispositivo:$ adb push libVkLayer_khronos_validation.so /data/local/tmp
Utilizza
adb shell
erun-as
per caricare il livello tramite il processo dell'app. Vale a dire che il file binario ha lo stesso accesso ai dispositivi dell'app, senza richiedere l'accesso 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
Carica un file binario del livello di convalida da un altro APK
Puoi usare adb
per installare un APK che
contiene il livello e poi attivalo.
adb install --abi abi path_to_apk
Abilita i livelli al di fuori dell'applicazione
Puoi attivare i livelli Vulkan a livello di app o a livello globale. Impostazioni per app permangono nei riavvii, mentre le proprietà globali vengono cancellate riavvio.
Attivare i livelli in base alle singole app
I passaggi seguenti spiegano come attivare i livelli in base alle singole app:
Utilizza le impostazioni della shell adb per abilitare i livelli:
$ adb shell settings put global enable_gpu_debug_layers 1
Specifica l'applicazione di destinazione su cui abilitare i livelli su:
$ adb shell settings put global gpu_debug_app <package_name>
Specifica l'elenco di livelli da attivare (dall'alto verso il basso), separando ciascuno livello dai due punti:
$ adb shell settings put global gpu_debug_layers <layer1:layer2:layerN>
Dato che abbiamo un singolo livello di convalida di Khronos, è probabile che il comando simile al seguente:
$ adb shell settings put global gpu_debug_layers VK_LAYER_KHRONOS_validation
Specifica uno o più pacchetti in cui cercare i livelli all'interno di:
$ adb shell settings put global gpu_debug_layer_app <package1:package2:packageN>
Puoi verificare se le impostazioni sono abilitate utilizzando i seguenti comandi:
$ 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
Poiché le impostazioni che applichi vengono mantenute tra i riavvii del dispositivo, è consigliabile cancella le impostazioni dopo aver caricato i livelli:
$ 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
Abilita i livelli a livello globale
Puoi abilitare uno o più livelli a livello globale fino al prossimo riavvio. Questo tenta di caricare i livelli per tutte le applicazioni, incluse quelle native eseguibili.
$ adb shell setprop debug.vulkan.layers <layer1:layer2:layerN>