1# Copyright © 2020 Hoe Hao Cheng 2# 3# Permission is hereby granted, free of charge, to any person obtaining a 4# copy of this software and associated documentation files (the "Software"), 5# to deal in the Software without restriction, including without limitation 6# the rights to use, copy, modify, merge, publish, distribute, sublicense, 7# and/or sell copies of the Software, and to permit persons to whom the 8# Software is furnished to do so, subject to the following conditions: 9# 10# The above copyright notice and this permission notice (including the next 11# paragraph) shall be included in all copies or substantial portions of the 12# Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20# IN THE SOFTWARE. 21# 22 23import re 24from xml.etree import ElementTree 25from typing import List,Tuple 26 27class Version: 28 device_version = (1,0,0) 29 struct_version = (1,0) 30 31 def __init__(self, version, struct=()): 32 self.device_version = version 33 34 if not struct: 35 self.struct_version = (version[0], version[1]) 36 else: 37 self.struct_version = struct 38 39 # e.g. "VK_MAKE_VERSION(1,2,0)" 40 def version(self): 41 return ("VK_MAKE_VERSION(" 42 + str(self.device_version[0]) 43 + "," 44 + str(self.device_version[1]) 45 + "," 46 + str(self.device_version[2]) 47 + ")") 48 49 # e.g. "10" 50 def struct(self): 51 return (str(self.struct_version[0])+str(self.struct_version[1])) 52 53 # the sType of the extension's struct 54 # e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT 55 # for VK_EXT_transform_feedback and struct="FEATURES" 56 def stype(self, struct: str): 57 return ("VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_" 58 + str(self.struct_version[0]) + "_" + str(self.struct_version[1]) 59 + '_' + struct) 60 61class Extension: 62 name = None 63 alias = None 64 is_required = False 65 is_nonstandard = False 66 enable_conds = None 67 core_since = None 68 69 # these are specific to zink_device_info.py: 70 has_properties = False 71 has_features = False 72 guard = False 73 74 # these are specific to zink_instance.py: 75 platform_guard = None 76 77 def __init__(self, name, alias="", required=False, nonstandard=False, 78 properties=False, features=False, conditions=None, guard=False): 79 self.name = name 80 self.alias = alias 81 self.is_required = required 82 self.is_nonstandard = nonstandard 83 self.has_properties = properties 84 self.has_features = features 85 self.enable_conds = conditions 86 self.guard = guard 87 88 if alias == "" and (properties == True or features == True): 89 raise RuntimeError("alias must be available when properties and/or features are used") 90 91 # e.g.: "VK_EXT_robustness2" -> "robustness2" 92 def pure_name(self): 93 return '_'.join(self.name.split('_')[2:]) 94 95 # e.g.: "VK_EXT_robustness2" -> "EXT_robustness2" 96 def name_with_vendor(self): 97 return self.name[3:] 98 99 # e.g.: "VK_EXT_robustness2" -> "Robustness2" 100 def name_in_camel_case(self): 101 return "".join([x.title() for x in self.name.split('_')[2:]]) 102 103 # e.g.: "VK_EXT_robustness2" -> "VK_EXT_ROBUSTNESS2_EXTENSION_NAME" 104 # do note that inconsistencies exist, i.e. we have 105 # VK_EXT_ROBUSTNESS_2_EXTENSION_NAME defined in the headers, but then 106 # we also have VK_KHR_MAINTENANCE1_EXTENSION_NAME 107 def extension_name(self): 108 return self.name.upper() + "_EXTENSION_NAME" 109 110 # generate a C string literal for the extension 111 def extension_name_literal(self): 112 return '"' + self.name + '"' 113 114 # get the field in zink_device_info that refers to the extension's 115 # feature/properties struct 116 # e.g. rb2_<suffix> for VK_EXT_robustness2 117 def field(self, suffix: str): 118 return self.alias + '_' + suffix 119 120 def physical_device_struct(self, struct: str): 121 if self.name_in_camel_case().endswith(struct): 122 struct = "" 123 124 return ("VkPhysicalDevice" 125 + self.name_in_camel_case() 126 + struct 127 + self.vendor()) 128 129 # the sType of the extension's struct 130 # e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT 131 # for VK_EXT_transform_feedback and struct="FEATURES" 132 def stype(self, struct: str): 133 return ("VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_" 134 + self.pure_name().upper() 135 + '_' + struct + '_' 136 + self.vendor()) 137 138 # e.g. EXT in VK_EXT_robustness2 139 def vendor(self): 140 return self.name.split('_')[1] 141 142# Type aliases 143Layer = Extension 144 145class ExtensionRegistryEntry: 146 # type of extension - right now it's either "instance" or "device" 147 ext_type = "" 148 # the version in which the extension is promoted to core VK 149 promoted_in = None 150 # functions added by the extension are referred to as "commands" in the registry 151 device_commands = None 152 pdevice_commands = None 153 instance_commands = None 154 constants = None 155 features_struct = None 156 properties_struct = None 157 # some instance extensions are locked behind certain platforms 158 platform_guard = "" 159 160class ExtensionRegistry: 161 # key = extension name, value = registry entry 162 registry = dict() 163 164 def __init__(self, vkxml_path: str): 165 vkxml = ElementTree.parse(vkxml_path) 166 167 commands_type = dict() 168 aliases = dict() 169 platform_guards = dict() 170 171 for cmd in vkxml.findall("commands/command"): 172 name = cmd.find("./proto/name") 173 174 if name is not None and name.text: 175 commands_type[name.text] = cmd.find("./param/type").text 176 elif cmd.get("name") is not None: 177 aliases[cmd.get("name")] = cmd.get("alias") 178 179 for (cmd, alias) in aliases.items(): 180 commands_type[cmd] = commands_type[alias] 181 182 for platform in vkxml.findall("platforms/platform"): 183 name = platform.get("name") 184 guard = platform.get("protect") 185 platform_guards[name] = guard 186 187 for ext in vkxml.findall("extensions/extension"): 188 # Reserved extensions are marked with `supported="disabled"` 189 if ext.get("supported") == "disabled": 190 continue 191 192 name = ext.attrib["name"] 193 194 entry = ExtensionRegistryEntry() 195 entry.ext_type = ext.attrib["type"] 196 entry.promoted_in = self.parse_promotedto(ext.get("promotedto")) 197 198 entry.device_commands = [] 199 entry.pdevice_commands = [] 200 entry.instance_commands = [] 201 202 for cmd in ext.findall("require/command"): 203 cmd_name = cmd.get("name") 204 if cmd_name: 205 if commands_type[cmd_name] in ("VkDevice", "VkCommandBuffer", "VkQueue"): 206 entry.device_commands.append(cmd_name) 207 elif commands_type[cmd_name] in ("VkPhysicalDevice"): 208 entry.pdevice_commands.append(cmd_name) 209 else: 210 entry.instance_commands.append(cmd_name) 211 212 entry.constants = [] 213 for enum in ext.findall("require/enum"): 214 enum_name = enum.get("name") 215 enum_extends = enum.get("extends") 216 # we are only interested in VK_*_EXTENSION_NAME, which does not 217 # have an "extends" attribute 218 if not enum_extends: 219 entry.constants.append(enum_name) 220 221 for ty in ext.findall("require/type"): 222 ty_name = ty.get("name") 223 if (self.is_features_struct(ty_name) and 224 entry.features_struct is None): 225 entry.features_struct = ty_name 226 elif (self.is_properties_struct(ty_name) and 227 entry.properties_struct is None): 228 entry.properties_struct = ty_name 229 230 if ext.get("platform") is not None: 231 entry.platform_guard = platform_guards[ext.get("platform")] 232 233 self.registry[name] = entry 234 235 def in_registry(self, ext_name: str): 236 return ext_name in self.registry 237 238 def get_registry_entry(self, ext_name: str): 239 if self.in_registry(ext_name): 240 return self.registry[ext_name] 241 242 # Parses e.g. "VK_VERSION_x_y" to integer tuple (x, y) 243 # For any erroneous inputs, None is returned 244 def parse_promotedto(self, promotedto: str): 245 result = None 246 247 if promotedto and promotedto.startswith("VK_VERSION_"): 248 (major, minor) = promotedto.split('_')[-2:] 249 result = (int(major), int(minor)) 250 251 return result 252 253 def is_features_struct(self, struct: str): 254 return re.match(r"VkPhysicalDevice.*Features.*", struct) is not None 255 256 def is_properties_struct(self, struct: str): 257 return re.match(r"VkPhysicalDevice.*Properties.*", struct) is not None 258