| /* |
| * Copyright (c) 2019 The Khronos Group Inc. |
| * Copyright (c) 2019 Valve Corporation |
| * Copyright (c) 2019 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: Charles Giessen <charles@lunarg.com> |
| * |
| */ |
| |
| #pragma once |
| |
| #include <iomanip> |
| #include <iostream> |
| #include <ostream> |
| #include <stack> |
| #include <sstream> |
| #include <string> |
| |
| #include <assert.h> |
| |
| std::string insert_quotes(std::string s) { return "\"" + s + "\""; } |
| |
| std::string to_string_16(uint8_t uid[16]) { |
| std::stringstream stream; |
| stream << std::setw(2) << std::hex; |
| stream << (int)uid[0] << (int)uid[1] << (int)uid[2] << (int)uid[3] << "-"; |
| stream << (int)uid[4] << (int)uid[5] << "-"; |
| stream << (int)uid[6] << (int)uid[7] << "-"; |
| stream << (int)uid[8] << (int)uid[9] << "-"; |
| stream << (int)uid[10] << (int)uid[11] << (int)uid[12] << (int)uid[13] << (int)uid[14] << (int)uid[15]; |
| |
| return stream.str(); |
| } |
| |
| std::string to_string_8(uint8_t uid[8]) { |
| std::stringstream stream; |
| stream << std::setw(2) << std::hex; |
| stream << (int)uid[0] << (int)uid[1] << (int)uid[2] << (int)uid[3] << "-"; |
| stream << (int)uid[4] << (int)uid[5] << (int)uid[6] << (int)uid[7]; |
| |
| return stream.str(); |
| } |
| |
| std::string VkVersionString(uint32_t version) { |
| uint32_t major = version >> 22; |
| uint32_t minor = (version >> 12) & 0x3ff; |
| uint32_t patch = version & 0xfff; |
| return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); |
| } |
| |
| std::string VkVersionString(VulkanVersion v) { |
| return std::to_string(v.major) + "." + std::to_string(v.minor) + "." + std::to_string(v.patch); |
| } |
| |
| enum class OutputType { text, html, json }; |
| |
| class Printer { |
| public: |
| Printer(OutputType output_type, std::ostream &out, const uint32_t selected_gpu, const VulkanVersion vulkan_version) |
| : output_type(output_type), out(out) { |
| switch (output_type) { |
| case (OutputType::text): |
| out << "==========\n"; |
| out << "VULKANINFO\n"; |
| out << "==========\n\n"; |
| out << "Vulkan Instance Version: " << VkVersionString(vulkan_version) << "\n\n\n"; |
| |
| break; |
| case (OutputType::html): |
| out << "<!doctype html>\n"; |
| out << "<html lang='en'>\n"; |
| out << "\t<head>\n"; |
| out << "\t\t<title>vulkaninfo</title>\n"; |
| out << "\t\t<style>\n"; |
| out << "\t\thtml {\n"; |
| out << "\t\t\tbackground-color: #0b1e48;\n"; |
| out << "\t\t\tbackground-image: url(\"https://vulkan.lunarg.com/img/bg-starfield.jpg\");\n"; |
| out << "\t\t\tbackground-position: center;\n"; |
| out << "\t\t\t-webkit-background-size: cover;\n"; |
| out << "\t\t\t-moz-background-size: cover;\n"; |
| out << "\t\t\t-o-background-size: cover;\n"; |
| out << "\t\t\tbackground-size: cover;\n"; |
| out << "\t\t\tbackground-attachment: fixed;\n"; |
| out << "\t\t\tbackground-repeat: no-repeat;\n"; |
| out << "\t\t\theight: 100%;\n"; |
| out << "\t\t}\n"; |
| out << "\t\t#header {\n"; |
| out << "\t\t\tz-index: -1;\n"; |
| out << "\t\t}\n"; |
| out << "\t\t#header>img {\n"; |
| out << "\t\t\tposition: absolute;\n"; |
| out << "\t\t\twidth: 160px;\n"; |
| out << "\t\t\tmargin-left: -280px;\n"; |
| out << "\t\t\ttop: -10px;\n"; |
| out << "\t\t\tleft: 50%;\n"; |
| out << "\t\t}\n"; |
| out << "\t\t#header>h1 {\n"; |
| out << "\t\t\tfont-family: Arial, \"Helvetica Neue\", Helvetica, sans-serif;\n"; |
| out << "\t\t\tfont-size: 44px;\n"; |
| out << "\t\t\tfont-weight: 200;\n"; |
| out << "\t\t\ttext-shadow: 4px 4px 5px #000;\n"; |
| out << "\t\t\tcolor: #eee;\n"; |
| out << "\t\t\tposition: absolute;\n"; |
| out << "\t\t\twidth: 400px;\n"; |
| out << "\t\t\tmargin-left: -80px;\n"; |
| out << "\t\t\ttop: 8px;\n"; |
| out << "\t\t\tleft: 50%;\n"; |
| out << "\t\t}\n"; |
| out << "\t\tbody {\n"; |
| out << "\t\t\tfont-family: Consolas, monaco, monospace;\n"; |
| out << "\t\t\tfont-size: 14px;\n"; |
| out << "\t\t\tline-height: 20px;\n"; |
| out << "\t\t\tcolor: #eee;\n"; |
| out << "\t\t\theight: 100%;\n"; |
| out << "\t\t\tmargin: 0;\n"; |
| out << "\t\t\toverflow: hidden;\n"; |
| out << "\t\t}\n"; |
| out << "\t\t#wrapper {\n"; |
| out << "\t\t\tbackground-color: rgba(0, 0, 0, 0.7);\n"; |
| out << "\t\t\tborder: 1px solid #446;\n"; |
| out << "\t\t\tbox-shadow: 0px 0px 10px #000;\n"; |
| out << "\t\t\tpadding: 8px 12px;\n\n"; |
| out << "\t\t\tdisplay: inline-block;\n"; |
| out << "\t\t\tposition: absolute;\n"; |
| out << "\t\t\ttop: 80px;\n"; |
| out << "\t\t\tbottom: 25px;\n"; |
| out << "\t\t\tleft: 50px;\n"; |
| out << "\t\t\tright: 50px;\n"; |
| out << "\t\t\toverflow: auto;\n"; |
| out << "\t\t}\n"; |
| out << "\t\tdetails>details {\n"; |
| out << "\t\t\tmargin-left: 22px;\n"; |
| out << "\t\t}\n"; |
| out << "\t\tdetails>summary:only-child::-webkit-details-marker {\n"; |
| out << "\t\t\tdisplay: none;\n"; |
| out << "\t\t}\n"; |
| out << "\t\t.var, .type, .val {\n"; |
| out << "\t\t\tdisplay: inline;\n"; |
| out << "\t\t}\n"; |
| out << "\t\t.var {\n"; |
| out << "\t\t}\n"; |
| out << "\t\t.type {\n"; |
| out << "\t\t\tcolor: #acf;\n"; |
| out << "\t\t\tmargin: 0 12px;\n"; |
| out << "\t\t}\n"; |
| out << "\t\t.val {\n"; |
| out << "\t\t\tcolor: #afa;\n"; |
| out << "\t\t\tbackground: #222;\n"; |
| out << "\t\t\ttext-align: right;\n"; |
| out << "\t\t}\n"; |
| out << "\t\t</style>\n"; |
| out << "\t</head>\n"; |
| out << "\t<body>\n"; |
| out << "\t\t<div id='header'>\n"; |
| out << "\t\t\t<h1>vulkaninfo</h1>\n"; |
| out << "\t\t</div>\n"; |
| out << "\t\t<div id='wrapper'>\n"; |
| |
| out << "\t\t\t<details><summary>Vulkan Instance Version: <span class='val'>" << VkVersionString(vulkan_version) |
| << "</span></summary></details>\n\t\t\t<br />\n"; |
| indents += 3; |
| break; |
| case (OutputType::json): |
| out << "{\n"; |
| out << "\t\"$schema\": \"https://schema.khronos.org/vulkan/devsim_1_0_0.json#\",\n"; |
| out << "\t\"comments\": {\n"; |
| out << "\t\t\"desc\": \"JSON configuration file describing GPU " << selected_gpu |
| << ". Generated using the vulkaninfo program.\",\n"; |
| out << "\t\t\"vulkanApiVersion\": \"" << VkVersionString(vulkan_version) << "\"\n"; |
| out << "\t}"; |
| indents++; |
| is_first_item.push(false); |
| break; |
| default: |
| break; |
| } |
| } |
| ~Printer() { |
| switch (output_type) { |
| case (OutputType::text): |
| |
| break; |
| case (OutputType::html): |
| out << "\t\t</div>\n"; |
| out << "\t</body>\n"; |
| out << "</html>\n"; |
| indents -= 3; |
| break; |
| case (OutputType::json): |
| out << "\n}\n"; |
| indents--; |
| is_first_item.pop(); |
| assert(is_first_item.empty() && "mismatched number of ObjectStart/ObjectEnd or ArrayStart/ArrayEnd's"); |
| break; |
| } |
| assert(indents == 0 && "indents must be zero at program end"); |
| }; |
| |
| Printer(const Printer &) = delete; |
| const Printer &operator=(const Printer &) = delete; |
| |
| OutputType Type() { return output_type; } |
| |
| // Custom Formatting |
| // use by prepending with p.SetXXX().ObjectStart/ArrayStart |
| |
| Printer &SetHeader() { |
| set_next_header = true; |
| return *this; |
| } |
| |
| Printer &SetSubHeader() { |
| set_next_subheader = true; |
| return *this; |
| } |
| |
| Printer &SetOpenDetails() { |
| set_details_open = true; |
| return *this; |
| } |
| |
| Printer &SetTitleAsType() { |
| set_object_name_as_type = true; |
| return *this; |
| } |
| |
| Printer &SetAsType() { |
| set_as_type = true; |
| return *this; |
| } |
| |
| Printer &SetElementIndex(int index) { |
| assert(index >= 0 && "cannot set element index to a negative value"); |
| element_index = index; |
| return *this; |
| } |
| |
| void ObjectStart(std::string object_name) { |
| switch (output_type) { |
| case (OutputType::text): { |
| out << std::string(static_cast<size_t>(indents), '\t') << object_name; |
| if (element_index != -1) { |
| out << "[" << element_index << "]"; |
| } |
| out << ":\n"; |
| size_t headersize = object_name.size() + 1; |
| if (element_index != -1) { |
| headersize += 1 + std::to_string(element_index).size(); |
| element_index = -1; |
| } |
| PrintHeaderUnderlines(headersize); |
| break; |
| } |
| case (OutputType::html): |
| out << std::string(static_cast<size_t>(indents), '\t'); |
| if (set_details_open) { |
| out << "<details open>"; |
| set_details_open = false; |
| } else { |
| out << "<details>"; |
| } |
| out << "<summary>"; |
| if (set_object_name_as_type) { |
| out << "<span class='type'>" << object_name << "</span>"; |
| set_object_name_as_type = false; |
| } else { |
| out << object_name; |
| } |
| if (element_index != -1) { |
| out << "[<span class='val'>" << element_index << "</span>]"; |
| element_index = -1; |
| } |
| out << "</summary>\n"; |
| break; |
| case (OutputType::json): |
| if (!is_first_item.top()) { |
| out << ",\n"; |
| } else { |
| is_first_item.top() = false; |
| } |
| out << std::string(static_cast<size_t>(indents), '\t'); |
| // Objects with no name are elements in an array of objects |
| if (object_name == "" || element_index != -1) { |
| out << "{\n"; |
| element_index = -1; |
| } else { |
| out << "\"" << object_name << "\": {\n"; |
| } |
| |
| is_first_item.push(true); |
| break; |
| default: |
| break; |
| } |
| indents++; |
| } |
| void ObjectEnd() { |
| indents--; |
| assert(indents >= 0 && "indents cannot go below zero"); |
| switch (output_type) { |
| case (OutputType::text): |
| |
| break; |
| case (OutputType::html): |
| out << std::string(static_cast<size_t>(indents), '\t') << "</details>\n"; |
| break; |
| case (OutputType::json): |
| out << "\n" << std::string(static_cast<size_t>(indents), '\t') << "}"; |
| is_first_item.pop(); |
| break; |
| default: |
| break; |
| } |
| } |
| void ArrayStart(std::string array_name, size_t element_count = 0) { |
| switch (output_type) { |
| case (OutputType::text): |
| out << std::string(static_cast<size_t>(indents), '\t') << array_name << ": " |
| << "count = " << element_count << "\n"; |
| PrintHeaderUnderlines(array_name.size() + 1); |
| break; |
| case (OutputType::html): |
| out << std::string(static_cast<size_t>(indents), '\t'); |
| if (set_details_open) { |
| out << "<details open>"; |
| set_details_open = false; |
| } else { |
| out << "<details>"; |
| } |
| out << "<summary>" << array_name << ": count = <span class='val'>" << element_count << "</span></summary>\n"; |
| break; |
| case (OutputType::json): |
| if (!is_first_item.top()) { |
| out << ",\n"; |
| } else { |
| is_first_item.top() = false; |
| } |
| out << std::string(static_cast<size_t>(indents), '\t') << "\"" << array_name << "\": " |
| << "[\n"; |
| is_first_item.push(true); |
| break; |
| default: |
| break; |
| } |
| indents++; |
| } |
| void ArrayEnd() { |
| indents--; |
| assert(indents >= 0 && "indents cannot go below zero"); |
| switch (output_type) { |
| case (OutputType::text): |
| |
| break; |
| case (OutputType::html): |
| out << std::string(static_cast<size_t>(indents), '\t') << "</details>\n"; |
| break; |
| case (OutputType::json): |
| out << "\n" << std::string(static_cast<size_t>(indents), '\t') << "]"; |
| is_first_item.pop(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| // For printing key-value pairs. |
| // min_key_width lines up the values listed |
| // value_description is for reference information and is displayed inside parenthesis after the value |
| template <typename T> |
| void PrintKeyValue(std::string key, T value, size_t min_key_width = 0, std::string value_description = "") { |
| switch (output_type) { |
| case (OutputType::text): |
| if (min_key_width > key.size()) { |
| out << std::string(static_cast<size_t>(indents), '\t') << key << std::string(min_key_width - key.size(), ' '); |
| } else { |
| out << std::string(static_cast<size_t>(indents), '\t') << key; |
| } |
| out << " = " << value; |
| if (value_description != "") { |
| out << " (" << value_description << ")"; |
| } |
| out << "\n"; |
| break; |
| case (OutputType::html): |
| out << std::string(static_cast<size_t>(indents), '\t') << "<details><summary>" << key; |
| if (min_key_width > key.size()) { |
| out << std::string(min_key_width - key.size(), ' '); |
| } |
| if (set_as_type) { |
| set_as_type = false; |
| out << " = <span class='type'>" << value << "</span>"; |
| } else { |
| out << " = <span class='val'>" << value << "</span>"; |
| } |
| if (value_description != "") { |
| out << " (<span class='val'>" << value_description << "</span>)"; |
| } |
| out << "</summary></details>\n"; |
| break; |
| case (OutputType::json): |
| if (!is_first_item.top()) { |
| out << ",\n"; |
| } else { |
| is_first_item.top() = false; |
| } |
| out << std::string(static_cast<size_t>(indents), '\t') << "\"" << key << "\": " << value; |
| default: |
| break; |
| } |
| } |
| |
| // For printing key - string pairs (necessary because of json) |
| void PrintKeyString(std::string key, std::string value, size_t min_key_width = 0, std::string value_description = "") { |
| switch (output_type) { |
| case (OutputType::text): |
| case (OutputType::html): |
| PrintKeyValue(key, value, min_key_width, value_description); |
| break; |
| case (OutputType::json): |
| PrintKeyValue(key, std::string("\"") + value + "\"", min_key_width, value_description); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| // For printing key - string pairs (necessary because of json) |
| void PrintKeyBool(std::string key, bool value, size_t min_key_width = 0, std::string value_description = "") { |
| switch (output_type) { |
| case (OutputType::text): |
| case (OutputType::html): |
| PrintKeyValue(key, value ? "true" : "false", min_key_width, value_description); |
| break; |
| case (OutputType::json): |
| PrintKeyValue(key, value, min_key_width, value_description); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| // print inside array |
| template <typename T> |
| void PrintElement(T element, std::string value_description = "") { |
| switch (output_type) { |
| case (OutputType::text): |
| out << std::string(static_cast<size_t>(indents), '\t') << element; |
| if (value_description != "") { |
| out << " (" << value_description << ")"; |
| } |
| out << "\n"; |
| break; |
| case (OutputType::html): |
| out << std::string(static_cast<size_t>(indents), '\t') << "<details><summary>"; |
| if (set_as_type) { |
| set_as_type = false; |
| out << "<span class='type'>" << element << "</span>"; |
| } else { |
| out << "<span class='val'>" << element << "</span>"; |
| } |
| if (value_description != "") { |
| out << " (<span class='val'>" << value_description << "</span>)"; |
| } |
| out << "</summary></details>\n"; |
| break; |
| case (OutputType::json): |
| if (!is_first_item.top()) { |
| out << ",\n"; |
| } else { |
| is_first_item.top() = false; |
| } |
| out << std::string(static_cast<size_t>(indents), '\t') << element; |
| break; |
| default: |
| break; |
| } |
| } |
| void PrintExtension(std::string ext_name, uint32_t revision, int min_width = 0) { |
| switch (output_type) { |
| case (OutputType::text): |
| out << std::string(static_cast<size_t>(indents), '\t') << ext_name << std::string(min_width - ext_name.size(), ' ') |
| << " : extension revision " << revision << "\n"; |
| break; |
| case (OutputType::html): |
| out << std::string(static_cast<size_t>(indents), '\t') << "<details><summary><span class='type'>" << ext_name |
| << "</span>" << std::string(min_width - ext_name.size(), ' ') << " : extension revision <span class='val'>" |
| << revision << "</span></summary></details>\n"; |
| break; |
| case (OutputType::json): |
| |
| break; |
| default: |
| break; |
| } |
| } |
| void AddNewline() { |
| if (output_type == OutputType::text) { |
| out << "\n"; |
| } |
| } |
| void IndentIncrease() { |
| if (output_type == OutputType::text) { |
| indents++; |
| } |
| } |
| void IndentDecrease() { |
| if (output_type == OutputType::text) { |
| indents--; |
| assert(indents >= 0 && "indents cannot go below zero"); |
| } |
| } |
| |
| protected: |
| OutputType output_type; |
| std::ostream &out; |
| int indents = 0; |
| |
| // header, subheader |
| bool set_next_header = false; |
| bool set_next_subheader = false; |
| |
| // html coloring |
| bool set_as_type = false; |
| |
| // open <details> |
| bool set_details_open = false; |
| |
| // make object titles the color of types |
| bool set_object_name_as_type = false; |
| |
| // objects which are in an array |
| int element_index = -1; // negative one is the sentinel value |
| |
| // json |
| std::stack<bool> is_first_item; |
| |
| // utility |
| void PrintHeaderUnderlines(size_t length) { |
| assert(indents >= 0 && "indents must not be negative"); |
| assert(length <= 10000 && "length shouldn't be unreasonably large"); |
| if (set_next_header) { |
| out << std::string(static_cast<size_t>(indents), '\t') << std::string(length, '=') << "\n"; |
| set_next_header = false; |
| } else if (set_next_subheader) { |
| out << std::string(static_cast<size_t>(indents), '\t') << std::string(length, '-') << "\n"; |
| set_next_subheader = false; |
| } |
| } |
| }; |