1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
|
page.title=Vulkan Validation Layers on Android
@jd:body
<div id="qv-wrapper">
<div id="qv">
<h2>On this page</h2>
<ol>
<li><a href="#gls">Getting Layer Source</a></li>
<li><a href="#ias">Android Studio Integration</a>
<ol>
<li><a href="#asbl">Building Layers</a></li>
<li><a href="#asil">Installing Layers</a></li>
</ol>
</li>
<li><a href="#cli">Integrating on the Command Line</a>
<ol>
<li><a href="#clibl">Building Layers</a></li>
<li><a href="#cliil">Installing Layers</a></li>
</ol>
</li>
<li><a href="#verifying">Verifying Layer Build</a></li>
<li><a href="#enabling">Enabling Layers</a></li>
<li><a href="#debug">Enabling the Debug Callback</a></li>
</ol>
</div>
</div>
<p>
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 <em>validation layers</em>. Validation layers intercept
or hook Vulkan entry points for various debug and validation purposes.
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
This page explains how to:
<ul>
<li>Get source code for validation layers.</li>
<li>Build the layers.</li>
<li>Incorporate the layers into your app.</li>
</ul>
</p>
<h2 id="gls">Getting Layer Source</h2>
<p>
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 <a href="#asil">Android Studio</a> or from the <a href="cliil">
command line</a>.
</p>
<h3 id="ftn">From the NDK (Recommended)</h3>
<p>
<a href="{@docRoot}ndk/downloads/index.html">NDK Revision 12</a> 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 <a href="#building">build them</a>. Alternatively, you can pull source code from the
Khronos Group repository.
</pre>
</p>
<h3 id="ftr">From the repository</h3>
<p>
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
<a class="external-link" href="https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers">
GitHub repository</a> belonging to the Khronos Group. To do so, perform the following steps.
</p>
<ol style="1">
<li>
Clone the Vulkan directory by entering the following command in your terminal window:
<pre class="no-pretty-print">
$ git clone git@github.com:KhronosGroup/Vulkan-LoaderAndValidationLayers.git
</pre>
<p class="note"><strong>Note: </strong>You must have a private SSH key associated with
GitHub, or this command fails with a {@code Permission denied (publickey)} message.</p>
</li>
<li>
Navigate to the directory containing the layer source code, and
check out the repo's stable Android branch, called {@code android_layers}:
<pre class="no-pretty-print">
$ cd Vulkan-LoaderAndValidationLayers
$ git checkout android_layers
</pre>
</li>
<li>
Begin preparation for building by entering the following commands on the command line:
<ul>
<li>For Linux or OS X:
<ul>
<li>
<pre class="no-pretty-print">
$ cd build-android
$ ./android-generate</pre>
</li>
</ul>
</li>
<li>For Windows:
<ul>
<li>
<pre class="no-pretty-print">
> cd build-android
> android-generate.bat</pre>
</li>
</ul>
</li>
</ul>
</li>
<li>
Continue by following the build instructions for your platform.
These instructions are in the {@code BUILD.md} file contained in the local instance of the
repository you cloned.
</li>
</ul>
</ol>
<h3 id="ias">Android Studio Integration</h3>
<p>
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.
</p>
<h4 id="asbl">Building layers</h4>
<p>
To integrate layers directory into Android Studio application, perform these steps:
</p>
<li>
Add layers to your Android Studio application's project by specifying their corresponding
Gradle projects in {@code settings.gradle}, which is normally a peer to app directory.
The following example shows how to do this, based on the assumption that you're
<a href="#ftn">using the {@code build.gradle} files from the NDK</a>.
<pre>
// 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}")
}
</pre>
</li>
Your next step is to provide the built layers to the app by installing them.
<h4 id="asil">Installing layers</h4>
<li>
To install your layers, add the layer Gradle projects to your application's jniLibs dependencies
in your {@code build.gradle} module. This module normally resides under the {@code app/} directory.
The following example shows how to do this:
<pre>
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
</pre>
</li>
<li>
Develop, build, and debug as you usually would. When you build, Android Studio automatically
builds the layers and copies them into your APK.
</li>
<li>
Debug your application. Android Studio allows you to trace through the layer source code.
</li>
<li>
For best performance, remove the layers before you do your release build.
</li>
</ol>
<h3 id="cli">From the Command Line</h3>
This section explains how to build and install your layers if your project does not use
Android Studio.
<h4 id="clibl">Building layers</h4>
<p>
To build validation layers on Linux or OS X, enter these commands on the command line:
</p>
<ul>
<li>
Using Gradle:
<pre class="no-pretty-print">
$ cd generated/gradle-build
$ # configure SDK and NDK path in local.properties
$ gradlew assembleAllDebug
</pre>
</li>
<li>
Using Android makefiles:
<pre class="no-pretty-print">
$ ndk-build</pre>
</li>
</ul>
<p>
To build validation layers on Windows, enter these commands on the command line:
</p>
<ul>
<li>
Using Gradle:
<pre class="no-pretty-print">
> cd generated\gradle-build
> REM configure SDK and NDK path in local.properties
> gradlew.bat assembleAllDebug
</pre>
</li>
<li>
Using Android makefiles:
<pre class="no-pretty-print">
> ndk-build.cmd
</pre>
</li>
</ul>
</p>
</li>
</ol>
<h4 id="cliil">Installing layers</h4>
<p>
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.
</p>
<pre class="no-pretty-print">
$ mkdir ./src/main/jniLibs
</pre>
<p>
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:
</p>
<pre class="no-pretty-print">
$ 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/
</pre>
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:
</p>
<pre class="no-pretty-print">
$ cp -r .../build-android/libs/* ./src/main/jniLibs/
</pre>
</li>
</ol>
<h2 id="verifying">Verifying Layer Build</h2>
<p>
Regardless of whether you build using Gradle or Android makefiles, the build process produces
a file structure like the following:
</p>
<pre class="no-pretty-print">
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
...
</pre>
<p>
The following example shows how to verify that your APK contains the validation layers
as expected:
</p>
<pre class="no-pretty-print">
$ 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
...
</pre>
<h2 id="enabling">Enabling Layers</h2>
<p>The Vulkan API allows an app to enable both instance layers and device layers.</p>
<h3>Instance layers</h3>
<p>
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.
</p>
<p>
You can call {@code vkEnumerateInstanceLayerProperties()} to list the available instance layers
and their properties. The system enables instance layers when {@code vkCreateInstace()} executes.
</p>
<p>
The following code snippet shows how an app can use the Vulkan API to programmatically enable and
query an instance layer:
</p>
<pre>
// 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;
...
</pre>
<h3>Device layers</h3>
<p>
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()}.
</p>
<p>
You can call {@code vkEnumerateDeviceLayerProperties} to list the available layers
and their properties. The system enables device layers when it calls {@code vkCreateDevice()}.
</p>
<p>
The following code snippet shows how an app can use the Vulkan API to programmatically enable a
device layer.
</p>
<pre>
// 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;
...
</pre>
<h2 id="debug">Enabling the Debug Callback</h2>
<p>
The Debug Report extension {@code VK_EXT_debug_report} allows your application to control
layer behavior when an event occurs.</p>
<p>
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.
</p>
<pre>
// 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);
}
</pre>
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:
</p>
<pre>
#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;
}
</pre>
|