comparison fuhtark_test/Vulkan-Headers-1.4.334/registry/reg.py @ 1501:f40d9d814c08 default tip

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()