From 292e6ed02141b015950d03390c6e8576b0673ef6 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Thu, 27 Oct 2022 14:14:27 -0600 Subject: [PATCH] vulkaninfo: Add Driver Version handling Previously, driverVersion fields were treated as hex values, when in reality each vendor has their own specific format. This commit adds parsers for Nvidia, Intel (windows only), and relies on the Vulkan API format for everyone else. AMD uses the Vulkan API format. This change applies only to the summary, text, and html output. Add ostream<< operators for VulkanVersion and VkConformanceVersion, rather than having bespoke to_string functions. When printing versions, use the major.minor.patch as the primary output and put the uint32_t form of the version in parenthesis. This moves the more useful version info into the spotlight, putting the less useful in parenthesis. Rework the way secondary values (stuff in parenthesis after a value) are printed. --- vulkaninfo/outputprinter.h | 84 ++++++++++++++++++++++------------------------ vulkaninfo/vulkaninfo.cpp | 71 ++++++++++++++++++++------------------- vulkaninfo/vulkaninfo.h | 65 +++++++++++++++++++++++++++++------ 3 files changed, 131 insertions(+), 89 deletions(-) diff --git a/vulkaninfo/outputprinter.h b/vulkaninfo/outputprinter.h index 2efd179..f77df3b 100644 --- a/vulkaninfo/outputprinter.h +++ b/vulkaninfo/outputprinter.h @@ -51,20 +51,9 @@ std::string to_string(const std::array &uid) { return ss.str(); } -std::string VkVersionString(uint32_t version) { - uint32_t major = version >> 22; - uint32_t minor = (version >> 12) & 0x3ff; - uint32_t patch = version & 0xfff; - return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); -} - -std::string VkVersionString(VulkanVersion v) { - return std::to_string(v.major) + "." + std::to_string(v.minor) + "." + std::to_string(v.patch); -} - -std::string VkConformanceVersionString(const VkConformanceVersion &c) { - return std::to_string(c.major) + "." + std::to_string(c.minor) + "." + std::to_string(c.subminor) + "." + - std::to_string(c.patch); +std::ostream &operator<<(std::ostream &out, const VkConformanceVersion &v) { + return out << static_cast(v.major) << "." << static_cast(v.minor) << "." + << static_cast(v.subminor) << "." << static_cast(v.patch); } enum class OutputType { text, html, json, vkconfig_output }; @@ -88,7 +77,7 @@ class Printer { out << "==========\n"; out << "VULKANINFO\n"; out << "==========\n\n"; - out << "Vulkan Instance Version: " << VkVersionString(vulkan_version) << "\n\n\n"; + out << "Vulkan Instance Version: " << VulkanVersion(vulkan_version) << "\n\n\n"; break; case (OutputType::html): @@ -180,7 +169,7 @@ class Printer { out << "\t\t\t

vulkaninfo

\n"; out << "\t\t\n"; out << "\t\t
\n"; - out << "\t\t\t
Vulkan Instance Version: " << VkVersionString(vulkan_version) + out << "\t\t\t
Vulkan Instance Version: " << VulkanVersion(vulkan_version) << "
\n\t\t\t
\n"; node.indents = 3; break; @@ -293,11 +282,19 @@ class Printer { get_top().element_index = index; return *this; } + Printer &SetValueDescription(std::string str) { + value_description = str; + return *this; + } void ObjectStart(std::string object_name, int32_t count_subobjects = -1) { switch (output_type) { case (OutputType::text): { out << std::string(static_cast(get_top().indents), '\t') << object_name; + if (!value_description.empty()) { + out << " (" << value_description << ")"; + value_description = {}; + } if (get_top().element_index != -1) { out << "[" << get_top().element_index << "]"; } @@ -332,6 +329,10 @@ class Printer { } else { out << object_name; } + if (!value_description.empty()) { + out << " (" << value_description << ")"; + value_description = {}; + } if (get_top().element_index != -1) { out << "[" << get_top().element_index << "]"; get_top().element_index = -1; @@ -355,6 +356,9 @@ class Printer { } else { out << "\"" << object_name << "\": {\n"; } + if (!value_description.empty()) { + value_description = {}; + } break; case (OutputType::vkconfig_output): if (!get_top().is_first_item) { @@ -370,6 +374,9 @@ class Printer { } else { out << "\"" << object_name << "\": {\n"; } + if (!value_description.empty()) { + value_description = {}; + } break; default: break; @@ -461,7 +468,7 @@ class Printer { // For printing key-value pairs. // value_description is for reference information and is displayed inside parenthesis after the value template - void PrintKeyValue(std::string key, T value, std::string value_description = "") { + void PrintKeyValue(std::string key, T value) { switch (output_type) { case (OutputType::text): out << std::string(static_cast(get_top().indents), '\t') << key; @@ -471,6 +478,7 @@ class Printer { out << " = " << value; if (value_description != "") { out << " (" << value_description << ")"; + value_description = {}; } out << "\n"; break; @@ -485,19 +493,13 @@ class Printer { } else { out << " = " << value << ""; } - if (value_description != "") { + if (!value_description.empty()) { out << " (" << value_description << ")"; + value_description = {}; } out << "
\n"; break; case (OutputType::json): - if (!get_top().is_first_item) { - out << ",\n"; - } else { - get_top().is_first_item = false; - } - out << std::string(static_cast(get_top().indents), '\t') << "\"" << key << "\": " << value; - break; case (OutputType::vkconfig_output): if (!get_top().is_first_item) { out << ",\n"; @@ -505,8 +507,9 @@ class Printer { get_top().is_first_item = false; } out << std::string(static_cast(get_top().indents), '\t') << "\"" << key << "\": "; - if (value_description != "") { + if (!value_description.empty()) { out << "\"" << value << " (" << value_description << ")\""; + value_description = {}; } else { out << value; } @@ -537,15 +540,15 @@ class Printer { } // For printing key - string pairs (necessary because of json) - void PrintKeyString(std::string key, std::string value, std::string value_description = "") { + void PrintKeyString(std::string key, std::string value) { switch (output_type) { case (OutputType::text): case (OutputType::html): - PrintKeyValue(key, value, value_description); + PrintKeyValue(key, value); break; case (OutputType::json): case (OutputType::vkconfig_output): - PrintKeyValue(key, std::string("\"") + value + "\"", value_description); + PrintKeyValue(key, std::string("\"") + EscapeJSONCString(value) + "\""); break; default: break; @@ -553,20 +556,14 @@ class Printer { } // For printing key - string pairs (necessary because of json) - void PrintKeyBool(std::string key, bool value, std::string value_description = "") { - PrintKeyValue(key, value ? "true" : "false", value_description); - } + void PrintKeyBool(std::string key, bool value) { PrintKeyValue(key, value ? "true" : "false"); } // print inside array template - void PrintElement(T element, std::string value_description = "") { + void PrintElement(T element) { switch (output_type) { case (OutputType::text): - out << std::string(static_cast(get_top().indents), '\t') << element; - if (value_description != "") { - out << " (" << value_description << ")"; - } - out << "\n"; + out << std::string(static_cast(get_top().indents), '\t') << element << "\n"; break; case (OutputType::html): out << std::string(static_cast(get_top().indents), '\t') << "
"; @@ -576,9 +573,6 @@ class Printer { } else { out << "" << element << ""; } - if (value_description != "") { - out << " (" << value_description << ")"; - } out << "
\n"; break; case (OutputType::json): @@ -594,11 +588,11 @@ class Printer { break; } } - void PrintString(std::string string, std::string value_description = "") { + void PrintString(std::string string) { switch (output_type) { case (OutputType::text): case (OutputType::html): - PrintElement(string, value_description); + PrintElement(string); break; case (OutputType::json): case (OutputType::vkconfig_output): @@ -707,6 +701,10 @@ class Printer { // Helper to get the current top of the object_stack StackNode &get_top() { return object_stack.top(); } + // Optional 'description' for values + // Must be set right before the Print() function is called + std::string value_description; + // can only be called after a node was manually pushed onto the stack in the constructor void push_node(bool array) { StackNode node{}; diff --git a/vulkaninfo/vulkaninfo.cpp b/vulkaninfo/vulkaninfo.cpp index cde4653..0464daf 100644 --- a/vulkaninfo/vulkaninfo.cpp +++ b/vulkaninfo/vulkaninfo.cpp @@ -69,7 +69,7 @@ void DumpLayers(Printer &p, std::vector layers, const std::v IndentWrapper indent(p); for (auto &layer : layers) { - auto v_str = VkVersionString(layer.layer_properties.specVersion); + std::string v_str = VulkanVersion(layer.layer_properties.specVersion); auto props = layer.layer_properties; std::string header = p.DecorateAsType(props.layerName) + " (" + props.description + ") Vulkan version " + @@ -80,7 +80,7 @@ void DumpLayers(Printer &p, std::vector layers, const std::v ArrayWrapper arr_devices(p, "Devices", gpus.size()); for (auto &gpu : gpus) { - p.PrintKeyValue("GPU id", gpu->id, gpu->props.deviceName); + 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(); @@ -99,14 +99,14 @@ void DumpLayers(Printer &p, std::vector layers, const std::v ObjectWrapper obj_name(p, layer.layer_properties.layerName); p.SetMinKeyWidth(21); p.PrintKeyString("layerName", layer.layer_properties.layerName); - p.PrintKeyString("version", VkVersionString(layer.layer_properties.specVersion)); + p.PrintKeyString("version", VulkanVersion(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.PrintKeyValue("GPU id", gpu->id, 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); } @@ -295,8 +295,14 @@ void GpuDumpProps(Printer &p, AppGpu &gpu) { { ObjectWrapper obj(p, "VkPhysicalDeviceProperties"); p.SetMinKeyWidth(17); - p.PrintKeyValue("apiVersion", props.apiVersion, VkVersionString(props.apiVersion)); - p.PrintKeyValue("driverVersion", props.driverVersion, to_hex_str(props.driverVersion)); + 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", VulkanVersion(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)); @@ -607,11 +613,12 @@ void DumpGpuProfileCapabilities(Printer &p, AppGpu &gpu) { { ObjectWrapper props_obj(p, "VkPhysicalDeviceProperties"); auto props = gpu.GetDeviceProperties(); - p.PrintKeyValue("apiVersion", props.apiVersion, VkVersionString(props.apiVersion)); + 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"); @@ -718,7 +725,7 @@ void PrintProfileBaseInfo(Printer &p, const std::string &device_name, uint32_t a const std::vector &capabilities) { ObjectWrapper vk_info(p, device_name); p.PrintKeyValue("version", 1); - p.PrintKeyString("api-version", VkVersionString(apiVersion)); + p.PrintKeyString("api-version", VulkanVersion(apiVersion).str()); p.PrintKeyString("label", device_label); p.PrintKeyString("description", "Exported from vulkaninfo"); { ObjectWrapper contributors(p, "contributors"); } @@ -742,9 +749,9 @@ void PrintProfileBaseInfo(Printer &p, const std::string &device_name, uint32_t a void DumpGpuProfileInfo(Printer &p, AppGpu &gpu) { ObjectWrapper profiles(p, "profiles"); - std::string device_label = std::string(gpu.props.deviceName) + " driver " + VkVersionString(gpu.props.driverVersion); + std::string device_label = std::string(gpu.props.deviceName) + " driver " + gpu.GetDriverVersionString(); std::string device_name = - std::string("VP_VULKANINFO_") + std::string(gpu.props.deviceName) + "_" + VkVersionString(gpu.props.driverVersion); + std::string("VP_VULKANINFO_") + std::string(gpu.props.deviceName) + "_" + gpu.GetDriverVersionString(); ; for (auto &c : device_name) { if (c == ' ' || c == '.') c = '_'; @@ -781,10 +788,10 @@ void DumpSummaryInstance(Printer &p, AppInstance &inst) { 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, VkVersionString(layer.layer_properties.specVersion).size()); + layer_version_max = std::max(layer_version_max, VulkanVersion(layer.layer_properties.specVersion).str().size()); } for (auto &layer : inst.global_layers) { - auto v_str = VkVersionString(layer.layer_properties.specVersion); + auto v_str = VulkanVersion(layer.layer_properties.specVersion).str(); auto props = layer.layer_properties; auto name_padding = std::string(layer_name_max - strlen(props.layerName), ' '); @@ -800,32 +807,26 @@ 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", props.apiVersion, VkVersionString(props.apiVersion)); - p.PrintKeyValue("driverVersion", props.driverVersion, to_hex_str(props.driverVersion)); + p.PrintKeyValue("apiVersion", VulkanVersion(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.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) && - (gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME) || gpu.api_version.minor >= 2)) { - void *place = gpu.props2.pNext; - while (place) { - VkBaseOutStructure *structure = static_cast(place); - if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES) { - VkPhysicalDeviceDriverProperties *driver_props = reinterpret_cast(structure); - DumpVkDriverId(p, "driverID", driver_props->driverID); - p.PrintKeyString("driverName", driver_props->driverName); - p.PrintKeyString("driverInfo", driver_props->driverInfo); - p.PrintKeyString("conformanceVersion", VkConformanceVersionString(driver_props->conformanceVersion)); - } - if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES) { - VkPhysicalDeviceIDProperties *device_id_props = reinterpret_cast(structure); - p.PrintKeyValue("deviceUUID", device_id_props->deviceUUID); - p.PrintKeyValue("driverUUID", device_id_props->driverUUID); - } - place = structure->pNext; - } + if (gpu.found_driver_props) { + DumpVkDriverId(p, "driverID", gpu.driver_props.driverID); + p.PrintKeyString("driverName", gpu.driver_props.driverName); + p.PrintKeyString("driverInfo", gpu.driver_props.driverInfo); + p.PrintKeyValue("conformanceVersion", gpu.driver_props.conformanceVersion); + } + if (gpu.found_device_id_props) { + p.PrintKeyValue("deviceUUID", gpu.device_id_props.deviceUUID); + p.PrintKeyValue("driverUUID", gpu.device_id_props.driverUUID); } } @@ -986,7 +987,7 @@ PrinterCreateDetails get_printer_create_details(ParsedResults &parse_data, AppIn 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_VULKANINFO_") + std::string(selected_gpu.props.deviceName) + "_" + - VkVersionString(selected_gpu.props.driverVersion); + selected_gpu.GetDriverVersionString(); for (auto &c : create.file_name) { if (c == ' ' || c == '.') c = '_'; } @@ -995,7 +996,7 @@ PrinterCreateDetails get_printer_create_details(ParsedResults &parse_data, AppIn break; case (OutputCategory::vkconfig_output): create.output_type = OutputType::vkconfig_output; - create.start_string = "{\n\t\"Vulkan Instance Version\": \"" + VkVersionString(inst.vk_version) + "\""; + create.start_string = "{\n\t\"Vulkan Instance Version\": \"" + VulkanVersion(inst.vk_version).str() + "\""; break; } return create; diff --git a/vulkaninfo/vulkaninfo.h b/vulkaninfo/vulkaninfo.h index 04775b7..2492041 100644 --- a/vulkaninfo/vulkaninfo.h +++ b/vulkaninfo/vulkaninfo.h @@ -558,21 +558,23 @@ struct SurfaceExtension { }; struct VulkanVersion { - uint32_t major; - uint32_t minor; - uint32_t patch; + uint32_t major = 1; + uint32_t minor = 0; + uint32_t patch = 0; + VulkanVersion() = default; + VulkanVersion(uint32_t version) noexcept + : major{VK_VERSION_MAJOR(version)}, minor{VK_VERSION_MINOR(version)}, patch{VK_VERSION_PATCH(version)} {} + std::string str() { return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); } + operator std::string() { return str(); } }; - -VulkanVersion make_vulkan_version(uint32_t version) { - return {VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), VK_VERSION_PATCH(version)}; -} +std::ostream &operator<<(std::ostream &out, const VulkanVersion &v) { return out << v.major << "." << v.minor << "." << v.patch; } struct AppInstance { VkDll dll; VkInstance instance; uint32_t instance_version; - VulkanVersion vk_version{}; + VulkanVersion vk_version; VkDebugReportCallbackEXT debug_callback = VK_NULL_HANDLE; @@ -634,7 +636,7 @@ struct AppInstance { if (err) THROW_VK_ERR("vkEnumerateInstanceVersion", err); } - vk_version = make_vulkan_version(instance_version); + vk_version = VulkanVersion(instance_version); // fallback to baked header version if loader returns 0 for the patch version if (VK_VERSION_PATCH(instance_version) == 0) vk_version.patch = VK_VERSION_PATCH(VK_HEADER_VERSION); @@ -1482,11 +1484,16 @@ struct AppGpu { AppInstance &inst; uint32_t id{}; VkPhysicalDevice phys_device = VK_NULL_HANDLE; - VulkanVersion api_version{}; + VulkanVersion api_version; VkPhysicalDeviceProperties props{}; VkPhysicalDeviceProperties2KHR props2{}; + VkPhysicalDeviceDriverProperties driver_props{}; + VkPhysicalDeviceIDProperties device_id_props{}; + bool found_driver_props = false; + bool found_device_id_props = false; + std::vector queue_props; std::vector queue_props2; std::vector extended_queue_props; @@ -1518,7 +1525,7 @@ struct AppGpu { inst.dll.fp_vkGetPhysicalDeviceProperties(phys_device, &props); // needs to find the minimum of the instance and device version, and use that to print the device info - api_version = make_vulkan_version(props.apiVersion); + api_version = VulkanVersion(props.apiVersion); inst.dll.fp_vkGetPhysicalDeviceMemoryProperties(phys_device, &memory_props); @@ -1558,6 +1565,22 @@ struct AppGpu { setup_queue_properties2_chain(queue_props2[i], chain_for_queue_props2[i]); } inst.ext_funcs.vkGetPhysicalDeviceQueueFamilyProperties2KHR(phys_device, &queue_prop2_count, queue_props2.data()); + + if ((CheckPhysicalDeviceExtensionIncluded(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME) || api_version.minor >= 2)) { + void *place = props2.pNext; + while (place) { + VkBaseOutStructure *structure = static_cast(place); + if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES) { + driver_props = *reinterpret_cast(structure); + found_driver_props = true; + + } else if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES) { + device_id_props = *reinterpret_cast(structure); + found_device_id_props = true; + } + place = structure->pNext; + } + } } // Use the queue_props2 if they exist, else fallback on vulkan 1.0 queue_props @@ -1756,6 +1779,26 @@ struct AppGpu { return props; } } + + // Vendor specific driverVersion mapping scheme + // If one isn't present, fall back to the standard Vulkan scheme + std::string GetDriverVersionString() { + uint32_t v = props.driverVersion; + if ((found_driver_props && driver_props.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY) || + (!found_driver_props && props.deviceID == 4318)) { + return std::to_string((v >> 22) & 0x3ff) + "." + std::to_string((v >> 14) & 0x0ff) + "." + + std::to_string((v >> 6) & 0x0ff) + "." + std::to_string((v)&0x003ff); + } else if ((found_driver_props && driver_props.driverID == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) +#if defined(WIN32) + || (!found_driver_props && props.deviceID == 0x8086) // only do the fallback check if running in windows +#endif + ) { + return std::to_string((v >> 14)) + "." + std::to_string((v)&0x3fff); + } else { + // AMD uses the standard vulkan scheme + return VulkanVersion(v).str(); + } + } }; std::vector GetToolingInfo(AppGpu &gpu) { -- 2.7.4