2 * Copyright (c) 2019 The Khronos Group Inc.
3 * Copyright (c) 2019 Valve Corporation
4 * Copyright (c) 2019 LunarG, Inc.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * Author: Charles Giessen <charles@lunarg.com>
33 std::string insert_quotes(std::string s) { return "\"" + s + "\""; }
35 std::string to_string_16(uint8_t uid[16]) {
37 ss << std::hex << std::setfill('0');
38 for (int i = 0; i < 16; ++i) {
39 if (i == 4 || i == 6 || i == 8 || i == 10) ss << '-';
40 ss << std::setw(2) << static_cast<unsigned>(uid[i]);
45 std::string to_string_8(uint8_t uid[8]) {
47 ss << std::hex << std::setfill('0');
48 for (int i = 0; i < 8; ++i) {
49 if (i == 4) ss << '-';
50 ss << std::setw(2) << static_cast<unsigned>(uid[i]);
55 std::string VkVersionString(uint32_t version) {
56 uint32_t major = version >> 22;
57 uint32_t minor = (version >> 12) & 0x3ff;
58 uint32_t patch = version & 0xfff;
59 return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch);
62 std::string VkVersionString(VulkanVersion v) {
63 return std::to_string(v.major) + "." + std::to_string(v.minor) + "." + std::to_string(v.patch);
66 enum class OutputType { text, html, json, vkconfig_output };
70 Printer(OutputType output_type, std::ostream &out, const uint32_t selected_gpu, const VulkanVersion vulkan_version)
71 : output_type(output_type), out(out) {
72 switch (output_type) {
73 case (OutputType::text):
74 out << "==========\n";
75 out << "VULKANINFO\n";
76 out << "==========\n\n";
77 out << "Vulkan Instance Version: " << VkVersionString(vulkan_version) << "\n\n\n";
80 case (OutputType::html):
81 out << "<!doctype html>\n";
82 out << "<html lang='en'>\n";
84 out << "\t\t<title>vulkaninfo</title>\n";
85 out << "\t\t<style>\n";
86 out << "\t\thtml {\n";
87 out << "\t\t\tbackground-color: #0b1e48;\n";
88 out << "\t\t\tbackground-image: url(\"https://vulkan.lunarg.com/img/bg-starfield.jpg\");\n";
89 out << "\t\t\tbackground-position: center;\n";
90 out << "\t\t\t-webkit-background-size: cover;\n";
91 out << "\t\t\t-moz-background-size: cover;\n";
92 out << "\t\t\t-o-background-size: cover;\n";
93 out << "\t\t\tbackground-size: cover;\n";
94 out << "\t\t\tbackground-attachment: fixed;\n";
95 out << "\t\t\tbackground-repeat: no-repeat;\n";
96 out << "\t\t\theight: 100%;\n";
98 out << "\t\t#header {\n";
99 out << "\t\t\tz-index: -1;\n";
101 out << "\t\t#header>img {\n";
102 out << "\t\t\tposition: absolute;\n";
103 out << "\t\t\twidth: 160px;\n";
104 out << "\t\t\tmargin-left: -280px;\n";
105 out << "\t\t\ttop: -10px;\n";
106 out << "\t\t\tleft: 50%;\n";
108 out << "\t\t#header>h1 {\n";
109 out << "\t\t\tfont-family: Arial, \"Helvetica Neue\", Helvetica, sans-serif;\n";
110 out << "\t\t\tfont-size: 44px;\n";
111 out << "\t\t\tfont-weight: 200;\n";
112 out << "\t\t\ttext-shadow: 4px 4px 5px #000;\n";
113 out << "\t\t\tcolor: #eee;\n";
114 out << "\t\t\tposition: absolute;\n";
115 out << "\t\t\twidth: 400px;\n";
116 out << "\t\t\tmargin-left: -80px;\n";
117 out << "\t\t\ttop: 8px;\n";
118 out << "\t\t\tleft: 50%;\n";
120 out << "\t\tbody {\n";
121 out << "\t\t\tfont-family: Consolas, monaco, monospace;\n";
122 out << "\t\t\tfont-size: 14px;\n";
123 out << "\t\t\tline-height: 20px;\n";
124 out << "\t\t\tcolor: #eee;\n";
125 out << "\t\t\theight: 100%;\n";
126 out << "\t\t\tmargin: 0;\n";
127 out << "\t\t\toverflow: hidden;\n";
129 out << "\t\t#wrapper {\n";
130 out << "\t\t\tbackground-color: rgba(0, 0, 0, 0.7);\n";
131 out << "\t\t\tborder: 1px solid #446;\n";
132 out << "\t\t\tbox-shadow: 0px 0px 10px #000;\n";
133 out << "\t\t\tpadding: 8px 12px;\n\n";
134 out << "\t\t\tdisplay: inline-block;\n";
135 out << "\t\t\tposition: absolute;\n";
136 out << "\t\t\ttop: 80px;\n";
137 out << "\t\t\tbottom: 25px;\n";
138 out << "\t\t\tleft: 50px;\n";
139 out << "\t\t\tright: 50px;\n";
140 out << "\t\t\toverflow: auto;\n";
142 out << "\t\tdetails>details {\n";
143 out << "\t\t\tmargin-left: 22px;\n";
145 out << "\t\tdetails>summary:only-child::-webkit-details-marker {\n";
146 out << "\t\t\tdisplay: none;\n";
148 out << "\t\t.var, .type, .val {\n";
149 out << "\t\t\tdisplay: inline;\n";
151 out << "\t\t.var {\n";
153 out << "\t\t.type {\n";
154 out << "\t\t\tcolor: #acf;\n";
155 out << "\t\t\tmargin: 0 12px;\n";
157 out << "\t\t.val {\n";
158 out << "\t\t\tcolor: #afa;\n";
159 out << "\t\t\tbackground: #222;\n";
160 out << "\t\t\ttext-align: right;\n";
162 out << "\t\t</style>\n";
163 out << "\t</head>\n";
165 out << "\t\t<div id='header'>\n";
166 out << "\t\t\t<h1>vulkaninfo</h1>\n";
167 out << "\t\t</div>\n";
168 out << "\t\t<div id='wrapper'>\n";
170 out << "\t\t\t<details><summary>Vulkan Instance Version: <span class='val'>" << VkVersionString(vulkan_version)
171 << "</span></summary></details>\n\t\t\t<br />\n";
174 case (OutputType::json):
176 out << "\t\"$schema\": \"https://schema.khronos.org/vulkan/devsim_1_0_0.json#\",\n";
177 out << "\t\"comments\": {\n";
178 out << "\t\t\"desc\": \"JSON configuration file describing GPU " << selected_gpu
179 << ". Generated using the vulkaninfo program.\",\n";
180 out << "\t\t\"vulkanApiVersion\": \"" << VkVersionString(vulkan_version) << "\"\n";
183 is_first_item.push(false);
185 case (OutputType::vkconfig_output):
187 out << "\t\"Vulkan Instance Version\": \"" << VkVersionString(vulkan_version) << "\"";
189 is_first_item.push(false);
196 switch (output_type) {
197 case (OutputType::text):
200 case (OutputType::html):
201 out << "\t\t</div>\n";
202 out << "\t</body>\n";
206 case (OutputType::json):
207 case (OutputType::vkconfig_output):
211 assert(is_first_item.empty() && "mismatched number of ObjectStart/ObjectEnd or ArrayStart/ArrayEnd's");
214 assert(indents == 0 && "indents must be zero at program end");
217 Printer(const Printer &) = delete;
218 const Printer &operator=(const Printer &) = delete;
220 OutputType Type() { return output_type; }
223 // use by prepending with p.SetXXX().ObjectStart/ArrayStart
225 Printer &SetHeader() {
226 set_next_header = true;
230 Printer &SetSubHeader() {
231 set_next_subheader = true;
235 Printer &SetOpenDetails() {
236 set_details_open = true;
240 Printer &SetTitleAsType() {
241 set_object_name_as_type = true;
245 Printer &SetAsType() {
250 Printer &SetElementIndex(int index) {
251 assert(index >= 0 && "cannot set element index to a negative value");
252 element_index = index;
256 void ObjectStart(std::string object_name, int32_t count_subobjects = -1) {
257 switch (output_type) {
258 case (OutputType::text): {
259 out << std::string(static_cast<size_t>(indents), '\t') << object_name;
260 if (element_index != -1) {
261 out << "[" << element_index << "]";
264 if (count_subobjects >= 0) {
265 out << " count = " << count_subobjects;
268 size_t headersize = object_name.size() + 1;
269 if (count_subobjects >= 0) {
270 headersize += 9 + std::to_string(count_subobjects).size();
272 if (element_index != -1) {
273 headersize += 2 + std::to_string(element_index).size();
276 PrintHeaderUnderlines(headersize);
279 case (OutputType::html):
280 out << std::string(static_cast<size_t>(indents), '\t');
281 if (set_details_open) {
282 out << "<details open>";
283 set_details_open = false;
288 if (set_object_name_as_type) {
289 out << "<span class='type'>" << object_name << "</span>";
290 set_object_name_as_type = false;
294 if (element_index != -1) {
295 out << "[<span class='val'>" << element_index << "</span>]";
298 if (count_subobjects >= 0) {
299 out << ": count = <span class='val'>" << std::to_string(count_subobjects) << "</span>";
301 out << "</summary>\n";
303 case (OutputType::json):
304 if (!is_first_item.top()) {
307 is_first_item.top() = false;
309 out << std::string(static_cast<size_t>(indents), '\t');
310 // Objects with no name are elements in an array of objects
311 if (object_name == "" || element_index != -1) {
315 out << "\"" << object_name << "\": {\n";
318 is_first_item.push(true);
320 case (OutputType::vkconfig_output):
321 if (!is_first_item.top()) {
324 is_first_item.top() = false;
326 out << std::string(static_cast<size_t>(indents), '\t');
328 if (element_index != -1) {
329 out << "\"" << object_name << "[" << element_index << "]\": {\n";
332 out << "\"" << object_name << "\": {\n";
334 is_first_item.push(true);
343 assert(indents >= 0 && "indents cannot go below zero");
344 switch (output_type) {
345 case (OutputType::text):
348 case (OutputType::html):
349 out << std::string(static_cast<size_t>(indents), '\t') << "</details>\n";
351 case (OutputType::json):
352 case (OutputType::vkconfig_output):
353 out << "\n" << std::string(static_cast<size_t>(indents), '\t') << "}";
360 void ArrayStart(std::string array_name, int32_t element_count = 0) {
361 switch (output_type) {
362 case (OutputType::text): {
363 out << std::string(static_cast<size_t>(indents), '\t') << array_name << ":";
364 size_t underline_count = array_name.size() + 1;
365 if (element_count >= 0) {
366 out << " count = " << element_count;
367 underline_count += 9 + std::to_string(element_count).size();
370 PrintHeaderUnderlines(underline_count);
373 case (OutputType::html):
374 out << std::string(static_cast<size_t>(indents), '\t');
375 if (set_details_open) {
376 out << "<details open>";
377 set_details_open = false;
381 out << "<summary>" << array_name;
382 if (element_count >= 0) {
383 out << ": count = <span class='val'>" << element_count << "</span>";
385 out << "</summary>\n";
387 case (OutputType::json):
388 case (OutputType::vkconfig_output):
389 if (!is_first_item.top()) {
392 is_first_item.top() = false;
394 out << std::string(static_cast<size_t>(indents), '\t') << "\"" << array_name << "\": "
396 is_first_item.push(true);
405 assert(indents >= 0 && "indents cannot go below zero");
406 switch (output_type) {
407 case (OutputType::text):
410 case (OutputType::html):
411 out << std::string(static_cast<size_t>(indents), '\t') << "</details>\n";
413 case (OutputType::json):
414 case (OutputType::vkconfig_output):
415 out << "\n" << std::string(static_cast<size_t>(indents), '\t') << "]";
423 // For printing key-value pairs.
424 // min_key_width lines up the values listed
425 // value_description is for reference information and is displayed inside parenthesis after the value
426 template <typename T>
427 void PrintKeyValue(std::string key, T value, size_t min_key_width = 0, std::string value_description = "") {
428 switch (output_type) {
429 case (OutputType::text):
430 if (min_key_width > key.size()) {
431 out << std::string(static_cast<size_t>(indents), '\t') << key << std::string(min_key_width - key.size(), ' ');
433 out << std::string(static_cast<size_t>(indents), '\t') << key;
435 out << " = " << value;
436 if (value_description != "") {
437 out << " (" << value_description << ")";
441 case (OutputType::html):
442 out << std::string(static_cast<size_t>(indents), '\t') << "<details><summary>" << key;
443 if (min_key_width > key.size()) {
444 out << std::string(min_key_width - key.size(), ' ');
448 out << " = <span class='type'>" << value << "</span>";
450 out << " = <span class='val'>" << value << "</span>";
452 if (value_description != "") {
453 out << " (<span class='val'>" << value_description << "</span>)";
455 out << "</summary></details>\n";
457 case (OutputType::json):
458 if (!is_first_item.top()) {
461 is_first_item.top() = false;
463 out << std::string(static_cast<size_t>(indents), '\t') << "\"" << key << "\": " << value;
465 case (OutputType::vkconfig_output):
466 if (!is_first_item.top()) {
469 is_first_item.top() = false;
471 out << std::string(static_cast<size_t>(indents), '\t') << "\"" << key << "\": ";
472 if (value_description != "") {
473 out << "\"" << value << " (" << value_description << ")\"";
482 // For printing key - string pairs (necessary because of json)
483 void PrintKeyString(std::string key, std::string value, size_t min_key_width = 0, std::string value_description = "") {
484 switch (output_type) {
485 case (OutputType::text):
486 case (OutputType::html):
487 PrintKeyValue(key, value, min_key_width, value_description);
489 case (OutputType::json):
490 case (OutputType::vkconfig_output):
491 PrintKeyValue(key, std::string("\"") + value + "\"", min_key_width, value_description);
498 // For printing key - string pairs (necessary because of json)
499 void PrintKeyBool(std::string key, bool value, size_t min_key_width = 0, std::string value_description = "") {
500 switch (output_type) {
501 case (OutputType::text):
502 case (OutputType::html):
503 case (OutputType::vkconfig_output):
504 PrintKeyValue(key, value ? "true" : "false", min_key_width, value_description);
506 case (OutputType::json):
507 PrintKeyValue(key, value, min_key_width, value_description);
514 // print inside array
515 template <typename T>
516 void PrintElement(T element, std::string value_description = "") {
517 switch (output_type) {
518 case (OutputType::text):
519 out << std::string(static_cast<size_t>(indents), '\t') << element;
520 if (value_description != "") {
521 out << " (" << value_description << ")";
525 case (OutputType::html):
526 out << std::string(static_cast<size_t>(indents), '\t') << "<details><summary>";
529 out << "<span class='type'>" << element << "</span>";
531 out << "<span class='val'>" << element << "</span>";
533 if (value_description != "") {
534 out << " (<span class='val'>" << value_description << "</span>)";
536 out << "</summary></details>\n";
538 case (OutputType::json):
539 case (OutputType::vkconfig_output):
540 if (!is_first_item.top()) {
543 is_first_item.top() = false;
545 out << std::string(static_cast<size_t>(indents), '\t') << element;
551 void PrintString(std::string string, std::string value_description = "") {
552 switch (output_type) {
553 case (OutputType::text):
554 case (OutputType::html):
555 PrintElement(string, value_description);
557 case (OutputType::json):
558 case (OutputType::vkconfig_output):
559 PrintElement("\"" + string + "\"", value_description);
564 void PrintExtension(std::string ext_name, uint32_t revision, int min_width = 0) {
565 switch (output_type) {
566 case (OutputType::text):
567 out << std::string(static_cast<size_t>(indents), '\t') << ext_name << std::string(min_width - ext_name.size(), ' ')
568 << " : extension revision " << revision << "\n";
570 case (OutputType::html):
571 out << std::string(static_cast<size_t>(indents), '\t') << "<details><summary>" << DecorateAsType(ext_name)
572 << std::string(min_width - ext_name.size(), ' ') << " : extension revision "
573 << DecorateAsValue(std::to_string(revision)) << "</summary></details>\n";
575 case (OutputType::json):
577 case (OutputType::vkconfig_output):
578 ObjectStart(ext_name);
579 PrintKeyValue("specVersion", revision);
587 if (output_type == OutputType::text) {
591 void IndentIncrease() {
592 if (output_type == OutputType::text) {
596 void IndentDecrease() {
597 if (output_type == OutputType::text) {
599 assert(indents >= 0 && "indents cannot go below zero");
603 std::string DecorateAsType(const std::string &input) {
604 if (output_type == OutputType::html)
605 return "<span class='type'>" + input + "</span>";
610 std::string DecorateAsValue(const std::string &input) {
611 if (output_type == OutputType::html)
612 return "<span class='val'>" + input + "</span>";
618 OutputType output_type;
623 bool set_next_header = false;
624 bool set_next_subheader = false;
627 bool set_as_type = false;
630 bool set_details_open = false;
632 // make object titles the color of types
633 bool set_object_name_as_type = false;
635 // objects which are in an array
636 int element_index = -1; // negative one is the sentinel value
639 std::stack<bool> is_first_item;
642 void PrintHeaderUnderlines(size_t length) {
643 assert(indents >= 0 && "indents must not be negative");
644 assert(length <= 10000 && "length shouldn't be unreasonably large");
645 if (set_next_header) {
646 out << std::string(static_cast<size_t>(indents), '\t') << std::string(length, '=') << "\n";
647 set_next_header = false;
648 } else if (set_next_subheader) {
649 out << std::string(static_cast<size_t>(indents), '\t') << std::string(length, '-') << "\n";
650 set_next_subheader = false;
655 class ObjectWrapper {
657 ObjectWrapper(Printer &p, std::string object_name, int32_t count_subobjects = -1) : p(p) {
658 p.ObjectStart(object_name, count_subobjects);
660 ~ObjectWrapper() { p.ObjectEnd(); }
668 ArrayWrapper(Printer &p, std::string array_name, int32_t element_count = 0) : p(p) { p.ArrayStart(array_name, element_count); }
669 ~ArrayWrapper() { p.ArrayEnd(); }