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