diff --git a/docs/html/ndk/guides/graphics/validation-layer.jd b/docs/html/ndk/guides/graphics/validation-layer.jd new file mode 100644 index 0000000000000..3a61f1847bbb8 --- /dev/null +++ b/docs/html/ndk/guides/graphics/validation-layer.jd @@ -0,0 +1,603 @@ +page.title=Vulkan Validation Layers on Android +@jd:body + +
+Most explicit graphics APIs do not perform error-checking, because doing so can result in a +performance penalty. Vulkan provides error-checking in a manner that lets you use this feature at +development time, but exclude it from the release build of your app, thus avoiding the penalty when +it matters most. You do this by enabling validation layers. Validation layers intercept +or hook Vulkan entry points for various debug and validation purposes. +
+ ++Each validation layer can contain definitions for one or more of these entry points, and +intercepts the entry points for which it contains definitions. When a validation +layer does not define an entry point, the system passes the entry point on to the next +layer. Ultimately, an entry point not defined in any layer reaches the driver, the +base level, unvalidated. +
+ ++The Android SDK, NDK, and Vulkan samples include Vulkan validation layers for +use during development. You can hook these validation layers into the graphics stack, allowing +them to report validation issues. This instrumentation allows you to catch and fix misuses +during development. +
+ ++This page explains how to: +
+This section explains how to build layers from source. +If you have precompiled layers, you can skip this section, and instead read about how to +install your layers using Android Studio or from the +command line. +
++NDK Revision 12 and later contains source +code for Android validation layers that is known-good, and ready to build. This code resides under +the {@code <ndk-root>/sources/third_party/vulkan/src/build-android/generated/gradle-build} +directory. This version of the layers should be sufficient for most needs. If so, your next task is +to build them. Alternatively, you can pull source code from the +Khronos Group repository. + +
+ ++Although we recommend that you use the source code provided with the NDK, you can also pull more +recent versions of the source code directly from the + +GitHub repository belonging to the Khronos Group. To do so, perform the following steps. +
+ ++$ git clone git@github.com:KhronosGroup/Vulkan-LoaderAndValidationLayers.git ++ +
Note: You must have a private SSH key associated with +GitHub, or this command fails with a {@code Permission denied (publickey)} message.
++$ cd Vulkan-LoaderAndValidationLayers +$ git checkout android_layers ++
+Android Studio builds the validation layers when it builds the rest of the app. +This flow makes it easier for you to trace through the layers at runtime. Each layer's +source code corresponds to a single Gradle project, which you can specify directly in your Android +Studio app. For example, there is a {@code build.gradle} project for threading, and another +one for parameter validation. +
+ ++To integrate layers directory into Android Studio application, perform these steps: +
+
+// configure your path to the source code generated on your machine
+def layerProjRoot = file('/path/to/ndk-root/.../build-android/generated/gradle-build')
+String[] layers = ['threading',
+ 'parameter_validation',
+ 'object_tracker',
+ 'core_validation',
+ 'device_limits',
+ 'image',
+ 'swapchain',
+ 'unique_objects']
+for (layer in layers) {
+ include ":"+ layer
+ project(":" + layer.toString()).projectDir = new File("${layerProjRoot}/${layer}")
+}
+
+
+android.sources {
+ main {
+ jni { ... }
+ jniLibs {
+ dependencies {
+ project ":threading"
+ project ":parameter_validation"
+ project ":object_tracker"
+ project ":core_validation"
+ project ":device_limits"
+ project ":image"
+ project ":swapchain"
+ project ":unique_objects"
+ }
+ }
+ }
+} // android.sources
+
++To build validation layers on Linux or OS X, enter these commands on the command line: +
++$ cd build-android +$ ./android-generate +$ cd generated/gradle-build +$ # configure SDK and NDK path in local.properties +$ gradlew assembleAllDebug ++
+$ cd build-android +$ ./android-generate +$ ndk-build ++
+To build validation layers on Windows, enter these commands on the command line: +
++> cd build-android +> android-generate.bat +> cd generated\gradle-build +> REM configure SDK and NDK path in local.properties +> gradlew.bat assembleAllDebug ++
+> cd build-android +> android-generate.bat +> ndk-build.cmd ++
+After building the layers, you must provide them to your app. To do so, you must first +create a {@code jniLibs} folder in your app's project directory under +{@code ./src/main/}, and copy the libs to it. The following example shows how to do this. +
+ ++$ mkdir ./src/main/jniLibs ++ +
+The next step depends on whether you are using Gradle or Android makefiles. If you're using +Gradle, each built layer resides in its own directory. Consolidate the layers into a single +directory, as the following example shows: +
+ ++$ cp -r .../build-android/generated/gradle-build/threading/build/outputs/native/debug/all/lib/* ./src/main/jniLibs/ +$ cp -r .../build-android/generated/gradle-build/parameter_validation/build/outputs/native/debug/all/lib/* ./src/main/jniLibs/ +$ cp -r .../build-android/generated/gradle-build/object_tracker/build/outputs/native/debug/all/lib/* ./src/main/jniLibs/ +$ cp -r .../build-android/generated/gradle-build/core_validation/build/outputs/native/debug/all/lib/* ./src/main/jniLibs/ +$ cp -r .../build-android/generated/gradle-build/device_limits/build/outputs/native/debug/all/lib/* ./src/main/jniLibs/ +$ cp -r .../build-android/generated/gradle-build/image/build/outputs/native/debug/all/lib/* ./src/main/jniLibs/ +$ cp -r .../build-android/generated/gradle-build/swapchain/build/outputs/native/debug/all/lib/* ./src/main/jniLibs/ +$ cp -r .../build-android/generated/gradle-build/unique_objects/build/outputs/native/debug/all/lib/* ./src/main/jniLibs/ ++ +If you're using Android makefiles, the built layers reside in {@code lib} folders, +with one {@code lib} folder under each architecture’s root directory. Consolidate the +makefiles under the {@code jniLibs} directory as this example shows: + +
+$ cp -r .../build-android/libs/* ./src/main/jniLibs/ ++ + + +
+Regardless of whether you build using Gradle or Android makefiles, the build process produces +a file structure like the following: +
+ ++src/main/jniLibs/ + arm64-v8a/ + libVkLayer_core_validation.so + libVkLayer_device_limits.so + libVkLayer_image.so + libVkLayer_object_tracker.so + libVkLayer_parameter_validation.so + libVkLayer_swapchain.so + libVkLayer_threading.so + libVkLayer_unique_objects.so + armeabi-v7a/ + libVkLayer_core_validation.so + ... ++ +
+The following example shows how to verify that your APK contains the validation layers +as expected: +
+ ++$ jar -xvf project.apk + ... + inflated: lib/arm64-v8a/libVkLayer_threading.so + inflated: lib/arm64-v8a/libVkLayer_object_tracker.so + inflated: lib/arm64-v8a/libVkLayer_swapchain.so + inflated: lib/arm64-v8a/libVkLayer_unique_objects.so + inflated: lib/arm64-v8a/libVkLayer_parameter_validation.so + inflated: lib/arm64-v8a/libVkLayer_image.so + inflated: lib/arm64-v8a/libVkLayer_core_validation.so + inflated: lib/arm64-v8a/libVkLayer_device_limits.so + ... ++ + +
The Vulkan API allows an app to enable both instance layers and device layers.
+ ++A layer that can intercept Vulkan instance-level entry points is called an instance layer. +Instance-level entry points are those with {@code VkInstance} or {@code VkPhysicalDevice} +as the first parameter. +
+ ++You can call {@code vkEnumerateInstanceLayerProperties()} to list the available instance layers +and their properties. The system enables instance layers when {@code vkCreateInstace()} executes. +
+ ++The following code snippet shows how an app can use the Vulkan API to programmatically enable and +query an instance layer: +
+ +
+// Get instance layer count using null pointer as last parameter
+uint32_t instance_layer_present_count = 0;
+vkEnumerateInstanceLayerProperties(&instance_layer_present_count, nullptr);
+
+// Enumerate instance layers with valid pointer in last parameter
+VkLayerProperties* layer_props =
+ (VkLayerProperties*)malloc(instance_layer_present_count * sizeof(VkLayerProperties));
+vkEnumerateInstanceLayerProperties(&instance_layer_present_count, layer_props));
+
+// Make sure the desired instance validation layers are available
+// NOTE: These are not listed in an arbitrary order. Threading must be
+// first, and unique_objects must be last. This is the order they
+// will be inserted by the loader.
+const char *instance_layers[] = {
+ "VK_LAYER_GOOGLE_threading",
+ "VK_LAYER_LUNARG_parameter_validation",
+ "VK_LAYER_LUNARG_object_tracker",
+ "VK_LAYER_LUNARG_core_validation",
+ "VK_LAYER_LUNARG_device_limits",
+ "VK_LAYER_LUNARG_image",
+ "VK_LAYER_LUNARG_swapchain",
+ "VK_LAYER_GOOGLE_unique_objects"
+};
+
+uint32_t instance_layer_request_count =
+ sizeof(instance_layers) / sizeof(instance_layers[0]);
+for (uint32_t i = 0; i < instance_layer_request_count; i++) {
+ bool found = false;
+ for (uint32_t j = 0; j < instance_layer_present_count; j++) {
+ if (strcmp(instance_layers[i], layer_props[j].layerName) == 0) {
+ found = true;
+ }
+ }
+ if (!found) {
+ error();
+ }
+}
+
+// Pass desired instance layers into vkCreateInstance
+VkInstanceCreateInfo instance_info = {};
+instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+instance_info.enabledLayerCount = instance_layer_request_count;
+instance_info.ppEnabledLayerNames = instance_layers;
+...
+
+
++A layer that can intercept device-level entry points is called a device layer. Device-level entry +points are those whose first parameter is {@code VkDevice}, {@code VkCommandBuffer}, +or {@code VkQueue}. The list of +device layers to enable is included in the {@code ppEnabledLayerNames} field of the +{@code VkDeviceCreateInfo} +struct that the app passes into {@code vkCreateDevice()}. +
+ ++You can call {@code vkEnumerateDeviceLayerProperties} to list the available layers +and their properties. The system enables device layers when it calls {@code vkCreateDevice()}. +
+ ++The following code snippet shows how an app can use the Vulkan API to programmatically enable a +device layer. +
+ +
+
+// Get device layer count using null as last parameter
+uint32_t device_layer_present_count = 0;
+vkEnumerateDeviceLayerProperties(&device_layer_present_count, nullptr);
+
+// Enumerate device layers with valid pointer in last parameter
+VkLayerProperties* layer_props =
+ (VkLayerProperties *)malloc(device_layer_present_count * sizeof(VkLayerProperties));
+vkEnumerateDeviceLayerProperties(physical_device, device_layer_present_count, layer_props));
+
+// Make sure the desired device validation layers are available
+// Ensure threading is first and unique_objects is last!
+const char *device_layers[] = {
+ "VK_LAYER_GOOGLE_threading",
+ "VK_LAYER_LUNARG_parameter_validation",
+ "VK_LAYER_LUNARG_object_tracker",
+ "VK_LAYER_LUNARG_core_validation",
+ "VK_LAYER_LUNARG_device_limits",
+ "VK_LAYER_LUNARG_image",
+ "VK_LAYER_LUNARG_swapchain",
+ "VK_LAYER_GOOGLE_unique_objects"
+};
+
+uint32_t device_layer_request_count =
+ sizeof(device_layers) / sizeof(device_layers[0]);
+for (uint32_t i = 0; i < device_layer_request_count; i++) {
+ bool found = false;
+ for (uint32_t j = 0; j < device_layer_present_count; j++) {
+ if (strcmp(device_layers[i],
+ layer_props[j].layerName) == 0) {
+ found = true;
+ }
+ }
+ if (!found) {
+ error();
+ }
+}
+
+// Pass desired device layers into vkCreateDevice
+VkDeviceCreateInfo device_info = {};
+device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+device_info.enabledLayerCount = device_layer_request_count;
+device_info.ppEnabledLayerNames = device_layers;
+...
+
+
++The Debug Report extension {@code VK_EXT_debug_report} allows your application to control +layer behavior when an event occurs.
+ ++Before using this extension, you must first make sure that the platform supports it. +The following example shows how to check for debug extension support and +register a callback if the extension is supported. +
+ +
+// Get the instance extension count
+uint32_t inst_ext_count = 0;
+vkEnumerateInstanceExtensionProperties(nullptr, &inst_ext_count, nullptr);
+
+// Enumerate the instance extensions
+VkExtensionProperties* inst_exts =
+ (VkExtensionProperties *)malloc(inst_ext_count * sizeof(VkExtensionProperties));
+vkEnumerateInstanceExtensionProperties(nullptr, &inst_ext_count, inst_exts);
+
+const char * enabled_inst_exts[16] = {};
+uint32_t enabled_inst_ext_count = 0;
+
+// Make sure the debug report extension is available
+for (uint32_t i = 0; i < inst_ext_count; i++) {
+ if (strcmp(inst_exts[i].extensionName,
+ VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) {
+ enabled_inst_exts[enabled_inst_ext_count++] =
+ VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
+ }
+}
+
+if (enabled_inst_ext_count == 0)
+ return;
+
+// Pass the instance extensions into vkCreateInstance
+VkInstanceCreateInfo instance_info = {};
+instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+instance_info.enabledExtensionCount = enabled_inst_ext_count;
+instance_info.ppEnabledExtensionNames = enabled_inst_exts;
+
+PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT;
+PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT;
+
+vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)
+ vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
+vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)
+ vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
+
+assert(vkCreateDebugReportCallbackEXT);
+assert(vkDestroyDebugReportCallbackEXT);
+
+// Create the debug callback with desired settings
+VkDebugReportCallbackEXT debugReportCallback;
+if (vkCreateDebugReportCallbackEXT) {
+ VkDebugReportCallbackCreateInfoEXT debugReportCallbackCreateInfo;
+ debugReportCallbackCreateInfo.sType =
+ VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
+ debugReportCallbackCreateInfo.pNext = NULL;
+ debugReportCallbackCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT |
+ VK_DEBUG_REPORT_WARNING_BIT_EXT |
+ VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
+ debugReportCallbackCreateInfo.pfnCallback = DebugReportCallback;
+ debugReportCallbackCreateInfo.pUserData = NULL;
+
+ vkCreateDebugReportCallbackEXT(instance, &debugReportCallbackCreateInfo,
+ nullptr, &debugReportCallback);
+}
+
+// Later, when shutting down Vulkan, call the following
+if (vkDestroyDebugReportCallbackEXT) {
+ vkDestroyDebugReportCallbackEXT(instance, debugReportCallback, nullptr);
+}
+
+
+
+Once your app has registered and enabled the debug callback, the system routes debugging
+messages to a callback that you register. An example of such a callback appears below:
+
+
+
+
+#include <android/log.h>
+
+static VKAPI_ATTR VkBool32 VKAPI_CALL DebugReportCallback(
+ VkDebugReportFlagsEXT msgFlags,
+ VkDebugReportObjectTypeEXT objType,
+ uint64_t srcObject, size_t location,
+ int32_t msgCode, const char * pLayerPrefix,
+ const char * pMsg, void * pUserData )
+{
+ if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
+ __android_log_print(ANDROID_LOG_ERROR,
+ "AppName",
+ "ERROR: [%s] Code %i : %s",
+ pLayerPrefix, msgCode, pMsg);
+ } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
+ __android_log_print(ANDROID_LOG_WARN,
+ "AppName",
+ "WARNING: [%s] Code %i : %s",
+ pLayerPrefix, msgCode, pMsg);
+ } else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
+ __android_log_print(ANDROID_LOG_WARN,
+ "AppName",
+ "PERFORMANCE WARNING: [%s] Code %i : %s",
+ pLayerPrefix, msgCode, pMsg);
+ } else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
+ __android_log_print(ANDROID_LOG_INFO,
+ "AppName", "INFO: [%s] Code %i : %s",
+ pLayerPrefix, msgCode, pMsg);
+ } else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
+ __android_log_print(ANDROID_LOG_VERBOSE,
+ "AppName", "DEBUG: [%s] Code %i : %s",
+ pLayerPrefix, msgCode, pMsg);
+ }
+
+ // 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;
+}
+
+
+
+