vkinfo.cpp revision ca472ab8e43efe92db30660db9997117e4b00ab9
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    float queue_priorities[] = {0.0};
165    const VkDeviceQueueCreateInfo queue_create_info = {
166        .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
167        .queueFamilyIndex = 0,
168        .queueCount = 1,
169        queue_priorities
170    };
171    const VkDeviceCreateInfo create_info = {
172        .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
173        .queueCreateInfoCount = 1,
174        .pQueueCreateInfos = &queue_create_info,
175        .enabledExtensionCount = num_extensions,
176        .ppEnabledExtensionNames = extensions,
177        .pEnabledFeatures = &info.features,
178    };
179    result = vkCreateDevice(gpu, &create_info, nullptr, &device);
180    if (result != VK_SUCCESS)
181        die("vkCreateDevice", result);
182    vkDestroyDevice(device, nullptr);
183}
184
185void GatherInfo(VulkanInfo* info) {
186    VkResult result;
187    uint32_t count;
188
189    result = vkEnumerateInstanceLayerProperties(&count, nullptr);
190    if (result != VK_SUCCESS)
191        die("vkEnumerateInstanceLayerProperties (count)", result);
192    do {
193        info->layers.resize(count);
194        result =
195            vkEnumerateInstanceLayerProperties(&count, info->layers.data());
196    } while (result == VK_INCOMPLETE);
197    if (result != VK_SUCCESS)
198        die("vkEnumerateInstanceLayerProperties (data)", result);
199    info->layer_extensions.resize(info->layers.size());
200
201    EnumerateInstanceExtensions(nullptr, &info->extensions);
202    for (size_t i = 0; i < info->layers.size(); i++) {
203        EnumerateInstanceExtensions(info->layers[i].layerName,
204                                    &info->layer_extensions[i]);
205    }
206
207    const char* kDesiredExtensions[] = {
208        VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
209    };
210    const char*
211        extensions[sizeof(kDesiredExtensions) / sizeof(kDesiredExtensions[0])];
212    uint32_t num_extensions = 0;
213    for (const auto& desired_ext : kDesiredExtensions) {
214        bool available = HasExtension(info->extensions, desired_ext);
215        for (size_t i = 0; !available && i < info->layer_extensions.size(); i++)
216            available = HasExtension(info->layer_extensions[i], desired_ext);
217        if (available)
218            extensions[num_extensions++] = desired_ext;
219    }
220
221    const VkInstanceCreateInfo create_info = {
222        .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
223        .enabledExtensionCount = num_extensions,
224        .ppEnabledExtensionNames = extensions,
225    };
226    VkInstance instance;
227    result = vkCreateInstance(&create_info, nullptr, &instance);
228    if (result != VK_SUCCESS)
229        die("vkCreateInstance", result);
230
231    uint32_t num_gpus;
232    result = vkEnumeratePhysicalDevices(instance, &num_gpus, nullptr);
233    if (result != VK_SUCCESS)
234        die("vkEnumeratePhysicalDevices (count)", result);
235    std::vector<VkPhysicalDevice> gpus(num_gpus, VK_NULL_HANDLE);
236    do {
237        gpus.resize(num_gpus, VK_NULL_HANDLE);
238        result = vkEnumeratePhysicalDevices(instance, &num_gpus, gpus.data());
239    } while (result == VK_INCOMPLETE);
240    if (result != VK_SUCCESS)
241        die("vkEnumeratePhysicalDevices (data)", result);
242
243    info->gpus.resize(num_gpus);
244    for (size_t i = 0; i < gpus.size(); i++)
245        GatherGpuInfo(gpus[i], info->gpus.at(i));
246
247    vkDestroyInstance(instance, nullptr);
248}
249
250// ----------------------------------------------------------------------------
251
252struct Options {
253    bool layer_description;
254    bool layer_extensions;
255};
256
257const size_t kMaxIndent = 8;
258const size_t kIndentSize = 3;
259std::array<char, kMaxIndent * kIndentSize + 1> kIndent;
260const char* Indent(size_t n) {
261    static bool initialized = false;
262    if (!initialized) {
263        kIndent.fill(' ');
264        kIndent.back() = '\0';
265        initialized = true;
266    }
267    return kIndent.data() +
268           (kIndent.size() - (kIndentSize * std::min(n, kMaxIndent) + 1));
269}
270
271uint32_t ExtractMajorVersion(uint32_t version) {
272    return (version >> 22) & 0x3FF;
273}
274uint32_t ExtractMinorVersion(uint32_t version) {
275    return (version >> 12) & 0x3FF;
276}
277uint32_t ExtractPatchVersion(uint32_t version) {
278    return (version >> 0) & 0xFFF;
279}
280
281const char* VkPhysicalDeviceTypeStr(VkPhysicalDeviceType type) {
282    switch (type) {
283        case VK_PHYSICAL_DEVICE_TYPE_OTHER:
284            return "OTHER";
285        case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
286            return "INTEGRATED_GPU";
287        case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
288            return "DISCRETE_GPU";
289        case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
290            return "VIRTUAL_GPU";
291        case VK_PHYSICAL_DEVICE_TYPE_CPU:
292            return "CPU";
293        default:
294            return "<UNKNOWN>";
295    }
296}
297
298void PrintExtensions(const std::vector<VkExtensionProperties>& extensions,
299                     const Options& /*options*/,
300                     size_t indent) {
301    for (const auto& e : extensions)
302        printf("%s%s (v%u)\n", Indent(indent), e.extensionName, e.specVersion);
303}
304
305void PrintLayers(
306    const std::vector<VkLayerProperties>& layers,
307    const std::vector<std::vector<VkExtensionProperties>> extensions,
308    const Options& options,
309    size_t indent) {
310    for (size_t i = 0; i < layers.size(); i++) {
311        printf("%s%s %u.%u.%u/%u\n", Indent(indent), layers[i].layerName,
312               ExtractMajorVersion(layers[i].specVersion),
313               ExtractMinorVersion(layers[i].specVersion),
314               ExtractPatchVersion(layers[i].specVersion),
315               layers[i].implementationVersion);
316        if (options.layer_description)
317            printf("%s%s\n", Indent(indent + 1), layers[i].description);
318        if (options.layer_extensions && !extensions[i].empty()) {
319            if (!extensions[i].empty()) {
320                printf("%sExtensions [%zu]:\n", Indent(indent + 1),
321                       extensions[i].size());
322                PrintExtensions(extensions[i], options, indent + 2);
323            }
324        }
325    }
326}
327
328void PrintGpuInfo(const GpuInfo& info, const Options& options, size_t indent) {
329    VkResult result;
330    std::ostringstream strbuf;
331
332    printf("%s\"%s\" (%s) %u.%u.%u/%#x [%04x:%04x]\n", Indent(indent),
333           info.properties.deviceName,
334           VkPhysicalDeviceTypeStr(info.properties.deviceType),
335           ExtractMajorVersion(info.properties.apiVersion),
336           ExtractMinorVersion(info.properties.apiVersion),
337           ExtractPatchVersion(info.properties.apiVersion),
338           info.properties.driverVersion, info.properties.vendorID,
339           info.properties.deviceID);
340
341    for (uint32_t heap = 0; heap < info.memory.memoryHeapCount; heap++) {
342        if ((info.memory.memoryHeaps[heap].flags &
343             VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
344            strbuf << "DEVICE_LOCAL";
345        printf("%sHeap %u: %" PRIu64 " MiB (0x%" PRIx64 " B) %s\n",
346               Indent(indent + 1), heap,
347               info.memory.memoryHeaps[heap].size / 0x100000,
348               info.memory.memoryHeaps[heap].size, strbuf.str().c_str());
349        strbuf.str(std::string());
350
351        for (uint32_t type = 0; type < info.memory.memoryTypeCount; type++) {
352            if (info.memory.memoryTypes[type].heapIndex != heap)
353                continue;
354            VkMemoryPropertyFlags flags =
355                info.memory.memoryTypes[type].propertyFlags;
356            if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
357                strbuf << " DEVICE_LOCAL";
358            if ((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
359                strbuf << " HOST_VISIBLE";
360            if ((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
361                strbuf << " COHERENT";
362            if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
363                strbuf << " CACHED";
364            if ((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
365                strbuf << " LAZILY_ALLOCATED";
366            printf("%sType %u:%s\n", Indent(indent + 2), type,
367                   strbuf.str().c_str());
368            strbuf.str(std::string());
369        }
370    }
371
372    for (uint32_t family = 0; family < info.queue_families.size(); family++) {
373        const VkQueueFamilyProperties& qprops = info.queue_families[family];
374        VkQueueFlags flags = qprops.queueFlags;
375        char flags_str[5];
376        flags_str[0] = (flags & VK_QUEUE_GRAPHICS_BIT) ? 'G' : '_';
377        flags_str[1] = (flags & VK_QUEUE_COMPUTE_BIT) ? 'C' : '_';
378        flags_str[2] = (flags & VK_QUEUE_TRANSFER_BIT) ? 'T' : '_';
379        flags_str[3] = (flags & VK_QUEUE_SPARSE_BINDING_BIT) ? 'S' : '_';
380        flags_str[4] = '\0';
381        printf(
382            "%sQueue Family %u: %ux %s\n"
383            "%stimestampValidBits: %ub\n"
384            "%sminImageTransferGranularity: (%u,%u,%u)\n",
385            Indent(indent + 1), family, qprops.queueCount, flags_str,
386            Indent(indent + 2), qprops.timestampValidBits, Indent(indent + 2),
387            qprops.minImageTransferGranularity.width,
388            qprops.minImageTransferGranularity.height,
389            qprops.minImageTransferGranularity.depth);
390    }
391
392    // clang-format off
393    printf("%sFeatures:\n", Indent(indent + 1));
394    printf("%srobustBufferAccess: %s\n", Indent(indent + 2), info.features.robustBufferAccess ? "YES" : "NO");
395    printf("%sfullDrawIndexUint32: %s\n", Indent(indent + 2), info.features.fullDrawIndexUint32 ? "YES" : "NO");
396    printf("%simageCubeArray: %s\n", Indent(indent + 2), info.features.imageCubeArray ? "YES" : "NO");
397    printf("%sindependentBlend: %s\n", Indent(indent + 2), info.features.independentBlend ? "YES" : "NO");
398    printf("%sgeometryShader: %s\n", Indent(indent + 2), info.features.geometryShader ? "YES" : "NO");
399    printf("%stessellationShader: %s\n", Indent(indent + 2), info.features.tessellationShader ? "YES" : "NO");
400    printf("%ssampleRateShading: %s\n", Indent(indent + 2), info.features.sampleRateShading ? "YES" : "NO");
401    printf("%sdualSrcBlend: %s\n", Indent(indent + 2), info.features.dualSrcBlend ? "YES" : "NO");
402    printf("%slogicOp: %s\n", Indent(indent + 2), info.features.logicOp ? "YES" : "NO");
403    printf("%smultiDrawIndirect: %s\n", Indent(indent + 2), info.features.multiDrawIndirect ? "YES" : "NO");
404    printf("%sdrawIndirectFirstInstance: %s\n", Indent(indent + 2), info.features.drawIndirectFirstInstance ? "YES" : "NO");
405    printf("%sdepthClamp: %s\n", Indent(indent + 2), info.features.depthClamp ? "YES" : "NO");
406    printf("%sdepthBiasClamp: %s\n", Indent(indent + 2), info.features.depthBiasClamp ? "YES" : "NO");
407    printf("%sfillModeNonSolid: %s\n", Indent(indent + 2), info.features.fillModeNonSolid ? "YES" : "NO");
408    printf("%sdepthBounds: %s\n", Indent(indent + 2), info.features.depthBounds ? "YES" : "NO");
409    printf("%swideLines: %s\n", Indent(indent + 2), info.features.wideLines ? "YES" : "NO");
410    printf("%slargePoints: %s\n", Indent(indent + 2), info.features.largePoints ? "YES" : "NO");
411    printf("%salphaToOne: %s\n", Indent(indent + 2), info.features.alphaToOne ? "YES" : "NO");
412    printf("%smultiViewport: %s\n", Indent(indent + 2), info.features.multiViewport ? "YES" : "NO");
413    printf("%ssamplerAnisotropy: %s\n", Indent(indent + 2), info.features.samplerAnisotropy ? "YES" : "NO");
414    printf("%stextureCompressionETC2: %s\n", Indent(indent + 2), info.features.textureCompressionETC2 ? "YES" : "NO");
415    printf("%stextureCompressionASTC_LDR: %s\n", Indent(indent + 2), info.features.textureCompressionASTC_LDR ? "YES" : "NO");
416    printf("%stextureCompressionBC: %s\n", Indent(indent + 2), info.features.textureCompressionBC ? "YES" : "NO");
417    printf("%socclusionQueryPrecise: %s\n", Indent(indent + 2), info.features.occlusionQueryPrecise ? "YES" : "NO");
418    printf("%spipelineStatisticsQuery: %s\n", Indent(indent + 2), info.features.pipelineStatisticsQuery ? "YES" : "NO");
419    printf("%svertexPipelineStoresAndAtomics: %s\n", Indent(indent + 2), info.features.vertexPipelineStoresAndAtomics ? "YES" : "NO");
420    printf("%sfragmentStoresAndAtomics: %s\n", Indent(indent + 2), info.features.fragmentStoresAndAtomics ? "YES" : "NO");
421    printf("%sshaderTessellationAndGeometryPointSize: %s\n", Indent(indent + 2), info.features.shaderTessellationAndGeometryPointSize ? "YES" : "NO");
422    printf("%sshaderImageGatherExtended: %s\n", Indent(indent + 2), info.features.shaderImageGatherExtended ? "YES" : "NO");
423    printf("%sshaderStorageImageExtendedFormats: %s\n", Indent(indent + 2), info.features.shaderStorageImageExtendedFormats ? "YES" : "NO");
424    printf("%sshaderStorageImageMultisample: %s\n", Indent(indent + 2), info.features.shaderStorageImageMultisample ? "YES" : "NO");
425    printf("%sshaderStorageImageReadWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageReadWithoutFormat ? "YES" : "NO");
426    printf("%sshaderStorageImageWriteWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO");
427    printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO");
428    printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO");
429    printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO");
430    printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO");
431    printf("%sshaderClipDistance: %s\n", Indent(indent + 2), info.features.shaderClipDistance ? "YES" : "NO");
432    printf("%sshaderCullDistance: %s\n", Indent(indent + 2), info.features.shaderCullDistance ? "YES" : "NO");
433    printf("%sshaderFloat64: %s\n", Indent(indent + 2), info.features.shaderFloat64 ? "YES" : "NO");
434    printf("%sshaderInt64: %s\n", Indent(indent + 2), info.features.shaderInt64 ? "YES" : "NO");
435    printf("%sshaderInt16: %s\n", Indent(indent + 2), info.features.shaderInt16 ? "YES" : "NO");
436    printf("%sshaderResourceResidency: %s\n", Indent(indent + 2), info.features.shaderResourceResidency ? "YES" : "NO");
437    printf("%sshaderResourceMinLod: %s\n", Indent(indent + 2), info.features.shaderResourceMinLod ? "YES" : "NO");
438    printf("%ssparseBinding: %s\n", Indent(indent + 2), info.features.sparseBinding ? "YES" : "NO");
439    printf("%ssparseResidencyBuffer: %s\n", Indent(indent + 2), info.features.sparseResidencyBuffer ? "YES" : "NO");
440    printf("%ssparseResidencyImage2D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage2D ? "YES" : "NO");
441    printf("%ssparseResidencyImage3D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage3D ? "YES" : "NO");
442    printf("%ssparseResidency2Samples: %s\n", Indent(indent + 2), info.features.sparseResidency2Samples ? "YES" : "NO");
443    printf("%ssparseResidency4Samples: %s\n", Indent(indent + 2), info.features.sparseResidency4Samples ? "YES" : "NO");
444    printf("%ssparseResidency8Samples: %s\n", Indent(indent + 2), info.features.sparseResidency8Samples ? "YES" : "NO");
445    printf("%ssparseResidency16Samples: %s\n", Indent(indent + 2), info.features.sparseResidency16Samples ? "YES" : "NO");
446    printf("%ssparseResidencyAliased: %s\n", Indent(indent + 2), info.features.sparseResidencyAliased ? "YES" : "NO");
447    printf("%svariableMultisampleRate: %s\n", Indent(indent + 2), info.features.variableMultisampleRate ? "YES" : "NO");
448    printf("%sinheritedQueries: %s\n", Indent(indent + 2), info.features.inheritedQueries ? "YES" : "NO");
449    // clang-format on
450
451    printf("%sExtensions [%zu]:\n", Indent(indent + 1), info.extensions.size());
452    if (!info.extensions.empty())
453        PrintExtensions(info.extensions, options, indent + 2);
454    printf("%sLayers [%zu]:\n", Indent(indent + 1), info.layers.size());
455    if (!info.layers.empty())
456        PrintLayers(info.layers, info.layer_extensions, options, indent + 2);
457}
458
459void PrintInfo(const VulkanInfo& info, const Options& options) {
460    std::ostringstream strbuf;
461    size_t indent = 0;
462
463    printf("%sInstance Extensions [%zu]:\n", Indent(indent),
464           info.extensions.size());
465    PrintExtensions(info.extensions, options, indent + 1);
466    printf("%sInstance Layers [%zu]:\n", Indent(indent), info.layers.size());
467    if (!info.layers.empty())
468        PrintLayers(info.layers, info.layer_extensions, options, indent + 1);
469
470    printf("%sPhysicalDevices [%zu]:\n", Indent(indent), info.gpus.size());
471    for (const auto& gpu : info.gpus)
472        PrintGpuInfo(gpu, options, indent + 1);
473}
474
475}  // namespace
476
477// ----------------------------------------------------------------------------
478
479int main(int argc, char const* argv[]) {
480    Options options = {
481        .layer_description = false, .layer_extensions = false,
482    };
483    for (int argi = 1; argi < argc; argi++) {
484        if (strcmp(argv[argi], "-v") == 0) {
485            options.layer_description = true;
486            options.layer_extensions = true;
487        } else if (strcmp(argv[argi], "-layer_description") == 0) {
488            options.layer_description = true;
489        } else if (strcmp(argv[argi], "-layer_extensions") == 0) {
490            options.layer_extensions = true;
491        }
492    }
493
494    VulkanInfo info;
495    GatherInfo(&info);
496    PrintInfo(info, options);
497    return 0;
498}
499