vkinfo.cpp revision 00f10fec7d5cb96bc980a6d272dc8d33597a1b20
1/*
2 * Copyright 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <algorithm>
18#include <array>
19#include <inttypes.h>
20#include <stdlib.h>
21#include <sstream>
22#include <vector>
23
24#include <vulkan/vulkan.h>
25#include <vulkan/vk_ext_debug_report.h>
26
27#define LOG_TAG "vkinfo"
28#include <log/log.h>
29
30namespace {
31
32struct GpuInfo {
33    VkPhysicalDeviceProperties properties;
34    VkPhysicalDeviceMemoryProperties memory;
35    VkPhysicalDeviceFeatures features;
36    std::vector<VkQueueFamilyProperties> queue_families;
37    std::vector<VkExtensionProperties> extensions;
38    std::vector<VkLayerProperties> layers;
39    std::vector<std::vector<VkExtensionProperties>> layer_extensions;
40};
41struct VulkanInfo {
42    std::vector<VkExtensionProperties> extensions;
43    std::vector<VkLayerProperties> layers;
44    std::vector<std::vector<VkExtensionProperties>> layer_extensions;
45    std::vector<GpuInfo> gpus;
46};
47
48// ----------------------------------------------------------------------------
49
50[[noreturn]] void die(const char* proc, VkResult result) {
51    const char* result_str;
52    switch (result) {
53        // clang-format off
54        case VK_SUCCESS: result_str = "VK_SUCCESS"; break;
55        case VK_NOT_READY: result_str = "VK_NOT_READY"; break;
56        case VK_TIMEOUT: result_str = "VK_TIMEOUT"; break;
57        case VK_EVENT_SET: result_str = "VK_EVENT_SET"; break;
58        case VK_EVENT_RESET: result_str = "VK_EVENT_RESET"; break;
59        case VK_INCOMPLETE: result_str = "VK_INCOMPLETE"; break;
60        case VK_ERROR_OUT_OF_HOST_MEMORY: result_str = "VK_ERROR_OUT_OF_HOST_MEMORY"; break;
61        case VK_ERROR_OUT_OF_DEVICE_MEMORY: result_str = "VK_ERROR_OUT_OF_DEVICE_MEMORY"; break;
62        case VK_ERROR_INITIALIZATION_FAILED: result_str = "VK_ERROR_INITIALIZATION_FAILED"; break;
63        case VK_ERROR_DEVICE_LOST: result_str = "VK_ERROR_DEVICE_LOST"; break;
64        case VK_ERROR_MEMORY_MAP_FAILED: result_str = "VK_ERROR_MEMORY_MAP_FAILED"; break;
65        case VK_ERROR_LAYER_NOT_PRESENT: result_str = "VK_ERROR_LAYER_NOT_PRESENT"; break;
66        case VK_ERROR_EXTENSION_NOT_PRESENT: result_str = "VK_ERROR_EXTENSION_NOT_PRESENT"; break;
67        case VK_ERROR_INCOMPATIBLE_DRIVER: result_str = "VK_ERROR_INCOMPATIBLE_DRIVER"; break;
68        default: result_str = "<unknown VkResult>"; break;
69            // clang-format on
70    }
71    fprintf(stderr, "%s failed: %s (%d)\n", proc, result_str, result);
72    exit(1);
73}
74
75bool HasExtension(const std::vector<VkExtensionProperties>& extensions,
76                  const char* name) {
77    return std::find_if(extensions.cbegin(), extensions.cend(),
78                        [=](const VkExtensionProperties& prop) {
79                            return strcmp(prop.extensionName, name) == 0;
80                        }) != extensions.end();
81}
82
83void EnumerateInstanceExtensions(
84    const char* layer_name,
85    std::vector<VkExtensionProperties>* extensions) {
86    VkResult result;
87    uint32_t count;
88    result =
89        vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr);
90    if (result != VK_SUCCESS)
91        die("vkEnumerateInstanceExtensionProperties (count)", result);
92    do {
93        extensions->resize(count);
94        result = vkEnumerateInstanceExtensionProperties(layer_name, &count,
95                                                        extensions->data());
96    } while (result == VK_INCOMPLETE);
97    if (result != VK_SUCCESS)
98        die("vkEnumerateInstanceExtensionProperties (data)", result);
99}
100
101void EnumerateDeviceExtensions(VkPhysicalDevice gpu,
102                               const char* layer_name,
103                               std::vector<VkExtensionProperties>* extensions) {
104    VkResult result;
105    uint32_t count;
106    result =
107        vkEnumerateDeviceExtensionProperties(gpu, layer_name, &count, nullptr);
108    if (result != VK_SUCCESS)
109        die("vkEnumerateDeviceExtensionProperties (count)", result);
110    do {
111        extensions->resize(count);
112        result = vkEnumerateDeviceExtensionProperties(gpu, layer_name, &count,
113                                                      extensions->data());
114    } while (result == VK_INCOMPLETE);
115    if (result != VK_SUCCESS)
116        die("vkEnumerateDeviceExtensionProperties (data)", result);
117}
118
119void GatherGpuInfo(VkPhysicalDevice gpu, GpuInfo& info) {
120    VkResult result;
121    uint32_t count;
122
123    vkGetPhysicalDeviceProperties(gpu, &info.properties);
124    vkGetPhysicalDeviceMemoryProperties(gpu, &info.memory);
125    vkGetPhysicalDeviceFeatures(gpu, &info.features);
126
127    vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count, nullptr);
128    info.queue_families.resize(count);
129    vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count,
130                                             info.queue_families.data());
131
132    result = vkEnumerateDeviceLayerProperties(gpu, &count, nullptr);
133    if (result != VK_SUCCESS)
134        die("vkEnumerateDeviceLayerProperties (count)", result);
135    do {
136        info.layers.resize(count);
137        result =
138            vkEnumerateDeviceLayerProperties(gpu, &count, info.layers.data());
139    } while (result == VK_INCOMPLETE);
140    if (result != VK_SUCCESS)
141        die("vkEnumerateDeviceLayerProperties (data)", result);
142    info.layer_extensions.resize(info.layers.size());
143
144    EnumerateDeviceExtensions(gpu, nullptr, &info.extensions);
145    for (size_t i = 0; i < info.layers.size(); i++) {
146        EnumerateDeviceExtensions(gpu, info.layers[i].layerName,
147                                  &info.layer_extensions[i]);
148    }
149
150    const std::array<const char*, 1> kDesiredExtensions = {
151        {VK_KHR_SWAPCHAIN_EXTENSION_NAME},
152    };
153    const char* extensions[kDesiredExtensions.size()];
154    uint32_t num_extensions = 0;
155    for (const auto& desired_ext : kDesiredExtensions) {
156        bool available = HasExtension(info.extensions, desired_ext);
157        for (size_t i = 0; !available && i < info.layer_extensions.size(); i++)
158            available = HasExtension(info.layer_extensions[i], desired_ext);
159        if (available)
160            extensions[num_extensions++] = desired_ext;
161    }
162
163    VkDevice device;
164    const VkDeviceQueueCreateInfo queue_create_info = {
165        .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
166        .queueFamilyIndex = 0,
167        .queueCount = 1,
168    };
169    const VkDeviceCreateInfo create_info = {
170        .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
171        .queueCreateInfoCount = 1,
172        .pQueueCreateInfos = &queue_create_info,
173        .enabledExtensionCount = num_extensions,
174        .ppEnabledExtensionNames = extensions,
175        .pEnabledFeatures = &info.features,
176    };
177    result = vkCreateDevice(gpu, &create_info, nullptr, &device);
178    if (result != VK_SUCCESS)
179        die("vkCreateDevice", result);
180    vkDestroyDevice(device, nullptr);
181}
182
183void GatherInfo(VulkanInfo* info) {
184    VkResult result;
185    uint32_t count;
186
187    result = vkEnumerateInstanceLayerProperties(&count, nullptr);
188    if (result != VK_SUCCESS)
189        die("vkEnumerateInstanceLayerProperties (count)", result);
190    do {
191        info->layers.resize(count);
192        result =
193            vkEnumerateInstanceLayerProperties(&count, info->layers.data());
194    } while (result == VK_INCOMPLETE);
195    if (result != VK_SUCCESS)
196        die("vkEnumerateInstanceLayerProperties (data)", result);
197    info->layer_extensions.resize(info->layers.size());
198
199    EnumerateInstanceExtensions(nullptr, &info->extensions);
200    for (size_t i = 0; i < info->layers.size(); i++) {
201        EnumerateInstanceExtensions(info->layers[i].layerName,
202                                    &info->layer_extensions[i]);
203    }
204
205    const char* kDesiredExtensions[] = {
206        VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
207    };
208    const char*
209        extensions[sizeof(kDesiredExtensions) / sizeof(kDesiredExtensions[0])];
210    uint32_t num_extensions = 0;
211    for (const auto& desired_ext : kDesiredExtensions) {
212        bool available = HasExtension(info->extensions, desired_ext);
213        for (size_t i = 0; !available && i < info->layer_extensions.size(); i++)
214            available = HasExtension(info->layer_extensions[i], desired_ext);
215        if (available)
216            extensions[num_extensions++] = desired_ext;
217    }
218
219    const VkInstanceCreateInfo create_info = {
220        .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
221        .enabledExtensionCount = num_extensions,
222        .ppEnabledExtensionNames = extensions,
223    };
224    VkInstance instance;
225    result = vkCreateInstance(&create_info, nullptr, &instance);
226    if (result != VK_SUCCESS)
227        die("vkCreateInstance", result);
228
229    uint32_t num_gpus;
230    result = vkEnumeratePhysicalDevices(instance, &num_gpus, nullptr);
231    if (result != VK_SUCCESS)
232        die("vkEnumeratePhysicalDevices (count)", result);
233    std::vector<VkPhysicalDevice> gpus(num_gpus, VK_NULL_HANDLE);
234    do {
235        gpus.resize(num_gpus, VK_NULL_HANDLE);
236        result = vkEnumeratePhysicalDevices(instance, &num_gpus, gpus.data());
237    } while (result == VK_INCOMPLETE);
238    if (result != VK_SUCCESS)
239        die("vkEnumeratePhysicalDevices (data)", result);
240
241    info->gpus.resize(num_gpus);
242    for (size_t i = 0; i < gpus.size(); i++)
243        GatherGpuInfo(gpus[i], info->gpus.at(i));
244
245    vkDestroyInstance(instance, nullptr);
246}
247
248// ----------------------------------------------------------------------------
249
250struct Options {
251    bool layer_description;
252    bool layer_extensions;
253};
254
255const size_t kMaxIndent = 8;
256const size_t kIndentSize = 3;
257std::array<char, kMaxIndent * kIndentSize + 1> kIndent;
258const char* Indent(size_t n) {
259    static bool initialized = false;
260    if (!initialized) {
261        kIndent.fill(' ');
262        kIndent.back() = '\0';
263        initialized = true;
264    }
265    return kIndent.data() +
266           (kIndent.size() - (kIndentSize * std::min(n, kMaxIndent) + 1));
267}
268
269uint32_t ExtractMajorVersion(uint32_t version) {
270    return (version >> 22) & 0x3FF;
271}
272uint32_t ExtractMinorVersion(uint32_t version) {
273    return (version >> 12) & 0x3FF;
274}
275uint32_t ExtractPatchVersion(uint32_t version) {
276    return (version >> 0) & 0xFFF;
277}
278
279const char* VkPhysicalDeviceTypeStr(VkPhysicalDeviceType type) {
280    switch (type) {
281        case VK_PHYSICAL_DEVICE_TYPE_OTHER:
282            return "OTHER";
283        case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
284            return "INTEGRATED_GPU";
285        case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
286            return "DISCRETE_GPU";
287        case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
288            return "VIRTUAL_GPU";
289        case VK_PHYSICAL_DEVICE_TYPE_CPU:
290            return "CPU";
291        default:
292            return "<UNKNOWN>";
293    }
294}
295
296void PrintExtensions(const std::vector<VkExtensionProperties>& extensions,
297                     const Options& /*options*/,
298                     size_t indent) {
299    for (const auto& e : extensions)
300        printf("%s%s (v%u)\n", Indent(indent), e.extensionName, e.specVersion);
301}
302
303void PrintLayers(
304    const std::vector<VkLayerProperties>& layers,
305    const std::vector<std::vector<VkExtensionProperties>> extensions,
306    const Options& options,
307    size_t indent) {
308    for (size_t i = 0; i < layers.size(); i++) {
309        printf("%s%s %u.%u.%u/%u\n", Indent(indent), layers[i].layerName,
310               ExtractMajorVersion(layers[i].specVersion),
311               ExtractMinorVersion(layers[i].specVersion),
312               ExtractPatchVersion(layers[i].specVersion),
313               layers[i].implementationVersion);
314        if (options.layer_description)
315            printf("%s%s\n", Indent(indent + 1), layers[i].description);
316        if (options.layer_extensions && !extensions[i].empty()) {
317            if (!extensions[i].empty()) {
318                printf("%sExtensions [%zu]:\n", Indent(indent + 1),
319                       extensions[i].size());
320                PrintExtensions(extensions[i], options, indent + 2);
321            }
322        }
323    }
324}
325
326void PrintGpuInfo(const GpuInfo& info, const Options& options, size_t indent) {
327    VkResult result;
328    std::ostringstream strbuf;
329
330    printf("%s\"%s\" (%s) %u.%u.%u/%#x [%04x:%04x]\n", Indent(indent),
331           info.properties.deviceName,
332           VkPhysicalDeviceTypeStr(info.properties.deviceType),
333           ExtractMajorVersion(info.properties.apiVersion),
334           ExtractMinorVersion(info.properties.apiVersion),
335           ExtractPatchVersion(info.properties.apiVersion),
336           info.properties.driverVersion, info.properties.vendorID,
337           info.properties.deviceID);
338
339    for (uint32_t heap = 0; heap < info.memory.memoryHeapCount; heap++) {
340        if ((info.memory.memoryHeaps[heap].flags &
341             VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
342            strbuf << "DEVICE_LOCAL";
343        printf("%sHeap %u: %" PRIu64 " MiB (0x%" PRIx64 " B) %s\n",
344               Indent(indent + 1), heap,
345               info.memory.memoryHeaps[heap].size / 0x100000,
346               info.memory.memoryHeaps[heap].size, strbuf.str().c_str());
347        strbuf.str(std::string());
348
349        for (uint32_t type = 0; type < info.memory.memoryTypeCount; type++) {
350            if (info.memory.memoryTypes[type].heapIndex != heap)
351                continue;
352            VkMemoryPropertyFlags flags =
353                info.memory.memoryTypes[type].propertyFlags;
354            if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
355                strbuf << " DEVICE_LOCAL";
356            if ((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
357                strbuf << " HOST_VISIBLE";
358            if ((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
359                strbuf << " COHERENT";
360            if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
361                strbuf << " CACHED";
362            if ((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
363                strbuf << " LAZILY_ALLOCATED";
364            printf("%sType %u:%s\n", Indent(indent + 2), type,
365                   strbuf.str().c_str());
366            strbuf.str(std::string());
367        }
368    }
369
370    for (uint32_t family = 0; family < info.queue_families.size(); family++) {
371        const VkQueueFamilyProperties& qprops = info.queue_families[family];
372        VkQueueFlags flags = qprops.queueFlags;
373        char flags_str[5];
374        flags_str[0] = (flags & VK_QUEUE_GRAPHICS_BIT) ? 'G' : '_';
375        flags_str[1] = (flags & VK_QUEUE_COMPUTE_BIT) ? 'C' : '_';
376        flags_str[2] = (flags & VK_QUEUE_TRANSFER_BIT) ? 'T' : '_';
377        flags_str[3] = (flags & VK_QUEUE_SPARSE_BINDING_BIT) ? 'S' : '_';
378        flags_str[4] = '\0';
379        printf(
380            "%sQueue Family %u: %ux %s\n"
381            "%stimestampValidBits: %ub\n"
382            "%sminImageTransferGranularity: (%u,%u,%u)\n",
383            Indent(indent + 1), family, qprops.queueCount, flags_str,
384            Indent(indent + 2), qprops.timestampValidBits, Indent(indent + 2),
385            qprops.minImageTransferGranularity.width,
386            qprops.minImageTransferGranularity.height,
387            qprops.minImageTransferGranularity.depth);
388    }
389
390    // clang-format off
391    printf("%sFeatures:\n", Indent(indent + 1));
392    printf("%srobustBufferAccess: %s\n", Indent(indent + 2), info.features.robustBufferAccess ? "YES" : "NO");
393    printf("%sfullDrawIndexUint32: %s\n", Indent(indent + 2), info.features.fullDrawIndexUint32 ? "YES" : "NO");
394    printf("%simageCubeArray: %s\n", Indent(indent + 2), info.features.imageCubeArray ? "YES" : "NO");
395    printf("%sindependentBlend: %s\n", Indent(indent + 2), info.features.independentBlend ? "YES" : "NO");
396    printf("%sgeometryShader: %s\n", Indent(indent + 2), info.features.geometryShader ? "YES" : "NO");
397    printf("%stessellationShader: %s\n", Indent(indent + 2), info.features.tessellationShader ? "YES" : "NO");
398    printf("%ssampleRateShading: %s\n", Indent(indent + 2), info.features.sampleRateShading ? "YES" : "NO");
399    printf("%sdualSrcBlend: %s\n", Indent(indent + 2), info.features.dualSrcBlend ? "YES" : "NO");
400    printf("%slogicOp: %s\n", Indent(indent + 2), info.features.logicOp ? "YES" : "NO");
401    printf("%smultiDrawIndirect: %s\n", Indent(indent + 2), info.features.multiDrawIndirect ? "YES" : "NO");
402    printf("%sdrawIndirectFirstInstance: %s\n", Indent(indent + 2), info.features.drawIndirectFirstInstance ? "YES" : "NO");
403    printf("%sdepthClamp: %s\n", Indent(indent + 2), info.features.depthClamp ? "YES" : "NO");
404    printf("%sdepthBiasClamp: %s\n", Indent(indent + 2), info.features.depthBiasClamp ? "YES" : "NO");
405    printf("%sfillModeNonSolid: %s\n", Indent(indent + 2), info.features.fillModeNonSolid ? "YES" : "NO");
406    printf("%sdepthBounds: %s\n", Indent(indent + 2), info.features.depthBounds ? "YES" : "NO");
407    printf("%swideLines: %s\n", Indent(indent + 2), info.features.wideLines ? "YES" : "NO");
408    printf("%slargePoints: %s\n", Indent(indent + 2), info.features.largePoints ? "YES" : "NO");
409    printf("%salphaToOne: %s\n", Indent(indent + 2), info.features.alphaToOne ? "YES" : "NO");
410    printf("%smultiViewport: %s\n", Indent(indent + 2), info.features.multiViewport ? "YES" : "NO");
411    printf("%ssamplerAnisotropy: %s\n", Indent(indent + 2), info.features.samplerAnisotropy ? "YES" : "NO");
412    printf("%stextureCompressionETC2: %s\n", Indent(indent + 2), info.features.textureCompressionETC2 ? "YES" : "NO");
413    printf("%stextureCompressionASTC_LDR: %s\n", Indent(indent + 2), info.features.textureCompressionASTC_LDR ? "YES" : "NO");
414    printf("%stextureCompressionBC: %s\n", Indent(indent + 2), info.features.textureCompressionBC ? "YES" : "NO");
415    printf("%socclusionQueryPrecise: %s\n", Indent(indent + 2), info.features.occlusionQueryPrecise ? "YES" : "NO");
416    printf("%spipelineStatisticsQuery: %s\n", Indent(indent + 2), info.features.pipelineStatisticsQuery ? "YES" : "NO");
417    printf("%svertexPipelineStoresAndAtomics: %s\n", Indent(indent + 2), info.features.vertexPipelineStoresAndAtomics ? "YES" : "NO");
418    printf("%sfragmentStoresAndAtomics: %s\n", Indent(indent + 2), info.features.fragmentStoresAndAtomics ? "YES" : "NO");
419    printf("%sshaderTessellationAndGeometryPointSize: %s\n", Indent(indent + 2), info.features.shaderTessellationAndGeometryPointSize ? "YES" : "NO");
420    printf("%sshaderImageGatherExtended: %s\n", Indent(indent + 2), info.features.shaderImageGatherExtended ? "YES" : "NO");
421    printf("%sshaderStorageImageExtendedFormats: %s\n", Indent(indent + 2), info.features.shaderStorageImageExtendedFormats ? "YES" : "NO");
422    printf("%sshaderStorageImageMultisample: %s\n", Indent(indent + 2), info.features.shaderStorageImageMultisample ? "YES" : "NO");
423    printf("%sshaderStorageImageReadWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageReadWithoutFormat ? "YES" : "NO");
424    printf("%sshaderStorageImageWriteWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO");
425    printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO");
426    printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO");
427    printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO");
428    printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO");
429    printf("%sshaderClipDistance: %s\n", Indent(indent + 2), info.features.shaderClipDistance ? "YES" : "NO");
430    printf("%sshaderCullDistance: %s\n", Indent(indent + 2), info.features.shaderCullDistance ? "YES" : "NO");
431    printf("%sshaderFloat64: %s\n", Indent(indent + 2), info.features.shaderFloat64 ? "YES" : "NO");
432    printf("%sshaderInt64: %s\n", Indent(indent + 2), info.features.shaderInt64 ? "YES" : "NO");
433    printf("%sshaderInt16: %s\n", Indent(indent + 2), info.features.shaderInt16 ? "YES" : "NO");
434    printf("%sshaderResourceResidency: %s\n", Indent(indent + 2), info.features.shaderResourceResidency ? "YES" : "NO");
435    printf("%sshaderResourceMinLod: %s\n", Indent(indent + 2), info.features.shaderResourceMinLod ? "YES" : "NO");
436    printf("%ssparseBinding: %s\n", Indent(indent + 2), info.features.sparseBinding ? "YES" : "NO");
437    printf("%ssparseResidencyBuffer: %s\n", Indent(indent + 2), info.features.sparseResidencyBuffer ? "YES" : "NO");
438    printf("%ssparseResidencyImage2D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage2D ? "YES" : "NO");
439    printf("%ssparseResidencyImage3D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage3D ? "YES" : "NO");
440    printf("%ssparseResidency2Samples: %s\n", Indent(indent + 2), info.features.sparseResidency2Samples ? "YES" : "NO");
441    printf("%ssparseResidency4Samples: %s\n", Indent(indent + 2), info.features.sparseResidency4Samples ? "YES" : "NO");
442    printf("%ssparseResidency8Samples: %s\n", Indent(indent + 2), info.features.sparseResidency8Samples ? "YES" : "NO");
443    printf("%ssparseResidency16Samples: %s\n", Indent(indent + 2), info.features.sparseResidency16Samples ? "YES" : "NO");
444    printf("%ssparseResidencyAliased: %s\n", Indent(indent + 2), info.features.sparseResidencyAliased ? "YES" : "NO");
445    printf("%svariableMultisampleRate: %s\n", Indent(indent + 2), info.features.variableMultisampleRate ? "YES" : "NO");
446    printf("%sinheritedQueries: %s\n", Indent(indent + 2), info.features.inheritedQueries ? "YES" : "NO");
447    // clang-format on
448
449    printf("%sExtensions [%zu]:\n", Indent(indent + 1), info.extensions.size());
450    if (!info.extensions.empty())
451        PrintExtensions(info.extensions, options, indent + 2);
452    printf("%sLayers [%zu]:\n", Indent(indent + 1), info.layers.size());
453    if (!info.layers.empty())
454        PrintLayers(info.layers, info.layer_extensions, options, indent + 2);
455}
456
457void PrintInfo(const VulkanInfo& info, const Options& options) {
458    std::ostringstream strbuf;
459    size_t indent = 0;
460
461    printf("%sInstance Extensions [%zu]:\n", Indent(indent),
462           info.extensions.size());
463    PrintExtensions(info.extensions, options, indent + 1);
464    printf("%sInstance Layers [%zu]:\n", Indent(indent), info.layers.size());
465    if (!info.layers.empty())
466        PrintLayers(info.layers, info.layer_extensions, options, indent + 1);
467
468    printf("%sPhysicalDevices [%zu]:\n", Indent(indent), info.gpus.size());
469    for (const auto& gpu : info.gpus)
470        PrintGpuInfo(gpu, options, indent + 1);
471}
472
473}  // namespace
474
475// ----------------------------------------------------------------------------
476
477int main(int argc, char const* argv[]) {
478    Options options = {
479        .layer_description = false, .layer_extensions = false,
480    };
481    for (int argi = 1; argi < argc; argi++) {
482        if (strcmp(argv[argi], "-v") == 0) {
483            options.layer_description = true;
484            options.layer_extensions = true;
485        } else if (strcmp(argv[argi], "-layer_description") == 0) {
486            options.layer_description = true;
487        } else if (strcmp(argv[argi], "-layer_extensions") == 0) {
488            options.layer_extensions = true;
489        }
490    }
491
492    VulkanInfo info;
493    GatherInfo(&info);
494    PrintInfo(info, options);
495    return 0;
496}
497