vulkaninfo: Make json output print extensions
[platform/upstream/Vulkan-Tools.git] / vulkaninfo / vulkaninfo.cpp
1 /*
2  * Copyright (c) 2015-2020 The Khronos Group Inc.
3  * Copyright (c) 2015-2020 Valve Corporation
4  * Copyright (c) 2015-2020 LunarG, Inc.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
19  * Author: David Pinedo <david@lunarg.com>
20  * Author: Mark Lobodzinski <mark@lunarg.com>
21  * Author: Rene Lindsay <rene@lunarg.com>
22  * Author: Jeremy Kniager <jeremyk@lunarg.com>
23  * Author: Shannon McPherson <shannon@lunarg.com>
24  * Author: Bob Ellison <bob@lunarg.com>
25  * Author: Charles Giessen <charles@lunarg.com>
26  *
27  */
28
29 #include "vulkaninfo.hpp"
30
31 #ifdef _WIN32
32 // Initialize User32 pointers
33 PFN_AdjustWindowRect User32Handles::pfnAdjustWindowRect = nullptr;
34 PFN_CreateWindowExA User32Handles::pfnCreateWindowExA = nullptr;
35 PFN_DefWindowProcA User32Handles::pfnDefWindowProcA = nullptr;
36 PFN_DestroyWindow User32Handles::pfnDestroyWindow = nullptr;
37 PFN_LoadIconA User32Handles::pfnLoadIconA = nullptr;
38 PFN_RegisterClassExA User32Handles::pfnRegisterClassExA = nullptr;
39
40 HMODULE User32Handles::user32DllHandle = nullptr;
41
42 #endif
43
44 // =========== Dump Functions ========= //
45
46 void DumpExtensions(Printer &p, std::string layer_name, std::vector<VkExtensionProperties> extensions, bool do_indent) {
47     std::sort(extensions.begin(), extensions.end(), [](VkExtensionProperties &a, VkExtensionProperties &b) -> int {
48         return std::string(a.extensionName) < std::string(b.extensionName);
49     });
50
51     size_t max_length = 0;
52     for (const auto &ext : extensions) {
53         max_length = std::max(max_length, std::strlen(ext.extensionName));
54     }
55
56     ObjectWrapper obj(p, layer_name + " Extensions", extensions.size());
57     if (do_indent) p.IndentDecrease();
58     for (auto &ext : extensions) {
59         p.PrintExtension(ext.extensionName, ext.specVersion, max_length);
60     }
61     if (do_indent) p.IndentIncrease();
62 }
63 void DumpExtensions(Printer &p, std::string layer_name, std::vector<VkExtensionProperties> extensions) {
64     DumpExtensions(p, layer_name, extensions, false);
65 }
66 void DumpLayers(Printer &p, std::vector<LayerExtensionList> layers, const std::vector<std::unique_ptr<AppGpu>> &gpus) {
67     std::sort(layers.begin(), layers.end(), [](LayerExtensionList &left, LayerExtensionList &right) -> int {
68         return std::strncmp(left.layer_properties.layerName, right.layer_properties.layerName, VK_MAX_DESCRIPTION_SIZE) < 0;
69     });
70     switch (p.Type()) {
71         case OutputType::text:
72         case OutputType::html: {
73             p.SetHeader();
74             ArrayWrapper arr(p, "Layers", layers.size());
75             IndentWrapper indent(p);
76
77             for (auto &layer : layers) {
78                 auto v_str = VkVersionString(layer.layer_properties.specVersion);
79                 auto props = layer.layer_properties;
80
81                 std::string header = p.DecorateAsType(props.layerName) + " (" + props.description + ") Vulkan version " +
82                                      p.DecorateAsValue(v_str) + ", layer version " +
83                                      p.DecorateAsValue(std::to_string(props.implementationVersion));
84                 ObjectWrapper obj(p, header);
85                 DumpExtensions(p, "Layer", layer.extension_properties);
86
87                 ArrayWrapper arr(p, "Devices", gpus.size());
88                 for (auto &gpu : gpus) {
89                     p.PrintKeyValue("GPU id", gpu->id, 0, gpu->props.deviceName);
90                     auto exts = gpu->AppGetPhysicalDeviceLayerExtensions(props.layerName);
91                     DumpExtensions(p, "Layer-Device", exts);
92                     p.AddNewline();
93                 }
94             }
95             break;
96         }
97
98         case OutputType::json: {
99             ArrayWrapper arr(p, "ArrayOfVkLayerProperties", layers.size());
100             int i = 0;
101             for (auto &layer : layers) {
102                 p.SetElementIndex(i++);
103                 DumpVkLayerProperties(p, "layerProperty", layer.layer_properties);
104             }
105             break;
106         }
107         case OutputType::vkconfig_output: {
108             ObjectWrapper obj(p, "Layer Properties");
109             for (auto &layer : layers) {
110                 ObjectWrapper obj_name(p, layer.layer_properties.layerName);
111                 p.PrintKeyString("layerName", layer.layer_properties.layerName, 21);
112                 p.PrintKeyString("version", VkVersionString(layer.layer_properties.specVersion), 21);
113                 p.PrintKeyValue("implementation version", layer.layer_properties.implementationVersion, 21);
114                 p.PrintKeyString("description", layer.layer_properties.description, 21);
115                 DumpExtensions(p, "Layer", layer.extension_properties);
116                 ObjectWrapper obj_devices(p, "Devices");
117                 for (auto &gpu : gpus) {
118                     ObjectWrapper obj(p, gpu->props.deviceName);
119                     p.PrintKeyValue("GPU id", gpu->id, 0, gpu->props.deviceName);
120                     auto exts = gpu->AppGetPhysicalDeviceLayerExtensions(layer.layer_properties.layerName);
121                     DumpExtensions(p, "Layer-Device", exts);
122                 }
123             }
124             break;
125         }
126     }
127 }
128
129 void DumpSurfaceFormats(Printer &p, AppInstance &inst, AppSurface &surface) {
130     std::vector<VkSurfaceFormatKHR> formats;
131     if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) {
132         for (auto &format : surface.surf_formats2) {
133             formats.push_back(format.surfaceFormat);
134         }
135     } else {
136         for (auto &format : surface.surf_formats) {
137             formats.push_back(format);
138         }
139     }
140     ObjectWrapper obj(p, "Formats", formats.size());
141     int i = 0;
142     for (auto &format : formats) {
143         p.SetElementIndex(i++);
144         DumpVkSurfaceFormatKHR(p, "SurfaceFormat", format);
145     }
146 }
147
148 void DumpPresentModes(Printer &p, AppSurface &surface) {
149     ArrayWrapper arr(p, "Present Modes", surface.surf_present_modes.size());
150     for (auto &mode : surface.surf_present_modes) {
151         p.SetAsType().PrintString(VkPresentModeKHRString(mode));
152     }
153 }
154
155 void DumpSurfaceCapabilities(Printer &p, AppInstance &inst, AppGpu &gpu, AppSurface &surface) {
156     auto &surf_cap = surface.surface_capabilities;
157     p.SetSubHeader();
158     DumpVkSurfaceCapabilitiesKHR(p, "VkSurfaceCapabilitiesKHR", surf_cap);
159
160     if (inst.CheckExtensionEnabled(VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME)) {
161         p.SetSubHeader();
162         ObjectWrapper obj(p, "VkSurfaceCapabilities2EXT");
163         {
164             ArrayWrapper arr(p, "supportedSurfaceCounters");
165             if (surface.surface_capabilities2_ext.supportedSurfaceCounters == 0) p.PrintString("None");
166             if (surface.surface_capabilities2_ext.supportedSurfaceCounters & VK_SURFACE_COUNTER_VBLANK_EXT) {
167                 p.SetAsType().PrintString("VK_SURFACE_COUNTER_VBLANK_EXT");
168             }
169         }
170     }
171     if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) {
172         chain_iterator_surface_capabilities2(p, inst, gpu, surface.surface_capabilities2_khr.pNext, inst.vk_version);
173     }
174 }
175
176 void DumpSurface(Printer &p, AppInstance &inst, AppGpu &gpu, AppSurface &surface, std::set<std::string> surface_types) {
177     ObjectWrapper obj(p, std::string("GPU id : ") + p.DecorateAsValue(std::to_string(gpu.id)) + " (" + gpu.props.deviceName + ")");
178
179     if (surface_types.size() == 0) {
180         p.SetAsType().PrintKeyString("Surface type", "No type found");
181     } else if (surface_types.size() == 1) {
182         p.SetAsType().PrintKeyString("Surface type", surface.surface_extension.name);
183     } else {
184         ArrayWrapper arr(p, "Surface types", surface_types.size());
185         for (auto &name : surface_types) {
186             p.PrintString(name);
187         }
188     }
189
190     DumpSurfaceFormats(p, inst, surface);
191     DumpPresentModes(p, surface);
192     DumpSurfaceCapabilities(p, inst, gpu, surface);
193
194     p.AddNewline();
195 }
196
197 struct SurfaceTypeGroup {
198     AppSurface *surface;
199     AppGpu *gpu;
200     std::set<std::string> surface_types;
201 };
202
203 bool operator==(AppSurface const &a, AppSurface const &b) {
204     return a.phys_device == b.phys_device && a.surf_present_modes == b.surf_present_modes && a.surf_formats == b.surf_formats &&
205            a.surf_formats2 == b.surf_formats2 && a.surface_capabilities == b.surface_capabilities &&
206            a.surface_capabilities2_khr == b.surface_capabilities2_khr && a.surface_capabilities2_ext == b.surface_capabilities2_ext;
207 }
208
209 void DumpPresentableSurfaces(Printer &p, AppInstance &inst, const std::vector<std::unique_ptr<AppGpu>> &gpus,
210                              const std::vector<std::unique_ptr<AppSurface>> &surfaces) {
211     p.SetHeader();
212     ObjectWrapper obj(p, "Presentable Surfaces");
213     IndentWrapper indent(p);
214
215     std::vector<SurfaceTypeGroup> surface_list;
216
217     for (auto &surface : surfaces) {
218         auto exists = surface_list.end();
219         for (auto it = surface_list.begin(); it != surface_list.end(); it++) {
220             // check for duplicat surfaces that differ only by the surface extension
221             if (*(it->surface) == *(surface.get())) {
222                 exists = it;
223                 break;
224             }
225         }
226         if (exists != surface_list.end()) {
227             exists->surface_types.insert(surface.get()->surface_extension.name);
228         } else {
229             // find surface.phys_device's corresponding AppGpu
230             AppGpu *corresponding_gpu = nullptr;
231             for (auto &gpu : gpus) {
232                 if (gpu->phys_device == surface->phys_device) corresponding_gpu = gpu.get();
233             }
234             if (corresponding_gpu != nullptr)
235                 surface_list.push_back({surface.get(), corresponding_gpu, {surface.get()->surface_extension.name}});
236         }
237     }
238     for (auto &group : surface_list) {
239         DumpSurface(p, inst, *group.gpu, *group.surface, group.surface_types);
240     }
241     p.AddNewline();
242 }
243
244 void DumpGroups(Printer &p, AppInstance &inst) {
245     if (inst.CheckExtensionEnabled(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME)) {
246         auto groups = GetGroups(inst);
247         if (groups.size() == 0) {
248             p.SetHeader();
249             ObjectWrapper obj(p, "Groups");
250             p.PrintString("No Device Groups Found");
251             p.AddNewline();
252             return;
253         }
254
255         p.SetHeader();
256         ObjectWrapper obj(p, "Device Groups");
257         IndentWrapper indent(p);
258
259         int group_id = 0;
260         for (auto &group : groups) {
261             ObjectWrapper obj(p, "Group " + std::to_string(group_id));
262             auto group_props = GetGroupProps(inst, group);
263             {
264                 ObjectWrapper obj(p, "Properties");
265                 {
266                     ArrayWrapper arr(p, "physicalDevices", group.physicalDeviceCount);
267                     int id = 0;
268                     for (auto &prop : group_props) {
269                         p.PrintString(std::string(prop.deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(id++)) + ")");
270                     }
271                 }
272                 p.PrintKeyValue("subsetAllocation", group.subsetAllocation);
273             }
274             p.AddNewline();
275
276             auto group_capabilities = GetGroupCapabilities(inst, group);
277             if (group_capabilities.first == false) {
278                 p.PrintKeyString("Present Capabilities",
279                                  "Group does not support VK_KHR_device_group, skipping printing present capabilities");
280             } else {
281                 ObjectWrapper obj(p, "Present Capabilities");
282                 for (uint32_t i = 0; i < group.physicalDeviceCount; i++) {
283                     ObjectWrapper obj(
284                         p, std::string(group_props[i].deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(i)) + ")");
285                     ArrayWrapper arr(p, "Can present images from the following devices", group.physicalDeviceCount);
286
287                     for (uint32_t j = 0; j < group.physicalDeviceCount; j++) {
288                         uint32_t mask = 1 << j;
289                         if (group_capabilities.second.presentMask[i] & mask) {
290                             p.PrintString(std::string(group_props[j].deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(j)) +
291                                           ")");
292                         }
293                     }
294                 }
295                 DumpVkDeviceGroupPresentModeFlagsKHR(p, "Present modes", group_capabilities.second.modes);
296             }
297             p.AddNewline();
298             group_id++;
299         }
300         p.AddNewline();
301     }
302 }
303
304 void GpuDumpProps(Printer &p, AppGpu &gpu) {
305     auto props = gpu.GetDeviceProperties();
306     p.SetSubHeader();
307     {
308         ObjectWrapper obj(p, "VkPhysicalDeviceProperties");
309         p.PrintKeyValue("apiVersion", props.apiVersion, 14, VkVersionString(props.apiVersion));
310         p.PrintKeyValue("driverVersion", props.driverVersion, 14, to_hex_str(props.driverVersion));
311         p.PrintKeyString("vendorID", to_hex_str(props.vendorID), 14);
312         p.PrintKeyString("deviceID", to_hex_str(props.deviceID), 14);
313         p.PrintKeyString("deviceType", VkPhysicalDeviceTypeString(props.deviceType), 14);
314         p.PrintKeyString("deviceName", props.deviceName, 14);
315         if (p.Type() == OutputType::vkconfig_output) {
316             ArrayWrapper arr(p, "pipelineCacheUUID", VK_UUID_SIZE);
317             for (uint32_t i = 0; i < VK_UUID_SIZE; ++i) {
318                 p.PrintElement(static_cast<uint32_t>(props.pipelineCacheUUID[i]));
319             }
320         }
321     }
322     p.AddNewline();
323     DumpVkPhysicalDeviceLimits(p, "VkPhysicalDeviceLimits",
324                                gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)
325                                    ? gpu.props2.properties.limits
326                                    : gpu.props.limits);
327     p.AddNewline();
328     DumpVkPhysicalDeviceSparseProperties(p, "VkPhysicalDeviceSparseProperties",
329                                          gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)
330                                              ? gpu.props2.properties.sparseProperties
331                                              : gpu.props.sparseProperties);
332     p.AddNewline();
333     if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
334         void *place = gpu.props2.pNext;
335         chain_iterator_phys_device_props2(p, gpu.inst, gpu, place, gpu.api_version);
336         p.AddNewline();
337     }
338 }
339 void GpuDumpPropsJson(Printer &p, AppGpu &gpu) {
340     auto props = gpu.GetDeviceProperties();
341     ObjectWrapper obj(p, "VkPhysicalDeviceProperties");
342     p.PrintKeyValue("apiVersion", props.apiVersion, 14, VkVersionString(props.apiVersion));
343     p.PrintKeyValue("driverVersion", props.driverVersion, 14, to_hex_str(props.driverVersion));
344     p.PrintKeyValue("vendorID", props.vendorID, 14);
345     p.PrintKeyValue("deviceID", props.deviceID, 14);
346     p.PrintKeyValue("deviceType", props.deviceType, 14);
347     p.PrintKeyString("deviceName", props.deviceName, 14);
348     {
349         ArrayWrapper arr(p, "pipelineCacheUUID", VK_UUID_SIZE);
350         for (uint32_t i = 0; i < VK_UUID_SIZE; ++i) {
351             p.PrintElement(static_cast<uint32_t>(props.pipelineCacheUUID[i]));
352         }
353     }
354
355     DumpVkPhysicalDeviceLimits(p, "VkPhysicalDeviceLimits",
356                                gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)
357                                    ? gpu.props2.properties.limits
358                                    : gpu.props.limits);
359     DumpVkPhysicalDeviceSparseProperties(p, "VkPhysicalDeviceSparseProperties",
360                                          gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)
361                                              ? gpu.props2.properties.sparseProperties
362                                              : gpu.props.sparseProperties);
363 }
364
365 void GpuDumpQueueProps(Printer &p, std::vector<SurfaceExtension> &surfaces, AppQueueFamilyProperties &queue) {
366     p.SetSubHeader().SetElementIndex(static_cast<int>(queue.queue_index));
367     ObjectWrapper obj(p, "queueProperties");
368     if (p.Type() == OutputType::vkconfig_output) {
369         DumpVkExtent3D(p, "minImageTransferGranularity", queue.props.minImageTransferGranularity);
370     } else {
371         p.PrintKeyValue("minImageTransferGranularity", queue.props.minImageTransferGranularity, 27);
372     }
373     p.PrintKeyValue("queueCount", queue.props.queueCount, 27);
374     p.PrintKeyString("queueFlags", VkQueueFlagsString(queue.props.queueFlags), 27);
375     p.PrintKeyValue("timestampValidBits", queue.props.timestampValidBits, 27);
376
377     if (queue.is_present_platform_agnostic) {
378         p.PrintKeyString("present support", queue.platforms_support_present ? "true" : "false", 27);
379     } else {
380         size_t width = 0;
381         for (auto &surface : surfaces) {
382             if (surface.name.size() > width) width = surface.name.size();
383         }
384         ObjectWrapper obj(p, "present support");
385         for (auto &surface : surfaces) {
386             p.PrintKeyString(surface.name, surface.supports_present ? "true" : "false", width);
387         }
388     }
389
390     p.AddNewline();
391 }
392
393 void GpuDumpQueuePropsJson(Printer &p, std::vector<SurfaceExtension> &surfaces, AppQueueFamilyProperties &queue) {
394     ObjectWrapper obj(p, "");
395     DumpVkExtent3D(p, "minImageTransferGranularity", queue.props.minImageTransferGranularity);
396     p.PrintKeyValue("queueCount", queue.props.queueCount, 27);
397     p.PrintKeyValue("queueFlags", queue.props.queueFlags, 27);
398     p.PrintKeyValue("timestampValidBits", queue.props.timestampValidBits, 27);
399 }
400
401 // This prints a number of bytes in a human-readable format according to prefixes of the International System of Quantities (ISQ),
402 // defined in ISO/IEC 80000. The prefixes used here are not SI prefixes, but rather the binary prefixes based on powers of 1024
403 // (kibi-, mebi-, gibi- etc.).
404 #define kBufferSize 32
405
406 std::string NumToNiceStr(const size_t sz) {
407     const char prefixes[] = "KMGTPEZY";
408     char buf[kBufferSize];
409     int which = -1;
410     double result = (double)sz;
411     while (result > 1024 && which < 7) {
412         result /= 1024;
413         ++which;
414     }
415
416     char unit[] = "\0i";
417     if (which >= 0) {
418         unit[0] = prefixes[which];
419     }
420 #ifdef _WIN32
421     _snprintf_s(buf, kBufferSize * sizeof(char), kBufferSize, "%.2f %sB", result, unit);
422 #else
423     snprintf(buf, kBufferSize, "%.2f %sB", result, unit);
424 #endif
425     return std::string(buf);
426 }
427
428 std::string append_human_readible(VkDeviceSize memory) {
429     return std::to_string(memory) + " (" + to_hex_str(memory) + ") (" + NumToNiceStr(static_cast<size_t>(memory)) + ")";
430 }
431
432 void GpuDumpMemoryProps(Printer &p, AppGpu &gpu) {
433     p.SetHeader();
434     ObjectWrapper obj(p, "VkPhysicalDeviceMemoryProperties");
435     IndentWrapper indent(p);
436     {
437         ObjectWrapper obj(p, "memoryHeaps", gpu.memory_props.memoryHeapCount);
438
439         for (uint32_t i = 0; i < gpu.memory_props.memoryHeapCount; ++i) {
440             p.SetElementIndex(static_cast<int>(i));
441             ObjectWrapper obj(p, "memoryHeaps");
442
443             p.PrintKeyString("size", append_human_readible(gpu.memory_props.memoryHeaps[i].size), 6);
444             p.PrintKeyString("budget", append_human_readible(gpu.heapBudget[i]), 6);
445             p.PrintKeyString("usage", append_human_readible(gpu.heapUsage[i]), 6);
446             DumpVkMemoryHeapFlags(p, "flags", gpu.memory_props.memoryHeaps[i].flags, 6);
447         }
448     }
449     {
450         ObjectWrapper obj(p, "memoryTypes", gpu.memory_props.memoryTypeCount);
451
452         for (uint32_t i = 0; i < gpu.memory_props.memoryTypeCount; ++i) {
453             p.SetElementIndex(static_cast<int>(i));
454             ObjectWrapper obj(p, "memoryTypes");
455             p.PrintKeyValue("heapIndex", gpu.memory_props.memoryTypes[i].heapIndex, 13);
456
457             auto flags = gpu.memory_props.memoryTypes[i].propertyFlags;
458             DumpVkMemoryPropertyFlags(p, "propertyFlags = " + to_hex_str(flags), flags);
459
460             ObjectWrapper usable_for(p, "usable for");
461             const uint32_t memtype_bit = 1U << i;
462
463             // only linear and optimal tiling considered
464             std::vector<VkFormat> tiling_optimal_formats;
465             std::vector<VkFormat> tiling_linear_formats;
466             for (auto &image_tiling : gpu.memory_image_support_types) {
467                 p.SetOpenDetails();
468                 ArrayWrapper arr(p, VkImageTilingString(VkImageTiling(image_tiling.tiling)), -1);
469                 bool has_any_support_types = false;
470                 bool regular = false;
471                 bool transient = false;
472                 bool sparse = false;
473                 for (auto &image_format : image_tiling.formats) {
474                     if (image_format.type_support.size() > 0) {
475                         bool has_a_support_type = false;
476                         for (auto &img_type : image_format.type_support) {
477                             if (img_type.Compatible(memtype_bit)) {
478                                 has_a_support_type = true;
479                                 has_any_support_types = true;
480                                 if (img_type.type == ImageTypeSupport::Type::regular) regular = true;
481                                 if (img_type.type == ImageTypeSupport::Type::transient) transient = true;
482                                 if (img_type.type == ImageTypeSupport::Type::sparse) sparse = true;
483                             }
484                         }
485                         if (has_a_support_type) {
486                             if (image_format.format == color_format) {
487                                 p.PrintString("color images");
488                             } else {
489                                 p.PrintString(VkFormatString(image_format.format));
490                             }
491                         }
492                     }
493                 }
494                 if (!has_any_support_types) {
495                     p.PrintString("None");
496                 } else {
497                     if (regular && !transient && sparse) p.PrintString("(non-transient)");
498                     if (regular && transient && !sparse) p.PrintString("(non-sparse)");
499                     if (regular && !transient && !sparse) p.PrintString("(non-sparse, non-transient)");
500                     if (!regular && transient && sparse) p.PrintString("(sparse and transient only)");
501                     if (!regular && !transient && sparse) p.PrintString("(sparse only)");
502                     if (!regular && transient && !sparse) p.PrintString("(transient only)");
503                 }
504             }
505         }
506     }
507     p.AddNewline();
508 }
509
510 void GpuDumpMemoryPropsJson(Printer &p, AppGpu &gpu) {
511     ObjectWrapper obj(p, "VkPhysicalDeviceMemoryProperties");
512     {
513         ArrayWrapper arr(p, "memoryHeaps", gpu.memory_props.memoryHeapCount);
514         for (uint32_t i = 0; i < gpu.memory_props.memoryHeapCount; ++i) {
515             ObjectWrapper obj(p, "");
516             p.PrintKeyValue("flags", gpu.memory_props.memoryHeaps[i].flags);
517             p.PrintKeyValue("size", gpu.memory_props.memoryHeaps[i].size);
518         }
519     }
520     {
521         ArrayWrapper arr(p, "memoryTypes", gpu.memory_props.memoryTypeCount);
522         for (uint32_t i = 0; i < gpu.memory_props.memoryTypeCount; ++i) {
523             ObjectWrapper obj(p, "");
524             p.PrintKeyValue("heapIndex", gpu.memory_props.memoryTypes[i].heapIndex, 13);
525             p.PrintKeyValue("propertyFlags", gpu.memory_props.memoryTypes[i].propertyFlags, 13);
526         }
527     }
528 }
529
530 void GpuDumpFeatures(Printer &p, AppGpu &gpu) {
531     p.SetHeader();
532     DumpVkPhysicalDeviceFeatures(p, "VkPhysicalDeviceFeatures", gpu.features);
533     p.AddNewline();
534     if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
535         void *place = gpu.features2.pNext;
536         chain_iterator_phys_device_features2(p, gpu, place, gpu.api_version);
537     }
538 }
539
540 void GpuDumpFormatProperty(Printer &p, VkFormat fmt, VkFormatProperties prop) {
541     switch (p.Type()) {
542         case OutputType::text: {
543             ObjectWrapper obj(p, "Properties");
544             DumpVkFormatFeatureFlags(p, "linearTiling", prop.linearTilingFeatures);
545             DumpVkFormatFeatureFlags(p, "optimalTiling", prop.optimalTilingFeatures);
546             DumpVkFormatFeatureFlags(p, "bufferFeatures", prop.bufferFeatures);
547             break;
548         }
549         case OutputType::html: {
550             p.SetTitleAsType();
551             ObjectWrapper obj(p, VkFormatString(fmt));
552             p.SetOpenDetails();
553             DumpVkFormatFeatureFlags(p, "linearTiling", prop.linearTilingFeatures);
554             p.SetOpenDetails();
555             DumpVkFormatFeatureFlags(p, "optimalTiling", prop.optimalTilingFeatures);
556             p.SetOpenDetails();
557             DumpVkFormatFeatureFlags(p, "bufferFeatures", prop.bufferFeatures);
558             break;
559         }
560         case OutputType::json: {
561             ObjectWrapper obj(p, "");
562             p.PrintKeyValue("formatID", fmt);
563             p.PrintKeyValue("linearTilingFeatures", prop.linearTilingFeatures);
564             p.PrintKeyValue("optimalTilingFeatures", prop.optimalTilingFeatures);
565             p.PrintKeyValue("bufferFeatures", prop.bufferFeatures);
566             break;
567         }
568         case OutputType::vkconfig_output: {
569             ObjectWrapper obj(p, VkFormatString(fmt));
570             DumpVkFormatFeatureFlags(p, "linearTiling", prop.linearTilingFeatures);
571             DumpVkFormatFeatureFlags(p, "optimalTiling", prop.optimalTilingFeatures);
572             DumpVkFormatFeatureFlags(p, "bufferFeatures", prop.bufferFeatures);
573             break;
574         }
575     }
576 }
577
578 void GpuDumpToolingInfo(Printer &p, AppGpu &gpu) {
579     auto tools = GetToolingInfo(gpu);
580     if (tools.size() > 0) {
581         p.SetSubHeader();
582         ObjectWrapper obj(p, "Tooling Info");
583         for (auto tool : tools) {
584             DumpVkPhysicalDeviceToolPropertiesEXT(p, tool.name, tool);
585             p.AddNewline();
586         }
587     }
588 }
589
590 void GpuDevDump(Printer &p, AppGpu &gpu) {
591     p.SetHeader();
592     ObjectWrapper obj(p, "Format Properties");
593     IndentWrapper indent(p);
594
595     if (p.Type() == OutputType::text) {
596         auto fmtPropMap = FormatPropMap(gpu);
597
598         int counter = 0;
599         std::vector<VkFormat> unsupported_formats;
600         for (auto &prop : fmtPropMap) {
601             VkFormatProperties props;
602             props.linearTilingFeatures = prop.first.linear;
603             props.optimalTilingFeatures = prop.first.optimal;
604             props.bufferFeatures = prop.first.buffer;
605             if (props.linearTilingFeatures == 0 && props.optimalTilingFeatures == 0 && props.bufferFeatures == 0) {
606                 unsupported_formats = prop.second;
607                 continue;
608             }
609
610             p.SetElementIndex(counter++);
611             ObjectWrapper obj(p, "Common Format Group");
612             IndentWrapper indent(p);
613             {
614                 ArrayWrapper arr(p, "Formats", prop.second.size());
615                 for (auto &fmt : prop.second) {
616                     p.SetAsType().PrintString(VkFormatString(fmt));
617                 }
618             }
619             GpuDumpFormatProperty(p, VK_FORMAT_UNDEFINED, props);
620             p.AddNewline();
621         }
622
623         ArrayWrapper arr(p, "Unsupported Formats", unsupported_formats.size());
624         for (auto &fmt : unsupported_formats) {
625             p.SetAsType().PrintString(VkFormatString(fmt));
626         }
627     } else {
628         for (auto &format : gpu.supported_format_ranges) {
629             if (gpu.FormatRangeSupported(format)) {
630                 for (int32_t fmt_counter = format.first_format; fmt_counter <= format.last_format; ++fmt_counter) {
631                     VkFormat fmt = static_cast<VkFormat>(fmt_counter);
632
633                     VkFormatProperties props;
634                     gpu.inst.dll.fp_vkGetPhysicalDeviceFormatProperties(gpu.phys_device, fmt, &props);
635
636                     GpuDumpFormatProperty(p, fmt, props);
637                 }
638             }
639         }
640     }
641
642     p.AddNewline();
643 }
644
645 void GpuDevDumpJson(Printer &p, AppGpu &gpu) {
646     ArrayWrapper arr(p, "ArrayOfVkFormatProperties");
647     for (auto &format : gpu.supported_format_ranges) {
648         if (gpu.FormatRangeSupported(format)) {
649             for (int32_t fmt_counter = format.first_format; fmt_counter <= format.last_format; ++fmt_counter) {
650                 VkFormat fmt = static_cast<VkFormat>(fmt_counter);
651
652                 VkFormatProperties props;
653                 gpu.inst.dll.fp_vkGetPhysicalDeviceFormatProperties(gpu.phys_device, fmt, &props);
654
655                 // don't print format properties that are unsupported
656                 if ((props.linearTilingFeatures || props.optimalTilingFeatures || props.bufferFeatures) == 0) continue;
657
658                 GpuDumpFormatProperty(p, fmt, props);
659             }
660         }
661     }
662 }
663 // Print gpu info for text, html, & vkconfig_output
664 // Uses a seperate function than schema-json for clarity
665 void DumpGpu(Printer &p, AppGpu &gpu, bool show_formats) {
666     ObjectWrapper obj(p, "GPU" + std::to_string(gpu.id));
667     IndentWrapper indent(p);
668
669     GpuDumpProps(p, gpu);
670     DumpExtensions(p, "Device", gpu.device_extensions);
671     p.AddNewline();
672     {
673         p.SetHeader();
674         ObjectWrapper obj(p, "VkQueueFamilyProperties");
675         for (uint32_t i = 0; i < gpu.queue_count; i++) {
676             AppQueueFamilyProperties queue_props = AppQueueFamilyProperties(gpu, i);
677             GpuDumpQueueProps(p, gpu.inst.surface_extensions, queue_props);
678         }
679     }
680     GpuDumpMemoryProps(p, gpu);
681     GpuDumpFeatures(p, gpu);
682     GpuDumpToolingInfo(p, gpu);
683
684     if (p.Type() != OutputType::text || show_formats) {
685         GpuDevDump(p, gpu);
686     }
687
688     p.AddNewline();
689 }
690
691 // Print gpu info for json
692 void DumpGpuJson(Printer &p, AppGpu &gpu) {
693     GpuDumpPropsJson(p, gpu);
694     {
695         ArrayWrapper arr(p, "ArrayOfVkQueueFamilyProperties");
696         for (uint32_t i = 0; i < gpu.queue_count; i++) {
697             AppQueueFamilyProperties queue_props = AppQueueFamilyProperties(gpu, i);
698             GpuDumpQueuePropsJson(p, gpu.inst.surface_extensions, queue_props);
699         }
700     }
701     {
702         ArrayWrapper arr(p, "ArrayOfVkExtensionProperties");
703         for (auto &ext : gpu.device_extensions) {
704             p.PrintExtension(ext.extensionName, ext.specVersion);
705         }
706     }
707
708     GpuDumpMemoryPropsJson(p, gpu);
709     DumpVkPhysicalDeviceFeatures(p, "VkPhysicalDeviceFeatures", gpu.features);
710     GpuDevDumpJson(p, gpu);
711 }
712
713 // Print summary of system
714 void DumpSummaryInstance(Printer &p, AppInstance &inst) {
715     p.SetSubHeader();
716     DumpExtensions(p, "Instance", inst.global_extensions, true);
717     p.AddNewline();
718
719     p.SetSubHeader();
720     ArrayWrapper arr(p, "Instance Layers", inst.global_layers.size());
721     IndentWrapper indent(p);
722     std::sort(inst.global_layers.begin(), inst.global_layers.end(), [](LayerExtensionList &left, LayerExtensionList &right) -> int {
723         return std::strncmp(left.layer_properties.layerName, right.layer_properties.layerName, VK_MAX_DESCRIPTION_SIZE) < 0;
724     });
725     size_t layer_name_max = 0;
726     size_t layer_desc_max = 0;
727     size_t layer_version_max = 0;
728
729     // find max of each type to align everything in columns
730     for (auto &layer : inst.global_layers) {
731         auto props = layer.layer_properties;
732         layer_name_max = std::max(layer_name_max, strlen(props.layerName));
733         layer_desc_max = std::max(layer_desc_max, strlen(props.description));
734         layer_version_max = std::max(layer_version_max, VkVersionString(layer.layer_properties.specVersion).size());
735     }
736     for (auto &layer : inst.global_layers) {
737         auto v_str = VkVersionString(layer.layer_properties.specVersion);
738         auto props = layer.layer_properties;
739
740         auto name_padding = std::string(layer_name_max - strlen(props.layerName), ' ');
741         auto desc_padding = std::string(layer_desc_max - strlen(props.description), ' ');
742         auto version_padding = std::string(layer_version_max - v_str.size(), ' ');
743         p.PrintString(std::string(props.layerName) + name_padding + " " + props.description + desc_padding + " " + v_str + " " +
744                       version_padding + " version " + std::to_string(props.implementationVersion));
745     }
746     p.AddNewline();
747 }
748
749 void DumpSummaryGPU(Printer &p, AppGpu &gpu) {
750     ObjectWrapper obj(p, "GPU" + std::to_string(gpu.id));
751     auto props = gpu.GetDeviceProperties();
752     p.PrintKeyValue("apiVersion", props.apiVersion, 18, VkVersionString(props.apiVersion));
753     p.PrintKeyValue("driverVersion", props.driverVersion, 18, to_hex_str(props.driverVersion));
754     p.PrintKeyString("vendorID", to_hex_str(props.vendorID), 18);
755     p.PrintKeyString("deviceID", to_hex_str(props.deviceID), 18);
756     p.PrintKeyString("deviceType", VkPhysicalDeviceTypeString(props.deviceType), 18);
757     p.PrintKeyString("deviceName", props.deviceName, 18);
758
759     if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) &&
760         (gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME) || gpu.api_version.minor >= 2)) {
761         void *place = gpu.props2.pNext;
762         while (place) {
763             struct VkStructureHeader *structure = (struct VkStructureHeader *)place;
764             if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES) {
765                 VkPhysicalDeviceDriverProperties *props = (VkPhysicalDeviceDriverProperties *)structure;
766                 DumpVkDriverId(p, "driverID", props->driverID, 18);
767                 p.PrintKeyString("driverName", props->driverName, 18);
768                 p.PrintKeyString("driverInfo", props->driverInfo, 18);
769                 DumpVkConformanceVersion(p, "conformanceVersion", props->conformanceVersion, 18);
770             }
771             place = structure->pNext;
772         }
773     }
774 }
775
776 #if defined(VK_ENABLE_BETA_EXTENSIONS)
777 void DumpPortability(Printer &p, AppGpu &gpu) {
778     if (gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
779         if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
780             void *props_place = gpu.props2.pNext;
781             while (props_place) {
782                 struct VkStructureHeader *structure = (struct VkStructureHeader *)props_place;
783                 if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR) {
784                     VkPhysicalDevicePortabilitySubsetPropertiesKHR *props =
785                         (VkPhysicalDevicePortabilitySubsetPropertiesKHR *)structure;
786                     DumpVkPhysicalDevicePortabilitySubsetPropertiesKHR(p, "VkPhysicalDevicePortabilitySubsetPropertiesKHR", *props);
787                     break;
788                 }
789                 props_place = structure->pNext;
790             }
791
792             void *feats_place = gpu.features2.pNext;
793             while (feats_place) {
794                 struct VkStructureHeader *structure = (struct VkStructureHeader *)feats_place;
795                 if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR) {
796                     VkPhysicalDevicePortabilitySubsetFeaturesKHR *features =
797                         (VkPhysicalDevicePortabilitySubsetFeaturesKHR *)structure;
798                     DumpVkPhysicalDevicePortabilitySubsetFeaturesKHR(p, "VkPhysicalDevicePortabilitySubsetFeaturesKHR", *features);
799                     break;
800                 }
801                 feats_place = structure->pNext;
802             }
803         }
804     }
805 }
806 #endif  // defined(VK_ENABLE_BETA_EXTENSIONS)
807
808 // ============ Printing Logic ============= //
809
810 #ifdef _WIN32
811 // Enlarges the console window to have a large scrollback size.
812 static void ConsoleEnlarge() {
813     const HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
814
815     // make the console window bigger
816     CONSOLE_SCREEN_BUFFER_INFO csbi;
817     COORD buffer_size;
818     if (GetConsoleScreenBufferInfo(console_handle, &csbi)) {
819         buffer_size.X = csbi.dwSize.X + 30;
820         buffer_size.Y = 20000;
821         SetConsoleScreenBufferSize(console_handle, buffer_size);
822     }
823
824     SMALL_RECT r;
825     r.Left = r.Top = 0;
826     r.Right = csbi.dwSize.X - 1 + 30;
827     r.Bottom = 50;
828     SetConsoleWindowInfo(console_handle, true, &r);
829
830     // change the console window title
831     SetConsoleTitle(TEXT(app_short_name));
832 }
833 #endif
834
835 void print_usage(const char *argv0) {
836     std::cout << "\nvulkaninfo - Summarize Vulkan information in relation to the current environment.\n\n";
837     std::cout << "USAGE: " << argv0 << " [options]\n\n";
838     std::cout << "OPTIONS:\n";
839     std::cout << "-h, --help          Print this help.\n";
840     std::cout << "--html              Produce an html version of vulkaninfo output, saved as\n";
841     std::cout << "                    \"vulkaninfo.html\" in the directory in which the command\n";
842     std::cout << "                    is run.\n";
843     std::cout << "-j, --json          Produce a json version of vulkaninfo to standard output of the\n";
844     std::cout << "                    first gpu in the system conforming to the DevSim schema.\n";
845     std::cout << "--json=<gpu-number> For a multi-gpu system, a single gpu can be targetted by\n";
846     std::cout << "                    specifying the gpu-number associated with the gpu of \n";
847     std::cout << "                    interest. This number can be determined by running\n";
848     std::cout << "                    vulkaninfo without any options specified.\n";
849 #if defined(VK_ENABLE_BETA_EXTENSIONS)
850     std::cout << "--portability       Produce a json version of vulkaninfo to standard output of the first\n";
851     std::cout << "                    gpu in the system conforming to the DevSim Portability Subset schema.\n";
852     std::cout << "--portability=<N>   Produce the json output conforming to the DevSim Portability\n";
853     std::cout << "                    Subset Schema for the GPU specified to standard output,\n";
854     std::cout << "                    where N is the GPU desired.\n";
855 #endif  // defined(VK_ENABLE_BETA_EXTENSIONS)
856     std::cout << "--show-formats      Display the format properties of each physical device.\n";
857     std::cout << "                    Note: This option does not affect html or json output;\n";
858     std::cout << "                    they will always print format properties.\n\n";
859     std::cout << "--summary           Show a summary of the instance and GPU's on a system.\n\n";
860 }
861
862 int main(int argc, char **argv) {
863 #ifdef _WIN32
864     if (ConsoleIsExclusive()) ConsoleEnlarge();
865     if (!LoadUser32Dll()) {
866         fprintf(stderr, "Failed to load user32.dll library!\n");
867         WAIT_FOR_CONSOLE_DESTROY;
868         exit(1);
869     }
870 #endif
871
872     uint32_t selected_gpu = 0;
873     bool show_formats = false;
874     char *output_path = nullptr;
875
876     // Combinations of output: html only, html AND json, json only, human readable only
877     for (int i = 1; i < argc; ++i) {
878         // A internal-use-only format for communication with the Vulkan Configurator tool
879         // Usage "--vkconfig_output <path>"
880         if (0 == strcmp("--vkconfig_output", argv[i]) && argc > (i + 1)) {
881             human_readable_output = false;
882             vkconfig_output = true;
883             output_path = argv[i + 1];
884             ++i;
885         } else if (strncmp("--json", argv[i], 6) == 0 || strcmp(argv[i], "-j") == 0) {
886             if (strlen(argv[i]) > 7 && strncmp("--json=", argv[i], 7) == 0) {
887                 selected_gpu = static_cast<uint32_t>(strtol(argv[i] + 7, nullptr, 10));
888             }
889             human_readable_output = false;
890             json_output = true;
891             portability_json = false;
892 #if defined(VK_ENABLE_BETA_EXTENSIONS)
893         } else if (strncmp("--portability", argv[i], 13) == 0) {
894             if (strlen(argv[i]) > 14 && strncmp("--portability=", argv[i], 14) == 0) {
895                 selected_gpu = static_cast<uint32_t>(strtol(argv[i] + 14, nullptr, 10));
896             }
897             human_readable_output = false;
898             portability_json = true;
899             json_output = false;
900 #endif  // defined(VK_ENABLE_BETA_EXTENSIONS)
901         } else if (strcmp(argv[i], "--summary") == 0) {
902             summary = true;
903         } else if (strcmp(argv[i], "--html") == 0) {
904             human_readable_output = false;
905             html_output = true;
906         } else if (strcmp(argv[i], "--show-formats") == 0) {
907             show_formats = true;
908         } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
909             print_usage(argv[0]);
910             return 1;
911         } else {
912             print_usage(argv[0]);
913             return 1;
914         }
915     }
916     std::vector<std::unique_ptr<Printer>> printers;
917     std::ostream out(std::cout.rdbuf());
918     std::ofstream html_out;
919     std::ofstream vkconfig_out;
920
921     // if any essential vulkan call fails, it throws an exception
922     try {
923         AppInstance instance = {};
924         SetupWindowExtensions(instance);
925
926         auto pNext_chains = get_chain_infos();
927
928         auto phys_devices = instance.FindPhysicalDevices();
929
930         std::vector<std::unique_ptr<AppSurface>> surfaces;
931 #if defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_WIN32_KHR) ||      \
932     defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) || defined(VK_USE_PLATFORM_WAYLAND_KHR) || \
933     defined(VK_USE_PLATFORM_DIRECTFB_EXT)
934         for (auto &surface_extension : instance.surface_extensions) {
935             surface_extension.create_window(instance);
936             surface_extension.surface = surface_extension.create_surface(instance);
937             for (auto &phys_device : phys_devices) {
938                 surfaces.push_back(std::unique_ptr<AppSurface>(
939                     new AppSurface(instance, phys_device, surface_extension, pNext_chains.surface_capabilities2)));
940             }
941         }
942 #endif
943
944         std::vector<std::unique_ptr<AppGpu>> gpus;
945
946         uint32_t gpu_counter = 0;
947         for (auto &phys_device : phys_devices) {
948             gpus.push_back(std::unique_ptr<AppGpu>(new AppGpu(instance, gpu_counter++, phys_device, pNext_chains)));
949         }
950
951         if (selected_gpu >= gpus.size()) {
952             std::cout << "The selected gpu (" << selected_gpu << ") is not a valid GPU index. ";
953             if (gpus.size() == 1)
954                 std::cout << "The only available GPU selection is 0.\n";
955             else
956                 std::cout << "The available GPUs are in the range of 0 to " << gpus.size() - 1 << ".\n";
957             return 0;
958         }
959
960         if (human_readable_output) {
961             printers.push_back(std::unique_ptr<Printer>(new Printer(OutputType::text, out, selected_gpu, instance.vk_version)));
962         }
963         if (html_output) {
964             html_out = std::ofstream("vulkaninfo.html");
965             printers.push_back(
966                 std::unique_ptr<Printer>(new Printer(OutputType::html, html_out, selected_gpu, instance.vk_version)));
967         }
968         if (json_output) {
969             std::string start_string =
970                 std::string("{\n\t\"$schema\": \"https://schema.khronos.org/vulkan/devsim_1_0_0.json#\",\n") +
971                 "\t\"comments\": {\n\t\t\"desc\": \"JSON configuration file describing GPU " + std::to_string(selected_gpu) +
972                 ". Generated using the vulkaninfo program.\",\n\t\t\"vulkanApiVersion\": \"" +
973                 VkVersionString(instance.vk_version) + "\"\n" + "\t}";
974             printers.push_back(
975                 std::unique_ptr<Printer>(new Printer(OutputType::json, out, selected_gpu, instance.vk_version, start_string)));
976         }
977 #if defined(VK_ENABLE_BETA_EXTENSIONS)
978         if (portability_json) {
979             if (!gpus.at(selected_gpu)->CheckPhysicalDeviceExtensionIncluded(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
980                 std::cerr << "Cannot create a json because the current selected GPU (" << selected_gpu
981                           << ") does not support the VK_KHR_portability_subset extension.\n";
982             } else {
983                 std::string start_string =
984                     std::string(
985                         "{\n\t\"$schema\": "
986                         "\"https://schema.khronos.org/vulkan/devsim_VK_KHR_portability_subset-provisional-1.json#\",\n") +
987                     "\t\"comments\": {\n\t\t\"desc\": \"JSON configuration file describing GPU " + std::to_string(selected_gpu) +
988                     "'s portability features and properties. Generated using the vulkaninfo program.\",\n\t\t\"vulkanApiVersion\": "
989                     "\"" +
990                     VkVersionString(instance.vk_version) + "\"\n" + "\t}";
991                 printers.push_back(
992                     std::unique_ptr<Printer>(new Printer(OutputType::json, out, selected_gpu, instance.vk_version, start_string)));
993             }
994         }
995 #endif  // defined(VK_ENABLE_BETA_EXTENSIONS)
996         if (vkconfig_output) {
997 #ifdef WIN32
998             vkconfig_out = std::ofstream(std::string(output_path) + "\\vulkaninfo.json");
999 #else
1000             vkconfig_out = std::ofstream(std::string(output_path) + "/vulkaninfo.json");
1001 #endif
1002             std::string start_string = "{\n\t\"Vulkan Instance Version\": \"" + VkVersionString(instance.vk_version) + "\"";
1003             printers.push_back(std::unique_ptr<Printer>(
1004                 new Printer(OutputType::vkconfig_output, vkconfig_out, selected_gpu, instance.vk_version, start_string)));
1005         }
1006
1007         for (auto &p : printers) {
1008             if (summary) {
1009                 DumpSummaryInstance(*p.get(), instance);
1010                 p->SetHeader();
1011                 ObjectWrapper obj(*p, "Devices");
1012                 IndentWrapper indent(*p);
1013                 for (auto &gpu : gpus) {
1014                     DumpSummaryGPU(*p.get(), *gpu.get());
1015                 }
1016             } else if (p->Type() == OutputType::json) {
1017                 if (portability_json) {
1018 #if defined(VK_ENABLE_BETA_EXTENSIONS)
1019                     DumpPortability(*p.get(), *gpus.at(selected_gpu).get());
1020 #endif  // defined(VK_ENABLE_BETA_EXTENSIONS)
1021                 } else if (json_output) {
1022                     DumpLayers(*p.get(), instance.global_layers, gpus);
1023                     DumpGpuJson(*p.get(), *gpus.at(selected_gpu).get());
1024                 }
1025             } else {
1026                 // text, html, vkconfig_output
1027                 p->SetHeader();
1028                 DumpExtensions(*p.get(), "Instance", instance.global_extensions);
1029                 p->AddNewline();
1030
1031                 DumpLayers(*p.get(), instance.global_layers, gpus);
1032
1033 #if defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_WIN32_KHR) ||      \
1034     defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) || defined(VK_USE_PLATFORM_WAYLAND_KHR) || \
1035     defined(VK_USE_PLATFORM_DIRECTFB_EXT)
1036                 DumpPresentableSurfaces(*p.get(), instance, gpus, surfaces);
1037 #endif
1038                 DumpGroups(*p.get(), instance);
1039
1040                 p->SetHeader();
1041                 ObjectWrapper obj(*p, "Device Properties and Extensions");
1042                 IndentWrapper indent(*p);
1043
1044                 for (auto &gpu : gpus) {
1045                     DumpGpu(*p.get(), *gpu.get(), show_formats);
1046                 }
1047             }
1048         }
1049
1050 #if defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_WIN32_KHR) ||      \
1051     defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) || defined(VK_USE_PLATFORM_WAYLAND_KHR) || \
1052     defined(VK_USE_PLATFORM_DIRECTFB_EXT)
1053
1054         for (auto &surface_extension : instance.surface_extensions) {
1055             AppDestroySurface(instance, surface_extension.surface);
1056             surface_extension.destroy_window(instance);
1057         }
1058 #endif
1059     } catch (std::exception &e) {
1060         // Print the error to stderr and leave all outputs in a valid state (mainly for json)
1061         std::cerr << "ERROR at " << e.what() << "\n";
1062         for (auto &p : printers) {
1063             if (p) {
1064                 p->FinishOutput();
1065             }
1066         }
1067     }
1068     // Call the printer's descrtuctor before the file handle gets closed
1069     for (auto &p : printers) {
1070         p.reset(nullptr);
1071     }
1072
1073     WAIT_FOR_CONSOLE_DESTROY;
1074 #ifdef _WIN32
1075     FreeUser32Dll();
1076 #endif
1077
1078     return 0;
1079 }