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 def __init__(self, name, alias="", required=False, nonstandard=False, 75 properties=False, features=False, conditions=None, guard=False, 76 core_since=None): 77 self.name = name 78 self.alias = alias 79 self.is_required = required 80 self.is_nonstandard = nonstandard 81 self.has_properties = properties 82 self.has_features = features 83 self.enable_conds = conditions 84 self.guard = guard 85 self.core_since = core_since 86 87 if alias == "" and (properties == True or features == True): 88 raise RuntimeError("alias must be available when properties and/or features are used") 89 90 # e.g.: "VK_EXT_robustness2" -> "robustness2" 91 def pure_name(self): 92 return '_'.join(self.name.split('_')[2:]) 93 94 # e.g.: "VK_EXT_robustness2" -> "EXT_robustness2" 95 def name_with_vendor(self): 96 return self.name[3:] 97 98 # e.g.: "VK_EXT_robustness2" -> "Robustness2" 99 def name_in_camel_case(self): 100 return "".join([x.title() for x in self.name.split('_')[2:]]) 101 102 # e.g.: "VK_EXT_robustness2" -> "VK_EXT_ROBUSTNESS2_EXTENSION_NAME" 103 # do note that inconsistencies exist, i.e. we have 104 # VK_EXT_ROBUSTNESS_2_EXTENSION_NAME defined in the headers, but then 105 # we also have VK_KHR_MAINTENANCE1_EXTENSION_NAME 106 def extension_name(self): 107 return self.name.upper() + "_EXTENSION_NAME" 108 109 # generate a C string literal for the extension 110 def extension_name_literal(self): 111 return '"' + self.name + '"' 112 113 # get the field in zink_device_info that refers to the extension's 114 # feature/properties struct 115 # e.g. rb2_<suffix> for VK_EXT_robustness2 116 def field(self, suffix: str): 117 return self.alias + '_' + suffix 118 119 def physical_device_struct(self, struct: str): 120 if self.name_in_camel_case().endswith(struct): 121 struct = "" 122 123 return ("VkPhysicalDevice" 124 + self.name_in_camel_case() 125 + struct 126 + self.vendor()) 127 128 # the sType of the extension's struct 129 # e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT 130 # for VK_EXT_transform_feedback and struct="FEATURES" 131 def stype(self, struct: str): 132 return ("VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_" 133 + self.pure_name().upper() 134 + '_' + struct + '_' 135 + self.vendor()) 136 137 # e.g. EXT in VK_EXT_robustness2 138 def vendor(self): 139 return self.name.split('_')[1] 140 141# Type aliases 142Layer = Extension 143 144class ExtensionRegistryEntry: 145 # type of extension - right now it's either "instance" or "device" 146 ext_type = "" 147 # the version in which the extension is promoted to core VK 148 promoted_in = None 149 # functions added by the extension are referred to as "commands" in the registry 150 device_commands = None 151 pdevice_commands = None 152 instance_commands = None 153 constants = None 154 features_struct = None 155 properties_struct = None 156 157class ExtensionRegistry: 158 # key = extension name, value = registry entry 159 registry = dict() 160 161 def __init__(self, vkxml_path: str): 162 vkxml = ElementTree.parse(vkxml_path) 163 164 commands_type = dict() 165 aliases = dict() 166 167 for cmd in vkxml.findall("commands/command"): 168 name = cmd.find("./proto/name") 169 170 if name is not None and name.text: 171 commands_type[name.text] = cmd.find("./param/type").text 172 elif cmd.get("name") is not None: 173 aliases[cmd.get("name")] = cmd.get("alias") 174 175 for (cmd, alias) in aliases.items(): 176 commands_type[cmd] = commands_type[alias] 177 178 for ext in vkxml.findall("extensions/extension"): 179 # Reserved extensions are marked with `supported="disabled"` 180 if ext.get("supported") == "disabled": 181 continue 182 183 name = ext.attrib["name"] 184 185 entry = ExtensionRegistryEntry() 186 entry.ext_type = ext.attrib["type"] 187 entry.promoted_in = self.parse_promotedto(ext.get("promotedto")) 188 189 entry.device_commands = [] 190 entry.pdevice_commands = [] 191 entry.instance_commands = [] 192 193 for cmd in ext.findall("require/command"): 194 cmd_name = cmd.get("name") 195 if cmd_name: 196 if commands_type[cmd_name] in ("VkDevice", "VkCommandBuffer", "VkQueue"): 197 entry.device_commands.append(cmd_name) 198 elif commands_type[cmd_name] in ("VkPhysicalDevice"): 199 entry.pdevice_commands.append(cmd_name) 200 else: 201 entry.instance_commands.append(cmd_name) 202 203 entry.constants = [] 204 for enum in ext.findall("require/enum"): 205 enum_name = enum.get("name") 206 enum_extends = enum.get("extends") 207 # we are only interested in VK_*_EXTENSION_NAME, which does not 208 # have an "extends" attribute 209 if not enum_extends: 210 entry.constants.append(enum_name) 211 212 for ty in ext.findall("require/type"): 213 ty_name = ty.get("name") 214 if (self.is_features_struct(ty_name) and 215 entry.features_struct is None): 216 entry.features_struct = ty_name 217 elif (self.is_properties_struct(ty_name) and 218 entry.properties_struct is None): 219 entry.properties_struct = ty_name 220 221 self.registry[name] = entry 222 223 def in_registry(self, ext_name: str): 224 return ext_name in self.registry 225 226 def get_registry_entry(self, ext_name: str): 227 if self.in_registry(ext_name): 228 return self.registry[ext_name] 229 230 # Parses e.g. "VK_VERSION_x_y" to integer tuple (x, y) 231 # For any erroneous inputs, None is returned 232 def parse_promotedto(self, promotedto: str): 233 result = None 234 235 if promotedto and promotedto.startswith("VK_VERSION_"): 236 (major, minor) = promotedto.split('_')[-2:] 237 result = (int(major), int(minor)) 238 239 return result 240 241 def is_features_struct(self, struct: str): 242 return re.match(r"VkPhysicalDevice.*Features.*", struct) is not None 243 244 def is_properties_struct(self, struct: str): 245 return re.match(r"VkPhysicalDevice.*Properties.*", struct) is not None 246