├── .gitignore ├── .gitmodules ├── README.md ├── extensionsGenerator.php └── templates ├── VulkanDeviceInfoExtensions.cpp └── VulkanDeviceInfoExtensions.h /.gitignore: -------------------------------------------------------------------------------- 1 | /out -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Vulkan-Docs"] 2 | path = Vulkan-Docs 3 | url = https://github.com/KhronosGroup/Vulkan-Docs.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VulkanCapsViewerGenerator 2 | Extension header generator based on the official Vulkan registry xml 3 | -------------------------------------------------------------------------------- /extensionsGenerator.php: -------------------------------------------------------------------------------- 1 | types->type as $alias_type) { 12 | if ($alias_type['name'] && strcasecmp($alias_type['name'], $type['alias']) === 0) { 13 | $this->aliases[(string)$type['name']] = $alias_type; 14 | return $alias_type; 15 | } 16 | } 17 | } 18 | return $type; 19 | } 20 | 21 | function __construct($xml) { 22 | // We're only interested in types extending VkPhysicalDeviceProperties2 or VkPhysicalDeviceFeatures2 23 | foreach ($xml->types->type as $type) { 24 | $actual_type = $this->getActualType($type, $xml); 25 | if (($actual_type['structextends']) && (strcasecmp($actual_type['structextends'], 'VkPhysicalDeviceProperties2') === 0)) { 26 | $this->property_types[] = $actual_type; 27 | } 28 | if (($actual_type['structextends']) && (stripos($actual_type['structextends'], 'VkPhysicalDeviceFeatures2') !== false)) { 29 | $this->feature_types[] = $actual_type; 30 | } 31 | } 32 | } 33 | 34 | /** 35 | * Checks if the given type is aliased, and returns the type name to look for 36 | */ 37 | private function getActualTypeName($type_name) { 38 | $actual_name = $type_name; 39 | if ($this->aliases[(string)$type_name]) { 40 | $actual_name = $this->aliases[(string)$type_name]['name']; 41 | } 42 | return $actual_name; 43 | } 44 | 45 | public function getProperties2Type($name) { 46 | $actual_name = $this->getActualTypeName($name); 47 | foreach ($this->property_types as $property_type) { 48 | if (strcasecmp($property_type['name'], $actual_name) === 0) { 49 | return $property_type; 50 | } 51 | } 52 | return null; 53 | } 54 | 55 | public function getFeatures2Type($name) { 56 | $actual_name = $this->getActualTypeName($name); 57 | foreach ($this->feature_types as $feature_type) { 58 | if (strcasecmp($feature_type['name'], $actual_name) === 0) { 59 | return $feature_type; 60 | } 61 | } 62 | return null; 63 | } 64 | 65 | public static function getsType($node) { 66 | foreach ($node->member as $member) { 67 | if ($member['values'] && (stripos((string)$member['values'], 'VK_STRUCTURE_TYPE_') !== false)) { 68 | return $node->member['values']; 69 | } 70 | } 71 | } 72 | } 73 | 74 | class Extension { 75 | public $name; 76 | public $stype; 77 | public $group; 78 | public $features2; 79 | public $properties2; 80 | } 81 | 82 | class ExtensionContainer { 83 | public $extensions = []; 84 | 85 | function __construct($xml, $typecontainer) { 86 | foreach ($xml->extensions->extension as $ext_node) { 87 | $features2_node = null; 88 | $properties2_node = null; 89 | // We're only interested in extensions with property or feature types 90 | foreach ($ext_node->require as $require) { 91 | foreach ($require as $requirement) { 92 | if (strcasecmp($requirement->getName, 'type')) { 93 | $ft2 = $typecontainer->getFeatures2Type((string)$requirement['name']); 94 | if (!$features2_node && $ft2) { 95 | $features2_node = $ft2; 96 | } 97 | $prop2 = $typecontainer->getProperties2Type((string)$requirement['name']); 98 | if (!$properties2_node && $prop2) { 99 | $properties2_node = $prop2; 100 | } 101 | } 102 | } 103 | } 104 | if ($features2_node || $properties2_node) { 105 | $ext = new Extension(); 106 | $ext->name = (string)$ext_node['name']; 107 | $ext->group = substr($ext->name, 3, strpos($ext->name, '_', 3) - 3); 108 | $ext->features2 = $features2_node; 109 | $ext->properties2 = $properties2_node; 110 | $this->extensions[] = $ext; 111 | } 112 | } 113 | } 114 | 115 | } 116 | 117 | class CppBuilder { 118 | 119 | private $header_functions = []; 120 | private $vk_header_version = null; 121 | 122 | function __construct($vk_header_version) { 123 | $this->vk_header_version = $vk_header_version; 124 | } 125 | 126 | public function generateFeatures2CodeBlock($extension) { 127 | $sType = TypeContainer::getsType($extension->features2); 128 | $res = "\tif (extensionSupported(\"{$extension->name}\")) {\n"; 129 | $res .= "\t\tconst char* extension(\"{$extension->name}\");\n"; 130 | $res .= "\t\t{$extension->features2['name']} extFeatures { $sType };\n"; 131 | $res .= "\t\tVkPhysicalDeviceFeatures2 deviceFeatures2(initDeviceFeatures2(&extFeatures));\n"; 132 | $res .= "\t\tpfnGetPhysicalDeviceFeatures2KHR(device, &deviceFeatures2);\n"; 133 | foreach ($extension->features2->member as $member) { 134 | // Skip types that are not related to feature details 135 | $type = (string)$member->type; 136 | if (in_array($type, ['VkStructureType', 'void'])) { 137 | continue; 138 | } 139 | $name = (string)$member->name; 140 | $res .= "\t\tpushFeature2(extension, \"$name\", extFeatures.$name);\n"; 141 | } 142 | $res .= "\t}\n"; 143 | return $res; 144 | } 145 | 146 | public function generateProperties2CodeBlock($extension) { 147 | $sType = TypeContainer::getsType($extension->properties2); 148 | $res = "\tif (extensionSupported(\"{$extension->name}\")) {\n"; 149 | $res .= "\t\tconst char* extension(\"{$extension->name}\");\n"; 150 | $res .= "\t\t{$extension->properties2['name']} extProps { $sType };\n"; 151 | $res .= "\t\tVkPhysicalDeviceProperties2 deviceProps2(initDeviceProperties2(&extProps));\n"; 152 | $res .= "\t\tpfnGetPhysicalDeviceProperties2KHR(device, &deviceProps2);\n"; 153 | // @todo: QVariant vs. QVariant::fromValue 154 | foreach ($extension->properties2->member as $member) { 155 | // Skip types that are not related to feature details 156 | $type = (string)$member->type; 157 | if (in_array($type, ['VkStructureType', 'void'])) { 158 | continue; 159 | } 160 | $name = (string)$member->name; 161 | $vktype = (string)$member->type; 162 | if ($type == "VkExtent2D") { 163 | $res .= "\t\tpushProperty2(extension, \"$name\", QVariant::fromValue(QVariantList({ extProps.$name.width, extProps.$name.height })));\n"; 164 | continue; 165 | } 166 | // Properties may can be arrays 167 | $dim = 0; 168 | if (!empty(trim((string)$member))) { 169 | $m = (string)$member; 170 | // Special case enum in dim 171 | if ($member->enum) { 172 | // @todo: Take size from xml 173 | $enum_dim = 0; 174 | switch ((string)$member->enum) { 175 | case 'VK_UUID_SIZE': 176 | case 'VK_UUID_SIZE_KHR': 177 | $enum_dim = 16; 178 | break; 179 | case 'VK_LUID_SIZE': 180 | case 'VK_LUID_SIZE_HR': 181 | $enum_dim = 8; 182 | break; 183 | } 184 | if ($enum_dim > 0) { 185 | $res .= "\t\tpushProperty2(extension, \"$name\", QVariant::fromValue(arrayToQVariantList(extProps.$name, $enum_dim)));\n"; 186 | continue; 187 | } 188 | } else { 189 | $dim = (int) filter_var($m, FILTER_SANITIZE_NUMBER_INT); 190 | } 191 | } 192 | if ($dim == 0) { 193 | switch($vktype) { 194 | case 'VkBool32': 195 | $qtype = "QVariant(bool(extProps.$name))"; 196 | break; 197 | case 'VkConformanceVersionKHR': 198 | $qtype = "QString::fromStdString(vulkanResources::conformanceVersionKHRString(extProps.$name))"; 199 | break; 200 | case 'VkDeviceSize': 201 | case 'uint64_t': 202 | $qtype = "QVariant::fromValue(extProps.$name)"; 203 | break; 204 | default: 205 | $qtype = "QVariant(extProps.$name)"; 206 | } 207 | $res .= "\t\tpushProperty2(extension, \"$name\", $qtype);\n"; 208 | } else { 209 | $vars = []; 210 | for ($i = 0; $i < $dim; $i++) { 211 | $vars[] = "extProps.".$name."[".$i."]"; 212 | } 213 | $qlist = implode(', ', $vars); 214 | $res .= "\t\tpushProperty2(extension, \"$name\", QVariant::fromValue(QVariantList({ $qlist })));\n"; 215 | } 216 | } 217 | $res .= "\t}\n"; 218 | return $res; 219 | } 220 | 221 | public function addHeaderFunction(string $function_name) { 222 | $this->header_functions[] = $function_name; 223 | } 224 | 225 | public function writeHeader(string $file_name, string $output_dir) { 226 | if (!file_exists('templates/VulkanDeviceInfoExtensions.h')) { 227 | echo "Template $file_name does not exist!\n"; 228 | return; 229 | } 230 | $header_source = file_get_contents('templates/VulkanDeviceInfoExtensions.h'); 231 | $header_replace = ''; 232 | foreach ($this->header_functions as $header_func) { 233 | $header_replace .= " void $header_func();\n"; 234 | } 235 | $header_source = str_replace('{{header_functions}}', $header_replace, $header_source); 236 | $header_source = str_replace('{{VK_HEADER_VERSION}}', $this->vk_header_version, $header_source); 237 | file_put_contents("$output_dir/VulkanDeviceInfoExtensions.h", $header_source); 238 | } 239 | 240 | public function writeImplementation(string $file_name, string $output_dir, $extension_container) { 241 | if (!file_exists('templates/VulkanDeviceInfoExtensions.cpp')) { 242 | echo "Template $file_name does not exist!\n"; 243 | return; 244 | } 245 | $impl_source = file_get_contents('templates/VulkanDeviceInfoExtensions.cpp'); 246 | 247 | $fn_calls = array_map(function ($fn) { return " ".$fn."();"; }, $this->header_functions); 248 | 249 | $function_calls_features2 = array_filter($fn_calls, function ($fn) { return (stripos($fn, "readPhysicalFeatures_") !== false); }); 250 | $impl_source = str_replace('{{implementation_features_function_calls}}', implode("\n", $function_calls_features2), $impl_source); 251 | 252 | $function_calls_properties2 = array_filter($fn_calls, function ($fn) { return (stripos($fn, "readPhysicalProperties_") !== false); }); 253 | $impl_source = str_replace('{{implementation_properties_function_calls}}', implode("\n", $function_calls_properties2), $impl_source); 254 | 255 | // Get extension groups (ext, khr, nv, etc.) 256 | $ext_groups = []; 257 | foreach ($extension_container->extensions as $ext) { 258 | if (!in_array($ext->group, $ext_groups)) { 259 | $ext_groups[] = $ext->group; 260 | } 261 | } 262 | sort($ext_groups); 263 | 264 | $cpp_features_block = ''; 265 | $cpp_properties_block = ''; 266 | foreach ($ext_groups as $ext_group) { 267 | // Features2 268 | $ext_arr = array_filter($extension_container->extensions, function ($ext) use ($ext_group) { return ($ext->group == $ext_group && $ext->features2); }); 269 | if (count($ext_arr) > 0 ) { 270 | $cpp_features_block .= "void VulkanDeviceInfoExtensions::readPhysicalFeatures_$ext_group() {\n"; 271 | foreach ($ext_arr as $extension) { 272 | $cpp_features_block .= $this->generateFeatures2CodeBlock($extension); 273 | } 274 | $cpp_features_block .= "}\n"; 275 | } 276 | // Properties2 277 | $ext_arr = array_filter($extension_container->extensions, function ($ext) use ($ext_group) { return ($ext->group == $ext_group && $ext->properties2); }); 278 | if (count($ext_arr) > 0 ) { 279 | $cpp_properties_block .= "void VulkanDeviceInfoExtensions::readPhysicalProperties_$ext_group() {\n"; 280 | foreach ($ext_arr as $extension) { 281 | $cpp_properties_block .= $this->generateProperties2CodeBlock($extension); 282 | } 283 | $cpp_properties_block .= "}\n"; 284 | } 285 | } 286 | 287 | if (!empty($cpp_features_block)) { 288 | $impl_source = str_replace('{{implementation_features2_functions}}', $cpp_features_block, $impl_source); 289 | } 290 | if (!empty($cpp_properties_block)) { 291 | $impl_source = str_replace('{{implementation_properties2_functions}}', $cpp_properties_block, $impl_source); 292 | } 293 | 294 | file_put_contents("$output_dir/VulkanDeviceInfoExtensions.cpp", $impl_source); 295 | } 296 | 297 | } 298 | 299 | $xml = simplexml_load_file("Vulkan-Docs/xml/vk.xml") or exit("Could not read vk.xml"); 300 | $header_version_node = $xml->xpath("./types/type/name[.='VK_HEADER_VERSION']/.."); 301 | $vk_header_version = filter_var($header_version_node[0], FILTER_SANITIZE_NUMBER_INT); 302 | 303 | $output_dir = "out"; 304 | // $output_dir = "V:/Vulkan/VulkanCapsViewer/"; 305 | if (!is_dir($output_dir)) { 306 | mkdir($output_dir); 307 | } 308 | 309 | echo "Generating C++ files for header version $vk_header_version...\n"; 310 | 311 | $type_container = new TypeContainer($xml); 312 | $extension_container = new ExtensionContainer($xml, $type_container); 313 | 314 | // Get extension groups (ext, khr, nv, etc.) 315 | $ext_groups = []; 316 | foreach ($extension_container->extensions as $ext) { 317 | if (!in_array($ext->group, $ext_groups)) { 318 | $ext_groups[] = $ext->group; 319 | } 320 | } 321 | sort($ext_groups); 322 | 323 | // Generate CPP Code 324 | $cpp_builder = new CppBuilder($vk_header_version); 325 | foreach ($ext_groups as $ext_group) { 326 | // Features2 327 | $ext_arr = array_filter($extension_container->extensions, function ($ext) use ($ext_group) { return ($ext->group == $ext_group && $ext->features2); }); 328 | if (count($ext_arr) > 0 ) { 329 | $cpp_builder->addHeaderFunction("readPhysicalFeatures_$ext_group"); 330 | } 331 | // Properties2 332 | $ext_arr = array_filter($extension_container->extensions, function ($ext) use ($ext_group) { return ($ext->group == $ext_group && $ext->properties2); }); 333 | if (count($ext_arr) > 0 ) { 334 | $cpp_builder->addHeaderFunction("readPhysicalProperties_$ext_group"); 335 | } 336 | } 337 | $cpp_builder->writeHeader("$output_dir/VulkanDeviceInfoExtensions.h", $output_dir); 338 | $cpp_builder->writeImplementation("$output_dir/VulkanDeviceInfoExtensions.cpp", $output_dir, $extension_container); 339 | 340 | // Output extension list for changelog 341 | $extenstion_list_file = $output_dir."/extensionlist.txt"; 342 | if (file_exists($extenstion_list_file)) { 343 | unlink($extenstion_list_file); 344 | } 345 | foreach ($ext_groups as $ext_group) { 346 | $ext_arr = array_filter($extension_container->extensions, function ($ext) use ($ext_group) { return ($ext->group == $ext_group && ($ext->features2 || $ext->properties2)); }); 347 | if (count($ext_arr) > 0) { 348 | file_put_contents($extenstion_list_file, "$ext_group\n", FILE_APPEND); 349 | foreach ($ext_arr as $ext) { 350 | file_put_contents($extenstion_list_file, "$ext->name\n", FILE_APPEND); 351 | } 352 | } 353 | } 354 | 355 | echo "C++ files written to \"/out\""; -------------------------------------------------------------------------------- /templates/VulkanDeviceInfoExtensions.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Vulkan hardware capability viewer 3 | * 4 | * THIS HEADER IS AUTO-GENERATED, DO NOT CHANGE 5 | * See https://github.com/SaschaWillems/VulkanCapsViewerGenerator for the generator 6 | * 7 | * Copyright (C) by Sascha Willems (www.saschawillems.de) 8 | * 9 | * This code is free software, you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License version 3 as published by the Free Software Foundation. 12 | * 13 | * Please review the following information to ensure the GNU Lesser 14 | * General Public License version 3 requirements will be met: 15 | * http://opensource.org/licenses/lgpl-3.0.html 16 | * 17 | * The code is distributed WITHOUT ANY WARRANTY; without even the 18 | * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 19 | * PURPOSE. See the GNU LGPL 3.0 for more details. 20 | */ 21 | 22 | #include "VulkanDeviceInfoExtensions.h" 23 | 24 | bool VulkanDeviceInfoExtensions::extensionSupported(const char* extensionName) 25 | { 26 | for (auto& ext : extensions) { 27 | if (strcmp(ext.extensionName, extensionName) == 0) { 28 | return true; 29 | } 30 | } 31 | return false; 32 | } 33 | 34 | VkPhysicalDeviceProperties2 VulkanDeviceInfoExtensions::initDeviceProperties2(void * pNext) { 35 | VkPhysicalDeviceProperties2 props2{}; 36 | props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; 37 | props2.pNext = pNext; 38 | return props2; 39 | } 40 | 41 | {{implementation_properties2_functions}} 42 | 43 | void VulkanDeviceInfoExtensions::readExtendedProperties() { 44 | {{implementation_properties_function_calls}} 45 | } 46 | 47 | VkPhysicalDeviceFeatures2 VulkanDeviceInfoExtensions::initDeviceFeatures2(void *pNext) { 48 | VkPhysicalDeviceFeatures2 features2{}; 49 | features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; 50 | features2.pNext = pNext; 51 | return features2; 52 | } 53 | 54 | void VulkanDeviceInfoExtensions::pushFeature2(const char* extension, std::string name, bool supported) { 55 | features2.push_back(Feature2(name, supported, extension)); 56 | } 57 | 58 | {{implementation_features2_functions}} 59 | 60 | void VulkanDeviceInfoExtensions::readExtendedFeatures() { 61 | {{implementation_features_function_calls}} 62 | } 63 | -------------------------------------------------------------------------------- /templates/VulkanDeviceInfoExtensions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Vulkan hardware capability viewer 3 | * 4 | * THIS HEADER IS AUTO-GENERATED, DO NOT CHANGE 5 | * See https://github.com/SaschaWillems/VulkanCapsViewerGenerator for the generator 6 | * 7 | * Copyright (C) by Sascha Willems (www.saschawillems.de) 8 | * 9 | * This code is free software, you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License version 3 as published by the Free Software Foundation. 12 | * 13 | * Please review the following information to ensure the GNU Lesser 14 | * General Public License version 3 requirements will be met: 15 | * http://opensource.org/licenses/lgpl-3.0.html 16 | * 17 | * The code is distributed WITHOUT ANY WARRANTY; without even the 18 | * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 19 | * PURPOSE. See the GNU LGPL 3.0 for more details. 20 | */ 21 | 22 | #ifndef VULKANDEVICEINFOEXTENSIONS_H 23 | #define VULKANDEVICEINFOEXTENSIONS_H 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include "vulkan/vulkan.h" 30 | #include "vulkanpfn.h" 31 | #include "vulkanresources.h" 32 | 33 | #ifdef __ANDROID__ 34 | #include 35 | #include "vulkanandroid.h" 36 | #endif 37 | 38 | struct Feature2 { 39 | std::string name; 40 | VkBool32 supported; 41 | const char* extension; 42 | Feature2(std::string n, VkBool32 supp, const char* ext) : name(n), supported(supp), extension(ext) {} 43 | }; 44 | 45 | struct Property2 { 46 | std::string name; 47 | QVariant value; 48 | const char* extension; 49 | Property2(std::string n, QVariant val, const char* ext) : name(n), value(val), extension(ext) {} 50 | }; 51 | 52 | class VulkanDeviceInfoExtensions 53 | { 54 | private: 55 | VkPhysicalDeviceFeatures2 initDeviceFeatures2(void *pNext); 56 | VkPhysicalDeviceProperties2 initDeviceProperties2(void * pNext); 57 | template 58 | void pushProperty2(const char* extension, std::string name, T value) { 59 | properties2.push_back(Property2(name, QVariant(value), extension)); 60 | }; 61 | template 62 | QVariantList arrayToQVariantList(T array, size_t size) { 63 | QVariantList res; 64 | for (size_t i = 0; i < size; i++) { 65 | res.push_back(QVariant(array[i])); 66 | } 67 | return res; 68 | }; 69 | void pushFeature2(const char* extension, std::string name, bool supported); 70 | bool extensionSupported(const char* extensionName); 71 | {{header_functions}} 72 | public: 73 | const uint32_t vkHeaderVersion = {{VK_HEADER_VERSION}}; 74 | std::vector features2; 75 | std::vector properties2; 76 | std::vector extensions; 77 | VkPhysicalDevice device; 78 | void readExtendedFeatures(); 79 | void readExtendedProperties(); 80 | }; 81 | 82 | #endif // VULKANDEVICEINFOEXTENSIONS_H 83 | --------------------------------------------------------------------------------