| /* |
| * Copyright (c) 2016 Valve Corporation |
| * Copyright (c) 2016 LunarG, Inc. |
| * |
| * 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: Mark Young <marky@lunarg.com> |
| */ |
| |
| #include <cstring> |
| #include <exception> |
| #include <fstream> |
| #include <iostream> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| #include <time.h> |
| #include <inttypes.h> |
| |
| const char APP_VERSION[] = "Version 1.0"; |
| #define MAX_STRING_LENGTH 1024 |
| |
| #ifdef _WIN32 |
| #pragma warning(disable : 4996) |
| #include "shlwapi.h" |
| #else |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/statvfs.h> |
| #include <sys/utsname.h> |
| #include <dirent.h> |
| #include <unistd.h> |
| #endif |
| |
| #include <json/json.h> |
| |
| #include <vulkan/vulkan.h> |
| |
| #if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/) || defined MINGW_HAS_SECURE_API |
| #include <basetsd.h> |
| #define snprintf sprintf_s |
| #endif |
| |
| enum ElementAlign { ALIGN_LEFT = 0, ALIGN_CENTER, ALIGN_RIGHT }; |
| |
| struct PhysicalDeviceInfo { |
| VkPhysicalDevice vulkan_phys_dev; |
| std::vector<VkQueueFamilyProperties> queue_fam_props; |
| }; |
| |
| struct GlobalItems { |
| std::ofstream html_file_stream; |
| bool sdk_found; |
| std::string sdk_path; |
| VkInstance instance; |
| std::vector<PhysicalDeviceInfo> phys_devices; |
| std::vector<VkDevice> log_devices; |
| uint32_t cur_table; |
| std::string exe_directory; |
| bool is_odd_row; |
| |
| #ifdef _WIN32 |
| bool is_wow64; |
| #endif |
| }; |
| |
| // Create a global variable used to store the global settings |
| GlobalItems global_items = {}; |
| |
| // Error messages thrown by the application |
| enum ErrorResults { |
| VULKAN_CANT_FIND_DRIVER = -2, |
| MISSING_DRIVER_REGISTRY = -3, |
| MISSING_DRIVER_JSON = -4, |
| MISSING_DRIVER_LIB = -5, |
| }; |
| |
| // Structure used to store name/value pairs read from the |
| // Vulkan layer settings file (if one exists). |
| struct SettingPair { |
| std::string name; |
| std::string value; |
| }; |
| |
| void StartOutput(std::string title); |
| void EndOutput(); |
| void PrintSystemInfo(void); |
| void PrintVulkanInfo(void); |
| void PrintDriverInfo(void); |
| void PrintRunTimeInfo(void); |
| void PrintSDKInfo(void); |
| void PrintExplicitLayerJsonInfo(const char *layer_json_filename, |
| Json::Value root, uint32_t num_cols); |
| void PrintImplicitLayerJsonInfo(const char *layer_json_filename, |
| Json::Value root); |
| void PrintLayerInfo(void); |
| void PrintLayerSettingsFileInfo(void); |
| void PrintTestResults(void); |
| std::string TrimWhitespace(const std::string &str, |
| const std::string &whitespace = " \t\n\r"); |
| |
| int main(int argc, char **argv) { |
| int err_val = 0; |
| try { |
| time_t time_raw_format; |
| struct tm *ptr_time; |
| char html_file_name[MAX_STRING_LENGTH]; |
| char full_file[MAX_STRING_LENGTH]; |
| char temp[MAX_STRING_LENGTH]; |
| const char* output_path = NULL; |
| bool generate_unique_file = false; |
| |
| // Check and handle command-line arguments |
| if (argc > 1) { |
| for (int iii = 1; iii < argc; iii++) { |
| if (0 == strcmp("--unique_output", argv[iii])) { |
| generate_unique_file = true; |
| } else if (0 == strcmp("--output_path", argv[iii]) && |
| argc > (iii + 1)) { |
| output_path = argv[iii + 1]; |
| ++iii; |
| } else { |
| std::cout << "Usage of via.exe:" << std::endl |
| << " via.exe [--unique_output] " |
| "[--output_path <path>]" << std::endl |
| << " [--unique_output] Optional " |
| "parameter to generate a unique html" |
| << std::endl << " " |
| "output file in the form " |
| "\'via_YYYY_MM_DD_HH_MM.html\'" |
| << std::endl << " [--output_path <path>" |
| "] Optional parameter to generate the output at" |
| << std::endl << " " |
| " a given path" << std::endl; |
| throw(-1); |
| } |
| } |
| } |
| |
| // If the user wants a specific output path, write it to the buffer |
| // and then continue writing the rest of the name below |
| size_t file_name_offset = 0; |
| if (output_path != NULL) { |
| file_name_offset = strlen(output_path) + 1; |
| strncpy(html_file_name, output_path, MAX_STRING_LENGTH - 1); |
| #ifdef _WIN32 |
| strncpy(html_file_name + file_name_offset - 1, "\\", |
| MAX_STRING_LENGTH - file_name_offset); |
| #else |
| strncpy(html_file_name + file_name_offset - 1, "/", |
| MAX_STRING_LENGTH - file_name_offset); |
| #endif |
| } |
| |
| // If the user wants a unique file, generate a file with the current |
| // time and date incorporated into it. |
| if (generate_unique_file) { |
| time(&time_raw_format); |
| ptr_time = localtime(&time_raw_format); |
| if (strftime(html_file_name + file_name_offset, |
| MAX_STRING_LENGTH - 1, |
| "via_%Y_%m_%d_%H_%M.html", ptr_time) == 0) { |
| std::cerr << "Couldn't prepare formatted string" << std::endl; |
| throw(-1); |
| } |
| } else { |
| strncpy(html_file_name + file_name_offset, "via.html", |
| MAX_STRING_LENGTH - 1 - file_name_offset); |
| } |
| |
| // Write the output file to the current executing directory, or, if |
| // that fails, write it out to the user's home folder. |
| global_items.html_file_stream.open(html_file_name); |
| if (global_items.html_file_stream.fail()) { |
| #ifdef _WIN32 |
| char home_drive[32]; |
| if (0 != GetEnvironmentVariableA("HOMEDRIVE", home_drive, 31) || |
| 0 != GetEnvironmentVariableA("HOMEPATH", temp, |
| MAX_STRING_LENGTH - 1)) { |
| std::cerr << "Error failed to get either HOMEDRIVE or HOMEPATH " |
| "from environment settings!" |
| << std::endl; |
| throw(-1); |
| } |
| snprintf(full_file, MAX_STRING_LENGTH - 1, "%s%s\\%s", home_drive, |
| temp, html_file_name); |
| #else |
| snprintf(full_file, MAX_STRING_LENGTH - 1, "~/%s", html_file_name); |
| #endif |
| global_items.html_file_stream.open(full_file); |
| if (global_items.html_file_stream.fail()) { |
| std::cerr << "Error failed opening html file stream to " |
| "either current" |
| " folder as " |
| << html_file_name << " or home folder as " |
| << full_file << std::endl; |
| throw(-1); |
| } |
| } |
| |
| global_items.cur_table = 0; |
| |
| // Determine where we are executing at. |
| #ifdef _WIN32 |
| int bytes = GetModuleFileName(NULL, temp, MAX_STRING_LENGTH - 1); |
| if (0 < bytes) { |
| std::string exe_location = temp; |
| global_items.exe_directory = |
| exe_location.substr(0, exe_location.rfind("\\")); |
| |
| size_t index = 0; |
| while (true) { |
| index = global_items.exe_directory.find("\\", index); |
| if (index == std::string::npos) { |
| break; |
| } |
| global_items.exe_directory.replace(index, 1, "/"); |
| index++; |
| } |
| } else { |
| global_items.exe_directory = ""; |
| } |
| |
| #elif __GNUC__ |
| ssize_t len = ::readlink("/proc/self/exe", temp, MAX_STRING_LENGTH - 1); |
| if (0 < len) { |
| std::string exe_location = temp; |
| global_items.exe_directory = |
| exe_location.substr(0, exe_location.rfind("/")); |
| } else { |
| global_items.exe_directory = ""; |
| } |
| #endif |
| |
| StartOutput("LunarG VIA"); |
| |
| PrintSystemInfo(); |
| PrintVulkanInfo(); |
| PrintTestResults(); |
| EndOutput(); |
| } catch (int e) { |
| // Print out a useful message for any common errors. |
| switch (e) { |
| case MISSING_DRIVER_REGISTRY: |
| std::cout << "ERROR: Failed to find Vulkan Driver JSON in registry" |
| << std::endl; |
| break; |
| case MISSING_DRIVER_JSON: |
| std::cout << "ERROR: Failed to find Vulkan Driver JSON" |
| << std::endl; |
| break; |
| case MISSING_DRIVER_LIB: |
| std::cout << "ERROR: Failed to find Vulkan Driver Lib" << std::endl; |
| break; |
| case VULKAN_CANT_FIND_DRIVER: |
| std::cout << "ERROR: Vulkan failed to find a compatible driver" |
| << std::endl; |
| break; |
| default: |
| std::cout << "ERROR: Uknown failure occurred. Refer to HTML for " |
| "more info" |
| << std::endl; |
| break; |
| } |
| err_val = e; |
| } |
| global_items.html_file_stream.close(); |
| |
| if (err_val == 0) { |
| std::cout << "SUCCESS: Validation completed properly" << std::endl; |
| } |
| return err_val; |
| } |
| |
| // Output helper functions: |
| //============================= |
| |
| // Start writing to the HTML file by creating the appropriate |
| // header information including the appropriate CSS and JavaScript |
| // items. |
| void StartOutput(std::string output) { |
| global_items.html_file_stream << "<!DOCTYPE html>" << std::endl; |
| global_items.html_file_stream << "<HTML lang=\"en\" xml:lang=\"en\" " |
| "xmlns=\"http://www.w3.org/1999/xhtml\">" |
| << std::endl; |
| global_items.html_file_stream << std::endl |
| << "<HEAD>" << std::endl |
| << " <TITLE>" << output << "</TITLE>" |
| << std::endl; |
| |
| global_items.html_file_stream |
| << " <META charset=\"UTF-8\">" << std::endl |
| << " <style media=\"screen\" type=\"text/css\">" << std::endl |
| << " html {" << std::endl |
| // By defining the color first, this won't override the background image |
| // (unless the images aren't there). |
| << " background-color: #0b1e48;" << std::endl |
| // The following changes try to load the text image twice (locally, then |
| // off the web) followed by the background image twice (locally, then |
| // off the web). The background color will only show if both background |
| // image loads fail. In this way, a user will see their local copy on |
| // their machine, while a person they share it with will see the web |
| // images (or the background color). |
| << " background-image: url(\"file:///" |
| << global_items.exe_directory << "/images/lunarg_via.png\"), " |
| << "url(\"https://vulkan.lunarg.com/img/lunarg_via.png\"), " |
| "url(\"file:///" |
| << global_items.exe_directory << "/images/bg-starfield.jpg\"), " |
| << "url(\"https://vulkan.lunarg.com/img/bg-starfield.jpg\");" |
| << std::endl |
| << " background-position: center top, center top, center, " |
| "center;" |
| << std::endl |
| << " -webkit-background-size: auto, auto, cover, cover;" |
| << std::endl |
| << " -moz-background-size: auto, auto, cover, cover;" |
| << std::endl |
| << " -o-background-size: auto, auto, cover, cover;" |
| << std::endl |
| << " background-size: auto, auto, cover, cover;" << std::endl |
| << " background-attachment: scroll, scroll, fixed, fixed;" |
| << std::endl |
| << " background-repeat: no-repeat, no-repeat, no-repeat, " |
| "no-repeat;" |
| << std::endl |
| << " }" << std::endl |
| // h1.section is used for section headers, and h1.version is used to |
| // print out the application version text (which shows up just under |
| // the title). |
| << " h1.section {" << std::endl |
| << " font-family: sans-serif;" << std::endl |
| << " font-size: 35px;" << std::endl |
| << " color: #FFFFFF;" << std::endl |
| << " }" << std::endl |
| << " h1.version {" << std::endl |
| << " font-family: sans-serif;" << std::endl |
| << " font-size: 25px;" << std::endl |
| << " color: #FFFFFF;" << std::endl |
| << " }" << std::endl |
| << " table {" << std::endl |
| << " min-width: 600px;" << std::endl |
| << " width: 70%;" << std::endl |
| << " border-collapse: collapse;" << std::endl |
| << " border-color: grey;" << std::endl |
| << " font-family: sans-serif;" << std::endl |
| << " }" << std::endl |
| << " td.header {" << std::endl |
| << " padding: 18px;" << std::endl |
| << " border: 1px solid #ccc;" << std::endl |
| << " font-size: 18px;" << std::endl |
| << " color: #fff;" << std::endl |
| << " }" << std::endl |
| << " td.odd {" << std::endl |
| << " padding: 10px;" << std::endl |
| << " border: 1px solid #ccc;" << std::endl |
| << " font-size: 16px;" << std::endl |
| << " color: rgb(255, 255, 255);" << std::endl |
| << " }" << std::endl |
| << " td.even {" << std::endl |
| << " padding: 10px;" << std::endl |
| << " border: 1px solid #ccc;" << std::endl |
| << " font-size: 16px;" << std::endl |
| << " color: rgb(220, 220, 220);" << std::endl |
| << " }" << std::endl |
| << " tr.header {" << std::endl |
| << " background-color: rgba(255,255,255,0.5);" << std::endl |
| << " }" << std::endl |
| << " tr.odd {" << std::endl |
| << " background-color: rgba(0,0,0,0.6);" << std::endl |
| << " }" << std::endl |
| << " tr.even {" << std::endl |
| << " background-color: rgba(0,0,0,0.7);" << std::endl |
| << " }" << std::endl |
| << " </style>" << std::endl |
| << " <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/" |
| << "2.2.4/jquery.min.js\"></script>" << std::endl |
| << " <script type=\"text/javascript\">" << std::endl |
| << " $( document ).ready(function() {" << std::endl |
| << " $('table tr:not(.header)').hide();" << std::endl |
| << " $('.header').click(function() {" << std::endl |
| << " " |
| "$(this).nextUntil('tr.header').slideToggle(300);" |
| << std::endl |
| << " });" << std::endl |
| << " });" << std::endl |
| << " </script>" << std::endl |
| << "</HEAD>" << std::endl |
| << std::endl |
| << "<BODY>" << std::endl |
| << std::endl; |
| // We need space from the top for the VIA texture |
| for (uint32_t space = 0; space < 15; space++) { |
| global_items.html_file_stream << " <BR />" << std::endl; |
| } |
| // All the silly " " are to make sure the version lines up directly |
| // under the VIA portion of the log. |
| global_items.html_file_stream << " <H1 class=\"version\"><center>"; |
| for (uint32_t space = 0; space < 65; space++) { |
| global_items.html_file_stream << " "; |
| } |
| global_items.html_file_stream << APP_VERSION << "</center></h1>" |
| << std::endl |
| << " <BR />" << std::endl |
| << " <BR />" << std::endl; |
| } |
| |
| // Close out writing to the HTML file. |
| void EndOutput() { |
| global_items.html_file_stream << "</BODY>" << std::endl |
| << std::endl |
| << "</HTML>" << std::endl; |
| } |
| |
| void BeginSection(std::string section_str) { |
| global_items.html_file_stream << " <H1 class=\"section\"><center>" |
| << section_str << "</center></h1>" |
| << std::endl; |
| } |
| |
| void EndSection() { |
| global_items.html_file_stream << " <BR/>" << std::endl |
| << " <BR/>" << std::endl; |
| } |
| |
| void PrintStandardText(std::string section) { |
| global_items.html_file_stream << " <H2><font color=\"White\">" << section |
| << "</font></H2>" << std::endl; |
| } |
| |
| void PrintBeginTable(const char *table_name, uint32_t num_cols) { |
| |
| global_items.html_file_stream |
| << " <table align=\"center\">" << std::endl |
| << " <tr class=\"header\">" << std::endl |
| << " <td colspan=\"" << num_cols << "\" class=\"header\">" |
| << table_name << "</td>" << std::endl |
| << " </tr>" << std::endl; |
| |
| global_items.is_odd_row = true; |
| } |
| |
| void PrintBeginTableRow() { |
| std::string class_str = ""; |
| if (global_items.is_odd_row) { |
| class_str = " class=\"odd\""; |
| } else { |
| class_str = " class=\"even\""; |
| } |
| global_items.html_file_stream << " <tr" << class_str << ">" |
| << std::endl; |
| } |
| |
| void PrintTableElement(std::string element, ElementAlign align = ALIGN_LEFT) { |
| std::string align_str = ""; |
| std::string class_str = ""; |
| if (align == ALIGN_RIGHT) { |
| align_str = " align=\"right\""; |
| } |
| if (global_items.is_odd_row) { |
| class_str = " class=\"odd\""; |
| } else { |
| class_str = " class=\"even\""; |
| } |
| global_items.html_file_stream << " <td" << align_str << class_str |
| << ">" << element << "</td>" << std::endl; |
| } |
| |
| void PrintEndTableRow() { |
| global_items.html_file_stream << " </tr>" << std::endl; |
| global_items.is_odd_row = !global_items.is_odd_row; |
| } |
| |
| void PrintEndTable() { |
| global_items.html_file_stream << " </table>" << std::endl; |
| } |
| |
| // Generate the full library location for a file based on the location of |
| // the JSON file referencing it, and the library location contained in that |
| // JSON file. |
| bool GenerateLibraryPath(const char *json_location, const char *library_info, |
| const uint32_t max_length, char *library_location) { |
| bool success = false; |
| char final_path[MAX_STRING_LENGTH]; |
| char *working_string_ptr; |
| uint32_t len = |
| (max_length > MAX_STRING_LENGTH) ? MAX_STRING_LENGTH : max_length; |
| |
| if (NULL == json_location || NULL == library_info || |
| NULL == library_location) { |
| goto out; |
| } |
| |
| // Remove json file from json path to get just the file base location |
| strncpy(final_path, json_location, len); |
| working_string_ptr = strrchr(final_path, '\\'); |
| if (working_string_ptr == NULL) { |
| working_string_ptr = strrchr(final_path, '/'); |
| } |
| if (working_string_ptr != NULL) { |
| working_string_ptr++; |
| *working_string_ptr = '\0'; |
| } |
| |
| // Determine if the library is relative or absolute |
| if (library_info[0] == '\\' || library_info[0] == '/' || |
| library_info[1] == ':') { |
| // Absolute path |
| strncpy(library_location, library_info, len); |
| success = true; |
| } else { |
| uint32_t i = 0; |
| // Relative path, so we need to use the JSON's location |
| while (library_info[i] == '.' && library_info[i + 1] == '.' && |
| (library_info[i + 2] == '\\' || library_info[i + 2] == '/')) { |
| i += 3; |
| // Go up a folder in the json path |
| working_string_ptr = strrchr(final_path, '\\'); |
| if (working_string_ptr == NULL) { |
| working_string_ptr = strrchr(final_path, '/'); |
| } |
| if (working_string_ptr != NULL) { |
| working_string_ptr++; |
| *working_string_ptr = '\0'; |
| } |
| } |
| while (library_info[i] == '.' && |
| (library_info[i + 1] == '\\' || library_info[i + 1] == '/')) { |
| i += 2; |
| } |
| strncpy(library_location, final_path, MAX_STRING_LENGTH - 1); |
| strncat(library_location, &library_info[i], len); |
| success = true; |
| } |
| |
| out: |
| return success; |
| } |
| |
| #ifdef _WIN32 |
| // Registry utility fuctions to simplify reading data from the |
| // Windows registry. |
| |
| const char g_uninstall_reg_path[] = |
| "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; |
| |
| bool ReadRegKeyString(HKEY regFolder, const char *keyPath, |
| const char *valueName, const int maxLength, |
| char *retString) { |
| bool retVal = false; |
| DWORD bufLen = maxLength; |
| DWORD keyFlags = KEY_READ; |
| HKEY hKey; |
| LONG lret; |
| |
| if (global_items.is_wow64) { |
| keyFlags |= KEY_WOW64_64KEY; |
| } |
| |
| *retString = '\0'; |
| lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey); |
| if (lret == ERROR_SUCCESS) { |
| lret = RegQueryValueExA(hKey, valueName, NULL, NULL, (BYTE *)retString, |
| &bufLen); |
| if (lret == ERROR_SUCCESS) { |
| retVal = true; |
| } |
| RegCloseKey(hKey); |
| } |
| |
| return retVal; |
| } |
| |
| bool WriteRegKeyString(HKEY regFolder, const char *keyPath, char *valueName, |
| char *valueValue) { |
| bool retVal = false; |
| DWORD keyFlags = KEY_WRITE; |
| HKEY hKey; |
| LONG lret; |
| |
| if (global_items.is_wow64) { |
| keyFlags |= KEY_WOW64_64KEY; |
| } |
| |
| lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey); |
| if (lret == ERROR_SUCCESS) { |
| lret = RegSetKeyValueA(hKey, NULL, valueName, REG_SZ, |
| (BYTE *)valueValue, (DWORD)(strlen(valueValue))); |
| if (lret == ERROR_SUCCESS) { |
| retVal = true; |
| } |
| RegCloseKey(hKey); |
| } |
| |
| return retVal; |
| } |
| |
| bool DeleteRegKeyString(HKEY regFolder, const char *keyPath, char *valueName) { |
| bool retVal = false; |
| DWORD keyFlags = KEY_WRITE; |
| HKEY hKey; |
| LONG lret; |
| |
| if (global_items.is_wow64) { |
| keyFlags |= KEY_WOW64_64KEY; |
| } |
| |
| lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey); |
| if (lret == ERROR_SUCCESS) { |
| lret = RegDeleteKeyValueA(hKey, NULL, valueName); |
| if (lret == ERROR_SUCCESS) { |
| retVal = true; |
| } |
| RegCloseKey(hKey); |
| } |
| |
| return retVal; |
| } |
| |
| bool ReadRegKeyDword(HKEY regFolder, const char *keyPath, const char *valueName, |
| unsigned int *returnInt) { |
| bool retVal = false; |
| DWORD bufLen = sizeof(DWORD); |
| DWORD keyFlags = KEY_READ; |
| HKEY hKey; |
| LONG lret; |
| |
| if (global_items.is_wow64) { |
| keyFlags |= KEY_WOW64_64KEY; |
| } |
| |
| *returnInt = 0; |
| lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey); |
| if (lret == ERROR_SUCCESS) { |
| lret = RegQueryValueExA(hKey, valueName, NULL, NULL, (BYTE *)returnInt, |
| &bufLen); |
| if (lret == ERROR_SUCCESS) { |
| retVal = true; |
| } |
| RegCloseKey(hKey); |
| } |
| |
| return retVal; |
| } |
| |
| bool FindNextRegKey(HKEY regFolder, const char *keyPath, const char *keySearch, |
| const int itemIndex, const int maxLength, char *retString) { |
| bool retVal = false; |
| DWORD bufLen = MAX_STRING_LENGTH - 1; |
| DWORD keyFlags = KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE; |
| HKEY hKey; |
| LONG lret; |
| int itemCount = 0; |
| |
| if (global_items.is_wow64) { |
| keyFlags |= KEY_WOW64_64KEY; |
| } |
| |
| *retString = '\0'; |
| lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey); |
| if (lret == ERROR_SUCCESS) { |
| DWORD index = 0; |
| char keyName[MAX_STRING_LENGTH]; |
| |
| do { |
| lret = RegEnumKeyExA(hKey, index, keyName, &bufLen, NULL, NULL, |
| NULL, NULL); |
| if (ERROR_SUCCESS != lret) { |
| break; |
| } |
| if (strlen(keySearch) == 0 || NULL != strstr(keyName, keySearch)) { |
| if (itemIndex == itemCount) { |
| strncpy_s(retString, maxLength, keyName, bufLen); |
| retVal = true; |
| break; |
| } else { |
| itemCount++; |
| } |
| } |
| bufLen = MAX_STRING_LENGTH - 1; |
| ++index; |
| } while (true); |
| } |
| |
| return retVal; |
| } |
| |
| bool FindNextRegValue(HKEY regFolder, const char *keyPath, |
| const char *valueSearch, const int startIndex, |
| const int maxLength, char *retString, uint32_t *retValue) { |
| bool retVal = false; |
| DWORD bufLen = MAX_STRING_LENGTH - 1; |
| DWORD keyFlags = KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE; |
| HKEY hKey = 0; |
| LONG lret; |
| |
| if (global_items.is_wow64) { |
| keyFlags |= KEY_WOW64_64KEY; |
| } |
| |
| *retValue = 0; |
| *retString = '\0'; |
| lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey); |
| if (lret == ERROR_SUCCESS) { |
| DWORD index = startIndex; |
| char valueName[MAX_STRING_LENGTH]; |
| |
| do { |
| DWORD type = REG_DWORD; |
| DWORD value = 0; |
| DWORD len = 4; |
| valueName[0] = '\0'; |
| |
| lret = RegEnumValueA(hKey, index, valueName, &bufLen, NULL, &type, |
| (LPBYTE)&value, &len); |
| if (ERROR_SUCCESS != lret) { |
| break; |
| } |
| if (type == REG_DWORD) { |
| *retValue = value; |
| } |
| if (strlen(valueSearch) == 0 || |
| NULL != strstr(valueName, valueSearch)) { |
| strncpy_s(retString, maxLength, valueName, bufLen); |
| retVal = true; |
| break; |
| } |
| |
| bufLen = MAX_STRING_LENGTH - 1; |
| ++index; |
| } while (true); |
| } |
| |
| return retVal; |
| } |
| |
| // Registry prototypes for Windows |
| bool ReadRegKeyDword(HKEY regFolder, const char *keyPath, const char *valueName, |
| unsigned int *returnInt); |
| bool ReadRegKeyString(HKEY regFolder, const char *keyPath, |
| const char *valueName, const int maxLength, |
| char *retString); |
| bool FindNextRegKey(HKEY regFolder, const char *keyPath, const char *keySearch, |
| const int startIndex, const int maxLength, char *retString); |
| bool FindNextRegValue(HKEY regFolder, const char *keyPath, |
| const char *valueSearch, const int startIndex, |
| const int maxLength, char *retString, uint32_t *retValue); |
| bool WriteRegKeyString(HKEY regFolder, const char *keyPath, char *valueName, |
| char *valueValue); |
| bool DeleteRegKeyString(HKEY regFolder, const char *keyPath, char *valueName); |
| |
| // Functionality to determine if this 32-bit process is running on Windows 64. |
| // |
| void IsWow64() { |
| typedef BOOL(WINAPI * LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); |
| |
| // IsWow64Process is not available on all supported versions of Windows. |
| // Use GetModuleHandle to get a handle to the DLL that contains the function |
| // and GetProcAddress to get a pointer to the function if available. |
| |
| LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( |
| GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); |
| |
| if (NULL != fnIsWow64Process) { |
| BOOL isWOW = FALSE; |
| if (!fnIsWow64Process(GetCurrentProcess(), &isWOW)) { |
| printf("Error : Failed to determine properly if on Win64!"); |
| } |
| |
| if (isWOW == TRUE) { |
| global_items.is_wow64 = true; |
| } |
| } |
| } |
| |
| // Run the test in the specified directory with the corresponding |
| // command-line arguments. |
| // Returns 0 on no error, 1 if test file wasn't found, and -1 |
| // on any other errors. |
| int RunTestInDirectory(std::string path, std::string test, |
| std::string cmd_line) { |
| int err_code = -1; |
| char orig_dir[MAX_STRING_LENGTH]; |
| orig_dir[0] = '\0'; |
| if (0 != GetCurrentDirectoryA(MAX_STRING_LENGTH - 1, orig_dir) && |
| TRUE == SetCurrentDirectoryA(path.c_str())) { |
| if (TRUE == PathFileExists(test.c_str())) { |
| err_code = system(cmd_line.c_str()); |
| } else { |
| err_code = 1; |
| } |
| SetCurrentDirectoryA(orig_dir); |
| } |
| return err_code; |
| } |
| |
| // Print out any information about the current system that we can |
| // capture to ease in debugging/investigation at a later time. |
| void PrintSystemInfo(void) { |
| OSVERSIONINFOEX os_info; |
| SYSTEM_INFO sys_info; |
| MEMORYSTATUSEX mem_stat; |
| DWORD ser_ver = 0; |
| DWORD sect_per_cluster = 0; |
| DWORD bytes_per_sect = 0; |
| DWORD num_free_cluster = 0; |
| DWORD total_num_cluster = 0; |
| char system_root_dir[MAX_STRING_LENGTH]; |
| char generic_string[MAX_STRING_LENGTH]; |
| char output_string[MAX_STRING_LENGTH]; |
| char os_size[32]; |
| std::string cur_directory; |
| std::string exe_directory; |
| |
| // Determine if this 32-bit process is on Win64. |
| IsWow64(); |
| |
| #if _WIN64 |
| strncpy(os_size, " 64-bit", 31); |
| #else |
| strncpy(os_size, " 32-bit", 31); |
| #endif |
| |
| BeginSection("System Info"); |
| |
| // Environment section has information about the OS and the |
| // execution environment. |
| PrintBeginTable("Environment", 3); |
| |
| ZeroMemory(&sys_info, sizeof(SYSTEM_INFO)); |
| GetSystemInfo(&sys_info); |
| |
| ZeroMemory(&os_info, sizeof(OSVERSIONINFOEX)); |
| os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); |
| |
| ZeroMemory(&mem_stat, sizeof(MEMORYSTATUSEX)); |
| mem_stat.dwLength = sizeof(MEMORYSTATUSEX); |
| |
| // Since this is Windows #ifdef code, determine the version of Windows |
| // that the applciation is running on. It's not trivial and has to |
| // refer to items queried in the above structures as well as the |
| // Windows registry. |
| if (TRUE == GetVersionEx((LPOSVERSIONINFO)(&os_info))) { |
| switch (os_info.dwMajorVersion) { |
| case 10: |
| if (os_info.wProductType == VER_NT_WORKSTATION) { |
| if (ReadRegKeyString( |
| HKEY_LOCAL_MACHINE, |
| "Software\\Microsoft\\Windows NT\\CurrentVersion", |
| "ProductName", MAX_STRING_LENGTH - 1, generic_string)) { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement(generic_string); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| |
| if (ReadRegKeyString( |
| HKEY_LOCAL_MACHINE, |
| "Software\\Microsoft\\Windows NT\\CurrentVersion", |
| "CurrentBuild", MAX_STRING_LENGTH - 1, |
| output_string)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Build"); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| if (ReadRegKeyString( |
| HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windo" |
| "ws NT\\CurrentVersion", |
| "BuildBranch", MAX_STRING_LENGTH - 1, |
| output_string)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Branch"); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| } |
| } |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Windows 10 (or newer)"); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| } |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Windows Server 2016 (or newer)"); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| } |
| break; |
| case 6: |
| switch (os_info.dwMinorVersion) { |
| case 3: |
| if (os_info.wProductType == VER_NT_WORKSTATION) { |
| if (ReadRegKeyString( |
| HKEY_LOCAL_MACHINE, |
| "Software\\Microsoft\\Windows NT\\CurrentVersion", |
| "ProductName", MAX_STRING_LENGTH - 1, |
| generic_string)) { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement(generic_string); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| |
| if (ReadRegKeyString( |
| HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windo" |
| "ws NT\\CurrentVersion", |
| "CurrentBuild", MAX_STRING_LENGTH - 1, |
| output_string)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Build"); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| |
| if (ReadRegKeyString(HKEY_LOCAL_MACHINE, |
| "Software\\Microsoft\\Windo" |
| "ws NT\\CurrentVersion", |
| "BuildBranch", |
| MAX_STRING_LENGTH - 1, |
| output_string)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Branch"); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| } |
| } |
| } |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Windows Server 2012 R2 (or newer)"); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| } |
| break; |
| case 2: |
| if (os_info.wProductType == VER_NT_WORKSTATION) { |
| if (ReadRegKeyString( |
| HKEY_LOCAL_MACHINE, |
| "Software\\Microsoft\\Windows NT\\CurrentVersion", |
| "ProductName", MAX_STRING_LENGTH - 1, |
| generic_string)) { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement(generic_string); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| |
| if (ReadRegKeyString( |
| HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windo" |
| "ws NT\\CurrentVersion", |
| "CurrentBuild", MAX_STRING_LENGTH - 1, |
| output_string)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Build"); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| if (ReadRegKeyString(HKEY_LOCAL_MACHINE, |
| "Software\\Microsoft\\Windo" |
| "ws NT\\CurrentVersion", |
| "BuildBranch", |
| MAX_STRING_LENGTH - 1, |
| output_string)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Branch"); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| } |
| } |
| } |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Windows Server 2012 (or newer)"); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| } |
| break; |
| case 1: |
| if (os_info.wProductType == VER_NT_WORKSTATION) { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Windows 7 (or newer)"); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Windows Server 2008 R2 (or newer)"); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| } |
| break; |
| default: |
| if (os_info.wProductType == VER_NT_WORKSTATION) { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Windows Vista (or newer)"); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Windows Server 2008 (or newer)"); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| } |
| break; |
| } |
| break; |
| case 5: |
| ser_ver = GetSystemMetrics(SM_SERVERR2); |
| switch (os_info.dwMinorVersion) { |
| case 2: |
| if ((os_info.wProductType == VER_NT_WORKSTATION) && |
| (sys_info.wProcessorArchitecture == |
| PROCESSOR_ARCHITECTURE_AMD64)) { |
| strncpy(generic_string, "Windows XP Professional x64", |
| MAX_STRING_LENGTH - 1); |
| } else if (os_info.wSuiteMask & VER_SUITE_WH_SERVER) { |
| strncpy(generic_string, "Windows Home Server", |
| MAX_STRING_LENGTH - 1); |
| } else if (ser_ver != 0) { |
| strncpy(generic_string, "Windows Server 2003 R2", |
| MAX_STRING_LENGTH - 1); |
| } else { |
| strncpy(generic_string, "Windows Server 2003", |
| MAX_STRING_LENGTH - 1); |
| } |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement(generic_string); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| break; |
| case 1: |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Windows XP"); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| break; |
| case 0: |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Windows 2000"); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| break; |
| default: |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Unknown Windows OS"); |
| PrintTableElement(os_size); |
| PrintEndTableRow(); |
| break; |
| } |
| break; |
| } |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement("Windows"); |
| PrintTableElement("Error retrieving Windows Version"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| throw(-1); |
| } |
| |
| if (0 != GetEnvironmentVariableA("SYSTEMROOT", system_root_dir, |
| MAX_STRING_LENGTH - 1)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("System Root"); |
| PrintTableElement(system_root_dir); |
| PrintEndTableRow(); |
| } |
| if (0 != GetEnvironmentVariableA("PROGRAMDATA", generic_string, |
| MAX_STRING_LENGTH - 1)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Program Data"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| if (0 != GetEnvironmentVariableA("PROGRAMFILES", generic_string, |
| MAX_STRING_LENGTH - 1)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Program Files"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| if (0 != GetEnvironmentVariableA("PROGRAMFILES(X86)", generic_string, |
| MAX_STRING_LENGTH - 1)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Program Files (x86)"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| if (0 != GetEnvironmentVariableA("TEMP", generic_string, |
| MAX_STRING_LENGTH - 1)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("TEMP"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| if (0 != |
| GetEnvironmentVariableA("TMP", generic_string, MAX_STRING_LENGTH - 1)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("TMP"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| |
| PrintEndTable(); |
| |
| // Output whatever generic hardware information we can find out about the |
| // system. Including how much memory and disk space is available. |
| PrintBeginTable("Hardware", 3); |
| |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u", |
| sys_info.dwNumberOfProcessors); |
| PrintBeginTableRow(); |
| PrintTableElement("CPUs"); |
| PrintTableElement("Number of Logical Cores"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| switch (sys_info.wProcessorArchitecture) { |
| case PROCESSOR_ARCHITECTURE_AMD64: |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Type"); |
| PrintTableElement("x86_64"); |
| PrintEndTableRow(); |
| break; |
| case PROCESSOR_ARCHITECTURE_ARM: |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Type"); |
| PrintTableElement("ARM"); |
| PrintEndTableRow(); |
| break; |
| case PROCESSOR_ARCHITECTURE_IA64: |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Type"); |
| PrintTableElement("IA64"); |
| PrintEndTableRow(); |
| break; |
| case PROCESSOR_ARCHITECTURE_INTEL: |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Type"); |
| PrintTableElement("x86"); |
| PrintEndTableRow(); |
| break; |
| default: |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Type"); |
| PrintTableElement("Unknown"); |
| PrintEndTableRow(); |
| break; |
| } |
| |
| if (TRUE == GlobalMemoryStatusEx(&mem_stat)) { |
| if ((mem_stat.ullTotalPhys >> 40) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u TB", |
| static_cast<uint32_t>(mem_stat.ullTotalPhys >> 40)); |
| PrintBeginTableRow(); |
| PrintTableElement("Memory"); |
| PrintTableElement("Physical"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } else if ((mem_stat.ullTotalPhys >> 30) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u GB", |
| static_cast<uint32_t>(mem_stat.ullTotalPhys >> 30)); |
| PrintBeginTableRow(); |
| PrintTableElement("Memory"); |
| PrintTableElement("Physical"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } else if ((mem_stat.ullTotalPhys >> 20) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u MB", |
| static_cast<uint32_t>(mem_stat.ullTotalPhys >> 20)); |
| PrintBeginTableRow(); |
| PrintTableElement("Memory"); |
| PrintTableElement("Physical"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } else if ((mem_stat.ullTotalPhys >> 10) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u KB", |
| static_cast<uint32_t>(mem_stat.ullTotalPhys >> 10)); |
| PrintBeginTableRow(); |
| PrintTableElement("Memory"); |
| PrintTableElement("Physical"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } else { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u bytes", |
| static_cast<uint32_t>(mem_stat.ullTotalPhys)); |
| PrintBeginTableRow(); |
| PrintTableElement("Memory"); |
| PrintTableElement("Physical"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| } |
| |
| if (TRUE == GetDiskFreeSpaceA(NULL, §_per_cluster, &bytes_per_sect, |
| &num_free_cluster, &total_num_cluster)) { |
| uint64_t bytes_free = (uint64_t)bytes_per_sect * |
| (uint64_t)sect_per_cluster * |
| (uint64_t)num_free_cluster; |
| uint64_t bytes_total = (uint64_t)bytes_per_sect * |
| (uint64_t)sect_per_cluster * |
| (uint64_t)total_num_cluster; |
| double perc_free = (double)bytes_free / (double)bytes_total; |
| if ((bytes_total >> 40) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u TB", |
| static_cast<uint32_t>(bytes_total >> 40)); |
| PrintBeginTableRow(); |
| PrintTableElement("Disk Space"); |
| PrintTableElement("Total"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } else if ((bytes_total >> 30) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u GB", |
| static_cast<uint32_t>(bytes_total >> 30)); |
| PrintBeginTableRow(); |
| PrintTableElement("Disk Space"); |
| PrintTableElement("Total"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } else if ((bytes_total >> 20) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u MB", |
| static_cast<uint32_t>(bytes_total >> 20)); |
| PrintBeginTableRow(); |
| PrintTableElement("Disk Space"); |
| PrintTableElement("Total"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } else if ((bytes_total >> 10) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u KB", |
| static_cast<uint32_t>(bytes_total >> 10)); |
| PrintBeginTableRow(); |
| PrintTableElement("Disk Space"); |
| PrintTableElement("Total"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| snprintf(output_string, MAX_STRING_LENGTH - 1, "%4.2f%%", |
| (static_cast<float>(perc_free) * 100.f)); |
| if ((bytes_free >> 40) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u TB", |
| static_cast<uint32_t>(bytes_free >> 40)); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Free"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Free Perc"); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| } else if ((bytes_free >> 30) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u GB", |
| static_cast<uint32_t>(bytes_free >> 30)); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Free"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Free Perc"); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| } else if ((bytes_free >> 20) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u MB", |
| static_cast<uint32_t>(bytes_free >> 20)); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Free"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Free Perc"); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| } else if ((bytes_free >> 10) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u KB", |
| static_cast<uint32_t>(bytes_free >> 10)); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Free"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Free Perc"); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| } |
| } |
| |
| PrintEndTable(); |
| |
| // Print out information about this executable. |
| PrintBeginTable("Executable", 2); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("Exe Directory"); |
| PrintTableElement(global_items.exe_directory); |
| PrintEndTableRow(); |
| |
| if (0 != GetCurrentDirectoryA(MAX_STRING_LENGTH - 1, generic_string)) { |
| cur_directory = generic_string; |
| PrintBeginTableRow(); |
| PrintTableElement("Current Directory"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } else { |
| cur_directory = ""; |
| } |
| |
| PrintBeginTableRow(); |
| PrintTableElement("Vulkan API Version"); |
| uint32_t major = VK_VERSION_MAJOR(VK_API_VERSION_1_0); |
| uint32_t minor = VK_VERSION_MINOR(VK_API_VERSION_1_0); |
| uint32_t patch = VK_VERSION_PATCH(VK_HEADER_VERSION); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d.%d.%d", major, minor, |
| patch); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("Byte Format"); |
| #if _WIN64 || __x86_64__ || __ppc64__ |
| PrintTableElement("64-bit"); |
| #else |
| PrintTableElement("32-bit"); |
| #endif |
| PrintEndTableRow(); |
| |
| PrintEndTable(); |
| |
| // Now print out the remaining system info. |
| PrintDriverInfo(); |
| PrintRunTimeInfo(); |
| PrintSDKInfo(); |
| PrintLayerInfo(); |
| PrintLayerSettingsFileInfo(); |
| EndSection(); |
| } |
| |
| // Determine what version an executable or library file is. |
| bool GetFileVersion(const char *filename, const uint32_t max_len, |
| char *version_string) { |
| DWORD ver_handle; |
| UINT size = 0; |
| LPBYTE buffer = NULL; |
| DWORD ver_size = GetFileVersionInfoSize(filename, &ver_handle); |
| bool success = false; |
| |
| if (ver_size > 0) { |
| LPSTR ver_data = (LPSTR)malloc(sizeof(char) * ver_size); |
| |
| if (GetFileVersionInfo(filename, ver_handle, ver_size, ver_data)) { |
| if (VerQueryValue(ver_data, "\\", (VOID FAR * FAR *)&buffer, |
| &size)) { |
| if (size) { |
| VS_FIXEDFILEINFO *ver_info = (VS_FIXEDFILEINFO *)buffer; |
| if (ver_info->dwSignature == 0xfeef04bd) { |
| DWORD max_size = |
| ver_size > max_len ? max_len : ver_size; |
| snprintf(version_string, max_len, "%d.%d.%d.%d", |
| (ver_info->dwFileVersionMS >> 16) & 0xffff, |
| (ver_info->dwFileVersionMS >> 0) & 0xffff, |
| (ver_info->dwFileVersionLS >> 16) & 0xffff, |
| (ver_info->dwFileVersionLS >> 0) & 0xffff); |
| success = true; |
| } |
| } |
| } |
| } |
| free(ver_data); |
| } |
| |
| return success; |
| } |
| |
| // Print out the information for every driver in the appropriate |
| // Windows registry location and its corresponding JSON file. |
| void PrintDriverInfo(void) { |
| bool failed = false; |
| const char vulkan_reg_base[] = "SOFTWARE\\Khronos\\Vulkan"; |
| const char vulkan_reg_base_wow64[] = |
| "SOFTWARE\\WOW6432Node\\Khronos\\Vulkan"; |
| char reg_key_loc[MAX_STRING_LENGTH]; |
| char cur_vulkan_driver_json[MAX_STRING_LENGTH]; |
| char generic_string[MAX_STRING_LENGTH]; |
| char full_driver_path[MAX_STRING_LENGTH]; |
| char system_path[MAX_STRING_LENGTH]; |
| char count_str[64]; |
| uint32_t i = 0; |
| uint32_t j = 0; |
| std::ifstream *stream = NULL; |
| bool found_registry = false; |
| bool found_json = false; |
| bool found_lib = false; |
| |
| GetEnvironmentVariableA("SYSTEMROOT", generic_string, MAX_STRING_LENGTH); |
| #if _WIN64 || __x86_64__ || __ppc64__ |
| snprintf(system_path, MAX_STRING_LENGTH - 1, "%s\\system32\\", |
| generic_string); |
| snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\Drivers", |
| vulkan_reg_base); |
| #else |
| if (global_items.is_wow64) { |
| snprintf(system_path, MAX_STRING_LENGTH - 1, "%s\\sysWOW64\\", |
| generic_string); |
| snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\Drivers", |
| vulkan_reg_base_wow64); |
| } else { |
| snprintf(system_path, MAX_STRING_LENGTH - 1, "%s\\system32\\", |
| generic_string); |
| snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\Drivers", |
| vulkan_reg_base); |
| } |
| #endif |
| |
| PrintBeginTable("Vulkan Driver Info", 3); |
| PrintBeginTableRow(); |
| PrintTableElement("Registry Location"); |
| PrintTableElement(reg_key_loc); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| // Find the registry settings indicating the location of the driver |
| // JSON files. |
| uint32_t returned_value = 0; |
| while (FindNextRegValue(HKEY_LOCAL_MACHINE, reg_key_loc, "", i, |
| MAX_STRING_LENGTH - 1, cur_vulkan_driver_json, |
| &returned_value)) { |
| found_registry = true; |
| |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "Driver %d", i++); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(cur_vulkan_driver_json); |
| |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%08x", |
| returned_value); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| // Parse the driver JSON file. |
| std::ifstream *stream = NULL; |
| stream = new std::ifstream(cur_vulkan_driver_json, std::ifstream::in); |
| if (nullptr == stream || stream->fail()) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Error reading JSON file"); |
| PrintTableElement(cur_vulkan_driver_json); |
| PrintEndTableRow(); |
| |
| failed = true; |
| continue; |
| } else { |
| Json::Value root = Json::nullValue; |
| Json::Reader reader; |
| if (!reader.parse(*stream, root, false) || root.isNull()) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Error reading JSON file"); |
| PrintTableElement(reader.getFormattedErrorMessages()); |
| PrintEndTableRow(); |
| |
| failed = true; |
| stream->close(); |
| delete stream; |
| continue; |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("JSON File Version"); |
| if (!root["file_format_version"].isNull()) { |
| PrintTableElement(root["file_format_version"].asString()); |
| } else { |
| PrintTableElement("MISSING!"); |
| } |
| PrintEndTableRow(); |
| |
| if (!root["ICD"].isNull()) { |
| found_json = true; |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("API Version"); |
| if (!root["ICD"]["api_version"].isNull()) { |
| PrintTableElement( |
| root["ICD"]["api_version"].asString()); |
| } else { |
| PrintTableElement("MISSING!"); |
| } |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Library Path"); |
| if (!root["ICD"]["library_path"].isNull()) { |
| std::string driver_name = root["ICD"]["library_path"].asString(); |
| std::string system_name = system_path; |
| system_name += "\\"; |
| system_name += driver_name; |
| PrintTableElement(driver_name); |
| PrintEndTableRow(); |
| |
| if (GenerateLibraryPath( |
| cur_vulkan_driver_json, |
| driver_name.c_str(), |
| MAX_STRING_LENGTH - 1, full_driver_path)) { |
| |
| if (GetFileVersion(full_driver_path, |
| MAX_STRING_LENGTH - 1, |
| generic_string)) { |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Library File Version"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| found_lib = true; |
| } else if (GetFileVersion(system_name.c_str(), |
| MAX_STRING_LENGTH - 1, |
| generic_string)) { |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Library File Version"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| found_lib = true; |
| } else { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "Failed to find driver %s " |
| " or %sreferenced by JSON %s", |
| root["ICD"]["library_path"] |
| .asString() |
| .c_str(), |
| full_driver_path, |
| cur_vulkan_driver_json); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| } else { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "Failed to find driver %s " |
| "referenced by JSON %s", |
| full_driver_path, cur_vulkan_driver_json); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| } else { |
| PrintTableElement("MISSING!"); |
| PrintEndTableRow(); |
| } |
| |
| j = 0; |
| Json::Value dev_exts = root["ICD"]["device_extensions"]; |
| if (!dev_exts.isNull() && dev_exts.isArray()) { |
| snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", |
| dev_exts.size()); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Device Extensions"); |
| PrintTableElement(count_str); |
| PrintEndTableRow(); |
| |
| for (Json::ValueIterator dev_ext_it = dev_exts.begin(); |
| dev_ext_it != dev_exts.end(); dev_ext_it++) { |
| Json::Value dev_ext = (*dev_ext_it); |
| Json::Value dev_ext_name = dev_ext["name"]; |
| if (!dev_ext_name.isNull()) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "[%d]", j); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(dev_ext_name.asString()); |
| PrintEndTableRow(); |
| } |
| } |
| } |
| Json::Value inst_exts = root["ICD"]["instance_extensions"]; |
| j = 0; |
| if (!inst_exts.isNull() && inst_exts.isArray()) { |
| snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", |
| inst_exts.size()); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Instance Extensions"); |
| PrintTableElement(count_str); |
| PrintEndTableRow(); |
| |
| for (Json::ValueIterator inst_ext_it = |
| inst_exts.begin(); |
| inst_ext_it != inst_exts.end(); inst_ext_it++) { |
| Json::Value inst_ext = (*inst_ext_it); |
| Json::Value inst_ext_name = inst_ext["name"]; |
| if (!inst_ext_name.isNull()) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "[%d]", j); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(inst_ext_name.asString()); |
| PrintEndTableRow(); |
| } |
| } |
| } |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("ICD Section"); |
| PrintTableElement("MISSING!"); |
| PrintEndTableRow(); |
| } |
| } |
| |
| stream->close(); |
| delete stream; |
| stream = NULL; |
| } |
| } |
| if (!found_registry || !found_json || !found_lib) { |
| failed = true; |
| } |
| |
| PrintEndTable(); |
| |
| if (failed) { |
| if (!found_registry) { |
| throw MISSING_DRIVER_REGISTRY; |
| } else if (!found_json) { |
| throw MISSING_DRIVER_JSON; |
| } else if (!found_lib) { |
| throw MISSING_DRIVER_LIB; |
| } else { |
| throw(-1); |
| } |
| } |
| } |
| |
| // Print out whatever Vulkan runtime information we can gather from the system |
| // via registry, standard system paths, etc. |
| void PrintRunTimeInfo(void) { |
| char generic_string[MAX_STRING_LENGTH]; |
| char count_string[MAX_STRING_LENGTH]; |
| char version_string[MAX_STRING_LENGTH]; |
| char output_string[MAX_STRING_LENGTH]; |
| char dll_search[MAX_STRING_LENGTH]; |
| char dll_prefix[MAX_STRING_LENGTH]; |
| uint32_t i = 0; |
| uint32_t install_count = 0; |
| FILE *fp = NULL; |
| |
| PrintBeginTable("Vulkan Runtimes", 3); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("Runtimes In Registry"); |
| PrintTableElement(g_uninstall_reg_path); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| // Find all Vulkan Runtime keys in the registry, and loop through each. |
| while (FindNextRegKey(HKEY_LOCAL_MACHINE, g_uninstall_reg_path, "VulkanRT", |
| i, MAX_STRING_LENGTH - 1, output_string)) { |
| snprintf(count_string, MAX_STRING_LENGTH - 1, "[%d]", i++); |
| |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%s\\%s", |
| g_uninstall_reg_path, output_string); |
| |
| // Get the version from the registry |
| if (ReadRegKeyString(HKEY_LOCAL_MACHINE, generic_string, |
| "DisplayVersion", MAX_STRING_LENGTH - 1, |
| version_string)) { |
| } else { |
| strncpy(version_string, output_string, MAX_STRING_LENGTH - 1); |
| } |
| |
| // Get the install count for this runtime from the registry |
| if (ReadRegKeyDword(HKEY_LOCAL_MACHINE, generic_string, "InstallCount", |
| &install_count)) { |
| snprintf(output_string, MAX_STRING_LENGTH - 1, |
| "%s [Install Count = %d]", version_string, install_count); |
| } else { |
| snprintf(output_string, MAX_STRING_LENGTH - 1, "%s", |
| version_string); |
| } |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(count_string, ALIGN_RIGHT); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| } |
| |
| i = 0; |
| GetEnvironmentVariableA("SYSTEMROOT", generic_string, MAX_STRING_LENGTH); |
| #if _WIN64 || __x86_64__ || __ppc64__ |
| snprintf(dll_prefix, MAX_STRING_LENGTH - 1, "%s\\system32\\", |
| generic_string); |
| #else |
| if (global_items.is_wow64) { |
| snprintf(dll_prefix, MAX_STRING_LENGTH - 1, "%s\\sysWOW64\\", |
| generic_string); |
| } else { |
| snprintf(dll_prefix, MAX_STRING_LENGTH - 1, "%s\\system32\\", |
| generic_string); |
| } |
| #endif |
| |
| PrintBeginTableRow(); |
| PrintTableElement("Runtimes in System Folder"); |
| PrintTableElement(dll_prefix); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| strncpy(dll_search, dll_prefix, MAX_STRING_LENGTH - 1); |
| strncat(dll_search, "Vulkan-*.dll", MAX_STRING_LENGTH - 1); |
| |
| WIN32_FIND_DATAA ffd; |
| HANDLE hFind = FindFirstFileA(dll_search, &ffd); |
| if (hFind != INVALID_HANDLE_VALUE) { |
| do { |
| if (0 == (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { |
| snprintf(count_string, MAX_STRING_LENGTH - 1, "DLL %d", i++); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(count_string, ALIGN_RIGHT); |
| PrintTableElement(ffd.cFileName); |
| |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%s\\%s", |
| dll_prefix, ffd.cFileName); |
| if (GetFileVersion(generic_string, MAX_STRING_LENGTH - 1, |
| version_string)) { |
| snprintf(output_string, MAX_STRING_LENGTH - 1, "Version %s", |
| version_string); |
| PrintTableElement(output_string); |
| } else { |
| PrintTableElement(""); |
| } |
| PrintEndTableRow(); |
| } |
| } while (FindNextFileA(hFind, &ffd) != 0); |
| FindClose(hFind); |
| } |
| |
| PrintBeginTableRow(); |
| PrintTableElement("Runtime Used by App"); |
| if (!system("where vulkan-1.dll > where_vulkan")) { |
| fp = fopen("where_vulkan", "rt"); |
| if (NULL != fp) { |
| if (NULL != fgets(generic_string, MAX_STRING_LENGTH - 1, fp)) { |
| int i = (int)strlen(generic_string) - 1; |
| while (generic_string[i] == '\n' || generic_string[i] == '\r' || |
| generic_string[i] == '\t' || generic_string[i] == ' ') { |
| generic_string[i] = '\0'; |
| i--; |
| } |
| |
| if (GetFileVersion(generic_string, MAX_STRING_LENGTH - 1, |
| version_string)) { |
| PrintTableElement(generic_string); |
| PrintTableElement(version_string); |
| } else { |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| } |
| } |
| fclose(fp); |
| } |
| DeleteFileA("where_vulkan"); |
| } else { |
| PrintTableElement("Unknown"); |
| PrintTableElement("Unknown"); |
| } |
| PrintEndTableRow(); |
| |
| PrintEndTable(); |
| } |
| |
| // Print out information on whatever LunarG Vulkan SDKs we can find on |
| // the system using the registry, and environmental variables. This |
| // includes listing what layers are available from the SDK. |
| void PrintSDKInfo(void) { |
| const char vulkan_reg_base[] = "SOFTWARE\\Khronos\\Vulkan"; |
| const char vulkan_reg_base_wow64[] = |
| "SOFTWARE\\WOW6432Node\\Khronos\\Vulkan"; |
| char generic_string[MAX_STRING_LENGTH]; |
| char count_string[MAX_STRING_LENGTH]; |
| char output_string[MAX_STRING_LENGTH]; |
| char cur_vulkan_layer_json[MAX_STRING_LENGTH]; |
| char sdk_env_dir[MAX_STRING_LENGTH]; |
| char reg_key_loc[MAX_STRING_LENGTH]; |
| uint32_t i = 0; |
| uint32_t j = 0; |
| FILE *fp = NULL; |
| bool found = false; |
| bool failed = false; |
| |
| PrintBeginTable("LunarG Vulkan SDKs", 3); |
| PrintBeginTableRow(); |
| PrintTableElement("SDKs Found In Registry"); |
| PrintTableElement(g_uninstall_reg_path); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| while (FindNextRegKey(HKEY_LOCAL_MACHINE, g_uninstall_reg_path, "VulkanSDK", |
| i, MAX_STRING_LENGTH, output_string)) { |
| found = true; |
| snprintf(count_string, MAX_STRING_LENGTH - 1, "[%d]", i++); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%s\\%s", |
| g_uninstall_reg_path, output_string); |
| if (ReadRegKeyString(HKEY_LOCAL_MACHINE, generic_string, "InstallDir", |
| MAX_STRING_LENGTH, output_string)) { |
| } |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(count_string, ALIGN_RIGHT); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| } |
| if (!found) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("NONE FOUND", ALIGN_RIGHT); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } |
| |
| if (0 != GetEnvironmentVariableA("VK_SDK_PATH", sdk_env_dir, |
| MAX_STRING_LENGTH - 1)) { |
| PrintBeginTableRow(); |
| PrintTableElement("VK_SDK_PATH"); |
| global_items.sdk_found = true; |
| global_items.sdk_path = sdk_env_dir; |
| PrintTableElement(sdk_env_dir); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } else if (0 != GetEnvironmentVariableA("VULKAN_SDK", sdk_env_dir, |
| MAX_STRING_LENGTH - 1)) { |
| PrintBeginTableRow(); |
| PrintTableElement("VULKAN_SDK"); |
| global_items.sdk_found = true; |
| global_items.sdk_path = sdk_env_dir; |
| PrintTableElement(sdk_env_dir); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement("VK_SDK_PATH"); |
| PrintTableElement("No installed SDK"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } |
| |
| #if _WIN64 || __x86_64__ || __ppc64__ |
| snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\ExplicitLayers", |
| vulkan_reg_base); |
| #else |
| if (global_items.is_wow64) { |
| snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\ExplicitLayers", |
| vulkan_reg_base_wow64); |
| } else { |
| snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\ExplicitLayers", |
| vulkan_reg_base); |
| } |
| #endif |
| |
| PrintBeginTableRow(); |
| PrintTableElement("SDK Explicit Layers"); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| found = false; |
| i = 0; |
| uint32_t returned_value = 0; |
| while (FindNextRegValue(HKEY_LOCAL_MACHINE, reg_key_loc, "", i, |
| MAX_STRING_LENGTH, cur_vulkan_layer_json, |
| &returned_value)) { |
| found = true; |
| |
| // Create a short json file name so we don't use up too much space |
| snprintf(output_string, MAX_STRING_LENGTH - 1, ".%s", |
| &cur_vulkan_layer_json[strlen(sdk_env_dir)]); |
| |
| snprintf(count_string, MAX_STRING_LENGTH - 1, "[%d]", i++); |
| PrintBeginTableRow(); |
| PrintTableElement(count_string, ALIGN_RIGHT); |
| PrintTableElement(output_string); |
| |
| snprintf(output_string, MAX_STRING_LENGTH - 1, "0x%08x", returned_value); |
| PrintTableElement(output_string); |
| PrintEndTableRow(); |
| |
| std::ifstream *stream = NULL; |
| stream = new std::ifstream(cur_vulkan_layer_json, std::ifstream::in); |
| if (nullptr == stream || stream->fail()) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("ERROR reading JSON file!"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| Json::Value root = Json::nullValue; |
| Json::Reader reader; |
| if (!reader.parse(*stream, root, false) || root.isNull()) { |
| // Report to the user the failure and their locations in the |
| // document. |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("ERROR parsing JSON file!"); |
| PrintTableElement(reader.getFormattedErrorMessages()); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| PrintExplicitLayerJsonInfo(cur_vulkan_layer_json, root, 3); |
| } |
| |
| stream->close(); |
| delete stream; |
| stream = NULL; |
| } |
| } |
| if (!found) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("NONE FOUND", ALIGN_RIGHT); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } |
| |
| PrintEndTable(); |
| |
| if (failed) { |
| throw(-1); |
| } |
| } |
| |
| // Print out whatever layers we can find out from the Windows' |
| // registry and other environmental variables that may be used |
| // to point the Vulkan loader at a layer path. |
| void PrintLayerInfo(void) { |
| const char vulkan_reg_base[] = "SOFTWARE\\Khronos\\Vulkan"; |
| const char vulkan_reg_base_wow64[] = |
| "SOFTWARE\\WOW6432Node\\Khronos\\Vulkan"; |
| char vulkan_impl_layer_reg_key[MAX_STRING_LENGTH]; |
| char cur_vulkan_layer_json[MAX_STRING_LENGTH]; |
| char generic_string[MAX_STRING_LENGTH]; |
| char full_layer_path[MAX_STRING_LENGTH]; |
| char env_value[MAX_STRING_LENGTH]; |
| uint32_t i = 0; |
| uint32_t j = 0; |
| FILE *fp = NULL; |
| bool failed = false; |
| |
| // Dump implicit layer information first. |
| #if _WIN64 || __x86_64__ || __ppc64__ |
| snprintf(vulkan_impl_layer_reg_key, MAX_STRING_LENGTH - 1, |
| "%s\\ImplicitLayers", vulkan_reg_base); |
| #else |
| if (global_items.is_wow64) { |
| snprintf(vulkan_impl_layer_reg_key, MAX_STRING_LENGTH - 1, |
| "%s\\ImplicitLayers", vulkan_reg_base_wow64); |
| } else { |
| snprintf(vulkan_impl_layer_reg_key, MAX_STRING_LENGTH - 1, |
| "%s\\ImplicitLayers", vulkan_reg_base); |
| } |
| #endif |
| |
| PrintBeginTable("Implicit Layers", 4); |
| PrintBeginTableRow(); |
| PrintTableElement("Registry"); |
| PrintTableElement(vulkan_impl_layer_reg_key); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| // For each implicit layer listed in the registry, find its JSON and |
| // print out the useful information stored in it. |
| uint32_t returned_value = 0; |
| while (FindNextRegValue(HKEY_LOCAL_MACHINE, vulkan_impl_layer_reg_key, "", |
| i, MAX_STRING_LENGTH, cur_vulkan_layer_json, |
| &returned_value)) { |
| |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", i++); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(cur_vulkan_layer_json); |
| PrintTableElement(""); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%08x", returned_value); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| std::ifstream *stream = NULL; |
| stream = new std::ifstream(cur_vulkan_layer_json, std::ifstream::in); |
| if (nullptr == stream || stream->fail()) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("ERROR reading JSON file!"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| Json::Value root = Json::nullValue; |
| Json::Reader reader; |
| if (!reader.parse(*stream, root, false) || root.isNull()) { |
| // Report to the user the failure and their locations in the |
| // document. |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("ERROR parsing JSON file!"); |
| PrintTableElement(reader.getFormattedErrorMessages()); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| PrintImplicitLayerJsonInfo(cur_vulkan_layer_json, root); |
| } |
| |
| stream->close(); |
| delete stream; |
| stream = NULL; |
| } |
| } |
| PrintEndTable(); |
| |
| // If the user's system has VK_LAYER_PATH set, dump out the layer |
| // information found in that folder. This is important because if |
| // a user is having problems with the layers, they may be using |
| // non-standard layers. |
| if (0 != GetEnvironmentVariableA("VK_LAYER_PATH", env_value, |
| MAX_STRING_LENGTH - 1)) { |
| WIN32_FIND_DATAA ffd; |
| HANDLE hFind; |
| |
| PrintBeginTable("VK_LAYER_PATH Explicit Layers", 3); |
| PrintBeginTableRow(); |
| PrintTableElement("VK_LAYER_PATH"); |
| PrintTableElement(env_value); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| // Look for any JSON files in that folder. |
| snprintf(full_layer_path, MAX_STRING_LENGTH - 1, "%s\\*.json", |
| env_value); |
| i = 0; |
| hFind = FindFirstFileA(full_layer_path, &ffd); |
| if (hFind != INVALID_HANDLE_VALUE) { |
| do { |
| if (0 == (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", |
| i++); |
| snprintf(cur_vulkan_layer_json, MAX_STRING_LENGTH - 1, |
| "%s\\%s", env_value, ffd.cFileName); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(ffd.cFileName); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| std::ifstream *stream = NULL; |
| stream = new std::ifstream(cur_vulkan_layer_json, |
| std::ifstream::in); |
| if (nullptr == stream || stream->fail()) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("ERROR reading JSON file!"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| Json::Value root = Json::nullValue; |
| Json::Reader reader; |
| if (!reader.parse(*stream, root, false) || |
| root.isNull()) { |
| // Report to the user the failure and their |
| // locations in the document. |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("ERROR parsing JSON file!"); |
| PrintTableElement( |
| reader.getFormattedErrorMessages()); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| PrintExplicitLayerJsonInfo(cur_vulkan_layer_json, |
| root, 3); |
| } |
| |
| stream->close(); |
| delete stream; |
| stream = NULL; |
| } |
| } |
| } while (FindNextFileA(hFind, &ffd) != 0); |
| |
| FindClose(hFind); |
| } |
| |
| PrintEndTable(); |
| } |
| |
| if (failed) { |
| throw(-1); |
| } |
| } |
| |
| #elif __GNUC__ |
| |
| // Utility function to determine if a driver may exist in the folder. |
| bool CheckDriver(std::string &folder_loc, std::string &object_name) { |
| bool success = false; |
| std::string full_name = folder_loc; |
| if (folder_loc.c_str()[folder_loc.size() - 1] != '/') { |
| full_name += "/"; |
| } |
| full_name += object_name; |
| if (access(full_name.c_str(), R_OK) != -1) { |
| success = true; |
| } |
| return success; |
| } |
| |
| // Pointer to a function sed to validate if the system object is found |
| typedef bool (*PFN_CheckIfValid)(std::string &folder_loc, |
| std::string &object_name); |
| |
| bool FindLinuxSystemObject(std::string object_name, PFN_CheckIfValid func, |
| bool break_on_first) { |
| bool found_one = false; |
| std::string path_to_check; |
| char *env_value = getenv("LD_LIBRARY_PATH"); |
| |
| for (uint32_t iii = 0; iii < 5; iii++) { |
| switch (iii) { |
| case 0: |
| path_to_check = "/usr/lib"; |
| break; |
| case 1: |
| #if __x86_64__ || __ppc64__ |
| path_to_check = "/usr/lib/x86_64-linux-gnu"; |
| #else |
| path_to_check = "/usr/lib/i386-linux-gnu"; |
| #endif |
| break; |
| case 2: |
| #if __x86_64__ || __ppc64__ |
| path_to_check = "/usr/lib64"; |
| #else |
| path_to_check = "/usr/lib32"; |
| #endif |
| break; |
| case 3: |
| path_to_check = "/usr/local/lib"; |
| break; |
| case 4: |
| #if __x86_64__ || __ppc64__ |
| path_to_check = "/usr/local/lib64"; |
| #else |
| path_to_check = "/usr/local/lib32"; |
| #endif |
| break; |
| default: |
| continue; |
| } |
| |
| if (func(path_to_check, object_name)) { |
| // We found one runtime, clear any failures |
| found_one = true; |
| if (break_on_first) { |
| goto out; |
| } |
| } |
| } |
| |
| // LD_LIBRARY_PATH may have multiple folders listed in it (colon |
| // ':' delimited) |
| if (env_value != NULL) { |
| char *tok = strtok(env_value, ":"); |
| while (tok != NULL) { |
| path_to_check = tok; |
| if (func(path_to_check, object_name)) { |
| // We found one runtime, clear any failures |
| found_one = true; |
| } |
| tok = strtok(NULL, ":"); |
| } |
| } |
| |
| out: |
| return found_one; |
| } |
| |
| // Print out any information about the current system that we can |
| // capture to ease in debugging/investigation at a later time. |
| void PrintSystemInfo(void) { |
| FILE *fp; |
| char path[1035]; |
| char generic_string[MAX_STRING_LENGTH]; |
| utsname buffer; |
| struct statvfs fs_stats; |
| int num_cpus; |
| uint64_t memory; |
| char *env_value; |
| bool failed = false; |
| std::string cur_directory; |
| std::string exe_directory; |
| std::string desktop_session; |
| |
| BeginSection("System Info"); |
| |
| // Environment section has information about the OS and the |
| // execution environment. |
| PrintBeginTable("Environment", 3); |
| |
| fp = popen("cat /etc/os-release", "r"); |
| if (fp == NULL) { |
| PrintBeginTableRow(); |
| PrintTableElement("ERROR"); |
| PrintTableElement("Failed to cat /etc/os-release"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| // Read the output a line at a time - output it. |
| while (fgets(path, sizeof(path) - 1, fp) != NULL) { |
| if (NULL != strstr(path, "PRETTY_NAME")) { |
| uint32_t index; |
| index = strlen(path) - 1; |
| while (path[index] == ' ' || path[index] == '\t' || |
| path[index] == '\r' || path[index] == '\n' || |
| path[index] == '\"') { |
| path[index] = '\0'; |
| index = strlen(path) - 1; |
| } |
| index = 13; |
| while (path[index] == ' ' || path[index] == '\t' || |
| path[index] == '\"') { |
| index++; |
| } |
| PrintBeginTableRow(); |
| PrintTableElement("Linux"); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Distro"); |
| PrintTableElement(&path[index]); |
| PrintEndTableRow(); |
| break; |
| } |
| } |
| pclose(fp); |
| } |
| |
| errno = 0; |
| if (uname(&buffer) != 0) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("ERROR"); |
| PrintTableElement("Failed to query uname"); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Kernel Build"); |
| PrintTableElement(buffer.release); |
| PrintEndTableRow(); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Machine Target"); |
| PrintTableElement(buffer.machine); |
| PrintEndTableRow(); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Version"); |
| PrintTableElement(buffer.version); |
| PrintEndTableRow(); |
| } |
| |
| env_value = getenv("DESKTOP_SESSION"); |
| if (env_value != NULL) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("DESKTOP_SESSION"); |
| PrintTableElement(env_value); |
| PrintEndTableRow(); |
| |
| desktop_session = env_value; |
| } |
| env_value = getenv("LD_LIBRARY_PATH"); |
| if (env_value != NULL) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("LD_LIBRARY_PATH"); |
| PrintTableElement(env_value); |
| PrintEndTableRow(); |
| } |
| env_value = getenv("GDK_BACKEND"); |
| if (env_value != NULL) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("GDK_BACKEND"); |
| PrintTableElement(env_value); |
| PrintEndTableRow(); |
| } |
| env_value = getenv("DISPLAY"); |
| if (env_value != NULL) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("DISPLAY"); |
| PrintTableElement(env_value); |
| PrintEndTableRow(); |
| } |
| env_value = getenv("WAYLAND_DISPLAY"); |
| if (env_value != NULL) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("WAYLAND_DISPLAY"); |
| PrintTableElement(env_value); |
| PrintEndTableRow(); |
| } |
| env_value = getenv("MIR_SOCKET"); |
| if (env_value != NULL) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("MIR_SOCKET"); |
| PrintTableElement(env_value); |
| PrintEndTableRow(); |
| } |
| |
| PrintEndTable(); |
| |
| // Output whatever generic hardware information we can find out about the |
| // system. Including how much memory and disk space is available. |
| PrintBeginTable("Hardware", 3); |
| |
| num_cpus = sysconf(_SC_NPROCESSORS_ONLN); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", num_cpus); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("CPUs"); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| memory = (sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE)) >> 10; |
| if ((memory >> 10) > 0) { |
| memory >>= 10; |
| if ((memory >> 20) > 0) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u TB", |
| static_cast<uint32_t>(memory >> 20)); |
| } else if ((memory >> 10) > 0) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u GB", |
| static_cast<uint32_t>(memory >> 10)); |
| } else { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u MB", |
| static_cast<uint32_t>(memory)); |
| } |
| } else { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u KB", |
| static_cast<uint32_t>(memory)); |
| } |
| PrintBeginTableRow(); |
| PrintTableElement("Memory"); |
| PrintTableElement("Physical"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| if (0 == statvfs("/etc/os-release", &fs_stats)) { |
| uint64_t bytes_total = |
| (uint64_t)fs_stats.f_bsize * (uint64_t)fs_stats.f_bavail; |
| if ((bytes_total >> 40) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u TB", |
| static_cast<uint32_t>(bytes_total >> 40)); |
| PrintBeginTableRow(); |
| PrintTableElement("Disk Space"); |
| PrintTableElement("Free"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } else if ((bytes_total >> 30) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u GB", |
| static_cast<uint32_t>(bytes_total >> 30)); |
| PrintBeginTableRow(); |
| PrintTableElement("Disk Space"); |
| PrintTableElement("Free"); |
| PrintTableElement(generic_string); |
| } else if ((bytes_total >> 20) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u MB", |
| static_cast<uint32_t>(bytes_total >> 20)); |
| PrintBeginTableRow(); |
| PrintTableElement("Disk Space"); |
| PrintTableElement("Free"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } else if ((bytes_total >> 10) > 0x0ULL) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u KB", |
| static_cast<uint32_t>(bytes_total >> 10)); |
| PrintBeginTableRow(); |
| PrintTableElement("Disk Space"); |
| PrintTableElement("Free"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } else { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u bytes", |
| static_cast<uint32_t>(bytes_total)); |
| PrintBeginTableRow(); |
| PrintTableElement("Disk Space"); |
| PrintTableElement("Free"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| } |
| PrintEndTable(); |
| |
| // Print out information about this executable. |
| PrintBeginTable("Executable", 2); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("Exe Directory"); |
| PrintTableElement(global_items.exe_directory); |
| PrintEndTableRow(); |
| |
| if (getcwd(generic_string, MAX_STRING_LENGTH - 1) != NULL) { |
| cur_directory = generic_string; |
| PrintBeginTableRow(); |
| PrintTableElement("Current Directory"); |
| PrintTableElement(cur_directory); |
| PrintEndTableRow(); |
| } else { |
| cur_directory = ""; |
| } |
| |
| PrintBeginTableRow(); |
| PrintTableElement("App Version"); |
| PrintTableElement(APP_VERSION); |
| PrintEndTableRow(); |
| |
| uint32_t major = VK_VERSION_MAJOR(VK_API_VERSION_1_0); |
| uint32_t minor = VK_VERSION_MINOR(VK_API_VERSION_1_0); |
| uint32_t patch = VK_VERSION_PATCH(VK_HEADER_VERSION); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d.%d.%d", major, minor, |
| patch); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("Vulkan API Version"); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("Byte Format"); |
| #if __x86_64__ || __ppc64__ |
| PrintTableElement("64-bit"); |
| #else |
| PrintTableElement("32-bit"); |
| #endif |
| PrintEndTableRow(); |
| |
| PrintEndTable(); |
| |
| // Print out the rest of the useful system information. |
| PrintDriverInfo(); |
| PrintRunTimeInfo(); |
| PrintSDKInfo(); |
| PrintLayerInfo(); |
| PrintLayerSettingsFileInfo(); |
| EndSection(); |
| |
| if (failed) { |
| throw(-1); |
| } |
| } |
| |
| // Print out the information for every driver JSON in the appropriate |
| // system folders. |
| void PrintDriverInfo(void) { |
| bool failed = false; |
| char generic_string[MAX_STRING_LENGTH]; |
| char full_driver_path[MAX_STRING_LENGTH]; |
| uint32_t i = 0; |
| uint32_t j = 0; |
| bool found_json = false; |
| bool found_lib = false; |
| |
| PrintBeginTable("Vulkan Driver Info", 3); |
| |
| // There are three folders ICD JSONs could be in. So, |
| // try all three. |
| for (uint32_t dir = 0; dir < 5; dir++) { |
| std::string cur_driver_path; |
| std::string cur_driver_json; |
| switch (dir) { |
| case 0: |
| cur_driver_path = "/etc/vulkan/icd.d"; |
| break; |
| case 1: |
| cur_driver_path = "/usr/share/vulkan/icd.d"; |
| break; |
| case 2: |
| cur_driver_path = "/usr/local/etc/vulkan/icd.d"; |
| break; |
| case 3: |
| cur_driver_path = "/usr/local/share/vulkan/icd.d"; |
| break; |
| case 4: { |
| char *env_value = getenv("VK_DRIVERS_PATH"); |
| if (NULL == env_value) { |
| continue; |
| } |
| cur_driver_path = env_value; |
| break; |
| } |
| default: |
| continue; |
| } |
| |
| PrintBeginTableRow(); |
| PrintTableElement(cur_driver_path.c_str()); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| // Loop through each JSON file found in the current |
| // location. |
| DIR *layer_dir = opendir(cur_driver_path.c_str()); |
| if (NULL == layer_dir) { |
| continue; |
| } |
| dirent *cur_ent; |
| i = 0; |
| while ((cur_ent = readdir(layer_dir)) != NULL) { |
| if (NULL != strstr(cur_ent->d_name, ".json")) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", i++); |
| cur_driver_json = cur_driver_path; |
| cur_driver_json += "/"; |
| cur_driver_json += cur_ent->d_name; |
| |
| PrintBeginTableRow(); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(cur_ent->d_name); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| bool found_lib_this_time = false; |
| std::ifstream *stream = NULL; |
| stream = new std::ifstream(cur_driver_json.c_str(), |
| std::ifstream::in); |
| if (nullptr == stream || stream->fail()) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Error reading JSON file"); |
| PrintTableElement(cur_driver_json); |
| PrintEndTableRow(); |
| |
| failed = true; |
| continue; |
| } else { |
| Json::Value root = Json::nullValue; |
| Json::Reader reader; |
| if (!reader.parse(*stream, root, false) || root.isNull()) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Error reading JSON file"); |
| PrintTableElement(reader.getFormattedErrorMessages()); |
| PrintEndTableRow(); |
| |
| failed = true; |
| stream->close(); |
| delete stream; |
| continue; |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("JSON File Version"); |
| if (!root["file_format_version"].isNull()) { |
| PrintTableElement( |
| root["file_format_version"].asString()); |
| } else { |
| PrintTableElement("MISSING!"); |
| } |
| PrintEndTableRow(); |
| |
| if (!root["ICD"].isNull()) { |
| found_json = true; |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("API Version"); |
| if (!root["ICD"]["api_version"].isNull()) { |
| PrintTableElement( |
| root["ICD"]["api_version"].asString()); |
| } else { |
| PrintTableElement("MISSING!"); |
| } |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Library Path"); |
| if (!root["ICD"]["library_path"].isNull()) { |
| std::string driver_name = root["ICD"]["library_path"].asString(); |
| PrintTableElement(driver_name); |
| PrintEndTableRow(); |
| |
| if (GenerateLibraryPath( |
| cur_driver_json.c_str(), |
| driver_name.c_str(), |
| MAX_STRING_LENGTH, full_driver_path)) { |
| // First try the generated path. |
| if (access(full_driver_path, R_OK) != -1) { |
| found_lib_this_time = true; |
| } else if (driver_name.find("/") == std::string::npos) { |
| if (FindLinuxSystemObject(driver_name, |
| CheckDriver, |
| true)) { |
| found_lib_this_time = true; |
| } |
| } |
| if (!found_lib_this_time) { |
| snprintf(generic_string, |
| MAX_STRING_LENGTH - 1, |
| "Failed to find driver %s " |
| "referenced by JSON %s", |
| full_driver_path, |
| cur_driver_json.c_str()); |
| } else { |
| found_lib = true; |
| } |
| } else { |
| snprintf(generic_string, |
| MAX_STRING_LENGTH - 1, |
| "Failed to find driver %s " |
| "referenced by JSON %s", |
| full_driver_path, |
| cur_driver_json.c_str()); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| } else { |
| PrintTableElement("MISSING!"); |
| PrintEndTableRow(); |
| } |
| |
| char count_str[MAX_STRING_LENGTH]; |
| j = 0; |
| Json::Value dev_exts = |
| root["ICD"]["device_extensions"]; |
| if (!dev_exts.isNull() && dev_exts.isArray()) { |
| snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", |
| dev_exts.size()); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Device Extensions"); |
| PrintTableElement(count_str); |
| PrintEndTableRow(); |
| |
| for (Json::ValueIterator dev_ext_it = |
| dev_exts.begin(); |
| dev_ext_it != dev_exts.end(); |
| dev_ext_it++) { |
| Json::Value dev_ext = (*dev_ext_it); |
| Json::Value dev_ext_name = dev_ext["name"]; |
| if (!dev_ext_name.isNull()) { |
| snprintf(generic_string, |
| MAX_STRING_LENGTH - 1, "[%d]", |
| j); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(generic_string, |
| ALIGN_RIGHT); |
| PrintTableElement( |
| dev_ext_name.asString()); |
| PrintEndTableRow(); |
| } |
| } |
| } |
| Json::Value inst_exts = |
| root["ICD"]["instance_extensions"]; |
| j = 0; |
| if (!inst_exts.isNull() && inst_exts.isArray()) { |
| snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", |
| inst_exts.size()); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Instance Extensions"); |
| PrintTableElement(count_str); |
| PrintEndTableRow(); |
| |
| for (Json::ValueIterator inst_ext_it = |
| |
| inst_exts.begin(); |
| inst_ext_it != inst_exts.end(); |
| inst_ext_it++) { |
| Json::Value inst_ext = (*inst_ext_it); |
| Json::Value inst_ext_name = |
| inst_ext["name"]; |
| if (!inst_ext_name.isNull()) { |
| snprintf(generic_string, |
| MAX_STRING_LENGTH - 1, "[%d]", |
| j); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(generic_string, |
| ALIGN_RIGHT); |
| PrintTableElement( |
| inst_ext_name.asString()); |
| PrintEndTableRow(); |
| } |
| } |
| } |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("ICD Section"); |
| PrintTableElement("MISSING!"); |
| PrintEndTableRow(); |
| } |
| } |
| |
| stream->close(); |
| delete stream; |
| stream = NULL; |
| } |
| } |
| } |
| } |
| if (!found_json || !found_lib) { |
| failed = true; |
| } |
| PrintEndTable(); |
| if (failed) { |
| if (!found_json) { |
| throw MISSING_DRIVER_JSON; |
| } else if (!found_lib) { |
| throw MISSING_DRIVER_LIB; |
| } else { |
| throw(-1); |
| } |
| } |
| } |
| |
| // Print out all the runtime files found in a given location. This way we |
| // capture the full state of the system. |
| bool PrintRuntimesInFolder(std::string &folder_loc, std::string &object_name, bool print_header = true) { |
| DIR *runtime_dir; |
| bool success = false; |
| bool failed = false; |
| |
| runtime_dir = opendir(folder_loc.c_str()); |
| if (NULL != runtime_dir) { |
| bool file_found = false; |
| FILE *pfp; |
| uint32_t i = 0; |
| dirent *cur_ent; |
| std::string command_str; |
| std::stringstream generic_str; |
| char path[1035]; |
| |
| if (print_header) { |
| PrintBeginTableRow(); |
| PrintTableElement(folder_loc, ALIGN_RIGHT); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } |
| |
| while ((cur_ent = readdir(runtime_dir)) != NULL) { |
| if (NULL != strstr(cur_ent->d_name, object_name.c_str()) && |
| strlen(cur_ent->d_name) == 14) { |
| |
| // Get the source of this symbolic link |
| command_str = "stat -c%N "; |
| command_str += folder_loc; |
| command_str += "/"; |
| command_str += cur_ent->d_name; |
| pfp = popen(command_str.c_str(), "r"); |
| |
| generic_str << "[" << i++ << "]"; |
| |
| PrintBeginTableRow(); |
| PrintTableElement(generic_str.str(), ALIGN_RIGHT); |
| |
| file_found = true; |
| |
| if (pfp == NULL) { |
| PrintTableElement(cur_ent->d_name); |
| PrintTableElement("Failed to retrieve symbolic link"); |
| failed = true; |
| } else { |
| if (NULL != fgets(path, sizeof(path) - 1, pfp)) { |
| std::string cmd = path; |
| size_t arrow_loc = cmd.find("->"); |
| if (arrow_loc == std::string::npos) { |
| std::string trimmed_path = |
| TrimWhitespace(path, " \t\n\r\'\""); |
| |
| PrintTableElement(trimmed_path); |
| PrintTableElement(""); |
| } else { |
| std::string before_arrow = cmd.substr(0, arrow_loc); |
| std::string trim_before = |
| TrimWhitespace(before_arrow, " \t\n\r\'\""); |
| std::string after_arrow = |
| cmd.substr(arrow_loc + 2, std::string::npos); |
| std::string trim_after = |
| TrimWhitespace(after_arrow, " \t\n\r\'\""); |
| PrintTableElement(trim_before); |
| PrintTableElement(trim_after); |
| } |
| } else { |
| PrintTableElement(cur_ent->d_name); |
| PrintTableElement("Failed to retrieve symbolic link"); |
| } |
| |
| PrintEndTableRow(); |
| |
| pclose(pfp); |
| } |
| } |
| } |
| if (!file_found) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("No libvulkan.so files found"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } |
| closedir(runtime_dir); |
| |
| success = !failed; |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement(folder_loc, ALIGN_RIGHT); |
| PrintTableElement("No such folder"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } |
| |
| return success; |
| } |
| |
| // Utility function to determine if a runtime exists in the folder |
| bool CheckRuntime(std::string &folder_loc, std::string &object_name) { |
| return PrintRuntimesInFolder(folder_loc, object_name); |
| } |
| |
| // Print out whatever Vulkan runtime information we can gather from the |
| // standard system paths, etc. |
| void PrintRunTimeInfo(void) { |
| const char vulkan_so_prefix[] = "libvulkan.so."; |
| char path[1035]; |
| char generic_string[MAX_STRING_LENGTH]; |
| char buff[PATH_MAX]; |
| std::string runtime_dir_name; |
| FILE *pfp; |
| bool failed = false; |
| PrintBeginTable("Vulkan Runtimes", 3); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("Possible Runtime Folders"); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| if (!FindLinuxSystemObject(vulkan_so_prefix, CheckRuntime, false)) { |
| failed = true; |
| } |
| |
| ssize_t len = ::readlink("/proc/self/exe", buff, sizeof(buff) - 1); |
| if (len != -1) { |
| buff[len] = '\0'; |
| |
| std::string runtime_dir_id = "Runtime Folder Used By via"; |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "ldd %s", buff); |
| pfp = popen(generic_string, "r"); |
| if (pfp == NULL) { |
| PrintBeginTableRow(); |
| PrintTableElement(runtime_dir_id); |
| PrintTableElement("Failed to query via library info"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| bool found = false; |
| while (fgets(path, sizeof(path) - 1, pfp) != NULL) { |
| if (NULL != strstr(path, vulkan_so_prefix)) { |
| std::string cmd = path; |
| size_t arrow_loc = cmd.find("=>"); |
| if (arrow_loc == std::string::npos) { |
| std::string trimmed_path = |
| TrimWhitespace(path, " \t\n\r\'\""); |
| PrintBeginTableRow(); |
| PrintTableElement(runtime_dir_id); |
| PrintTableElement(trimmed_path); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } else { |
| std::string after_arrow = cmd.substr(arrow_loc + 2); |
| std::string before_slash = |
| after_arrow.substr(0, after_arrow.rfind("/")); |
| std::string trimmed = |
| TrimWhitespace(before_slash, " \t\n\r\'\""); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(runtime_dir_id); |
| PrintTableElement(trimmed); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| std::string find_so = vulkan_so_prefix; |
| if (!PrintRuntimesInFolder(trimmed, find_so, false)) { |
| failed = true; |
| } else { |
| // We found one runtime, clear any failures |
| if (failed) { |
| failed = false; |
| } |
| } |
| } |
| found = !failed; |
| break; |
| } |
| } |
| if (!found) { |
| PrintBeginTableRow(); |
| PrintTableElement(runtime_dir_id); |
| PrintTableElement( |
| "Failed to find Vulkan SO used for via"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } |
| pclose(pfp); |
| } |
| PrintEndTableRow(); |
| } |
| |
| PrintEndTable(); |
| |
| if (failed) { |
| throw(-1); |
| } |
| } |
| |
| // Print out the explicit layers that are stored in any of the standard |
| // locations. |
| bool PrintExplicitLayersInFolder(std::string &id, std::string &folder_loc) { |
| DIR *layer_dir; |
| bool success = false; |
| |
| layer_dir = opendir(folder_loc.c_str()); |
| if (NULL != layer_dir) { |
| dirent *cur_ent; |
| std::string cur_layer; |
| char generic_string[MAX_STRING_LENGTH]; |
| uint32_t i = 0; |
| bool failed = false; |
| bool found_json = false; |
| |
| PrintBeginTableRow(); |
| PrintTableElement(id); |
| PrintTableElement(folder_loc); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| // Loop through each JSON in a given folder |
| while ((cur_ent = readdir(layer_dir)) != NULL) { |
| if (NULL != strstr(cur_ent->d_name, ".json")) { |
| found_json = true; |
| |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", i++); |
| cur_layer = folder_loc; |
| cur_layer += "/"; |
| cur_layer += cur_ent->d_name; |
| |
| // Parse the JSON file |
| std::ifstream *stream = NULL; |
| stream = new std::ifstream(cur_layer, std::ifstream::in); |
| if (nullptr == stream || stream->fail()) { |
| PrintBeginTableRow(); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(cur_ent->d_name); |
| PrintTableElement("ERROR reading JSON file!"); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| Json::Value root = Json::nullValue; |
| Json::Reader reader; |
| if (!reader.parse(*stream, root, false) || root.isNull()) { |
| // Report to the user the failure and their |
| // locations in the document. |
| PrintBeginTableRow(); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(cur_ent->d_name); |
| PrintTableElement(reader.getFormattedErrorMessages()); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(cur_ent->d_name); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| // Dump out the standard explicit layer information. |
| PrintExplicitLayerJsonInfo(cur_layer.c_str(), root, 3); |
| } |
| |
| stream->close(); |
| delete stream; |
| stream = NULL; |
| } |
| } |
| } |
| if (!found_json) { |
| PrintBeginTableRow(); |
| PrintTableElement(id); |
| PrintTableElement(folder_loc); |
| PrintTableElement("No JSON files found"); |
| PrintEndTableRow(); |
| } |
| closedir(layer_dir); |
| |
| success = !failed; |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement(id); |
| PrintTableElement(folder_loc); |
| PrintTableElement("No such folder"); |
| PrintEndTableRow(); |
| |
| // This isn't a failure, just an attempt to read information |
| success = true; |
| } |
| |
| return success; |
| } |
| |
| // Print out information on whatever LunarG Vulkan SDKs we can find on |
| // the system using the standard locations and environmental variables. |
| // This includes listing what layers are available from the SDK. |
| void PrintSDKInfo(void) { |
| bool failed = false; |
| bool sdk_exists = false; |
| std::string sdk_path; |
| std::string sdk_env_name; |
| const char vulkan_so_prefix[] = "libvulkan.so."; |
| DIR *sdk_dir; |
| dirent *cur_ent; |
| char *env_value; |
| |
| PrintBeginTable("LunarG Vulkan SDKs", 3); |
| |
| for (uint32_t dir = 0; dir < 2; dir++) { |
| switch (dir) { |
| case 0: |
| sdk_env_name = "VK_SDK_PATH"; |
| env_value = getenv(sdk_env_name.c_str()); |
| if (env_value == NULL) { |
| continue; |
| } |
| sdk_path = env_value; |
| break; |
| case 1: |
| sdk_env_name = "VULKAN_SDK"; |
| env_value = getenv(sdk_env_name.c_str()); |
| if (env_value == NULL) { |
| continue; |
| } |
| sdk_path = env_value; |
| break; |
| default: |
| failed = true; |
| continue; |
| } |
| |
| std::string explicit_layer_path = sdk_path; |
| explicit_layer_path += "/etc/explicit_layer.d"; |
| |
| sdk_dir = opendir(explicit_layer_path.c_str()); |
| if (NULL != sdk_dir) { |
| while ((cur_ent = readdir(sdk_dir)) != NULL) { |
| if (NULL != strstr(cur_ent->d_name, vulkan_so_prefix) && |
| strlen(cur_ent->d_name) == 14) { |
| } |
| } |
| closedir(sdk_dir); |
| |
| if (!PrintExplicitLayersInFolder(sdk_env_name, |
| explicit_layer_path)) { |
| failed = true; |
| } |
| |
| global_items.sdk_found = true; |
| global_items.sdk_path = sdk_path; |
| sdk_exists = true; |
| } |
| } |
| |
| if (!sdk_exists) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("No installed SDKs found"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } |
| |
| PrintEndTable(); |
| |
| if (failed) { |
| throw(-1); |
| } |
| } |
| |
| // Print out whatever layers we can find out from other environmental |
| // variables that may be used to point the Vulkan loader at a layer path. |
| void PrintLayerInfo(void) { |
| uint32_t i = 0; |
| char generic_string[MAX_STRING_LENGTH]; |
| bool failed = false; |
| char cur_vulkan_layer_json[MAX_STRING_LENGTH]; |
| DIR *layer_dir; |
| dirent *cur_ent; |
| const char implicit_layer_dir[] = "/etc/vulkan/implicit_layer.d"; |
| const char explicit_layer_dir[] = "/etc/vulkan/explicit_layer.d"; |
| std::string layer_path; |
| |
| // Dump out implicit layer information first |
| PrintBeginTable("Implicit Layers", 3); |
| PrintBeginTableRow(); |
| PrintTableElement("Location"); |
| PrintTableElement(implicit_layer_dir); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| layer_dir = opendir(implicit_layer_dir); |
| if (NULL != layer_dir) { |
| while ((cur_ent = readdir(layer_dir)) != NULL) { |
| if (NULL != strstr(cur_ent->d_name, ".json")) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", i++); |
| snprintf(cur_vulkan_layer_json, MAX_STRING_LENGTH - 1, "%s/%s", |
| implicit_layer_dir, cur_ent->d_name); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(cur_ent->d_name); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| std::ifstream *stream = NULL; |
| stream = |
| new std::ifstream(cur_vulkan_layer_json, std::ifstream::in); |
| if (nullptr == stream || stream->fail()) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("ERROR reading JSON file!"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| Json::Value root = Json::nullValue; |
| Json::Reader reader; |
| if (!reader.parse(*stream, root, false) || root.isNull()) { |
| // Report to the user the failure and their |
| // locations in the document. |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("ERROR parsing JSON file!"); |
| PrintTableElement(reader.getFormattedErrorMessages()); |
| PrintEndTableRow(); |
| failed = true; |
| } else { |
| PrintExplicitLayerJsonInfo(cur_vulkan_layer_json, root, |
| 3); |
| } |
| |
| stream->close(); |
| delete stream; |
| stream = NULL; |
| } |
| } |
| } |
| closedir(layer_dir); |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Directory does not exist"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } |
| PrintEndTable(); |
| |
| // Dump out any explicit layer information. |
| PrintBeginTable("Explicit Layers", 3); |
| |
| std::string explicit_layer_id = "Global path"; |
| std::string explicit_layer_path = explicit_layer_dir; |
| |
| if (!PrintExplicitLayersInFolder(explicit_layer_id, explicit_layer_path)) { |
| failed = true; |
| } |
| |
| explicit_layer_id = "VK_LAYER_PATH"; |
| char *env_value = getenv("VK_LAYER_PATH"); |
| if (NULL != env_value) { |
| explicit_layer_path = env_value; |
| if (!PrintExplicitLayersInFolder(explicit_layer_id, |
| explicit_layer_path)) { |
| failed = true; |
| } |
| } |
| |
| PrintEndTable(); |
| |
| if (failed) { |
| throw(-1); |
| } |
| } |
| |
| // Run the test in the specified directory with the corresponding |
| // command-line arguments. |
| // Returns 0 on no error, 1 if test file wasn't found, and -1 |
| // on any other errors. |
| int RunTestInDirectory(std::string path, std::string test, |
| std::string cmd_line) { |
| char orig_dir[MAX_STRING_LENGTH]; |
| int err_code = -1; |
| orig_dir[0] = '\0'; |
| if (NULL != getcwd(orig_dir, MAX_STRING_LENGTH - 1)) { |
| int err = chdir(path.c_str()); |
| if (-1 != err) { |
| if (-1 != access(test.c_str(), X_OK)) { |
| printf("cmd_line - %s\n", cmd_line.c_str()); |
| err_code = system(cmd_line.c_str()); |
| } else { |
| // Can't run because it's either not there or an actual |
| // exe. So, just return a separate error code. |
| err_code = 1; |
| } |
| } |
| chdir(orig_dir); |
| } |
| return err_code; |
| } |
| |
| #endif |
| |
| // Following functions should be OS agnostic: |
| //========================================== |
| |
| // Trim any whitespace preceeding or following the actual |
| // content inside of a string. The actual items labeled |
| // as whitespace are passed in as the second set of |
| // parameters. |
| std::string TrimWhitespace(const std::string &str, |
| const std::string &whitespace) { |
| const auto strBegin = str.find_first_not_of(whitespace); |
| if (strBegin == std::string::npos) { |
| return ""; // no content |
| } |
| |
| const auto strEnd = str.find_last_not_of(whitespace); |
| const auto strRange = strEnd - strBegin + 1; |
| |
| return str.substr(strBegin, strRange); |
| } |
| |
| // Print any information found on the current vk_layer_settings.txt |
| // file being used. It looks in the current folder first, and then will |
| // look in any defined by the registry variable VK_LAYER_SETTINGS_PATH. |
| void PrintLayerSettingsFileInfo(void) { |
| bool failed = false; |
| char *settings_path = NULL; |
| std::string settings_file; |
| std::map<std::string, std::vector<SettingPair>> settings; |
| |
| PrintBeginTable("Layer Settings File", 4); |
| |
| // If the settings path environment variable is set, use that. |
| #ifdef _WIN32 |
| char generic_string[MAX_STRING_LENGTH]; |
| if (0 != GetEnvironmentVariableA("VK_LAYER_SETTINGS_PATH", generic_string, |
| MAX_STRING_LENGTH - 1)) { |
| settings_path = generic_string; |
| settings_file = settings_path; |
| settings_file += '\\'; |
| } |
| #else |
| settings_path = getenv("VK_LAYER_SETTINGS_PATH"); |
| if (NULL != settings_path) { |
| settings_file = settings_path; |
| settings_file += '/'; |
| } |
| #endif |
| settings_file += "vk_layer_settings.txt"; |
| |
| PrintBeginTableRow(); |
| PrintTableElement("VK_LAYER_SETTINGS_PATH"); |
| if (NULL != settings_path) { |
| PrintTableElement(settings_path); |
| } else { |
| PrintTableElement("Not Defined"); |
| } |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| // Load the file from the appropriate location |
| PrintBeginTableRow(); |
| PrintTableElement("Settings File"); |
| PrintTableElement("vk_layer_settings.txt"); |
| std::ifstream *settings_stream = |
| new std::ifstream(settings_file, std::ifstream::in); |
| if (nullptr == settings_stream || settings_stream->fail()) { |
| // No file was found. This is NOT an error. |
| PrintTableElement("Not Found"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } else { |
| // We found a file, so parse it. |
| PrintTableElement("Found"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| // The settings file is a text file where: |
| // - # indicates a comment |
| // - Settings are stored in the fasion: |
| // <layer_name>.<setting> = <value> |
| while (settings_stream->good()) { |
| std::string cur_line; |
| getline(*settings_stream, cur_line); |
| std::string trimmed_line = TrimWhitespace(cur_line); |
| |
| // Skip blank and comment lines |
| if (trimmed_line.length() == 0 || trimmed_line.c_str()[0] == '#') { |
| continue; |
| } |
| |
| // If no equal, treat as unknown |
| size_t equal_loc = trimmed_line.find("="); |
| if (equal_loc == std::string::npos) { |
| continue; |
| } |
| |
| SettingPair new_pair; |
| |
| std::string before_equal = trimmed_line.substr(0, equal_loc); |
| std::string after_equal = |
| trimmed_line.substr(equal_loc + 1, std::string::npos); |
| new_pair.value = TrimWhitespace(after_equal); |
| |
| std::string trimmed_setting = TrimWhitespace(before_equal); |
| |
| // Look for period |
| std::string setting_layer = "--None--"; |
| std::string setting_name = ""; |
| size_t period_loc = trimmed_setting.find("."); |
| if (period_loc == std::string::npos) { |
| setting_name = trimmed_setting; |
| } else { |
| setting_layer = trimmed_setting.substr(0, period_loc); |
| setting_name = |
| trimmed_setting.substr(period_loc + 1, std::string::npos); |
| } |
| new_pair.name = setting_name; |
| |
| // Add items to settings map for now |
| if (settings.find(setting_layer) == settings.end()) { |
| // Not found |
| std::vector<SettingPair> new_vector; |
| new_vector.push_back(new_pair); |
| settings[setting_layer] = new_vector; |
| } else { |
| // Already exists |
| std::vector<SettingPair> &cur_vector = settings[setting_layer]; |
| cur_vector.push_back(new_pair); |
| } |
| } |
| |
| // Now that all items have been grouped in the settings map |
| // appropriately, print |
| // them out |
| for (auto layer_iter = settings.begin(); layer_iter != settings.end(); |
| layer_iter++) { |
| std::vector<SettingPair> &cur_vector = layer_iter->second; |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(layer_iter->first, ALIGN_RIGHT); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| for (uint32_t cur_item = 0; cur_item < cur_vector.size(); |
| cur_item++) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement(cur_vector[cur_item].name); |
| PrintTableElement(cur_vector[cur_item].value); |
| PrintEndTableRow(); |
| } |
| } |
| |
| settings_stream->close(); |
| delete settings_stream; |
| } |
| PrintEndTable(); |
| |
| if (failed) { |
| throw(-1); |
| } |
| } |
| |
| // Print out the information stored in an explicit layer's JSON file. |
| void PrintExplicitLayerJsonInfo(const char *layer_json_filename, |
| Json::Value root, uint32_t num_cols) { |
| char generic_string[MAX_STRING_LENGTH]; |
| uint32_t cur_col; |
| uint32_t ext; |
| if (!root["layer"].isNull()) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Name"); |
| if (!root["layer"]["name"].isNull()) { |
| PrintTableElement(root["layer"]["name"].asString()); |
| } else { |
| PrintTableElement("MISSING!"); |
| } |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Description"); |
| if (!root["layer"]["description"].isNull()) { |
| PrintTableElement(root["layer"]["description"].asString()); |
| } else { |
| PrintTableElement("MISSING!"); |
| } |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("API Version"); |
| if (!root["layer"]["api_version"].isNull()) { |
| PrintTableElement(root["layer"]["api_version"].asString()); |
| } else { |
| PrintTableElement("MISSING!"); |
| } |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("JSON File Version"); |
| if (!root["file_format_version"].isNull()) { |
| PrintTableElement(root["file_format_version"].asString()); |
| } else { |
| PrintTableElement("MISSING!"); |
| } |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Library Path"); |
| if (!root["layer"]["library_path"].isNull()) { |
| PrintTableElement(root["layer"]["library_path"].asString()); |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| |
| #ifdef _WIN32 |
| // On Windows, we can query the file version, so do so. |
| char full_layer_path[MAX_STRING_LENGTH]; |
| if (GenerateLibraryPath( |
| layer_json_filename, |
| root["layer"]["library_path"].asString().c_str(), |
| MAX_STRING_LENGTH, full_layer_path) && |
| GetFileVersion(full_layer_path, MAX_STRING_LENGTH, |
| generic_string)) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Layer File Version"); |
| PrintTableElement(generic_string); |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| } |
| #endif |
| } else { |
| PrintTableElement("MISSING!"); |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| } |
| |
| char count_str[MAX_STRING_LENGTH]; |
| Json::Value dev_exts = root["layer"]["device_extensions"]; |
| ext = 0; |
| if (!dev_exts.isNull() && dev_exts.isArray()) { |
| snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", dev_exts.size()); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Device Extensions"); |
| PrintTableElement(count_str); |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| |
| for (Json::ValueIterator dev_ext_it = dev_exts.begin(); |
| dev_ext_it != dev_exts.end(); dev_ext_it++) { |
| Json::Value dev_ext = (*dev_ext_it); |
| Json::Value dev_ext_name = dev_ext["name"]; |
| if (!dev_ext_name.isNull()) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", |
| ext); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(dev_ext_name.asString()); |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| } |
| } |
| } |
| Json::Value inst_exts = root["layer"]["instance_extensions"]; |
| ext = 0; |
| if (!inst_exts.isNull() && inst_exts.isArray()) { |
| snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", inst_exts.size()); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Instance Extensions"); |
| PrintTableElement(count_str); |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| |
| for (Json::ValueIterator inst_ext_it = inst_exts.begin(); |
| inst_ext_it != inst_exts.end(); inst_ext_it++) { |
| Json::Value inst_ext = (*inst_ext_it); |
| Json::Value inst_ext_name = inst_ext["name"]; |
| if (!inst_ext_name.isNull()) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", |
| ext); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(inst_ext_name.asString()); |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| } |
| } |
| } |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Layer Section"); |
| PrintTableElement("MISSING!"); |
| cur_col = 3; |
| while (num_cols > cur_col) { |
| PrintTableElement(""); |
| cur_col++; |
| } |
| PrintEndTableRow(); |
| } |
| } |
| |
| // Print out the information about an Implicit layer stored in |
| // it's JSON file. For the most part, it is similar to an |
| // explicit layer, so we re-use that code. However, implicit |
| // layers have a DISABLE environment variable that can be used |
| // to disable the layer by default. Additionally, some implicit |
| // layers have an ENABLE environment variable so that they are |
| // disabled by default, but can be enabled. |
| void PrintImplicitLayerJsonInfo(const char *layer_json_filename, |
| Json::Value root) { |
| bool enabled = true; |
| std::string enable_env_variable = "--NONE--"; |
| bool enable_var_set = false; |
| char enable_env_value[16]; |
| std::string disable_env_variable = "--NONE--"; |
| bool disable_var_set = false; |
| char disable_env_value[16]; |
| |
| PrintExplicitLayerJsonInfo(layer_json_filename, root, 4); |
| |
| Json::Value enable = root["layer"]["enable_environment"]; |
| if (!enable.isNull()) { |
| for (Json::Value::iterator en_iter = enable.begin(); |
| en_iter != enable.end(); en_iter++) { |
| if (en_iter.key().isNull()) { |
| continue; |
| } |
| enable_env_variable = en_iter.key().asString(); |
| // If an enable define exists, set it to disabled by default. |
| enabled = false; |
| #ifdef _WIN32 |
| if (0 != GetEnvironmentVariableA(enable_env_variable.c_str(), |
| enable_env_value, 15)) { |
| #else |
| char *enable_env = getenv(enable_env_variable.c_str()); |
| if (NULL != enable_env) { |
| strncpy(enable_env_value, enable_env, 15); |
| enable_env_value[15] = '\0'; |
| #endif |
| if (atoi(enable_env_value) != 0) { |
| enable_var_set = true; |
| enabled = true; |
| } |
| } |
| break; |
| } |
| } |
| Json::Value disable = root["layer"]["disable_environment"]; |
| if (!disable.isNull()) { |
| for (Json::Value::iterator dis_iter = disable.begin(); |
| dis_iter != disable.end(); dis_iter++) { |
| if (dis_iter.key().isNull()) { |
| continue; |
| } |
| disable_env_variable = dis_iter.key().asString(); |
| #ifdef _WIN32 |
| if (0 != GetEnvironmentVariableA(disable_env_variable.c_str(), |
| disable_env_value, 15)) { |
| #else |
| char *disable_env = getenv(disable_env_variable.c_str()); |
| if (NULL != disable_env) { |
| strncpy(disable_env_value, disable_env, 15); |
| disable_env_value[15] = '\0'; |
| #endif |
| if (atoi(disable_env_value) > 0) { |
| disable_var_set = true; |
| enabled = false; |
| } |
| } |
| break; |
| } |
| } |
| |
| // Print the overall state (ENABLED or DISABLED) so we can |
| // quickly determine if this layer is being used. |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Enabled State"); |
| PrintTableElement(enabled ? "ENABLED" : "DISABLED"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Enable Env Var", ALIGN_RIGHT); |
| PrintTableElement(enable_env_variable); |
| if (enable_var_set) { |
| PrintTableElement(""); |
| } else { |
| PrintTableElement("Not Defined"); |
| } |
| PrintEndTableRow(); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Disable Env Var", ALIGN_RIGHT); |
| PrintTableElement(disable_env_variable); |
| if (disable_var_set) { |
| PrintTableElement(disable_env_value); |
| } else { |
| PrintTableElement("Not Defined"); |
| } |
| PrintEndTableRow(); |
| } |
| |
| // Perform Vulkan commands to find out what extensions are available |
| // to a Vulkan Instance, and attempt to create one. |
| void PrintInstanceInfo(void) { |
| VkApplicationInfo app_info; |
| VkInstanceCreateInfo inst_info; |
| uint32_t ext_count; |
| std::vector<VkExtensionProperties> ext_props; |
| VkResult status; |
| char generic_string[MAX_STRING_LENGTH]; |
| |
| memset(&app_info, 0, sizeof(VkApplicationInfo)); |
| app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; |
| app_info.pNext = NULL; |
| app_info.pApplicationName = "via"; |
| app_info.applicationVersion = 1; |
| app_info.pEngineName = "via"; |
| app_info.engineVersion = 1; |
| app_info.apiVersion = VK_API_VERSION_1_0; |
| |
| memset(&inst_info, 0, sizeof(VkInstanceCreateInfo)); |
| inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
| inst_info.pNext = NULL; |
| inst_info.pApplicationInfo = &app_info; |
| inst_info.enabledLayerCount = 0; |
| inst_info.ppEnabledLayerNames = NULL; |
| inst_info.enabledExtensionCount = 0; |
| inst_info.ppEnabledExtensionNames = NULL; |
| |
| PrintBeginTable("Instance", 3); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("vkEnumerateInstanceExtensionProperties"); |
| status = vkEnumerateInstanceExtensionProperties(NULL, &ext_count, NULL); |
| if (status) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "ERROR: Failed to determine num inst extensions - %d", status); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } else { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d extensions found", |
| ext_count); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| ext_props.resize(ext_count); |
| status = vkEnumerateInstanceExtensionProperties(NULL, &ext_count, |
| ext_props.data()); |
| if (status) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "ERROR: Failed to enumerate inst extensions - %d", status); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } else { |
| for (uint32_t iii = 0; iii < ext_count; iii++) { |
| PrintBeginTableRow(); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", iii); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(ext_props[iii].extensionName); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "Spec Vers %d", |
| ext_props[iii].specVersion); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| } |
| } |
| |
| PrintBeginTableRow(); |
| PrintTableElement("vkCreateInstance"); |
| status = vkCreateInstance(&inst_info, NULL, &global_items.instance); |
| if (status == VK_ERROR_INCOMPATIBLE_DRIVER) { |
| PrintTableElement("ERROR: Incompatible Driver"); |
| } else if (status == VK_ERROR_OUT_OF_HOST_MEMORY) { |
| PrintTableElement("ERROR: Out of memory"); |
| } else if (status) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "ERROR: Failed to create - %d", status); |
| PrintTableElement(generic_string); |
| } else { |
| PrintTableElement("SUCCESSFUL"); |
| } |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| PrintEndTable(); |
| if (VK_SUCCESS != status) { |
| throw(-1); |
| } |
| } |
| |
| // Print out any information we can find out about physical devices using |
| // the Vulkan commands. There should be one for each Vulkan capable device |
| // on the system. |
| void PrintPhysDevInfo(void) { |
| VkPhysicalDeviceProperties props; |
| std::vector<VkPhysicalDevice> phys_devices; |
| VkResult status; |
| char generic_string[MAX_STRING_LENGTH]; |
| uint32_t gpu_count = 0; |
| uint32_t iii; |
| uint32_t jjj; |
| bool failed = false; |
| |
| PrintBeginTable("Physical Devices", 4); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("vkEnumeratePhysicalDevices"); |
| status = |
| vkEnumeratePhysicalDevices(global_items.instance, &gpu_count, NULL); |
| if (status) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "ERROR: Failed to query - %d", status); |
| PrintTableElement(generic_string); |
| failed = true; |
| } else { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", gpu_count); |
| PrintTableElement(generic_string); |
| } |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| // If we failed here, the rest will have issues, so, unlike everywhere |
| // else, we'll just return immediately. |
| if (failed) { |
| throw VULKAN_CANT_FIND_DRIVER; |
| } |
| |
| phys_devices.resize(gpu_count); |
| global_items.phys_devices.resize(gpu_count); |
| status = vkEnumeratePhysicalDevices(global_items.instance, &gpu_count, |
| phys_devices.data()); |
| if (VK_SUCCESS != status) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Failed to enumerate physical devices!"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| failed = true; |
| } |
| for (iii = 0; iii < gpu_count; iii++) { |
| global_items.phys_devices[iii].vulkan_phys_dev = phys_devices[iii]; |
| |
| PrintBeginTableRow(); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", iii); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| if (status) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "ERROR: Failed to query - %d", status); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } else { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%p", |
| phys_devices[iii]); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| vkGetPhysicalDeviceProperties(phys_devices[iii], &props); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Vendor"); |
| switch (props.vendorID) { |
| case 0x8086: |
| case 0x8087: |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "Intel [0x%04x]", props.vendorID); |
| break; |
| case 0x1002: |
| case 0x1022: |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "AMD [0x%04x]", |
| props.vendorID); |
| break; |
| case 0x10DE: |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "Nvidia [0x%04x]", props.vendorID); |
| break; |
| case 0x1EB5: |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "ARM [0x%04x]", |
| props.vendorID); |
| break; |
| case 0x5143: |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "Qualcomm [0x%04x]", props.vendorID); |
| break; |
| case 0x1099: |
| case 0x10C3: |
| case 0x1249: |
| case 0x4E8: |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "Samsung [0x%04x]", props.vendorID); |
| break; |
| default: |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%04x", |
| props.vendorID); |
| break; |
| } |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Device Name"); |
| PrintTableElement(props.deviceName); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Device ID"); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%x", |
| props.deviceID); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Device Type"); |
| switch (props.deviceType) { |
| case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: |
| PrintTableElement("Integrated GPU"); |
| break; |
| case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: |
| PrintTableElement("Discrete GPU"); |
| break; |
| case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: |
| PrintTableElement("Virtual GPU"); |
| break; |
| case VK_PHYSICAL_DEVICE_TYPE_CPU: |
| PrintTableElement("CPU"); |
| break; |
| case VK_PHYSICAL_DEVICE_TYPE_OTHER: |
| PrintTableElement("Other"); |
| break; |
| default: |
| PrintTableElement("INVALID!"); |
| break; |
| } |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Driver Version"); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d.%d.%d", |
| VK_VERSION_MAJOR(props.driverVersion), |
| VK_VERSION_MINOR(props.driverVersion), |
| VK_VERSION_PATCH(props.driverVersion)); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("API Version"); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d.%d.%d", |
| VK_VERSION_MAJOR(props.apiVersion), |
| VK_VERSION_MINOR(props.apiVersion), |
| VK_VERSION_PATCH(props.apiVersion)); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| uint32_t queue_fam_count; |
| vkGetPhysicalDeviceQueueFamilyProperties(phys_devices[iii], |
| &queue_fam_count, NULL); |
| if (queue_fam_count > 0) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Queue Families"); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", |
| queue_fam_count); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| global_items.phys_devices[iii].queue_fam_props.resize( |
| queue_fam_count); |
| vkGetPhysicalDeviceQueueFamilyProperties( |
| phys_devices[iii], &queue_fam_count, |
| global_items.phys_devices[iii].queue_fam_props.data()); |
| for (jjj = 0; jjj < queue_fam_count; jjj++) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", |
| jjj); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement("Queue Count"); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", |
| global_items.phys_devices[iii] |
| .queue_fam_props[jjj] |
| .queueCount); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement("Queue Flags"); |
| generic_string[0] = '\0'; |
| bool prev_set = false; |
| if (global_items.phys_devices[iii] |
| .queue_fam_props[jjj] |
| .queueFlags & |
| VK_QUEUE_GRAPHICS_BIT) { |
| strncat(generic_string, "GRAPHICS", |
| MAX_STRING_LENGTH - 1); |
| prev_set = true; |
| } |
| if (global_items.phys_devices[iii] |
| .queue_fam_props[jjj] |
| .queueFlags & |
| VK_QUEUE_COMPUTE_BIT) { |
| if (prev_set) { |
| strncat(generic_string, " | ", |
| MAX_STRING_LENGTH - 1); |
| } |
| strncat(generic_string, "COMPUTE", |
| MAX_STRING_LENGTH - 1); |
| prev_set = true; |
| } |
| if (global_items.phys_devices[iii] |
| .queue_fam_props[jjj] |
| .queueFlags & |
| VK_QUEUE_TRANSFER_BIT) { |
| if (prev_set) { |
| strncat(generic_string, " | ", |
| MAX_STRING_LENGTH - 1); |
| } |
| strncat(generic_string, "TRANSFER", |
| MAX_STRING_LENGTH - 1); |
| prev_set = true; |
| } |
| if (global_items.phys_devices[iii] |
| .queue_fam_props[jjj] |
| .queueFlags & |
| VK_QUEUE_SPARSE_BINDING_BIT) { |
| if (prev_set) { |
| strncat(generic_string, " | ", |
| MAX_STRING_LENGTH - 1); |
| } |
| strncat(generic_string, "SPARSE_BINDING", |
| MAX_STRING_LENGTH - 1); |
| prev_set = true; |
| } |
| if (!prev_set) { |
| strncat(generic_string, "--NONE--", |
| MAX_STRING_LENGTH - 1); |
| } |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement("Timestamp Valid Bits"); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%x", |
| global_items.phys_devices[iii] |
| .queue_fam_props[jjj] |
| .timestampValidBits); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement("Image Granularity"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement("Width", ALIGN_RIGHT); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%x", |
| global_items.phys_devices[iii] |
| .queue_fam_props[jjj] |
| .minImageTransferGranularity.width); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement("Height", ALIGN_RIGHT); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%x", |
| global_items.phys_devices[iii] |
| .queue_fam_props[jjj] |
| .minImageTransferGranularity.height); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement("Depth", ALIGN_RIGHT); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%x", |
| global_items.phys_devices[iii] |
| .queue_fam_props[jjj] |
| .minImageTransferGranularity.depth); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| } else { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("vkGetPhysicalDeviceQueueFamilyProperties"); |
| PrintTableElement("FAILED: Returned 0!"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| } |
| |
| VkPhysicalDeviceMemoryProperties memory_props; |
| vkGetPhysicalDeviceMemoryProperties(phys_devices[iii], |
| &memory_props); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Memory Heaps"); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", |
| memory_props.memoryHeapCount); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| for (jjj = 0; jjj < memory_props.memoryHeapCount; jjj++) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", jjj); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement("Property Flags"); |
| generic_string[0] = '\0'; |
| bool prev_set = false; |
| if (memory_props.memoryHeaps[jjj].flags & |
| VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { |
| strncat(generic_string, "DEVICE_LOCAL", |
| MAX_STRING_LENGTH - 1); |
| prev_set = true; |
| } |
| if (!prev_set) { |
| strncat(generic_string, "--NONE--", MAX_STRING_LENGTH - 1); |
| } |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement("Heap Size"); |
| snprintf( |
| generic_string, MAX_STRING_LENGTH - 1, "%" PRIu64 "", |
| static_cast<uint64_t>(memory_props.memoryHeaps[jjj].size)); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Memory Types"); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", |
| memory_props.memoryTypeCount); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| for (jjj = 0; jjj < memory_props.memoryTypeCount; jjj++) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", jjj); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement("Property Flags"); |
| generic_string[0] = '\0'; |
| bool prev_set = false; |
| if (memory_props.memoryTypes[jjj].propertyFlags & |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { |
| strncat(generic_string, "DEVICE_LOCAL", |
| MAX_STRING_LENGTH - 1); |
| prev_set = true; |
| } |
| if (memory_props.memoryTypes[jjj].propertyFlags & |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { |
| if (prev_set) { |
| strncat(generic_string, " | ", MAX_STRING_LENGTH - 1); |
| } |
| strncat(generic_string, "HOST_VISIBLE", |
| MAX_STRING_LENGTH - 1); |
| prev_set = true; |
| } |
| if (memory_props.memoryTypes[jjj].propertyFlags & |
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) { |
| if (prev_set) { |
| strncat(generic_string, " | ", MAX_STRING_LENGTH - 1); |
| } |
| strncat(generic_string, "HOST_COHERENT", |
| MAX_STRING_LENGTH - 1); |
| prev_set = true; |
| } |
| if (memory_props.memoryTypes[jjj].propertyFlags & |
| VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { |
| if (prev_set) { |
| strncat(generic_string, " | ", MAX_STRING_LENGTH - 1); |
| } |
| strncat(generic_string, "HOST_CACHED", |
| MAX_STRING_LENGTH - 1); |
| prev_set = true; |
| } |
| if (memory_props.memoryTypes[jjj].propertyFlags & |
| VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) { |
| if (prev_set) { |
| strncat(generic_string, " | ", MAX_STRING_LENGTH - 1); |
| } |
| strncat(generic_string, "LAZILY_ALLOC", |
| MAX_STRING_LENGTH - 1); |
| prev_set = true; |
| } |
| if (!prev_set) { |
| strncat(generic_string, "--NONE--", MAX_STRING_LENGTH - 1); |
| } |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement(""); |
| PrintTableElement("Heap Index"); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", |
| memory_props.memoryTypes[jjj].heapIndex); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| |
| uint32_t num_ext_props; |
| std::vector<VkExtensionProperties> ext_props; |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| PrintTableElement("Device Extensions"); |
| status = vkEnumerateDeviceExtensionProperties( |
| phys_devices[iii], NULL, &num_ext_props, NULL); |
| if (VK_SUCCESS != status) { |
| PrintTableElement("FAILED querying number of extensions"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| failed = true; |
| } else { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", |
| num_ext_props); |
| PrintTableElement(generic_string); |
| ext_props.resize(num_ext_props); |
| status = vkEnumerateDeviceExtensionProperties( |
| phys_devices[iii], NULL, &num_ext_props, ext_props.data()); |
| if (VK_SUCCESS != status) { |
| PrintTableElement("FAILED querying actual extension info"); |
| PrintEndTableRow(); |
| |
| failed = true; |
| } else { |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| for (jjj = 0; jjj < num_ext_props; jjj++) { |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", |
| jjj); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement(ext_props[jjj].extensionName); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "Spec Vers %d", ext_props[jjj].specVersion); |
| PrintTableElement(generic_string); |
| PrintEndTableRow(); |
| } |
| } |
| } |
| } |
| } |
| |
| PrintEndTable(); |
| |
| if (failed) { |
| throw VULKAN_CANT_FIND_DRIVER; |
| } |
| } |
| |
| // Using the previously determine information, attempt to create a logical |
| // device for each physical device we found. |
| void PrintLogicalDeviceInfo(void) { |
| VkDeviceCreateInfo device_create_info; |
| VkDeviceQueueCreateInfo queue_create_info; |
| VkResult status = VK_SUCCESS; |
| uint32_t dev_count = |
| static_cast<uint32_t>(global_items.phys_devices.size()); |
| char generic_string[MAX_STRING_LENGTH]; |
| bool failed = false; |
| |
| PrintBeginTable("Logical Devices", 3); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("vkCreateDevice"); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", dev_count); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| global_items.log_devices.resize(dev_count); |
| for (uint32_t dev = 0; dev < dev_count; dev++) { |
| memset(&device_create_info, 0, sizeof(VkDeviceCreateInfo)); |
| device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
| device_create_info.pNext = NULL; |
| device_create_info.queueCreateInfoCount = 0; |
| device_create_info.pQueueCreateInfos = NULL; |
| device_create_info.enabledLayerCount = 0; |
| device_create_info.ppEnabledLayerNames = NULL; |
| device_create_info.enabledExtensionCount = 0; |
| device_create_info.ppEnabledExtensionNames = NULL; |
| device_create_info.queueCreateInfoCount = 1; |
| device_create_info.enabledLayerCount = 0; |
| device_create_info.ppEnabledLayerNames = NULL; |
| device_create_info.enabledExtensionCount = 0; |
| device_create_info.ppEnabledExtensionNames = NULL; |
| |
| memset(&queue_create_info, 0, sizeof(VkDeviceQueueCreateInfo)); |
| float queue_priority = 0; |
| queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
| queue_create_info.pNext = NULL; |
| queue_create_info.queueCount = 1; |
| queue_create_info.pQueuePriorities = &queue_priority; |
| |
| for (uint32_t queue = 0; |
| queue < global_items.phys_devices[dev].queue_fam_props.size(); |
| queue++) { |
| if (0 != (global_items.phys_devices[dev] |
| .queue_fam_props[queue] |
| .queueFlags & |
| VK_QUEUE_GRAPHICS_BIT)) { |
| queue_create_info.queueFamilyIndex = queue; |
| break; |
| } |
| } |
| device_create_info.pQueueCreateInfos = &queue_create_info; |
| |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", dev); |
| PrintTableElement(generic_string); |
| |
| status = vkCreateDevice(global_items.phys_devices[dev].vulkan_phys_dev, |
| &device_create_info, NULL, |
| &global_items.log_devices[dev]); |
| if (VK_ERROR_INCOMPATIBLE_DRIVER == status) { |
| PrintTableElement("FAILED: Incompatible Driver"); |
| failed = true; |
| } else if (VK_ERROR_OUT_OF_HOST_MEMORY == status) { |
| PrintTableElement("FAILED: Out of Host Memory"); |
| failed = true; |
| } else if (VK_SUCCESS != status) { |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, |
| "FAILED : VkResult code = 0x%x", status); |
| PrintTableElement(generic_string); |
| failed = true; |
| } else { |
| PrintTableElement("SUCCESSFUL"); |
| } |
| |
| PrintEndTableRow(); |
| } |
| |
| PrintEndTable(); |
| if (failed) { |
| throw(-1); |
| } |
| } |
| |
| // Clean up all the Vulkan items we previously created and print |
| // out if there are any problems. |
| void PrintCleanupInfo(void) { |
| char generic_string[MAX_STRING_LENGTH]; |
| uint32_t dev_count = |
| static_cast<uint32_t>(global_items.phys_devices.size()); |
| |
| PrintBeginTable("Cleanup", 3); |
| |
| PrintBeginTableRow(); |
| PrintTableElement("vkDestroyDevice"); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", dev_count); |
| PrintTableElement(generic_string); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| for (uint32_t dev = 0; dev < dev_count; dev++) { |
| vkDestroyDevice(global_items.log_devices[dev], NULL); |
| PrintBeginTableRow(); |
| PrintTableElement(""); |
| snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", dev); |
| PrintTableElement(generic_string, ALIGN_RIGHT); |
| PrintTableElement("SUCCESSFUL"); |
| PrintEndTableRow(); |
| } |
| |
| PrintBeginTableRow(); |
| PrintTableElement("vkDestroyInstance"); |
| vkDestroyInstance(global_items.instance, NULL); |
| PrintTableElement("SUCCESSFUL"); |
| PrintTableElement(""); |
| PrintEndTableRow(); |
| |
| PrintEndTable(); |
| } |
| |
| // Run any external tests we can find, and print the results of those |
| // tests. |
| void PrintTestResults(void) { |
| bool failed = false; |
| |
| BeginSection("External Tests"); |
| if (global_items.sdk_found) { |
| std::string cube_exe; |
| std::string full_cmd; |
| std::string path = global_items.sdk_path; |
| |
| #ifdef _WIN32 |
| cube_exe = "cube.exe"; |
| |
| #if _WIN64 |
| path += "\\Bin"; |
| #else |
| path += "\\Bin32"; |
| #endif |
| #else // gcc |
| cube_exe = "./cube"; |
| path += "/../examples/build"; |
| #endif |
| full_cmd = cube_exe; |
| full_cmd += " --c 100"; |
| |
| PrintBeginTable("Cube", 2); |
| |
| PrintBeginTableRow(); |
| PrintTableElement(full_cmd); |
| int test_result = RunTestInDirectory(path, cube_exe, full_cmd); |
| if (test_result == 0) { |
| PrintTableElement("SUCCESSFUL"); |
| } else if (test_result == 1) { |
| PrintTableElement("Not Found"); |
| } else { |
| PrintTableElement("FAILED!"); |
| failed = true; |
| } |
| PrintEndTableRow(); |
| |
| full_cmd += " --validate"; |
| |
| PrintBeginTableRow(); |
| PrintTableElement(full_cmd); |
| test_result = RunTestInDirectory(path, cube_exe, full_cmd); |
| if (test_result == 0) { |
| PrintTableElement("SUCCESSFUL"); |
| } else if (test_result == 1) { |
| PrintTableElement("Not Found"); |
| } else { |
| PrintTableElement("FAILED!"); |
| failed = true; |
| } |
| PrintEndTableRow(); |
| |
| PrintEndTable(); |
| } else { |
| PrintStandardText("No SDK found by VIA, skipping test section"); |
| } |
| EndSection(); |
| |
| if (failed) { |
| throw(-1); |
| } |
| } |
| |
| // Print information on any Vulkan commands we can (or can't) execute. |
| void PrintVulkanInfo(void) { |
| BeginSection("Vulkan API Calls"); |
| |
| PrintInstanceInfo(); |
| PrintPhysDevInfo(); |
| PrintLogicalDeviceInfo(); |
| PrintCleanupInfo(); |
| |
| EndSection(); |
| } |