Mercurial > games > semicongine
comparison fuhtark_test/Vulkan-Headers-1.4.334/registry/reg.py @ 1501:f40d9d814c08 default tip main
did: correct vulkan-api generator
| author | sam <sam@basx.dev> |
|---|---|
| date | Wed, 26 Nov 2025 23:34:29 +0700 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1500:91c8c3b7cbf0 | 1501:f40d9d814c08 |
|---|---|
| 1 #!/usr/bin/env python3 -i | |
| 2 # | |
| 3 # Copyright 2013-2025 The Khronos Group Inc. | |
| 4 # | |
| 5 # SPDX-License-Identifier: Apache-2.0 | |
| 6 | |
| 7 """Types and classes for manipulating an API registry.""" | |
| 8 | |
| 9 import copy | |
| 10 import re | |
| 11 import sys | |
| 12 import xml.etree.ElementTree as etree | |
| 13 from collections import defaultdict, deque, namedtuple | |
| 14 | |
| 15 from generator import GeneratorOptions, OutputGenerator, noneStr, write | |
| 16 from apiconventions import APIConventions | |
| 17 | |
| 18 def apiNameMatch(str, supported): | |
| 19 """Return whether a required api name matches a pattern specified for an | |
| 20 XML <feature> 'api' attribute or <extension> 'supported' attribute. | |
| 21 | |
| 22 - str - API name such as 'vulkan' or 'openxr'. May be None, in which | |
| 23 case it never matches (this should not happen). | |
| 24 - supported - comma-separated list of XML API names. May be None, in | |
| 25 which case str always matches (this is the usual case).""" | |
| 26 | |
| 27 if str is not None: | |
| 28 return supported is None or str in supported.split(',') | |
| 29 | |
| 30 # Fallthrough case - either str is None or the test failed | |
| 31 return False | |
| 32 | |
| 33 def matchAPIProfile(api, profile, elem): | |
| 34 """Return whether an API and profile | |
| 35 being generated matches an element's profile | |
| 36 | |
| 37 - api - string naming the API to match | |
| 38 - profile - string naming the profile to match | |
| 39 - elem - Element which (may) have 'api' and 'profile' | |
| 40 attributes to match to. | |
| 41 | |
| 42 If a tag is not present in the Element, the corresponding API | |
| 43 or profile always matches. | |
| 44 | |
| 45 Otherwise, the tag must exactly match the API or profile. | |
| 46 | |
| 47 Thus, if 'profile' = core: | |
| 48 | |
| 49 - `<remove>` with no attribute will match | |
| 50 - `<remove profile="core">` will match | |
| 51 - `<remove profile="compatibility">` will not match | |
| 52 | |
| 53 Possible match conditions: | |
| 54 | |
| 55 ``` | |
| 56 Requested Element | |
| 57 Profile Profile | |
| 58 --------- -------- | |
| 59 None None Always matches | |
| 60 'string' None Always matches | |
| 61 None 'string' Does not match. Cannot generate multiple APIs | |
| 62 or profiles, so if an API/profile constraint | |
| 63 is present, it must be asked for explicitly. | |
| 64 'string' 'string' Strings must match | |
| 65 ``` | |
| 66 | |
| 67 ** In the future, we will allow regexes for the attributes, | |
| 68 not just strings, so that `api="^(gl|gles2)"` will match. Even | |
| 69 this is not really quite enough, we might prefer something | |
| 70 like `"gl(core)|gles1(common-lite)"`.""" | |
| 71 # Match 'api', if present | |
| 72 elem_api = elem.get('api') | |
| 73 if elem_api: | |
| 74 if api is None: | |
| 75 raise UserWarning("No API requested, but 'api' attribute is present with value '" | |
| 76 + elem_api + "'") | |
| 77 elif api != elem_api: | |
| 78 # Requested API does not match attribute | |
| 79 return False | |
| 80 elem_profile = elem.get('profile') | |
| 81 if elem_profile: | |
| 82 if profile is None: | |
| 83 raise UserWarning("No profile requested, but 'profile' attribute is present with value '" | |
| 84 + elem_profile + "'") | |
| 85 elif profile != elem_profile: | |
| 86 # Requested profile does not match attribute | |
| 87 return False | |
| 88 return True | |
| 89 | |
| 90 | |
| 91 def mergeAPIs(tree, fromApiNames, toApiName): | |
| 92 """Merge multiple APIs using the precedence order specified in apiNames. | |
| 93 Also deletes <remove> elements. | |
| 94 | |
| 95 tree - Element at the root of the hierarchy to merge. | |
| 96 apiNames - list of strings of API names.""" | |
| 97 | |
| 98 stack = deque() | |
| 99 stack.append(tree) | |
| 100 | |
| 101 while len(stack) > 0: | |
| 102 parent = stack.pop() | |
| 103 | |
| 104 for child in parent.findall('*'): | |
| 105 if child.tag == 'remove': | |
| 106 # Remove <remove> elements | |
| 107 parent.remove(child) | |
| 108 else: | |
| 109 stack.append(child) | |
| 110 | |
| 111 supportedList = child.get('supported') | |
| 112 if supportedList: | |
| 113 supportedList = supportedList.split(',') | |
| 114 for apiName in [toApiName] + fromApiNames: | |
| 115 if apiName in supportedList: | |
| 116 child.set('supported', toApiName) | |
| 117 | |
| 118 if child.get('api'): | |
| 119 definitionName = None | |
| 120 definitionVariants = [] | |
| 121 | |
| 122 # Keep only one definition with the same name if there are multiple definitions | |
| 123 if child.tag in ['type']: | |
| 124 if child.get('name') is not None: | |
| 125 definitionName = child.get('name') | |
| 126 definitionVariants = parent.findall(f"{child.tag}[@name='{definitionName}']") | |
| 127 else: | |
| 128 definitionName = child.find('name').text | |
| 129 definitionVariants = parent.findall(f"{child.tag}/name[.='{definitionName}']/..") | |
| 130 elif child.tag in ['member', 'param']: | |
| 131 definitionName = child.find('name').text | |
| 132 definitionVariants = parent.findall(f"{child.tag}/name[.='{definitionName}']/..") | |
| 133 elif child.tag in ['enum', 'feature']: | |
| 134 definitionName = child.get('name') | |
| 135 definitionVariants = parent.findall(f"{child.tag}[@name='{definitionName}']") | |
| 136 elif child.tag in ['require']: | |
| 137 # No way to correlate require tags because they do not have a definite identifier in the way they | |
| 138 # are used in the latest forms of the XML so the best we can do is simply enable all of them | |
| 139 if child.get('api') in fromApiNames: | |
| 140 child.set('api', toApiName) | |
| 141 elif child.tag in ['command']: | |
| 142 definitionName = child.find('proto/name').text | |
| 143 definitionVariants = parent.findall(f"{child.tag}/proto/name[.='{definitionName}']/../..") | |
| 144 | |
| 145 if definitionName: | |
| 146 bestMatchApi = None | |
| 147 requires = None | |
| 148 for apiName in [toApiName] + fromApiNames: | |
| 149 for variant in definitionVariants: | |
| 150 # Keep any requires attributes from the target API | |
| 151 if variant.get('requires') and variant.get('api') == apiName: | |
| 152 requires = variant.get('requires') | |
| 153 # Find the best matching definition | |
| 154 if apiName in variant.get('api').split(',') and bestMatchApi is None: | |
| 155 bestMatchApi = variant.get('api') | |
| 156 | |
| 157 if bestMatchApi: | |
| 158 for variant in definitionVariants: | |
| 159 if variant.get('api') != bestMatchApi: | |
| 160 # Only keep best matching definition | |
| 161 parent.remove(variant) | |
| 162 else: | |
| 163 # Add requires attribute from the target API if it is not overridden | |
| 164 if requires is not None and variant.get('requires') is None: | |
| 165 variant.set('requires', requires) | |
| 166 variant.set('api', toApiName) | |
| 167 | |
| 168 | |
| 169 def mergeInternalFeatures(tree, apiName): | |
| 170 """Merge internal API features (apitype='internal') into their public dependents. | |
| 171 | |
| 172 This processes the tree to find features marked with apitype='internal' and merges | |
| 173 their <require>, <deprecate>, and <remove> blocks into the first public feature | |
| 174 that depends on them. After merging, the internal features are removed from the tree. | |
| 175 | |
| 176 tree - Element at the root of the hierarchy (typically <registry>) | |
| 177 apiName - the API name to process (e.g., 'vulkan', 'vulkansc') | |
| 178 """ | |
| 179 import copy | |
| 180 | |
| 181 # Find all features in the tree | |
| 182 features = tree.findall('feature') | |
| 183 | |
| 184 # Separate internal and public features | |
| 185 internal_features = [] | |
| 186 public_features = [] | |
| 187 | |
| 188 for feature in features: | |
| 189 api = feature.get('api', '') | |
| 190 apitype = feature.get('apitype', '') | |
| 191 | |
| 192 # Only process features matching the target API | |
| 193 if apiName not in api.split(','): | |
| 194 continue | |
| 195 | |
| 196 if apitype == 'internal': | |
| 197 internal_features.append(feature) | |
| 198 else: | |
| 199 public_features.append(feature) | |
| 200 | |
| 201 # Build a simple dependency map from the 'depends' attributes | |
| 202 def get_dependencies(feature): | |
| 203 """Extract all dependencies from a feature's depends attribute.""" | |
| 204 depends = feature.get('depends', '') | |
| 205 if not depends: | |
| 206 return set() | |
| 207 # Parse the depends expression - for simplicity, extract feature names | |
| 208 # Dependencies can be like "VK_VERSION_1_0" or "VK_VERSION_1_0+VK_KHR_feature" | |
| 209 deps = set() | |
| 210 # Split on + and , to get individual dependencies | |
| 211 for dep in depends.replace('+', ',').split(','): | |
| 212 dep = dep.strip() | |
| 213 if dep: | |
| 214 deps.add(dep) | |
| 215 return deps | |
| 216 | |
| 217 def has_dependency(feature, target_name, all_features_map, visited=None): | |
| 218 """Check if feature depends on target_name (directly or transitively).""" | |
| 219 if visited is None: | |
| 220 visited = set() | |
| 221 | |
| 222 feature_name = feature.get('name') | |
| 223 if feature_name in visited: | |
| 224 return False | |
| 225 visited.add(feature_name) | |
| 226 | |
| 227 deps = get_dependencies(feature) | |
| 228 if target_name in deps: | |
| 229 return True | |
| 230 | |
| 231 # Check transitive dependencies | |
| 232 for dep_name in deps: | |
| 233 if dep_name in all_features_map: | |
| 234 if has_dependency(all_features_map[dep_name], target_name, all_features_map, visited): | |
| 235 return True | |
| 236 return False | |
| 237 | |
| 238 # Create a map of all features for dependency lookups | |
| 239 all_features_map = {f.get('name'): f for f in public_features + internal_features} | |
| 240 | |
| 241 # For each internal feature, find its first public dependent and merge | |
| 242 for internal_feature in internal_features: | |
| 243 internal_name = internal_feature.get('name') | |
| 244 target_feature = None | |
| 245 | |
| 246 # Find the first public feature that depends on this internal feature | |
| 247 for public_feature in public_features: | |
| 248 if has_dependency(public_feature, internal_name, all_features_map): | |
| 249 target_feature = public_feature | |
| 250 break | |
| 251 | |
| 252 if target_feature is not None: | |
| 253 # Merge require blocks | |
| 254 for require in internal_feature.findall('require'): | |
| 255 require_copy = copy.deepcopy(require) | |
| 256 target_feature.append(require_copy) | |
| 257 | |
| 258 # Merge deprecate blocks | |
| 259 for deprecate in internal_feature.findall('deprecate'): | |
| 260 deprecate_copy = copy.deepcopy(deprecate) | |
| 261 target_feature.append(deprecate_copy) | |
| 262 | |
| 263 # Merge remove blocks | |
| 264 for remove in internal_feature.findall('remove'): | |
| 265 remove_copy = copy.deepcopy(remove) | |
| 266 target_feature.append(remove_copy) | |
| 267 | |
| 268 # Remove the internal feature from the tree | |
| 269 tree.remove(internal_feature) | |
| 270 | |
| 271 | |
| 272 def stripNonmatchingAPIs(tree, apiName, actuallyDelete = True): | |
| 273 """Remove tree Elements with 'api' attributes matching apiName. | |
| 274 | |
| 275 tree - Element at the root of the hierarchy to strip. Only its | |
| 276 children can actually be removed, not the tree itself. | |
| 277 apiName - string which much match a command-separated component of | |
| 278 the 'api' attribute. | |
| 279 actuallyDelete - only delete matching elements if True.""" | |
| 280 | |
| 281 stack = deque() | |
| 282 stack.append(tree) | |
| 283 | |
| 284 while len(stack) > 0: | |
| 285 parent = stack.pop() | |
| 286 | |
| 287 for child in parent.findall('*'): | |
| 288 api = child.get('api') | |
| 289 | |
| 290 if apiNameMatch(apiName, api): | |
| 291 # Add child to the queue | |
| 292 stack.append(child) | |
| 293 elif not apiNameMatch(apiName, api): | |
| 294 # Child does not match requested api. Remove it. | |
| 295 if actuallyDelete: | |
| 296 parent.remove(child) | |
| 297 | |
| 298 | |
| 299 class BaseInfo: | |
| 300 """Base class for information about a registry feature | |
| 301 (type/group/enum/command/API/extension). | |
| 302 | |
| 303 Represents the state of a registry feature, used during API generation. | |
| 304 """ | |
| 305 | |
| 306 def __init__(self, elem): | |
| 307 self.required = False | |
| 308 """should this feature be defined during header generation | |
| 309 (has it been removed by a profile or version)?""" | |
| 310 | |
| 311 self.declared = False | |
| 312 "has this feature been defined already?" | |
| 313 | |
| 314 self.elem = elem | |
| 315 "etree Element for this feature" | |
| 316 | |
| 317 self.deprecatedbyversion = None | |
| 318 self.deprecatedbyextensions = [] | |
| 319 self.deprecatedlink = None | |
| 320 | |
| 321 def resetState(self): | |
| 322 """Reset required/declared to initial values. Used | |
| 323 prior to generating a new API interface.""" | |
| 324 self.required = False | |
| 325 self.declared = False | |
| 326 | |
| 327 def compareKeys(self, info, key, required = False): | |
| 328 """Return True if self.elem and info.elem have the same attribute | |
| 329 value for key. | |
| 330 If 'required' is not True, also returns True if neither element | |
| 331 has an attribute value for key.""" | |
| 332 | |
| 333 if required and key not in self.elem.keys(): | |
| 334 return False | |
| 335 return self.elem.get(key) == info.elem.get(key) | |
| 336 | |
| 337 def compareElem(self, info, infoName): | |
| 338 """Return True if self.elem and info.elem have the same definition. | |
| 339 info - the other object | |
| 340 infoName - 'type' / 'group' / 'enum' / 'command' / 'feature' / | |
| 341 'extension'""" | |
| 342 | |
| 343 if infoName == 'enum': | |
| 344 if self.compareKeys(info, 'extends'): | |
| 345 # Either both extend the same type, or no type | |
| 346 if (self.compareKeys(info, 'value', required = True) or | |
| 347 self.compareKeys(info, 'bitpos', required = True)): | |
| 348 # If both specify the same value or bit position, | |
| 349 # they are equal | |
| 350 return True | |
| 351 elif (self.compareKeys(info, 'extnumber') and | |
| 352 self.compareKeys(info, 'offset') and | |
| 353 self.compareKeys(info, 'dir')): | |
| 354 # If both specify the same relative offset, they are equal | |
| 355 return True | |
| 356 elif (self.compareKeys(info, 'alias')): | |
| 357 # If both are aliases of the same value | |
| 358 return True | |
| 359 else: | |
| 360 return False | |
| 361 else: | |
| 362 # The same enum cannot extend two different types | |
| 363 return False | |
| 364 else: | |
| 365 # Non-<enum>s should never be redefined | |
| 366 return False | |
| 367 | |
| 368 | |
| 369 class TypeInfo(BaseInfo): | |
| 370 """Registry information about a type. No additional state | |
| 371 beyond BaseInfo is required.""" | |
| 372 | |
| 373 def __init__(self, elem): | |
| 374 BaseInfo.__init__(self, elem) | |
| 375 self.additionalValidity = [] | |
| 376 self.removedValidity = [] | |
| 377 | |
| 378 def getMembers(self): | |
| 379 """Get a collection of all member elements for this type, if any.""" | |
| 380 return self.elem.findall('member') | |
| 381 | |
| 382 def resetState(self): | |
| 383 BaseInfo.resetState(self) | |
| 384 self.additionalValidity = [] | |
| 385 self.removedValidity = [] | |
| 386 | |
| 387 | |
| 388 class GroupInfo(BaseInfo): | |
| 389 """Registry information about a group of related enums | |
| 390 in an <enums> block, generally corresponding to a C "enum" type.""" | |
| 391 | |
| 392 def __init__(self, elem): | |
| 393 BaseInfo.__init__(self, elem) | |
| 394 | |
| 395 | |
| 396 class EnumInfo(BaseInfo): | |
| 397 """Registry information about an enum""" | |
| 398 | |
| 399 def __init__(self, elem): | |
| 400 BaseInfo.__init__(self, elem) | |
| 401 self.type = elem.get('type') | |
| 402 """numeric type of the value of the <enum> tag | |
| 403 ( '' for GLint, 'u' for GLuint, 'ull' for GLuint64 )""" | |
| 404 if self.type is None: | |
| 405 self.type = '' | |
| 406 | |
| 407 | |
| 408 class CmdInfo(BaseInfo): | |
| 409 """Registry information about a command""" | |
| 410 | |
| 411 def __init__(self, elem): | |
| 412 BaseInfo.__init__(self, elem) | |
| 413 self.additionalValidity = [] | |
| 414 self.removedValidity = [] | |
| 415 | |
| 416 def getParams(self): | |
| 417 """Get a collection of all param elements for this command, if any.""" | |
| 418 return self.elem.findall('param') | |
| 419 | |
| 420 def resetState(self): | |
| 421 BaseInfo.resetState(self) | |
| 422 self.additionalValidity = [] | |
| 423 self.removedValidity = [] | |
| 424 | |
| 425 | |
| 426 class FeatureInfo(BaseInfo): | |
| 427 """Registry information about an API <feature> | |
| 428 or <extension>.""" | |
| 429 | |
| 430 def __init__(self, elem): | |
| 431 BaseInfo.__init__(self, elem) | |
| 432 self.name = elem.get('name') | |
| 433 "feature name string (e.g. 'VK_KHR_surface')" | |
| 434 | |
| 435 self.emit = False | |
| 436 "has this feature been defined already?" | |
| 437 | |
| 438 self.sortorder = int(elem.get('sortorder', 0)) | |
| 439 """explicit numeric sort key within feature and extension groups. | |
| 440 Defaults to 0.""" | |
| 441 | |
| 442 # Determine element category (vendor). Only works | |
| 443 # for <extension> elements. | |
| 444 if elem.tag == 'feature': | |
| 445 # Element category (vendor) is meaningless for <feature> | |
| 446 self.category = 'VERSION' | |
| 447 """category, e.g. VERSION or khr/vendor tag""" | |
| 448 | |
| 449 self.version = elem.get('name') | |
| 450 """feature name string""" | |
| 451 | |
| 452 self.versionNumber = elem.get('number') | |
| 453 """versionNumber - API version number, taken from the 'number' | |
| 454 attribute of <feature>. Extensions do not have API version | |
| 455 numbers and are assigned number 0.""" | |
| 456 | |
| 457 self.number = 0 | |
| 458 self.supported = None | |
| 459 | |
| 460 self.deprecates = elem.findall('deprecate') | |
| 461 else: | |
| 462 # Extract vendor portion of <APIprefix>_<vendor>_<name> | |
| 463 self.category = self.name.split('_', 2)[1] | |
| 464 self.version = "0" | |
| 465 self.versionNumber = "0" | |
| 466 | |
| 467 self.number = int(elem.get('number','0')) | |
| 468 """extension number, used for ordering and for assigning | |
| 469 enumerant offsets. <feature> features do not have extension | |
| 470 numbers and are assigned number 0, as are extensions without | |
| 471 numbers, so sorting works.""" | |
| 472 | |
| 473 self.supported = elem.get('supported', 'disabled') | |
| 474 | |
| 475 class SpirvInfo(BaseInfo): | |
| 476 """Registry information about an API <spirvextensions> | |
| 477 or <spirvcapability>.""" | |
| 478 | |
| 479 def __init__(self, elem): | |
| 480 BaseInfo.__init__(self, elem) | |
| 481 | |
| 482 class FormatInfo(BaseInfo): | |
| 483 """Registry information about an API <format>.""" | |
| 484 | |
| 485 def __init__(self, elem, condition): | |
| 486 BaseInfo.__init__(self, elem) | |
| 487 # Need to save the condition here when it is known | |
| 488 self.condition = condition | |
| 489 | |
| 490 class SyncStageInfo(BaseInfo): | |
| 491 """Registry information about <syncstage>.""" | |
| 492 | |
| 493 def __init__(self, elem, condition): | |
| 494 BaseInfo.__init__(self, elem) | |
| 495 # Need to save the condition here when it is known | |
| 496 self.condition = condition | |
| 497 | |
| 498 class SyncAccessInfo(BaseInfo): | |
| 499 """Registry information about <syncaccess>.""" | |
| 500 | |
| 501 def __init__(self, elem, condition): | |
| 502 BaseInfo.__init__(self, elem) | |
| 503 # Need to save the condition here when it is known | |
| 504 self.condition = condition | |
| 505 | |
| 506 class SyncPipelineInfo(BaseInfo): | |
| 507 """Registry information about <syncpipeline>.""" | |
| 508 | |
| 509 def __init__(self, elem): | |
| 510 BaseInfo.__init__(self, elem) | |
| 511 | |
| 512 class Registry: | |
| 513 """Object representing an API registry, loaded from an XML file.""" | |
| 514 | |
| 515 def __init__(self, gen=None, genOpts=None): | |
| 516 if gen is None: | |
| 517 # If not specified, give a default object so messaging will work | |
| 518 self.gen = OutputGenerator() | |
| 519 else: | |
| 520 self.gen = gen | |
| 521 "Output generator used to write headers / messages" | |
| 522 | |
| 523 if genOpts is None: | |
| 524 # If no generator is provided, we may still need the XML API name | |
| 525 # (for example, in genRef.py). | |
| 526 self.genOpts = GeneratorOptions(apiname = APIConventions().xml_api_name) | |
| 527 else: | |
| 528 self.genOpts = genOpts | |
| 529 "Options controlling features to write and how to format them" | |
| 530 | |
| 531 self.gen.registry = self | |
| 532 self.gen.genOpts = self.genOpts | |
| 533 self.gen.genOpts.registry = self | |
| 534 | |
| 535 # Store mergeInternalApis in self to avoid repeated lookups | |
| 536 self.mergeInternalApis = getattr(self.genOpts, 'mergeInternalApis', True) | |
| 537 | |
| 538 self.tree = None | |
| 539 "ElementTree containing the root `<registry>`" | |
| 540 | |
| 541 self.typedict = {} | |
| 542 "dictionary of TypeInfo objects keyed by type name" | |
| 543 | |
| 544 self.groupdict = {} | |
| 545 "dictionary of GroupInfo objects keyed by group name" | |
| 546 | |
| 547 self.enumdict = {} | |
| 548 "dictionary of EnumInfo objects keyed by enum name" | |
| 549 | |
| 550 self.cmddict = {} | |
| 551 "dictionary of CmdInfo objects keyed by command name" | |
| 552 | |
| 553 self.aliasdict = {} | |
| 554 "dictionary of type and command names mapped to their alias, such as VkFooKHR -> VkFoo" | |
| 555 | |
| 556 self.enumvaluedict = {} | |
| 557 "dictionary of enum values mapped to their type, such as VK_FOO_VALUE -> VkFoo" | |
| 558 | |
| 559 self.apidict = {} | |
| 560 "dictionary of FeatureInfo objects for `<feature>` elements keyed by API name" | |
| 561 | |
| 562 self.extensions = [] | |
| 563 "list of `<extension>` Elements" | |
| 564 | |
| 565 self.extdict = {} | |
| 566 "dictionary of FeatureInfo objects for `<extension>` elements keyed by extension name" | |
| 567 | |
| 568 self.spirvextdict = {} | |
| 569 "dictionary of FeatureInfo objects for `<spirvextension>` elements keyed by spirv extension name" | |
| 570 | |
| 571 self.spirvcapdict = {} | |
| 572 "dictionary of FeatureInfo objects for `<spirvcapability>` elements keyed by spirv capability name" | |
| 573 | |
| 574 self.formatsdict = {} | |
| 575 "dictionary of FeatureInfo objects for `<format>` elements keyed by VkFormat name" | |
| 576 | |
| 577 self.syncstagedict = {} | |
| 578 "dictionary of Sync*Info objects for `<syncstage>` elements keyed by VkPipelineStageFlagBits2 name" | |
| 579 | |
| 580 self.syncaccessdict = {} | |
| 581 "dictionary of Sync*Info objects for `<syncaccess>` elements keyed by VkAccessFlagBits2 name" | |
| 582 | |
| 583 self.syncpipelinedict = {} | |
| 584 "dictionary of Sync*Info objects for `<syncpipeline>` elements keyed by pipeline type name" | |
| 585 | |
| 586 self.emitFeatures = False | |
| 587 """True to actually emit features for a version / extension, | |
| 588 or False to just treat them as emitted""" | |
| 589 | |
| 590 self.breakPat = None | |
| 591 "regexp pattern to break on when generating names" | |
| 592 # self.breakPat = re.compile('VkFenceImportFlagBits.*') | |
| 593 | |
| 594 self.requiredextensions = [] # Hack - can remove it after validity generator goes away | |
| 595 | |
| 596 # ** Global types for automatic source generation ** | |
| 597 # Length Member data | |
| 598 self.commandextensiontuple = namedtuple('commandextensiontuple', | |
| 599 ['command', # The name of the command being modified | |
| 600 'value', # The value to append to the command | |
| 601 'extension']) # The name of the extension that added it | |
| 602 self.validextensionstructs = defaultdict(list) | |
| 603 self.commandextensionsuccesses = [] | |
| 604 self.commandextensionerrors = [] | |
| 605 | |
| 606 self.filename = None | |
| 607 | |
| 608 def loadElementTree(self, tree): | |
| 609 """Load ElementTree into a Registry object and parse it.""" | |
| 610 self.tree = tree | |
| 611 self.parseTree() | |
| 612 | |
| 613 def loadFile(self, file): | |
| 614 """Load an API registry XML file into a Registry object and parse it""" | |
| 615 self.filename = file | |
| 616 self.tree = etree.parse(file) | |
| 617 self.parseTree() | |
| 618 | |
| 619 def setGenerator(self, gen): | |
| 620 """Specify output generator object. | |
| 621 | |
| 622 `None` restores the default generator.""" | |
| 623 self.gen = gen | |
| 624 self.gen.setRegistry(self) | |
| 625 | |
| 626 def addElementInfo(self, elem, info, infoName, dictionary): | |
| 627 """Add information about an element to the corresponding dictionary. | |
| 628 | |
| 629 Intended for internal use only. | |
| 630 | |
| 631 - elem - `<type>`/`<enums>`/`<enum>`/`<command>`/`<feature>`/`<extension>`/`<spirvextension>`/`<spirvcapability>`/`<format>`/`<syncstage>`/`<syncaccess>`/`<syncpipeline>` Element | |
| 632 - info - corresponding {Type|Group|Enum|Cmd|Feature|Spirv|Format|SyncStage|SyncAccess|SyncPipeline}Info object | |
| 633 - infoName - 'type' / 'group' / 'enum' / 'command' / 'feature' / 'extension' / 'spirvextension' / 'spirvcapability' / 'format' / 'syncstage' / 'syncaccess' / 'syncpipeline' | |
| 634 - dictionary - self.{type|group|enum|cmd|api|ext|format|spirvext|spirvcap|sync}dict | |
| 635 | |
| 636 The dictionary key is the element 'name' attribute.""" | |
| 637 | |
| 638 # self.gen.logMsg('diag', 'Adding ElementInfo.required =', | |
| 639 # info.required, 'name =', elem.get('name')) | |
| 640 key = elem.get('name') | |
| 641 if key in dictionary: | |
| 642 if not dictionary[key].compareElem(info, infoName): | |
| 643 self.gen.logMsg('warn', 'Attempt to redefine', key, | |
| 644 '(this should not happen)') | |
| 645 else: | |
| 646 dictionary[key] = info | |
| 647 | |
| 648 def lookupElementInfo(self, fname, dictionary): | |
| 649 """Find a {Type|Enum|Cmd}Info object by name. | |
| 650 | |
| 651 Intended for internal use only. | |
| 652 | |
| 653 If an object qualified by API name exists, use that. | |
| 654 | |
| 655 - fname - name of type / enum / command | |
| 656 - dictionary - self.{type|enum|cmd}dict""" | |
| 657 key = (fname, self.genOpts.apiname) | |
| 658 if key in dictionary: | |
| 659 # self.gen.logMsg('diag', 'Found API-specific element for feature', fname) | |
| 660 return dictionary[key] | |
| 661 if fname in dictionary: | |
| 662 # self.gen.logMsg('diag', 'Found generic element for feature', fname) | |
| 663 return dictionary[fname] | |
| 664 | |
| 665 return None | |
| 666 | |
| 667 def breakOnName(self, regexp): | |
| 668 """Specify a feature name regexp to break on when generating features.""" | |
| 669 self.breakPat = re.compile(regexp) | |
| 670 | |
| 671 def addEnumValue(self, enum, type_name): | |
| 672 """Track aliasing and map back from enum values to their type""" | |
| 673 # Record alias, if any | |
| 674 value = enum.get('name') | |
| 675 alias = enum.get('alias') | |
| 676 if alias: | |
| 677 self.aliasdict[value] = alias | |
| 678 # Map the value back to the type | |
| 679 if type_name in self.aliasdict: | |
| 680 type_name = self.aliasdict[type_name] | |
| 681 if value in self.enumvaluedict: | |
| 682 # Some times the same enum is defined by multiple extensions | |
| 683 assert(type_name == self.enumvaluedict[value]) | |
| 684 else: | |
| 685 self.enumvaluedict[value] = type_name | |
| 686 | |
| 687 def parseTree(self): | |
| 688 """Parse the registry Element, once created""" | |
| 689 # This must be the Element for the root <registry> | |
| 690 if self.tree is None: | |
| 691 raise RuntimeError("Tree not initialized!") | |
| 692 self.reg = self.tree.getroot() | |
| 693 | |
| 694 # Preprocess the tree in one of the following ways: | |
| 695 # - either merge a set of APIs to another API based on their 'api' attributes | |
| 696 # - or remove all elements with non-matching 'api' attributes | |
| 697 # The preprocessing happens through a breath-first tree traversal. | |
| 698 # This is a blunt hammer, but eliminates the need to track and test | |
| 699 # the apis deeper in processing to select the correct elements and | |
| 700 # avoid duplicates. | |
| 701 # Schema validation should prevent duplicate elements with | |
| 702 # overlapping api attributes, or where one element has an api | |
| 703 # attribute and the other does not. | |
| 704 | |
| 705 if self.genOpts.mergeApiNames: | |
| 706 mergeAPIs(self.reg, self.genOpts.mergeApiNames.split(','), self.genOpts.apiname) | |
| 707 else: | |
| 708 stripNonmatchingAPIs(self.reg, self.genOpts.apiname, actuallyDelete = True) | |
| 709 | |
| 710 # Merge internal features (apitype="internal") into their public dependents | |
| 711 # This happens after API merging/stripping so we work with the correct API | |
| 712 if self.mergeInternalApis: | |
| 713 mergeInternalFeatures(self.reg, self.genOpts.apiname) | |
| 714 | |
| 715 self.aliasdict = {} | |
| 716 self.enumvaluedict = {} | |
| 717 | |
| 718 # Create dictionary of registry types from toplevel <types> tags | |
| 719 # and add 'name' attribute to each <type> tag (where missing) | |
| 720 # based on its <name> element. | |
| 721 # | |
| 722 # There is usually one <types> block; more are OK | |
| 723 # Required <type> attributes: 'name' or nested <name> tag contents | |
| 724 self.typedict = {} | |
| 725 for type_elem in self.reg.findall('types/type'): | |
| 726 # If the <type> does not already have a 'name' attribute, set | |
| 727 # it from contents of its <name> tag. | |
| 728 name = type_elem.get('name') | |
| 729 if name is None: | |
| 730 name_elem = type_elem.find('name') | |
| 731 if name_elem is None or not name_elem.text: | |
| 732 raise RuntimeError("Type without a name!") | |
| 733 name = name_elem.text | |
| 734 type_elem.set('name', name) | |
| 735 self.addElementInfo(type_elem, TypeInfo(type_elem), 'type', self.typedict) | |
| 736 | |
| 737 # Record alias, if any | |
| 738 alias = type_elem.get('alias') | |
| 739 if alias: | |
| 740 self.aliasdict[name] = alias | |
| 741 | |
| 742 # Create dictionary of registry enum groups from <enums> tags. | |
| 743 # | |
| 744 # Required <enums> attributes: 'name'. If no name is given, one is | |
| 745 # generated, but that group cannot be identified and turned into an | |
| 746 # enum type definition - it is just a container for <enum> tags. | |
| 747 self.groupdict = {} | |
| 748 for group in self.reg.findall('enums'): | |
| 749 self.addElementInfo(group, GroupInfo(group), 'group', self.groupdict) | |
| 750 | |
| 751 # Create dictionary of registry enums from <enum> tags | |
| 752 # | |
| 753 # <enums> tags usually define different namespaces for the values | |
| 754 # defined in those tags, but the actual names all share the | |
| 755 # same dictionary. | |
| 756 # Required <enum> attributes: 'name', 'value' | |
| 757 # For containing <enums> which have type="enum" or type="bitmask", | |
| 758 # tag all contained <enum>s are required. This is a stopgap until | |
| 759 # a better scheme for tagging core and extension enums is created. | |
| 760 self.enumdict = {} | |
| 761 for enums in self.reg.findall('enums'): | |
| 762 required = (enums.get('type') is not None) | |
| 763 type_name = enums.get('name') | |
| 764 # Enum values are defined only for the type that is not aliased to something else. | |
| 765 assert(type_name not in self.aliasdict) | |
| 766 for enum in enums.findall('enum'): | |
| 767 enumInfo = EnumInfo(enum) | |
| 768 enumInfo.required = required | |
| 769 self.addElementInfo(enum, enumInfo, 'enum', self.enumdict) | |
| 770 self.addEnumValue(enum, type_name) | |
| 771 | |
| 772 # Create dictionary of registry commands from <command> tags | |
| 773 # and add 'name' attribute to each <command> tag (where missing) | |
| 774 # based on its <proto><name> element. | |
| 775 # | |
| 776 # There is usually only one <commands> block; more are OK. | |
| 777 # Required <command> attributes: 'name' or <proto><name> tag contents | |
| 778 self.cmddict = {} | |
| 779 # List of commands which alias others. Contains | |
| 780 # [ name, aliasName, element ] | |
| 781 # for each alias | |
| 782 cmdAlias = [] | |
| 783 for cmd in self.reg.findall('commands/command'): | |
| 784 # If the <command> does not already have a 'name' attribute, set | |
| 785 # it from contents of its <proto><name> tag. | |
| 786 name = cmd.get('name') | |
| 787 if name is None: | |
| 788 name_elem = cmd.find('proto/name') | |
| 789 if name_elem is None or not name_elem.text: | |
| 790 raise RuntimeError("Command without a name!") | |
| 791 name = cmd.set('name', name_elem.text) | |
| 792 ci = CmdInfo(cmd) | |
| 793 self.addElementInfo(cmd, ci, 'command', self.cmddict) | |
| 794 alias = cmd.get('alias') | |
| 795 if alias: | |
| 796 cmdAlias.append([name, alias, cmd]) | |
| 797 self.aliasdict[name] = alias | |
| 798 | |
| 799 # Now loop over aliases, injecting a copy of the aliased command's | |
| 800 # Element with the aliased prototype name replaced with the command | |
| 801 # name - if it exists. | |
| 802 # Copy the 'export' sttribute (whether it exists or not) from the | |
| 803 # original, aliased command, since that can be different for a | |
| 804 # command and its alias. | |
| 805 for (name, alias, cmd) in cmdAlias: | |
| 806 if alias in self.cmddict: | |
| 807 aliasInfo = self.cmddict[alias] | |
| 808 cmdElem = copy.deepcopy(aliasInfo.elem) | |
| 809 cmdElem.find('proto/name').text = name | |
| 810 cmdElem.set('name', name) | |
| 811 cmdElem.set('alias', alias) | |
| 812 export = cmd.get('export') | |
| 813 if export is not None: | |
| 814 # Replicate the command's 'export' attribute | |
| 815 cmdElem.set('export', export) | |
| 816 elif cmdElem.get('export') is not None: | |
| 817 # Remove the 'export' attribute, if the alias has one but | |
| 818 # the command does not. | |
| 819 del cmdElem.attrib['export'] | |
| 820 ci = CmdInfo(cmdElem) | |
| 821 # Replace the dictionary entry for the CmdInfo element | |
| 822 self.cmddict[name] = ci | |
| 823 | |
| 824 # @ newString = etree.tostring(base, encoding="unicode").replace(aliasValue, aliasName) | |
| 825 # @elem.append(etree.fromstring(replacement)) | |
| 826 else: | |
| 827 self.gen.logMsg('warn', 'No matching <command> found for command', | |
| 828 cmd.get('name'), 'alias', alias) | |
| 829 | |
| 830 # Create dictionaries of API and extension interfaces | |
| 831 # from toplevel <api> and <extension> tags. | |
| 832 self.apidict = {} | |
| 833 format_condition = dict() | |
| 834 for feature in self.reg.findall('feature'): | |
| 835 featureInfo = FeatureInfo(feature) | |
| 836 self.addElementInfo(feature, featureInfo, 'feature', self.apidict) | |
| 837 | |
| 838 # Add additional enums defined only in <feature> tags | |
| 839 # to the corresponding enumerated type. | |
| 840 # When seen here, the <enum> element, processed to contain the | |
| 841 # numeric enum value, is added to the corresponding <enums> | |
| 842 # element, as well as adding to the enum dictionary. It is no | |
| 843 # longer removed from the <require> element it is introduced in. | |
| 844 # Instead, generateRequiredInterface ignores <enum> elements | |
| 845 # that extend enumerated types. | |
| 846 # | |
| 847 # For <enum> tags which are actually just constants, if there is | |
| 848 # no 'extends' tag but there is a 'value' or 'bitpos' tag, just | |
| 849 # add an EnumInfo record to the dictionary. That works because | |
| 850 # output generation of constants is purely dependency-based, and | |
| 851 # does not need to iterate through the XML tags. | |
| 852 for elem in feature.findall('require'): | |
| 853 for enum in elem.findall('enum'): | |
| 854 addEnumInfo = False | |
| 855 groupName = enum.get('extends') | |
| 856 if groupName is not None: | |
| 857 # self.gen.logMsg('diag', 'Found extension enum', | |
| 858 # enum.get('name')) | |
| 859 # Add version number attribute to the <enum> element | |
| 860 enum.set('version', featureInfo.version) | |
| 861 # Look up the GroupInfo with matching groupName | |
| 862 if groupName in self.groupdict: | |
| 863 # self.gen.logMsg('diag', 'Matching group', | |
| 864 # groupName, 'found, adding element...') | |
| 865 gi = self.groupdict[groupName] | |
| 866 gi.elem.append(copy.deepcopy(enum)) | |
| 867 else: | |
| 868 self.gen.logMsg('warn', 'NO matching group', | |
| 869 groupName, 'for enum', enum.get('name'), 'found.') | |
| 870 if groupName == "VkFormat": | |
| 871 format_name = enum.get('name') | |
| 872 if enum.get('alias'): | |
| 873 format_name = enum.get('alias') | |
| 874 format_condition[format_name] = featureInfo.name | |
| 875 addEnumInfo = True | |
| 876 elif enum.get('value') or enum.get('bitpos') or enum.get('alias'): | |
| 877 # self.gen.logMsg('diag', 'Adding extension constant "enum"', | |
| 878 # enum.get('name')) | |
| 879 addEnumInfo = True | |
| 880 if addEnumInfo: | |
| 881 enumInfo = EnumInfo(enum) | |
| 882 self.addElementInfo(enum, enumInfo, 'enum', self.enumdict) | |
| 883 self.addEnumValue(enum, groupName) | |
| 884 | |
| 885 sync_pipeline_stage_condition = dict() | |
| 886 sync_access_condition = dict() | |
| 887 | |
| 888 self.extensions = self.reg.findall('extensions/extension') | |
| 889 self.extdict = {} | |
| 890 for feature in self.extensions: | |
| 891 featureInfo = FeatureInfo(feature) | |
| 892 self.addElementInfo(feature, featureInfo, 'extension', self.extdict) | |
| 893 | |
| 894 # Add additional enums defined only in <extension> tags | |
| 895 # to the corresponding core type. | |
| 896 # Algorithm matches that of enums in a "feature" tag as above. | |
| 897 # | |
| 898 # This code also adds a 'extnumber' attribute containing the | |
| 899 # extension number, used for enumerant value calculation. | |
| 900 for elem in feature.findall('require'): | |
| 901 for enum in elem.findall('enum'): | |
| 902 addEnumInfo = False | |
| 903 groupName = enum.get('extends') | |
| 904 if groupName is not None: | |
| 905 # self.gen.logMsg('diag', 'Found extension enum', | |
| 906 # enum.get('name')) | |
| 907 | |
| 908 # Add <extension> block's extension number attribute to | |
| 909 # the <enum> element unless specified explicitly, such | |
| 910 # as when redefining an enum in another extension. | |
| 911 extnumber = enum.get('extnumber') | |
| 912 if not extnumber: | |
| 913 enum.set('extnumber', str(featureInfo.number)) | |
| 914 | |
| 915 enum.set('extname', featureInfo.name) | |
| 916 enum.set('supported', noneStr(featureInfo.supported)) | |
| 917 # Look up the GroupInfo with matching groupName | |
| 918 if groupName in self.groupdict: | |
| 919 # self.gen.logMsg('diag', 'Matching group', | |
| 920 # groupName, 'found, adding element...') | |
| 921 gi = self.groupdict[groupName] | |
| 922 gi.elem.append(copy.deepcopy(enum)) | |
| 923 else: | |
| 924 self.gen.logMsg('warn', 'NO matching group', | |
| 925 groupName, 'for enum', enum.get('name'), 'found.') | |
| 926 # This is Vulkan-specific | |
| 927 if groupName == "VkFormat": | |
| 928 format_name = enum.get('name') | |
| 929 if enum.get('alias'): | |
| 930 format_name = enum.get('alias') | |
| 931 if format_name in format_condition: | |
| 932 format_condition[format_name] += f",{featureInfo.name}" | |
| 933 else: | |
| 934 format_condition[format_name] = featureInfo.name | |
| 935 elif groupName == "VkPipelineStageFlagBits2": | |
| 936 stage_flag = enum.get('name') | |
| 937 if enum.get('alias'): | |
| 938 stage_flag = enum.get('alias') | |
| 939 featureName = elem.get('depends') if elem.get('depends') is not None else featureInfo.name | |
| 940 if stage_flag in sync_pipeline_stage_condition: | |
| 941 sync_pipeline_stage_condition[stage_flag] += f",{featureName}" | |
| 942 else: | |
| 943 sync_pipeline_stage_condition[stage_flag] = featureName | |
| 944 elif groupName == "VkAccessFlagBits2": | |
| 945 access_flag = enum.get('name') | |
| 946 if enum.get('alias'): | |
| 947 access_flag = enum.get('alias') | |
| 948 featureName = elem.get('depends') if elem.get('depends') is not None else featureInfo.name | |
| 949 if access_flag in sync_access_condition: | |
| 950 sync_access_condition[access_flag] += f",{featureName}" | |
| 951 else: | |
| 952 sync_access_condition[access_flag] = featureName | |
| 953 | |
| 954 addEnumInfo = True | |
| 955 elif enum.get('value') or enum.get('bitpos') or enum.get('alias'): | |
| 956 # self.gen.logMsg('diag', 'Adding extension constant "enum"', | |
| 957 # enum.get('name')) | |
| 958 addEnumInfo = True | |
| 959 if addEnumInfo: | |
| 960 enumInfo = EnumInfo(enum) | |
| 961 self.addElementInfo(enum, enumInfo, 'enum', self.enumdict) | |
| 962 self.addEnumValue(enum, groupName) | |
| 963 | |
| 964 # Parse out all spirv tags in dictionaries | |
| 965 # Use addElementInfo to catch duplicates | |
| 966 for spirv in self.reg.findall('spirvextensions/spirvextension'): | |
| 967 spirvInfo = SpirvInfo(spirv) | |
| 968 self.addElementInfo(spirv, spirvInfo, 'spirvextension', self.spirvextdict) | |
| 969 for spirv in self.reg.findall('spirvcapabilities/spirvcapability'): | |
| 970 spirvInfo = SpirvInfo(spirv) | |
| 971 self.addElementInfo(spirv, spirvInfo, 'spirvcapability', self.spirvcapdict) | |
| 972 | |
| 973 for format in self.reg.findall('formats/format'): | |
| 974 condition = None | |
| 975 format_name = format.get('name') | |
| 976 if format_name in format_condition: | |
| 977 condition = format_condition[format_name] | |
| 978 formatInfo = FormatInfo(format, condition) | |
| 979 self.addElementInfo(format, formatInfo, 'format', self.formatsdict) | |
| 980 | |
| 981 for stage in self.reg.findall('sync/syncstage'): | |
| 982 condition = None | |
| 983 stage_flag = stage.get('name') | |
| 984 if stage_flag in sync_pipeline_stage_condition: | |
| 985 condition = sync_pipeline_stage_condition[stage_flag] | |
| 986 syncInfo = SyncStageInfo(stage, condition) | |
| 987 self.addElementInfo(stage, syncInfo, 'syncstage', self.syncstagedict) | |
| 988 | |
| 989 for access in self.reg.findall('sync/syncaccess'): | |
| 990 condition = None | |
| 991 access_flag = access.get('name') | |
| 992 if access_flag in sync_access_condition: | |
| 993 condition = sync_access_condition[access_flag] | |
| 994 syncInfo = SyncAccessInfo(access, condition) | |
| 995 self.addElementInfo(access, syncInfo, 'syncaccess', self.syncaccessdict) | |
| 996 | |
| 997 for pipeline in self.reg.findall('sync/syncpipeline'): | |
| 998 syncInfo = SyncPipelineInfo(pipeline) | |
| 999 self.addElementInfo(pipeline, syncInfo, 'syncpipeline', self.syncpipelinedict) | |
| 1000 | |
| 1001 def dumpReg(self, maxlen=120, filehandle=sys.stdout): | |
| 1002 """Dump all the dictionaries constructed from the Registry object. | |
| 1003 | |
| 1004 Diagnostic to dump the dictionaries to specified file handle (default stdout). | |
| 1005 Truncates type / enum / command elements to maxlen characters (default 120)""" | |
| 1006 write('***************************************', file=filehandle) | |
| 1007 write(' ** Dumping Registry contents **', file=filehandle) | |
| 1008 write('***************************************', file=filehandle) | |
| 1009 write('// Types', file=filehandle) | |
| 1010 for name in self.typedict: | |
| 1011 tobj = self.typedict[name] | |
| 1012 write(' Type', name, '->', etree.tostring(tobj.elem)[0:maxlen], file=filehandle) | |
| 1013 write('// Groups', file=filehandle) | |
| 1014 for name in self.groupdict: | |
| 1015 gobj = self.groupdict[name] | |
| 1016 write(' Group', name, '->', etree.tostring(gobj.elem)[0:maxlen], file=filehandle) | |
| 1017 write('// Enums', file=filehandle) | |
| 1018 for name in self.enumdict: | |
| 1019 eobj = self.enumdict[name] | |
| 1020 write(' Enum', name, '->', etree.tostring(eobj.elem)[0:maxlen], file=filehandle) | |
| 1021 write('// Commands', file=filehandle) | |
| 1022 for name in self.cmddict: | |
| 1023 cobj = self.cmddict[name] | |
| 1024 write(' Command', name, '->', etree.tostring(cobj.elem)[0:maxlen], file=filehandle) | |
| 1025 write('// APIs', file=filehandle) | |
| 1026 for key in self.apidict: | |
| 1027 write(' API Version ', key, '->', | |
| 1028 etree.tostring(self.apidict[key].elem)[0:maxlen], file=filehandle) | |
| 1029 write('// Extensions', file=filehandle) | |
| 1030 for key in self.extdict: | |
| 1031 write(' Extension', key, '->', | |
| 1032 etree.tostring(self.extdict[key].elem)[0:maxlen], file=filehandle) | |
| 1033 write('// SPIR-V', file=filehandle) | |
| 1034 for key in self.spirvextdict: | |
| 1035 write(' SPIR-V Extension', key, '->', | |
| 1036 etree.tostring(self.spirvextdict[key].elem)[0:maxlen], file=filehandle) | |
| 1037 for key in self.spirvcapdict: | |
| 1038 write(' SPIR-V Capability', key, '->', | |
| 1039 etree.tostring(self.spirvcapdict[key].elem)[0:maxlen], file=filehandle) | |
| 1040 write('// VkFormat', file=filehandle) | |
| 1041 for key in self.formatsdict: | |
| 1042 write(' VkFormat', key, '->', | |
| 1043 etree.tostring(self.formatsdict[key].elem)[0:maxlen], file=filehandle) | |
| 1044 | |
| 1045 def markTypeRequired(self, typename, required): | |
| 1046 """Require (along with its dependencies) or remove (but not its dependencies) a type. | |
| 1047 | |
| 1048 - typename - name of type | |
| 1049 - required - boolean (to tag features as required or not) | |
| 1050 """ | |
| 1051 self.gen.logMsg('diag', 'tagging type:', typename, '-> required =', required) | |
| 1052 | |
| 1053 # Get TypeInfo object for <type> tag corresponding to typename | |
| 1054 typeinfo = self.lookupElementInfo(typename, self.typedict) | |
| 1055 if typeinfo is not None: | |
| 1056 if required: | |
| 1057 # Tag type dependencies in 'alias' and 'required' attributes as | |
| 1058 # required. This does not un-tag dependencies in a <remove> | |
| 1059 # tag. See comments in markRequired() below for the reason. | |
| 1060 for attrib_name in ['requires', 'alias']: | |
| 1061 depname = typeinfo.elem.get(attrib_name) | |
| 1062 if depname: | |
| 1063 self.gen.logMsg('diag', 'Generating dependent type', | |
| 1064 depname, 'for', attrib_name, 'type', typename) | |
| 1065 # Do not recurse on self-referential structures. | |
| 1066 if typename != depname: | |
| 1067 self.markTypeRequired(depname, required) | |
| 1068 else: | |
| 1069 self.gen.logMsg('diag', 'type', typename, 'is self-referential') | |
| 1070 # Tag types used in defining this type (e.g. in nested | |
| 1071 # <type> tags) | |
| 1072 # Look for <type> in entire <command> tree, | |
| 1073 # not just immediate children | |
| 1074 for subtype in typeinfo.elem.findall('.//type'): | |
| 1075 self.gen.logMsg('diag', 'markRequired: type requires dependent <type>', subtype.text) | |
| 1076 if typename != subtype.text: | |
| 1077 self.markTypeRequired(subtype.text, required) | |
| 1078 else: | |
| 1079 self.gen.logMsg('diag', 'type', typename, 'is self-referential') | |
| 1080 # Tag enums used in defining this type, for example in | |
| 1081 # <member><name>member</name>[<enum>MEMBER_SIZE</enum>]</member> | |
| 1082 for subenum in typeinfo.elem.findall('.//enum'): | |
| 1083 self.gen.logMsg('diag', 'markRequired: type requires dependent <enum>', subenum.text) | |
| 1084 self.markEnumRequired(subenum.text, required) | |
| 1085 # Tag type dependency in 'bitvalues' attributes as | |
| 1086 # required. This ensures that the bit values for a flag | |
| 1087 # are emitted | |
| 1088 depType = typeinfo.elem.get('bitvalues') | |
| 1089 if depType: | |
| 1090 self.gen.logMsg('diag', 'Generating bitflag type', | |
| 1091 depType, 'for type', typename) | |
| 1092 self.markTypeRequired(depType, required) | |
| 1093 group = self.lookupElementInfo(depType, self.groupdict) | |
| 1094 if group is not None: | |
| 1095 group.flagType = typeinfo | |
| 1096 | |
| 1097 typeinfo.required = required | |
| 1098 elif '.h' not in typename: | |
| 1099 self.gen.logMsg('warn', 'type:', typename, 'IS NOT DEFINED') | |
| 1100 | |
| 1101 def markEnumRequired(self, enumname, required): | |
| 1102 """Mark an enum as required or not. | |
| 1103 | |
| 1104 - enumname - name of enum | |
| 1105 - required - boolean (to tag features as required or not)""" | |
| 1106 | |
| 1107 self.gen.logMsg('diag', 'markEnumRequired: tagging enum:', enumname, '-> required =', required) | |
| 1108 enum = self.lookupElementInfo(enumname, self.enumdict) | |
| 1109 if enum is not None: | |
| 1110 # If the enum is part of a group, and is being removed, then | |
| 1111 # look it up in that <enums> tag and remove the Element there, | |
| 1112 # so that it is not visible to generators (which traverse the | |
| 1113 # <enums> tag elements rather than using the dictionaries). | |
| 1114 if not required: | |
| 1115 groupName = enum.elem.get('extends') | |
| 1116 if groupName is not None: | |
| 1117 self.gen.logMsg('diag', f'markEnumRequired: Removing extending enum {enum.elem.get("name")}') | |
| 1118 | |
| 1119 # Look up the Info with matching groupName | |
| 1120 if groupName in self.groupdict: | |
| 1121 gi = self.groupdict[groupName] | |
| 1122 gienum = gi.elem.find(f"enum[@name='{enumname}']") | |
| 1123 if gienum is not None: | |
| 1124 # Remove copy of this enum from the group | |
| 1125 gi.elem.remove(gienum) | |
| 1126 else: | |
| 1127 self.gen.logMsg('warn', 'markEnumRequired: Cannot remove enum', | |
| 1128 enumname, 'not found in group', | |
| 1129 groupName) | |
| 1130 else: | |
| 1131 self.gen.logMsg('warn', 'markEnumRequired: Cannot remove enum', | |
| 1132 enumname, 'from nonexistent group', | |
| 1133 groupName) | |
| 1134 else: | |
| 1135 # This enum is not an extending enum. | |
| 1136 # The XML tree must be searched for all <enums> that | |
| 1137 # might have it, so we know the parent to delete from. | |
| 1138 | |
| 1139 enumName = enum.elem.get('name') | |
| 1140 | |
| 1141 self.gen.logMsg('diag', f'markEnumRequired: Removing non-extending enum {enumName}') | |
| 1142 | |
| 1143 count = 0 | |
| 1144 for enums in self.reg.findall('enums'): | |
| 1145 for thisEnum in enums.findall('enum'): | |
| 1146 if thisEnum.get('name') == enumName: | |
| 1147 # Actually remove it | |
| 1148 count = count + 1 | |
| 1149 enums.remove(thisEnum) | |
| 1150 | |
| 1151 if count == 0: | |
| 1152 self.gen.logMsg('warn', f'markEnumRequired: {enumName}) not found in any <enums> tag') | |
| 1153 | |
| 1154 enum.required = required | |
| 1155 # Tag enum dependencies in 'alias' attribute as required | |
| 1156 depname = enum.elem.get('alias') | |
| 1157 if depname: | |
| 1158 self.gen.logMsg('diag', 'markEnumRequired: Generating dependent enum', | |
| 1159 depname, 'for alias', enumname, 'required =', enum.required) | |
| 1160 self.markEnumRequired(depname, required) | |
| 1161 else: | |
| 1162 self.gen.logMsg('warn', f'markEnumRequired: {enumname} IS NOT DEFINED') | |
| 1163 | |
| 1164 def markCmdRequired(self, cmdname, required): | |
| 1165 """Mark a command as required or not. | |
| 1166 | |
| 1167 - cmdname - name of command | |
| 1168 - required - boolean (to tag features as required or not)""" | |
| 1169 self.gen.logMsg('diag', 'tagging command:', cmdname, '-> required =', required) | |
| 1170 cmd = self.lookupElementInfo(cmdname, self.cmddict) | |
| 1171 if cmd is not None: | |
| 1172 cmd.required = required | |
| 1173 | |
| 1174 # Tag command dependencies in 'alias' attribute as required | |
| 1175 # | |
| 1176 # This is usually not done, because command 'aliases' are not | |
| 1177 # actual C language aliases like type and enum aliases. Instead | |
| 1178 # they are just duplicates of the function signature of the | |
| 1179 # alias. This means that there is no dependency of a command | |
| 1180 # alias on what it aliases. One exception is validity includes, | |
| 1181 # where the spec markup needs the promoted-to validity include | |
| 1182 # even if only the promoted-from command is being built. | |
| 1183 if self.genOpts.requireCommandAliases: | |
| 1184 depname = cmd.elem.get('alias') | |
| 1185 if depname: | |
| 1186 self.gen.logMsg('diag', 'Generating dependent command', | |
| 1187 depname, 'for alias', cmdname) | |
| 1188 self.markCmdRequired(depname, required) | |
| 1189 | |
| 1190 # Tag all parameter types of this command as required. | |
| 1191 # This does not remove types of commands in a <remove> | |
| 1192 # tag, because many other commands may use the same type. | |
| 1193 # We could be more clever and reference count types, | |
| 1194 # instead of using a boolean. | |
| 1195 if required: | |
| 1196 # Look for <type> in entire <command> tree, | |
| 1197 # not just immediate children | |
| 1198 for type_elem in cmd.elem.findall('.//type'): | |
| 1199 self.gen.logMsg('diag', 'markRequired: command implicitly requires dependent type', type_elem.text) | |
| 1200 self.markTypeRequired(type_elem.text, required) | |
| 1201 else: | |
| 1202 self.gen.logMsg('warn', 'command:', cmdname, 'IS NOT DEFINED') | |
| 1203 | |
| 1204 def markRequired(self, featurename, feature, required): | |
| 1205 """Require or remove features specified in the Element. | |
| 1206 | |
| 1207 - featurename - name of the feature | |
| 1208 - feature - Element for `<require>` or `<remove>` tag | |
| 1209 - required - boolean (to tag features as required or not)""" | |
| 1210 self.gen.logMsg('diag', 'markRequired (feature = <too long to print>, required =', required, ')') | |
| 1211 | |
| 1212 # Loop over types, enums, and commands in the tag | |
| 1213 # @@ It would be possible to respect 'api' and 'profile' attributes | |
| 1214 # in individual features, but that is not done yet. | |
| 1215 for typeElem in feature.findall('type'): | |
| 1216 self.markTypeRequired(typeElem.get('name'), required) | |
| 1217 for enumElem in feature.findall('enum'): | |
| 1218 self.markEnumRequired(enumElem.get('name'), required) | |
| 1219 | |
| 1220 for cmdElem in feature.findall('command'): | |
| 1221 self.markCmdRequired(cmdElem.get('name'), required) | |
| 1222 | |
| 1223 # Extensions may need to extend existing commands or other items in the future. | |
| 1224 # So, look for extend tags. | |
| 1225 for extendElem in feature.findall('extend'): | |
| 1226 extendType = extendElem.get('type') | |
| 1227 if extendType == 'command': | |
| 1228 commandName = extendElem.get('name') | |
| 1229 successExtends = extendElem.get('successcodes') | |
| 1230 if successExtends is not None: | |
| 1231 for success in successExtends.split(','): | |
| 1232 self.commandextensionsuccesses.append(self.commandextensiontuple(command=commandName, | |
| 1233 value=success, | |
| 1234 extension=featurename)) | |
| 1235 errorExtends = extendElem.get('errorcodes') | |
| 1236 if errorExtends is not None: | |
| 1237 for error in errorExtends.split(','): | |
| 1238 self.commandextensionerrors.append(self.commandextensiontuple(command=commandName, | |
| 1239 value=error, | |
| 1240 extension=featurename)) | |
| 1241 else: | |
| 1242 self.gen.logMsg('warn', 'extend type:', extendType, 'IS NOT SUPPORTED') | |
| 1243 | |
| 1244 def getAlias(self, elem, dict): | |
| 1245 """Check for an alias in the same require block. | |
| 1246 | |
| 1247 - elem - Element to check for an alias""" | |
| 1248 | |
| 1249 # Try to find an alias | |
| 1250 alias = elem.get('alias') | |
| 1251 if alias is None: | |
| 1252 name = elem.get('name') | |
| 1253 typeinfo = self.lookupElementInfo(name, dict) | |
| 1254 if not typeinfo: | |
| 1255 self.gen.logMsg('error', name, 'is not a known name') | |
| 1256 alias = typeinfo.elem.get('alias') | |
| 1257 | |
| 1258 return alias | |
| 1259 | |
| 1260 def checkForCorrectionAliases(self, alias, require, tag): | |
| 1261 """Check for an alias in the same require block. | |
| 1262 | |
| 1263 - alias - String name of the alias | |
| 1264 - require - `<require>` block from the registry | |
| 1265 - tag - tag to look for in the require block""" | |
| 1266 | |
| 1267 # For the time being, the code below is bypassed. It has the effect | |
| 1268 # of excluding "spelling aliases" created to comply with the style | |
| 1269 # guide, but this leaves references out of the specification and | |
| 1270 # causes broken internal links. | |
| 1271 # | |
| 1272 # if alias and require.findall(tag + "[@name='" + alias + "']"): | |
| 1273 # return True | |
| 1274 | |
| 1275 return False | |
| 1276 | |
| 1277 def fillFeatureDictionary(self, interface, featurename, api, profile): | |
| 1278 """Capture added interfaces for a `<version>` or `<extension>`. | |
| 1279 | |
| 1280 - interface - Element for `<version>` or `<extension>`, containing | |
| 1281 `<require>` and `<remove>` tags | |
| 1282 - featurename - name of the feature | |
| 1283 - api - string specifying API name being generated | |
| 1284 - profile - string specifying API profile being generated""" | |
| 1285 | |
| 1286 # Explicitly initialize known types - errors for unhandled categories | |
| 1287 self.gen.featureDictionary[featurename] = { | |
| 1288 "enumconstant": {}, | |
| 1289 "command": {}, | |
| 1290 "enum": {}, | |
| 1291 "struct": {}, | |
| 1292 "handle": {}, | |
| 1293 "basetype": {}, | |
| 1294 "include": {}, | |
| 1295 "define": {}, | |
| 1296 "bitmask": {}, | |
| 1297 "union": {}, | |
| 1298 "funcpointer": {}, | |
| 1299 } | |
| 1300 | |
| 1301 # <require> marks things that are required by this version/profile | |
| 1302 for require in interface.findall('require'): | |
| 1303 if matchAPIProfile(api, profile, require): | |
| 1304 | |
| 1305 # Determine the required extension or version needed for a require block | |
| 1306 # Assumes that only one of these is specified | |
| 1307 # 'extension', and therefore 'required_key', may be a boolean | |
| 1308 # expression of extension names. | |
| 1309 # 'required_key' is used only as a dictionary key at | |
| 1310 # present, and passed through to the script generators, so | |
| 1311 # they must be prepared to parse that boolean expression. | |
| 1312 required_key = require.get('depends') | |
| 1313 | |
| 1314 # Loop over types, enums, and commands in the tag | |
| 1315 for typeElem in require.findall('type'): | |
| 1316 typename = typeElem.get('name') | |
| 1317 typeinfo = self.lookupElementInfo(typename, self.typedict) | |
| 1318 | |
| 1319 if typeinfo: | |
| 1320 # Remove aliases in the same extension/feature; these are always added as a correction. Do not need the original to be visible. | |
| 1321 alias = self.getAlias(typeElem, self.typedict) | |
| 1322 if not self.checkForCorrectionAliases(alias, require, 'type'): | |
| 1323 # Resolve the type info to the actual type, so we get an accurate read for 'structextends' | |
| 1324 while alias: | |
| 1325 typeinfo = self.lookupElementInfo(alias, self.typedict) | |
| 1326 if not typeinfo: | |
| 1327 raise RuntimeError(f"Missing alias {alias}") | |
| 1328 alias = typeinfo.elem.get('alias') | |
| 1329 | |
| 1330 typecat = typeinfo.elem.get('category') | |
| 1331 typeextends = typeinfo.elem.get('structextends') | |
| 1332 if not required_key in self.gen.featureDictionary[featurename][typecat]: | |
| 1333 self.gen.featureDictionary[featurename][typecat][required_key] = {} | |
| 1334 if not typeextends in self.gen.featureDictionary[featurename][typecat][required_key]: | |
| 1335 self.gen.featureDictionary[featurename][typecat][required_key][typeextends] = [] | |
| 1336 self.gen.featureDictionary[featurename][typecat][required_key][typeextends].append(typename) | |
| 1337 else: | |
| 1338 self.gen.logMsg('warn', f'fillFeatureDictionary: NOT filling for {typename}') | |
| 1339 | |
| 1340 | |
| 1341 for enumElem in require.findall('enum'): | |
| 1342 enumname = enumElem.get('name') | |
| 1343 typeinfo = self.lookupElementInfo(enumname, self.enumdict) | |
| 1344 | |
| 1345 # Remove aliases in the same extension/feature; these are always added as a correction. Do not need the original to be visible. | |
| 1346 alias = self.getAlias(enumElem, self.enumdict) | |
| 1347 if not self.checkForCorrectionAliases(alias, require, 'enum'): | |
| 1348 enumextends = enumElem.get('extends') | |
| 1349 if not required_key in self.gen.featureDictionary[featurename]['enumconstant']: | |
| 1350 self.gen.featureDictionary[featurename]['enumconstant'][required_key] = {} | |
| 1351 if not enumextends in self.gen.featureDictionary[featurename]['enumconstant'][required_key]: | |
| 1352 self.gen.featureDictionary[featurename]['enumconstant'][required_key][enumextends] = [] | |
| 1353 self.gen.featureDictionary[featurename]['enumconstant'][required_key][enumextends].append(enumname) | |
| 1354 else: | |
| 1355 self.gen.logMsg('warn', f'fillFeatureDictionary: NOT filling for {typename}') | |
| 1356 | |
| 1357 for cmdElem in require.findall('command'): | |
| 1358 # Remove aliases in the same extension/feature; these are always added as a correction. Do not need the original to be visible. | |
| 1359 alias = self.getAlias(cmdElem, self.cmddict) | |
| 1360 if not self.checkForCorrectionAliases(alias, require, 'command'): | |
| 1361 if not required_key in self.gen.featureDictionary[featurename]['command']: | |
| 1362 self.gen.featureDictionary[featurename]['command'][required_key] = [] | |
| 1363 self.gen.featureDictionary[featurename]['command'][required_key].append(cmdElem.get('name')) | |
| 1364 else: | |
| 1365 self.gen.logMsg('warn', f'fillFeatureDictionary: NOT filling for {typename}') | |
| 1366 | |
| 1367 def requireFeatures(self, interface, featurename, api, profile): | |
| 1368 """Process `<require>` tags for a `<version>` or `<extension>`. | |
| 1369 | |
| 1370 - interface - Element for `<version>` or `<extension>`, containing | |
| 1371 `<require>` tags | |
| 1372 - featurename - name of the feature | |
| 1373 - api - string specifying API name being generated | |
| 1374 - profile - string specifying API profile being generated""" | |
| 1375 | |
| 1376 # <require> marks things that are required by this version/profile | |
| 1377 for feature in interface.findall('require'): | |
| 1378 if matchAPIProfile(api, profile, feature): | |
| 1379 self.markRequired(featurename, feature, True) | |
| 1380 | |
| 1381 def deprecateFeatures(self, interface, featurename, api, profile): | |
| 1382 """Process `<require>` tags for a `<version>` or `<extension>`. | |
| 1383 | |
| 1384 - interface - Element for `<version>` or `<extension>`, containing | |
| 1385 `<require>` tags | |
| 1386 - featurename - name of the feature | |
| 1387 - api - string specifying API name being generated | |
| 1388 - profile - string specifying API profile being generated""" | |
| 1389 | |
| 1390 versionmatch = APIConventions().is_api_version_name(featurename) | |
| 1391 | |
| 1392 # <deprecate> marks things that are deprecated by this version/profile | |
| 1393 for deprecation in interface.findall('deprecate'): | |
| 1394 if matchAPIProfile(api, profile, deprecation): | |
| 1395 for typeElem in deprecation.findall('type'): | |
| 1396 type = self.lookupElementInfo(typeElem.get('name'), self.typedict) | |
| 1397 if type: | |
| 1398 if versionmatch is not False: | |
| 1399 type.deprecatedbyversion = featurename | |
| 1400 else: | |
| 1401 type.deprecatedbyextensions.append(featurename) | |
| 1402 type.deprecatedlink = deprecation.get('explanationlink') | |
| 1403 else: | |
| 1404 self.gen.logMsg('error', typeElem.get('name'), ' is tagged for deprecation but not present in registry') | |
| 1405 for enumElem in deprecation.findall('enum'): | |
| 1406 enum = self.lookupElementInfo(enumElem.get('name'), self.enumdict) | |
| 1407 if enum: | |
| 1408 if versionmatch is not False: | |
| 1409 enum.deprecatedbyversion = featurename | |
| 1410 else: | |
| 1411 enum.deprecatedbyextensions.append(featurename) | |
| 1412 enum.deprecatedlink = deprecation.get('explanationlink') | |
| 1413 else: | |
| 1414 self.gen.logMsg('error', enumElem.get('name'), ' is tagged for deprecation but not present in registry') | |
| 1415 for cmdElem in deprecation.findall('command'): | |
| 1416 cmd = self.lookupElementInfo(cmdElem.get('name'), self.cmddict) | |
| 1417 if cmd: | |
| 1418 if versionmatch is not False: | |
| 1419 cmd.deprecatedbyversion = featurename | |
| 1420 else: | |
| 1421 cmd.deprecatedbyextensions.append(featurename) | |
| 1422 cmd.deprecatedlink = deprecation.get('explanationlink') | |
| 1423 else: | |
| 1424 self.gen.logMsg('error', cmdElem.get('name'), ' is tagged for deprecation but not present in registry') | |
| 1425 | |
| 1426 def removeFeatures(self, interface, featurename, api, profile): | |
| 1427 """Process `<remove>` tags for a `<version>` or `<extension>`. | |
| 1428 | |
| 1429 - interface - Element for `<version>` or `<extension>`, containing | |
| 1430 `<remove>` tags | |
| 1431 - featurename - name of the feature | |
| 1432 - api - string specifying API name being generated | |
| 1433 - profile - string specifying API profile being generated""" | |
| 1434 | |
| 1435 # <remove> marks things that are removed by this version/profile | |
| 1436 for feature in interface.findall('remove'): | |
| 1437 if matchAPIProfile(api, profile, feature): | |
| 1438 self.markRequired(featurename, feature, False) | |
| 1439 | |
| 1440 def assignAdditionalValidity(self, interface, api, profile): | |
| 1441 # Loop over all usage inside all <require> tags. | |
| 1442 for feature in interface.findall('require'): | |
| 1443 if matchAPIProfile(api, profile, feature): | |
| 1444 for v in feature.findall('usage'): | |
| 1445 if v.get('command'): | |
| 1446 self.cmddict[v.get('command')].additionalValidity.append(copy.deepcopy(v)) | |
| 1447 if v.get('struct'): | |
| 1448 self.typedict[v.get('struct')].additionalValidity.append(copy.deepcopy(v)) | |
| 1449 | |
| 1450 def removeAdditionalValidity(self, interface, api, profile): | |
| 1451 # Loop over all usage inside all <remove> tags. | |
| 1452 for feature in interface.findall('remove'): | |
| 1453 if matchAPIProfile(api, profile, feature): | |
| 1454 for v in feature.findall('usage'): | |
| 1455 if v.get('command'): | |
| 1456 self.cmddict[v.get('command')].removedValidity.append(copy.deepcopy(v)) | |
| 1457 if v.get('struct'): | |
| 1458 self.typedict[v.get('struct')].removedValidity.append(copy.deepcopy(v)) | |
| 1459 | |
| 1460 def generateFeature(self, fname, ftype, dictionary, explicit=False): | |
| 1461 """Generate a single type / enum group / enum / command, | |
| 1462 and all its dependencies as needed. | |
| 1463 | |
| 1464 - fname - name of feature (`<type>`/`<enum>`/`<command>`) | |
| 1465 - ftype - type of feature, 'type' | 'enum' | 'command' | |
| 1466 - dictionary - of *Info objects - self.{type|enum|cmd}dict | |
| 1467 - explicit - True if this is explicitly required by the top-level | |
| 1468 XML <require> tag, False if it is a dependency of an explicit | |
| 1469 requirement.""" | |
| 1470 | |
| 1471 self.gen.logMsg('diag', 'generateFeature: generating', ftype, fname) | |
| 1472 | |
| 1473 if not (explicit or self.genOpts.requireDepends): | |
| 1474 self.gen.logMsg('diag', 'generateFeature: NOT generating', ftype, fname, 'because generator does not require dependencies') | |
| 1475 return | |
| 1476 | |
| 1477 f = self.lookupElementInfo(fname, dictionary) | |
| 1478 if f is None: | |
| 1479 # No such feature. This is an error, but reported earlier | |
| 1480 self.gen.logMsg('diag', 'No entry found for feature', fname, | |
| 1481 'returning!') | |
| 1482 return | |
| 1483 | |
| 1484 # If feature is not required, or has already been declared, return | |
| 1485 if not f.required: | |
| 1486 self.gen.logMsg('diag', 'Skipping', ftype, fname, '(not required)') | |
| 1487 return | |
| 1488 if f.declared: | |
| 1489 self.gen.logMsg('diag', 'Skipping', ftype, fname, '(already declared)') | |
| 1490 return | |
| 1491 # Always mark feature declared, as though actually emitted | |
| 1492 f.declared = True | |
| 1493 | |
| 1494 # Determine if this is an alias, and of what, if so | |
| 1495 alias = f.elem.get('alias') | |
| 1496 if alias: | |
| 1497 self.gen.logMsg('diag', fname, 'is an alias of', alias) | |
| 1498 | |
| 1499 # Pull in dependent declaration(s) of the feature. | |
| 1500 # For types, there may be one type in the 'requires' attribute of | |
| 1501 # the element, one in the 'alias' attribute, and many in | |
| 1502 # embedded <type> and <enum> tags within the element. | |
| 1503 # For commands, there may be many in <type> tags within the element. | |
| 1504 # For enums, no dependencies are allowed (though perhaps if you | |
| 1505 # have a uint64 enum, it should require that type). | |
| 1506 genProc = None | |
| 1507 followupFeature = None | |
| 1508 if ftype == 'type': | |
| 1509 genProc = self.gen.genType | |
| 1510 | |
| 1511 # Generate type dependencies in 'alias' and 'requires' attributes | |
| 1512 if alias: | |
| 1513 self.generateFeature(alias, 'type', self.typedict) | |
| 1514 requires = f.elem.get('requires') | |
| 1515 if requires: | |
| 1516 self.gen.logMsg('diag', 'Generating required dependent type', | |
| 1517 requires) | |
| 1518 self.generateFeature(requires, 'type', self.typedict) | |
| 1519 | |
| 1520 # Generate types used in defining this type (e.g. in nested | |
| 1521 # <type> tags) | |
| 1522 # Look for <type> in entire <command> tree, | |
| 1523 # not just immediate children | |
| 1524 for subtype in f.elem.findall('.//type'): | |
| 1525 self.gen.logMsg('diag', 'Generating required dependent <type>', | |
| 1526 subtype.text) | |
| 1527 self.generateFeature(subtype.text, 'type', self.typedict) | |
| 1528 | |
| 1529 # Generate enums used in defining this type, for example in | |
| 1530 # <member><name>member</name>[<enum>MEMBER_SIZE</enum>]</member> | |
| 1531 for subtype in f.elem.findall('.//enum'): | |
| 1532 self.gen.logMsg('diag', 'Generating required dependent <enum>', | |
| 1533 subtype.text) | |
| 1534 self.generateFeature(subtype.text, 'enum', self.enumdict) | |
| 1535 | |
| 1536 # If the type is an enum group, look up the corresponding | |
| 1537 # group in the group dictionary and generate that instead. | |
| 1538 if f.elem.get('category') == 'enum': | |
| 1539 self.gen.logMsg('diag', 'Type', fname, 'is an enum group, so generate that instead') | |
| 1540 group = self.lookupElementInfo(fname, self.groupdict) | |
| 1541 if alias is not None: | |
| 1542 # An alias of another group name. | |
| 1543 # Pass to genGroup with 'alias' parameter = aliased name | |
| 1544 self.gen.logMsg('diag', 'Generating alias', fname, | |
| 1545 'for enumerated type', alias) | |
| 1546 # Now, pass the *aliased* GroupInfo to the genGroup, but | |
| 1547 # with an additional parameter which is the alias name. | |
| 1548 genProc = self.gen.genGroup | |
| 1549 f = self.lookupElementInfo(alias, self.groupdict) | |
| 1550 elif group is None: | |
| 1551 self.gen.logMsg('warn', 'Skipping enum type', fname, | |
| 1552 ': No matching enumerant group') | |
| 1553 return | |
| 1554 else: | |
| 1555 genProc = self.gen.genGroup | |
| 1556 f = group | |
| 1557 | |
| 1558 # @ The enum group is not ready for generation. At this | |
| 1559 # @ point, it contains all <enum> tags injected by | |
| 1560 # @ <extension> tags without any verification of whether | |
| 1561 # @ they are required or not. It may also contain | |
| 1562 # @ duplicates injected by multiple consistent | |
| 1563 # @ definitions of an <enum>. | |
| 1564 | |
| 1565 # @ Pass over each enum, marking its enumdict[] entry as | |
| 1566 # @ required or not. Mark aliases of enums as required, | |
| 1567 # @ too. | |
| 1568 | |
| 1569 enums = group.elem.findall('enum') | |
| 1570 | |
| 1571 self.gen.logMsg('diag', 'generateFeature: checking enums for group', fname) | |
| 1572 | |
| 1573 # Check for required enums, including aliases | |
| 1574 # LATER - Check for, report, and remove duplicates? | |
| 1575 enumAliases = [] | |
| 1576 for elem in enums: | |
| 1577 name = elem.get('name') | |
| 1578 | |
| 1579 required = False | |
| 1580 | |
| 1581 extname = elem.get('extname') | |
| 1582 version = elem.get('version') | |
| 1583 if extname is not None: | |
| 1584 # 'supported' attribute was injected when the <enum> element was | |
| 1585 # moved into the <enums> group in Registry.parseTree() | |
| 1586 supported_list = elem.get('supported').split(",") | |
| 1587 if self.genOpts.defaultExtensions in supported_list: | |
| 1588 required = True | |
| 1589 elif re.match(self.genOpts.addExtensions, extname) is not None: | |
| 1590 required = True | |
| 1591 elif version is not None: | |
| 1592 required = re.match(self.genOpts.emitversions, version) is not None | |
| 1593 else: | |
| 1594 required = True | |
| 1595 | |
| 1596 self.gen.logMsg('diag', '* required =', required, 'for', name) | |
| 1597 if required: | |
| 1598 # Mark this element as required (in the element, not the EnumInfo) | |
| 1599 elem.set('required', 'true') | |
| 1600 # If it is an alias, track that for later use | |
| 1601 enumAlias = elem.get('alias') | |
| 1602 if enumAlias: | |
| 1603 enumAliases.append(enumAlias) | |
| 1604 for elem in enums: | |
| 1605 name = elem.get('name') | |
| 1606 if name in enumAliases: | |
| 1607 elem.set('required', 'true') | |
| 1608 self.gen.logMsg('diag', '* also need to require alias', name) | |
| 1609 if f is None: | |
| 1610 raise RuntimeError("Should not get here") | |
| 1611 if f.elem.get('category') == 'bitmask': | |
| 1612 followupFeature = f.elem.get('bitvalues') | |
| 1613 elif ftype == 'command': | |
| 1614 # Generate command dependencies in 'alias' attribute | |
| 1615 if alias: | |
| 1616 self.generateFeature(alias, 'command', self.cmddict) | |
| 1617 | |
| 1618 genProc = self.gen.genCmd | |
| 1619 for type_elem in f.elem.findall('.//type'): | |
| 1620 depname = type_elem.text | |
| 1621 self.gen.logMsg('diag', 'Generating required parameter type', | |
| 1622 depname) | |
| 1623 self.generateFeature(depname, 'type', self.typedict) | |
| 1624 elif ftype == 'enum': | |
| 1625 # Generate enum dependencies in 'alias' attribute | |
| 1626 if alias: | |
| 1627 self.generateFeature(alias, 'enum', self.enumdict) | |
| 1628 genProc = self.gen.genEnum | |
| 1629 | |
| 1630 # Actually generate the type only if emitting declarations | |
| 1631 if self.emitFeatures: | |
| 1632 self.gen.logMsg('diag', 'Emitting', ftype, 'decl for', fname) | |
| 1633 if genProc is None: | |
| 1634 raise RuntimeError("genProc is None when we should be emitting") | |
| 1635 genProc(f, fname, alias) | |
| 1636 else: | |
| 1637 self.gen.logMsg('diag', 'Skipping', ftype, fname, | |
| 1638 '(should not be emitted)') | |
| 1639 | |
| 1640 if followupFeature: | |
| 1641 self.gen.logMsg('diag', 'Generating required bitvalues <enum>', | |
| 1642 followupFeature) | |
| 1643 self.generateFeature(followupFeature, "type", self.typedict) | |
| 1644 | |
| 1645 def generateRequiredInterface(self, interface): | |
| 1646 """Generate all interfaces required by an API version or extension. | |
| 1647 | |
| 1648 - interface - Element for `<version>` or `<extension>`""" | |
| 1649 | |
| 1650 # Loop over all features inside all <require> tags. | |
| 1651 for features in interface.findall('require'): | |
| 1652 for t in features.findall('type'): | |
| 1653 self.generateFeature(t.get('name'), 'type', self.typedict, explicit=True) | |
| 1654 for e in features.findall('enum'): | |
| 1655 # If this is an enum extending an enumerated type, do not | |
| 1656 # generate it - this has already been done in reg.parseTree, | |
| 1657 # by copying this element into the enumerated type. | |
| 1658 enumextends = e.get('extends') | |
| 1659 if not enumextends: | |
| 1660 self.generateFeature(e.get('name'), 'enum', self.enumdict, explicit=True) | |
| 1661 for c in features.findall('command'): | |
| 1662 self.generateFeature(c.get('name'), 'command', self.cmddict, explicit=True) | |
| 1663 | |
| 1664 def generateSpirv(self, spirv, dictionary): | |
| 1665 if spirv is None: | |
| 1666 self.gen.logMsg('diag', 'No entry found for element', name, | |
| 1667 'returning!') | |
| 1668 return | |
| 1669 | |
| 1670 name = spirv.elem.get('name') | |
| 1671 # No known alias for spirv elements | |
| 1672 alias = None | |
| 1673 if spirv.emit: | |
| 1674 genProc = self.gen.genSpirv | |
| 1675 genProc(spirv, name, alias) | |
| 1676 | |
| 1677 def stripUnsupportedAPIs(self, dictionary, attribute, supportedDictionary): | |
| 1678 """Strip unsupported APIs from attributes of APIs. | |
| 1679 dictionary - *Info dictionary of APIs to be updated | |
| 1680 attribute - attribute name to look for in each API | |
| 1681 supportedDictionary - dictionary in which to look for supported | |
| 1682 API elements in the attribute""" | |
| 1683 | |
| 1684 for key in dictionary: | |
| 1685 eleminfo = dictionary[key] | |
| 1686 attribstring = eleminfo.elem.get(attribute) | |
| 1687 if attribstring is not None: | |
| 1688 apis = [] | |
| 1689 stripped = False | |
| 1690 for api in attribstring.split(','): | |
| 1691 ##print('Checking API {} referenced by {}'.format(api, key)) | |
| 1692 if api in supportedDictionary and supportedDictionary[api].required: | |
| 1693 apis.append(api) | |
| 1694 else: | |
| 1695 stripped = True | |
| 1696 ##print('\t**STRIPPING API {} from {}'.format(api, key)) | |
| 1697 | |
| 1698 # Update the attribute after stripping stuff. | |
| 1699 # Could sort apis before joining, but it is not a clear win | |
| 1700 if stripped: | |
| 1701 eleminfo.elem.set(attribute, ','.join(apis)) | |
| 1702 | |
| 1703 def stripUnsupportedAPIsFromList(self, dictionary, supportedDictionary): | |
| 1704 """Strip unsupported APIs from attributes of APIs. | |
| 1705 dictionary - dictionary of list of structure name strings | |
| 1706 supportedDictionary - dictionary in which to look for supported | |
| 1707 API elements in the attribute""" | |
| 1708 | |
| 1709 for key in dictionary: | |
| 1710 attribstring = dictionary[key] | |
| 1711 if attribstring is not None: | |
| 1712 apis = [] | |
| 1713 stripped = False | |
| 1714 for api in attribstring: | |
| 1715 ##print('Checking API {} referenced by {}'.format(api, key)) | |
| 1716 if supportedDictionary[api].required: | |
| 1717 apis.append(api) | |
| 1718 else: | |
| 1719 stripped = True | |
| 1720 ##print('\t**STRIPPING API {} from {}'.format(api, key)) | |
| 1721 | |
| 1722 # Update the attribute after stripping stuff. | |
| 1723 # Could sort apis before joining, but it is not a clear win | |
| 1724 if stripped: | |
| 1725 dictionary[key] = apis | |
| 1726 | |
| 1727 def generateFormat(self, format, dictionary): | |
| 1728 if format is None: | |
| 1729 self.gen.logMsg('diag', 'No entry found for format element', | |
| 1730 'returning!') | |
| 1731 return | |
| 1732 | |
| 1733 name = format.elem.get('name') | |
| 1734 # No known alias for VkFormat elements | |
| 1735 alias = None | |
| 1736 if format.emit: | |
| 1737 genProc = self.gen.genFormat | |
| 1738 genProc(format, name, alias) | |
| 1739 | |
| 1740 def generateSyncStage(self, sync): | |
| 1741 genProc = self.gen.genSyncStage | |
| 1742 genProc(sync) | |
| 1743 | |
| 1744 def generateSyncAccess(self, sync): | |
| 1745 genProc = self.gen.genSyncAccess | |
| 1746 genProc(sync) | |
| 1747 | |
| 1748 def generateSyncPipeline(self, sync): | |
| 1749 genProc = self.gen.genSyncPipeline | |
| 1750 genProc(sync) | |
| 1751 | |
| 1752 def tagValidExtensionStructs(self): | |
| 1753 """Construct a "validextensionstructs" list for parent structures | |
| 1754 based on "structextends" tags in child structures. | |
| 1755 Only do this for structures tagged as required.""" | |
| 1756 | |
| 1757 for typeinfo in self.typedict.values(): | |
| 1758 type_elem = typeinfo.elem | |
| 1759 if typeinfo.required and type_elem.get('category') == 'struct': | |
| 1760 struct_extends = type_elem.get('structextends') | |
| 1761 if struct_extends is not None: | |
| 1762 for parent in struct_extends.split(','): | |
| 1763 # self.gen.logMsg('diag', type_elem.get('name'), 'extends', parent) | |
| 1764 self.validextensionstructs[parent].append(type_elem.get('name')) | |
| 1765 | |
| 1766 # Sort the lists so they do not depend on the XML order | |
| 1767 for parent in self.validextensionstructs: | |
| 1768 self.validextensionstructs[parent].sort() | |
| 1769 | |
| 1770 def apiGen(self): | |
| 1771 """Generate interface for specified versions using the current | |
| 1772 generator and generator options""" | |
| 1773 | |
| 1774 self.gen.logMsg('diag', '*******************************************') | |
| 1775 self.gen.logMsg('diag', ' Registry.apiGen file:', self.genOpts.filename, | |
| 1776 'api:', self.genOpts.apiname, | |
| 1777 'profile:', self.genOpts.profile) | |
| 1778 self.gen.logMsg('diag', '*******************************************') | |
| 1779 | |
| 1780 # Could reset required/declared flags for all features here. | |
| 1781 # This has been removed as never used. The initial motivation was | |
| 1782 # the idea of calling apiGen() repeatedly for different targets, but | |
| 1783 # this has never been done. The 20% or so build-time speedup that | |
| 1784 # might result is not worth the effort to make it actually work. | |
| 1785 # | |
| 1786 # self.apiReset() | |
| 1787 | |
| 1788 # Compile regexps used to select versions & extensions | |
| 1789 regVersions = re.compile(self.genOpts.versions) | |
| 1790 regEmitVersions = re.compile(self.genOpts.emitversions) | |
| 1791 regAddExtensions = re.compile(self.genOpts.addExtensions) | |
| 1792 regRemoveExtensions = re.compile(self.genOpts.removeExtensions) | |
| 1793 regEmitExtensions = re.compile(self.genOpts.emitExtensions) | |
| 1794 regEmitSpirv = re.compile(self.genOpts.emitSpirv) | |
| 1795 regEmitFormats = re.compile(self.genOpts.emitFormats) | |
| 1796 | |
| 1797 # Get all matching API feature names & add to list of FeatureInfo | |
| 1798 # Note we used to select on feature version attributes, not names. | |
| 1799 features = [] | |
| 1800 apiMatch = False | |
| 1801 for key in self.apidict: | |
| 1802 fi = self.apidict[key] | |
| 1803 api = fi.elem.get('api') | |
| 1804 if apiNameMatch(self.genOpts.apiname, api): | |
| 1805 apiMatch = True | |
| 1806 if regVersions.match(fi.name): | |
| 1807 # Matches API & version #s being generated. Mark for | |
| 1808 # emission and add to the features[] list . | |
| 1809 # @@ Could use 'declared' instead of 'emit'? | |
| 1810 fi.emit = (regEmitVersions.match(fi.name) is not None) | |
| 1811 features.append(fi) | |
| 1812 if not fi.emit: | |
| 1813 self.gen.logMsg('diag', 'NOT tagging feature api =', api, | |
| 1814 'name =', fi.name, 'version =', fi.version, | |
| 1815 'for emission (does not match emitversions pattern)') | |
| 1816 else: | |
| 1817 self.gen.logMsg('diag', 'Including feature api =', api, | |
| 1818 'name =', fi.name, 'version =', fi.version, | |
| 1819 'for emission (matches emitversions pattern)') | |
| 1820 else: | |
| 1821 self.gen.logMsg('diag', 'NOT including feature api =', api, | |
| 1822 'name =', fi.name, 'version =', fi.version, | |
| 1823 '(does not match requested versions)') | |
| 1824 else: | |
| 1825 self.gen.logMsg('diag', 'NOT including feature api =', api, | |
| 1826 'name =', fi.name, | |
| 1827 '(does not match requested API)') | |
| 1828 if not apiMatch: | |
| 1829 self.gen.logMsg('warn', 'No matching API versions found!') | |
| 1830 | |
| 1831 # Get all matching extensions, in order by their extension number, | |
| 1832 # and add to the list of features. | |
| 1833 # Start with extensions whose 'supported' attributes match the API | |
| 1834 # being generated. Add extensions matching the pattern specified in | |
| 1835 # regExtensions, then remove extensions matching the pattern | |
| 1836 # specified in regRemoveExtensions | |
| 1837 for (extName, ei) in sorted(self.extdict.items(), key=lambda x: x[1].number if x[1].number is not None else '0'): | |
| 1838 extName = ei.name | |
| 1839 include = False | |
| 1840 | |
| 1841 # Include extension if defaultExtensions is not None and is | |
| 1842 # exactly matched by the 'supported' attribute. | |
| 1843 if apiNameMatch(self.genOpts.defaultExtensions, | |
| 1844 ei.elem.get('supported')): | |
| 1845 self.gen.logMsg('diag', 'Including extension', | |
| 1846 extName, "(defaultExtensions matches the 'supported' attribute)") | |
| 1847 include = True | |
| 1848 | |
| 1849 # Include additional extensions if the extension name matches | |
| 1850 # the regexp specified in the generator options. This allows | |
| 1851 # forcing extensions into an interface even if they are not | |
| 1852 # tagged appropriately in the registry. | |
| 1853 # However, we still respect the 'supported' attribute. | |
| 1854 if regAddExtensions.match(extName) is not None: | |
| 1855 if not apiNameMatch(self.genOpts.apiname, ei.elem.get('supported')): | |
| 1856 self.gen.logMsg('diag', 'NOT including extension', | |
| 1857 extName, '(matches explicitly requested, but does not match the \'supported\' attribute)') | |
| 1858 include = False | |
| 1859 else: | |
| 1860 self.gen.logMsg('diag', 'Including extension', | |
| 1861 extName, '(matches explicitly requested extensions to add)') | |
| 1862 include = True | |
| 1863 # Remove extensions if the name matches the regexp specified | |
| 1864 # in generator options. This allows forcing removal of | |
| 1865 # extensions from an interface even if they are tagged that | |
| 1866 # way in the registry. | |
| 1867 if regRemoveExtensions.match(extName) is not None: | |
| 1868 self.gen.logMsg('diag', 'Removing extension', | |
| 1869 extName, '(matches explicitly requested extensions to remove)') | |
| 1870 include = False | |
| 1871 | |
| 1872 # If the extension is to be included, add it to the | |
| 1873 # extension features list. | |
| 1874 if include: | |
| 1875 ei.emit = (regEmitExtensions.match(extName) is not None) | |
| 1876 features.append(ei) | |
| 1877 if not ei.emit: | |
| 1878 self.gen.logMsg('diag', 'NOT tagging extension', | |
| 1879 extName, | |
| 1880 'for emission (does not match emitextensions pattern)') | |
| 1881 | |
| 1882 # Hack - can be removed when validity generator goes away | |
| 1883 # (Jon) I am not sure what this does, or if it should | |
| 1884 # respect the ei.emit flag above. | |
| 1885 self.requiredextensions.append(extName) | |
| 1886 else: | |
| 1887 self.gen.logMsg('diag', 'NOT including extension', | |
| 1888 extName, '(does not match api attribute or explicitly requested extensions)') | |
| 1889 | |
| 1890 # Add all spirv elements to list | |
| 1891 # generators decide to emit them all or not | |
| 1892 # Currently no filtering as no client of these elements needs filtering | |
| 1893 spirvexts = [] | |
| 1894 for key in self.spirvextdict: | |
| 1895 si = self.spirvextdict[key] | |
| 1896 si.emit = (regEmitSpirv.match(key) is not None) | |
| 1897 spirvexts.append(si) | |
| 1898 spirvcaps = [] | |
| 1899 for key in self.spirvcapdict: | |
| 1900 si = self.spirvcapdict[key] | |
| 1901 si.emit = (regEmitSpirv.match(key) is not None) | |
| 1902 spirvcaps.append(si) | |
| 1903 | |
| 1904 formats = [] | |
| 1905 for key in self.formatsdict: | |
| 1906 si = self.formatsdict[key] | |
| 1907 si.emit = (regEmitFormats.match(key) is not None) | |
| 1908 formats.append(si) | |
| 1909 | |
| 1910 # Sort the features list, if a sort procedure is defined | |
| 1911 if self.genOpts.sortProcedure: | |
| 1912 self.genOpts.sortProcedure(features) | |
| 1913 | |
| 1914 # Passes 1+2: loop over requested API versions and extensions tagging | |
| 1915 # types/commands/features as required (in an <require> block) or no | |
| 1916 # longer required (in an <remove> block). <remove>s are processed | |
| 1917 # after all <require>s, so removals win. | |
| 1918 # If a profile other than 'None' is being generated, it must | |
| 1919 # match the profile attribute (if any) of the <require> and | |
| 1920 # <remove> tags. | |
| 1921 self.gen.logMsg('diag', 'PASS 1: TAG FEATURES') | |
| 1922 for f in features: | |
| 1923 self.gen.logMsg('diag', 'PASS 1: Tagging required and features for', f.name) | |
| 1924 self.fillFeatureDictionary(f.elem, f.name, self.genOpts.apiname, self.genOpts.profile) | |
| 1925 self.requireFeatures(f.elem, f.name, self.genOpts.apiname, self.genOpts.profile) | |
| 1926 self.deprecateFeatures(f.elem, f.name, self.genOpts.apiname, self.genOpts.profile) | |
| 1927 self.assignAdditionalValidity(f.elem, self.genOpts.apiname, self.genOpts.profile) | |
| 1928 | |
| 1929 for f in features: | |
| 1930 self.gen.logMsg('diag', 'PASS 2: Tagging removed features for', f.name) | |
| 1931 self.removeFeatures(f.elem, f.name, self.genOpts.apiname, self.genOpts.profile) | |
| 1932 self.removeAdditionalValidity(f.elem, self.genOpts.apiname, self.genOpts.profile) | |
| 1933 | |
| 1934 # Now, strip references to APIs that are not required. | |
| 1935 # At present such references may occur in: | |
| 1936 # Structs in <type category="struct"> 'structextends' attributes | |
| 1937 # Enums in <command> 'successcodes' and 'errorcodes' attributes | |
| 1938 self.stripUnsupportedAPIs(self.typedict, 'structextends', self.typedict) | |
| 1939 self.stripUnsupportedAPIs(self.cmddict, 'successcodes', self.enumdict) | |
| 1940 self.stripUnsupportedAPIs(self.cmddict, 'errorcodes', self.enumdict) | |
| 1941 self.stripUnsupportedAPIsFromList(self.validextensionstructs, self.typedict) | |
| 1942 | |
| 1943 # Construct lists of valid extension structures | |
| 1944 self.tagValidExtensionStructs() | |
| 1945 | |
| 1946 # @@May need to strip <spirvcapability> / <spirvextension> <enable> | |
| 1947 # tags of these forms: | |
| 1948 # <enable version="VK_API_VERSION_1_0"/> | |
| 1949 # <enable struct="VkPhysicalDeviceFeatures" feature="geometryShader" requires="VK_VERSION_1_0"/> | |
| 1950 # <enable extension="VK_KHR_shader_draw_parameters"/> | |
| 1951 # <enable property="VkPhysicalDeviceVulkan12Properties" member="shaderDenormPreserveFloat16" value="VK_TRUE" requires="VK_VERSION_1_2,VK_KHR_shader_float_controls"/> | |
| 1952 | |
| 1953 # Pass 3: loop over specified API versions and extensions printing | |
| 1954 # declarations for required things which have not already been | |
| 1955 # generated. | |
| 1956 self.gen.logMsg('diag', 'PASS 3: GENERATE INTERFACES FOR FEATURES') | |
| 1957 self.gen.beginFile(self.genOpts) | |
| 1958 for f in features: | |
| 1959 self.gen.logMsg('diag', 'PASS 3: Generating interface for', | |
| 1960 f.name) | |
| 1961 emit = self.emitFeatures = f.emit | |
| 1962 if not emit: | |
| 1963 self.gen.logMsg('diag', 'PASS 3: NOT declaring feature', | |
| 1964 f.elem.get('name'), 'because it is not tagged for emission') | |
| 1965 # Generate the interface (or just tag its elements as having been | |
| 1966 # emitted, if they have not been). | |
| 1967 self.gen.beginFeature(f.elem, emit) | |
| 1968 self.generateRequiredInterface(f.elem) | |
| 1969 self.gen.endFeature() | |
| 1970 # Generate spirv elements | |
| 1971 for s in spirvexts: | |
| 1972 self.generateSpirv(s, self.spirvextdict) | |
| 1973 for s in spirvcaps: | |
| 1974 self.generateSpirv(s, self.spirvcapdict) | |
| 1975 for s in formats: | |
| 1976 self.generateFormat(s, self.formatsdict) | |
| 1977 for s in self.syncstagedict: | |
| 1978 self.generateSyncStage(self.syncstagedict[s]) | |
| 1979 for s in self.syncaccessdict: | |
| 1980 self.generateSyncAccess(self.syncaccessdict[s]) | |
| 1981 for s in self.syncpipelinedict: | |
| 1982 self.generateSyncPipeline(self.syncpipelinedict[s]) | |
| 1983 self.gen.endFile() | |
| 1984 | |
| 1985 def apiReset(self): | |
| 1986 """Reset type/enum/command dictionaries before generating another API. | |
| 1987 | |
| 1988 Use between apiGen() calls to reset internal state.""" | |
| 1989 for datatype in self.typedict: | |
| 1990 self.typedict[datatype].resetState() | |
| 1991 for enum in self.enumdict: | |
| 1992 self.enumdict[enum].resetState() | |
| 1993 for cmd in self.cmddict: | |
| 1994 self.cmddict[cmd].resetState() | |
| 1995 for cmd in self.apidict: | |
| 1996 self.apidict[cmd].resetState() |
