Die meisten expliziten Grafik-APIs führen keine Fehlerprüfung durch, da dies Leistungseinbußen nach sich ziehen. Vulkan bietet Validierungsebenen, Fehlerprüfung während der Entwicklung, um Leistungseinbußen im veröffentlichen Sie den Build Ihrer App. Validierungsebenen basieren auf einem allgemeinen Zweck Schichtmechanismus, der API-Einstiegspunkte abfängt.
Einzelne Khronos-Validierungsebene
Bisher gab es bei Vulkan mehrere Validierungsebenen, die aktiviert werden mussten
und zwar in einer bestimmten Reihenfolge. Ab dem Vulkan SDK-Release 1.1.106.0 wird deine App
nur eine einzelne Überprüfung
Ebene
VK_LAYER_KHRONOS_validation
, um alle Funktionen der vorherigen Version zu nutzen
Validierungsschichten.
Im APK gepackte Validierungsebenen verwenden
Durch das Packen der Validierungsebenen in deinem APK wird eine optimale Kompatibilität sichergestellt. Die Validierungsebenen sind als vorgefertigte Binärdateien verfügbar oder können aus Quellcode verfügbar.
Vordefinierte Binärprogramme verwenden
Laden Sie die neuesten Binärdateien der Android Vulkan Validation-Ebene von GitHub herunter Veröffentlichungsseite
Am einfachsten können Sie die Ebenen zu Ihrem APK hinzufügen, indem Sie die vordefinierte Ebene extrahieren.
in das Verzeichnis src/main/jniLibs/
deines Moduls mit ABI
(z. B. arm64-v8a
oder x86-64
) intakt:
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
Validierungsebene aus Quellcode erstellen
Rufen Sie für das Debuggen im Quellcode der Validierungsebene die neueste Quelle aus dem Khronos Group GitHub Repository und folgen Sie die Build-Anleitung dort.
Prüfen, ob die Validierungsschicht richtig gepackt ist
Dabei spielt es keine Rolle, ob Sie die vorgefertigten Khronos-Ebenen oder die aus der Quelle erzeugt der Build-Prozess eine endgültige Dateistruktur in Ihrem APK, z. B. Folgendes:
lib/ arm64-v8a/ libVkLayer_khronos_validation.so armeabi-v7a/ libVkLayer_khronos_validation.so x86/ libVkLayer_khronos_validation.so x86-64/ libVkLayer_khronos_validation.so
Mit dem folgenden Befehl kannst du prüfen, ob dein APK die Validierung enthält erwartungsgemäß ein:
$ 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
Validierungsebene während der Instanzerstellung aktivieren
Mit der Vulkan API kann eine App bei der Instanzerstellung Ebenen aktivieren. Eintrag Punkte, die eine Ebene abfängt, muss eines der folgenden Objekte erster Parameter:
VkInstance
VkPhysicalDevice
VkDevice
VkCommandBuffer
VkQueue
vkEnumerateInstanceLayerProperties()
anrufen
um die verfügbaren Ebenen und ihre Eigenschaften aufzulisten. Vulkan aktiviert Ebenen, wenn
vkCreateInstance()
ausgeführt wird.
Das folgende Code-Snippet zeigt, wie eine App die Vulkan API für Folgendes verwenden kann: Ebenen programmatisch abfragen und aktivieren:
// 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,
Standard-Logcat-Ausgabe
Die Validierungsebene gibt im Logcat Warn- und Fehlermeldungen aus, die mit einem
VALIDATION
-Tag. Eine Validierungsschichtnachricht sieht wie folgt aus (mit
zum leichteren Scrollen:
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-Callback aktivieren
Mit der Debug Utils-Erweiterung VK_EXT_debug_utils
kann Ihre Anwendung ein
Debug Messenger, der Validierungsebenen-Nachrichten an eine von der Anwendung bereitgestellte Anwendung übergibt,
Callback des Nutzers an. Ihr Gerät implementiert diese Erweiterung möglicherweise nicht, sie ist jedoch in
der neuesten Validierungsschichten. Es gibt auch eine nicht mehr unterstützte Erweiterung namens
VK_EXT_debug_report
, die ähnliche Funktionen bietet, wenn
VK_EXT_debug_utils
ist nicht verfügbar.
Bevor Sie die Erweiterung „Debug Utils“ verwenden, sollten Sie sich vergewissern, oder eine geladene Validierungsschicht unterstützt. Das folgende Beispiel zeigt, wie Sie prüfen Sie, ob die Erweiterung „Debug utils“ unterstützt wird, und registrieren Sie einen Rückruf, wenn die Erweiterung von der Geräte- oder der Validierungsebene unterstützt wird.
// 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); }
Nachdem Ihre App den Callback registriert und aktiviert hat, leitet das System das Debugging weiter. Nachrichten an sie senden.
#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; }
Externe Validierungsebenen verwenden
Sie müssen Validierungsebenen nicht in Ihr APK packen. Geräte mit Android 9 (API-Level 28) und höher können Validierungsebenen außerhalb deines Binärprogramms verwenden dynamisch ein- und ausschalten. Befolgen Sie die Schritte in diesem Abschnitt, Validierungsschichten an Ihr Testgerät an:
Anwendung für die Verwendung externer Validierungsebenen aktivieren
Das Sicherheitsmodell und die Richtlinien von Android unterscheiden sich erheblich von anderen Plattformen. Zum Laden externer Validierungsebenen muss eine der folgenden Bedingungen erfüllt sein muss wahr sein:
Die Ziel-App kann fehlerbereinigt werden. Diese Option führt zu mehr Fehlerbehebung Informationen, die sich jedoch negativ auf die Leistung Ihrer App auswirken können.
Die Ziel-App wird in einem userdebug-Build des Betriebssystems ausgeführt, gewährt Root-Zugriff.
Apps, die nur auf Android 11 (API-Level 30) oder höher ausgerichtet sind: Ihr Ziel-Android Manifestdatei enthält Folgendes: Element
meta-data
:<meta-data android:name="com.android.graphics.injectLayers.enable" android:value="true"/>
Externe Validierungsebene laden
Auf Geräten mit Android 9 (API-Level 28) und höher kann Vulkan die Validierungsebene aus dem lokalen Speicher der App abgerufen werden. Wird gestartet In Android 10 (API-Level 29) kann Vulkan die Validierungsebene auch über eine separate APK-Datei. Sie können eine beliebige Methode wählen sofern deine Android-Version dies unterstützt.
Eine Validierungsschicht-Binärdatei aus dem lokalen Speicher des Geräts laden
Weil Vulkan im temporären Datenspeicher deines Geräts nach dem Binärprogramm sucht -Verzeichnis ist, müssen Sie die Binärdatei zuerst mithilfe von Android Debug Bridge (adb):
Verwenden Sie den Befehl
adb push
, um die Binärprogramm in den Datenspeicher Ihrer App auf dem Gerät einfügen:$ adb push libVkLayer_khronos_validation.so /data/local/tmp
adb shell
undrun-as
verwenden um den Layer über Ihren App-Prozess zu laden. Das heißt, das Binärprogramm denselben Gerätezugriff wie die App hat, ohne dass ein Root-Zugriff erforderlich ist.$ 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
Validierungsschicht-Binärprogramm aus einem anderen APK laden
Du kannst adb
verwenden, um ein APK zu installieren, das
enthält die Ebene und aktiviert dann die Ebene.
adb install --abi abi path_to_apk
Ebenen außerhalb der Anwendung aktivieren
Du kannst Vulkan-Ebenen entweder für einzelne Apps oder global aktivieren. App-spezifische Einstellungen bleibt nach Neustarts bestehen, während globale Properties gelöscht am neu starten.
Ebenen für einzelne Apps aktivieren
In den folgenden Schritten wird beschrieben, wie Ebenen für einzelne Apps aktiviert werden:
Verwenden Sie die ADB-Shell-Einstellungen, um die Ebenen zu aktivieren:
$ adb shell settings put global enable_gpu_debug_layers 1
Geben Sie die Zielanwendung an, in der die Ebenen aktiviert werden sollen:
$ adb shell settings put global gpu_debug_app <package_name>
Liste der zu aktivierenden Ebenen angeben (von oben nach unten) mit Trennung der einzelnen Ebenen mit einem Doppelpunkt verbinden:
$ adb shell settings put global gpu_debug_layers <layer1:layer2:layerN>
Da wir nur eine einzige Khronos-Validierungsschicht haben, Beispiel:
$ adb shell settings put global gpu_debug_layers VK_LAYER_KHRONOS_validation
Geben Sie ein oder mehrere Pakete an, in denen nach Ebenen gesucht werden soll:
$ adb shell settings put global gpu_debug_layer_app <package1:package2:packageN>
Mit den folgenden Befehlen können Sie prüfen, ob die Einstellungen aktiviert sind:
$ 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
Da die angewendeten Einstellungen bei jedem Neustart des Geräts bestehen bleiben, sollten Sie Einstellungen nach dem Laden der Ebenen löschen:
$ 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
Ebenen global aktivieren
Sie können eine oder mehrere Ebenen bis zum nächsten Neustart global aktivieren. Dadurch wird versucht, die Layer für alle Anwendungen zu laden, einschließlich nativer ausführbare Dateien.
$ adb shell setprop debug.vulkan.layers <layer1:layer2:layerN>