1bf215546Sopenharmony_ci# Copyright © 2020 Hoe Hao Cheng 2bf215546Sopenharmony_ci# 3bf215546Sopenharmony_ci# Permission is hereby granted, free of charge, to any person obtaining a 4bf215546Sopenharmony_ci# copy of this software and associated documentation files (the "Software"), 5bf215546Sopenharmony_ci# to deal in the Software without restriction, including without limitation 6bf215546Sopenharmony_ci# the rights to use, copy, modify, merge, publish, distribute, sublicense, 7bf215546Sopenharmony_ci# and/or sell copies of the Software, and to permit persons to whom the 8bf215546Sopenharmony_ci# Software is furnished to do so, subject to the following conditions: 9bf215546Sopenharmony_ci# 10bf215546Sopenharmony_ci# The above copyright notice and this permission notice (including the next 11bf215546Sopenharmony_ci# paragraph) shall be included in all copies or substantial portions of the 12bf215546Sopenharmony_ci# Software. 13bf215546Sopenharmony_ci# 14bf215546Sopenharmony_ci# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15bf215546Sopenharmony_ci# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16bf215546Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17bf215546Sopenharmony_ci# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18bf215546Sopenharmony_ci# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19bf215546Sopenharmony_ci# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20bf215546Sopenharmony_ci# IN THE SOFTWARE. 21bf215546Sopenharmony_ci# 22bf215546Sopenharmony_ci 23bf215546Sopenharmony_ciimport re 24bf215546Sopenharmony_cifrom xml.etree import ElementTree 25bf215546Sopenharmony_cifrom typing import List,Tuple 26bf215546Sopenharmony_ci 27bf215546Sopenharmony_ciclass Version: 28bf215546Sopenharmony_ci device_version = (1,0,0) 29bf215546Sopenharmony_ci struct_version = (1,0) 30bf215546Sopenharmony_ci 31bf215546Sopenharmony_ci def __init__(self, version, struct=()): 32bf215546Sopenharmony_ci self.device_version = version 33bf215546Sopenharmony_ci 34bf215546Sopenharmony_ci if not struct: 35bf215546Sopenharmony_ci self.struct_version = (version[0], version[1]) 36bf215546Sopenharmony_ci else: 37bf215546Sopenharmony_ci self.struct_version = struct 38bf215546Sopenharmony_ci 39bf215546Sopenharmony_ci # e.g. "VK_MAKE_VERSION(1,2,0)" 40bf215546Sopenharmony_ci def version(self): 41bf215546Sopenharmony_ci return ("VK_MAKE_VERSION(" 42bf215546Sopenharmony_ci + str(self.device_version[0]) 43bf215546Sopenharmony_ci + "," 44bf215546Sopenharmony_ci + str(self.device_version[1]) 45bf215546Sopenharmony_ci + "," 46bf215546Sopenharmony_ci + str(self.device_version[2]) 47bf215546Sopenharmony_ci + ")") 48bf215546Sopenharmony_ci 49bf215546Sopenharmony_ci # e.g. "10" 50bf215546Sopenharmony_ci def struct(self): 51bf215546Sopenharmony_ci return (str(self.struct_version[0])+str(self.struct_version[1])) 52bf215546Sopenharmony_ci 53bf215546Sopenharmony_ci # the sType of the extension's struct 54bf215546Sopenharmony_ci # e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT 55bf215546Sopenharmony_ci # for VK_EXT_transform_feedback and struct="FEATURES" 56bf215546Sopenharmony_ci def stype(self, struct: str): 57bf215546Sopenharmony_ci return ("VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_" 58bf215546Sopenharmony_ci + str(self.struct_version[0]) + "_" + str(self.struct_version[1]) 59bf215546Sopenharmony_ci + '_' + struct) 60bf215546Sopenharmony_ci 61bf215546Sopenharmony_ciclass Extension: 62bf215546Sopenharmony_ci name = None 63bf215546Sopenharmony_ci alias = None 64bf215546Sopenharmony_ci is_required = False 65bf215546Sopenharmony_ci is_nonstandard = False 66bf215546Sopenharmony_ci enable_conds = None 67bf215546Sopenharmony_ci core_since = None 68bf215546Sopenharmony_ci 69bf215546Sopenharmony_ci # these are specific to zink_device_info.py: 70bf215546Sopenharmony_ci has_properties = False 71bf215546Sopenharmony_ci has_features = False 72bf215546Sopenharmony_ci guard = False 73bf215546Sopenharmony_ci features_promoted = False 74bf215546Sopenharmony_ci properties_promoted = False 75bf215546Sopenharmony_ci 76bf215546Sopenharmony_ci 77bf215546Sopenharmony_ci # these are specific to zink_instance.py: 78bf215546Sopenharmony_ci platform_guard = None 79bf215546Sopenharmony_ci 80bf215546Sopenharmony_ci def __init__(self, name, alias="", required=False, nonstandard=False, 81bf215546Sopenharmony_ci properties=False, features=False, conditions=None, guard=False): 82bf215546Sopenharmony_ci self.name = name 83bf215546Sopenharmony_ci self.alias = alias 84bf215546Sopenharmony_ci self.is_required = required 85bf215546Sopenharmony_ci self.is_nonstandard = nonstandard 86bf215546Sopenharmony_ci self.has_properties = properties 87bf215546Sopenharmony_ci self.has_features = features 88bf215546Sopenharmony_ci self.enable_conds = conditions 89bf215546Sopenharmony_ci self.guard = guard 90bf215546Sopenharmony_ci 91bf215546Sopenharmony_ci if alias == "" and (properties == True or features == True): 92bf215546Sopenharmony_ci raise RuntimeError("alias must be available when properties and/or features are used") 93bf215546Sopenharmony_ci 94bf215546Sopenharmony_ci # e.g.: "VK_EXT_robustness2" -> "robustness2" 95bf215546Sopenharmony_ci def pure_name(self): 96bf215546Sopenharmony_ci return '_'.join(self.name.split('_')[2:]) 97bf215546Sopenharmony_ci 98bf215546Sopenharmony_ci # e.g.: "VK_EXT_robustness2" -> "EXT_robustness2" 99bf215546Sopenharmony_ci def name_with_vendor(self): 100bf215546Sopenharmony_ci return self.name[3:] 101bf215546Sopenharmony_ci 102bf215546Sopenharmony_ci # e.g.: "VK_EXT_robustness2" -> "Robustness2" 103bf215546Sopenharmony_ci def name_in_camel_case(self): 104bf215546Sopenharmony_ci return "".join([x.title() for x in self.name.split('_')[2:]]) 105bf215546Sopenharmony_ci 106bf215546Sopenharmony_ci # e.g.: "VK_EXT_robustness2" -> "VK_EXT_ROBUSTNESS_2" 107bf215546Sopenharmony_ci def name_in_snake_uppercase(self): 108bf215546Sopenharmony_ci def replace(original): 109bf215546Sopenharmony_ci # we do not split the types into two, e.g. INT_32 110bf215546Sopenharmony_ci match_types = re.match(".*(int|float)(8|16|32|64)$", original) 111bf215546Sopenharmony_ci 112bf215546Sopenharmony_ci # do not match win32 113bf215546Sopenharmony_ci match_os = re.match(".*win32$", original) 114bf215546Sopenharmony_ci 115bf215546Sopenharmony_ci # try to match extensions with alphanumeric names, like robustness2 116bf215546Sopenharmony_ci match_alphanumeric = re.match("([a-z]+)(\d+)", original) 117bf215546Sopenharmony_ci 118bf215546Sopenharmony_ci if match_types is not None or match_os is not None: 119bf215546Sopenharmony_ci return original.upper() 120bf215546Sopenharmony_ci 121bf215546Sopenharmony_ci if match_alphanumeric is not None: 122bf215546Sopenharmony_ci return (match_alphanumeric[1].upper() 123bf215546Sopenharmony_ci + '_' 124bf215546Sopenharmony_ci + match_alphanumeric[2]) 125bf215546Sopenharmony_ci 126bf215546Sopenharmony_ci return original.upper() 127bf215546Sopenharmony_ci 128bf215546Sopenharmony_ci replaced = list(map(replace, self.name.split('_'))) 129bf215546Sopenharmony_ci return '_'.join(replaced) 130bf215546Sopenharmony_ci 131bf215546Sopenharmony_ci # e.g.: "VK_EXT_robustness2" -> "ROBUSTNESS_2" 132bf215546Sopenharmony_ci def pure_name_in_snake_uppercase(self): 133bf215546Sopenharmony_ci return '_'.join(self.name_in_snake_uppercase().split('_')[2:]) 134bf215546Sopenharmony_ci 135bf215546Sopenharmony_ci # e.g.: "VK_EXT_robustness2" -> "VK_EXT_ROBUSTNESS_2_EXTENSION_NAME" 136bf215546Sopenharmony_ci def extension_name(self): 137bf215546Sopenharmony_ci return self.name_in_snake_uppercase() + "_EXTENSION_NAME" 138bf215546Sopenharmony_ci 139bf215546Sopenharmony_ci # generate a C string literal for the extension 140bf215546Sopenharmony_ci def extension_name_literal(self): 141bf215546Sopenharmony_ci return '"' + self.name + '"' 142bf215546Sopenharmony_ci 143bf215546Sopenharmony_ci # get the field in zink_device_info that refers to the extension's 144bf215546Sopenharmony_ci # feature/properties struct 145bf215546Sopenharmony_ci # e.g. rb2_<suffix> for VK_EXT_robustness2 146bf215546Sopenharmony_ci def field(self, suffix: str): 147bf215546Sopenharmony_ci return self.alias + '_' + suffix 148bf215546Sopenharmony_ci 149bf215546Sopenharmony_ci def physical_device_struct(self, struct: str): 150bf215546Sopenharmony_ci if self.name_in_camel_case().endswith(struct): 151bf215546Sopenharmony_ci struct = "" 152bf215546Sopenharmony_ci 153bf215546Sopenharmony_ci return ("VkPhysicalDevice" 154bf215546Sopenharmony_ci + self.name_in_camel_case() 155bf215546Sopenharmony_ci + struct 156bf215546Sopenharmony_ci + self.vendor()) 157bf215546Sopenharmony_ci 158bf215546Sopenharmony_ci # the sType of the extension's struct 159bf215546Sopenharmony_ci # e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT 160bf215546Sopenharmony_ci # for VK_EXT_transform_feedback and struct="FEATURES" 161bf215546Sopenharmony_ci def stype(self, struct: str): 162bf215546Sopenharmony_ci return ("VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_" 163bf215546Sopenharmony_ci + self.pure_name_in_snake_uppercase() 164bf215546Sopenharmony_ci + '_' + struct + '_' 165bf215546Sopenharmony_ci + self.vendor()) 166bf215546Sopenharmony_ci 167bf215546Sopenharmony_ci # e.g. EXT in VK_EXT_robustness2 168bf215546Sopenharmony_ci def vendor(self): 169bf215546Sopenharmony_ci return self.name.split('_')[1] 170bf215546Sopenharmony_ci 171bf215546Sopenharmony_ci# Type aliases 172bf215546Sopenharmony_ciLayer = Extension 173bf215546Sopenharmony_ci 174bf215546Sopenharmony_ciclass ExtensionRegistryEntry: 175bf215546Sopenharmony_ci # type of extension - right now it's either "instance" or "device" 176bf215546Sopenharmony_ci ext_type = "" 177bf215546Sopenharmony_ci # the version in which the extension is promoted to core VK 178bf215546Sopenharmony_ci promoted_in = None 179bf215546Sopenharmony_ci # functions added by the extension are referred to as "commands" in the registry 180bf215546Sopenharmony_ci device_commands = None 181bf215546Sopenharmony_ci pdevice_commands = None 182bf215546Sopenharmony_ci instance_commands = None 183bf215546Sopenharmony_ci constants = None 184bf215546Sopenharmony_ci features_struct = None 185bf215546Sopenharmony_ci features_fields = None 186bf215546Sopenharmony_ci features_promoted = False 187bf215546Sopenharmony_ci properties_struct = None 188bf215546Sopenharmony_ci properties_fields = None 189bf215546Sopenharmony_ci properties_promoted = False 190bf215546Sopenharmony_ci # some instance extensions are locked behind certain platforms 191bf215546Sopenharmony_ci platform_guard = "" 192bf215546Sopenharmony_ci 193bf215546Sopenharmony_ciclass ExtensionRegistry: 194bf215546Sopenharmony_ci # key = extension name, value = registry entry 195bf215546Sopenharmony_ci registry = dict() 196bf215546Sopenharmony_ci 197bf215546Sopenharmony_ci def __init__(self, vkxml_path: str): 198bf215546Sopenharmony_ci vkxml = ElementTree.parse(vkxml_path) 199bf215546Sopenharmony_ci 200bf215546Sopenharmony_ci commands_type = dict() 201bf215546Sopenharmony_ci command_aliases = dict() 202bf215546Sopenharmony_ci platform_guards = dict() 203bf215546Sopenharmony_ci struct_aliases = dict() 204bf215546Sopenharmony_ci 205bf215546Sopenharmony_ci for cmd in vkxml.findall("commands/command"): 206bf215546Sopenharmony_ci name = cmd.find("./proto/name") 207bf215546Sopenharmony_ci 208bf215546Sopenharmony_ci if name is not None and name.text: 209bf215546Sopenharmony_ci commands_type[name.text] = cmd.find("./param/type").text 210bf215546Sopenharmony_ci elif cmd.get("name") is not None: 211bf215546Sopenharmony_ci command_aliases[cmd.get("name")] = cmd.get("alias") 212bf215546Sopenharmony_ci 213bf215546Sopenharmony_ci for typ in vkxml.findall("types/type"): 214bf215546Sopenharmony_ci if typ.get("category") != "struct": 215bf215546Sopenharmony_ci continue 216bf215546Sopenharmony_ci 217bf215546Sopenharmony_ci name = typ.get("name") 218bf215546Sopenharmony_ci alias = typ.get("alias") 219bf215546Sopenharmony_ci 220bf215546Sopenharmony_ci if name and alias: 221bf215546Sopenharmony_ci struct_aliases[name] = alias 222bf215546Sopenharmony_ci 223bf215546Sopenharmony_ci for (cmd, alias) in command_aliases.items(): 224bf215546Sopenharmony_ci commands_type[cmd] = commands_type[alias] 225bf215546Sopenharmony_ci 226bf215546Sopenharmony_ci for platform in vkxml.findall("platforms/platform"): 227bf215546Sopenharmony_ci name = platform.get("name") 228bf215546Sopenharmony_ci guard = platform.get("protect") 229bf215546Sopenharmony_ci platform_guards[name] = guard 230bf215546Sopenharmony_ci 231bf215546Sopenharmony_ci for ext in vkxml.findall("extensions/extension"): 232bf215546Sopenharmony_ci # Reserved extensions are marked with `supported="disabled"` 233bf215546Sopenharmony_ci if ext.get("supported") == "disabled": 234bf215546Sopenharmony_ci continue 235bf215546Sopenharmony_ci 236bf215546Sopenharmony_ci name = ext.attrib["name"] 237bf215546Sopenharmony_ci 238bf215546Sopenharmony_ci entry = ExtensionRegistryEntry() 239bf215546Sopenharmony_ci entry.ext_type = ext.attrib["type"] 240bf215546Sopenharmony_ci entry.promoted_in = self.parse_promotedto(ext.get("promotedto")) 241bf215546Sopenharmony_ci 242bf215546Sopenharmony_ci entry.device_commands = [] 243bf215546Sopenharmony_ci entry.pdevice_commands = [] 244bf215546Sopenharmony_ci entry.instance_commands = [] 245bf215546Sopenharmony_ci entry.features_fields = [] 246bf215546Sopenharmony_ci entry.properties_fields = [] 247bf215546Sopenharmony_ci 248bf215546Sopenharmony_ci for cmd in ext.findall("require/command"): 249bf215546Sopenharmony_ci cmd_name = cmd.get("name") 250bf215546Sopenharmony_ci if cmd_name: 251bf215546Sopenharmony_ci if commands_type[cmd_name] in ("VkDevice", "VkCommandBuffer", "VkQueue"): 252bf215546Sopenharmony_ci entry.device_commands.append(cmd_name) 253bf215546Sopenharmony_ci elif commands_type[cmd_name] in ("VkPhysicalDevice"): 254bf215546Sopenharmony_ci entry.pdevice_commands.append(cmd_name) 255bf215546Sopenharmony_ci else: 256bf215546Sopenharmony_ci entry.instance_commands.append(cmd_name) 257bf215546Sopenharmony_ci 258bf215546Sopenharmony_ci entry.constants = [] 259bf215546Sopenharmony_ci for enum in ext.findall("require/enum"): 260bf215546Sopenharmony_ci enum_name = enum.get("name") 261bf215546Sopenharmony_ci enum_extends = enum.get("extends") 262bf215546Sopenharmony_ci # we are only interested in VK_*_EXTENSION_NAME, which does not 263bf215546Sopenharmony_ci # have an "extends" attribute 264bf215546Sopenharmony_ci if not enum_extends: 265bf215546Sopenharmony_ci entry.constants.append(enum_name) 266bf215546Sopenharmony_ci 267bf215546Sopenharmony_ci for ty in ext.findall("require/type"): 268bf215546Sopenharmony_ci ty_name = ty.get("name") 269bf215546Sopenharmony_ci if (self.is_features_struct(ty_name) and 270bf215546Sopenharmony_ci entry.features_struct is None): 271bf215546Sopenharmony_ci entry.features_struct = ty_name 272bf215546Sopenharmony_ci 273bf215546Sopenharmony_ci elif (self.is_properties_struct(ty_name) and 274bf215546Sopenharmony_ci entry.properties_struct is None): 275bf215546Sopenharmony_ci entry.properties_struct = ty_name 276bf215546Sopenharmony_ci 277bf215546Sopenharmony_ci if entry.features_struct: 278bf215546Sopenharmony_ci struct_name = entry.features_struct 279bf215546Sopenharmony_ci if entry.features_struct in struct_aliases: 280bf215546Sopenharmony_ci struct_name = struct_aliases[entry.features_struct] 281bf215546Sopenharmony_ci entry.features_promoted = True 282bf215546Sopenharmony_ci 283bf215546Sopenharmony_ci elif entry.promoted_in is not None: 284bf215546Sopenharmony_ci # if the extension is promoted but a core-Vulkan alias is not 285bf215546Sopenharmony_ci # available for the features, then consider the features struct 286bf215546Sopenharmony_ci # non-core-promoted 287bf215546Sopenharmony_ci entry.features_promoted = False 288bf215546Sopenharmony_ci 289bf215546Sopenharmony_ci for field in vkxml.findall("./types/type[@name='{}']/member".format(struct_name)): 290bf215546Sopenharmony_ci field_name = field.find("name").text 291bf215546Sopenharmony_ci 292bf215546Sopenharmony_ci # we ignore sType and pNext since they are irrelevant 293bf215546Sopenharmony_ci if field_name not in ["sType", "pNext"]: 294bf215546Sopenharmony_ci entry.features_fields.append(field_name) 295bf215546Sopenharmony_ci 296bf215546Sopenharmony_ci if entry.properties_struct: 297bf215546Sopenharmony_ci struct_name = entry.properties_struct 298bf215546Sopenharmony_ci if entry.properties_struct in struct_aliases: 299bf215546Sopenharmony_ci struct_name = struct_aliases[entry.properties_struct] 300bf215546Sopenharmony_ci entry.properties_promoted = True 301bf215546Sopenharmony_ci 302bf215546Sopenharmony_ci elif entry.promoted_in is not None: 303bf215546Sopenharmony_ci # if the extension is promoted but a core-Vulkan alias is not 304bf215546Sopenharmony_ci # available for the properties, then it is not promoted to core 305bf215546Sopenharmony_ci entry.properties_promoted = False 306bf215546Sopenharmony_ci 307bf215546Sopenharmony_ci for field in vkxml.findall("./types/type[@name='{}']/member".format(struct_name)): 308bf215546Sopenharmony_ci field_name = field.find("name").text 309bf215546Sopenharmony_ci 310bf215546Sopenharmony_ci # we ignore sType and pNext since they are irrelevant 311bf215546Sopenharmony_ci if field_name not in ["sType", "pNext"]: 312bf215546Sopenharmony_ci entry.properties_fields.append(field_name) 313bf215546Sopenharmony_ci 314bf215546Sopenharmony_ci if ext.get("platform") is not None: 315bf215546Sopenharmony_ci entry.platform_guard = platform_guards[ext.get("platform")] 316bf215546Sopenharmony_ci 317bf215546Sopenharmony_ci self.registry[name] = entry 318bf215546Sopenharmony_ci 319bf215546Sopenharmony_ci def in_registry(self, ext_name: str): 320bf215546Sopenharmony_ci return ext_name in self.registry 321bf215546Sopenharmony_ci 322bf215546Sopenharmony_ci def get_registry_entry(self, ext_name: str): 323bf215546Sopenharmony_ci if self.in_registry(ext_name): 324bf215546Sopenharmony_ci return self.registry[ext_name] 325bf215546Sopenharmony_ci 326bf215546Sopenharmony_ci # Parses e.g. "VK_VERSION_x_y" to integer tuple (x, y) 327bf215546Sopenharmony_ci # For any erroneous inputs, None is returned 328bf215546Sopenharmony_ci def parse_promotedto(self, promotedto: str): 329bf215546Sopenharmony_ci result = None 330bf215546Sopenharmony_ci 331bf215546Sopenharmony_ci if promotedto and promotedto.startswith("VK_VERSION_"): 332bf215546Sopenharmony_ci (major, minor) = promotedto.split('_')[-2:] 333bf215546Sopenharmony_ci result = (int(major), int(minor)) 334bf215546Sopenharmony_ci 335bf215546Sopenharmony_ci return result 336bf215546Sopenharmony_ci 337bf215546Sopenharmony_ci def is_features_struct(self, struct: str): 338bf215546Sopenharmony_ci return re.match(r"VkPhysicalDevice.*Features.*", struct) is not None 339bf215546Sopenharmony_ci 340bf215546Sopenharmony_ci def is_properties_struct(self, struct: str): 341bf215546Sopenharmony_ci return re.match(r"VkPhysicalDevice.*Properties.*", struct) is not None 342