Android'de Vulkan doğrulama katmanları

Uygunsuz grafik API'lerinin çoğu hata kontrolü yapmaz çünkü bunu yapmak performans cezasına neden olabilir. Vulkan, sağladığı doğrulama katmanlarına sahiptir. sırasında hatalardan kaçınmak için geliştirme sırasında en iyi uygulamaları paylaşacağız. Doğrulama katmanları genel bir amaca hizmet eder müdahale eden bir katman oluşturma mekanizmasıdır.

Tek Khronos doğrulama katmanı

Vulkan daha önce etkinleştirilmesi gereken birden fazla doğrulama katmanı sağlıyordu. seçim yapar. Uygulamanız, 1.1.106.0 Vulkan SDK'sı sürümünden itibaren yalnızca tek bir doğrulama katman, VK_LAYER_KHRONOS_validation (önceki sürümdeki tüm özellikleri kullanın) doğrulama katmanlarını kullanabilirsiniz.

APK'nızda paketlenen doğrulama katmanlarını kullanın

APK'nızın içinde doğrulama katmanlarını paketlemek, optimum uyumluluk sağlar. Doğrulama katmanları, önceden oluşturulmuş ikili programlar olarak kullanılabilir veya kaynak kodudur.

Önceden oluşturulmuş ikili programları kullanın

GitHub'dan en yeni Android Vulkan Doğrulama katmanı ikili programlarını indirin sürüm sayfasını ziyaret edin.

Katmanları APK'nıza eklemenin en kolay yolu, önceden oluşturulmuş katmanı çıkarmaktır ABI ile modülünüzün src/main/jniLibs/ dizini için ikili programlar dizinleri (arm64-v8a veya x86-64 gibi) olduğu gibi.

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

Kaynak koddan doğrulama katmanı oluşturma

Doğrulama katmanı kaynak kodunda hata ayıklamak için Khronos Grubu GitHub repository'ler (repository) burada bulabilirsiniz.

Doğrulama katmanının doğru şekilde paketlendiğini doğrulama

Yapıları Khronos önceden oluşturulmuş katman veya katmanlarla yapıp yapmamanızdan bağımsız olarak derleme işlemi, APK'nızda aşağıdaki gibi nihai bir dosya yapısı oluşturur: şu:

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 komut, APK'nızın doğrulama içerdiğini nasıl doğrulayacağınızı gösterir katmanını beklendiği gibi ekleyin:

$ 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, uygulamaların örnek oluşturma sırasında katmanları etkinleştirmesine olanak tanır. Giriş kesişim noktasının aşağıdaki nesnelerden birine sahip olması gerektiğini ilk parametre:

  • VkInstance
  • VkPhysicalDevice
  • VkDevice
  • VkCommandBuffer
  • VkQueue

vkEnumerateInstanceLayerProperties() numaralı telefonu arayın kullanılabilir katmanları ve özelliklerini listeleyin. Vulkan, aşağıdaki durumlarda katmanları etkinleştirir vkCreateInstance() yürütülür.

Aşağıdaki kod snippet'i, bir uygulamanın aşağıdaki işlemler için Vulkan API'sini nasıl kullanabileceğini gösterir: katmanları programlı olarak sorgulayın ve etkinleştirin:

// 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 etiketi. Doğrulama katmanı mesajı aşağıdaki gibi görünür ( daha kolay kaydırma için buraya satır sonu eklendi):

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ğırmasını etkinleştir

Debug Utils uzantısı VK_EXT_debug_utils, uygulamanızın doğrulama katmanı mesajlarını uygulama tarafından sağlanan bir iletiye geçiren hata ayıklama mesajı geri arama. Cihazınız bu uzantıyı uygulamayabilir, ancak uzantı şurada uygulanmıştır: son doğrulama katmanlarını kullanın. Ayrıca, VK_EXT_debug_report, aşağıdaki durumlarda benzer özellikler sağlar: VK_EXT_debug_utils kullanılamıyor.

Debug Utils uzantısını kullanmadan önce cihazınızın yüklenen bir doğrulama katmanı tarafından desteklendiğinden emin olun. Aşağıdaki örnekte, hata ayıklama yardımcı programı uzantısının desteklenip desteklenmediğini kontrol edin ve Uzantı, cihaz veya doğrulama katmanı tarafından destekleniyor.

// 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 kaydedilip geri çağırmayı etkinleştirdikten sonra sistem, hata ayıklamayı yönlendirir iletilerse buna benzer.

#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

APK'nızda doğrulama katmanlarını paketlemeniz gerekmez; Android çalıştıran cihazlar 9 (API düzeyi 28) ve sonraki sürümler, ikili programınızın dışındaki doğrulama katmanlarını kullanabilir ve dinamik olarak kapatıp açın. Verileri aktarmak için bu bölümdeki adımları doğrulama katmanlarını test cihazınıza yükleyin:

Uygulamanızın harici doğrulama katmanlarını kullanmasına izin verin

Android'in güvenlik modeli ve politikaları, diğer modellerden önemli ölçüde farklıdır platformlar. Harici doğrulama katmanlarını yüklemek için aşağıdaki koşullardan biri doğru olmalıdır:

  • Hedef uygulama hata ayıklanabilir. Bu seçenek, daha fazla hata ayıklamaya olanak tanır uygulamanızın performansını olumsuz yönde etkileyebilir.

  • Hedef uygulama, kullanıcı hata ayıklama derlemesinde kök erişimi verir.

  • Yalnızca Android 11 (API düzeyi 30) veya sonraki sürümleri hedefleyen uygulamalar: Hedefiniz Android manifesto dosyasında şunlar yer alır: meta-data öğesi:

    <meta-data android:name="com.android.graphics.injectLayers.enable"
      android:value="true"/>
    

Harici doğrulama katmanı yükleme

Android 9 (API düzeyi 28) ve sonraki sürümleri çalıştıran cihazlar, Vulkan'ın doğrulama katmanını kullanabilirsiniz. Başlangıç Vulkan, doğrulama katmanını bir API düzeyi 29'dan da ayrı APK oluşturun. İstediğiniz yöntemi seçebilirsiniz Android sürümünüz destekliyorsa.

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 alanında ikili programı aradığı için dizine aktarmak için önce Android Hata Ayıklama'yı kullanarak ikili programı Köprü (adb), aşağıdaki gibidir:

  1. Şunu yüklemek için adb push komutunu kullanın: uygulamanızın cihazdaki veri depolama alanına ikili program katmanından yararlanın:

    $ adb push libVkLayer_khronos_validation.so /data/local/tmp
    
  2. adb shell ve run-as kullanın komutlarını kullanabilirsiniz. Yani ikili programdaki uygulamanın, root erişimi gerektirmeden sahip olduğu cihaz erişimine sahip olması gerekir.

    $ 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
    
  3. Katmanı etkinleştirin.

Başka bir APK'dan doğrulama katmanı ikili programı yükle

Şu özelliklere sahip bir APK yüklemek için adb uygulamasını kullanabilirsiniz: katmanı ekleyin ve ardından katmanı etkinleştirin.

adb install --abi abi path_to_apk

Uygulamanın dışındaki katmanları etkinleştir

Vulkan katmanlarını uygulama başına veya genel olarak etkinleştirebilirsiniz. Uygulamaya özgü ayarlar Global özellikler açık olduğunda temizlenir, ancak yeniden başlatmalar sırasında kalır tekrar başlat.

Katmanları uygulama bazında etkinleştirme

Aşağıdaki adımlarda, katmanların uygulama bazında nasıl etkinleştirileceği açıklanmaktadır:

  1. Katmanları etkinleştirmek için adb kabuk ayarlarını kullanın:

    $ adb shell settings put global enable_gpu_debug_layers 1
    
  2. Katmanların etkinleştirileceği hedef uygulamayı belirtin:

    $ adb shell settings put global gpu_debug_app <package_name>
    
  3. Etkinleştirilecek katmanların listesini, yukarıdan aşağıya doğru her birini ayırarak belirtin iki nokta üst üste ekleyin:

    $ adb shell settings put global gpu_debug_layers <layer1:layer2:layerN>
    

    Tek bir Khronos doğrulama katmanımız olduğu için, komut muhtemelen gibi görünür:

    $ adb shell settings put global gpu_debug_layers VK_LAYER_KHRONOS_validation
    
  4. 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>
    
ziyaret edin.

Ayarların etkinleştirilip etkinleştirilmediğini aşağıdaki komutları kullanarak 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şlatma işlemlerinde de kalıcı olduğundan, katmanlar yüklendikten sonra ayarları temizleyin:

$ 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ı genel olarak etkinleştir

Bir veya daha fazla katmanı bir sonraki yeniden başlatmaya kadar genel olarak etkinleştirebilirsiniz. Bu işlem, yerel uygulamalar da dahil olmak üzere tüm uygulamaların katmanlarını yüklemeye çalışır. yürütülebilir dosyalar.

$ adb shell setprop debug.vulkan.layers <layer1:layer2:layerN>