/* * Copyright (c) 2015-2026 The Khronos Group Inc. * Copyright (c) 2015-2026 Valve Corporation * Copyright (c) 2015-2026 LunarG, Inc. * Copyright (c) 2023-2024 RasterGrid Kft. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Author: Courtney Goeltzenleuchter * Author: David Pinedo * Author: Mark Lobodzinski * Author: Rene Lindsay * Author: Jeremy Kniager * Author: Shannon McPherson * Author: Bob Ellison * Author: Richard Wright * Author: Charles Giessen * */ #include #ifdef _WIN32 #include #endif #include "vulkaninfo.hpp" // Used to sort the formats into buckets by their properties. std::unordered_map> FormatPropMap(AppGpu &gpu) { std::unordered_map> map; for (const auto fmtRange : format_ranges) { if (gpu.FormatRangeSupported(fmtRange)) { for (int32_t fmt = fmtRange.first_format; fmt <= fmtRange.last_format; ++fmt) { PropFlags pf = get_format_properties(gpu, static_cast(fmt)); map[pf].insert(static_cast(fmt)); } } } return map; } // =========== Dump Functions ========= // void DumpExtensions(Printer &p, std::string section_name, std::vector extensions, bool do_indent = false) { std::sort(extensions.begin(), extensions.end(), [](VkExtensionProperties &a, VkExtensionProperties &b) -> int { return std::string(a.extensionName) < std::string(b.extensionName); }); size_t max_length = 0; for (const auto &ext : extensions) { max_length = std::max(max_length, std::strlen(ext.extensionName)); } #if defined(VK_ENABLE_BETA_EXTENSIONS) const std::string portability_ext_name = VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME; #endif // defined(VK_ENABLE_BETA_EXTENSIONS) ObjectWrapper obj(p, section_name, extensions.size()); if (do_indent) p.IndentDecrease(); for (auto &ext : extensions) { #if defined(VK_ENABLE_BETA_EXTENSIONS) if (p.Type() == OutputType::json && portability_ext_name == ext.extensionName) continue; #endif // defined(VK_ENABLE_BETA_EXTENSIONS) p.PrintExtension(ext.extensionName, ext.specVersion, max_length); } if (do_indent) p.IndentIncrease(); } void DumpLayers(Printer &p, std::vector layers, const std::vector> &gpus) { std::sort(layers.begin(), layers.end(), [](LayerExtensionList &left, LayerExtensionList &right) -> int { return std::strncmp(left.layer_properties.layerName, right.layer_properties.layerName, VK_MAX_DESCRIPTION_SIZE) < 0; }); switch (p.Type()) { case OutputType::text: case OutputType::html: { p.SetHeader(); ArrayWrapper arr_layers(p, "Layers", layers.size()); IndentWrapper indent(p); for (auto &layer : layers) { std::string v_str = APIVersion(layer.layer_properties.specVersion); auto props = layer.layer_properties; std::string header = p.DecorateAsType(props.layerName) + " (" + props.description + ") " API_NAME " version " + p.DecorateAsValue(v_str) + ", layer version " + p.DecorateAsValue(std::to_string(props.implementationVersion)); ObjectWrapper obj(p, header); DumpExtensions(p, "Layer Extensions", layer.extension_properties); ObjectWrapper arr_devices(p, "Devices", gpus.size()); for (auto &gpu : gpus) { p.SetValueDescription(std::string(gpu->props.deviceName)).PrintKeyValue("GPU id", gpu->id); auto exts = gpu->inst.AppGetPhysicalDeviceLayerExtensions(gpu->phys_device, props.layerName); DumpExtensions(p, "Layer-Device Extensions", exts); p.AddNewline(); } } break; } case OutputType::json: { assert(false && "unimplemented"); break; } case OutputType::vkconfig_output: { ObjectWrapper obj(p, "Layer Properties"); for (auto &layer : layers) { ObjectWrapper obj_name(p, layer.layer_properties.layerName); p.SetMinKeyWidth(21); p.PrintKeyString("layerName", layer.layer_properties.layerName); p.PrintKeyString("version", APIVersion(layer.layer_properties.specVersion).str()); p.PrintKeyValue("implementation version", layer.layer_properties.implementationVersion); p.PrintKeyString("description", layer.layer_properties.description); DumpExtensions(p, "Layer Extensions", layer.extension_properties); ObjectWrapper obj_devices(p, "Devices"); for (auto &gpu : gpus) { ObjectWrapper obj_gpu(p, gpu->props.deviceName); p.SetValueDescription(std::string(gpu->props.deviceName)).PrintKeyValue("GPU id", gpu->id); auto exts = gpu->inst.AppGetPhysicalDeviceLayerExtensions(gpu->phys_device, layer.layer_properties.layerName); DumpExtensions(p, "Layer-Device Extensions", exts); } } break; } } } void DumpSurfaceFormats(Printer &p, AppInstance &inst, AppSurface &surface) { std::vector formats; if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) { for (auto &format : surface.surf_formats2) { formats.push_back(format.surfaceFormat); } } else { for (auto &format : surface.surf_formats) { formats.push_back(format); } } ObjectWrapper obj(p, "Formats", formats.size()); int i = 0; for (auto &format : formats) { p.SetElementIndex(i++); DumpVkSurfaceFormatKHR(p, "SurfaceFormat", format); } } void DumpPresentModes(Printer &p, AppSurface &surface) { ArrayWrapper arr(p, "Present Modes", surface.surf_present_modes.size()); for (auto &mode : surface.surf_present_modes) { p.SetAsType().PrintString(VkPresentModeKHRString(mode)); } } void DumpSurfaceCapabilities(Printer &p, AppInstance &inst, AppGpu &gpu, AppSurface &surface) { auto &surf_cap = surface.surface_capabilities; p.SetSubHeader().SetIgnoreMinWidthInChild(); DumpVkSurfaceCapabilitiesKHR(p, "VkSurfaceCapabilitiesKHR", surf_cap); if (inst.CheckExtensionEnabled(VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME)) { p.SetSubHeader(); ObjectWrapper obj(p, "VkSurfaceCapabilities2EXT"); DumpVkSurfaceCounterFlagsEXT(p, "supportedSurfaceCounters", surface.surface_capabilities2_ext.supportedSurfaceCounters); } if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) { chain_iterator_surface_capabilities2(p, inst, gpu, surface.surface_capabilities2_khr.pNext); } if (inst.CheckExtensionEnabled(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME) || inst.CheckExtensionEnabled(VK_KHR_SURFACE_MAINTENANCE_1_EXTENSION_NAME)) { const char *surf_ext_name = VK_KHR_SURFACE_MAINTENANCE_1_EXTENSION_NAME; if (!inst.CheckExtensionEnabled(VK_KHR_SURFACE_MAINTENANCE_1_EXTENSION_NAME)) { surf_ext_name = VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME; } p.SetSubHeader(); ObjectWrapper obj(p, surf_ext_name); for (auto &mode : surface.surf_present_modes) { VkSurfacePresentModeKHR present_mode{}; present_mode.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_KHR; present_mode.presentMode = mode; VkPhysicalDeviceSurfaceInfo2KHR surface_info{}; surface_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR; surface_info.surface = surface.surface_extension.surface; surface_info.pNext = &present_mode; VkSurfacePresentModeCompatibilityKHR SurfacePresentModeCompatibilityKHR{}; SurfacePresentModeCompatibilityKHR.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_KHR; VkSurfacePresentScalingCapabilitiesKHR SurfacePresentScalingCapabilitiesKHR{}; SurfacePresentScalingCapabilitiesKHR.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT; SurfacePresentScalingCapabilitiesKHR.pNext = &SurfacePresentModeCompatibilityKHR; VkSurfaceCapabilities2KHR surface_caps2{}; surface_caps2.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR; surface_caps2.pNext = &SurfacePresentScalingCapabilitiesKHR; VkResult err = vkGetPhysicalDeviceSurfaceCapabilities2KHR(gpu.phys_device, &surface_info, &surface_caps2); if (err != VK_SUCCESS) { continue; } std::vector compatible_present_modes{SurfacePresentModeCompatibilityKHR.presentModeCount}; SurfacePresentModeCompatibilityKHR.pPresentModes = compatible_present_modes.data(); err = vkGetPhysicalDeviceSurfaceCapabilities2KHR(gpu.phys_device, &surface_info, &surface_caps2); if (err == VK_SUCCESS) { ObjectWrapper present_mode_obj(p, VkPresentModeKHRString(mode)); p.PrintKeyValue("minImageCount", surface_caps2.surfaceCapabilities.minImageCount); p.PrintKeyValue("maxImageCount", surface_caps2.surfaceCapabilities.maxImageCount); DumpVkSurfacePresentScalingCapabilitiesKHR(p, "VkSurfacePresentScalingCapabilitiesKHR", SurfacePresentScalingCapabilitiesKHR); DumpVkSurfacePresentModeCompatibilityKHR(p, "VkSurfacePresentModeCompatibilityKHR", SurfacePresentModeCompatibilityKHR); } } } } void DumpSurface(Printer &p, AppInstance &inst, AppGpu &gpu, AppSurface &surface, std::set surface_types) { std::string surface_type_list_str = ""; if (surface_types.size() > 0) { surface_type_list_str = " ["; bool is_first = true; for (auto &name : surface_types) { if (!is_first) { surface_type_list_str += ", "; } else { is_first = false; } surface_type_list_str += name; } surface_type_list_str += "]"; } ObjectWrapper obj(p, std::string("GPU id : ") + p.DecorateAsValue(std::to_string(gpu.id)) + " (" + gpu.props.deviceName + ")" + surface_type_list_str); if (surface_types.size() == 0) { p.SetAsType().PrintKeyString("Surface type", "No type found"); } else if (surface_types.size() == 1) { p.SetAsType().PrintKeyString("Surface type", surface.surface_extension.name); } else { ArrayWrapper arr(p, "Surface types", surface_types.size()); for (auto &name : surface_types) { p.PrintString(name); } } DumpSurfaceFormats(p, inst, surface); DumpPresentModes(p, surface); DumpSurfaceCapabilities(p, inst, gpu, surface); p.AddNewline(); } struct SurfaceTypeGroup { AppSurface *surface; AppGpu *gpu; std::set surface_types; }; bool operator==(AppSurface const &a, AppSurface const &b) { return a.phys_device == b.phys_device && a.surf_present_modes == b.surf_present_modes && a.surf_formats == b.surf_formats && a.surf_formats2 == b.surf_formats2 && a.surface_capabilities == b.surface_capabilities && a.surface_capabilities2_khr == b.surface_capabilities2_khr && a.surface_capabilities2_ext == b.surface_capabilities2_ext; } #if defined(VULKANINFO_WSI_ENABLED) void DumpPresentableSurfaces(Printer &p, AppInstance &inst, const std::vector> &gpus, const std::vector> &surfaces) { // Don't print anything if no surfaces are found if (surfaces.size() == 0) return; p.SetHeader(); ObjectWrapper obj(p, "Presentable Surfaces"); IndentWrapper indent(p); std::vector surface_list; for (auto &surface : surfaces) { auto exists = surface_list.end(); for (auto it = surface_list.begin(); it != surface_list.end(); it++) { // check for duplicate surfaces that differ only by the surface extension if (*(it->surface) == *(surface.get())) { exists = it; break; } } if (exists != surface_list.end()) { exists->surface_types.insert(surface.get()->surface_extension.name); } else { // find surface.phys_device's corresponding AppGpu AppGpu *corresponding_gpu = nullptr; for (auto &gpu : gpus) { if (gpu->phys_device == surface->phys_device) corresponding_gpu = gpu.get(); } if (corresponding_gpu != nullptr) surface_list.push_back({surface.get(), corresponding_gpu, {surface.get()->surface_extension.name}}); } } for (auto &group : surface_list) { DumpSurface(p, inst, *group.gpu, *group.surface, group.surface_types); } p.AddNewline(); } #endif // defined(VULKANINFO_WSI_ENABLED) void DumpGroups(Printer &p, AppInstance &inst) { if (inst.CheckExtensionEnabled(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME)) { auto groups = GetGroups(inst); if (groups.size() == 0) { p.SetHeader(); ObjectWrapper obj(p, "Groups"); p.PrintString("No Device Groups Found"); p.AddNewline(); return; } p.SetHeader(); ObjectWrapper obj_device_groups(p, "Device Groups"); IndentWrapper indent(p); int group_id = 0; for (auto &group : groups) { ObjectWrapper obj_group(p, "Group " + std::to_string(group_id)); auto group_props = GetGroupProps(inst, group); { ObjectWrapper obj_properties(p, "Properties"); { ArrayWrapper arr(p, "physicalDevices", group.physicalDeviceCount); int id = 0; for (auto &prop : group_props) { p.PrintString(std::string(prop.deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(id++)) + ")"); } } p.PrintKeyValue("subsetAllocation", group.subsetAllocation); } p.AddNewline(); auto group_capabilities = GetGroupCapabilities(inst, group); if (!group_capabilities) { p.PrintKeyString("Present Capabilities", "Group does not support VK_KHR_device_group, skipping printing present capabilities"); } else { ObjectWrapper obj_caps(p, "Present Capabilities"); for (uint32_t i = 0; i < group.physicalDeviceCount; i++) { ObjectWrapper obj_device( p, std::string(group_props[i].deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(i)) + ")"); ArrayWrapper arr(p, "Can present images from the following devices", group.physicalDeviceCount); for (uint32_t j = 0; j < group.physicalDeviceCount; j++) { uint32_t mask = 1 << j; if (group_capabilities->presentMask[i] & mask) { p.PrintString(std::string(group_props[j].deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(j)) + ")"); } } } DumpVkDeviceGroupPresentModeFlagsKHR(p, "Present modes", group_capabilities->modes); } p.AddNewline(); group_id++; } p.AddNewline(); } } void GpuDumpProps(Printer &p, AppGpu &gpu, bool show_promoted_structs) { auto props = gpu.GetDeviceProperties(); p.SetSubHeader(); { ObjectWrapper obj(p, "VkPhysicalDeviceProperties"); p.SetMinKeyWidth(17); if (p.Type() == OutputType::json) { p.PrintKeyValue("apiVersion", props.apiVersion); p.PrintKeyValue("driverVersion", props.driverVersion); } else { p.SetValueDescription(std::to_string(props.apiVersion)).PrintKeyString("apiVersion", APIVersion(props.apiVersion)); p.SetValueDescription(std::to_string(props.driverVersion)) .PrintKeyString("driverVersion", gpu.GetDriverVersionString()); } p.PrintKeyString("vendorID", to_hex_str(props.vendorID)); p.PrintKeyString("deviceID", to_hex_str(props.deviceID)); p.PrintKeyString("deviceType", VkPhysicalDeviceTypeString(props.deviceType)); p.PrintKeyString("deviceName", props.deviceName); p.PrintKeyValue("pipelineCacheUUID", props.pipelineCacheUUID); } p.AddNewline(); DumpVkPhysicalDeviceLimits(p, "VkPhysicalDeviceLimits", gpu.props.limits); p.AddNewline(); DumpVkPhysicalDeviceSparseProperties(p, "VkPhysicalDeviceSparseProperties", gpu.props.sparseProperties); p.AddNewline(); if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { void *place = gpu.props2.pNext; chain_iterator_phys_device_props2(p, gpu.inst, gpu, show_promoted_structs, place); } } void GpuDumpQueueProps(Printer &p, AppGpu &gpu, const AppQueueFamilyProperties &queue) { VkQueueFamilyProperties props = queue.props; p.SetSubHeader().SetElementIndex(static_cast(queue.queue_index)); ObjectWrapper obj_queue_props(p, "queueProperties"); p.SetMinKeyWidth(27); if (p.Type() == OutputType::vkconfig_output) { DumpVkExtent3D(p, "minImageTransferGranularity", props.minImageTransferGranularity); } else { p.PrintKeyValue("minImageTransferGranularity", props.minImageTransferGranularity); } p.PrintKeyValue("queueCount", props.queueCount); p.PrintKeyString("queueFlags", VkQueueFlagsString(props.queueFlags)); p.PrintKeyValue("timestampValidBits", props.timestampValidBits); if (!queue.can_present) { p.PrintKeyString("present support", "false"); } else if (queue.can_always_present) { p.PrintKeyString("present support", "true"); } else { size_t width = 0; for (const auto &support : queue.present_support) { if (support.first.size() > width) width = support.first.size(); } ObjectWrapper obj_present_support(p, "present support"); p.SetMinKeyWidth(width); for (const auto &support : queue.present_support) { p.PrintKeyString(support.first, support.second ? "true" : "false"); } } chain_iterator_queue_properties2(p, gpu, queue.pNext); p.AddNewline(); } // This prints a number of bytes in a human-readable format according to prefixes of the International System of Quantities (ISQ), // defined in ISO/IEC 80000. The prefixes used here are not SI prefixes, but rather the binary prefixes based on powers of 1024 // (kibi-, mebi-, gibi- etc.). #define kBufferSize 32 std::string NumToNiceStr(const size_t sz) { const char prefixes[] = "KMGTPEZY"; char buf[kBufferSize]; int which = -1; double result = (double)sz; while (result > 1024 && which < 7) { result /= 1024; ++which; } char unit[] = "\0i"; if (which >= 0) { unit[0] = prefixes[which]; } #ifdef _WIN32 _snprintf_s(buf, kBufferSize * sizeof(char), kBufferSize, "%.2f %sB", result, unit); #else snprintf(buf, kBufferSize, "%.2f %sB", result, unit); #endif return std::string(buf); } std::string append_human_readable(VkDeviceSize memory) { return std::to_string(memory) + " (" + to_hex_str(memory) + ") (" + NumToNiceStr(static_cast(memory)) + ")"; } void GpuDumpMemoryProps(Printer &p, AppGpu &gpu) { p.SetHeader(); ObjectWrapper obj_mem_props(p, "VkPhysicalDeviceMemoryProperties"); IndentWrapper indent(p); { ObjectWrapper obj_mem_heaps(p, "memoryHeaps", gpu.memory_props.memoryHeapCount); for (uint32_t i = 0; i < gpu.memory_props.memoryHeapCount; ++i) { p.SetElementIndex(static_cast(i)); ObjectWrapper obj_mem_heap(p, "memoryHeaps"); p.SetMinKeyWidth(6); p.PrintKeyString("size", append_human_readable(gpu.memory_props.memoryHeaps[i].size)); if (gpu.CheckPhysicalDeviceExtensionIncluded(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME)) { p.PrintKeyString("budget", append_human_readable(gpu.heapBudget[i])); p.PrintKeyString("usage", append_human_readable(gpu.heapUsage[i])); } DumpVkMemoryHeapFlags(p, "flags", gpu.memory_props.memoryHeaps[i].flags); } } { ObjectWrapper obj_mem_types(p, "memoryTypes", gpu.memory_props.memoryTypeCount); for (uint32_t i = 0; i < gpu.memory_props.memoryTypeCount; ++i) { p.SetElementIndex(static_cast(i)); ObjectWrapper obj_mem_type(p, "memoryTypes"); p.SetMinKeyWidth(13); p.PrintKeyValue("heapIndex", gpu.memory_props.memoryTypes[i].heapIndex); auto flags = gpu.memory_props.memoryTypes[i].propertyFlags; DumpVkMemoryPropertyFlags(p, "propertyFlags = " + to_hex_str(flags), flags); ObjectWrapper usable_for(p, "usable for"); const uint32_t memtype_bit = 1U << i; // only linear and optimal tiling considered for (auto &image_tiling : gpu.memory_image_support_types) { p.SetOpenDetails(); ArrayWrapper arr(p, VkImageTilingString(VkImageTiling(image_tiling.tiling))); bool has_any_support_types = false; bool regular = false; bool transient = false; bool sparse = false; for (auto &image_format : image_tiling.formats) { if (image_format.type_support.size() > 0) { bool has_a_support_type = false; for (auto &img_type : image_format.type_support) { if (img_type.Compatible(memtype_bit)) { has_a_support_type = true; has_any_support_types = true; if (img_type.type == ImageTypeSupport::Type::regular) regular = true; if (img_type.type == ImageTypeSupport::Type::transient) transient = true; if (img_type.type == ImageTypeSupport::Type::sparse) sparse = true; } } if (has_a_support_type) { if (image_format.format == color_format) { p.PrintString("color images"); } else { p.PrintString(VkFormatString(image_format.format)); } } } } if (!has_any_support_types) { p.PrintString("None"); } else { if (regular && !transient && sparse) p.PrintString("(non-transient)"); if (regular && transient && !sparse) p.PrintString("(non-sparse)"); if (regular && !transient && !sparse) p.PrintString("(non-sparse, non-transient)"); if (!regular && transient && sparse) p.PrintString("(sparse and transient only)"); if (!regular && !transient && sparse) p.PrintString("(sparse only)"); if (!regular && transient && !sparse) p.PrintString("(transient only)"); } } } } p.AddNewline(); } void GpuDumpFeatures(Printer &p, AppGpu &gpu, bool show_promoted_structs) { p.SetHeader(); DumpVkPhysicalDeviceFeatures(p, "VkPhysicalDeviceFeatures", gpu.features); p.AddNewline(); if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { void *place = gpu.features2.pNext; chain_iterator_phys_device_features2(p, gpu, show_promoted_structs, place); } } void GpuDumpTextFormatProperty(Printer &p, const AppGpu &gpu, PropFlags formats, const std::set &format_list, uint32_t counter) { p.SetElementIndex(counter); ObjectWrapper obj_common_group(p, "Common Format Group"); IndentWrapper indent_inner(p); { ArrayWrapper arr_formats(p, "Formats", format_list.size()); for (auto &fmt : format_list) { p.SetAsType().PrintString(VkFormatString(fmt)); } } ObjectWrapper obj(p, "Properties"); if (gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME)) { DumpVkFormatFeatureFlags2(p, "linearTilingFeatures", formats.props3.linearTilingFeatures); DumpVkFormatFeatureFlags2(p, "optimalTilingFeatures", formats.props3.optimalTilingFeatures); DumpVkFormatFeatureFlags2(p, "bufferFeatures", formats.props3.bufferFeatures); } else { DumpVkFormatFeatureFlags(p, "linearTilingFeatures", formats.props.linearTilingFeatures); DumpVkFormatFeatureFlags(p, "optimalTilingFeatures", formats.props.optimalTilingFeatures); DumpVkFormatFeatureFlags(p, "bufferFeatures", formats.props.bufferFeatures); } p.AddNewline(); } void GpuDumpToolingInfo(Printer &p, AppGpu &gpu) { auto tools = GetToolingInfo(gpu); if (tools.size() > 0) { p.SetSubHeader(); ObjectWrapper obj(p, "Tooling Info"); for (auto tool : tools) { DumpVkPhysicalDeviceToolProperties(p, tool.name, tool); p.AddNewline(); } } } void GpuDumpCooperativeMatrix(Printer &p, AppGpu &gpu) { auto props = GetCooperativeMatrixInfo(gpu); if (props.size() > 0) { p.SetSubHeader(); ObjectWrapper obj(p, "vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR"); for (const auto prop : props) { DumpVkCooperativeMatrixPropertiesKHR(p, "VkCooperativeMatrixPropertiesKHR", prop); p.AddNewline(); } } } void GpuDumpCalibrateableTimeDomain(Printer &p, AppGpu &gpu) { auto props = GetTimeDomainInfo(gpu); if (props.size() > 0) { p.SetSubHeader(); ObjectWrapper obj_mem_props(p, "vkGetPhysicalDeviceCalibrateableTimeDomainsKHR"); { for (uint32_t i = 0; i < props.size(); ++i) { p.SetElementIndex(static_cast(i)); DumpVkTimeDomainKHR(p, "pTimeDomains", props[i]); p.AddNewline(); } } } } void GpuDumpFragmentShadingRate(Printer &p, AppGpu &gpu) { auto props = GetFragmentShadingRateInfo(gpu); if (props.size() > 0) { p.SetSubHeader(); ObjectWrapper obj(p, "vkGetPhysicalDeviceFragmentShadingRatesKHR"); for (const auto prop : props) { DumpVkPhysicalDeviceFragmentShadingRateKHR(p, "VkPhysicalDeviceFragmentShadingRateKHR", prop); p.AddNewline(); } } } // VK_EXT_sample_locations ('Multisample Properties' is too ambiguous of a name) void GpuDumpSampleLocations(Printer &p, AppGpu &gpu) { auto props = GetSampleLocationInfo(gpu); if (props.size() > 0) { p.SetSubHeader(); ObjectWrapper obj(p, "vkGetPhysicalDeviceMultisamplePropertiesEXT"); for (uint32_t i = 0; i < props.size(); i++) { const VkSampleCountFlagBits sample_count = (VkSampleCountFlagBits)(1 << i); DumpVkSampleCountFlagBits(p, "samples", sample_count); DumpVkMultisamplePropertiesEXT(p, "VkMultisamplePropertiesEXT", props[i]); p.AddNewline(); } } } void GpuDevDump(Printer &p, AppGpu &gpu) { p.SetHeader(); ObjectWrapper obj_format_props(p, "Format Properties"); IndentWrapper indent_outer(p); if (p.Type() == OutputType::text) { auto fmtPropMap = FormatPropMap(gpu); int counter = 0; std::set unsupported_formats; for (auto &prop : fmtPropMap) { VkFormatProperties props = prop.first.props; VkFormatProperties3 props3 = prop.first.props3; if (props.linearTilingFeatures == 0 && props.optimalTilingFeatures == 0 && props.bufferFeatures == 0 && props3.linearTilingFeatures == 0 && props3.optimalTilingFeatures == 0 && props3.bufferFeatures == 0) { unsupported_formats = prop.second; continue; } GpuDumpTextFormatProperty(p, gpu, prop.first, prop.second, counter++); } ArrayWrapper arr_unsupported_formats(p, "Unsupported Formats", unsupported_formats.size()); for (auto &fmt : unsupported_formats) { p.SetAsType().PrintString(VkFormatString(fmt)); } } else { std::set formats_to_print; for (auto &format_range : format_ranges) { if (gpu.FormatRangeSupported(format_range)) { for (int32_t fmt_counter = format_range.first_format; fmt_counter <= format_range.last_format; ++fmt_counter) { formats_to_print.insert(static_cast(fmt_counter)); } } } for (const auto &fmt : formats_to_print) { auto formats = get_format_properties(gpu, fmt); p.SetTitleAsType(); if (gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME)) { DumpVkFormatProperties3(p, VkFormatString(fmt), formats.props3); } else { DumpVkFormatProperties(p, VkFormatString(fmt), formats.props); } } } p.AddNewline(); } void DumpVkVideoProfileInfoKHRCustom(Printer &p, std::string name, const VkVideoProfileInfoKHR &obj) { // We use custom dumping here because we do not want to output ignored fields // e.g. for monochrome chromaBitDepth is ignored ObjectWrapper object{p, name}; DumpVkVideoCodecOperationFlagBitsKHR(p, "videoCodecOperation", obj.videoCodecOperation); DumpVkVideoChromaSubsamplingFlagsKHR(p, "chromaSubsampling", obj.chromaSubsampling); DumpVkVideoComponentBitDepthFlagsKHR(p, "lumaBitDepth", obj.lumaBitDepth); if (obj.chromaSubsampling != VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR) { DumpVkVideoComponentBitDepthFlagsKHR(p, "chromaBitDepth", obj.chromaBitDepth); } else { DumpVkVideoComponentBitDepthFlagsKHR(p, "chromaBitDepth", 0); } } void GpuDumpVideoProfiles(Printer &p, AppGpu &gpu, bool show_video_props) { p.SetHeader(); ObjectWrapper video_profiles_obj(p, "Video Profiles", gpu.video_profiles.size()); IndentWrapper indent_outer(p); if (p.Type() != OutputType::text || show_video_props) { // Video profile details per profile for (const auto &video_profile : gpu.video_profiles) { p.SetSubHeader(); ObjectWrapper video_profile_obj(p, video_profile->name); IndentWrapper indent_inner(p); { p.SetSubHeader(); ObjectWrapper profile_info_obj(p, "Video Profile Definition"); p.SetSubHeader(); DumpVkVideoProfileInfoKHRCustom(p, "VkVideoProfileInfoKHR", video_profile->profile_info); chain_iterator_video_profile_info(p, gpu, video_profile->profile_info.pNext); } { p.SetSubHeader(); ObjectWrapper capabilities_obj(p, "Video Profile Capabilities"); p.SetSubHeader(); DumpVkVideoCapabilitiesKHR(p, "VkVideoCapabilitiesKHR", video_profile->capabilities); chain_iterator_video_capabilities(p, gpu, video_profile->capabilities.pNext); } { p.SetSubHeader(); ObjectWrapper video_formats_obj(p, "Video Formats"); for (const auto &video_formats_it : video_profile->formats_by_category) { const auto &video_format_category_name = video_formats_it.first; const auto &video_format_props = video_formats_it.second; ObjectWrapper video_format_category(p, video_format_category_name, video_format_props.size()); for (size_t i = 0; i < video_format_props.size(); ++i) { ObjectWrapper video_format_obj(p, video_format_category_name + " Format #" + std::to_string(i + 1)); p.SetSubHeader(); DumpVkVideoFormatPropertiesKHR(p, "VkVideoFormatPropertiesKHR", video_format_props[i]); chain_iterator_video_format_properties(p, gpu, video_format_props[i].pNext); } } } p.AddNewline(); } } else { // Video profile list only for (const auto &video_profile : gpu.video_profiles) { p.PrintString(video_profile->name); } } p.AddNewline(); } AppDisplayPlane::AppDisplayPlane(AppGpu &gpu, uint32_t index, const VkDisplayPlanePropertiesKHR &in_prop) : global_index(index), properties(in_prop) { std::ostringstream display_name; supported_displays = GetVector("vkGetDisplayPlaneSupportedDisplaysKHR", vkGetDisplayPlaneSupportedDisplaysKHR, gpu.phys_device, global_index); } std::vector enumerate_display_planes(AppGpu &gpu) { std::vector result; if (vkGetPhysicalDeviceDisplayPlanePropertiesKHR) { auto planes = GetVector("vkGetPhysicalDeviceDisplayPlanePropertiesKHR", vkGetPhysicalDeviceDisplayPlanePropertiesKHR, gpu.phys_device); for (uint32_t i = 0; i < planes.size(); i++) { result.emplace_back(gpu, i, planes[i]); } } return result; } AppDisplayMode::AppDisplayMode(AppGpu &gpu, const VkDisplayModePropertiesKHR &in_properties, const std::set &supported_planes) : properties(in_properties) { for (auto plane : supported_planes) { VkDisplayPlaneCapabilitiesKHR cap; vkGetDisplayPlaneCapabilitiesKHR(gpu.phys_device, properties.displayMode, plane, &cap); capabilities[plane] = cap; } } static std::string MakeName(uint32_t index, const VkDisplayPropertiesKHR &prop) { std::stringstream name; name << "Display id : " << index << " (" << prop.displayName << ")"; return name.str(); } AppDisplay::AppDisplay(AppGpu &gpu, uint32_t index, const VkDisplayPropertiesKHR &in_properties, const std::vector &all_planes) : global_index(index), name(MakeName(index, in_properties)), properties(in_properties) { auto mode_props = GetVector("vkGetDisplayModePropertiesKHR", vkGetDisplayModePropertiesKHR, gpu.phys_device, properties.display); std::set supported_planes; for (const auto &plane : all_planes) { for (const auto &display : plane.supported_displays) { if (display == properties.display) { supported_planes.insert(plane.global_index); } } } for (const auto &prop : mode_props) { modes.emplace_back(gpu, prop, supported_planes); } } std::vector enumerate_displays(AppGpu &gpu, const std::vector &all_planes) { std::vector result; if (vkGetPhysicalDeviceDisplayPropertiesKHR) { auto properties = GetVector("vkGetPhysicalDeviceDisplayPropertiesKHR", vkGetPhysicalDeviceDisplayPropertiesKHR, gpu.phys_device); for (uint32_t i = 0; i < properties.size(); i++) { result.emplace_back(gpu, i, properties[i], all_planes); } } return result; } void GpuDumpDisplays(Printer &p, AppGpu &gpu) { if (gpu.displays.size() == 0) { return; } p.SetHeader(); ObjectWrapper display_props_obj(p, "Display Properties", gpu.displays.size()); for (const auto &display : gpu.displays) { p.SetSubHeader(); ObjectWrapper display_obj(p, display.name); DumpVkDisplayPropertiesKHR(p, "VkDisplayPropertiesKHR", display.properties); ArrayWrapper arr(p, "Display Modes", display.modes.size()); for (uint32_t m = 0; m < display.modes.size(); m++) { const auto &mode = display.modes[m]; std::ostringstream mode_name; mode_name << "Display Mode : " << p.DecorateAsValue(std::to_string(m)) << " (" << mode.properties.parameters.visibleRegion.width << " x " << mode.properties.parameters.visibleRegion.height << ")"; ObjectWrapper mode_obj(p, mode_name.str()); DumpVkDisplayModeParametersKHR(p, "VkDisplayModeParametersKHR", mode.properties.parameters); ArrayWrapper cap_arr(p, "Display Plane Capabilities", mode.capabilities.size()); for (const auto &cap : mode.capabilities) { std::ostringstream cap_name; cap_name << "Capabilities (Plane " << p.DecorateAsValue(std::to_string(cap.first)) << ")"; ObjectWrapper cap_obj(p, cap_name.str()); DumpVkDisplayPlaneCapabilitiesKHR(p, "VkDisplayPlaneCapabilitiesKHR", cap.second); } } } } void GpuDumpDisplayPlanes(Printer &p, AppGpu &gpu) { if (gpu.display_planes.size() == 0) { return; } p.SetHeader(); ObjectWrapper display_props_obj(p, "Display Plane Properties", gpu.display_planes.size()); for (const auto &plane : gpu.display_planes) { p.SetSubHeader(); ObjectWrapper display_obj(p, std::string("Display Plane id : ") + p.DecorateAsValue(std::to_string(plane.global_index))); auto *current = gpu.FindDisplay(plane.properties.currentDisplay); p.PrintKeyString("currentDisplay", current ? current->name.c_str() : "none"); DumpVkDisplayPlanePropertiesKHR(p, "VkDisplayPlanePropertiesKHR", plane.properties); ArrayWrapper arr(p, "Supported Displays", plane.supported_displays.size()); for (auto handle : plane.supported_displays) { auto *display = gpu.FindDisplay(handle); p.SetAsType().PrintString(display ? display->name.c_str() : "UNKNOWN"); } } } struct ShowSettings { bool all = false; bool tool_props = false; bool formats = false; bool promoted_structs = false; bool video_props = false; }; // Print gpu info for text, html, & vkconfig_output // Uses a separate function than schema-json for clarity void DumpGpu(Printer &p, AppGpu &gpu, const ShowSettings &show) { ObjectWrapper obj_gpu(p, "GPU" + std::to_string(gpu.id)); IndentWrapper indent(p); GpuDumpProps(p, gpu, show.promoted_structs); DumpExtensions(p, "Device Extensions", gpu.device_extensions); p.AddNewline(); { p.SetHeader(); ObjectWrapper obj_family_props(p, "VkQueueFamilyProperties"); for (const auto &queue_prop : gpu.extended_queue_props) { GpuDumpQueueProps(p, gpu, queue_prop); } } GpuDumpMemoryProps(p, gpu); GpuDumpFeatures(p, gpu, show.promoted_structs); if (show.tool_props) { GpuDumpToolingInfo(p, gpu); } if (show.all) { GpuDumpCooperativeMatrix(p, gpu); GpuDumpCalibrateableTimeDomain(p, gpu); GpuDumpFragmentShadingRate(p, gpu); GpuDumpSampleLocations(p, gpu); } if (p.Type() != OutputType::text || show.formats) { GpuDevDump(p, gpu); } if (!gpu.video_profiles.empty()) { GpuDumpVideoProfiles(p, gpu, show.video_props); } if (!gpu.displays.empty()) { GpuDumpDisplays(p, gpu); } if (!gpu.display_planes.empty()) { GpuDumpDisplayPlanes(p, gpu); } p.AddNewline(); } // Print capabilities section of profiles schema void DumpGpuProfileCapabilities(Printer &p, AppGpu &gpu, bool show_promoted_structs) { ObjectWrapper capabilities(p, "capabilities"); { ObjectWrapper temp_name_obj(p, "device"); DumpExtensions(p, "extensions", gpu.device_extensions); { ObjectWrapper obj(p, "features"); GpuDumpFeatures(p, gpu, show_promoted_structs); } { ObjectWrapper obj(p, "properties"); { ObjectWrapper props_obj(p, "VkPhysicalDeviceProperties"); auto props = gpu.GetDeviceProperties(); p.PrintKeyValue("apiVersion", props.apiVersion); p.PrintKeyValue("deviceID", props.deviceID); p.PrintKeyString("deviceName", props.deviceName); p.PrintKeyString("deviceType", std::string("VK_") + VkPhysicalDeviceTypeString(props.deviceType)); p.PrintKeyValue("driverVersion", props.driverVersion); DumpVkPhysicalDeviceLimits(p, "VkPhysicalDeviceLimits", gpu.props.limits); { ArrayWrapper arr(p, "pipelineCacheUUID"); for (const auto &uuid : props.pipelineCacheUUID) p.PrintElement(static_cast(uuid)); } DumpVkPhysicalDeviceSparseProperties(p, "VkPhysicalDeviceSparseProperties", gpu.props.sparseProperties); p.PrintKeyValue("vendorID", props.vendorID); } if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { void *place = gpu.props2.pNext; chain_iterator_phys_device_props2(p, gpu.inst, gpu, show_promoted_structs, place); } } { ObjectWrapper obj(p, "formats"); std::set already_printed_formats; for (const auto &format : format_ranges) { if (gpu.FormatRangeSupported(format)) { for (int32_t fmt_counter = format.first_format; fmt_counter <= format.last_format; ++fmt_counter) { VkFormat fmt = static_cast(fmt_counter); if (already_printed_formats.count(fmt) > 0) { continue; } auto formats = get_format_properties(gpu, fmt); // don't print format properties that are unsupported if (formats.props.linearTilingFeatures == 0 && formats.props.optimalTilingFeatures == 0 && formats.props.bufferFeatures == 0 && formats.props3.linearTilingFeatures == 0 && formats.props3.optimalTilingFeatures == 0 && formats.props3.bufferFeatures == 0) continue; ObjectWrapper format_obj(p, std::string("VK_") + VkFormatString(fmt)); { // Want to explicitly list VkFormatProperties in addition to VkFormatProperties3 if available DumpVkFormatProperties(p, "VkFormatProperties", formats.props); VkFormatProperties2 format_props2{}; format_props2.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; format_props2.formatProperties = formats.props; std::unique_ptr chain_for_format_props2; setup_format_properties2_chain(format_props2, chain_for_format_props2, gpu); vkGetPhysicalDeviceFormatProperties2KHR(gpu.phys_device, fmt, &format_props2); chain_iterator_format_properties2(p, gpu, format_props2.pNext); } already_printed_formats.insert(fmt); } } } } { ArrayWrapper arr(p, "queueFamiliesProperties"); for (const auto &extended_queue_prop : gpu.extended_queue_props) { ObjectWrapper queue_obj(p, ""); { ObjectWrapper obj_queue_props(p, "VkQueueFamilyProperties"); VkQueueFamilyProperties props = extended_queue_prop.props; DumpVkExtent3D(p, "minImageTransferGranularity", props.minImageTransferGranularity); p.PrintKeyValue("queueCount", props.queueCount); DumpVkQueueFlags(p, "queueFlags", props.queueFlags); p.PrintKeyValue("timestampValidBits", props.timestampValidBits); } chain_iterator_queue_properties2(p, gpu, extended_queue_prop.pNext); } } if (!gpu.video_profiles.empty()) { ArrayWrapper video_profiles(p, "videoProfiles"); for (const auto &video_profile : gpu.video_profiles) { ObjectWrapper video_profile_obj(p, ""); { ObjectWrapper profile_info_obj(p, "profile"); DumpVkVideoProfileInfoKHRCustom(p, "VkVideoProfileInfoKHR", video_profile->profile_info); chain_iterator_video_profile_info(p, gpu, video_profile->profile_info.pNext); } { ObjectWrapper capabilities_obj(p, "capabilities"); DumpVkVideoCapabilitiesKHR(p, "VkVideoCapabilitiesKHR", video_profile->capabilities); chain_iterator_video_capabilities(p, gpu, video_profile->capabilities.pNext); } { ArrayWrapper video_formats(p, "formats"); for (const auto &video_format : video_profile->formats) { ObjectWrapper video_format_obj(p, ""); DumpVkVideoFormatPropertiesKHR(p, "VkVideoFormatPropertiesKHR", video_format.properties); chain_iterator_video_format_properties(p, gpu, video_format.properties.pNext); } } } } } #if defined(VK_ENABLE_BETA_EXTENSIONS) // Print portability subset extension, features, and properties if available if (gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) && (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) || gpu.inst.api_version >= VK_API_VERSION_1_1)) { ObjectWrapper macos_obj(p, "macos-specific"); { ObjectWrapper ext_obj(p, "extensions"); const std::string portability_ext_name = VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME; for (const auto &ext : gpu.device_extensions) { if (portability_ext_name == ext.extensionName) { p.PrintExtension(ext.extensionName, ext.specVersion); } } } { ObjectWrapper features_obj(p, "features"); void *feats_place = gpu.features2.pNext; while (feats_place) { VkBaseOutStructure *structure = static_cast(feats_place); if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR) { auto *features = reinterpret_cast(structure); DumpVkPhysicalDevicePortabilitySubsetFeaturesKHR(p, "VkPhysicalDevicePortabilitySubsetFeaturesKHR", *features); break; } feats_place = structure->pNext; } } { ObjectWrapper property_obj(p, "properties"); void *props_place = gpu.props2.pNext; while (props_place) { VkBaseOutStructure *structure = static_cast(props_place); if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR) { auto *props = reinterpret_cast(structure); DumpVkPhysicalDevicePortabilitySubsetPropertiesKHR(p, "VkPhysicalDevicePortabilitySubsetPropertiesKHR", *props); break; } props_place = structure->pNext; } } } #endif // defined(VK_ENABLE_BETA_EXTENSIONS) } void PrintProfileBaseInfo(Printer &p, const std::string &device_name, uint32_t apiVersion, const std::string &device_label, const std::vector &capabilities) { ObjectWrapper vk_info(p, device_name); p.PrintKeyValue("version", 1); p.PrintKeyString("api-version", APIVersion(apiVersion).str()); p.PrintKeyString("label", device_label); p.PrintKeyString("description", std::string("Exported from ") + APP_SHORT_NAME); { ObjectWrapper contributors(p, "contributors"); } { ArrayWrapper contributors(p, "history"); ObjectWrapper element(p, ""); p.PrintKeyValue("revision", 1); std::time_t t = std::time(0); // get time now std::tm *now = std::localtime(&t); std::string date = std::to_string(now->tm_year + 1900) + '-' + std::to_string(now->tm_mon + 1) + '-' + std::to_string(now->tm_mday); p.PrintKeyString("date", date); p.PrintKeyString("author", std::string("Automated export from ") + APP_SHORT_NAME); p.PrintKeyString("comment", ""); } ArrayWrapper contributors(p, "capabilities"); for (const auto &str : capabilities) p.PrintString(str); } // Prints profiles section of profiles schema void DumpGpuProfileInfo(Printer &p, AppGpu &gpu) { ObjectWrapper profiles(p, "profiles"); std::string device_label = std::string(gpu.props.deviceName) + " driver " + gpu.GetDriverVersionString(); std::string device_name = std::string("VP_" APP_UPPER_CASE_NAME "_") + std::string(gpu.props.deviceName) + "_" + gpu.GetDriverVersionString(); ; for (auto &c : device_name) { if (c == ' ' || c == '.') c = '_'; } PrintProfileBaseInfo(p, device_name, gpu.props.apiVersion, device_label, {"device"}); #if defined(VK_ENABLE_BETA_EXTENSIONS) if (gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) && (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) || gpu.inst.api_version >= VK_API_VERSION_1_1)) { PrintProfileBaseInfo(p, device_name + "_portability_subset", gpu.props.apiVersion, device_label + " subset", {"device", "macos-specific"}); } #endif // defined(VK_ENABLE_BETA_EXTENSIONS) } // Print summary of system void DumpSummaryInstance(Printer &p, AppInstance &inst) { p.SetSubHeader(); DumpExtensions(p, "Instance Extensions", inst.global_extensions, true); p.AddNewline(); p.SetSubHeader(); ArrayWrapper arr(p, "Instance Layers", inst.global_layers.size()); IndentWrapper indent(p); std::sort(inst.global_layers.begin(), inst.global_layers.end(), [](LayerExtensionList &left, LayerExtensionList &right) -> int { return std::strncmp(left.layer_properties.layerName, right.layer_properties.layerName, VK_MAX_DESCRIPTION_SIZE) < 0; }); size_t layer_name_max = 0; size_t layer_desc_max = 0; size_t layer_version_max = 0; // find max of each type to align everything in columns for (auto &layer : inst.global_layers) { auto props = layer.layer_properties; layer_name_max = std::max(layer_name_max, strlen(props.layerName)); layer_desc_max = std::max(layer_desc_max, strlen(props.description)); layer_version_max = std::max(layer_version_max, APIVersion(layer.layer_properties.specVersion).str().size()); } for (auto &layer : inst.global_layers) { auto v_str = APIVersion(layer.layer_properties.specVersion).str(); auto props = layer.layer_properties; auto name_padding = std::string(layer_name_max - strlen(props.layerName), ' '); auto desc_padding = std::string(layer_desc_max - strlen(props.description), ' '); auto version_padding = std::string(layer_version_max - v_str.size(), ' '); p.PrintString(std::string(props.layerName) + name_padding + " " + props.description + desc_padding + " " + v_str + " " + version_padding + " version " + std::to_string(props.implementationVersion)); } p.AddNewline(); } void DumpSummaryGPU(Printer &p, AppGpu &gpu) { ObjectWrapper obj(p, "GPU" + std::to_string(gpu.id)); p.SetMinKeyWidth(18); auto props = gpu.GetDeviceProperties(); p.PrintKeyValue("apiVersion", APIVersion(props.apiVersion)); if (gpu.found_driver_props) { p.PrintKeyString("driverVersion", gpu.GetDriverVersionString()); } else { p.PrintKeyValue("driverVersion", props.driverVersion); } p.PrintKeyString("vendorID", to_hex_str(props.vendorID)); p.PrintKeyString("deviceID", to_hex_str(props.deviceID)); p.PrintKeyString("deviceType", VkPhysicalDeviceTypeString(props.deviceType)); p.PrintKeyString("deviceName", props.deviceName); if (gpu.found_driver_props) { DumpVkDriverId(p, "driverID", gpu.driverID); p.PrintKeyString("driverName", gpu.driverName); p.PrintKeyString("driverInfo", gpu.driverInfo); p.PrintKeyValue("conformanceVersion", gpu.conformanceVersion); } if (gpu.found_device_id_props) { p.PrintKeyValue("deviceUUID", gpu.deviceUUID); p.PrintKeyValue("driverUUID", gpu.driverUUID); } } // ============ Printing Logic ============= // #ifdef _WIN32 // Enlarges the console window to have a large scrollback size. static void ConsoleEnlarge() { const HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); // make the console window bigger CONSOLE_SCREEN_BUFFER_INFO csbi; COORD buffer_size; if (GetConsoleScreenBufferInfo(console_handle, &csbi)) { buffer_size.X = csbi.dwSize.X + 30; buffer_size.Y = 20000; SetConsoleScreenBufferSize(console_handle, buffer_size); } SMALL_RECT r; r.Left = r.Top = 0; r.Right = csbi.dwSize.X - 1 + 30; r.Bottom = 50; SetConsoleWindowInfo(console_handle, true, &r); // change the console window title SetConsoleTitle(TEXT(APP_SHORT_NAME)); } #endif // Global configuration enum class OutputCategory { text, html, profile_json, vkconfig_output, summary }; const char *help_message_body = "OPTIONS:\n" "[-h, --help] Print this help.\n" "[--summary] Show a summary of the instance and GPU's on a system.\n" "[-o , --output ]\n" " Print output to a new file whose name is specified by filename.\n" " File will be written to the current working directory.\n" "[--text] Produce a text version of " APP_SHORT_NAME " output to stdout. This is\n" " the default output.\n" "[--html] Produce an html version of " APP_SHORT_NAME " output, saved as\n" " \"" APP_SHORT_NAME ".html\" in the directory in which the command\n" " is run.\n" "[-j, --json] Produce a json version of " APP_SHORT_NAME " output conforming to the Vulkan\n" " Profiles schema, saved as \n" " \"VP_" APP_UPPER_CASE_NAME "_[DEVICE_NAME]_[DRIVER_VERSION].json\"\n" " of the first gpu in the system.\n" "[-j=, --json=]\n" " For a multi-gpu system, a single gpu can be targeted by\n" " specifying the gpu-number associated with the gpu of \n" " interest. This number can be determined by running\n" " " APP_SHORT_NAME " without any options specified.\n" "[--show-all] Show everything (includes all the below options)\n" "[--show-tool-props] Show the active VkPhysicalDeviceToolPropertiesEXT that " APP_SHORT_NAME " finds.\n" "[--show-formats] Display the format properties of each physical device.\n" " Note: This only affects text output.\n" "[--show-promoted-structs] Include structs promoted to core in pNext Chains.\n" "[--show-video-props]\n" " Display the video profile info, video capabilities and\n" " video format properties of each video profile supported\n" " by each physical device.\n" " Note: This only affects text output which by default\n" " only contains the list of supported video profile names.\n"; void print_usage(const std::string &executable_name) { std::cout << "\n" APP_SHORT_NAME " - Summarize " API_NAME " information in relation to the current environment.\n\n"; std::cout << "USAGE: \n"; std::cout << " " << executable_name << " --summary\n"; std::cout << " " << executable_name << " -o | --output \n"; std::cout << " " << executable_name << " -j | -j= | --json | --json=\n"; std::cout << " " << executable_name << " --text\n"; std::cout << " " << executable_name << " --html\n"; std::cout << " " << executable_name << " --show-all\n"; std::cout << " " << executable_name << " --show-formats\n"; std::cout << " " << executable_name << " --show-tool-props\n"; std::cout << " " << executable_name << " --show-promoted-structs\n"; std::cout << " " << executable_name << " --show-video-props\n"; std::cout << "\n" << help_message_body << std::endl; } struct ParsedResults { OutputCategory output_category = OutputCategory::text; uint32_t selected_gpu = 0; bool has_selected_gpu = false; // differentiate between selecting the 0th gpu and using the default 0th value ShowSettings show; bool print_to_file = false; std::string filename; // set if explicitly given, or if vkconfig_output has a argument std::string default_filename; }; util::vulkaninfo_optional parse_arguments(int argc, char **argv, std::string executable_name) { ParsedResults results{}; results.default_filename = APP_SHORT_NAME ".txt"; for (int i = 1; i < argc; ++i) { // A internal-use-only format for communication with the Vulkan Configurator tool // Usage "--vkconfig_output " // -o can be used to specify the filename instead if (0 == strcmp("--vkconfig_output", argv[i])) { results.output_category = OutputCategory::vkconfig_output; results.print_to_file = true; results.default_filename = APP_SHORT_NAME ".json"; if (argc > (i + 1) && argv[i + 1][0] != '-') { #ifdef WIN32 results.filename = (std::string(argv[i + 1]) + "\\" APP_SHORT_NAME ".json"); #else results.filename = (std::string(argv[i + 1]) + "/" APP_SHORT_NAME ".json"); #endif ++i; } } else if (strncmp("--json", argv[i], 6) == 0 || strncmp(argv[i], "-j", 2) == 0) { if (strlen(argv[i]) > 7 && strncmp("--json=", argv[i], 7) == 0) { results.selected_gpu = static_cast(strtol(argv[i] + 7, nullptr, 10)); results.has_selected_gpu = true; } if (strlen(argv[i]) > 3 && strncmp("-j=", argv[i], 3) == 0) { results.selected_gpu = static_cast(strtol(argv[i] + 3, nullptr, 10)); results.has_selected_gpu = true; } results.output_category = OutputCategory::profile_json; results.default_filename = APP_SHORT_NAME ".json"; results.print_to_file = true; } else if (strcmp(argv[i], "--summary") == 0) { results.output_category = OutputCategory::summary; } else if (strcmp(argv[i], "--text") == 0) { results.output_category = OutputCategory::text; results.default_filename = APP_SHORT_NAME ".txt"; } else if (strcmp(argv[i], "--html") == 0) { results.output_category = OutputCategory::html; results.print_to_file = true; results.default_filename = APP_SHORT_NAME ".html"; } else if (strcmp(argv[i], "--show-all") == 0) { results.show.all = true; results.show.tool_props = true; results.show.formats = true; results.show.promoted_structs = true; results.show.video_props = true; } else if (strcmp(argv[i], "--show-tool-props") == 0) { results.show.tool_props = true; } else if (strcmp(argv[i], "--show-formats") == 0) { results.show.formats = true; } else if (strcmp(argv[i], "--show-promoted-structs") == 0) { results.show.promoted_structs = true; } else if (strcmp(argv[i], "--show-video-props") == 0) { results.show.video_props = true; } else if ((strcmp(argv[i], "--output") == 0 || strcmp(argv[i], "-o") == 0) && argc > (i + 1)) { if (argv[i + 1][0] == '-') { std::cout << "-o or --output must be followed by a filename\n"; return {}; } results.print_to_file = true; results.filename = argv[i + 1]; ++i; } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { print_usage(executable_name); return {}; } else { print_usage(executable_name); return {}; } } return results; } PrinterCreateDetails get_printer_create_details(ParsedResults &parse_data, AppInstance &inst, AppGpu &selected_gpu, std::string const &executable_name) { PrinterCreateDetails create{}; create.print_to_file = parse_data.print_to_file; create.file_name = (!parse_data.filename.empty()) ? parse_data.filename : parse_data.default_filename; switch (parse_data.output_category) { default: case (OutputCategory::text): create.output_type = OutputType::text; break; case (OutputCategory::html): create.output_type = OutputType::html; break; case (OutputCategory::profile_json): create.output_type = OutputType::json; create.start_string = std::string("{\n\t\"$schema\": ") + "\"https://schema.khronos.org/vulkan/profiles-0.8-latest.json\""; if (parse_data.filename.empty()) { create.file_name = std::string("VP_" APP_UPPER_CASE_NAME "_") + std::string(selected_gpu.props.deviceName) + "_" + selected_gpu.GetDriverVersionString(); for (auto &c : create.file_name) { if (c == ' ' || c == '.') c = '_'; } create.file_name += ".json"; } break; case (OutputCategory::vkconfig_output): create.output_type = OutputType::vkconfig_output; create.start_string = "{\n\t\"" API_NAME " Instance Version\": \"" + inst.api_version.str() + "\""; break; } return create; } void RunPrinter(Printer &p, ParsedResults parse_data, AppInstance &instance, std::vector> &gpus, std::vector> &surfaces) { #ifdef VK_USE_PLATFORM_IOS_MVK p.SetAlwaysOpenDetails(true); #endif if (parse_data.output_category == OutputCategory::summary) { DumpSummaryInstance(p, instance); p.SetHeader(); ObjectWrapper obj(p, "Devices"); IndentWrapper indent(p); for (auto &gpu : gpus) { DumpSummaryGPU(p, *(gpu.get())); } } else if (parse_data.output_category == OutputCategory::profile_json) { DumpGpuProfileCapabilities(p, *(gpus.at(parse_data.selected_gpu).get()), parse_data.show.promoted_structs); DumpGpuProfileInfo(p, *(gpus.at(parse_data.selected_gpu).get())); } else { // text, html, vkconfig_output p.SetHeader(); DumpExtensions(p, "Instance Extensions", instance.global_extensions); p.AddNewline(); DumpLayers(p, instance.global_layers, gpus); #if defined(VULKANINFO_WSI_ENABLED) // Doesn't print anything if no surfaces are available DumpPresentableSurfaces(p, instance, gpus, surfaces); #endif // defined(VULKANINFO_WSI_ENABLED) DumpGroups(p, instance); p.SetHeader(); ObjectWrapper obj(p, "Device Properties and Extensions"); IndentWrapper indent(p); for (auto &gpu : gpus) { DumpGpu(p, *(gpu.get()), parse_data.show); } } } #ifdef VK_USE_PLATFORM_IOS_MVK // On iOS, we'll call this ourselves from a parent routine in the GUI int vulkanInfoMain(int argc, char **argv) { #else int main(int argc, char **argv) { #endif // Figure out the name of the executable, pull out the name if given a path // Default is `vulkaninfo` std::string executable_name = APP_SHORT_NAME; if (argc >= 1) { const auto argv_0 = std::string(argv[0]); // don't include path separator // Look for forward slash first, only look for backslash if that found nothing auto last_occurrence = argv_0.rfind('/'); if (last_occurrence == std::string::npos) { last_occurrence = argv_0.rfind('\\'); } if (last_occurrence != std::string::npos && last_occurrence + 1 < argv_0.size()) { executable_name = argv_0.substr(last_occurrence + 1); } } auto parsing_return = parse_arguments(argc, argv, executable_name); if (!parsing_return) return 1; ParsedResults parse_data = parsing_return.value(); #if defined(_MSC_VER) _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); if (ConsoleIsExclusive()) ConsoleEnlarge(); User32Handles local_user32_handles; user32_handles = &local_user32_handles; if (!local_user32_handles.load()) { fprintf(stderr, "Failed to load user32.dll library!\n"); if (parse_data.output_category == OutputCategory::text && !parse_data.print_to_file) wait_for_console_destroy(); return 1; } #endif int return_code = 0; // set in case of error std::unique_ptr printer; std::ostream std_out(std::cout.rdbuf()); std::ofstream file_out; std::ostream *out = &std_out; // if any essential vulkan call fails, it throws an exception try { AppInstance instance = {}; SetupWindowExtensions(instance); auto phys_devices = instance.FindPhysicalDevices(); #if defined(VULKANINFO_WSI_ENABLED) for (auto &surface_extension : instance.surface_extensions) { surface_extension.create_window(instance); surface_extension.surface = surface_extension.create_surface(instance); } #endif // defined(VULKANINFO_WSI_ENABLED) std::vector> gpus; uint32_t gpu_counter = 0; for (auto &phys_device : phys_devices) { gpus.push_back( std::unique_ptr(new AppGpu(instance, gpu_counter++, phys_device, parse_data.show.promoted_structs))); } std::vector> surfaces; #if defined(VULKANINFO_WSI_ENABLED) for (auto &surface_extension : instance.surface_extensions) { for (auto &gpu : gpus) { try { // check if the surface is supported by the physical device before adding it to the list VkBool32 supported = VK_FALSE; VkResult err = vkGetPhysicalDeviceSurfaceSupportKHR(gpu->phys_device, 0, surface_extension.surface, &supported); if (err != VK_SUCCESS || supported == VK_FALSE) continue; surfaces.push_back( std::unique_ptr(new AppSurface(instance, *gpu.get(), gpu->phys_device, surface_extension))); } catch (std::exception &e) { std::cerr << "ERROR while creating surface for extension " << surface_extension.name << " : " << e.what() << "\n"; } } } #endif // defined(VULKANINFO_WSI_ENABLED) if (parse_data.selected_gpu >= gpus.size()) { if (parse_data.has_selected_gpu) { std::cout << "The selected gpu (" << parse_data.selected_gpu << ") is not a valid GPU index. "; if (gpus.size() == 0) { std::cout << APP_SHORT_NAME " could not find any GPU's.\n"; return 1; } else { if (gpus.size() == 1) { std::cout << "The only available GPU selection is 0.\n"; } else { std::cout << "The available GPUs are in the range of 0 to " << gpus.size() - 1 << ".\n"; } return 1; } } else if (parse_data.output_category == OutputCategory::profile_json) { std::cout << APP_SHORT_NAME " could not find any GPU's.\n"; } } auto printer_data = get_printer_create_details(parse_data, instance, *gpus.at(parse_data.selected_gpu), executable_name); if (printer_data.print_to_file) { file_out = std::ofstream(printer_data.file_name); out = &file_out; } printer = std::unique_ptr(new Printer(printer_data, *out, instance.api_version)); RunPrinter(*(printer.get()), parse_data, instance, gpus, surfaces); // Call the printer's destructor before the file handle gets closed printer.reset(nullptr); #if defined(VULKANINFO_WSI_ENABLED) for (auto &surface_extension : instance.surface_extensions) { AppDestroySurface(instance, surface_extension.surface); surface_extension.destroy_window(instance); } #endif // defined(VULKANINFO_WSI_ENABLED) } catch (std::exception &e) { // Print the error to stderr and leave all outputs in a valid state (mainly for json) std::cerr << "ERROR at " << e.what() << "\n"; if (printer) { printer->FinishOutput(); } return_code = 1; // Call the printer's destructor before the file handle gets closed printer.reset(nullptr); } #ifdef _WIN32 if (parse_data.output_category == OutputCategory::text && !parse_data.print_to_file) wait_for_console_destroy(); #endif return return_code; }