vulkaninfo: Add video profiles support
[KhronosGroup/Vulkan-Tools.git] / vulkaninfo / vulkaninfo.cpp
blob7de9b3080dab1e0eadc5004ccfc32df2af252097
1 /*
2 * Copyright (c) 2015-2021 The Khronos Group Inc.
3 * Copyright (c) 2015-2021 Valve Corporation
4 * Copyright (c) 2015-2021 LunarG, Inc.
5 * Copyright (c) 2023-2024 RasterGrid Kft.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
20 * Author: David Pinedo <david@lunarg.com>
21 * Author: Mark Lobodzinski <mark@lunarg.com>
22 * Author: Rene Lindsay <rene@lunarg.com>
23 * Author: Jeremy Kniager <jeremyk@lunarg.com>
24 * Author: Shannon McPherson <shannon@lunarg.com>
25 * Author: Bob Ellison <bob@lunarg.com>
26 * Author: Richard Wright <richard@lunarg.com>
27 * Author: Charles Giessen <charles@lunarg.com>
31 #ifdef _WIN32
32 #include <crtdbg.h>
33 #endif
34 #include "vulkaninfo.hpp"
36 // Used to sort the formats into buckets by their properties.
37 std::unordered_map<PropFlags, std::set<VkFormat>> FormatPropMap(AppGpu &gpu) {
38 std::unordered_map<PropFlags, std::set<VkFormat>> map;
39 for (const auto fmtRange : format_ranges) {
40 if (gpu.FormatRangeSupported(fmtRange)) {
41 for (int32_t fmt = fmtRange.first_format; fmt <= fmtRange.last_format; ++fmt) {
42 PropFlags pf = get_format_properties(gpu, static_cast<VkFormat>(fmt));
43 map[pf].insert(static_cast<VkFormat>(fmt));
47 return map;
50 // =========== Dump Functions ========= //
52 void DumpExtensions(Printer &p, std::string section_name, std::vector<VkExtensionProperties> extensions, bool do_indent = false) {
53 std::sort(extensions.begin(), extensions.end(), [](VkExtensionProperties &a, VkExtensionProperties &b) -> int {
54 return std::string(a.extensionName) < std::string(b.extensionName);
55 });
57 size_t max_length = 0;
58 for (const auto &ext : extensions) {
59 max_length = std::max(max_length, std::strlen(ext.extensionName));
61 #if defined(VK_ENABLE_BETA_EXTENSIONS)
62 const std::string portability_ext_name = VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME;
63 #endif // defined(VK_ENABLE_BETA_EXTENSIONS)
64 ObjectWrapper obj(p, section_name, extensions.size());
65 if (do_indent) p.IndentDecrease();
66 for (auto &ext : extensions) {
67 #if defined(VK_ENABLE_BETA_EXTENSIONS)
68 if (p.Type() == OutputType::json && portability_ext_name == ext.extensionName) continue;
69 #endif // defined(VK_ENABLE_BETA_EXTENSIONS)
70 p.PrintExtension(ext.extensionName, ext.specVersion, max_length);
72 if (do_indent) p.IndentIncrease();
75 void DumpLayers(Printer &p, std::vector<LayerExtensionList> layers, const std::vector<std::unique_ptr<AppGpu>> &gpus) {
76 std::sort(layers.begin(), layers.end(), [](LayerExtensionList &left, LayerExtensionList &right) -> int {
77 return std::strncmp(left.layer_properties.layerName, right.layer_properties.layerName, VK_MAX_DESCRIPTION_SIZE) < 0;
78 });
79 switch (p.Type()) {
80 case OutputType::text:
81 case OutputType::html: {
82 p.SetHeader();
83 ArrayWrapper arr_layers(p, "Layers", layers.size());
84 IndentWrapper indent(p);
86 for (auto &layer : layers) {
87 std::string v_str = APIVersion(layer.layer_properties.specVersion);
88 auto props = layer.layer_properties;
90 std::string header = p.DecorateAsType(props.layerName) + " (" + props.description + ") " API_NAME " version " +
91 p.DecorateAsValue(v_str) + ", layer version " +
92 p.DecorateAsValue(std::to_string(props.implementationVersion));
93 ObjectWrapper obj(p, header);
94 DumpExtensions(p, "Layer Extensions", layer.extension_properties);
96 ObjectWrapper arr_devices(p, "Devices", gpus.size());
97 for (auto &gpu : gpus) {
98 p.SetValueDescription(std::string(gpu->props.deviceName)).PrintKeyValue("GPU id", gpu->id);
99 auto exts = gpu->inst.AppGetPhysicalDeviceLayerExtensions(gpu->phys_device, props.layerName);
100 DumpExtensions(p, "Layer-Device Extensions", exts);
101 p.AddNewline();
104 break;
107 case OutputType::json: {
108 assert(false && "unimplemented");
109 break;
111 case OutputType::vkconfig_output: {
112 ObjectWrapper obj(p, "Layer Properties");
113 for (auto &layer : layers) {
114 ObjectWrapper obj_name(p, layer.layer_properties.layerName);
115 p.SetMinKeyWidth(21);
116 p.PrintKeyString("layerName", layer.layer_properties.layerName);
117 p.PrintKeyString("version", APIVersion(layer.layer_properties.specVersion).str());
118 p.PrintKeyValue("implementation version", layer.layer_properties.implementationVersion);
119 p.PrintKeyString("description", layer.layer_properties.description);
120 DumpExtensions(p, "Layer Extensions", layer.extension_properties);
121 ObjectWrapper obj_devices(p, "Devices");
122 for (auto &gpu : gpus) {
123 ObjectWrapper obj_gpu(p, gpu->props.deviceName);
124 p.SetValueDescription(std::string(gpu->props.deviceName)).PrintKeyValue("GPU id", gpu->id);
125 auto exts = gpu->inst.AppGetPhysicalDeviceLayerExtensions(gpu->phys_device, layer.layer_properties.layerName);
126 DumpExtensions(p, "Layer-Device Extensions", exts);
129 break;
134 void DumpSurfaceFormats(Printer &p, AppInstance &inst, AppSurface &surface) {
135 std::vector<VkSurfaceFormatKHR> formats;
136 if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) {
137 for (auto &format : surface.surf_formats2) {
138 formats.push_back(format.surfaceFormat);
140 } else {
141 for (auto &format : surface.surf_formats) {
142 formats.push_back(format);
145 ObjectWrapper obj(p, "Formats", formats.size());
146 int i = 0;
147 for (auto &format : formats) {
148 p.SetElementIndex(i++);
149 DumpVkSurfaceFormatKHR(p, "SurfaceFormat", format);
153 void DumpPresentModes(Printer &p, AppSurface &surface) {
154 ArrayWrapper arr(p, "Present Modes", surface.surf_present_modes.size());
155 for (auto &mode : surface.surf_present_modes) {
156 p.SetAsType().PrintString(VkPresentModeKHRString(mode));
160 void DumpSurfaceCapabilities(Printer &p, AppInstance &inst, AppGpu &gpu, AppSurface &surface) {
161 auto &surf_cap = surface.surface_capabilities;
162 p.SetSubHeader().SetIgnoreMinWidthInChild();
163 DumpVkSurfaceCapabilitiesKHR(p, "VkSurfaceCapabilitiesKHR", surf_cap);
165 if (inst.CheckExtensionEnabled(VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME)) {
166 p.SetSubHeader();
167 ObjectWrapper obj(p, "VkSurfaceCapabilities2EXT");
168 DumpVkSurfaceCounterFlagsEXT(p, "supportedSurfaceCounters", surface.surface_capabilities2_ext.supportedSurfaceCounters);
170 if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) {
171 chain_iterator_surface_capabilities2(p, inst, gpu, surface.surface_capabilities2_khr.pNext);
173 if (inst.CheckExtensionEnabled(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME)) {
174 p.SetSubHeader();
175 ObjectWrapper obj(p, "VK_EXT_surface_maintenance1");
176 for (auto &mode : surface.surf_present_modes) {
177 VkSurfacePresentModeEXT present_mode{};
178 present_mode.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT;
179 present_mode.presentMode = mode;
181 VkPhysicalDeviceSurfaceInfo2KHR surface_info{};
182 surface_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
183 surface_info.surface = surface.surface_extension.surface;
184 surface_info.pNext = &present_mode;
186 VkSurfacePresentModeCompatibilityEXT SurfacePresentModeCompatibilityEXT{};
187 SurfacePresentModeCompatibilityEXT.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT;
189 VkSurfacePresentScalingCapabilitiesEXT SurfacePresentScalingCapabilitiesEXT{};
190 SurfacePresentScalingCapabilitiesEXT.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT;
191 SurfacePresentScalingCapabilitiesEXT.pNext = &SurfacePresentModeCompatibilityEXT;
193 VkSurfaceCapabilities2KHR surface_caps2{};
194 surface_caps2.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR;
195 surface_caps2.pNext = &SurfacePresentScalingCapabilitiesEXT;
197 VkResult err = vkGetPhysicalDeviceSurfaceCapabilities2KHR(gpu.phys_device, &surface_info, &surface_caps2);
198 if (err != VK_SUCCESS) {
199 continue;
202 std::vector<VkPresentModeKHR> compatible_present_modes{SurfacePresentModeCompatibilityEXT.presentModeCount};
203 SurfacePresentModeCompatibilityEXT.pPresentModes = compatible_present_modes.data();
205 err = vkGetPhysicalDeviceSurfaceCapabilities2KHR(gpu.phys_device, &surface_info, &surface_caps2);
207 if (err == VK_SUCCESS) {
208 ObjectWrapper present_mode_obj(p, VkPresentModeKHRString(mode));
210 p.PrintKeyValue("minImageCount", surface_caps2.surfaceCapabilities.minImageCount);
211 p.PrintKeyValue("maxImageCount", surface_caps2.surfaceCapabilities.maxImageCount);
213 DumpVkSurfacePresentScalingCapabilitiesEXT(p, "VkSurfacePresentScalingCapabilitiesEXT",
214 SurfacePresentScalingCapabilitiesEXT);
215 DumpVkSurfacePresentModeCompatibilityEXT(p, "VkSurfacePresentModeCompatibilityEXT",
216 SurfacePresentModeCompatibilityEXT);
222 void DumpSurface(Printer &p, AppInstance &inst, AppGpu &gpu, AppSurface &surface, std::set<std::string> surface_types) {
223 ObjectWrapper obj(p, std::string("GPU id : ") + p.DecorateAsValue(std::to_string(gpu.id)) + " (" + gpu.props.deviceName + ")");
225 if (surface_types.size() == 0) {
226 p.SetAsType().PrintKeyString("Surface type", "No type found");
227 } else if (surface_types.size() == 1) {
228 p.SetAsType().PrintKeyString("Surface type", surface.surface_extension.name);
229 } else {
230 ArrayWrapper arr(p, "Surface types", surface_types.size());
231 for (auto &name : surface_types) {
232 p.PrintString(name);
236 DumpSurfaceFormats(p, inst, surface);
237 DumpPresentModes(p, surface);
238 DumpSurfaceCapabilities(p, inst, gpu, surface);
240 p.AddNewline();
243 struct SurfaceTypeGroup {
244 AppSurface *surface;
245 AppGpu *gpu;
246 std::set<std::string> surface_types;
249 bool operator==(AppSurface const &a, AppSurface const &b) {
250 return a.phys_device == b.phys_device && a.surf_present_modes == b.surf_present_modes && a.surf_formats == b.surf_formats &&
251 a.surf_formats2 == b.surf_formats2 && a.surface_capabilities == b.surface_capabilities &&
252 a.surface_capabilities2_khr == b.surface_capabilities2_khr && a.surface_capabilities2_ext == b.surface_capabilities2_ext;
255 #if defined(VULKANINFO_WSI_ENABLED)
256 void DumpPresentableSurfaces(Printer &p, AppInstance &inst, const std::vector<std::unique_ptr<AppGpu>> &gpus,
257 const std::vector<std::unique_ptr<AppSurface>> &surfaces) {
258 // Don't print anything if no surfaces are found
259 if (surfaces.size() == 0) return;
260 p.SetHeader();
261 ObjectWrapper obj(p, "Presentable Surfaces");
262 IndentWrapper indent(p);
264 std::vector<SurfaceTypeGroup> surface_list;
266 for (auto &surface : surfaces) {
267 auto exists = surface_list.end();
268 for (auto it = surface_list.begin(); it != surface_list.end(); it++) {
269 // check for duplicate surfaces that differ only by the surface extension
270 if (*(it->surface) == *(surface.get())) {
271 exists = it;
272 break;
275 if (exists != surface_list.end()) {
276 exists->surface_types.insert(surface.get()->surface_extension.name);
277 } else {
278 // find surface.phys_device's corresponding AppGpu
279 AppGpu *corresponding_gpu = nullptr;
280 for (auto &gpu : gpus) {
281 if (gpu->phys_device == surface->phys_device) corresponding_gpu = gpu.get();
283 if (corresponding_gpu != nullptr)
284 surface_list.push_back({surface.get(), corresponding_gpu, {surface.get()->surface_extension.name}});
287 for (auto &group : surface_list) {
288 DumpSurface(p, inst, *group.gpu, *group.surface, group.surface_types);
290 p.AddNewline();
292 #endif // defined(VULKANINFO_WSI_ENABLED)
294 void DumpGroups(Printer &p, AppInstance &inst) {
295 if (inst.CheckExtensionEnabled(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME)) {
296 auto groups = GetGroups(inst);
297 if (groups.size() == 0) {
298 p.SetHeader();
299 ObjectWrapper obj(p, "Groups");
300 p.PrintString("No Device Groups Found");
301 p.AddNewline();
302 return;
305 p.SetHeader();
306 ObjectWrapper obj_device_groups(p, "Device Groups");
307 IndentWrapper indent(p);
309 int group_id = 0;
310 for (auto &group : groups) {
311 ObjectWrapper obj_group(p, "Group " + std::to_string(group_id));
312 auto group_props = GetGroupProps(inst, group);
314 ObjectWrapper obj_properties(p, "Properties");
316 ArrayWrapper arr(p, "physicalDevices", group.physicalDeviceCount);
317 int id = 0;
318 for (auto &prop : group_props) {
319 p.PrintString(std::string(prop.deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(id++)) + ")");
322 p.PrintKeyValue("subsetAllocation", group.subsetAllocation);
324 p.AddNewline();
326 auto group_capabilities = GetGroupCapabilities(inst, group);
327 if (!group_capabilities) {
328 p.PrintKeyString("Present Capabilities",
329 "Group does not support VK_KHR_device_group, skipping printing present capabilities");
330 } else {
331 ObjectWrapper obj_caps(p, "Present Capabilities");
332 for (uint32_t i = 0; i < group.physicalDeviceCount; i++) {
333 ObjectWrapper obj_device(
334 p, std::string(group_props[i].deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(i)) + ")");
335 ArrayWrapper arr(p, "Can present images from the following devices", group.physicalDeviceCount);
337 for (uint32_t j = 0; j < group.physicalDeviceCount; j++) {
338 uint32_t mask = 1 << j;
339 if (group_capabilities->presentMask[i] & mask) {
340 p.PrintString(std::string(group_props[j].deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(j)) +
341 ")");
345 DumpVkDeviceGroupPresentModeFlagsKHR(p, "Present modes", group_capabilities->modes);
347 p.AddNewline();
348 group_id++;
350 p.AddNewline();
354 void GpuDumpProps(Printer &p, AppGpu &gpu, bool show_promoted_structs) {
355 auto props = gpu.GetDeviceProperties();
356 p.SetSubHeader();
358 ObjectWrapper obj(p, "VkPhysicalDeviceProperties");
359 p.SetMinKeyWidth(17);
360 if (p.Type() == OutputType::json) {
361 p.PrintKeyValue("apiVersion", props.apiVersion);
362 p.PrintKeyValue("driverVersion", props.driverVersion);
363 } else {
364 p.SetValueDescription(std::to_string(props.apiVersion)).PrintKeyString("apiVersion", APIVersion(props.apiVersion));
365 p.SetValueDescription(std::to_string(props.driverVersion))
366 .PrintKeyString("driverVersion", gpu.GetDriverVersionString());
368 p.PrintKeyString("vendorID", to_hex_str(props.vendorID));
369 p.PrintKeyString("deviceID", to_hex_str(props.deviceID));
370 p.PrintKeyString("deviceType", VkPhysicalDeviceTypeString(props.deviceType));
371 p.PrintKeyString("deviceName", props.deviceName);
372 p.PrintKeyValue("pipelineCacheUUID", props.pipelineCacheUUID);
374 p.AddNewline();
375 DumpVkPhysicalDeviceLimits(p, "VkPhysicalDeviceLimits", gpu.props.limits);
376 p.AddNewline();
377 DumpVkPhysicalDeviceSparseProperties(p, "VkPhysicalDeviceSparseProperties", gpu.props.sparseProperties);
378 p.AddNewline();
379 if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
380 void *place = gpu.props2.pNext;
381 chain_iterator_phys_device_props2(p, gpu.inst, gpu, show_promoted_structs, place);
385 void GpuDumpQueueProps(Printer &p, AppGpu &gpu, const AppQueueFamilyProperties &queue) {
386 VkQueueFamilyProperties props = queue.props;
387 p.SetSubHeader().SetElementIndex(static_cast<int>(queue.queue_index));
388 ObjectWrapper obj_queue_props(p, "queueProperties");
389 p.SetMinKeyWidth(27);
390 if (p.Type() == OutputType::vkconfig_output) {
391 DumpVkExtent3D(p, "minImageTransferGranularity", props.minImageTransferGranularity);
392 } else {
393 p.PrintKeyValue("minImageTransferGranularity", props.minImageTransferGranularity);
395 p.PrintKeyValue("queueCount", props.queueCount);
396 p.PrintKeyString("queueFlags", VkQueueFlagsString(props.queueFlags));
397 p.PrintKeyValue("timestampValidBits", props.timestampValidBits);
399 if (!queue.can_present) {
400 p.PrintKeyString("present support", "false");
401 } else if (queue.can_always_present) {
402 p.PrintKeyString("present support", "true");
403 } else {
404 size_t width = 0;
405 for (const auto &support : queue.present_support) {
406 if (support.first.size() > width) width = support.first.size();
408 ObjectWrapper obj_present_support(p, "present support");
409 p.SetMinKeyWidth(width);
410 for (const auto &support : queue.present_support) {
411 p.PrintKeyString(support.first, support.second ? "true" : "false");
414 chain_iterator_queue_properties2(p, gpu, queue.pNext);
416 p.AddNewline();
419 // This prints a number of bytes in a human-readable format according to prefixes of the International System of Quantities (ISQ),
420 // defined in ISO/IEC 80000. The prefixes used here are not SI prefixes, but rather the binary prefixes based on powers of 1024
421 // (kibi-, mebi-, gibi- etc.).
422 #define kBufferSize 32
424 std::string NumToNiceStr(const size_t sz) {
425 const char prefixes[] = "KMGTPEZY";
426 char buf[kBufferSize];
427 int which = -1;
428 double result = (double)sz;
429 while (result > 1024 && which < 7) {
430 result /= 1024;
431 ++which;
434 char unit[] = "\0i";
435 if (which >= 0) {
436 unit[0] = prefixes[which];
438 #ifdef _WIN32
439 _snprintf_s(buf, kBufferSize * sizeof(char), kBufferSize, "%.2f %sB", result, unit);
440 #else
441 snprintf(buf, kBufferSize, "%.2f %sB", result, unit);
442 #endif
443 return std::string(buf);
446 std::string append_human_readable(VkDeviceSize memory) {
447 return std::to_string(memory) + " (" + to_hex_str(memory) + ") (" + NumToNiceStr(static_cast<size_t>(memory)) + ")";
450 void GpuDumpMemoryProps(Printer &p, AppGpu &gpu) {
451 p.SetHeader();
452 ObjectWrapper obj_mem_props(p, "VkPhysicalDeviceMemoryProperties");
453 IndentWrapper indent(p);
455 ObjectWrapper obj_mem_heaps(p, "memoryHeaps", gpu.memory_props.memoryHeapCount);
457 for (uint32_t i = 0; i < gpu.memory_props.memoryHeapCount; ++i) {
458 p.SetElementIndex(static_cast<int>(i));
459 ObjectWrapper obj_mem_heap(p, "memoryHeaps");
460 p.SetMinKeyWidth(6);
461 p.PrintKeyString("size", append_human_readable(gpu.memory_props.memoryHeaps[i].size));
462 if (gpu.CheckPhysicalDeviceExtensionIncluded(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME)) {
463 p.PrintKeyString("budget", append_human_readable(gpu.heapBudget[i]));
464 p.PrintKeyString("usage", append_human_readable(gpu.heapUsage[i]));
466 DumpVkMemoryHeapFlags(p, "flags", gpu.memory_props.memoryHeaps[i].flags);
470 ObjectWrapper obj_mem_types(p, "memoryTypes", gpu.memory_props.memoryTypeCount);
471 for (uint32_t i = 0; i < gpu.memory_props.memoryTypeCount; ++i) {
472 p.SetElementIndex(static_cast<int>(i));
473 ObjectWrapper obj_mem_type(p, "memoryTypes");
474 p.SetMinKeyWidth(13);
475 p.PrintKeyValue("heapIndex", gpu.memory_props.memoryTypes[i].heapIndex);
477 auto flags = gpu.memory_props.memoryTypes[i].propertyFlags;
478 DumpVkMemoryPropertyFlags(p, "propertyFlags = " + to_hex_str(flags), flags);
480 ObjectWrapper usable_for(p, "usable for");
481 const uint32_t memtype_bit = 1U << i;
483 // only linear and optimal tiling considered
484 for (auto &image_tiling : gpu.memory_image_support_types) {
485 p.SetOpenDetails();
486 ArrayWrapper arr(p, VkImageTilingString(VkImageTiling(image_tiling.tiling)));
487 bool has_any_support_types = false;
488 bool regular = false;
489 bool transient = false;
490 bool sparse = false;
491 for (auto &image_format : image_tiling.formats) {
492 if (image_format.type_support.size() > 0) {
493 bool has_a_support_type = false;
494 for (auto &img_type : image_format.type_support) {
495 if (img_type.Compatible(memtype_bit)) {
496 has_a_support_type = true;
497 has_any_support_types = true;
498 if (img_type.type == ImageTypeSupport::Type::regular) regular = true;
499 if (img_type.type == ImageTypeSupport::Type::transient) transient = true;
500 if (img_type.type == ImageTypeSupport::Type::sparse) sparse = true;
503 if (has_a_support_type) {
504 if (image_format.format == color_format) {
505 p.PrintString("color images");
506 } else {
507 p.PrintString(VkFormatString(image_format.format));
512 if (!has_any_support_types) {
513 p.PrintString("None");
514 } else {
515 if (regular && !transient && sparse) p.PrintString("(non-transient)");
516 if (regular && transient && !sparse) p.PrintString("(non-sparse)");
517 if (regular && !transient && !sparse) p.PrintString("(non-sparse, non-transient)");
518 if (!regular && transient && sparse) p.PrintString("(sparse and transient only)");
519 if (!regular && !transient && sparse) p.PrintString("(sparse only)");
520 if (!regular && transient && !sparse) p.PrintString("(transient only)");
525 p.AddNewline();
528 void GpuDumpFeatures(Printer &p, AppGpu &gpu, bool show_promoted_structs) {
529 p.SetHeader();
530 DumpVkPhysicalDeviceFeatures(p, "VkPhysicalDeviceFeatures", gpu.features);
531 p.AddNewline();
532 if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
533 void *place = gpu.features2.pNext;
534 chain_iterator_phys_device_features2(p, gpu, show_promoted_structs, place);
538 void GpuDumpTextFormatProperty(Printer &p, const AppGpu &gpu, PropFlags formats, const std::set<VkFormat> &format_list,
539 uint32_t counter) {
540 p.SetElementIndex(counter);
541 ObjectWrapper obj_common_group(p, "Common Format Group");
542 IndentWrapper indent_inner(p);
544 ArrayWrapper arr_formats(p, "Formats", format_list.size());
545 for (auto &fmt : format_list) {
546 p.SetAsType().PrintString(VkFormatString(fmt));
549 ObjectWrapper obj(p, "Properties");
550 if (gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME)) {
551 DumpVkFormatFeatureFlags2(p, "linearTilingFeatures", formats.props3.linearTilingFeatures);
552 DumpVkFormatFeatureFlags2(p, "optimalTilingFeatures", formats.props3.optimalTilingFeatures);
553 DumpVkFormatFeatureFlags2(p, "bufferFeatures", formats.props3.bufferFeatures);
554 } else {
555 DumpVkFormatFeatureFlags(p, "linearTilingFeatures", formats.props.linearTilingFeatures);
556 DumpVkFormatFeatureFlags(p, "optimalTilingFeatures", formats.props.optimalTilingFeatures);
557 DumpVkFormatFeatureFlags(p, "bufferFeatures", formats.props.bufferFeatures);
559 p.AddNewline();
562 void GpuDumpToolingInfo(Printer &p, AppGpu &gpu) {
563 auto tools = GetToolingInfo(gpu);
564 if (tools.size() > 0) {
565 p.SetSubHeader();
566 ObjectWrapper obj(p, "Tooling Info");
567 for (auto tool : tools) {
568 DumpVkPhysicalDeviceToolProperties(p, tool.name, tool);
569 p.AddNewline();
574 void GpuDevDump(Printer &p, AppGpu &gpu) {
575 p.SetHeader();
576 ObjectWrapper obj_format_props(p, "Format Properties");
577 IndentWrapper indent_outer(p);
579 if (p.Type() == OutputType::text) {
580 auto fmtPropMap = FormatPropMap(gpu);
581 int counter = 0;
582 std::set<VkFormat> unsupported_formats;
583 for (auto &prop : fmtPropMap) {
584 VkFormatProperties props = prop.first.props;
585 VkFormatProperties3 props3 = prop.first.props3;
586 if (props.linearTilingFeatures == 0 && props.optimalTilingFeatures == 0 && props.bufferFeatures == 0 &&
587 props3.linearTilingFeatures == 0 && props3.optimalTilingFeatures == 0 && props3.bufferFeatures == 0) {
588 unsupported_formats = prop.second;
589 continue;
591 GpuDumpTextFormatProperty(p, gpu, prop.first, prop.second, counter++);
594 ArrayWrapper arr_unsupported_formats(p, "Unsupported Formats", unsupported_formats.size());
595 for (auto &fmt : unsupported_formats) {
596 p.SetAsType().PrintString(VkFormatString(fmt));
598 } else {
599 std::set<VkFormat> formats_to_print;
600 for (auto &format_range : format_ranges) {
601 if (gpu.FormatRangeSupported(format_range)) {
602 for (int32_t fmt_counter = format_range.first_format; fmt_counter <= format_range.last_format; ++fmt_counter) {
603 formats_to_print.insert(static_cast<VkFormat>(fmt_counter));
607 for (const auto &fmt : formats_to_print) {
608 auto formats = get_format_properties(gpu, fmt);
609 p.SetTitleAsType();
610 if (gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME)) {
611 DumpVkFormatProperties3(p, VkFormatString(fmt), formats.props3);
612 } else {
613 DumpVkFormatProperties(p, VkFormatString(fmt), formats.props);
618 p.AddNewline();
621 void DumpVkVideoProfileInfoKHRCustom(Printer &p, std::string name, const VkVideoProfileInfoKHR &obj) {
622 // We use custom dumping here because we do not want to output ignored fields
623 // e.g. for monochrome chromaBitDepth is ignored
624 ObjectWrapper object{p, name};
625 DumpVkVideoCodecOperationFlagBitsKHR(p, "videoCodecOperation", obj.videoCodecOperation);
626 DumpVkVideoChromaSubsamplingFlagsKHR(p, "chromaSubsampling", obj.chromaSubsampling);
627 DumpVkVideoComponentBitDepthFlagsKHR(p, "lumaBitDepth", obj.lumaBitDepth);
628 if (obj.chromaSubsampling != VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR) {
629 DumpVkVideoComponentBitDepthFlagsKHR(p, "chromaBitDepth", obj.chromaBitDepth);
630 } else {
631 DumpVkVideoComponentBitDepthFlagsKHR(p, "chromaBitDepth", 0);
635 void GpuDumpVideoProfiles(Printer &p, AppGpu &gpu, bool show_video_props) {
636 p.SetHeader();
637 ArrayWrapper video_profiles_obj(p, "Video Profiles", gpu.video_profiles.size());
638 IndentWrapper indent_outer(p);
640 if (p.Type() != OutputType::text || show_video_props) {
641 // Video profile details per profile
642 for (const auto &video_profile : gpu.video_profiles) {
643 p.SetSubHeader();
644 ObjectWrapper video_profile_obj(p, video_profile->name);
645 IndentWrapper indent_inner(p);
647 p.SetSubHeader();
648 ObjectWrapper profile_info_obj(p, "Video Profile Definition");
649 p.SetSubHeader();
650 DumpVkVideoProfileInfoKHRCustom(p, "VkVideoProfileInfoKHR", video_profile->profile_info);
651 chain_iterator_video_profile_info(p, gpu, video_profile->profile_info.pNext);
654 p.SetSubHeader();
655 ObjectWrapper capabilities_obj(p, "Video Profile Capabilities");
656 p.SetSubHeader();
657 DumpVkVideoCapabilitiesKHR(p, "VkVideoCapabilitiesKHR", video_profile->capabilities);
658 chain_iterator_video_capabilities(p, gpu, video_profile->capabilities.pNext);
661 p.SetSubHeader();
662 ObjectWrapper video_formats_obj(p, "Video Formats");
663 for (const auto &video_formats_it : video_profile->formats_by_category) {
664 const auto &video_format_category_name = video_formats_it.first;
665 const auto &video_format_props = video_formats_it.second;
666 ArrayWrapper video_format_category(p, video_format_category_name, video_format_props.size());
667 for (size_t i = 0; i < video_format_props.size(); ++i) {
668 ObjectWrapper video_format_obj(p, video_format_category_name + " Format #" + std::to_string(i + 1));
669 p.SetSubHeader();
670 DumpVkVideoFormatPropertiesKHR(p, "VkVideoFormatPropertiesKHR", video_format_props[i]);
671 chain_iterator_video_format_properties(p, gpu, video_format_props[i].pNext);
676 p.AddNewline();
678 } else {
679 // Video profile list only
680 for (const auto &video_profile : gpu.video_profiles) {
681 p.PrintString(video_profile->name);
685 p.AddNewline();
688 // Print gpu info for text, html, & vkconfig_output
689 // Uses a separate function than schema-json for clarity
690 void DumpGpu(Printer &p, AppGpu &gpu, bool show_tooling_info, bool show_formats, bool show_promoted_structs,
691 bool show_video_props) {
692 ObjectWrapper obj_gpu(p, "GPU" + std::to_string(gpu.id));
693 IndentWrapper indent(p);
695 GpuDumpProps(p, gpu, show_promoted_structs);
696 DumpExtensions(p, "Device Extensions", gpu.device_extensions);
697 p.AddNewline();
699 p.SetHeader();
700 ObjectWrapper obj_family_props(p, "VkQueueFamilyProperties");
701 for (const auto &queue_prop : gpu.extended_queue_props) {
702 GpuDumpQueueProps(p, gpu, queue_prop);
705 GpuDumpMemoryProps(p, gpu);
706 GpuDumpFeatures(p, gpu, show_promoted_structs);
707 if (show_tooling_info) {
708 GpuDumpToolingInfo(p, gpu);
711 if (p.Type() != OutputType::text || show_formats) {
712 GpuDevDump(p, gpu);
715 if (!gpu.video_profiles.empty()) {
716 GpuDumpVideoProfiles(p, gpu, show_video_props);
719 p.AddNewline();
722 // Print capabilities section of profiles schema
723 void DumpGpuProfileCapabilities(Printer &p, AppGpu &gpu) {
724 ObjectWrapper capabilities(p, "capabilities");
726 ObjectWrapper temp_name_obj(p, "device");
727 DumpExtensions(p, "extensions", gpu.device_extensions);
729 ObjectWrapper obj(p, "features");
730 GpuDumpFeatures(p, gpu, false);
733 ObjectWrapper obj(p, "properties");
735 ObjectWrapper props_obj(p, "VkPhysicalDeviceProperties");
736 auto props = gpu.GetDeviceProperties();
737 p.PrintKeyValue("apiVersion", props.apiVersion);
738 p.PrintKeyValue("deviceID", props.deviceID);
739 p.PrintKeyString("deviceName", props.deviceName);
740 p.PrintKeyString("deviceType", std::string("VK_") + VkPhysicalDeviceTypeString(props.deviceType));
741 p.PrintKeyValue("driverVersion", props.driverVersion);
743 DumpVkPhysicalDeviceLimits(p, "VkPhysicalDeviceLimits", gpu.props.limits);
745 ArrayWrapper arr(p, "pipelineCacheUUID");
746 for (const auto &uuid : props.pipelineCacheUUID) p.PrintElement(static_cast<uint32_t>(uuid));
748 DumpVkPhysicalDeviceSparseProperties(p, "VkPhysicalDeviceSparseProperties", gpu.props.sparseProperties);
749 p.PrintKeyValue("vendorID", props.vendorID);
751 if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
752 void *place = gpu.props2.pNext;
753 chain_iterator_phys_device_props2(p, gpu.inst, gpu, false, place);
757 ObjectWrapper obj(p, "formats");
758 std::set<VkFormat> already_printed_formats;
759 for (const auto &format : format_ranges) {
760 if (gpu.FormatRangeSupported(format)) {
761 for (int32_t fmt_counter = format.first_format; fmt_counter <= format.last_format; ++fmt_counter) {
762 VkFormat fmt = static_cast<VkFormat>(fmt_counter);
763 if (already_printed_formats.count(fmt) > 0) {
764 continue;
766 auto formats = get_format_properties(gpu, fmt);
768 // don't print format properties that are unsupported
769 if (formats.props.linearTilingFeatures == 0 && formats.props.optimalTilingFeatures == 0 &&
770 formats.props.bufferFeatures == 0 && formats.props3.linearTilingFeatures == 0 &&
771 formats.props3.optimalTilingFeatures == 0 && formats.props3.bufferFeatures == 0)
772 continue;
774 ObjectWrapper format_obj(p, std::string("VK_") + VkFormatString(fmt));
776 // Want to explicitly list VkFormatProperties in addition to VkFormatProperties3 if available
777 DumpVkFormatProperties(p, "VkFormatProperties", formats.props);
778 VkFormatProperties2 format_props2{};
779 format_props2.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
780 format_props2.formatProperties = formats.props;
781 std::unique_ptr<format_properties2_chain> chain_for_format_props2;
782 setup_format_properties2_chain(format_props2, chain_for_format_props2, gpu);
783 vkGetPhysicalDeviceFormatProperties2KHR(gpu.phys_device, fmt, &format_props2);
784 chain_iterator_format_properties2(p, gpu, format_props2.pNext);
786 already_printed_formats.insert(fmt);
792 ArrayWrapper arr(p, "queueFamiliesProperties");
793 for (const auto &extended_queue_prop : gpu.extended_queue_props) {
794 ObjectWrapper queue_obj(p, "");
796 ObjectWrapper obj_queue_props(p, "VkQueueFamilyProperties");
797 VkQueueFamilyProperties props = extended_queue_prop.props;
798 DumpVkExtent3D(p, "minImageTransferGranularity", props.minImageTransferGranularity);
799 p.PrintKeyValue("queueCount", props.queueCount);
800 DumpVkQueueFlags(p, "queueFlags", props.queueFlags);
801 p.PrintKeyValue("timestampValidBits", props.timestampValidBits);
803 chain_iterator_queue_properties2(p, gpu, extended_queue_prop.pNext);
806 if (!gpu.video_profiles.empty()) {
807 ArrayWrapper video_profiles(p, "videoProfiles");
808 for (const auto &video_profile : gpu.video_profiles) {
809 ObjectWrapper video_profile_obj(p, "");
811 ObjectWrapper profile_info_obj(p, "profile");
812 DumpVkVideoProfileInfoKHRCustom(p, "VkVideoProfileInfoKHR", video_profile->profile_info);
813 chain_iterator_video_profile_info(p, gpu, video_profile->profile_info.pNext);
816 ObjectWrapper capabilities_obj(p, "capabilities");
817 DumpVkVideoCapabilitiesKHR(p, "VkVideoCapabilitiesKHR", video_profile->capabilities);
818 chain_iterator_video_capabilities(p, gpu, video_profile->capabilities.pNext);
821 ArrayWrapper video_formats(p, "formats");
822 for (const auto &video_format : video_profile->formats) {
823 ObjectWrapper video_format_obj(p, "");
824 DumpVkVideoFormatPropertiesKHR(p, "VkVideoFormatPropertiesKHR", video_format.properties);
825 chain_iterator_video_format_properties(p, gpu, video_format.properties.pNext);
831 #if defined(VK_ENABLE_BETA_EXTENSIONS)
832 // Print portability subset extension, features, and properties if available
833 if (gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) &&
834 (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) ||
835 gpu.inst.api_version >= VK_API_VERSION_1_1)) {
836 ObjectWrapper macos_obj(p, "macos-specific");
838 ObjectWrapper ext_obj(p, "extensions");
839 const std::string portability_ext_name = VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME;
840 for (const auto &ext : gpu.device_extensions) {
841 if (portability_ext_name == ext.extensionName) {
842 p.PrintExtension(ext.extensionName, ext.specVersion);
847 ObjectWrapper features_obj(p, "features");
848 void *feats_place = gpu.features2.pNext;
849 while (feats_place) {
850 VkBaseOutStructure *structure = static_cast<VkBaseOutStructure *>(feats_place);
851 if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR) {
852 auto *features = reinterpret_cast<VkPhysicalDevicePortabilitySubsetFeaturesKHR *>(structure);
853 DumpVkPhysicalDevicePortabilitySubsetFeaturesKHR(p, "VkPhysicalDevicePortabilitySubsetFeaturesKHR", *features);
854 break;
856 feats_place = structure->pNext;
860 ObjectWrapper property_obj(p, "properties");
861 void *props_place = gpu.props2.pNext;
862 while (props_place) {
863 VkBaseOutStructure *structure = static_cast<VkBaseOutStructure *>(props_place);
864 if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR) {
865 auto *props = reinterpret_cast<VkPhysicalDevicePortabilitySubsetPropertiesKHR *>(structure);
866 DumpVkPhysicalDevicePortabilitySubsetPropertiesKHR(p, "VkPhysicalDevicePortabilitySubsetPropertiesKHR", *props);
867 break;
869 props_place = structure->pNext;
873 #endif // defined(VK_ENABLE_BETA_EXTENSIONS)
875 void PrintProfileBaseInfo(Printer &p, const std::string &device_name, uint32_t apiVersion, const std::string &device_label,
876 const std::vector<std::string> &capabilities) {
877 ObjectWrapper vk_info(p, device_name);
878 p.PrintKeyValue("version", 1);
879 p.PrintKeyString("api-version", APIVersion(apiVersion).str());
880 p.PrintKeyString("label", device_label);
881 p.PrintKeyString("description", std::string("Exported from ") + APP_SHORT_NAME);
882 { ObjectWrapper contributors(p, "contributors"); }
884 ArrayWrapper contributors(p, "history");
885 ObjectWrapper element(p, "");
886 p.PrintKeyValue("revision", 1);
887 std::time_t t = std::time(0); // get time now
888 std::tm *now = std::localtime(&t);
889 std::string date =
890 std::to_string(now->tm_year + 1900) + '-' + std::to_string(now->tm_mon + 1) + '-' + std::to_string(now->tm_mday);
891 p.PrintKeyString("date", date);
892 p.PrintKeyString("author", std::string("Automated export from ") + APP_SHORT_NAME);
893 p.PrintKeyString("comment", "");
895 ArrayWrapper contributors(p, "capabilities");
896 for (const auto &str : capabilities) p.PrintString(str);
899 // Prints profiles section of profiles schema
900 void DumpGpuProfileInfo(Printer &p, AppGpu &gpu) {
901 ObjectWrapper profiles(p, "profiles");
903 std::string device_label = std::string(gpu.props.deviceName) + " driver " + gpu.GetDriverVersionString();
904 std::string device_name =
905 std::string("VP_" APP_UPPER_CASE_NAME "_") + std::string(gpu.props.deviceName) + "_" + gpu.GetDriverVersionString();
907 for (auto &c : device_name) {
908 if (c == ' ' || c == '.') c = '_';
910 PrintProfileBaseInfo(p, device_name, gpu.props.apiVersion, device_label, {"device"});
911 #if defined(VK_ENABLE_BETA_EXTENSIONS)
912 if (gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) &&
913 (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) ||
914 gpu.inst.api_version >= VK_API_VERSION_1_1)) {
915 PrintProfileBaseInfo(p, device_name + "_portability_subset", gpu.props.apiVersion, device_label + " subset",
916 {"device", "macos-specific"});
918 #endif // defined(VK_ENABLE_BETA_EXTENSIONS)
921 // Print summary of system
922 void DumpSummaryInstance(Printer &p, AppInstance &inst) {
923 p.SetSubHeader();
924 DumpExtensions(p, "Instance Extensions", inst.global_extensions, true);
925 p.AddNewline();
927 p.SetSubHeader();
928 ArrayWrapper arr(p, "Instance Layers", inst.global_layers.size());
929 IndentWrapper indent(p);
930 std::sort(inst.global_layers.begin(), inst.global_layers.end(), [](LayerExtensionList &left, LayerExtensionList &right) -> int {
931 return std::strncmp(left.layer_properties.layerName, right.layer_properties.layerName, VK_MAX_DESCRIPTION_SIZE) < 0;
933 size_t layer_name_max = 0;
934 size_t layer_desc_max = 0;
935 size_t layer_version_max = 0;
937 // find max of each type to align everything in columns
938 for (auto &layer : inst.global_layers) {
939 auto props = layer.layer_properties;
940 layer_name_max = std::max(layer_name_max, strlen(props.layerName));
941 layer_desc_max = std::max(layer_desc_max, strlen(props.description));
942 layer_version_max = std::max(layer_version_max, APIVersion(layer.layer_properties.specVersion).str().size());
944 for (auto &layer : inst.global_layers) {
945 auto v_str = APIVersion(layer.layer_properties.specVersion).str();
946 auto props = layer.layer_properties;
948 auto name_padding = std::string(layer_name_max - strlen(props.layerName), ' ');
949 auto desc_padding = std::string(layer_desc_max - strlen(props.description), ' ');
950 auto version_padding = std::string(layer_version_max - v_str.size(), ' ');
951 p.PrintString(std::string(props.layerName) + name_padding + " " + props.description + desc_padding + " " + v_str + " " +
952 version_padding + " version " + std::to_string(props.implementationVersion));
954 p.AddNewline();
957 void DumpSummaryGPU(Printer &p, AppGpu &gpu) {
958 ObjectWrapper obj(p, "GPU" + std::to_string(gpu.id));
959 p.SetMinKeyWidth(18);
960 auto props = gpu.GetDeviceProperties();
961 p.PrintKeyValue("apiVersion", APIVersion(props.apiVersion));
962 if (gpu.found_driver_props) {
963 p.PrintKeyString("driverVersion", gpu.GetDriverVersionString());
964 } else {
965 p.PrintKeyValue("driverVersion", props.driverVersion);
967 p.PrintKeyString("vendorID", to_hex_str(props.vendorID));
968 p.PrintKeyString("deviceID", to_hex_str(props.deviceID));
969 p.PrintKeyString("deviceType", VkPhysicalDeviceTypeString(props.deviceType));
970 p.PrintKeyString("deviceName", props.deviceName);
972 if (gpu.found_driver_props) {
973 DumpVkDriverId(p, "driverID", gpu.driverID);
974 p.PrintKeyString("driverName", gpu.driverName);
975 p.PrintKeyString("driverInfo", gpu.driverInfo);
976 p.PrintKeyValue("conformanceVersion", gpu.conformanceVersion);
978 if (gpu.found_device_id_props) {
979 p.PrintKeyValue("deviceUUID", gpu.deviceUUID);
980 p.PrintKeyValue("driverUUID", gpu.driverUUID);
984 // ============ Printing Logic ============= //
986 #ifdef _WIN32
987 // Enlarges the console window to have a large scrollback size.
988 static void ConsoleEnlarge() {
989 const HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
991 // make the console window bigger
992 CONSOLE_SCREEN_BUFFER_INFO csbi;
993 COORD buffer_size;
994 if (GetConsoleScreenBufferInfo(console_handle, &csbi)) {
995 buffer_size.X = csbi.dwSize.X + 30;
996 buffer_size.Y = 20000;
997 SetConsoleScreenBufferSize(console_handle, buffer_size);
1000 SMALL_RECT r;
1001 r.Left = r.Top = 0;
1002 r.Right = csbi.dwSize.X - 1 + 30;
1003 r.Bottom = 50;
1004 SetConsoleWindowInfo(console_handle, true, &r);
1006 // change the console window title
1007 SetConsoleTitle(TEXT(APP_SHORT_NAME));
1009 #endif
1011 // Global configuration
1012 enum class OutputCategory { text, html, profile_json, vkconfig_output, summary };
1013 const char *help_message_body =
1014 "OPTIONS:\n"
1015 "[-h, --help] Print this help.\n"
1016 "[--summary] Show a summary of the instance and GPU's on a system.\n"
1017 "[-o <filename>, --output <filename>]\n"
1018 " Print output to a new file whose name is specified by filename.\n"
1019 " File will be written to the current working directory.\n"
1020 "[--text] Produce a text version of " APP_SHORT_NAME
1021 " output to stdout. This is\n"
1022 " the default output.\n"
1023 "[--html] Produce an html version of " APP_SHORT_NAME
1024 " output, saved as\n"
1025 " \"" APP_SHORT_NAME
1026 ".html\" in the directory in which the command\n"
1027 " is run.\n"
1028 "[-j, --json] Produce a json version of " APP_SHORT_NAME
1029 " output conforming to the Vulkan\n"
1030 " Profiles schema, saved as \n"
1031 " \"VP_" APP_UPPER_CASE_NAME
1032 "_[DEVICE_NAME]_[DRIVER_VERSION].json\"\n"
1033 " of the first gpu in the system.\n"
1034 "[-j=<gpu-number>, --json=<gpu-number>]\n"
1035 " For a multi-gpu system, a single gpu can be targetted by\n"
1036 " specifying the gpu-number associated with the gpu of \n"
1037 " interest. This number can be determined by running\n"
1038 " " APP_SHORT_NAME
1039 " without any options specified.\n"
1040 "[--show-tool-props] Show the active VkPhysicalDeviceToolPropertiesEXT that " APP_SHORT_NAME
1041 " finds.\n"
1042 "[--show-formats] Display the format properties of each physical device.\n"
1043 " Note: This only affects text output.\n"
1044 "[--show-promoted-structs] Include structs promoted to core in pNext Chains.\n"
1045 "[--show-video-props]\n"
1046 " Display the video profile info, video capabilities and\n"
1047 " video format properties of each video profile supported\n"
1048 " by each physical device.\n"
1049 " Note: This only affects text output which by default\n"
1050 " only contains the list of supported video profile names.\n";
1052 void print_usage(const std::string &executable_name) {
1053 std::cout << "\n" APP_SHORT_NAME " - Summarize " API_NAME " information in relation to the current environment.\n\n";
1054 std::cout << "USAGE: \n";
1055 std::cout << " " << executable_name << " --summary\n";
1056 std::cout << " " << executable_name << " -o <filename> | --output <filename>\n";
1057 std::cout << " " << executable_name << " -j | -j=<gpu-number> | --json | --json=<gpu-number>\n";
1058 std::cout << " " << executable_name << " --text\n";
1059 std::cout << " " << executable_name << " --html\n";
1060 std::cout << " " << executable_name << " --show-formats\n";
1061 std::cout << " " << executable_name << " --show-tool-props\n";
1062 std::cout << " " << executable_name << " --show-promoted-structs\n";
1063 std::cout << "\n" << help_message_body << std::endl;
1066 struct ParsedResults {
1067 OutputCategory output_category;
1068 uint32_t selected_gpu;
1069 bool has_selected_gpu; // differentiate between selecting the 0th gpu and using the default 0th value
1070 bool show_tool_props;
1071 bool show_formats;
1072 bool show_promoted_structs;
1073 bool show_video_props;
1074 bool print_to_file;
1075 std::string filename; // set if explicitely given, or if vkconfig_output has a <path> argument
1076 std::string default_filename;
1079 util::vulkaninfo_optional<ParsedResults> parse_arguments(int argc, char **argv, std::string executable_name) {
1080 ParsedResults results{}; // default it to zero init everything
1081 results.output_category = OutputCategory::text; // default output category
1082 results.default_filename = APP_SHORT_NAME ".txt";
1083 for (int i = 1; i < argc; ++i) {
1084 // A internal-use-only format for communication with the Vulkan Configurator tool
1085 // Usage "--vkconfig_output <path>"
1086 // -o can be used to specify the filename instead
1087 if (0 == strcmp("--vkconfig_output", argv[i])) {
1088 results.output_category = OutputCategory::vkconfig_output;
1089 results.print_to_file = true;
1090 results.default_filename = APP_SHORT_NAME ".json";
1091 if (argc > (i + 1) && argv[i + 1][0] != '-') {
1092 #ifdef WIN32
1093 results.filename = (std::string(argv[i + 1]) + "\\" APP_SHORT_NAME ".json");
1094 #else
1095 results.filename = (std::string(argv[i + 1]) + "/" APP_SHORT_NAME ".json");
1096 #endif
1097 ++i;
1099 } else if (strncmp("--json", argv[i], 6) == 0 || strncmp(argv[i], "-j", 2) == 0) {
1100 if (strlen(argv[i]) > 7 && strncmp("--json=", argv[i], 7) == 0) {
1101 results.selected_gpu = static_cast<uint32_t>(strtol(argv[i] + 7, nullptr, 10));
1102 results.has_selected_gpu = true;
1104 if (strlen(argv[i]) > 3 && strncmp("-j=", argv[i], 3) == 0) {
1105 results.selected_gpu = static_cast<uint32_t>(strtol(argv[i] + 3, nullptr, 10));
1106 results.has_selected_gpu = true;
1108 results.output_category = OutputCategory::profile_json;
1109 results.default_filename = APP_SHORT_NAME ".json";
1110 results.print_to_file = true;
1111 } else if (strcmp(argv[i], "--summary") == 0) {
1112 results.output_category = OutputCategory::summary;
1113 } else if (strcmp(argv[i], "--text") == 0) {
1114 results.output_category = OutputCategory::text;
1115 results.default_filename = APP_SHORT_NAME ".txt";
1116 } else if (strcmp(argv[i], "--html") == 0) {
1117 results.output_category = OutputCategory::html;
1118 results.print_to_file = true;
1119 results.default_filename = APP_SHORT_NAME ".html";
1120 } else if (strcmp(argv[i], "--show-tool-props") == 0) {
1121 results.show_tool_props = true;
1122 } else if (strcmp(argv[i], "--show-formats") == 0) {
1123 results.show_formats = true;
1124 } else if (strcmp(argv[i], "--show-promoted-structs") == 0) {
1125 results.show_promoted_structs = true;
1126 } else if (strcmp(argv[i], "--show-video-props") == 0) {
1127 results.show_video_props = true;
1128 } else if ((strcmp(argv[i], "--output") == 0 || strcmp(argv[i], "-o") == 0) && argc > (i + 1)) {
1129 if (argv[i + 1][0] == '-') {
1130 std::cout << "-o or --output must be followed by a filename\n";
1131 return {};
1133 results.print_to_file = true;
1134 results.filename = argv[i + 1];
1135 ++i;
1136 } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
1137 print_usage(executable_name);
1138 return {};
1139 } else {
1140 print_usage(executable_name);
1141 return {};
1144 return results;
1147 PrinterCreateDetails get_printer_create_details(ParsedResults &parse_data, AppInstance &inst, AppGpu &selected_gpu,
1148 std::string const &executable_name) {
1149 PrinterCreateDetails create{};
1150 create.print_to_file = parse_data.print_to_file;
1151 create.file_name = (!parse_data.filename.empty()) ? parse_data.filename : parse_data.default_filename;
1152 switch (parse_data.output_category) {
1153 default:
1154 case (OutputCategory::text):
1155 create.output_type = OutputType::text;
1156 break;
1157 case (OutputCategory::html):
1158 create.output_type = OutputType::html;
1159 break;
1160 case (OutputCategory::profile_json):
1161 create.output_type = OutputType::json;
1162 create.start_string =
1163 std::string("{\n\t\"$schema\": ") + "\"https://schema.khronos.org/vulkan/profiles-0.8-latest.json\"";
1164 if (parse_data.filename.empty()) {
1165 create.file_name = std::string("VP_" APP_UPPER_CASE_NAME "_") + std::string(selected_gpu.props.deviceName) + "_" +
1166 selected_gpu.GetDriverVersionString();
1167 for (auto &c : create.file_name) {
1168 if (c == ' ' || c == '.') c = '_';
1170 create.file_name += ".json";
1172 break;
1173 case (OutputCategory::vkconfig_output):
1174 create.output_type = OutputType::vkconfig_output;
1175 create.start_string = "{\n\t\"" API_NAME " Instance Version\": \"" + inst.api_version.str() + "\"";
1176 break;
1178 return create;
1181 void RunPrinter(Printer &p, ParsedResults parse_data, AppInstance &instance, std::vector<std::unique_ptr<AppGpu>> &gpus,
1182 std::vector<std::unique_ptr<AppSurface>> &surfaces) {
1183 #ifdef VK_USE_PLATFORM_IOS_MVK
1184 p.SetAlwaysOpenDetails(true);
1185 #endif
1186 if (parse_data.output_category == OutputCategory::summary) {
1187 DumpSummaryInstance(p, instance);
1188 p.SetHeader();
1189 ObjectWrapper obj(p, "Devices");
1190 IndentWrapper indent(p);
1191 for (auto &gpu : gpus) {
1192 DumpSummaryGPU(p, *(gpu.get()));
1194 } else if (parse_data.output_category == OutputCategory::profile_json) {
1195 DumpGpuProfileCapabilities(p, *(gpus.at(parse_data.selected_gpu).get()));
1196 DumpGpuProfileInfo(p, *(gpus.at(parse_data.selected_gpu).get()));
1197 } else {
1198 // text, html, vkconfig_output
1199 p.SetHeader();
1200 DumpExtensions(p, "Instance Extensions", instance.global_extensions);
1201 p.AddNewline();
1203 DumpLayers(p, instance.global_layers, gpus);
1204 #if defined(VULKANINFO_WSI_ENABLED)
1205 // Doesn't print anything if no surfaces are available
1206 DumpPresentableSurfaces(p, instance, gpus, surfaces);
1207 #endif // defined(VULKANINFO_WSI_ENABLED)
1208 DumpGroups(p, instance);
1210 p.SetHeader();
1211 ObjectWrapper obj(p, "Device Properties and Extensions");
1212 IndentWrapper indent(p);
1214 for (auto &gpu : gpus) {
1215 DumpGpu(p, *(gpu.get()), parse_data.show_tool_props, parse_data.show_formats, parse_data.show_promoted_structs,
1216 parse_data.show_video_props);
1221 #ifdef VK_USE_PLATFORM_IOS_MVK
1222 // On iOS, we'll call this ourselves from a parent routine in the GUI
1223 int vulkanInfoMain(int argc, char **argv) {
1224 #else
1225 int main(int argc, char **argv) {
1226 #endif
1228 // Figure out the name of the executable, pull out the name if given a path
1229 // Default is `vulkaninfo`
1230 std::string executable_name = APP_SHORT_NAME;
1231 if (argc >= 1) {
1232 const auto argv_0 = std::string(argv[0]);
1233 // don't include path separator
1234 // Look for forward slash first, only look for backslash if that found nothing
1235 auto last_occurrence = argv_0.rfind('/');
1236 if (last_occurrence == std::string::npos) {
1237 last_occurrence = argv_0.rfind('\\');
1239 if (last_occurrence != std::string::npos && last_occurrence + 1 < argv_0.size()) {
1240 executable_name = argv_0.substr(last_occurrence + 1);
1244 auto parsing_return = parse_arguments(argc, argv, executable_name);
1245 if (!parsing_return) return 1;
1246 ParsedResults parse_data = parsing_return.value();
1248 #if defined(_MSC_VER)
1249 _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
1250 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
1251 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
1252 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
1254 if (ConsoleIsExclusive()) ConsoleEnlarge();
1255 User32Handles local_user32_handles;
1256 user32_handles = &local_user32_handles;
1257 if (!local_user32_handles.load()) {
1258 fprintf(stderr, "Failed to load user32.dll library!\n");
1259 if (parse_data.output_category == OutputCategory::text && !parse_data.print_to_file) wait_for_console_destroy();
1260 return 1;
1262 #endif
1264 int return_code = 0; // set in case of error
1265 std::unique_ptr<Printer> printer;
1266 std::ostream std_out(std::cout.rdbuf());
1267 std::ofstream file_out;
1268 std::ostream *out = &std_out;
1270 // if any essential vulkan call fails, it throws an exception
1271 try {
1272 AppInstance instance = {};
1273 SetupWindowExtensions(instance);
1275 auto phys_devices = instance.FindPhysicalDevices();
1277 #if defined(VULKANINFO_WSI_ENABLED)
1278 for (auto &surface_extension : instance.surface_extensions) {
1279 surface_extension.create_window(instance);
1280 surface_extension.surface = surface_extension.create_surface(instance);
1282 #endif // defined(VULKANINFO_WSI_ENABLED)
1284 std::vector<std::unique_ptr<AppGpu>> gpus;
1286 uint32_t gpu_counter = 0;
1287 for (auto &phys_device : phys_devices) {
1288 gpus.push_back(
1289 std::unique_ptr<AppGpu>(new AppGpu(instance, gpu_counter++, phys_device, parse_data.show_promoted_structs)));
1292 std::vector<std::unique_ptr<AppSurface>> surfaces;
1293 #if defined(VULKANINFO_WSI_ENABLED)
1294 for (auto &surface_extension : instance.surface_extensions) {
1295 for (auto &gpu : gpus) {
1296 try {
1297 // check if the surface is supported by the physical device before adding it to the list
1298 VkBool32 supported = VK_FALSE;
1299 VkResult err = vkGetPhysicalDeviceSurfaceSupportKHR(gpu->phys_device, 0, surface_extension.surface, &supported);
1300 if (err != VK_SUCCESS || supported == VK_FALSE) continue;
1302 surfaces.push_back(
1303 std::unique_ptr<AppSurface>(new AppSurface(instance, *gpu.get(), gpu->phys_device, surface_extension)));
1304 } catch (std::exception &e) {
1305 std::cerr << "ERROR while creating surface for extension " << surface_extension.name << " : " << e.what()
1306 << "\n";
1310 #endif // defined(VULKANINFO_WSI_ENABLED)
1312 if (parse_data.selected_gpu >= gpus.size()) {
1313 if (parse_data.has_selected_gpu) {
1314 std::cout << "The selected gpu (" << parse_data.selected_gpu << ") is not a valid GPU index. ";
1315 if (gpus.size() == 0) {
1316 std::cout << APP_SHORT_NAME " could not find any GPU's.\n";
1317 return 1;
1318 } else {
1319 if (gpus.size() == 1) {
1320 std::cout << "The only available GPU selection is 0.\n";
1321 } else {
1322 std::cout << "The available GPUs are in the range of 0 to " << gpus.size() - 1 << ".\n";
1324 return 1;
1326 } else if (parse_data.output_category == OutputCategory::profile_json) {
1327 std::cout << APP_SHORT_NAME " could not find any GPU's.\n";
1331 auto printer_data = get_printer_create_details(parse_data, instance, *gpus.at(parse_data.selected_gpu), executable_name);
1332 if (printer_data.print_to_file) {
1333 file_out = std::ofstream(printer_data.file_name);
1334 out = &file_out;
1336 printer = std::unique_ptr<Printer>(new Printer(printer_data, *out, instance.api_version));
1338 RunPrinter(*(printer.get()), parse_data, instance, gpus, surfaces);
1340 // Call the printer's destructor before the file handle gets closed
1341 printer.reset(nullptr);
1342 #if defined(VULKANINFO_WSI_ENABLED)
1343 for (auto &surface_extension : instance.surface_extensions) {
1344 AppDestroySurface(instance, surface_extension.surface);
1345 surface_extension.destroy_window(instance);
1347 #endif // defined(VULKANINFO_WSI_ENABLED)
1348 } catch (std::exception &e) {
1349 // Print the error to stderr and leave all outputs in a valid state (mainly for json)
1350 std::cerr << "ERROR at " << e.what() << "\n";
1351 if (printer) {
1352 printer->FinishOutput();
1354 return_code = 1;
1356 // Call the printer's destructor before the file handle gets closed
1357 printer.reset(nullptr);
1360 #ifdef _WIN32
1361 if (parse_data.output_category == OutputCategory::text && !parse_data.print_to_file) wait_for_console_destroy();
1362 #endif
1364 return return_code;